X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174..ed1e77d3adeb83d26fd1dfb16dd84cabdcefd250:/runtime/ErrorInstance.cpp diff --git a/runtime/ErrorInstance.cpp b/runtime/ErrorInstance.cpp index 91a6fc4..2bf1493 100644 --- a/runtime/ErrorInstance.cpp +++ b/runtime/ErrorInstance.cpp @@ -21,16 +21,172 @@ #include "config.h" #include "ErrorInstance.h" +#include "JSScope.h" +#include "JSCInlines.h" +#include "JSGlobalObjectFunctions.h" +#include + namespace JSC { -ASSERT_HAS_TRIVIAL_DESTRUCTOR(ErrorInstance); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorInstance); + +const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ErrorInstance) }; -const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(ErrorInstance) }; +ErrorInstance::ErrorInstance(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} -ErrorInstance::ErrorInstance(JSGlobalData& globalData, Structure* structure) - : JSNonFinalObject(globalData, structure) - , m_appendSourceToMessage(false) +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 = callFrame->codeBlock(); + codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column); + + int expressionStart = divotPoint - startOffset; + int expressionStop = divotPoint + endOffset; + + const String& sourceString = codeBlock->source()->source(); + if (!expressionStop || expressionStart > static_cast(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) , type, ErrorInstance::FoundExactSource); + else { + // No range information, so give a few characters of context. + const StringImpl* data = sourceString.impl(); + 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) && (*data)[start - 1] != '\n') + start--; + while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start])) + start++; + while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n') + stop++; + while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1])) + stop--; + message = appender(message, codeBlock->source()->getRange(start, stop), 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; +}; + +static bool addErrorInfoAndGetBytecodeOffset(ExecState* exec, VM& vm, JSObject* obj, bool useCurrentFrame, CallFrame*& callFrame, unsigned &bytecodeOffset) +{ + Vector stackTrace = Vector(); + + if (exec && stackTrace.isEmpty()) + vm.interpreter->getStackTrace(stackTrace); + + if (!stackTrace.isEmpty()) { + + ASSERT(exec == vm.topCallFrame || exec == exec->lexicalGlobalObject()->globalExec() || exec == exec->vmEntryGlobalObject()->globalExec()); + + StackFrame* stackFrame; + for (unsigned i = 0 ; i < stackTrace.size(); ++i) { + stackFrame = &stackTrace.at(i); + if (stackFrame->bytecodeOffset) + break; + } + + if (bytecodeOffset) { + FindFirstCallerFrameWithCodeblockFunctor functor(exec); + vm.topCallFrame->iterate(functor); + callFrame = functor.foundCallFrame(); + unsigned stackIndex = functor.index(); + bytecodeOffset = stackTrace.at(stackIndex).bytecodeOffset; + } + + unsigned line; + unsigned column; + stackFrame->computeLineAndColumn(line, column); + obj->putDirect(vm, vm.propertyNames->line, jsNumber(line), ReadOnly | DontDelete); + obj->putDirect(vm, vm.propertyNames->column, jsNumber(column), ReadOnly | DontDelete); + + if (!stackFrame->sourceURL.isEmpty()) + obj->putDirect(vm, vm.propertyNames->sourceURL, jsString(&vm, stackFrame->sourceURL), ReadOnly | DontDelete); + + if (!useCurrentFrame) + stackTrace.remove(0); + obj->putDirect(vm, vm.propertyNames->stack, vm.interpreter->stackTraceAsString(vm.topCallFrame, stackTrace), DontEnum); + + return true; + } + return false; +} + +void ErrorInstance::finishCreation(ExecState* exec, VM& vm, const String& message, bool useCurrentFrame) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + if (!message.isNull()) + putDirect(vm, vm.propertyNames->message, jsString(&vm, message), DontEnum); + + unsigned bytecodeOffset = hasSourceAppender(); + CallFrame* callFrame = nullptr; + bool hasTrace = addErrorInfoAndGetBytecodeOffset(exec, vm, this, useCurrentFrame, callFrame, bytecodeOffset); + + if (hasTrace && callFrame && hasSourceAppender()) { + if (callFrame && callFrame->codeBlock()) + appendSourceToError(callFrame, this, bytecodeOffset); + } +} + } // namespace JSC