Files
atom/docs/webkit.md
2012-01-26 10:16:43 -08:00

21 KiB

  • Get webkit source (~30 minutes) git clone --depth 1 https://github.com/WebKit/webkit.git cd webkit

  • Apply tmm1's patch (found at end of this document) patch -p1 < tmm1.patch

  • Build webkit (~2 hours) Tools/Scripts/build-webkit

tmm1's patch


diff --git a/LayoutTests/fast/js/exception-properties-expected.txt b/LayoutTests/fast/js/exception-properties-expected.txt
index 36d7e84..573ef10 100644
--- a/LayoutTests/fast/js/exception-properties-expected.txt
+++ b/LayoutTests/fast/js/exception-properties-expected.txt
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE


 PASS enumerableProperties(error) is []
-PASS enumerableProperties(nativeError) is ["line", "sourceId", "sourceURL"]
+PASS enumerableProperties(nativeError) is ["line", "sourceId", "sourceURL", "stack"]
 PASS Object.getPrototypeOf(nativeError).name is "RangeError"
 PASS Object.getPrototypeOf(nativeError).message is ""
 PASS successfullyParsed is true
diff --git a/LayoutTests/fast/js/script-tests/exception-properties.js b/LayoutTests/fast/js/script-tests/exception-properties.js
index 30789a0..f7c2871 100644
--- a/LayoutTests/fast/js/script-tests/exception-properties.js
+++ b/LayoutTests/fast/js/script-tests/exception-properties.js
@@ -16,7 +16,7 @@ try {
     var error = new Error("message");

     shouldBe('enumerableProperties(error)', '[]');
-    shouldBe('enumerableProperties(nativeError)', '["line", "sourceId", "sourceURL"]');
+    shouldBe('enumerableProperties(nativeError)', '["line", "sourceId", "sourceURL", "jscStack"]');

     shouldBe('Object.getPrototypeOf(nativeError).name', '"RangeError"');
     shouldBe('Object.getPrototypeOf(nativeError).message', '""');
diff --git a/LayoutTests/platform/chromium/test_expectations.txt b/LayoutTests/platform/chromium/test_expectations.txt
index f6e35e0..e6d0b90 100644
--- a/LayoutTests/platform/chromium/test_expectations.txt
+++ b/LayoutTests/platform/chromium/test_expectations.txt
@@ -518,6 +518,10 @@ WONTFIX SKIP : fast/frames/cross-site-this.html = FAIL
 // throw.  V8 follows the spec.
 WONTFIX SKIP : fast/js/reparsing-semicolon-insertion.html = FAIL

+// This tests stack-traces that are generated by JSC. This test should
+// fail since it is specific to jsc.
+WONTFIX SKIP : fast/js/stack-trace.html = FAIL
+
 // Rubber-banding is currently a CG only feature.
 WONTFIX : platform/chromium/rubberbanding = FAIL
 WONTFIX : platform/chromium/compositing/rubberbanding = IMAGE
diff --git a/Source/JavaScriptCore/JavaScriptCore.exp b/Source/JavaScriptCore/JavaScriptCore.exp
index 4bf3ed9..1d11aea 100644
--- a/Source/JavaScriptCore/JavaScriptCore.exp
+++ b/Source/JavaScriptCore/JavaScriptCore.exp
@@ -117,6 +117,7 @@ __ZN3JSC10JSFunction6s_infoE
 __ZN3JSC10JSFunctionC1EPNS_9ExecStateEPNS_14JSGlobalObjectEPNS_9StructureE
 __ZN3JSC10throwErrorEPNS_9ExecStateENS_7JSValueE
 __ZN3JSC10throwErrorEPNS_9ExecStateEPNS_8JSObjectE
+__ZN3JSC11Interpreter13getStackTraceEPNS_12JSGlobalDataEiRN3WTF6VectorINS_10StackFrameELm0EEE
 __ZN3JSC11JSByteArray10putByIndexEPNS_6JSCellEPNS_9ExecStateEjNS_7JSValueE
 __ZN3JSC11JSByteArray15createStructureERNS_12JSGlobalDataEPNS_14JSGlobalObjectENS_7JSValueEPKNS_9ClassInfoE
 __ZN3JSC11JSByteArray18getOwnPropertySlotEPNS_6JSCellEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
index 5095d5c..13ee907 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
+++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
@@ -208,6 +208,7 @@ EXPORTS
     ?getPropertyDescriptor@JSObject@JSC@@QAE_NPAVExecState@2@ABVIdentifier@2@AAVPropertyDescriptor@2@@Z
     ?getPropertyNames@JSObject@JSC@@SAXPAV12@PAVExecState@2@AAVPropertyNameArray@2@W4EnumerationMode@2@@Z
     ?getSlice@ArgList@JSC@@QBEXHAAV12@@Z
+    ?getStackTrace@Interpreter@JSC@@SAXPAVJSGlobalData@2@HAAV?$Vector@UStackFrame@JSC@@$0A@@WTF@@@Z
     ?getString@JSCell@JSC@@QBE?AVUString@2@PAVExecState@2@@Z
     ?getString@JSCell@JSC@@QBE_NPAVExecState@2@AAVUString@2@@Z
     ?getter@PropertyDescriptor@JSC@@QBE?AVJSValue@2@XZ
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 4194901..b9d07dd 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -45,7 +45,6 @@
 #include "JSActivation.h"
 #include "JSArray.h"
 #include "JSByteArray.h"
-#include "JSFunction.h"
 #include "JSNotAnObject.h"
 #include "JSPropertyNameIterator.h"
 #include "LiteralParser.h"
@@ -790,6 +789,95 @@ static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception,
     exception->putDirect(*globalData, globalData->propertyNames->message, jsString(globalData, message));
 }

+static void getCallerLine(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber)
+{
+    (void)globalData;
+    unsigned bytecodeOffset;
+    lineNumber = -1;
+    callFrame = callFrame->removeHostCallFrameFlag();
+
+    if (callFrame->callerFrame() == CallFrame::noCaller() || callFrame->callerFrame()->hasHostCallFrameFlag())
+        return;
+
+    CodeBlock* callerCodeBlock = callFrame->callerFrame()->removeHostCallFrameFlag()->codeBlock();
+
+#if ENABLE(INTERPRETER)
+    if (!globalData->canUseJIT())
+        bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC());
+#if ENABLE(JIT)
+    else
+        bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC());
+#endif
+#else
+    bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC());
+#endif
+
+    lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1);
+}
+
+static ALWAYS_INLINE const UString getSourceURLFromCallFrame(CallFrame* callFrame)
+{
+    if (callFrame->hasHostCallFrameFlag())
+        return UString();
+#if ENABLE(INTERPRETER)
+    if (!callFrame->globalData().canUseJIT())
+        return callFrame->codeBlock()->source()->url();
+#if ENABLE(JIT)
+    return callFrame->codeBlock()->ownerExecutable()->sourceURL();
+#endif
+#else
+    return callFrame->codeBlock()->ownerExecutable()->sourceURL();
+#endif
+}
+
+static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame)
+{
+    if (callFrame->hasHostCallFrameFlag())
+        return StackFrameNativeCode;
+
+    switch (callFrame->codeBlock()->codeType()) {
+    case EvalCode:
+        return StackFrameEvalCode;
+    case FunctionCode:
+        return StackFrameFunctionCode;
+    case GlobalCode:
+        return StackFrameGlobalCode;
+    }
+    ASSERT_NOT_REACHED();
+    return StackFrameGlobalCode;
+}
+
+void Interpreter::getStackTrace(JSGlobalData* globalData, int line, Vector<StackFrame>& results)
+{
+    int stackLimit = 32;
+    CallFrame* callFrame = globalData->topCallFrame->removeHostCallFrameFlag();
+    if (!callFrame || callFrame == CallFrame::noCaller() || !callFrame->codeBlock())
+        return;
+    UString sourceURL;
+    UString traceLevel;
+
+    for (int i = 0; i < stackLimit; ++i) {
+        if (!callFrame || callFrame == CallFrame::noCaller())
+            break;
+        if (callFrame->codeBlock()) {
+            sourceURL = getSourceURLFromCallFrame(callFrame);
+
+            StackFrame s = {
+              Strong<JSObject>(*globalData, callFrame->callee()),
+              Strong<CallFrame>(*globalData, callFrame),
+              getStackFrameCodeType(callFrame),
+              Strong<ExecutableBase>(*globalData, callFrame->codeBlock()->ownerExecutable()),
+              line,
+              sourceURL
+            };
+
+            results.append(s);
+        }
+        getCallerLine(globalData, callFrame, line);
+        callFrame = callFrame->callerFrame()->removeHostCallFrameFlag();
+    }
+}
+
 NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset)
 {
     CodeBlock* codeBlock = callFrame->codeBlock();
@@ -808,7 +896,9 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV

             // FIXME: should only really be adding these properties to VM generated exceptions,
             // but the inspector currently requires these for all thrown objects.
-            addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source());
+            Vector<StackFrame> stackTrace;
+            getStackTrace(&callFrame->globalData(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), stackTrace);
+            addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source(), stackTrace);
         }

         isInterrupt = isInterruptedExecutionException(exception) || isTerminatedExecutionException(exception);
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h
index 6dfd331..748e8fb 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.h
+++ b/Source/JavaScriptCore/interpreter/Interpreter.h
@@ -31,10 +31,12 @@

 #include "ArgList.h"
 #include "JSCell.h"
+#include "JSFunction.h"
 #include "JSValue.h"
 #include "JSObject.h"
 #include "Opcode.h"
 #include "RegisterFile.h"
+#include "StrongInlines.h"

 #include <wtf/HashMap.h>

@@ -42,8 +44,8 @@ namespace JSC {

     class CodeBlock;
     class EvalExecutable;
+    class ExecutableBase;
     class FunctionExecutable;
-    class JSFunction;
     class JSGlobalObject;
     class ProgramExecutable;
     class Register;
@@ -62,16 +64,79 @@ namespace JSC {
         WillExecuteStatement
     };

+    enum StackFrameCodeType {
+        StackFrameGlobalCode,
+        StackFrameEvalCode,
+        StackFrameFunctionCode,
+        StackFrameNativeCode
+    };
+
+    struct StackFrame {
+        Strong<JSObject> callee;
+        Strong<CallFrame> callFrame;
+        StackFrameCodeType codeType;
+        Strong<ExecutableBase> executable;
+        int line;
+        UString sourceURL;
+        UString toString() const
+        {
+            bool hasSourceURLInfo = !sourceURL.isNull() && !sourceURL.isEmpty();
+            bool hasLineInfo = line > -1;
+            String traceLine;
+            String sourceInfo;
+            JSObject* stackFrameCallee = callee.get();
+            UString functionName = "";
+
+            if (hasSourceURLInfo)
+             sourceInfo = hasLineInfo ? String::format("%s:%d", sourceURL.ascii().data(), line)
+                                      : String::format("%s", sourceURL.ascii().data());
+
+            if (stackFrameCallee && stackFrameCallee->inherits(&JSFunction::s_info))
+                functionName = asFunction(stackFrameCallee)->name(callFrame.get());
+
+            switch (codeType) {
+            case StackFrameEvalCode:
+                if (hasSourceURLInfo && !functionName.isEmpty())
+                    traceLine = String::format("at eval at %s (%s)", functionName.ascii().data(), sourceInfo.ascii().data());
+                else if (hasSourceURLInfo)
+                    traceLine = String::format("at eval at <anonymous> (%s)", sourceInfo.ascii().data());
+                else if (!functionName.isEmpty())
+                    traceLine = String::format("at eval at %s", functionName.ascii().data());
+                else
+                    traceLine = String::format("at eval");
+                break;
+            case StackFrameNativeCode:
+                if (!functionName.isEmpty())
+                    traceLine = String::format("at %s (native)", functionName.ascii().data());
+                else
+                    traceLine = "at (native)";
+                break;
+            case StackFrameFunctionCode:
+            case StackFrameGlobalCode:
+                if (hasSourceURLInfo && !functionName.isEmpty())
+                    traceLine = String::format("at %s (%s)", functionName.ascii().data(), sourceInfo.ascii().data());
+                else if (hasSourceURLInfo)
+                    traceLine = String::format("at %s", sourceInfo.ascii().data());
+                else if (!functionName.isEmpty())
+                    traceLine = String::format("at %s", functionName.ascii().data());
+                else
+                    traceLine = String::format("at unknown source");
+                break;
+            }
+            return traceLine.impl();
+        }
+    };
+
     class TopCallFrameSetter {
     public:
         TopCallFrameSetter(JSGlobalData& global, CallFrame* callFrame)
             : globalData(global)
-            , oldCallFrame(global.topCallFrame)
+            , oldCallFrame(global.topCallFrame)
         {
             global.topCallFrame = callFrame;
         }
-
-        ~TopCallFrameSetter()
+
+        ~TopCallFrameSetter()
         {
             globalData.topCallFrame = oldCallFrame;
         }
@@ -141,6 +206,8 @@ namespace JSC {

         NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset);
         NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine);
+        static const UString getTraceLine(CallFrame*, StackFrameCodeType, const UString&, int);
+        static void getStackTrace(JSGlobalData*, int line, Vector<StackFrame>& results);

         void dumpSampleData(ExecState* exec);
         void startSampling();
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index 47ec8c6..6d82f54 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -27,6 +27,7 @@
 #include "CurrentTime.h"
 #include "ExceptionHelpers.h"
 #include "InitializeThreading.h"
+#include "Interpreter.h"
 #include "JSArray.h"
 #include "JSFunction.h"
 #include "JSLock.h"
@@ -78,6 +79,7 @@ static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>&

 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
 #ifndef NDEBUG
 static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
@@ -184,6 +186,7 @@ protected:
         addFunction(globalData, "run", functionRun, 1);
         addFunction(globalData, "load", functionLoad, 1);
         addFunction(globalData, "checkSyntax", functionCheckSyntax, 1);
+        addFunction(globalData, "jscStack", functionJSCStack, 1);
         addFunction(globalData, "readline", functionReadline, 0);
         addFunction(globalData, "preciseTime", functionPreciseTime, 0);
 #if ENABLE(SAMPLING_FLAGS)
@@ -252,6 +255,22 @@ EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }

+EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
+{
+    String trace = "--> Stack trace:\n";
+    Vector<StackFrame> stackTrace;
+    Interpreter::getStackTrace(&exec->globalData(), -1, stackTrace);
+    int i = 0;
+
+    for (Vector<StackFrame>::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) {
+        StackFrame level = *iter;
+        trace += String::format("    %i   %s\n", i, level.toString().utf8().data());
+        i++;
+    }
+    fprintf(stderr, "%s", trace.utf8().data());
+    return JSValue::encode(jsUndefined());
+}
+
 EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
 {
     JSLock lock(SilenceAssertionsOnly);
diff --git a/Source/JavaScriptCore/parser/Parser.h b/Source/JavaScriptCore/parser/Parser.h
index f3d96ff..27dafae 100644
--- a/Source/JavaScriptCore/parser/Parser.h
+++ b/Source/JavaScriptCore/parser/Parser.h
@@ -1016,7 +1016,7 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(JSGlobalObject* lexicalGlobalObj
         else if (isEvalNode<ParsedNode>())
             *exception = createSyntaxError(lexicalGlobalObject, errMsg);
         else
-            *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source);
+            *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source, Vector<StackFrame>());
     }

     if (debugger && !ParsedNode::scopeIsFunction)
diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h
index 08d8644..ce11730 100644
--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h
+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h
@@ -52,6 +52,7 @@
     macro(input) \
     macro(isArray) \
     macro(isPrototypeOf) \
+    macro(stack) \
     macro(length) \
     macro(message) \
     macro(multiline) \
diff --git a/Source/JavaScriptCore/runtime/Error.cpp b/Source/JavaScriptCore/runtime/Error.cpp
index c3f2878..9b4af6f 100644
--- a/Source/JavaScriptCore/runtime/Error.cpp
+++ b/Source/JavaScriptCore/runtime/Error.cpp
@@ -27,6 +27,7 @@
 #include "ConstructData.h"
 #include "ErrorConstructor.h"
 #include "FunctionPrototype.h"
+#include "JSArray.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
 #include "JSObject.h"
@@ -39,6 +40,8 @@ namespace JSC {
 static const char* linePropertyName = "line";
 static const char* sourceIdPropertyName = "sourceId";
 static const char* sourceURLPropertyName = "sourceURL";
+static const char* stackPropertyName = "stack";
+static const char* messagePropertyName = "message";

 JSObject* createError(JSGlobalObject* globalObject, const UString& message)
 {
@@ -117,7 +120,7 @@ JSObject* createURIError(ExecState* exec, const UString& message)
     return createURIError(exec->lexicalGlobalObject(), message);
 }

-JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source)
+JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace)
 {
     intptr_t sourceID = source.provider()->asID();
     const UString& sourceURL = source.provider()->url();
@@ -128,13 +131,29 @@ JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, cons
         error->putDirect(*globalData, Identifier(globalData, sourceIdPropertyName), jsNumber((double)sourceID), ReadOnly | DontDelete);
     if (!sourceURL.isNull())
         error->putDirect(*globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete);
+    if (!stackTrace.isEmpty()) {
+        String trace = String::format("%s: ", JSObject::className(error).ascii().data());
+        trace += asString(error->getDirect(*globalData, Identifier(globalData, messagePropertyName)))->tryGetValue().ascii().data();
+        trace += "\n";
+        //JSArray* stackTraceArray = JSArray::create(*globalData, globalData->dynamicGlobalObject->arrayStructure());
+        for (unsigned i = 0; i < stackTrace.size(); i++) {
+            UString stackLevel = stackTrace[i].toString();
+            trace += "    ";
+            trace += stackLevel.ascii().data();
+            if (i < stackTrace.size() - 1)
+              trace += "\n";
+            //stackTraceArray->push(globalData->topCallFrame, jsString(globalData, stackLevel));
+        }
+        //error->putDirect(*globalData, Identifier(globalData, stackPropertyName), stackTraceArray, ReadOnly | DontDelete);
+        error->putDirect(*globalData, Identifier(globalData, stackPropertyName), jsString(globalData, UString(trace.impl())), ReadOnly | DontDelete);
+    }

     return error;
 }

-JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source)
+JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace)
 {
-    return addErrorInfo(&exec->globalData(), error, line, source);
+    return addErrorInfo(&exec->globalData(), error, line, source, stackTrace);
 }

 bool hasErrorInfo(ExecState* exec, JSObject* error)
diff --git a/Source/JavaScriptCore/runtime/Error.h b/Source/JavaScriptCore/runtime/Error.h
index 88b540a..59b3949 100644
--- a/Source/JavaScriptCore/runtime/Error.h
+++ b/Source/JavaScriptCore/runtime/Error.h
@@ -24,6 +24,7 @@
 #define Error_h

 #include "InternalFunction.h"
+#include "Interpreter.h"
 #include "JSObject.h"
 #include <stdint.h>

@@ -56,9 +57,9 @@ namespace JSC {

     // Methods to add
     bool hasErrorInfo(ExecState*, JSObject* error);
-    JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&);
+    JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&);
     // ExecState wrappers.
-    JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&);
+    JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&);

     // Methods to throw Errors.
     JS_EXPORT_PRIVATE JSValue throwError(ExecState*, JSValue);