/* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2003, 2008 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 * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "config.h" #include "ErrorInstance.h" #include "JSScope.h" #include "JSCInlines.h" #include "JSGlobalObjectFunctions.h" #include namespace JSC { STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ErrorInstance); const ClassInfo ErrorInstance::s_info = { "Error", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(ErrorInstance) }; ErrorInstance::ErrorInstance(VM& vm, Structure* structure) : JSNonFinalObject(vm, structure) { } 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