]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/ErrorInstance.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / ErrorInstance.cpp
index 91a6fc40eab8117d6c51c90fb2d083aeb38ccb8c..2bf1493bdf817e491b01ad67574376c24be1d2e6 100644 (file)
 #include "config.h"
 #include "ErrorInstance.h"
 
+#include "JSScope.h"
+#include "JSCInlines.h"
+#include "JSGlobalObjectFunctions.h"
+#include <wtf/Vector.h>
+
 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<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) , 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<StackFrame> stackTrace = Vector<StackFrame>();
+
+    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