Changeset 107980 in webkit
- Timestamp:
- Feb 16, 2012 2:38:39 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r107977 r107980 1 2012-02-16 Oliver Hunt <oliver@apple.com> 2 3 Implement Error.stack 4 https://bugs.webkit.org/show_bug.cgi?id=66994 5 6 Reviewed by Gavin Barraclough. 7 8 Add testcases for producing a stack trace on exception objects. 9 10 * fast/js/exception-properties-expected.txt: 11 * fast/js/script-tests/exception-properties.js: 12 * fast/js/script-tests/stack-trace.js: Added. 13 (printStack): 14 (hostThrower): 15 (callbacker): 16 (outer): 17 (inner): 18 (evaler): 19 (normalOuter): 20 (normalInner): 21 (scripterInner): 22 (scripterOuter): 23 (selfRecursive1): 24 (selfRecursive2): 25 (selfRecursive3): 26 (throwError): 27 (object.get getter1.o.valueOf): 28 (object.get getter1): 29 (object.get getter2): 30 (object.get getter3.o2.valueOf): 31 (object.get getter3): 32 (object.nonInlineable.callCount): 33 (object.nonInlineable): 34 (object.inlineable): 35 (yetAnotherInlinedCall): 36 (makeInlinableCall): 37 (.try.g): 38 (h): 39 (mapTest): 40 (mapTestDriver): 41 (dfgFunction): 42 (try.f): 43 * fast/js/stack-trace-expected.txt: Added. 44 * fast/js/stack-trace.html: Added. 45 1 46 2012-02-16 James Robinson <jamesr@chromium.org> 2 47 -
trunk/LayoutTests/fast/js/exception-properties-expected.txt
r106454 r107980 5 5 6 6 PASS enumerableProperties(error) is [] 7 PASS enumerableProperties(nativeError) is ["line", "sourceURL" ]7 PASS enumerableProperties(nativeError) is ["line", "sourceURL", "stack"] 8 8 PASS Object.getPrototypeOf(nativeError).name is "RangeError" 9 9 PASS Object.getPrototypeOf(nativeError).message is "" -
trunk/LayoutTests/fast/js/script-tests/exception-properties.js
r106454 r107980 17 17 18 18 shouldBe('enumerableProperties(error)', '[]'); 19 shouldBe('enumerableProperties(nativeError)', '["line", "sourceURL" ]');19 shouldBe('enumerableProperties(nativeError)', '["line", "sourceURL", "stack"]'); 20 20 21 21 shouldBe('Object.getPrototypeOf(nativeError).name', '"RangeError"'); -
trunk/Source/JavaScriptCore/ChangeLog
r107957 r107980 1 2012-02-16 Oliver Hunt <oliver@apple.com> 2 3 Implement Error.stack 4 https://bugs.webkit.org/show_bug.cgi?id=66994 5 6 Reviewed by Gavin Barraclough. 7 8 Implement support for stack traces on exception objects. This is a rewrite 9 of the core portion of the last stack walking logic, but the mechanical work 10 of adding the information to an exception comes from the original work by 11 Juan Carlos Montemayor Elosua. 12 13 * interpreter/Interpreter.cpp: 14 (JSC::getCallerInfo): 15 (JSC): 16 (JSC::getSourceURLFromCallFrame): 17 (JSC::getStackFrameCodeType): 18 (JSC::Interpreter::getStackTrace): 19 (JSC::Interpreter::throwException): 20 (JSC::Interpreter::privateExecute): 21 * interpreter/Interpreter.h: 22 (JSC): 23 (StackFrame): 24 (JSC::StackFrame::toString): 25 (Interpreter): 26 * jsc.cpp: 27 (GlobalObject::finishCreation): 28 (functionJSCStack): 29 * parser/Nodes.h: 30 (JSC::FunctionBodyNode::setInferredName): 31 * parser/Parser.h: 32 (JSC::::parse): 33 * runtime/CommonIdentifiers.h: 34 * runtime/Error.cpp: 35 (JSC::addErrorInfo): 36 * runtime/Error.h: 37 (JSC): 38 1 39 2012-02-15 Gavin Barraclough <barraclough@apple.com> 2 40 -
trunk/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
r107651 r107980 209 209 ?getPropertyNames@JSObject@JSC@@SAXPAV12@PAVExecState@2@AAVPropertyNameArray@2@W4EnumerationMode@2@@Z 210 210 ?getSlice@ArgList@JSC@@QBEXHAAV12@@Z 211 ?getStackTrace@Interpreter@JSC@@SAXPAVJSGlobalData@2@HAAV?$Vector@UStackFrame@JSC@@$0A@@WTF@@@Z 211 212 ?getString@JSCell@JSC@@QBE?AVUString@2@PAVExecState@2@@Z 212 213 ?getString@JSCell@JSC@@QBE_NPAVExecState@2@AAVUString@2@@Z -
trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp
r107499 r107980 46 46 #include "JSArray.h" 47 47 #include "JSByteArray.h" 48 #include "JSFunction.h"49 48 #include "JSNotAnObject.h" 50 49 #include "JSPropertyNameIterator.h" … … 61 60 #include "SamplingTool.h" 62 61 #include "StrictEvalActivation.h" 62 #include "StrongInlines.h" 63 63 #include "UStringConcatenate.h" 64 64 #include <limits.h> … … 791 791 } 792 792 793 static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber) 794 { 795 UNUSED_PARAM(globalData); 796 unsigned bytecodeOffset = 0; 797 lineNumber = -1; 798 ASSERT(!callFrame->hasHostCallFrameFlag()); 799 CallFrame* callerFrame = callFrame->codeBlock() ? callFrame->trueCallerFrame() : 0; 800 bool callframeIsHost = callerFrame->addHostCallFrameFlag() == callFrame->callerFrame(); 801 ASSERT(!callerFrame->hasHostCallFrameFlag()); 802 803 if (callerFrame == CallFrame::noCaller() || !callerFrame || !callerFrame->codeBlock()) 804 return callerFrame; 805 806 CodeBlock* callerCodeBlock = callerFrame->codeBlock(); 807 808 if (callframeIsHost) { 809 // Don't need to deal with inline callframes here as by definition we haven't 810 // inlined a call with an intervening native call frame. 811 #if ENABLE(INTERPRETER) 812 if (!globalData->canUseJIT()) { 813 bytecodeOffset = callerFrame->bytecodeOffsetForNonDFGCode(); 814 lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); 815 return callerFrame; 816 } 817 #endif 818 #if ENABLE(JIT) 819 #if ENABLE(DFG_JIT) 820 if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) 821 bytecodeOffset = callerCodeBlock->codeOrigin(callerFrame->codeOriginIndexForDFG()).bytecodeIndex; 822 else 823 #endif 824 bytecodeOffset = callerFrame->bytecodeOffsetForNonDFGCode(); 825 #endif 826 } else { 827 #if ENABLE(INTERPRETER) 828 if (!globalData->canUseJIT()) { 829 bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC()); 830 lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); 831 return callerFrame; 832 } 833 #endif 834 #if ENABLE(JIT) 835 #if ENABLE(DFG_JIT) 836 if (callFrame->isInlineCallFrame()) { 837 InlineCallFrame* icf = callFrame->inlineCallFrame(); 838 bytecodeOffset = icf->caller.bytecodeIndex; 839 if (InlineCallFrame* parentCallFrame = icf->caller.inlineCallFrame) { 840 FunctionExecutable* executable = static_cast<FunctionExecutable*>(parentCallFrame->executable.get()); 841 CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(parentCallFrame->isCall ? CodeForCall : CodeForConstruct); 842 ASSERT(newCodeBlock); 843 ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); 844 callerCodeBlock = newCodeBlock; 845 } 846 } else if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) { 847 CodeOrigin origin; 848 if (!callerCodeBlock->codeOriginForReturn(callFrame->returnPC(), origin)) 849 ASSERT_NOT_REACHED(); 850 bytecodeOffset = origin.bytecodeIndex; 851 if (InlineCallFrame* icf = origin.inlineCallFrame) { 852 FunctionExecutable* executable = static_cast<FunctionExecutable*>(icf->executable.get()); 853 CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(icf->isCall ? CodeForCall : CodeForConstruct); 854 ASSERT(newCodeBlock); 855 ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); 856 callerCodeBlock = newCodeBlock; 857 } 858 } else 859 #endif 860 bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); 861 #endif 862 } 863 864 lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset); 865 return callerFrame; 866 } 867 868 static ALWAYS_INLINE const UString getSourceURLFromCallFrame(CallFrame* callFrame) 869 { 870 ASSERT(!callFrame->hasHostCallFrameFlag()); 871 #if ENABLE(INTERPRETER) 872 #if ENABLE(JIT) 873 if (callFrame->globalData().canUseJIT()) 874 return callFrame->codeBlock()->ownerExecutable()->sourceURL(); 875 #endif 876 return callFrame->codeBlock()->source()->url(); 877 878 #else 879 return callFrame->codeBlock()->ownerExecutable()->sourceURL(); 880 #endif 881 } 882 883 static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame) 884 { 885 ASSERT(!callFrame->hasHostCallFrameFlag()); 886 887 switch (callFrame->codeBlock()->codeType()) { 888 case EvalCode: 889 return StackFrameEvalCode; 890 case FunctionCode: 891 return StackFrameFunctionCode; 892 case GlobalCode: 893 return StackFrameGlobalCode; 894 } 895 ASSERT_NOT_REACHED(); 896 return StackFrameGlobalCode; 897 } 898 899 void Interpreter::getStackTrace(JSGlobalData* globalData, int line, Vector<StackFrame>& results) 900 { 901 CallFrame* callFrame = globalData->topCallFrame->removeHostCallFrameFlag()->trueCallFrameFromVMCode(); 902 if (!callFrame || callFrame == CallFrame::noCaller()) 903 return; 904 905 while (callFrame && callFrame != CallFrame::noCaller()) { 906 UString sourceURL; 907 if (callFrame->codeBlock()) { 908 sourceURL = getSourceURLFromCallFrame(callFrame); 909 StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), getStackFrameCodeType(callFrame), Strong<ExecutableBase>(*globalData, callFrame->codeBlock()->ownerExecutable()), line, sourceURL}; 910 results.append(s); 911 } else { 912 StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), -1, UString()}; 913 results.append(s); 914 } 915 callFrame = getCallerInfo(globalData, callFrame, line); 916 } 917 } 918 793 919 NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) 794 920 { … … 809 935 // FIXME: should only really be adding these properties to VM generated exceptions, 810 936 // but the inspector currently requires these for all thrown objects. 811 addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); 937 Vector<StackFrame> stackTrace; 938 getStackTrace(&callFrame->globalData(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), stackTrace); 939 addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source(), stackTrace); 812 940 } 813 941 … … 1744 1872 #endif 1745 1873 1874 #define UPDATE_BYTECODE_OFFSET() \ 1875 do {\ 1876 callFrame->setBytecodeOffsetForNonDFGCode(vPC - codeBlock->instructions().data() + 1);\ 1877 } while (0) 1878 1746 1879 #if ENABLE(COMPUTED_GOTO_INTERPRETER) 1747 1880 #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto *vPC->u.opcode 1748 1881 #if ENABLE(OPCODE_STATS) 1749 #define DEFINE_OPCODE(opcode) opcode: OpcodeStats::recordInstruction(opcode); 1882 #define DEFINE_OPCODE(opcode) \ 1883 opcode:\ 1884 OpcodeStats::recordInstruction(opcode);\ 1885 UPDATE_BYTECODE_OFFSET(); 1750 1886 #else 1751 #define DEFINE_OPCODE(opcode) opcode: 1887 #define DEFINE_OPCODE(opcode) opcode: UPDATE_BYTECODE_OFFSET(); 1752 1888 #endif 1753 1889 NEXT_INSTRUCTION(); … … 1755 1891 #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto interpreterLoopStart 1756 1892 #if ENABLE(OPCODE_STATS) 1757 #define DEFINE_OPCODE(opcode) case opcode: OpcodeStats::recordInstruction(opcode); 1893 #define DEFINE_OPCODE(opcode) \ 1894 case opcode:\ 1895 OpcodeStats::recordInstruction(opcode);\ 1896 UPDATE_BYTECODE_OFFSET(); 1758 1897 #else 1759 #define DEFINE_OPCODE(opcode) case opcode: 1898 #define DEFINE_OPCODE(opcode) case opcode: UPDATE_BYTECODE_OFFSET(); 1760 1899 #endif 1761 1900 while (1) { // iterator loop begins -
trunk/Source/JavaScriptCore/interpreter/Interpreter.h
r107493 r107980 32 32 #include "ArgList.h" 33 33 #include "JSCell.h" 34 #include "JSFunction.h" 34 35 #include "JSValue.h" 35 36 #include "JSObject.h" … … 43 44 class CodeBlock; 44 45 class EvalExecutable; 46 class ExecutableBase; 45 47 class FunctionExecutable; 46 class JSFunction;47 48 class JSGlobalObject; 48 49 class ProgramExecutable; … … 63 64 }; 64 65 66 enum StackFrameCodeType { 67 StackFrameGlobalCode, 68 StackFrameEvalCode, 69 StackFrameFunctionCode, 70 StackFrameNativeCode 71 }; 72 73 struct StackFrame { 74 Strong<JSObject> callee; 75 StackFrameCodeType codeType; 76 Strong<ExecutableBase> executable; 77 int line; 78 UString sourceURL; 79 UString toString(CallFrame* callFrame) const 80 { 81 bool hasSourceURLInfo = !sourceURL.isNull() && !sourceURL.isEmpty(); 82 bool hasLineInfo = line > -1; 83 String traceLine; 84 JSObject* stackFrameCallee = callee.get(); 85 86 switch (codeType) { 87 case StackFrameEvalCode: 88 if (hasSourceURLInfo) { 89 traceLine = hasLineInfo ? String::format("eval code@%s:%d", sourceURL.ascii().data(), line) 90 : String::format("eval code@%s", sourceURL.ascii().data()); 91 } else 92 traceLine = String::format("eval code"); 93 break; 94 case StackFrameNativeCode: { 95 if (callee) { 96 UString functionName = getCalculatedDisplayName(callFrame, stackFrameCallee); 97 traceLine = String::format("%s@[native code]", functionName.ascii().data()); 98 } else 99 traceLine = "[native code]"; 100 break; 101 } 102 case StackFrameFunctionCode: { 103 UString functionName = getCalculatedDisplayName(callFrame, stackFrameCallee); 104 if (hasSourceURLInfo) { 105 traceLine = hasLineInfo ? String::format("%s@%s:%d", functionName.ascii().data(), sourceURL.ascii().data(), line) 106 : String::format("%s@%s", functionName.ascii().data(), sourceURL.ascii().data()); 107 } else 108 traceLine = String::format("%s\n", functionName.ascii().data()); 109 break; 110 } 111 case StackFrameGlobalCode: 112 if (hasSourceURLInfo) { 113 traceLine = hasLineInfo ? String::format("global code@%s:%d", sourceURL.ascii().data(), line) 114 : String::format("global code@%s", sourceURL.ascii().data()); 115 } else 116 traceLine = String::format("global code"); 117 118 } 119 return traceLine.impl(); 120 } 121 }; 122 65 123 class TopCallFrameSetter { 66 124 public: … … 152 210 NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset); 153 211 NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine); 212 static const UString getTraceLine(CallFrame*, StackFrameCodeType, const UString&, int); 213 JS_EXPORT_PRIVATE static void getStackTrace(JSGlobalData*, int line, Vector<StackFrame>& results); 154 214 155 215 void dumpSampleData(ExecState* exec); -
trunk/Source/JavaScriptCore/jsc.cpp
r106454 r107980 28 28 #include "ExceptionHelpers.h" 29 29 #include "InitializeThreading.h" 30 #include "Interpreter.h" 30 31 #include "JSArray.h" 31 32 #include "JSFunction.h" … … 79 80 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*); 80 81 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*); 82 static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*); 81 83 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*); 82 84 #ifndef NDEBUG … … 185 187 addFunction(globalData, "load", functionLoad, 1); 186 188 addFunction(globalData, "checkSyntax", functionCheckSyntax, 1); 189 addFunction(globalData, "jscStack", functionJSCStack, 1); 187 190 addFunction(globalData, "readline", functionReadline, 0); 188 191 addFunction(globalData, "preciseTime", functionPreciseTime, 0); … … 250 253 { 251 254 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data()); 255 return JSValue::encode(jsUndefined()); 256 } 257 258 EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec) 259 { 260 String trace = "--> Stack trace:\n"; 261 Vector<StackFrame> stackTrace; 262 Interpreter::getStackTrace(&exec->globalData(), -1, stackTrace); 263 int i = 0; 264 265 for (Vector<StackFrame>::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { 266 StackFrame level = *iter; 267 trace += String::format(" %i %s\n", i, level.toString(exec).utf8().data()); 268 i++; 269 } 270 fprintf(stderr, "%s", trace.utf8().data()); 252 271 return JSValue::encode(jsUndefined()); 253 272 } -
trunk/Source/JavaScriptCore/parser/Nodes.h
r106512 r107980 1494 1494 1495 1495 const Identifier& ident() { return m_ident; } 1496 void setInferredName(const Identifier& inferredName) { m_inferredName = inferredName; }1496 void setInferredName(const Identifier& inferredName) { ASSERT(!inferredName.isNull()); m_inferredName = inferredName; } 1497 1497 const Identifier& inferredName() { return m_inferredName.isEmpty() ? m_ident : m_inferredName; } 1498 1498 -
trunk/Source/JavaScriptCore/parser/Parser.h
r107338 r107980 1026 1026 *exception = createSyntaxError(lexicalGlobalObject, errMsg); 1027 1027 else 1028 *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source );1028 *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source, Vector<StackFrame>()); 1029 1029 } 1030 1030 -
trunk/Source/JavaScriptCore/runtime/CommonIdentifiers.h
r106454 r107980 63 63 macro(set) \ 64 64 macro(source) \ 65 macro(stack) \ 65 66 macro(test) \ 66 67 macro(toExponential) \ -
trunk/Source/JavaScriptCore/runtime/Error.cpp
r106454 r107980 27 27 #include "ConstructData.h" 28 28 #include "ErrorConstructor.h" 29 #include "ExceptionHelpers.h" 29 30 #include "FunctionPrototype.h" 31 #include "JSArray.h" 30 32 #include "JSFunction.h" 31 33 #include "JSGlobalObject.h" … … 117 119 } 118 120 119 JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source )121 JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace) 120 122 { 121 123 const UString& sourceURL = source.provider()->url(); … … 125 127 if (!sourceURL.isNull()) 126 128 error->putDirect(*globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete); 129 if (!stackTrace.isEmpty()) { 130 JSGlobalObject* globalObject = 0; 131 if (isTerminatedExecutionException(error) || isInterruptedExecutionException(error)) 132 globalObject = globalData->dynamicGlobalObject; 133 else 134 globalObject = error->globalObject(); 135 // We use the tryCreateUninitialized creation mechanism and related initialization 136 // functions as they're the only mechanism we currently have that will guarantee we 137 // don't call setters on the prototype. Technically it's faster than the alternative, 138 // but the numerous allocations that take place in this loop makes that last bit 139 // somewhat moot. 140 JSArray* stackTraceArray = JSArray::tryCreateUninitialized(*globalData, globalObject->arrayStructure(), stackTrace.size()); 141 if (!stackTraceArray) 142 return error; 143 for (unsigned i = 0; i < stackTrace.size(); i++) { 144 UString stackLevel = stackTrace[i].toString(globalObject->globalExec()); 145 stackTraceArray->initializeIndex(*globalData, i, jsString(globalData, stackLevel)); 146 } 147 stackTraceArray->completeInitialization(stackTrace.size()); 148 error->putDirect(*globalData, globalData->propertyNames->stack, stackTraceArray, ReadOnly | DontDelete); 149 } 127 150 128 151 return error; 129 152 } 130 153 131 JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source )154 JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace) 132 155 { 133 return addErrorInfo(&exec->globalData(), error, line, source );156 return addErrorInfo(&exec->globalData(), error, line, source, stackTrace); 134 157 } 135 158 -
trunk/Source/JavaScriptCore/runtime/Error.h
r106454 r107980 25 25 26 26 #include "InternalFunction.h" 27 #include "Interpreter.h" 27 28 #include "JSObject.h" 28 29 #include <stdint.h> … … 57 58 // Methods to add 58 59 bool hasErrorInfo(ExecState*, JSObject* error); 59 JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode& );60 JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&); 60 61 // ExecState wrappers. 61 JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode& );62 JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&); 62 63 63 64 // Methods to throw Errors.
Note: See TracChangeset
for help on using the changeset viewer.