summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/ErrorInstance.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/ErrorInstance.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/ErrorInstance.cpp198
1 files changed, 188 insertions, 10 deletions
diff --git a/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/Source/JavaScriptCore/runtime/ErrorInstance.cpp
index c831f9183..6c5b37fb0 100644
--- a/Source/JavaScriptCore/runtime/ErrorInstance.cpp
+++ b/Source/JavaScriptCore/runtime/ErrorInstance.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2003, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2008, 2016 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -21,30 +21,208 @@
#include "config.h"
#include "ErrorInstance.h"
+#include "CodeBlock.h"
+#include "InlineCallFrame.h"
#include "JSScope.h"
-#include "Operations.h"
+#include "JSCInlines.h"
+#include "ParseInt.h"
+#include <wtf/text/StringBuilder.h>
namespace JSC {
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorInstance);
-const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ErrorInstance) };
+const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ErrorInstance) };
ErrorInstance::ErrorInstance(VM& vm, Structure* structure)
: JSNonFinalObject(vm, structure)
- , m_appendSourceToMessage(false)
{
}
-void ErrorInstance::finishCreation(VM& vm, const String& message, Vector<StackFrame> stackTrace)
+ErrorInstance* ErrorInstance::create(ExecState* state, Structure* structure, JSValue message, SourceAppender appender, RuntimeType type, bool useCurrentFrame)
+{
+ VM& vm = state->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ String messageString = message.isUndefined() ? String() : message.toWTFString(state);
+ RETURN_IF_EXCEPTION(scope, nullptr);
+ return create(state, vm, structure, messageString, appender, type, useCurrentFrame);
+}
+
+static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset)
+{
+ ErrorInstance::SourceAppender appender = exception->sourceAppender();
+ exception->clearSourceAppender();
+ RuntimeType type = exception->runtimeTypeForCause();
+ exception->clearRuntimeTypeForCause();
+
+ if (!callFrame->codeBlock()->hasExpressionInfo())
+ return;
+
+ int startOffset = 0;
+ int endOffset = 0;
+ int divotPoint = 0;
+ unsigned line = 0;
+ unsigned column = 0;
+
+ CodeBlock* codeBlock;
+ CodeOrigin codeOrigin = callFrame->codeOrigin();
+ if (codeOrigin && codeOrigin.inlineCallFrame)
+ codeBlock = baselineCodeBlockForInlineCallFrame(codeOrigin.inlineCallFrame);
+ else
+ codeBlock = callFrame->codeBlock();
+
+ codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column);
+
+ int expressionStart = divotPoint - startOffset;
+ int expressionStop = divotPoint + endOffset;
+
+ StringView sourceString = codeBlock->source()->source();
+ if (!expressionStop || expressionStart > static_cast<int>(sourceString.length()))
+ return;
+
+ VM* vm = &callFrame->vm();
+ JSValue jsMessage = exception->getDirect(*vm, vm->propertyNames->message);
+ if (!jsMessage || !jsMessage.isString())
+ return;
+
+ String message = asString(jsMessage)->value(callFrame);
+ if (expressionStart < expressionStop)
+ message = appender(message, codeBlock->source()->getRange(expressionStart, expressionStop).toString(), type, ErrorInstance::FoundExactSource);
+ else {
+ // No range information, so give a few characters of context.
+ int dataLength = sourceString.length();
+ int start = expressionStart;
+ int stop = expressionStart;
+ // Get up to 20 characters of context to the left and right of the divot, clamping to the line.
+ // Then strip whitespace.
+ while (start > 0 && (expressionStart - start < 20) && sourceString[start - 1] != '\n')
+ start--;
+ while (start < (expressionStart - 1) && isStrWhiteSpace(sourceString[start]))
+ start++;
+ while (stop < dataLength && (stop - expressionStart < 20) && sourceString[stop] != '\n')
+ stop++;
+ while (stop > expressionStart && isStrWhiteSpace(sourceString[stop - 1]))
+ stop--;
+ message = appender(message, codeBlock->source()->getRange(start, stop).toString(), type, ErrorInstance::FoundApproximateSource);
+ }
+ exception->putDirect(*vm, vm->propertyNames->message, jsString(vm, message));
+
+}
+
+class FindFirstCallerFrameWithCodeblockFunctor {
+public:
+ FindFirstCallerFrameWithCodeblockFunctor(CallFrame* startCallFrame)
+ : m_startCallFrame(startCallFrame)
+ , m_foundCallFrame(nullptr)
+ , m_foundStartCallFrame(false)
+ , m_index(0)
+ { }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ if (!m_foundStartCallFrame && (visitor->callFrame() == m_startCallFrame))
+ m_foundStartCallFrame = true;
+
+ if (m_foundStartCallFrame) {
+ if (visitor->callFrame()->codeBlock()) {
+ m_foundCallFrame = visitor->callFrame();
+ return StackVisitor::Done;
+ }
+ m_index++;
+ }
+
+ return StackVisitor::Continue;
+ }
+
+ CallFrame* foundCallFrame() const { return m_foundCallFrame; }
+ unsigned index() const { return m_index; }
+
+private:
+ CallFrame* m_startCallFrame;
+ CallFrame* m_foundCallFrame;
+ bool m_foundStartCallFrame;
+ unsigned m_index;
+};
+
+void ErrorInstance::finishCreation(ExecState* exec, VM& vm, const String& message, bool useCurrentFrame)
{
Base::finishCreation(vm);
- ASSERT(inherits(info()));
+ ASSERT(inherits(vm, info()));
if (!message.isNull())
putDirect(vm, vm.propertyNames->message, jsString(&vm, message), DontEnum);
-
- if (!stackTrace.isEmpty())
- putDirect(vm, vm.propertyNames->stack, vm.interpreter->stackTraceAsString(vm.topCallFrame, stackTrace), DontEnum);
+
+ unsigned bytecodeOffset = 0;
+ CallFrame* callFrame = nullptr;
+ bool hasTrace = addErrorInfoAndGetBytecodeOffset(exec, vm, this, useCurrentFrame, callFrame, hasSourceAppender() ? &bytecodeOffset : nullptr);
+
+ if (hasTrace && callFrame && hasSourceAppender()) {
+ if (callFrame && callFrame->codeBlock())
+ appendSourceToError(callFrame, this, bytecodeOffset);
+ }
}
-
+
+// Based on ErrorPrototype's errorProtoFuncToString(), but is modified to
+// have no observable side effects to the user (i.e. does not call proxies,
+// and getters).
+String ErrorInstance::sanitizedToString(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSValue nameValue;
+ auto namePropertName = vm.propertyNames->name;
+ PropertySlot nameSlot(this, PropertySlot::InternalMethodType::VMInquiry);
+
+ JSValue currentObj = this;
+ unsigned prototypeDepth = 0;
+
+ // We only check the current object and its prototype (2 levels) because normal
+ // Error objects may have a name property, and if not, its prototype should have
+ // a name property for the type of error e.g. "SyntaxError".
+ while (currentObj.isCell() && prototypeDepth++ < 2) {
+ JSObject* obj = jsCast<JSObject*>(currentObj);
+ if (JSObject::getOwnPropertySlot(obj, exec, namePropertName, nameSlot) && nameSlot.isValue()) {
+ nameValue = nameSlot.getValue(exec, namePropertName);
+ break;
+ }
+ currentObj = obj->getPrototypeDirect();
+ }
+ ASSERT(!scope.exception());
+
+ String nameString;
+ if (!nameValue)
+ nameString = ASCIILiteral("Error");
+ else {
+ nameString = nameValue.toWTFString(exec);
+ RETURN_IF_EXCEPTION(scope, String());
+ }
+
+ JSValue messageValue;
+ auto messagePropertName = vm.propertyNames->message;
+ PropertySlot messageSlot(this, PropertySlot::InternalMethodType::VMInquiry);
+ if (JSObject::getOwnPropertySlot(this, exec, messagePropertName, messageSlot) && messageSlot.isValue())
+ messageValue = messageSlot.getValue(exec, messagePropertName);
+ ASSERT(!scope.exception());
+
+ String messageString;
+ if (!messageValue)
+ messageString = String();
+ else {
+ messageString = messageValue.toWTFString(exec);
+ RETURN_IF_EXCEPTION(scope, String());
+ }
+
+ if (!nameString.length())
+ return messageString;
+
+ if (!messageString.length())
+ return nameString;
+
+ StringBuilder builder;
+ builder.append(nameString);
+ builder.appendLiteral(": ");
+ builder.append(messageString);
+ return builder.toString();
+}
+
} // namespace JSC