Changeset 147846 in webkit
- Timestamp:
- Apr 6, 2013 11:24:37 AM (11 years ago)
- Location:
- trunk
- Files:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSContextRef.cpp
r147818 r147846 37 37 #include "JSObject.h" 38 38 #include "Operations.h" 39 #include "SourceProvider.h"40 39 #include <wtf/text/StringBuilder.h> 41 40 #include <wtf/text/StringHash.h> … … 177 176 ExecState* exec = toJS(ctx); 178 177 JSLockHolder lock(exec); 178 179 unsigned count = 0; 179 180 StringBuilder builder; 180 Vector<StackFrame> stackTrace; 181 Interpreter::getStackTrace(&exec->globalData(), stackTrace, maxStackSize); 182 183 for (size_t i = 0; i < stackTrace.size(); i++) { 181 CallFrame* callFrame = exec; 182 String functionName; 183 if (exec->callee()) { 184 if (asObject(exec->callee())->inherits(&InternalFunction::s_info)) { 185 functionName = asInternalFunction(exec->callee())->name(exec); 186 builder.appendLiteral("#0 "); 187 builder.append(functionName); 188 builder.appendLiteral("() "); 189 count++; 190 } 191 } 192 while (true) { 193 RELEASE_ASSERT(callFrame); 194 int signedLineNumber; 195 intptr_t sourceID; 184 196 String urlString; 185 String functionName; 186 StackFrame& frame = stackTrace[i]; 187 JSValue function = frame.callee.get(); 188 if (frame.callee) 189 functionName = frame.friendlyFunctionName(exec); 197 JSValue function; 198 199 exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function); 200 201 if (function) 202 functionName = jsCast<JSFunction*>(function)->name(exec); 190 203 else { 191 204 // Caller is unknown, but if frame is empty we should still add the frame, because 192 205 // something called us, and gave us arguments. 193 if ( i)206 if (count) 194 207 break; 195 208 } 196 unsigned lineNumber = frame.line();209 unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; 197 210 if (!builder.isEmpty()) 198 211 builder.append('\n'); 199 212 builder.append('#'); 200 builder.appendNumber( i);213 builder.appendNumber(count); 201 214 builder.append(' '); 202 215 builder.append(functionName); 203 216 builder.appendLiteral("() at "); 204 217 builder.append(urlString); 205 if (frame.codeType != StackFrameNativeCode) { 206 builder.append(':'); 207 builder.appendNumber(lineNumber); 208 } 209 if (!function) 218 builder.append(':'); 219 builder.appendNumber(lineNumber); 220 if (!function || ++count == maxStackSize) 210 221 break; 222 callFrame = callFrame->callerFrame(); 211 223 } 212 224 return OpaqueJSString::create(builder.toString()).leakRef(); -
trunk/Source/JavaScriptCore/ChangeLog
r147842 r147846 1 2013-04-06 Geoffrey Garen <ggaren@apple.com> 2 3 Rolled out 147820 and 147818 because they caused plugins tests to ASSERT 4 https://bugs.webkit.org/show_bug.cgi?id=114094 5 6 Reviewed by Anders Carlsson. 7 8 * API/JSContextRef.cpp: 9 (JSContextCreateBacktrace): 10 * bytecompiler/BytecodeGenerator.cpp: 11 (JSC::BytecodeGenerator::emitDebugHook): 12 * interpreter/Interpreter.cpp: 13 (JSC): 14 (JSC::Interpreter::dumpRegisters): 15 (JSC::Interpreter::unwindCallFrame): 16 (JSC::getLineNumberForCallFrame): 17 (JSC::getCallerInfo): 18 (JSC::Interpreter::getStackTrace): 19 (JSC::Interpreter::addStackTraceIfNecessary): 20 (JSC::Interpreter::retrieveCallerFromVMCode): 21 * interpreter/Interpreter.h: 22 (StackFrame): 23 (JSC::StackFrame::toString): 24 (JSC::StackFrame::friendlyLineNumber): 25 (Interpreter): 26 * runtime/Error.cpp: 27 (JSC::throwError): 28 * runtime/JSGlobalData.h: 29 (JSC): 30 (JSGlobalData): 31 * runtime/JSGlobalObject.cpp: 32 (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): 33 1 34 2013-04-06 Patrick Gansterer <paroga@webkit.org> 2 35 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r147818 r147846 2056 2056 return; 2057 2057 #endif 2058 emitExpressionInfo(charPosition, 0, 0);2059 2058 emitOpcode(op_debug); 2060 2059 instructions().append(debugHookID); -
trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp
r147820 r147846 200 200 201 201 202 static CallFrame* getCallerInfo(JSGlobalData*, CallFrame*, unsigned& bytecodeOffset, CodeBlock*& callerOut);202 static CallFrame* getCallerInfo(JSGlobalData*, CallFrame*, int& lineNumber, unsigned& bytecodeOffset, CodeBlock*& callerOut); 203 203 204 204 // Returns the depth of the scope chain within a given call frame. … … 423 423 unsigned bytecodeOffset = 0; 424 424 int line = 0; 425 CodeBlock* callerCodeBlock = 0; 426 getCallerInfo(&callFrame->globalData(), callFrame, bytecodeOffset, callerCodeBlock); 427 line = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset); 425 CodeBlock* unusedCallerCodeBlock = 0; 426 getCallerInfo(&callFrame->globalData(), callFrame, line, bytecodeOffset, unusedCallerCodeBlock); 428 427 dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line); 429 428 ++it; … … 509 508 if (callerFrame->hasHostCallFrameFlag()) 510 509 return false; 511 callFrame = getCallerInfo(&callFrame->globalData(), callFrame, bytecodeOffset, codeBlock); 510 int unusedLineNumber = 0; 511 callFrame = getCallerInfo(&callFrame->globalData(), callFrame, unusedLineNumber, bytecodeOffset, codeBlock); 512 512 return true; 513 513 } … … 565 565 } 566 566 567 static unsigned getBytecodeOffsetForCallFrame(CallFrame* callFrame) 568 { 567 static int getLineNumberForCallFrame(JSGlobalData* globalData, CallFrame* callFrame) 568 { 569 UNUSED_PARAM(globalData); 569 570 callFrame = callFrame->removeHostCallFrameFlag(); 570 571 CodeBlock* codeBlock = callFrame->codeBlock(); 571 572 if (!codeBlock) 572 return 0;573 #if ENABLE(JIT) 573 return -1; 574 #if ENABLE(JIT) || ENABLE(LLINT) 574 575 #if ENABLE(DFG_JIT) 575 576 if (codeBlock->getJITType() == JITCode::DFGJIT) 576 return codeBlock-> codeOrigin(callFrame->codeOriginIndexForDFG()).bytecodeIndex;577 #endif 578 return c allFrame->bytecodeOffsetForNonDFGCode();579 #endif 580 } 581 582 static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, unsigned& bytecodeOffset, CodeBlock*& caller)577 return codeBlock->lineNumberForBytecodeOffset(codeBlock->codeOrigin(callFrame->codeOriginIndexForDFG()).bytecodeIndex); 578 #endif 579 return codeBlock->lineNumberForBytecodeOffset(callFrame->bytecodeOffsetForNonDFGCode()); 580 #endif 581 } 582 583 static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber, unsigned& bytecodeOffset, CodeBlock*& caller) 583 584 { 584 585 ASSERT_UNUSED(globalData, globalData); 585 586 bytecodeOffset = 0; 587 lineNumber = -1; 586 588 ASSERT(!callFrame->hasHostCallFrameFlag()); 587 589 CallFrame* callerFrame = callFrame->codeBlock() ? callFrame->trueCallerFrame() : callFrame->callerFrame()->removeHostCallFrameFlag(); … … 653 655 RELEASE_ASSERT(callerCodeBlock); 654 656 caller = callerCodeBlock; 657 lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset); 655 658 return callerFrame; 656 659 } … … 678 681 } 679 682 680 unsigned StackFrame::line() 681 { 682 return codeBlock ? codeBlock->lineNumberForBytecodeOffset(bytecodeOffset) + lineOffset : 0; 683 } 684 685 unsigned StackFrame::column() 686 { 687 if (!code) 688 return 0; 689 int divot = 0; 690 int unusedStartOffset = 0; 691 int unusedEndOffset = 0; 692 expressionInfo(divot, unusedStartOffset, unusedEndOffset); 693 return code->charPositionToColumnNumber(divot); 694 } 695 696 void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset) 697 { 698 codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset); 699 divot += startOffset; 700 } 701 702 String StackFrame::toString(CallFrame* callFrame) 703 { 704 StringBuilder traceBuild; 705 String functionName = friendlyFunctionName(callFrame); 706 String sourceURL = friendlySourceURL(); 707 traceBuild.append(functionName); 708 if (!sourceURL.isEmpty()) { 709 if (!functionName.isEmpty()) 710 traceBuild.append('@'); 711 traceBuild.append(sourceURL); 712 if (codeType != StackFrameNativeCode) { 713 traceBuild.append(':'); 714 traceBuild.appendNumber(line()); 715 } 716 } 717 return traceBuild.toString().impl(); 718 } 719 720 void Interpreter::getStackTrace(JSGlobalData* globalData, Vector<StackFrame>& results, size_t maxStackSize) 683 void Interpreter::getStackTrace(JSGlobalData* globalData, Vector<StackFrame>& results) 721 684 { 722 685 CallFrame* callFrame = globalData->topCallFrame->removeHostCallFrameFlag(); 723 686 if (!callFrame || callFrame == CallFrame::noCaller()) 724 687 return; 725 unsigned bytecodeOffset = getBytecodeOffsetForCallFrame(callFrame); 688 int line = getLineNumberForCallFrame(globalData, callFrame); 689 726 690 callFrame = callFrame->trueCallFrameFromVMCode(); 727 691 if (!callFrame) 728 692 return; 729 CodeBlock* callerCodeBlock = callFrame->codeBlock(); 730 731 while (callFrame && callFrame != CallFrame::noCaller() && maxStackSize--) { 693 694 while (callFrame && callFrame != CallFrame::noCaller()) { 732 695 String sourceURL; 733 if (call erCodeBlock) {696 if (callFrame->codeBlock()) { 734 697 sourceURL = getSourceURLFromCallFrame(callFrame); 735 StackFrame s = { 736 Strong<JSObject>(*globalData, callFrame->callee()), 737 getStackFrameCodeType(callFrame), 738 Strong<ExecutableBase>(*globalData, callerCodeBlock->ownerExecutable()), 739 Strong<UnlinkedCodeBlock>(*globalData, callerCodeBlock->unlinkedCodeBlock()), 740 callerCodeBlock->source(), 741 callerCodeBlock->ownerExecutable()->lineNo(), 742 callerCodeBlock->sourceOffset(), 743 bytecodeOffset, 744 sourceURL 745 }; 698 StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), getStackFrameCodeType(callFrame), Strong<ExecutableBase>(*globalData, callFrame->codeBlock()->ownerExecutable()), line, sourceURL}; 746 699 results.append(s); 747 700 } else { 748 StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, String()};701 StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), -1, String()}; 749 702 results.append(s); 750 703 } 751 callFrame = getCallerInfo(globalData, callFrame, bytecodeOffset, callerCodeBlock); 752 } 753 } 754 755 void Interpreter::addStackTraceIfNecessary(CallFrame* callFrame, JSValue error) 704 unsigned unusedBytecodeOffset = 0; 705 CodeBlock* unusedCallerCodeBlock = 0; 706 callFrame = getCallerInfo(globalData, callFrame, line, unusedBytecodeOffset, unusedCallerCodeBlock); 707 } 708 } 709 710 void Interpreter::addStackTraceIfNecessary(CallFrame* callFrame, JSObject* error) 756 711 { 757 712 JSGlobalData* globalData = &callFrame->globalData(); 758 713 ASSERT(callFrame == globalData->topCallFrame || callFrame == callFrame->lexicalGlobalObject()->globalExec() || callFrame == callFrame->dynamicGlobalObject()->globalExec()); 714 if (error->hasProperty(callFrame, globalData->propertyNames->stack)) 715 return; 759 716 760 717 Vector<StackFrame> stackTrace; 761 718 getStackTrace(&callFrame->globalData(), stackTrace); 762 719 763 if (stackTrace.isEmpty() || !error.isObject())720 if (stackTrace.isEmpty()) 764 721 return; 765 JSObject* errorObject = asObject(error);722 766 723 JSGlobalObject* globalObject = 0; 767 724 if (isTerminatedExecutionException(error) || isInterruptedExecutionException(error)) 768 725 globalObject = globalData->dynamicGlobalObject; 769 726 else 770 globalObject = error Object->globalObject();727 globalObject = error->globalObject(); 771 728 772 729 // FIXME: JSStringJoiner could be more efficient than StringBuilder here. … … 777 734 builder.append('\n'); 778 735 } 779 780 if (errorObject->hasProperty(callFrame, globalData->propertyNames->stack)) 781 return; 782 errorObject->putDirect(*globalData, globalData->propertyNames->stack, jsString(globalData, builder.toString()), ReadOnly | DontDelete); 736 737 error->putDirect(*globalData, globalData->propertyNames->stack, jsString(globalData, builder.toString()), ReadOnly | DontDelete); 783 738 } 784 739 … … 1423 1378 return jsNull(); 1424 1379 1380 int lineNumber; 1425 1381 unsigned bytecodeOffset; 1426 1382 CodeBlock* unusedCallerCodeBlock = 0; 1427 CallFrame* callerFrame = getCallerInfo(&callFrame->globalData(), functionCallFrame, bytecodeOffset, unusedCallerCodeBlock);1383 CallFrame* callerFrame = getCallerInfo(&callFrame->globalData(), functionCallFrame, lineNumber, bytecodeOffset, unusedCallerCodeBlock); 1428 1384 if (!callerFrame) 1429 1385 return jsNull(); … … 1435 1391 ASSERT(caller.isObject()); 1436 1392 while (asObject(caller)->inherits(&JSBoundFunction::s_info)) { 1437 callerFrame = getCallerInfo(&callFrame->globalData(), callerFrame, bytecodeOffset, unusedCallerCodeBlock);1393 callerFrame = getCallerInfo(&callFrame->globalData(), callerFrame, lineNumber, bytecodeOffset, unusedCallerCodeBlock); 1438 1394 if (!callerFrame) 1439 1395 return jsNull(); -
trunk/Source/JavaScriptCore/interpreter/Interpreter.h
r147818 r147846 80 80 StackFrameCodeType codeType; 81 81 Strong<ExecutableBase> executable; 82 Strong<UnlinkedCodeBlock> codeBlock; 83 RefPtr<SourceProvider> code; 84 int lineOffset; 85 unsigned characterOffset; 86 unsigned bytecodeOffset; 82 int line; 87 83 String sourceURL; 88 JS_EXPORT_PRIVATE String toString(CallFrame*); 84 String toString(CallFrame* callFrame) const 85 { 86 StringBuilder traceBuild; 87 String functionName = friendlyFunctionName(callFrame); 88 String sourceURL = friendlySourceURL(); 89 traceBuild.append(functionName); 90 if (!sourceURL.isEmpty()) { 91 if (!functionName.isEmpty()) 92 traceBuild.append('@'); 93 traceBuild.append(sourceURL); 94 if (line > -1) { 95 traceBuild.append(':'); 96 traceBuild.appendNumber(line); 97 } 98 } 99 return traceBuild.toString().impl(); 100 } 89 101 String friendlySourceURL() const 90 102 { … … 126 138 return traceLine.isNull() ? emptyString() : traceLine; 127 139 } 128 JS_EXPORT_PRIVATE unsigned line(); 129 JS_EXPORT_PRIVATE unsigned column(); 130 JS_EXPORT_PRIVATE void expressionInfo(int& divot, int& startOffset, int& endOffset); 140 unsigned friendlyLineNumber() const 141 { 142 return line > -1 ? line : 0; 143 } 131 144 }; 132 145 … … 220 233 NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine, int column); 221 234 static const String getTraceLine(CallFrame*, StackFrameCodeType, const String&, int); 222 JS_EXPORT_PRIVATE static void getStackTrace(JSGlobalData*, Vector<StackFrame>& results , size_t maxStackSize = std::numeric_limits<size_t>::max());223 static void addStackTraceIfNecessary(CallFrame*, JS Valueerror);235 JS_EXPORT_PRIVATE static void getStackTrace(JSGlobalData*, Vector<StackFrame>& results); 236 static void addStackTraceIfNecessary(CallFrame*, JSObject* error); 224 237 225 238 void dumpSampleData(ExecState* exec); -
trunk/Source/JavaScriptCore/runtime/Error.cpp
r147818 r147846 156 156 JSValue throwError(ExecState* exec, JSValue error) 157 157 { 158 Interpreter::addStackTraceIfNecessary(exec, error); 158 if (error.isObject()) 159 return throwError(exec, asObject(error)); 159 160 exec->globalData().exception = error; 160 161 return error; -
trunk/Source/JavaScriptCore/runtime/JSGlobalData.h
r147818 r147846 55 55 #include <wtf/Forward.h> 56 56 #include <wtf/HashMap.h> 57 #include <wtf/RefCountedArray.h>58 57 #include <wtf/SimpleStats.h> 59 58 #include <wtf/ThreadSafeRefCounted.h> … … 83 82 class SourceProvider; 84 83 class SourceProviderCache; 85 struct StackFrame;86 84 class Stringifier; 87 85 class Structure; … … 331 329 332 330 JSValue exception; 333 RefCountedArray<StackFrame> exceptionStack;334 331 335 332 const ClassInfo* const jsArrayClassInfo; -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r147818 r147846 598 598 globalData.resetDateCache(); 599 599 } 600 // Clear the exception stack between entries601 globalData.exceptionStack = RefCountedArray<StackFrame>();602 600 } 603 601 -
trunk/Source/WebCore/ChangeLog
r147843 r147846 1 2013-04-06 Geoffrey Garen <ggaren@apple.com> 2 3 Rolled out 147820 and 147818 because they caused plugins tests to ASSERT 4 https://bugs.webkit.org/show_bug.cgi?id=114094 5 6 Reviewed by Anders Carlsson. 7 8 * bindings/js/ScriptCallStackFactory.cpp: 9 (WebCore::createScriptCallStack): 10 * inspector/ScriptCallFrame.cpp: 11 (WebCore::ScriptCallFrame::isEqual): 12 * inspector/ScriptCallFrame.h: 13 (ScriptCallFrame): 14 (WebCore::ScriptCallFrame::lineNumber): 15 1 16 2013-04-06 Anders Carlsson <andersca@apple.com> 2 17 -
trunk/Source/WebCore/bindings/js/ScriptCallStackFactory.cpp
r147818 r147846 59 59 if (JSC::ExecState* exec = JSMainThreadExecState::currentState()) { 60 60 Vector<StackFrame> stackTrace; 61 Interpreter::getStackTrace(&exec->globalData(), stackTrace, maxStackSize); 62 for (size_t i = 0; i < stackTrace.size(); i++) 63 frames.append(ScriptCallFrame(stackTrace[i].friendlyFunctionName(exec), stackTrace[i].friendlySourceURL(), stackTrace[i].line(), stackTrace[i].column())); 61 Interpreter::getStackTrace(&exec->globalData(), stackTrace); 62 for (Vector<StackFrame>::const_iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { 63 frames.append(ScriptCallFrame(iter->friendlyFunctionName(exec), iter->friendlySourceURL(), iter->friendlyLineNumber())); 64 if (frames.size() >= maxStackSize) 65 break; 66 } 64 67 } 65 68 if (frames.isEmpty() && !emptyIsAllowed) { … … 67 70 // a bound function is called from native code for example. 68 71 // Fallback to setting lineNumber to 0, and source and function name to "undefined". 69 frames.append(ScriptCallFrame("undefined", "undefined", 0 , 0));72 frames.append(ScriptCallFrame("undefined", "undefined", 0)); 70 73 } 71 74 return ScriptCallStack::create(frames); … … 75 78 { 76 79 Vector<ScriptCallFrame> frames; 77 Vector<StackFrame> stackTrace; 78 Interpreter::getStackTrace(&exec->globalData(), stackTrace, maxStackSize + 1); 79 for (size_t i = 1; i < stackTrace.size(); i++) { 80 // This early exit is necessary to maintain our old behaviour 81 // but the stack trace we produce now is complete and handles all 82 // ways in which code may be running 83 if (!stackTrace[i].callee && frames.size()) 80 CallFrame* callFrame = exec; 81 while (true) { 82 ASSERT(callFrame); 83 int signedLineNumber; 84 intptr_t sourceID; 85 String urlString; 86 JSValue function; 87 88 exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function); 89 String functionName; 90 if (function) 91 functionName = jsCast<JSFunction*>(function)->name(exec); 92 else { 93 // Caller is unknown, but if frames is empty we should still add the frame, because 94 // something called us, and gave us arguments. 95 if (!frames.isEmpty()) 96 break; 97 } 98 unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0; 99 frames.append(ScriptCallFrame(functionName, urlString, lineNumber)); 100 if (!function || frames.size() == maxStackSize) 84 101 break; 85 String functionName = stackTrace[i].friendlyFunctionName(exec); 86 frames.append(ScriptCallFrame(functionName, stackTrace[i].sourceURL, stackTrace[i].line(), stackTrace[i].column())); 102 callFrame = callFrame->callerFrame(); 87 103 } 88 89 104 return ScriptCallStack::create(frames); 90 105 } -
trunk/Source/WebCore/inspector/ScriptCallFrame.cpp
r147818 r147846 54 54 return m_functionName == o.m_functionName 55 55 && m_scriptName == o.m_scriptName 56 && m_lineNumber == o.m_lineNumber 57 && m_column == o.m_column; 56 && m_lineNumber == o.m_lineNumber; 58 57 } 59 58 -
trunk/Source/WebCore/inspector/ScriptCallFrame.h
r147818 r147846 45 45 class ScriptCallFrame { 46 46 public: 47 ScriptCallFrame(const String& functionName, const String& scriptName, unsigned lineNumber, unsigned column );47 ScriptCallFrame(const String& functionName, const String& scriptName, unsigned lineNumber, unsigned column = 0); 48 48 ~ScriptCallFrame(); 49 49 … … 51 51 const String& sourceURL() const { return m_scriptName; } 52 52 unsigned lineNumber() const { return m_lineNumber; } 53 unsigned columnNumber() const { return m_column; }54 53 55 54 bool isEqual(const ScriptCallFrame&) const; -
trunk/Tools/ChangeLog
r147833 r147846 1 2013-04-06 Geoffrey Garen <ggaren@apple.com> 2 3 Rolled out 147820 and 147818 because they caused plugins tests to ASSERT 4 https://bugs.webkit.org/show_bug.cgi?id=114094 5 6 Reviewed by Anders Carlsson. 7 8 * Scripts/run-jsc: 9 1 10 2013-04-05 Ojan Vafai <ojan@chromium.org> 2 11 -
trunk/Tools/Scripts/run-jsc
r147818 r147846 43 43 GetOptions("count|c=i" => \$count, 44 44 "verbose|v" => \$verbose); 45 die "$usage\n" if (@ARGV < 1); 45 46 46 47 my $jsc = jscProductDir() . "/jsc @ARGV";
Note: See TracChangeset
for help on using the changeset viewer.