diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
| commit | 41386e9cb918eed93b3f13648cbef387e371e451 (patch) | |
| tree | a97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/interpreter | |
| parent | e15dd966d523731101f70ccf768bba12435a0208 (diff) | |
| download | WebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz | |
webkitgtk-2.4.9webkitgtk-2.4.9
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
20 files changed, 1661 insertions, 859 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp index 09b24d5dc..8600b7228 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.cpp +++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp @@ -27,9 +27,9 @@ #include "AbstractPC.h" #include "CallFrame.h" -#include "JSObject.h" -#include "JSCInlines.h" #include "VM.h" +#include "JSObject.h" + namespace JSC { diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h index ac2aaa7ad..c30027d9e 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.h +++ b/Source/JavaScriptCore/interpreter/AbstractPC.h @@ -27,6 +27,7 @@ #define AbstractPC_h #include "MacroAssemblerCodeRef.h" +#include <wtf/Platform.h> namespace JSC { diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h index 30cad2a14..2ca3e9794 100644 --- a/Source/JavaScriptCore/interpreter/CachedCall.h +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -43,7 +43,7 @@ namespace JSC { , m_interpreter(callFrame->interpreter()) , m_entryScope(callFrame->vm(), function->scope()->globalObject()) { - ASSERT(!function->isHostFunctionNonInline()); + ASSERT(!function->isHostFunction()); if (callFrame->vm().isSafeToRecurse()) { m_arguments.resize(argumentCount); m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, &m_protoCallFrame, function, argumentCount + 1, function->scope(), m_arguments.data()); diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp index 259061df4..a226e9848 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013, 2014 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,10 +29,8 @@ #include "CallFrameInlines.h" #include "CodeBlock.h" #include "Interpreter.h" -#include "JSLexicalEnvironment.h" -#include "JSCInlines.h" +#include "Operations.h" #include "VMEntryScope.h" -#include <wtf/StringPrintStream.h> namespace JSC { @@ -118,11 +116,11 @@ CodeOrigin CallFrame::codeOrigin() return CodeOrigin(locationAsBytecodeOffset()); } -Register* CallFrame::topOfFrameInternal() +Register* CallFrame::frameExtentInternal() { CodeBlock* codeBlock = this->codeBlock(); ASSERT(codeBlock); - return registers() + codeBlock->stackPointerOffset(); + return registers() + virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); } JSGlobalObject* CallFrame::vmEntryGlobalObject() @@ -136,73 +134,4 @@ JSGlobalObject* CallFrame::vmEntryGlobalObject() return vm().entryScope->globalObject(); } -CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame) -{ - if (callerFrameOrVMEntryFrame() == currVMEntryFrame) { - VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); - currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame(); - return currVMEntryRecord->prevTopCallFrame(); - } - return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); -} - -JSLexicalEnvironment* CallFrame::lexicalEnvironment() const -{ - CodeBlock* codeBlock = this->codeBlock(); - RELEASE_ASSERT(codeBlock->needsActivation()); - VirtualRegister activationRegister = codeBlock->activationRegister(); - return registers()[activationRegister.offset()].Register::lexicalEnvironment(); -} - -JSLexicalEnvironment* CallFrame::lexicalEnvironmentOrNullptr() const -{ - CodeBlock* codeBlock = this->codeBlock(); - return codeBlock->needsActivation() ? lexicalEnvironment() : nullptr; -} - -void CallFrame::setActivation(JSLexicalEnvironment* lexicalEnvironment) -{ - CodeBlock* codeBlock = this->codeBlock(); - RELEASE_ASSERT(codeBlock->needsActivation()); - VirtualRegister activationRegister = codeBlock->activationRegister(); - registers()[activationRegister.offset()] = lexicalEnvironment; -} - -void CallFrame::dump(PrintStream& out) -{ - if (CodeBlock* codeBlock = this->codeBlock()) { - out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), "]"); - - out.print("("); - thisValue().dumpForBacktrace(out); - - for (size_t i = 0; i < argumentCount(); ++i) { - out.print(", "); - JSValue value = argument(i); - value.dumpForBacktrace(out); - } - - out.print(")"); - - return; - } - - out.print(returnPC()); -} - -const char* CallFrame::describeFrame() -{ - const size_t bufferSize = 200; - static char buffer[bufferSize + 1]; - - WTF::StringPrintStream stringStream; - - dump(stringStream); - - strncpy(buffer, stringStream.toCString().data(), bufferSize); - buffer[bufferSize] = '\0'; - - return buffer; -} - } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h index afa8d3ca7..48fbcd779 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.h +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2007, 2008, 2011, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2011, 2013 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,17 +24,16 @@ #define CallFrame_h #include "AbstractPC.h" +#include "VM.h" #include "JSStack.h" #include "MacroAssemblerCodeRef.h" #include "Register.h" #include "StackVisitor.h" -#include "VM.h" -#include "VMEntryRecord.h" namespace JSC { class Arguments; - class JSLexicalEnvironment; + class JSActivation; class Interpreter; class JSScope; @@ -43,19 +42,14 @@ namespace JSC { class ExecState : private Register { public: JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); } - JSObject* callee() const { return this[JSStack::Callee].object(); } + JSObject* callee() const { return this[JSStack::Callee].function(); } CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } - JSScope* scope(int scopeRegisterOffset) const + JSScope* scope() const { - ASSERT(this[scopeRegisterOffset].Register::scope()); - return this[scopeRegisterOffset].Register::scope(); + ASSERT(this[JSStack::ScopeChain].Register::scope()); + return this[JSStack::ScopeChain].Register::scope(); } - bool hasActivation() const; - JSLexicalEnvironment* lexicalEnvironment() const; - JSLexicalEnvironment* lexicalEnvironmentOrNullptr() const; - JSValue uncheckedActivation() const; - // Global object in which execution began. JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject(); @@ -75,30 +69,49 @@ namespace JSC { // But they're used in many places in legacy code, so they're not going away any time soon. void clearException() { vm().clearException(); } + void clearSupplementaryExceptionInfo() + { + vm().clearExceptionStack(); + } - Exception* exception() const { return vm().exception(); } - bool hadException() const { return !!vm().exception(); } - - Exception* lastException() const { return vm().lastException(); } - void clearLastException() { vm().clearLastException(); } + JSValue exception() const { return vm().exception(); } + bool hadException() const { return !vm().exception().isEmpty(); } - AtomicStringTable* atomicStringTable() const { return vm().atomicStringTable(); } const CommonIdentifiers& propertyNames() const { return *vm().propertyNames; } const MarkedArgumentBuffer& emptyList() const { return *vm().emptyList; } Interpreter* interpreter() { return vm().interpreter; } Heap* heap() { return &vm().heap; } - +#ifndef NDEBUG + void dumpCaller(); +#endif + static const HashTable& arrayConstructorTable(VM& vm) { return *vm.arrayConstructorTable; } + static const HashTable& arrayPrototypeTable(VM& vm) { return *vm.arrayPrototypeTable; } + static const HashTable& booleanPrototypeTable(VM& vm) { return *vm.booleanPrototypeTable; } + static const HashTable& dataViewTable(VM& vm) { return *vm.dataViewTable; } + static const HashTable& dateTable(VM& vm) { return *vm.dateTable; } + static const HashTable& dateConstructorTable(VM& vm) { return *vm.dateConstructorTable; } + static const HashTable& errorPrototypeTable(VM& vm) { return *vm.errorPrototypeTable; } + static const HashTable& globalObjectTable(VM& vm) { return *vm.globalObjectTable; } + static const HashTable& jsonTable(VM& vm) { return *vm.jsonTable; } + static const HashTable& numberConstructorTable(VM& vm) { return *vm.numberConstructorTable; } + static const HashTable& numberPrototypeTable(VM& vm) { return *vm.numberPrototypeTable; } + static const HashTable& objectConstructorTable(VM& vm) { return *vm.objectConstructorTable; } + static const HashTable& privateNamePrototypeTable(VM& vm) { return *vm.privateNamePrototypeTable; } + static const HashTable& regExpTable(VM& vm) { return *vm.regExpTable; } + static const HashTable& regExpConstructorTable(VM& vm) { return *vm.regExpConstructorTable; } + static const HashTable& regExpPrototypeTable(VM& vm) { return *vm.regExpPrototypeTable; } + static const HashTable& stringConstructorTable(VM& vm) { return *vm.stringConstructorTable; } +#if ENABLE(PROMISES) + static const HashTable& promisePrototypeTable(VM& vm) { return *vm.promisePrototypeTable; } + static const HashTable& promiseConstructorTable(VM& vm) { return *vm.promiseConstructorTable; } +#endif static CallFrame* create(Register* callFrameBase) { return static_cast<CallFrame*>(callFrameBase); } Register* registers() { return this; } - const Register* registers() const { return this; } CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; } - CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); } - - JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); - + CallFrame* callerFrame() const { return callerFrameAndPC().callerFrame; } static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); } ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); } @@ -163,20 +176,24 @@ namespace JSC { // CodeOrigin(0) if we're in native code. CodeOrigin codeOrigin(); - Register* topOfFrame() + Register* frameExtent() { - if (!codeBlock()) - return registers(); - return topOfFrameInternal(); + if (isVMEntrySentinel() || !codeBlock()) + return registers() - 1; + return frameExtentInternal(); } + Register* frameExtentInternal(); + #if USE(JSVALUE32_64) Instruction* currentVPC() const { + ASSERT(!isVMEntrySentinel()); return bitwise_cast<Instruction*>(this[JSStack::ArgumentCount].tag()); } void setCurrentVPC(Instruction* vpc) { + ASSERT(!isVMEntrySentinel()); this[JSStack::ArgumentCount].tag() = bitwise_cast<int32_t>(vpc); } #else @@ -185,27 +202,25 @@ namespace JSC { #endif void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } - void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; } - void setActivation(JSLexicalEnvironment*); - - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, - CallFrame* callerFrame, int argc, JSObject* callee) - { - ASSERT(callerFrame == noCaller() || callerFrame->stack()->containsAddress(this)); - - setCodeBlock(codeBlock); - setCallerFrame(callerFrame); - setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. - setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) - setCallee(callee); + void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; } + + ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope, + CallFrame* callerFrame, int argc, JSObject* callee) + { + ASSERT(callerFrame == noCaller() || callerFrame->isVMEntrySentinel() || callerFrame->stack()->containsAddress(this)); + + setCodeBlock(codeBlock); + setScope(scope); + setCallerFrame(callerFrame); + setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. + setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) + setCallee(callee); } // Read a register from the codeframe (or constant from the CodeBlock). Register& r(int); - Register& r(VirtualRegister); // Read a register for a non-constant Register& uncheckedR(int); - Register& uncheckedR(VirtualRegister); // Access to arguments as passed. (After capture, arguments may move to a different location.) size_t argumentCount() const { return argumentCountIncludingThis() - 1; } @@ -238,31 +253,51 @@ namespace JSC { this[argumentOffset(argument)] = value; } - JSValue getArgumentUnsafe(size_t argIndex) - { - // User beware! This method does not verify that there is a valid - // argument at the specified argIndex. This is used for debugging - // and verification code only. The caller is expected to know what - // he/she is doing when calling this method. - return this[argumentOffset(argIndex)].jsValue(); - } - static int thisArgumentOffset() { return argumentOffsetIncludingThis(0); } JSValue thisValue() { return this[thisArgumentOffset()].jsValue(); } void setThisValue(JSValue value) { this[thisArgumentOffset()] = value; } - // Under the constructor implemented in C++, thisValue holds the newTarget instead of the automatically constructed value. - // The result of this function is only effective under the "construct" context. - JSValue newTarget() { return thisValue(); } - JSValue argumentAfterCapture(size_t argument); static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } + // FIXME: Remove these. + int hostThisRegister() { return thisArgumentOffset(); } + JSValue hostThisValue() { return thisValue(); } + static CallFrame* noCaller() { return 0; } + bool isVMEntrySentinel() const + { + return !!this && codeBlock() == vmEntrySentinelCodeBlock(); + } + + CallFrame* vmEntrySentinelCallerFrame() const + { + ASSERT(isVMEntrySentinel()); + return this[JSStack::ScopeChain].callFrame(); + } + + void initializeVMEntrySentinelFrame(CallFrame* callFrame) + { + setCallerFrame(noCaller()); + setReturnPC(0); + setCodeBlock(vmEntrySentinelCodeBlock()); + static_cast<Register*>(this)[JSStack::ScopeChain] = callFrame; + setCallee(0); + setArgumentCountIncludingThis(0); + } + + CallFrame* callerFrameSkippingVMEntrySentinel() + { + CallFrame* caller = callerFrame(); + if (caller->isVMEntrySentinel()) + return caller->vmEntrySentinelCallerFrame(); + return caller; + } + void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[JSStack::ArgumentCount].payload() = count; } - void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = callee; } + void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); } void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; } void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); } @@ -274,10 +309,8 @@ namespace JSC { StackVisitor::visit<Functor>(this, functor); } - void dump(PrintStream&); - JS_EXPORT_PRIVATE const char* describeFrame(); - private: + static const intptr_t s_VMEntrySentinel = 1; #ifndef NDEBUG JSStack* stack(); @@ -285,8 +318,6 @@ namespace JSC { ExecState(); ~ExecState(); - Register* topOfFrameInternal(); - // The following are for internal use in debugging and verification // code only and not meant as an API for general usage: @@ -307,12 +338,22 @@ namespace JSC { return argIndex; } - void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + JSValue getArgumentUnsafe(size_t argIndex) + { + // User beware! This method does not verify that there is a valid + // argument at the specified argIndex. This is used for debugging + // and verification code only. The caller is expected to know what + // he/she is doing when calling this method. + return this[argumentOffset(argIndex)].jsValue(); + } CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); } const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } + static CodeBlock* vmEntrySentinelCodeBlock() { return reinterpret_cast<CodeBlock*>(s_VMEntrySentinel); } + friend class JSStack; + friend class VMInspector; }; } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h index 2c03b7452..e3326626d 100644 --- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -49,6 +49,11 @@ struct CallFrameClosure { { protoCallFrame->setArgument(argument, value); } + + void resetCallFrame() + { + protoCallFrame->setScope(scope); + } }; } diff --git a/Source/JavaScriptCore/interpreter/CallFrameInlines.h b/Source/JavaScriptCore/interpreter/CallFrameInlines.h index 3f430ec31..51d751a51 100644 --- a/Source/JavaScriptCore/interpreter/CallFrameInlines.h +++ b/Source/JavaScriptCore/interpreter/CallFrameInlines.h @@ -27,7 +27,6 @@ #define CallFrameInlines_h #include "CallFrame.h" -#include "CodeBlock.h" namespace JSC { @@ -139,20 +138,6 @@ inline unsigned CallFrame::locationAsCodeOriginIndex() const return Location::decode(locationAsRawBits()); } -inline bool CallFrame::hasActivation() const -{ - JSValue activation = uncheckedActivation(); - return !!activation && activation.isCell(); -} - -inline JSValue CallFrame::uncheckedActivation() const -{ - CodeBlock* codeBlock = this->codeBlock(); - RELEASE_ASSERT(codeBlock->needsActivation()); - VirtualRegister activationRegister = codeBlock->activationRegister(); - return registers()[activationRegister.offset()].jsValue(); -} - } // namespace JSC #endif // CallFrameInlines_h diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 518fc0b76..4fbc8229a 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,25 +30,25 @@ #include "config.h" #include "Interpreter.h" +#include "Arguments.h" #include "BatchedTransitionOptimizer.h" +#include "CallFrame.h" #include "CallFrameClosure.h" #include "CallFrameInlines.h" -#include "ClonedArguments.h" #include "CodeBlock.h" -#include "DirectArguments.h" #include "Heap.h" #include "Debugger.h" #include "DebuggerCallFrame.h" #include "ErrorInstance.h" #include "EvalCodeCache.h" -#include "Exception.h" #include "ExceptionHelpers.h" #include "GetterSetter.h" +#include "JSActivation.h" #include "JSArray.h" #include "JSBoundFunction.h" -#include "JSCInlines.h" -#include "JSLexicalEnvironment.h" +#include "JSNameScope.h" #include "JSNotAnObject.h" +#include "JSPropertyNameIterator.h" #include "JSStackInlines.h" #include "JSString.h" #include "JSWithScope.h" @@ -56,27 +56,24 @@ #include "LLIntThunks.h" #include "LegacyProfiler.h" #include "LiteralParser.h" +#include "NameInstance.h" #include "ObjectPrototype.h" +#include "Operations.h" #include "Parser.h" #include "ProtoCallFrame.h" #include "RegExpObject.h" #include "RegExpPrototype.h" #include "Register.h" #include "SamplingTool.h" -#include "ScopedArguments.h" -#include "StackAlignment.h" #include "StackVisitor.h" #include "StrictEvalActivation.h" #include "StrongInlines.h" -#include "Symbol.h" #include "VMEntryScope.h" -#include "VMInlines.h" #include "VirtualRegister.h" #include <limits.h> #include <stdio.h> #include <wtf/StackStats.h> -#include <wtf/StdLibExtras.h> #include <wtf/StringPrintStream.h> #include <wtf/Threading.h> #include <wtf/WTFThreadData.h> @@ -86,49 +83,26 @@ #include "JIT.h" #endif +#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__)) + using namespace std; namespace JSC { -String StackFrame::friendlySourceURL() const +Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec) + : m_interpreter(*exec->interpreter()) { - String traceLine; - - switch (codeType) { - case StackFrameEvalCode: - case StackFrameFunctionCode: - case StackFrameGlobalCode: - if (!sourceURL.isEmpty()) - traceLine = sourceURL.impl(); - break; - case StackFrameNativeCode: - traceLine = "[native code]"; - break; - } - return traceLine.isNull() ? emptyString() : traceLine; + if (!m_interpreter.m_errorHandlingModeReentry) + m_interpreter.stack().enableErrorStackReserve(); + m_interpreter.m_errorHandlingModeReentry++; } -String StackFrame::friendlyFunctionName(CallFrame* callFrame) const +Interpreter::ErrorHandlingMode::~ErrorHandlingMode() { - String traceLine; - JSObject* stackFrameCallee = callee.get(); - - switch (codeType) { - case StackFrameEvalCode: - traceLine = "eval code"; - break; - case StackFrameNativeCode: - if (callee) - traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); - break; - case StackFrameFunctionCode: - traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); - break; - case StackFrameGlobalCode: - traceLine = "global code"; - break; - } - return traceLine.isNull() ? emptyString() : traceLine; + m_interpreter.m_errorHandlingModeReentry--; + ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); + if (!m_interpreter.m_errorHandlingModeReentry) + m_interpreter.stack().disableErrorStackReserve(); } JSValue eval(CallFrame* callFrame) @@ -147,11 +121,13 @@ JSValue eval(CallFrame* callFrame) CallFrame* callerFrame = callFrame->callerFrame(); CodeBlock* callerCodeBlock = callerFrame->codeBlock(); - JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope(); + JSScope* callerScopeChain = callerFrame->scope(); EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); if (!eval) { if (!callerCodeBlock->isStrictMode()) { + // FIXME: We can use the preparser in strict mode, we just need additional logic + // to prevent duplicates. if (programSource.is8Bit()) { LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) @@ -166,8 +142,7 @@ JSValue eval(CallFrame* callFrame) // If the literal parser bailed, it should not have thrown exceptions. ASSERT(!callFrame->vm().exception()); - ThisTDZMode thisTDZMode = callerCodeBlock->unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded; - eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), thisTDZMode, programSource, callerScopeChain); + eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain); if (!eval) return jsUndefined(); } @@ -177,103 +152,109 @@ JSValue eval(CallFrame* callFrame) return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); } -unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset) +CallFrame* sizeAndAllocFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, int firstFreeRegister) { - if (UNLIKELY(!arguments.isCell())) { - if (arguments.isUndefinedOrNull()) + if (!arguments) { // f.apply(x, arguments), with arguments unmodified. + unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - argumentCountIncludingThis - JSStack::CallFrameHeaderSize - 1); + if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) { + callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); return 0; - - callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); - return 0; + } + return newCallFrame; } - - JSCell* cell = arguments.asCell(); - unsigned length; - switch (cell->type()) { - case DirectArgumentsType: - length = jsCast<DirectArguments*>(cell)->length(callFrame); - break; - case ScopedArgumentsType: - length =jsCast<ScopedArguments*>(cell)->length(callFrame); - break; - case StringType: - callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); + + if (arguments.isUndefinedOrNull()) { + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - 1 - JSStack::CallFrameHeaderSize - 1); + if (!stack->grow(newCallFrame->registers())) { + callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); + return 0; + } + return newCallFrame; + } + + if (!arguments.isObject()) { + callFrame->vm().throwException(callFrame, createInvalidParameterError(callFrame, "Function.prototype.apply", arguments)); return 0; - default: - ASSERT(arguments.isObject()); - if (isJSArray(cell)) - length = jsCast<JSArray*>(cell)->length(); - else - length = jsCast<JSObject*>(cell)->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); - break; } - - if (length >= firstVarArgOffset) - length -= firstVarArgOffset; - else - length = 0; - - return length; -} -unsigned sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset) -{ - unsigned length = sizeOfVarargs(callFrame, arguments, firstVarArgOffset); - - CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1); - if (length > maxArguments || !stack->ensureCapacityFor(calleeFrame->registers())) { - throwStackOverflowError(callFrame); + if (asObject(arguments)->classInfo() == Arguments::info()) { + Arguments* argsObject = asArguments(arguments); + unsigned argCount = argsObject->length(callFrame); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { + callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); + return 0; + } + return newCallFrame; + } + + if (isJSArray(arguments)) { + JSArray* array = asArray(arguments); + unsigned argCount = array->length(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { + callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); + return 0; + } + return newCallFrame; + } + + JSObject* argObject = asObject(arguments); + unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { + callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); return 0; } - - return length; + return newCallFrame; } -void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length) +void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments) { - if (UNLIKELY(!arguments.isCell())) + if (!arguments) { // f.apply(x, arguments), with arguments unmodified. + unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); + + newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); + newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < callFrame->argumentCount(); ++i) + newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i)); return; + } - JSCell* cell = arguments.asCell(); - switch (cell->type()) { - case DirectArgumentsType: - jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); + if (arguments.isUndefinedOrNull()) { + newCallFrame->setArgumentCountIncludingThis(1); + newCallFrame->setThisValue(thisValue); return; - case ScopedArgumentsType: - jsCast<ScopedArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); - return; - default: { - ASSERT(arguments.isObject()); - JSObject* object = jsCast<JSObject*>(cell); - if (isJSArray(object)) { - jsCast<JSArray*>(object)->copyToArguments(callFrame, firstElementDest, offset, length); - return; - } - unsigned i; - for (i = 0; i < length && object->canGetIndexQuickly(i + offset); ++i) - callFrame->r(firstElementDest + i) = object->getIndexQuickly(i + offset); - for (; i < length; ++i) - callFrame->r(firstElementDest + i) = object->get(callFrame, i + offset); + } + + if (asObject(arguments)->classInfo() == Arguments::info()) { + Arguments* argsObject = asArguments(arguments); + unsigned argCount = argsObject->length(callFrame); + newCallFrame->setArgumentCountIncludingThis(argCount + 1); + newCallFrame->setThisValue(thisValue); + argsObject->copyToArguments(callFrame, newCallFrame, argCount); return; - } } -} - -void setupVarargsFrame(CallFrame* callFrame, CallFrame* newCallFrame, JSValue arguments, uint32_t offset, uint32_t length) -{ - VirtualRegister calleeFrameOffset(newCallFrame - callFrame); + } - loadVarargs( - callFrame, - calleeFrameOffset + CallFrame::argumentOffset(0), - arguments, offset, length); + if (isJSArray(arguments)) { + JSArray* array = asArray(arguments); + unsigned argCount = array->length(); + newCallFrame->setArgumentCountIncludingThis(argCount + 1); + newCallFrame->setThisValue(thisValue); + array->copyToArguments(callFrame, newCallFrame, argCount); + return; + } - newCallFrame->setArgumentCountIncludingThis(length + 1); -} - -void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length) -{ - setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length); + JSObject* argObject = asObject(arguments); + unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + newCallFrame->setArgumentCountIncludingThis(argCount + 1); newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < argCount; ++i) { + newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i)); + if (UNLIKELY(callFrame->vm().exception())) + return; + } } Interpreter::Interpreter(VM& vm) @@ -295,7 +276,7 @@ void Interpreter::initialize(bool canUseJIT) { UNUSED_PARAM(canUseJIT); -#if ENABLE(COMPUTED_GOTO_OPCODES) +#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) m_opcodeTable = LLInt::opcodeMap(); for (int i = 0; i < numOpcodeIDs; ++i) m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); @@ -380,7 +361,7 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) --it; dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); --it; - // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header + dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); --it; #if ENABLE(JIT) AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); @@ -424,9 +405,13 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) bool Interpreter::isOpcode(Opcode opcode) { #if ENABLE(COMPUTED_GOTO_OPCODES) +#if !ENABLE(LLINT) + return static_cast<OpcodeID>(bitwise_cast<uintptr_t>(opcode)) <= op_end; +#else return opcode != HashTraits<Opcode>::emptyValue() && !HashTraits<Opcode>::isDeletedValue(opcode) && m_opcodeIDTable.contains(opcode); +#endif #else return opcode >= 0 && opcode <= op_end; #endif @@ -435,16 +420,46 @@ bool Interpreter::isOpcode(Opcode opcode) static bool unwindCallFrame(StackVisitor& visitor) { CallFrame* callFrame = visitor->callFrame(); + CodeBlock* codeBlock = visitor->codeBlock(); + CodeBlock* oldCodeBlock = codeBlock; + JSScope* scope = callFrame->scope(); + if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { - SuspendExceptionScope scope(&callFrame->vm()); - if (jsDynamicCast<JSFunction*>(callFrame->callee())) + if (callFrame->callee()) debugger->returnEvent(callFrame); else debugger->didExecuteProgram(callFrame); - ASSERT(!callFrame->hadException()); } - return !visitor->callerIsVMEntryFrame(); + JSValue activation; + if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) { +#if ENABLE(DFG_JIT) + RELEASE_ASSERT(!visitor->isInlinedFrame()); +#endif + activation = callFrame->uncheckedR(oldCodeBlock->activationRegister().offset()).jsValue(); + if (activation) + jsCast<JSActivation*>(activation)->tearOff(*scope->vm()); + } + + if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) { + if (Arguments* arguments = visitor->existingArguments()) { + if (activation) + arguments->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); +#if ENABLE(DFG_JIT) + else if (visitor->isInlinedFrame()) + arguments->tearOff(callFrame, visitor->inlineCallFrame()); +#endif + else + arguments->tearOff(callFrame); + } + } + + CallFrame* callerFrame = callFrame->callerFrame(); + if (callerFrame->isVMEntrySentinel()) { + callFrame->vm().topCallFrame = callerFrame->vmEntrySentinelCallerFrame(); + return false; + } + return true; } static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) @@ -481,9 +496,6 @@ void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) line = divotLine + lineOffset; column = divotColumn + (divotLine ? 1 : firstLineColumnOffset); - - if (executable->hasOverrideLineNumber()) - line = executable->overrideLineNumber(); } void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) @@ -529,15 +541,15 @@ public: { VM& vm = m_vm; if (m_remainingCapacityForFrameCapture) { - if (visitor->isJSFrame() && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { + if (visitor->isJSFrame()) { CodeBlock* codeBlock = visitor->codeBlock(); StackFrame s = { Strong<JSObject>(vm, visitor->callee()), getStackFrameCodeType(visitor), - Strong<ScriptExecutable>(vm, codeBlock->ownerExecutable()), + Strong<ExecutableBase>(vm, codeBlock->ownerExecutable()), Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), codeBlock->source(), - codeBlock->ownerExecutable()->firstLine(), + codeBlock->ownerExecutable()->lineNo(), codeBlock->firstLineColumnOffset(), codeBlock->sourceOffset(), visitor->bytecodeOffset(), @@ -545,7 +557,7 @@ public: }; m_results.append(s); } else { - StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ScriptExecutable>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; + StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; m_results.append(s); } @@ -564,6 +576,7 @@ private: void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize) { VM& vm = m_vm; + ASSERT(!vm.topCallFrame->isVMEntrySentinel()); CallFrame* callFrame = vm.topCallFrame; if (!callFrame) return; @@ -584,9 +597,9 @@ JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> st return jsString(&exec->vm(), builder.toString()); } -class GetCatchHandlerFunctor { +class GetExceptionHandlerFunctor { public: - GetCatchHandlerFunctor() + GetExceptionHandlerFunctor() : m_handler(0) { } @@ -600,7 +613,7 @@ public: return StackVisitor::Continue; unsigned bytecodeOffset = visitor->bytecodeOffset(); - m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset, CodeBlock::RequiredHandler::CatchHandler); + m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); if (m_handler) return StackVisitor::Done; @@ -613,9 +626,8 @@ private: class UnwindFunctor { public: - UnwindFunctor(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) - : m_vmEntryFrame(vmEntryFrame) - , m_callFrame(callFrame) + UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) + : m_callFrame(callFrame) , m_isTermination(isTermination) , m_codeBlock(codeBlock) , m_handler(handler) @@ -625,12 +637,11 @@ public: StackVisitor::Status operator()(StackVisitor& visitor) { VM& vm = m_callFrame->vm(); - m_vmEntryFrame = visitor->vmEntryFrame(); m_callFrame = visitor->callFrame(); m_codeBlock = visitor->codeBlock(); unsigned bytecodeOffset = visitor->bytecodeOffset(); - if (m_isTermination || !(m_handler = m_codeBlock ? m_codeBlock->handlerForBytecodeOffset(bytecodeOffset) : nullptr)) { + if (m_isTermination || !(m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { if (!unwindCallFrame(visitor)) { if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->exceptionUnwind(m_callFrame); @@ -643,19 +654,17 @@ public: } private: - VMEntryFrame*& m_vmEntryFrame; CallFrame*& m_callFrame; bool m_isTermination; CodeBlock*& m_codeBlock; HandlerInfo*& m_handler; }; -NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, Exception* exception) +NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& exceptionValue) { CodeBlock* codeBlock = callFrame->codeBlock(); bool isTermination = false; - JSValue exceptionValue = exception->value(); ASSERT(!exceptionValue.isEmpty()); ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); // This shouldn't be possible (hence the assertions), but we're already in the slowest of @@ -663,42 +672,39 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) exceptionValue = jsNull(); - if (exceptionValue.isObject()) - isTermination = isTerminatedExecutionException(exception); + if (exceptionValue.isObject()) { + isTermination = isTerminatedExecutionException(asObject(exceptionValue)); + } - ASSERT(callFrame->vm().exception() && callFrame->vm().exception()->stack().size()); + ASSERT(callFrame->vm().exceptionStack().size()); Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); - if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) { - // We need to clear the exception here in order to see if a new exception happens. + if (debugger && debugger->needsExceptionCallbacks()) { + // We need to clear the exception and the exception stack here in order to see if a new exception happens. // Afterwards, the values are put back to continue processing this error. - SuspendExceptionScope scope(&callFrame->vm()); + ClearExceptionScope scope(&callFrame->vm()); // This code assumes that if the debugger is enabled then there is no inlining. // If that assumption turns out to be false then we'll ignore the inlined call // frames. // https://bugs.webkit.org/show_bug.cgi?id=121754 - bool hasCatchHandler; + bool hasHandler; if (isTermination) - hasCatchHandler = false; + hasHandler = false; else { - GetCatchHandlerFunctor functor; + GetExceptionHandlerFunctor functor; callFrame->iterate(functor); - HandlerInfo* handler = functor.handler(); - ASSERT(!handler || handler->isCatchHandler()); - hasCatchHandler = !!handler; + hasHandler = !!functor.handler(); } - debugger->exception(callFrame, exceptionValue, hasCatchHandler); - ASSERT(!callFrame->hadException()); + debugger->exception(callFrame, exceptionValue, hasHandler); } - exception->setDidNotifyInspectorOfThrow(); // Calculate an exception handler vPC, unwinding call frames as necessary. HandlerInfo* handler = 0; VM& vm = callFrame->vm(); ASSERT(callFrame == vm.topCallFrame); - UnwindFunctor functor(vmEntryFrame, callFrame, isTermination, codeBlock, handler); + UnwindFunctor functor(callFrame, isTermination, codeBlock, handler); callFrame->iterate(functor); if (!handler) return 0; @@ -706,6 +712,19 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->exceptionUnwind(callFrame); + // Unwind the scope chain within the exception handler's call frame. + int targetScopeDepth = handler->scopeDepth; + if (codeBlock->needsActivation() && callFrame->uncheckedR(codeBlock->activationRegister().offset()).jsValue()) + ++targetScopeDepth; + + JSScope* scope = callFrame->scope(); + int scopeDelta = scope->depth() - targetScopeDepth; + RELEASE_ASSERT(scopeDelta >= 0); + + while (scopeDelta--) + scope = scope->next(); + callFrame->setScope(scope); + return handler; } @@ -739,16 +758,16 @@ private: JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) { SamplingScope samplingScope(this); - - JSScope* scope = thisObj->globalObject(); + + JSScope* scope = callFrame->scope(); VM& vm = *scope->vm(); ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); - RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); + VMEntryScope entryScope(vm, scope->globalObject()); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); @@ -792,7 +811,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J PropertySlot slot(globalObject); if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { if (entry) - return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName)); + return callFrame->vm().throwException(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); goto failedJSONP; } baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); @@ -855,37 +874,40 @@ failedJSONP: // If we get here, then we have already proven that the script is not a JSON // object. - VMEntryScope entryScope(vm, scope->globalObject()); - // Compile source to bytecode if necessary: if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) return checkedReturn(callFrame->vm().throwException(callFrame, error)); - if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall)) + if (JSObject* error = program->prepareForExecution(callFrame, scope, CodeForCall)) return checkedReturn(callFrame->vm().throwException(callFrame, error)); ProgramCodeBlock* codeBlock = program->codeBlock(); - if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + if (UNLIKELY(vm.watchdog.didFire(callFrame))) return throwTerminatedExecutionException(callFrame); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) + return checkedReturn(throwStackOverflowError(callFrame)); + ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1); + protoCallFrame.init(codeBlock, scope, 0, thisObj, 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); + profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - result = program->generatedJITCode()->execute(&vm, &protoCallFrame); + Watchdog::Scope watchdogScope(vm.watchdog); + + result = program->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); + profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); return checkedReturn(result); } @@ -899,27 +921,24 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT return jsNull(); bool isJSCall = (callType == CallTypeJS); - JSScope* scope = nullptr; + JSScope* scope; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - JSGlobalObject* globalObject; - - if (isJSCall) { + if (isJSCall) scope = callData.js.scope; - globalObject = scope->globalObject(); - } else { + else { ASSERT(callType == CallTypeHost); - globalObject = function->globalObject(); + scope = callFrame->scope(); } - VMEntryScope entryScope(vm, globalObject); + VMEntryScope entryScope(vm, scope->globalObject()); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); if (isJSCall) { // Compile the callee: - JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall); + JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); if (UNLIKELY(!!compileError)) { return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } @@ -929,11 +948,14 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT } else newCodeBlock = 0; - if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + if (UNLIKELY(vm.watchdog.didFire(callFrame))) return throwTerminatedExecutionException(callFrame); + if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) + return checkedReturn(throwStackOverflowError(callFrame)); + ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data()); + protoCallFrame.init(newCodeBlock, scope, function, thisValue, argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, function); @@ -941,15 +963,13 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); + Watchdog::Scope watchdogScope(vm.watchdog); // Execute the code: if (isJSCall) - result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame); - else { - result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame)); - if (callFrame->hadException()) - result = jsNull(); - } + result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); + else + result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(callData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); } if (LegacyProfiler* profiler = vm.enabledProfiler()) @@ -958,7 +978,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT return checkedReturn(result); } -JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) +JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) { VM& vm = callFrame->vm(); ASSERT(!callFrame->hadException()); @@ -969,27 +989,24 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc return checkedReturn(throwStackOverflowError(callFrame)); bool isJSConstruct = (constructType == ConstructTypeJS); - JSScope* scope = nullptr; + JSScope* scope; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - JSGlobalObject* globalObject; - - if (isJSConstruct) { + if (isJSConstruct) scope = constructData.js.scope; - globalObject = scope->globalObject(); - } else { + else { ASSERT(constructType == ConstructTypeHost); - globalObject = constructor->globalObject(); + scope = callFrame->scope(); } - VMEntryScope entryScope(vm, globalObject); + VMEntryScope entryScope(vm, scope->globalObject()); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); if (isJSConstruct) { // Compile the callee: - JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct); + JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForConstruct); if (UNLIKELY(!!compileError)) { return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } @@ -999,11 +1016,14 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc } else newCodeBlock = 0; - if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + if (UNLIKELY(vm.watchdog.didFire(callFrame))) return throwTerminatedExecutionException(callFrame); + if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) + return checkedReturn(throwStackOverflowError(callFrame)); + ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data()); + protoCallFrame.init(newCodeBlock, scope, constructor, jsUndefined(), argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, constructor); @@ -1011,12 +1031,13 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); + Watchdog::Scope watchdogScope(vm.watchdog); // Execute the code. if (isJSConstruct) - result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame); + result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); else { - result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame)); + result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(constructData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); if (!callFrame->hadException()) RELEASE_ASSERT(result.isObject()); @@ -1041,7 +1062,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE return CallFrameClosure(); // Compile the callee: - JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall); + JSObject* error = functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); if (error) { callFrame->vm().throwException(callFrame, error); return CallFrameClosure(); @@ -1051,7 +1072,12 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE size_t argsCount = argumentCountIncludingThis; - protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args); + if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + + protoCallFrame->init(newCodeBlock, scope, function, jsUndefined(), argsCount, args); // Return the successful closure: CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; return result; @@ -1063,23 +1089,25 @@ JSValue Interpreter::execute(CallFrameClosure& closure) SamplingScope samplingScope(this); ASSERT(!vm.isCollectorBusy()); - RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); StackStats::CheckPoint stackCheckPoint; + closure.resetCallFrame(); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(closure.oldCallFrame, closure.function); - if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame))) + if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame))) return throwTerminatedExecutionException(closure.oldCallFrame); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); + Watchdog::Scope watchdogScope(vm.watchdog); + + result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame, m_stack.getTopOfStack()); } if (LegacyProfiler* profiler = vm.enabledProfiler()) @@ -1096,7 +1124,6 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue ASSERT(scope->vm() == &callFrame->vm()); ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); - RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); @@ -1109,26 +1136,19 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue JSScope* variableObject; if ((numVariables || numFunctions) && eval->isStrictMode()) { - scope = StrictEvalActivation::create(callFrame, scope); + scope = StrictEvalActivation::create(callFrame); variableObject = scope; } else { for (JSScope* node = scope; ; node = node->next()) { RELEASE_ASSERT(node); - if (node->isGlobalObject()) { + if (node->isVariableObject() && !node->isNameScopeObject()) { variableObject = node; break; - } - if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) { - if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) { - variableObject = node; - break; - } } } - ASSERT(!variableObject->isNameScopeObject()); } - JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall); + JSObject* compileError = eval->prepareForExecution(callFrame, scope, CodeForCall); if (UNLIKELY(!!compileError)) return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); EvalCodeBlock* codeBlock = eval->codeBlock(); @@ -1136,7 +1156,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue if (numVariables || numFunctions) { BatchedTransitionOptimizer optimizer(vm, variableObject); if (variableObject->next()) - variableObject->globalObject()->varInjectionWatchpoint()->fireAll("Executed eval, fired VarInjection watchpoint"); + variableObject->globalObject()->varInjectionWatchpoint()->fireAll(); for (unsigned i = 0; i < numVariables; ++i) { const Identifier& ident = codeBlock->variable(i); @@ -1153,26 +1173,31 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue } } - if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + if (UNLIKELY(vm.watchdog.didFire(callFrame))) return throwTerminatedExecutionException(callFrame); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) + return checkedReturn(throwStackOverflowError(callFrame)); + ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1); + protoCallFrame.init(codeBlock, scope, 0, thisValue, 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); + Watchdog::Scope watchdogScope(vm.watchdog); + + result = eval->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); return checkedReturn(result); } @@ -1182,38 +1207,35 @@ NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHook Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); if (!debugger) return; - - ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); - ASSERT(!callFrame->hadException()); + ASSERT(callFrame->codeBlock()->hasDebuggerRequests() || callFrame->hadException()); switch (debugHookID) { case DidEnterCallFrame: debugger->callEvent(callFrame); - break; + return; case WillLeaveCallFrame: debugger->returnEvent(callFrame); - break; + return; case WillExecuteStatement: debugger->atStatement(callFrame); - break; + return; case WillExecuteProgram: debugger->willExecuteProgram(callFrame); - break; + return; case DidExecuteProgram: debugger->didExecuteProgram(callFrame); - break; + return; case DidReachBreakpoint: debugger->didReachBreakpoint(callFrame); - break; + return; } - ASSERT(!callFrame->hadException()); } void Interpreter::enableSampler() { #if ENABLE(OPCODE_SAMPLING) if (!m_sampler) { - m_sampler = std::make_unique<SamplingTool>(this); + m_sampler = adoptPtr(new SamplingTool(this)); m_sampler->setup(); } #endif diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h index ad9df6fc7..cfc721b38 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.h +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. * Copyright (C) 2012 Research In Motion Limited. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -33,12 +33,12 @@ #include "ArgList.h" #include "JSCJSValue.h" #include "JSCell.h" +#include "JSFunction.h" #include "JSObject.h" #include "JSStack.h" #include "LLIntData.h" #include "Opcode.h" #include "SourceProvider.h" -#include "StackAlignment.h" #include <wtf/HashMap.h> #include <wtf/text/StringBuilder.h> @@ -50,7 +50,6 @@ namespace JSC { class ExecutableBase; class FunctionExecutable; class VM; - class JSFunction; class JSGlobalObject; class LLIntOffsetsExtractor; class ProgramExecutable; @@ -81,7 +80,7 @@ namespace JSC { struct StackFrame { Strong<JSObject> callee; StackFrameCodeType codeType; - Strong<ScriptExecutable> executable; + Strong<ExecutableBase> executable; Strong<UnlinkedCodeBlock> codeBlock; RefPtr<SourceProvider> code; int lineOffset; @@ -90,28 +89,65 @@ namespace JSC { unsigned bytecodeOffset; String sourceURL; JS_EXPORT_PRIVATE String toString(CallFrame*); - String friendlySourceURL() const; - String friendlyFunctionName(CallFrame*) const; + String friendlySourceURL() const + { + String traceLine; + + switch (codeType) { + case StackFrameEvalCode: + case StackFrameFunctionCode: + case StackFrameGlobalCode: + if (!sourceURL.isEmpty()) + traceLine = sourceURL.impl(); + break; + case StackFrameNativeCode: + traceLine = "[native code]"; + break; + } + return traceLine.isNull() ? emptyString() : traceLine; + } + String friendlyFunctionName(CallFrame* callFrame) const + { + String traceLine; + JSObject* stackFrameCallee = callee.get(); + + switch (codeType) { + case StackFrameEvalCode: + traceLine = "eval code"; + break; + case StackFrameNativeCode: + if (callee) + traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); + break; + case StackFrameFunctionCode: + traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); + break; + case StackFrameGlobalCode: + traceLine = "global code"; + break; + } + return traceLine.isNull() ? emptyString() : traceLine; + } JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); private: void expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); }; - class SuspendExceptionScope { + class ClearExceptionScope { public: - SuspendExceptionScope(VM* vm) - : m_vm(vm) + ClearExceptionScope(VM* vm): m_vm(vm) { - oldException = vm->exception(); + vm->getExceptionInfo(oldException, oldExceptionStack); vm->clearException(); } - ~SuspendExceptionScope() + ~ClearExceptionScope() { - m_vm->setException(oldException); + m_vm->setExceptionInfo(oldException, oldExceptionStack); } private: - Exception* oldException; + JSC::JSValue oldException; + RefCountedArray<JSC::StackFrame> oldExceptionStack; VM* m_vm; }; @@ -121,11 +157,13 @@ namespace JSC { : vm(currentVM) , oldCallFrame(currentVM.topCallFrame) { + ASSERT(!callFrame->isVMEntrySentinel()); currentVM.topCallFrame = callFrame; } ~TopCallFrameSetter() { + ASSERT(!oldCallFrame->isVMEntrySentinel()); vm.topCallFrame = oldCallFrame; } private: @@ -139,35 +177,11 @@ namespace JSC { { ASSERT(vm); ASSERT(callFrame); + ASSERT(!callFrame->isVMEntrySentinel()); vm->topCallFrame = callFrame; } }; - class NativeCallFrameTracerWithRestore { - public: - ALWAYS_INLINE NativeCallFrameTracerWithRestore(VM* vm, VMEntryFrame* vmEntryFrame, CallFrame* callFrame) - : m_vm(vm) - { - ASSERT(vm); - ASSERT(callFrame); - m_savedTopVMEntryFrame = vm->topVMEntryFrame; - m_savedTopCallFrame = vm->topCallFrame; - vm->topVMEntryFrame = vmEntryFrame; - vm->topCallFrame = callFrame; - } - - ALWAYS_INLINE ~NativeCallFrameTracerWithRestore() - { - m_vm->topVMEntryFrame = m_savedTopVMEntryFrame; - m_vm->topCallFrame = m_savedTopCallFrame; - } - - private: - VM* m_vm; - VMEntryFrame* m_savedTopVMEntryFrame; - CallFrame* m_savedTopCallFrame; - }; - class Interpreter { WTF_MAKE_FAST_ALLOCATED; friend class CachedCall; @@ -176,6 +190,14 @@ namespace JSC { friend class VM; public: + class ErrorHandlingMode { + public: + JS_EXPORT_PRIVATE ErrorHandlingMode(ExecState*); + JS_EXPORT_PRIVATE ~ErrorHandlingMode(); + private: + Interpreter& m_interpreter; + }; + Interpreter(VM &); ~Interpreter(); @@ -196,7 +218,7 @@ namespace JSC { OpcodeID getOpcodeID(Opcode opcode) { ASSERT(m_initialized); -#if ENABLE(COMPUTED_GOTO_OPCODES) +#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) ASSERT(isOpcode(opcode)); return m_opcodeIDTable.get(opcode); #else @@ -208,14 +230,16 @@ namespace JSC { JSValue execute(ProgramExecutable*, CallFrame*, JSObject* thisObj); JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&); - JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); + JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&); JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*); void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); SamplingTool* sampler() { return m_sampler.get(); } - NEVER_INLINE HandlerInfo* unwind(VMEntryFrame*&, CallFrame*&, Exception*); + bool isInErrorHandlingMode() { return m_errorHandlingModeReentry; } + + NEVER_INLINE HandlerInfo* unwind(CallFrame*&, JSValue&); NEVER_INLINE void debug(CallFrame*, DebugHookID); JSString* stackTraceAsString(ExecState*, Vector<StackFrame>); @@ -230,8 +254,6 @@ namespace JSC { JS_EXPORT_PRIVATE void dumpCallFrame(CallFrame*); - void getStackTrace(Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max()); - private: enum ExecutionFlag { Normal, InitializeAndReturn }; @@ -239,7 +261,7 @@ namespace JSC { JSValue execute(CallFrameClosure&); - + void getStackTrace(Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max()); void dumpRegisters(CallFrame*); @@ -247,13 +269,13 @@ namespace JSC { void enableSampler(); int m_sampleEntryDepth; - std::unique_ptr<SamplingTool> m_sampler; + OwnPtr<SamplingTool> m_sampler; VM& m_vm; JSStack m_stack; int m_errorHandlingModeReentry; -#if ENABLE(COMPUTED_GOTO_OPCODES) +#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling #endif @@ -264,22 +286,8 @@ namespace JSC { }; JSValue eval(CallFrame*); - - inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis) - { - unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf( - stackAlignmentRegisters(), - numUsedStackSlots + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); - return CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); - } - - unsigned sizeOfVarargs(CallFrame* exec, JSValue arguments, uint32_t firstVarArgOffset); - static const unsigned maxArguments = 0x10000; - unsigned sizeFrameForVarargs(CallFrame* exec, JSStack*, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset); - void loadVarargs(CallFrame* execCaller, VirtualRegister firstElementDest, JSValue source, uint32_t offset, uint32_t length); - void setupVarargsFrame(CallFrame* execCaller, CallFrame* execCallee, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); - void setupVarargsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); - + CallFrame* sizeAndAllocFrameForVarargs(CallFrame*, JSStack*, JSValue, int); + void loadVarargs(CallFrame*, CallFrame*, JSValue, JSValue); } // namespace JSC #endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp index d755aaa7d..722e1bc23 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.cpp +++ b/Source/JavaScriptCore/interpreter/JSStack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013, 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,153 +31,121 @@ #include "ConservativeRoots.h" #include "Interpreter.h" -#include "JSCInlines.h" -#include "Options.h" -#include <wtf/Lock.h> namespace JSC { -#if !ENABLE(JIT) static size_t committedBytesCount = 0; -static StaticLock stackStatisticsMutex; -#endif // !ENABLE(JIT) +static Mutex& stackStatisticsMutex() +{ + DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); + return staticMutex; +} -JSStack::JSStack(VM& vm) +JSStack::JSStack(VM& vm, size_t capacity) : m_vm(vm) - , m_topCallFrame(vm.topCallFrame) -#if !ENABLE(JIT) , m_end(0) - , m_reservedZoneSizeInRegisters(0) -#endif + , m_topCallFrame(vm.topCallFrame) { -#if !ENABLE(JIT) - size_t capacity = Options::maxPerThreadStackUsage(); ASSERT(capacity && isPageAligned(capacity)); - m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize, capacity), OSAllocator::JSVMStackPages); - setStackLimit(highAddress()); - m_commitTop = highAddress(); - - m_lastStackTop = baseOfStack(); -#endif // !ENABLE(JIT) + m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); + updateStackLimit(highAddress()); + m_commitEnd = highAddress(); + + disableErrorStackReserve(); m_topCallFrame = 0; } -#if !ENABLE(JIT) JSStack::~JSStack() { - ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop); - m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit); - addToCommittedByteCount(-sizeToDecommit); + void* highAddress = reinterpret_cast<void*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); + m_reservation.decommit(reinterpret_cast<void*>(m_commitEnd), reinterpret_cast<intptr_t>(highAddress) - reinterpret_cast<intptr_t>(m_commitEnd)); + addToCommittedByteCount(-(reinterpret_cast<intptr_t>(highAddress) - reinterpret_cast<intptr_t>(m_commitEnd))); m_reservation.deallocate(); } -bool JSStack::growSlowCase(Register* newTopOfStack) +bool JSStack::growSlowCase(Register* newEnd) { - Register* newTopOfStackWithReservedZone = newTopOfStack - m_reservedZoneSizeInRegisters; - // If we have already committed enough memory to satisfy this request, // just update the end pointer and return. - if (newTopOfStackWithReservedZone >= m_commitTop) { - setStackLimit(newTopOfStack); + if (newEnd >= m_commitEnd) { + updateStackLimit(newEnd); return true; } // Compute the chunk size of additional memory to commit, and see if we // have it is still within our budget. If not, we'll fail to grow and // return false. - ptrdiff_t delta = reinterpret_cast<char*>(m_commitTop) - reinterpret_cast<char*>(newTopOfStackWithReservedZone); - delta = WTF::roundUpToMultipleOf(commitSize, delta); - Register* newCommitTop = m_commitTop - (delta / sizeof(Register)); - if (newCommitTop < reservationTop()) + long delta = roundUpAllocationSize(reinterpret_cast<char*>(m_commitEnd) - reinterpret_cast<char*>(newEnd), commitSize); + if (reinterpret_cast<char*>(m_commitEnd) - delta <= reinterpret_cast<char*>(m_useableEnd)) return false; - // Otherwise, the growth is still within our budget. Commit it and return true. - m_reservation.commit(newCommitTop, delta); + // Otherwise, the growth is still within our budget. Go ahead and commit + // it and return true. + m_reservation.commit(reinterpret_cast<char*>(m_commitEnd) - delta, delta); addToCommittedByteCount(delta); - m_commitTop = newCommitTop; - setStackLimit(newTopOfStack); + m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) - delta); + updateStackLimit(newEnd); return true; } void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots) { - conservativeRoots.add(topOfStack() + 1, highAddress()); + conservativeRoots.add(getBaseOfStack(), getTopOfStack()); } void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) { - conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks); -} - -void JSStack::sanitizeStack() -{ -#if !ASAN_ENABLED - ASSERT(topOfStack() <= baseOfStack()); - - if (m_lastStackTop < topOfStack()) { - char* begin = reinterpret_cast<char*>(m_lastStackTop + 1); - char* end = reinterpret_cast<char*>(topOfStack() + 1); - memset(begin, 0, end - begin); - } - - m_lastStackTop = topOfStack(); -#endif + conservativeRoots.add(getBaseOfStack(), getTopOfStack(), jitStubRoutines, codeBlocks); } void JSStack::releaseExcessCapacity() { - Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters; - ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop); - m_reservation.decommit(m_commitTop, delta); + ptrdiff_t delta = reinterpret_cast<uintptr_t>(highAddress()) - reinterpret_cast<uintptr_t>(m_commitEnd); + m_reservation.decommit(m_commitEnd, delta); addToCommittedByteCount(-delta); - m_commitTop = highAddressWithReservedZone; + m_commitEnd = highAddress(); } -void JSStack::addToCommittedByteCount(long byteCount) +void JSStack::initializeThreading() { - LockHolder locker(stackStatisticsMutex); - ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); - committedBytesCount += byteCount; + stackStatisticsMutex(); } -void JSStack::setReservedZoneSize(size_t reservedZoneSize) +size_t JSStack::committedByteCount() { - m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register); - if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters) - growSlowCase(m_end + 1); + MutexLocker locker(stackStatisticsMutex()); + return committedBytesCount; } -#endif // !ENABLE(JIT) -#if ENABLE(JIT) -Register* JSStack::lowAddress() const +void JSStack::addToCommittedByteCount(long byteCount) { - ASSERT(wtfThreadData().stack().isGrowingDownward()); - return reinterpret_cast<Register*>(m_vm.stackLimit()); + MutexLocker locker(stackStatisticsMutex()); + ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); + committedBytesCount += byteCount; } -Register* JSStack::highAddress() const +void JSStack::enableErrorStackReserve() { - ASSERT(wtfThreadData().stack().isGrowingDownward()); - return reinterpret_cast<Register*>(wtfThreadData().stack().origin()); + m_useableEnd = reservationEnd(); } -#endif // ENABLE(JIT) -size_t JSStack::committedByteCount() +void JSStack::disableErrorStackReserve() { -#if !ENABLE(JIT) - LockHolder locker(stackStatisticsMutex); - return committedBytesCount; -#else - // When using the C stack, we don't know how many stack pages are actually - // committed. So, we use the current stack usage as an estimate. - ASSERT(wtfThreadData().stack().isGrowingDownward()); - int8_t* current = reinterpret_cast<int8_t*>(¤t); - int8_t* high = reinterpret_cast<int8_t*>(wtfThreadData().stack().origin()); - return high - current; -#endif + char* useableEnd = reinterpret_cast<char*>(reservationEnd()) + commitSize; + m_useableEnd = reinterpret_cast_ptr<Register*>(useableEnd); + + // By the time we get here, we are guaranteed to be destructing the last + // Interpreter::ErrorHandlingMode that enabled this reserve in the first + // place. That means the stack space beyond m_useableEnd before we + // enabled the reserve was not previously in use. Hence, it is safe to + // shrink back to that m_useableEnd. + if (m_end < m_useableEnd) { + ASSERT(m_topCallFrame->frameExtent() >= m_useableEnd); + shrink(m_useableEnd); + } } } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h index 07cbbe69b..adf825d98 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.h +++ b/Source/JavaScriptCore/interpreter/JSStack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -35,6 +35,11 @@ #include <wtf/PageReservation.h> #include <wtf/VMTags.h> +#define ENABLE_DEBUG_JSSTACK 0 +#if !defined(NDEBUG) && !defined(ENABLE_DEBUG_JSSTACK) +#define ENABLE_DEBUG_JSSTACK 1 +#endif + namespace JSC { class CodeBlockSet; @@ -56,8 +61,8 @@ namespace JSC { WTF_MAKE_NONCOPYABLE(JSStack); public: enum CallFrameHeaderEntry { - CallerFrameAndPCSize = sizeof(CallerFrameAndPC) / sizeof(Register), - CodeBlock = CallerFrameAndPCSize, + CodeBlock = sizeof(CallerFrameAndPC) / sizeof(Register), + ScopeChain, Callee, ArgumentCount, CallFrameHeaderSize, @@ -67,84 +72,90 @@ namespace JSC { FirstArgument, }; + static const size_t defaultCapacity = 512 * 1024; static const size_t commitSize = 16 * 1024; // Allow 8k of excess registers before we start trying to reap the stack static const ptrdiff_t maxExcessCapacity = 8 * 1024; - JSStack(VM&); - - bool ensureCapacityFor(Register* newTopOfStack); - - bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); } - static size_t committedByteCount(); - -#if ENABLE(JIT) - void gatherConservativeRoots(ConservativeRoots&) { } - void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { } - void sanitizeStack() { } -#else + JSStack(VM&, size_t capacity = defaultCapacity); ~JSStack(); - + void gatherConservativeRoots(ConservativeRoots&); void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&); - void sanitizeStack(); - Register* baseOfStack() const + Register* getBaseOfStack() const { return highAddress() - 1; } size_t size() const { return highAddress() - lowAddress(); } - void setReservedZoneSize(size_t); + bool grow(Register*); + + static size_t committedByteCount(); + static void initializeThreading(); + + Register* getTopOfFrame(CallFrame*); + Register* getStartOfFrame(CallFrame*); + Register* getTopOfStack(); - inline Register* topOfStack(); -#endif // ENABLE(JIT) + bool entryCheck(class CodeBlock*, int); - private: + CallFrame* pushFrame(class CodeBlock*, JSScope*, int argsCount, JSObject* callee); -#if !ENABLE(JIT) + void popFrame(CallFrame*); + + bool containsAddress(Register* address) { return (lowAddress() <= address && address <= highAddress()); } + + void enableErrorStackReserve(); + void disableErrorStackReserve(); + +#if ENABLE(DEBUG_JSSTACK) + void installFence(CallFrame*, const char *function = "", int lineNo = 0); + void validateFence(CallFrame*, const char *function = "", int lineNo = 0); + static const int FenceSize = 4; +#else // !ENABLE(DEBUG_JSSTACK) + void installFence(CallFrame*, const char* = "", int = 0) { } + void validateFence(CallFrame*, const char* = "", int = 0) { } +#endif // !ENABLE(DEBUG_JSSTACK) + + private: Register* lowAddress() const { - return m_end + 1; + return m_end; } Register* highAddress() const { return reinterpret_cast_ptr<Register*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); } -#else - Register* lowAddress() const; - Register* highAddress() const; -#endif // !ENABLE(JIT) -#if !ENABLE(JIT) - inline Register* topOfFrameFor(CallFrame*); - - Register* reservationTop() const + Register* reservationEnd() const { - char* reservationTop = static_cast<char*>(m_reservation.base()); - return reinterpret_cast_ptr<Register*>(reservationTop); + char* reservationEnd = static_cast<char*>(m_reservation.base()); + return reinterpret_cast_ptr<Register*>(reservationEnd); } - bool grow(Register* newTopOfStack); - bool growSlowCase(Register* newTopOfStack); - void shrink(Register* newTopOfStack); +#if ENABLE(DEBUG_JSSTACK) + static JSValue generateFenceValue(size_t argIndex); + void installTrapsAfterFrame(CallFrame*); +#else + void installTrapsAfterFrame(CallFrame*) { } +#endif + + bool growSlowCase(Register*); + void shrink(Register*); void releaseExcessCapacity(); void addToCommittedByteCount(long); - void setStackLimit(Register* newTopOfStack); -#endif // !ENABLE(JIT) + void updateStackLimit(Register* newEnd); VM& m_vm; - CallFrame*& m_topCallFrame; -#if !ENABLE(JIT) Register* m_end; - Register* m_commitTop; + Register* m_commitEnd; + Register* m_useableEnd; PageReservation m_reservation; - Register* m_lastStackTop; - ptrdiff_t m_reservedZoneSizeInRegisters; -#endif // !ENABLE(JIT) + CallFrame*& m_topCallFrame; friend class LLIntOffsetsExtractor; }; diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h index 69508ab5d..5a2aff117 100644 --- a/Source/JavaScriptCore/interpreter/JSStackInlines.h +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,61 +33,262 @@ namespace JSC { -inline bool JSStack::ensureCapacityFor(Register* newTopOfStack) +inline Register* JSStack::getTopOfFrame(CallFrame* frame) { -#if !ENABLE(JIT) - return grow(newTopOfStack); -#else - ASSERT(wtfThreadData().stack().isGrowingDownward()); - return newTopOfStack >= m_vm.stackLimit(); -#endif + if (UNLIKELY(!frame)) + return getBaseOfStack(); + return frame->frameExtent(); } -#if !ENABLE(JIT) +inline Register* JSStack::getTopOfStack() +{ + return getTopOfFrame(m_topCallFrame); +} -inline Register* JSStack::topOfFrameFor(CallFrame* frame) +inline Register* JSStack::getStartOfFrame(CallFrame* frame) { - if (UNLIKELY(!frame)) - return baseOfStack(); - return frame->topOfFrame() - 1; + CallFrame* callerFrame = frame->callerFrameSkippingVMEntrySentinel(); + return getTopOfFrame(callerFrame); } -inline Register* JSStack::topOfStack() +inline bool JSStack::entryCheck(class CodeBlock* codeBlock, int argsCount) { - return topOfFrameFor(m_topCallFrame); + Register* oldEnd = getTopOfStack(); + + // Ensure that we have enough space for the parameters: + size_t paddedArgsCount = argsCount; + if (codeBlock) { + size_t numParameters = codeBlock->numParameters(); + if (paddedArgsCount < numParameters) + paddedArgsCount = numParameters; + } + + Register* newCallFrameSlot = oldEnd - paddedArgsCount - (2 * JSStack::CallFrameHeaderSize) + 1; + +#if ENABLE(DEBUG_JSSTACK) + newCallFrameSlot -= JSStack::FenceSize; +#endif + + Register* newEnd = newCallFrameSlot; + if (!!codeBlock) + newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); + + // Ensure that we have the needed stack capacity to push the new frame: + if (!grow(newEnd)) + return false; + + return true; } -inline void JSStack::shrink(Register* newTopOfStack) +inline CallFrame* JSStack::pushFrame(class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) +{ + ASSERT(!!scope); + Register* oldEnd = getTopOfStack(); + + // Ensure that we have enough space for the parameters: + size_t paddedArgsCount = argsCount; + if (codeBlock) { + size_t numParameters = codeBlock->numParameters(); + if (paddedArgsCount < numParameters) + paddedArgsCount = numParameters; + } + + Register* newCallFrameSlot = oldEnd - paddedArgsCount - (2 * JSStack::CallFrameHeaderSize) + 1; + +#if ENABLE(DEBUG_JSSTACK) + newCallFrameSlot -= JSStack::FenceSize; +#endif + + Register* newEnd = newCallFrameSlot; + if (!!codeBlock) + newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); + + // Ensure that we have the needed stack capacity to push the new frame: + if (!grow(newEnd)) + return 0; + + // Compute the address of the new VM sentinel frame for this invocation: + CallFrame* newVMEntrySentinelFrame = CallFrame::create(newCallFrameSlot + paddedArgsCount + JSStack::CallFrameHeaderSize); + ASSERT(!!newVMEntrySentinelFrame); + + // Compute the address of the new frame for this invocation: + CallFrame* newCallFrame = CallFrame::create(newCallFrameSlot); + ASSERT(!!newCallFrame); + + // The caller frame should always be the real previous frame on the stack, + // and not a potential GlobalExec that was passed in. Point callerFrame to + // the top frame on the stack. + CallFrame* callerFrame = m_topCallFrame; + + // Initialize the VM sentinel frame header: + newVMEntrySentinelFrame->initializeVMEntrySentinelFrame(callerFrame); + + // Initialize the callee frame header: + newCallFrame->init(codeBlock, 0, scope, newVMEntrySentinelFrame, argsCount, callee); + + ASSERT(!!newCallFrame->scope()); + + // Pad additional args if needed: + // Note: we need to subtract 1 from argsCount and paddedArgsCount to + // exclude the this pointer. + for (size_t i = argsCount-1; i < paddedArgsCount-1; ++i) + newCallFrame->setArgument(i, jsUndefined()); + + installFence(newCallFrame, __FUNCTION__, __LINE__); + validateFence(newCallFrame, __FUNCTION__, __LINE__); + installTrapsAfterFrame(newCallFrame); + + // Push the new frame: + m_topCallFrame = newCallFrame; + + return newCallFrame; +} + +inline void JSStack::popFrame(CallFrame* frame) +{ + validateFence(frame, __FUNCTION__, __LINE__); + + // Pop off the callee frame and the sentinel frame. + CallFrame* callerFrame = frame->callerFrame()->vmEntrySentinelCallerFrame(); + + // Pop to the caller: + m_topCallFrame = callerFrame; + + // If we are popping the very first frame from the stack i.e. no more + // frames before this, then we can now safely shrink the stack. In + // this case, we're shrinking all the way to the beginning since there + // are no more frames on the stack. + if (!callerFrame) + shrink(getBaseOfStack()); + + installTrapsAfterFrame(callerFrame); +} + +inline void JSStack::shrink(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return; - setStackLimit(newTopOfStack); - // Note: Clang complains of an unresolved linkage to maxExcessCapacity if - // invoke std::max() with it as an argument. To work around this, we first - // assign the constant to a local variable, and use the local instead. - ptrdiff_t maxExcessCapacity = JSStack::maxExcessCapacity; - ptrdiff_t maxExcessInRegisters = std::max(maxExcessCapacity, m_reservedZoneSizeInRegisters); - if (m_end == baseOfStack() && (highAddress() - m_commitTop) >= maxExcessInRegisters) + updateStackLimit(newEnd); + if (m_end == getBaseOfStack() && (m_commitEnd - getBaseOfStack()) >= maxExcessCapacity) releaseExcessCapacity(); } -inline bool JSStack::grow(Register* newTopOfStack) +inline bool JSStack::grow(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return true; - return growSlowCase(newTopOfStack); + return growSlowCase(newEnd); } -inline void JSStack::setStackLimit(Register* newTopOfStack) +inline void JSStack::updateStackLimit(Register* newEnd) { - Register* newEnd = newTopOfStack - 1; m_end = newEnd; - m_vm.setJSStackLimit(newTopOfStack); +#if USE(SEPARATE_C_AND_JS_STACK) + m_vm.setJSStackLimit(newEnd); +#endif +} + +#if ENABLE(DEBUG_JSSTACK) +inline JSValue JSStack::generateFenceValue(size_t argIndex) +{ + unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); + JSValue fenceValue = JSValue(fenceBits); + return fenceValue; } -#endif // !ENABLE(JIT) +// The JSStack fences mechanism works as follows: +// 1. A fence is a number (JSStack::FenceSize) of JSValues that are initialized +// with values generated by JSStack::generateFenceValue(). +// 2. When pushFrame() is called, the fence is installed after the max extent +// of the previous topCallFrame and the last arg of the new frame: +// +// | ... | +// |--------------------------------------| +// | Frame Header of previous frame | +// |--------------------------------------| +// topCallFrame --> | | +// | Locals of previous frame | +// |--------------------------------------| +// | *** the Fence *** | +// |--------------------------------------| +// | VM entry sentinel frame header | +// |--------------------------------------| +// | Args of new frame | +// |--------------------------------------| +// | Frame Header of new frame | +// |--------------------------------------| +// frame --> | Locals of new frame | +// | | +// +// 3. In popFrame() and elsewhere, we can call JSStack::validateFence() to +// assert that the fence contains the values we expect. + +inline void JSStack::installFence(CallFrame* frame, const char *function, int lineNo) +{ + UNUSED_PARAM(function); + UNUSED_PARAM(lineNo); + Register* startOfFrame = getStartOfFrame(frame); + + // The last argIndex is at: + size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; + size_t startIndex = maxIndex - FenceSize; + for (size_t i = startIndex; i < maxIndex; ++i) { + JSValue fenceValue = generateFenceValue(i); + frame->setArgument(i, fenceValue); + } +} + +inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) +{ + UNUSED_PARAM(function); + UNUSED_PARAM(lineNo); + ASSERT(!!frame->scope()); + Register* startOfFrame = getStartOfFrame(frame); + size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; + size_t startIndex = maxIndex - FenceSize; + for (size_t i = startIndex; i < maxIndex; ++i) { + JSValue fenceValue = generateFenceValue(i); + JSValue actualValue = frame->getArgumentUnsafe(i); + ASSERT(fenceValue == actualValue); + } +} + +// When debugging the JSStack, we install bad values after the extent of the +// topCallFrame at the end of pushFrame() and popFrame(). The intention is +// to trigger crashes in the event that memory in this supposedly unused +// region is read and consumed without proper initialization. After the trap +// words are installed, the stack looks like this: +// +// | ... | +// |-----------------------------| +// | Frame Header of frame | +// |-----------------------------| +// topCallFrame --> | | +// | Locals of frame | +// |-----------------------------| +// | *** Trap words *** | +// |-----------------------------| +// | Unused space ... | +// | ... | + +inline void JSStack::installTrapsAfterFrame(CallFrame* frame) +{ + Register* topOfFrame = getTopOfFrame(frame); + const int sizeOfTrap = 64; + int32_t* startOfTrap = reinterpret_cast<int32_t*>(topOfFrame); + int32_t* endOfTrap = startOfTrap - sizeOfTrap; + int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(m_commitEnd); + + // Make sure we're not exceeding the amount of available memory to write to: + if (endOfTrap < endOfCommitedMemory) + endOfTrap = endOfCommitedMemory; + + // Lay the traps: + int32_t* p = startOfTrap; + while (p > endOfTrap) + *p-- = 0xabadcafe; // A bad word to trigger a crash if deref'ed. +} +#endif // ENABLE(DEBUG_JSSTACK) } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp index eb80b2c23..a36001437 100644 --- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp @@ -27,25 +27,23 @@ #include "ProtoCallFrame.h" #include "CodeBlock.h" -#include "JSCInlines.h" -#include "StackAlignment.h" namespace JSC { -void ProtoCallFrame::init(CodeBlock* codeBlock, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs) +void ProtoCallFrame::init(CodeBlock* codeBlock, JSScope* scope, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs) { this->args = otherArgs; this->setCodeBlock(codeBlock); + this->setScope(scope); this->setCallee(callee); this->setArgumentCountIncludingThis(argCountIncludingThis); - if (codeBlock && argCountIncludingThis < codeBlock->numParameters()) - this->arityMissMatch = true; - else - this->arityMissMatch = false; - - // Round up argCountIncludingThis to keep the stack frame size aligned. - size_t paddedArgsCount = roundArgumentCountToAlignFrame(argCountIncludingThis); - this->setPaddedArgCount(paddedArgsCount); + size_t paddedArgsCount = argCountIncludingThis; + if (codeBlock) { + size_t numParameters = codeBlock->numParameters(); + if (paddedArgsCount < numParameters) + paddedArgsCount = numParameters; + } + this->setPaddedArgsCount(paddedArgsCount); this->clearCurrentVPC(); this->setThisValue(thisValue); } diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h index af33a3072..b7151eeff 100644 --- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h @@ -32,33 +32,34 @@ namespace JSC { struct ProtoCallFrame { Register codeBlockValue; + Register scopeChainValue; Register calleeValue; Register argCountAndCodeOriginValue; Register thisArg; - uint32_t paddedArgCount; - bool arityMissMatch; + size_t paddedArgCount; JSValue *args; - void init(CodeBlock*, JSObject*, JSValue, int, JSValue* otherArgs = 0); + void init(CodeBlock*, JSScope*, JSObject*, JSValue, int, JSValue* otherArgs = 0); CodeBlock* codeBlock() const { return codeBlockValue.Register::codeBlock(); } void setCodeBlock(CodeBlock* codeBlock) { codeBlockValue = codeBlock; } - JSObject* callee() const { return calleeValue.Register::object(); } - void setCallee(JSObject* callee) { calleeValue = callee; } + JSScope* scope() const { return scopeChainValue.Register::scope(); } + void setScope(JSScope* scope) { scopeChainValue = scope; } + + JSObject* callee() const { return calleeValue.Register::function(); } + void setCallee(JSObject* callee) { calleeValue = Register::withCallee(callee); } int argumentCountIncludingThis() const { return argCountAndCodeOriginValue.payload(); } int argumentCount() const { return argumentCountIncludingThis() - 1; } void setArgumentCountIncludingThis(int count) { argCountAndCodeOriginValue.payload() = count; } - void setPaddedArgCount(uint32_t argCount) { paddedArgCount = argCount; } + void setPaddedArgsCount(size_t argCount) { paddedArgCount = argCount; } void clearCurrentVPC() { argCountAndCodeOriginValue.tag() = 0; } JSValue thisValue() const { return thisArg.Register::jsValue(); } void setThisValue(JSValue value) { thisArg = value; } - bool needArityCheck() { return arityMissMatch; } - JSValue argument(size_t argumentIndex) { ASSERT(static_cast<int>(argumentIndex) < argumentCount()); diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h index 71ead7d2f..13a7e5866 100644 --- a/Source/JavaScriptCore/interpreter/Register.h +++ b/Source/JavaScriptCore/interpreter/Register.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -37,8 +37,9 @@ namespace JSC { class CodeBlock; class ExecState; - class JSLexicalEnvironment; + class JSActivation; class JSObject; + class JSPropertyNameIterator; class JSScope; typedef ExecState CallFrame; @@ -51,19 +52,18 @@ namespace JSC { Register(const JSValue&); Register& operator=(const JSValue&); JSValue jsValue() const; - JSValue asanUnsafeJSValue() const; EncodedJSValue encodedJSValue() const; Register& operator=(CallFrame*); Register& operator=(CodeBlock*); Register& operator=(JSScope*); - Register& operator=(JSObject*); int32_t i() const; - JSLexicalEnvironment* lexicalEnvironment() const; + JSActivation* activation() const; CallFrame* callFrame() const; CodeBlock* codeBlock() const; - JSObject* object() const; + JSObject* function() const; + JSPropertyNameIterator* propertyNameIterator() const; JSScope* scope() const; int32_t unboxedInt32() const; int64_t unboxedInt52() const; @@ -82,6 +82,8 @@ namespace JSC { return r; } + static Register withCallee(JSObject* callee); + private: union { EncodedJSValue value; @@ -111,12 +113,6 @@ namespace JSC { return *this; } - // FIXME (rdar://problem/19379214): ASan only needs to be suppressed for Register::jsValue() when called from prepareOSREntry(), but there is currently no way to express this short of adding a separate copy of the function. - SUPPRESS_ASAN ALWAYS_INLINE JSValue Register::asanUnsafeJSValue() const - { - return JSValue::decode(u.value); - } - ALWAYS_INLINE JSValue Register::jsValue() const { return JSValue::decode(u.value); diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp index 6fe792b7e..d922e7f8f 100644 --- a/Source/JavaScriptCore/interpreter/StackVisitor.cpp +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,11 +26,11 @@ #include "config.h" #include "StackVisitor.h" +#include "Arguments.h" #include "CallFrameInlines.h" -#include "ClonedArguments.h" #include "Executable.h" #include "Interpreter.h" -#include "JSCInlines.h" +#include "Operations.h" #include <wtf/DataLog.h> namespace JSC { @@ -38,20 +38,7 @@ namespace JSC { StackVisitor::StackVisitor(CallFrame* startFrame) { m_frame.m_index = 0; - CallFrame* topFrame; - if (startFrame) { - m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame; - topFrame = startFrame->vm().topCallFrame; - } else { - m_frame.m_VMEntryFrame = 0; - topFrame = 0; - } - m_frame.m_callerIsVMEntryFrame = false; - readFrame(topFrame); - - // Find the frame the caller wants to start unwinding from. - while (m_frame.callFrame() && m_frame.callFrame() != startFrame) - gotoNextFrame(); + readFrame(startFrame); } void StackVisitor::gotoNextFrame() @@ -61,15 +48,15 @@ void StackVisitor::gotoNextFrame() InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller; readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); - return; - } + + } else #endif // ENABLE(DFG_JIT) - m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; - readFrame(m_frame.callerFrame()); + readFrame(m_frame.callerFrame()); } void StackVisitor::readFrame(CallFrame* callFrame) { + ASSERT(!callFrame->isVMEntrySentinel()); if (!callFrame) { m_frame.setToEnd(); return; @@ -117,10 +104,9 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri { m_frame.m_callFrame = callFrame; m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame; - m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame); - m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame; + m_frame.m_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel(); m_frame.m_callee = callFrame->callee(); + m_frame.m_scope = callFrame->scope(); m_frame.m_codeBlock = callFrame->codeBlock(); m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 : codeOrigin ? codeOrigin->bytecodeIndex @@ -141,6 +127,7 @@ static int inlinedFrameOffset(CodeOrigin* codeOrigin) void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) { ASSERT(codeOrigin); + ASSERT(!callFrame->isVMEntrySentinel()); int frameOffset = inlinedFrameOffset(codeOrigin); bool isInlined = !!frameOffset; @@ -149,15 +136,14 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin m_frame.m_callFrame = callFrame; m_frame.m_inlineCallFrame = inlineCallFrame; - if (inlineCallFrame->argumentCountRegister.isValid()) - m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister.offset()).unboxedInt32(); - else - m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); + m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock(); m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); + m_frame.m_scope = callee->scope(); m_frame.m_callee = callee; + ASSERT(m_frame.scope()); ASSERT(m_frame.callee()); // The callerFrame just needs to be non-null to indicate that we @@ -196,7 +182,7 @@ String StackVisitor::Frame::functionName() switch (codeType()) { case CodeType::Eval: - traceLine = ASCIILiteral("eval code"); + traceLine = "eval code"; break; case CodeType::Native: if (callee) @@ -206,7 +192,7 @@ String StackVisitor::Frame::functionName() traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); break; case CodeType::Global: - traceLine = ASCIILiteral("global code"); + traceLine = "global code"; break; } return traceLine.isNull() ? emptyString() : traceLine; @@ -226,7 +212,7 @@ String StackVisitor::Frame::sourceURL() break; } case CodeType::Native: - traceLine = ASCIILiteral("[native code]"); + traceLine = "[native code]"; break; } return traceLine.isNull() ? emptyString() : traceLine; @@ -255,26 +241,48 @@ String StackVisitor::Frame::toString() return traceBuild.toString().impl(); } -ClonedArguments* StackVisitor::Frame::createArguments() +Arguments* StackVisitor::Frame::createArguments() { ASSERT(m_callFrame); CallFrame* physicalFrame = m_callFrame; - ClonedArguments* arguments; - ArgumentsMode mode; - if (Options::enableFunctionDotArguments()) - mode = ArgumentsMode::Cloned; - else - mode = ArgumentsMode::FakeValues; + VM& vm = physicalFrame->vm(); + Arguments* arguments; #if ENABLE(DFG_JIT) if (isInlinedFrame()) { ASSERT(m_inlineCallFrame); - arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode); + arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame); + arguments->tearOff(physicalFrame, m_inlineCallFrame); } else #endif - arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode); + { + arguments = Arguments::create(vm, physicalFrame); + arguments->tearOff(physicalFrame); + } return arguments; } +Arguments* StackVisitor::Frame::existingArguments() +{ + if (codeBlock()->codeType() != FunctionCode) + return 0; + if (!codeBlock()->usesArguments()) + return 0; + + VirtualRegister reg; + +#if ENABLE(DFG_JIT) + if (isInlinedFrame()) + reg = inlineCallFrame()->argumentsRegister; + else +#endif // ENABLE(DFG_JIT) + reg = codeBlock()->argumentsRegister(); + + JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue(); + if (!result) + return 0; + return jsCast<Arguments*>(result); +} + void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) { CodeBlock* codeBlock = this->codeBlock(); @@ -291,11 +299,8 @@ void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) unsigned divotColumn = 0; retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); - line = divotLine + codeBlock->ownerExecutable()->firstLine(); + line = divotLine + codeBlock->ownerExecutable()->lineNo(); column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); - - if (codeBlock->ownerExecutable()->hasOverrideLineNumber()) - line = codeBlock->ownerExecutable()->overrideLineNumber(); } void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) @@ -313,106 +318,157 @@ void StackVisitor::Frame::setToEnd() #endif } +#ifndef NDEBUG + +static const char* jitTypeName(JITCode::JITType jitType) +{ + switch (jitType) { + case JITCode::None: return "None"; + case JITCode::HostCallThunk: return "HostCallThunk"; + case JITCode::InterpreterThunk: return "InterpreterThunk"; + case JITCode::BaselineJIT: return "BaselineJIT"; + case JITCode::DFGJIT: return "DFGJIT"; + case JITCode::FTLJIT: return "FTLJIT"; + } + return "<unknown>"; +} + static void printIndents(int levels) { while (levels--) dataLogFString(" "); } -template<typename... Types> -void log(unsigned indent, const Types&... values) +static void printif(int indentLevels, const char* format, ...) { - printIndents(indent); - dataLog(values...); -} + va_list argList; + va_start(argList, format); -template<typename... Types> -void logF(unsigned indent, const char* format, const Types&... values) -{ - printIndents(indent); + if (indentLevels) + printIndents(indentLevels); -#if COMPILER(GCC_OR_CLANG) +#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wmissing-format-attribute" #endif - dataLogF(format, values...); + WTF::dataLogFV(format, argList); -#if COMPILER(GCC_OR_CLANG) +#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) #pragma GCC diagnostic pop #endif + + va_end(argList); } -void StackVisitor::Frame::print(int indent) +void StackVisitor::Frame::print(int indentLevel) { + int i = indentLevel; + if (!this->callFrame()) { - log(indent, "frame 0x0\n"); + printif(i, "frame 0x0\n"); return; } CodeBlock* codeBlock = this->codeBlock(); - logF(indent, "frame %p {\n", this->callFrame()); + printif(i, "frame %p {\n", this->callFrame()); - { - indent++; - - CallFrame* callFrame = m_callFrame; - CallFrame* callerFrame = this->callerFrame(); - void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; + CallFrame* callFrame = m_callFrame; + CallFrame* callerFrame = this->callerFrame(); + void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; - log(indent, "name: ", functionName(), "\n"); - log(indent, "sourceURL: ", sourceURL(), "\n"); + printif(i, " name '%s'\n", functionName().utf8().data()); + printif(i, " sourceURL '%s'\n", sourceURL().utf8().data()); + printif(i, " isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel()); - bool isInlined = false; #if ENABLE(DFG_JIT) - isInlined = isInlinedFrame(); - log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); - if (isInlinedFrame()) - logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame); + printif(i, " isInlinedFrame %d\n", isInlinedFrame()); + if (isInlinedFrame()) + printif(i, " InlineCallFrame %p\n", m_inlineCallFrame); #endif - logF(indent, "callee: %p\n", callee()); - logF(indent, "returnPC: %p\n", returnPC); - logF(indent, "callerFrame: %p\n", callerFrame); - unsigned locationRawBits = callFrame->locationAsRawBits(); - logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits); - logF(indent, "codeBlock: %p ", codeBlock); - if (codeBlock) - dataLog(*codeBlock); - dataLog("\n"); - if (codeBlock && !isInlined) { - indent++; - - if (callFrame->hasLocationAsBytecodeOffset()) { - unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); - log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); + printif(i, " callee %p\n", callee()); + printif(i, " returnPC %p\n", returnPC); + printif(i, " callerFrame %p\n", callerFrame); + unsigned locationRawBits = callFrame->locationAsRawBits(); + printif(i, " rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits); + printif(i, " codeBlock %p\n", codeBlock); + if (codeBlock) { + JITCode::JITType jitType = codeBlock->jitType(); + if (callFrame->hasLocationAsBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); + printif(i, " bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size()); #if ENABLE(DFG_JIT) - } else { - log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); - if (codeBlock->hasCodeOrigins()) { - unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); - log(indent, "codeOriginIndex: ", codeOriginIndex, " of ", codeBlock->codeOrigins().size(), "\n"); - - JITCode::JITType jitType = codeBlock->jitType(); - if (jitType != JITCode::FTLJIT) { - JITCode* jitCode = codeBlock->jitCode().get(); - logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); - } - } + } else { + unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); + printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size()); #endif - } - unsigned line = 0; - unsigned column = 0; - computeLineAndColumn(line, column); - log(indent, "line: ", line, "\n"); - log(indent, "column: ", column, "\n"); - - indent--; } - indent--; + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + printif(i, " line %d\n", line); + printif(i, " column %d\n", column); + printif(i, " jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType)); +#if ENABLE(DFG_JIT) + printif(i, " hasCodeOrigins %d\n", codeBlock->hasCodeOrigins()); + if (codeBlock->hasCodeOrigins()) { + JITCode* jitCode = codeBlock->jitCode().get(); + printif(i, " jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); + } +#endif } - log(indent, "}\n"); + printif(i, "}\n"); } +#endif // NDEBUG + } // namespace JSC + +#ifndef NDEBUG +using JSC::StackVisitor; + +// For debugging use +JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*); +JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame); + +class DebugPrintFrameFunctor { +public: + enum Action { + PrintOne, + PrintAll + }; + + DebugPrintFrameFunctor(Action action) + : m_action(action) + { + } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + visitor->print(2); + return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done; + } + +private: + Action m_action; +}; + +void debugPrintCallFrame(JSC::CallFrame* callFrame) +{ + if (!callFrame) + return; + DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne); + callFrame->iterate(functor); +} + +void debugPrintStack(JSC::CallFrame* topCallFrame) +{ + if (!topCallFrame) + return; + DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll); + topCallFrame->iterate(functor); +} + +#endif // !NDEBUG diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.h b/Source/JavaScriptCore/interpreter/StackVisitor.h index 0036a789c..990a226b3 100644 --- a/Source/JavaScriptCore/interpreter/StackVisitor.h +++ b/Source/JavaScriptCore/interpreter/StackVisitor.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,7 +26,6 @@ #ifndef StackVisitor_h #define StackVisitor_h -#include "VMEntryRecord.h" #include <wtf/text/WTFString.h> namespace JSC { @@ -34,12 +33,12 @@ namespace JSC { struct CodeOrigin; struct InlineCallFrame; +class Arguments; class CodeBlock; class ExecState; class JSFunction; class JSObject; class JSScope; -class ClonedArguments; class Register; typedef ExecState CallFrame; @@ -57,9 +56,9 @@ public: size_t index() const { return m_index; } size_t argumentCountIncludingThis() const { return m_argumentCountIncludingThis; } - bool callerIsVMEntryFrame() const { return m_callerIsVMEntryFrame; } CallFrame* callerFrame() const { return m_callerFrame; } JSObject* callee() const { return m_callee; } + JSScope* scope() const { return m_scope; } CodeBlock* codeBlock() const { return m_codeBlock; } unsigned bytecodeOffset() const { return m_bytecodeOffset; } #if ENABLE(DFG_JIT) @@ -78,11 +77,13 @@ public: CodeType codeType() const; JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); - ClonedArguments* createArguments(); - VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; } + Arguments* createArguments(); + Arguments* existingArguments(); CallFrame* callFrame() const { return m_callFrame; } +#ifndef NDEBUG JS_EXPORT_PRIVATE void print(int indentLevel); +#endif private: Frame() { } @@ -93,13 +94,11 @@ public: size_t m_index; size_t m_argumentCountIncludingThis; - VMEntryFrame* m_VMEntryFrame; - VMEntryFrame* m_CallerVMEntryFrame; CallFrame* m_callerFrame; JSObject* m_callee; + JSScope* m_scope; CodeBlock* m_codeBlock; unsigned m_bytecodeOffset; - bool m_callerIsVMEntryFrame; #if ENABLE(DFG_JIT) InlineCallFrame* m_inlineCallFrame; #endif @@ -145,32 +144,6 @@ private: Frame m_frame; }; -class CallerFunctor { -public: - CallerFunctor() - : m_hasSkippedFirstFrame(false) - , m_callerFrame(0) - { - } - - CallFrame* callerFrame() const { return m_callerFrame; } - - StackVisitor::Status operator()(StackVisitor& visitor) - { - if (!m_hasSkippedFirstFrame) { - m_hasSkippedFirstFrame = true; - return StackVisitor::Continue; - } - - m_callerFrame = visitor->callFrame(); - return StackVisitor::Done; - } - -private: - bool m_hasSkippedFirstFrame; - CallFrame* m_callerFrame; -}; - } // namespace JSC #endif // StackVisitor_h diff --git a/Source/JavaScriptCore/interpreter/VMEntryRecord.h b/Source/JavaScriptCore/interpreter/VMEntryRecord.h deleted file mode 100644 index 50ac65f78..000000000 --- a/Source/JavaScriptCore/interpreter/VMEntryRecord.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2014 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef VMEntryRecord_h -#define VMEntryRecord_h - -namespace JSC { - -typedef void VMEntryFrame; - -class ExecState; -class VM; - -struct VMEntryRecord { - /* - * This record stored in a vmEntryTo{JavaScript,Host} allocated frame. It is allocated on the stack - * after callee save registers where local variables would go. - */ - VM* m_vm; - ExecState* m_prevTopCallFrame; - VMEntryFrame* m_prevTopVMEntryFrame; - - ExecState* prevTopCallFrame() { return m_prevTopCallFrame; } - - VMEntryFrame* prevTopVMEntryFrame() { return m_prevTopVMEntryFrame; } -}; - -extern "C" VMEntryRecord* vmEntryRecord(VMEntryFrame*); - -} // namespace JSC - -#endif // VMEntryRecord_h diff --git a/Source/JavaScriptCore/interpreter/VMInspector.cpp b/Source/JavaScriptCore/interpreter/VMInspector.cpp new file mode 100644 index 000000000..fbb49413d --- /dev/null +++ b/Source/JavaScriptCore/interpreter/VMInspector.cpp @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "VMInspector.h" + +#if ENABLE(VMINSPECTOR) + +#include <wtf/ASCIICType.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +const char* VMInspector::getTypeName(JSValue value) +{ + if (value.isInt32()) + return "<Int32>"; + if (value.isBoolean()) + return "<Boolean>"; + if (value.isNull()) + return "<Empty>"; + if (value.isUndefined()) + return "<Undefined>"; + if (value.isCell()) + return "<Cell>"; + if (value.isEmpty()) + return "<Empty>"; + return ""; +} + +void VMInspector::dumpFrame0(CallFrame* frame) +{ + dumpFrame(frame, 0, 0, 0, 0); +} + +void VMInspector::dumpFrame(CallFrame* frame, const char* prefix, + const char* funcName, const char* file, int line) +{ + int frameCount = VMInspector::countFrames(frame); + if (frameCount < 0) + return; + + Instruction* vPC = 0; + if (frame->codeBlock()) + vPC = frame->currentVPC(); + + #define CAST reinterpret_cast + + if (prefix) + printf("%s ", prefix); + + printf("frame [%d] %p { cb %p:%s, retPC %p:%s, scope %p:%s, callee %p:%s, callerFrame %p:%s, argc %d, vPC %p }", + frameCount, frame, + CAST<void*>(frame[JSStack::CodeBlock].payload()), + getTypeName(frame[JSStack::CodeBlock].jsValue()), + CAST<void*>(frame[JSStack::ReturnPC].payload()), + getTypeName(frame[JSStack::ReturnPC].jsValue()), + CAST<void*>(frame[JSStack::ScopeChain].payload()), + getTypeName(frame[JSStack::ScopeChain].jsValue()), + CAST<void*>(frame[JSStack::Callee].payload()), + getTypeName(frame[JSStack::Callee].jsValue()), + CAST<void*>(frame[JSStack::CallerFrame].callFrame()), + getTypeName(frame[JSStack::CallerFrame].jsValue()), + frame[JSStack::ArgumentCount].payload(), + vPC); + + if (funcName || file || (line >= 0)) { + printf(" @"); + if (funcName) + printf(" %s", funcName); + if (file) + printf(" %s", file); + if (line >= 0) + printf(":%d", line); + } + printf("\n"); +} + +int VMInspector::countFrames(CallFrame* frame) +{ + int count = -1; + while (frame && !frame->isVMEntrySentinel()) { + count++; + frame = frame->callerFrame(); + } + return count; +} + + +//============================================================================ +// class FormatPrinter +// - implements functionality to support fprintf. +// +// The FormatPrinter classes do the real formatting and printing. +// By default, the superclass FormatPrinter will print to stdout (printf). +// Each of the subclass will implement the other ...printf() options. +// The subclasses are: +// +// FileFormatPrinter - fprintf +// StringFormatPrinter - sprintf +// StringNFormatPrinter - snprintf + +class FormatPrinter { +public: + virtual ~FormatPrinter() { } + + void print(const char* format, va_list args); + +protected: + // Low level printers: + bool printArg(const char* format, ...); + virtual bool printArg(const char* format, va_list args); + + // JS type specific printers: + void printWTFString(va_list args, bool verbose); +}; + + +// The public print() function is the real workhorse behind the printf +// family of functions. print() deciphers the % formatting, translate them +// to primitive formats, and dispatches to underlying printArg() functions +// to do the printing. +// +// The non-public internal printArg() function is virtual and is responsible +// for handling the variations between printf, fprintf, sprintf, and snprintf. + +void FormatPrinter::print(const char* format, va_list args) +{ + const char* p = format; + const char* errorStr; + + // buffer is only used for 2 purposes: + // 1. To temporarily hold a copy of normal chars (not needing formatting) + // to be passed to printArg() and printed. + // + // The incoming format string may contain a string of normal chars much + // longer than 128, but we handle this by breaking them out to 128 chars + // fragments and printing each fragment before re-using the buffer to + // load up the next fragment. + // + // 2. To hold a single "%..." format to be passed to printArg() to process + // a single va_arg. + + char buffer[129]; // 128 chars + null terminator. + char* end = &buffer[sizeof(buffer) - 1]; + const char* startOfFormatSpecifier = 0; + + while (true) { + char c = *p++; + char* curr = buffer; + + // Print leading normal chars: + while (c != '\0' && c != '%') { + *curr++ = c; + if (curr == end) { + // Out of buffer space. Flush the fragment, and start over. + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + curr = buffer; + } + c = *p++; + } + // If we have stuff in the buffer, flush the fragment: + if (curr != buffer) { + ASSERT(curr < end + 1); + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + } + + // End if there are not more chars to print: + if (c == '\0') + break; + + // If we get here, we've must have seen a '%': + startOfFormatSpecifier = p - 1; + ASSERT(*startOfFormatSpecifier == '%'); + c = *p++; + + // Check for "%%" case: + if (c == '%') { + bool success = printArg("%c", '%'); + if (!success) { + errorStr = p - 2; + goto handleError; + } + continue; + } + + // Check for JS (%J<x>) formatting extensions: + if (c == 'J') { + bool verbose = false; + + c = *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 2; // Rewind to % in "%J\0" + goto handleError; + } + + if (c == '+') { + verbose = true; + c= *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 3; // Rewind to % in "%J+\0" + goto handleError; + } + } + + switch (c) { + // %Js - WTF::String* + case 's': { + printWTFString(args, verbose); + continue; + } + } // END switch. + + // Check for non-JS extensions: + } else if (c == 'b') { + int value = va_arg(args, int); + printArg("%s", value ? "TRUE" : "FALSE"); + continue; + } + + // If we didn't handle the format in one of the above cases, + // rewind p and let the standard formatting check handle it + // if possible: + p = startOfFormatSpecifier; + ASSERT(*p == '%'); + + // Check for standard formatting: + // A format specifier always starts with a % and ends with some + // alphabet. We'll do the simple thing and scan until the next + // alphabet, or the end of string. + + // In the following, we're going to use buffer as storage for a copy + // of a single format specifier. Hence, conceptually, we can think of + // 'buffer' as synonymous with 'argFormat' here: + +#define ABORT_IF_FORMAT_TOO_LONG(curr) \ + do { \ + if (UNLIKELY(curr >= end)) \ + goto formatTooLong; \ + } while (false) + + curr = buffer; + *curr++ = *p++; // Output the first % in the format specifier. + c = *p++; // Grab the next char in the format specifier. + + // Checks for leading modifiers e.g. "%-d": + // 0, -, ' ', +, '\'' + if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for decimal digit field width modifiers e.g. "%2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for '.' e.g. "%2.f": + if (c == '.') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + + // Checks for decimal digit precision modifiers e.g. "%.2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for the modifier <m> where <m> can be: + // l, h, j, t, z + // e.g. "%ld" + if (c == 'l' || c == 'h' || c == 'j' || c == 't' || c == 'z' || c == 'L') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + char prevChar = c; + c = *p++; + + // Checks for the modifier ll or hh in %<x><m>: + if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for %<x> where <x> can be: + // d, i, n, o, u, x, X + // But hey, we're just going to do the simple thing and allow any + // alphabet. The user is expected to pass correct format specifiers. + // We won't do any format checking here. We'll just pass it on, and the + // underlying ...printf() implementation may do the needed checking + // at its discretion. + while (c != '\0' && !isASCIIAlpha(c)) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + if (c == '\0') { + // Uh oh. Bad format. We should have gotten an alphabet instead. + // Print the supposed format as a string instead: + errorStr = buffer; + goto handleError; + } + + // Otherwise, we have the alpha that terminates the format. + // Terminate the buffer (i.e. argFormat) string: + ASSERT(isASCIIAlpha(c)); + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr = '\0'; + + bool success = printArg(buffer, args); + if (!success) { + errorStr = buffer; + goto handleError; + } + } +#undef ABORT_IF_FORMAT_TOO_LONG + + return; + +formatTooLong: + // Print the error string: + ASSERT(!!startOfFormatSpecifier); + p = startOfFormatSpecifier; + ASSERT(p >= format); + printArg("ERROR @ Format too long at \"%s\"\n", p); + return; + +handleError: + // We've got an error. Can't do any more work. Print an error message if + // possible and then just return. + + // The errorStr may be pointing into the middle of buffer, or the original + // format string. Move the string to buffer for consistency, and also so + // that we can strip it of newlines below. + if (errorStr != buffer) { + size_t length = strlen(errorStr); + if (length > sizeof(buffer) - 1) + length = sizeof(buffer) - 1; + memmove(buffer, errorStr, length); + buffer[length] = '\0'; // Terminate the moved error string. + } + // Strip the newlines: + char* cp = buffer; + while (*cp) { + if (*cp == '\n' || *cp == '\r') + *cp = ' '; + cp++; + } + // Print the error string: + printArg("ERROR @ \"%s\"\n", buffer); +} + + +bool FormatPrinter::printArg(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool success = printArg(format, args); + va_end(args); + return success; +} + +bool FormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vprintf(format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +// %Js - WTF::String* +// verbose mode prints: WTF::String "<your string>" +void FormatPrinter::printWTFString(va_list args, bool verbose) +{ + const String* str = va_arg(args, const String*); + + // Print verbose header if appropriate: + if (verbose) + printArg("WTF::String \""); + + // Print the string itself: + if (!str->isEmpty()) { + if (str->is8Bit()) { + const LChar* chars = str->characters8(); + printArg("%s", reinterpret_cast<const char*>(chars)); + } else { + const UChar* chars = str->characters16(); + printArg("%S", reinterpret_cast<const wchar_t*>(chars)); + } + } + + // Print verbose footer if appropriate: + if (verbose) + printArg("\""); +} + + +//============================================================================ +// class FileFormatPrinter +// - implements functionality to support fprintf. + +class FileFormatPrinter: public FormatPrinter { +public: + FileFormatPrinter(FILE*); +private: + virtual bool printArg(const char* format, va_list args); + + FILE* m_file; +}; + +FileFormatPrinter::FileFormatPrinter(FILE* file) + : m_file(file) +{ +} + +bool FileFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vfprintf(m_file, format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringFormatPrinter +// - implements functionality to support sprintf. + +class StringFormatPrinter: public FormatPrinter { +public: + StringFormatPrinter(char* buffer); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; +}; + +StringFormatPrinter::StringFormatPrinter(char* buffer) + : m_buffer(buffer) +{ +} + +bool StringFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vsprintf(m_buffer, format, args); + m_buffer += count; + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringNFormatPrinter +// - implements functionality to support snprintf. + +class StringNFormatPrinter: public FormatPrinter { +public: + StringNFormatPrinter(char* buffer, size_t); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; + size_t m_size; +}; + + +StringNFormatPrinter::StringNFormatPrinter(char* buffer, size_t size) + : m_buffer(buffer) + , m_size(size) +{ +} + +bool StringNFormatPrinter::printArg(const char* format, va_list args) +{ + if (m_size > 0) { + int count = ::vsnprintf(m_buffer, m_size, format, args); + + // According to vsnprintf specs, ... + bool success = (count >= 0); + if (static_cast<size_t>(count) >= m_size) { + // If count > size, then we didn't have enough buffer space. + count = m_size; + } + + // Adjust the buffer to what's left if appropriate: + if (success) { + m_buffer += count; + m_size -= count; + } + return success; + } + // No more room to print. Declare it a fail: + return false; +} + + +//============================================================================ +// VMInspector printf family of methods: + +void VMInspector::fprintf(FILE* file, const char* format, ...) +{ + va_list args; + va_start(args, format); + FileFormatPrinter(file).print(format, args); + va_end(args); +} + +void VMInspector::printf(const char* format, ...) +{ + va_list args; + va_start(args, format); + FormatPrinter().print(format, args); + va_end(args); +} + +void VMInspector::sprintf(char* buffer, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringFormatPrinter(buffer).print(format, args); + va_end(args); +} + +void VMInspector::snprintf(char* buffer, size_t size, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringNFormatPrinter(buffer, size).print(format, args); + va_end(args); +} + +} // namespace JSC + +#endif // ENABLE(VMINSPECTOR) diff --git a/Source/JavaScriptCore/interpreter/VMInspector.h b/Source/JavaScriptCore/interpreter/VMInspector.h new file mode 100644 index 000000000..6623068dc --- /dev/null +++ b/Source/JavaScriptCore/interpreter/VMInspector.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VMInspector_h +#define VMInspector_h + +#define ENABLE_VMINSPECTOR 0 + +#include "CallFrame.h" +#include "JSCJSValue.h" +#include <stdarg.h> +#include <stdio.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +#if ENABLE(VMINSPECTOR) + +class VMInspector { +public: + static JS_EXPORT_PRIVATE const char* getTypeName(JSValue); + static JS_EXPORT_PRIVATE void dumpFrame0(CallFrame*); + static JS_EXPORT_PRIVATE void dumpFrame(CallFrame*, const char* prefix = 0, const char* funcName = 0, const char* file = 0, int line = -1); + static JS_EXPORT_PRIVATE int countFrames(CallFrame*); + + // Special family of ...printf() functions that support, in addition to the + // standard % formats (e.g. %d, %s, etc), the following extra JSC formatting + // options, %J<x>, where <x> consists of: + // + // + - verbose mode modifier. + // Used in combination with other options. Must come after the %J. + // s - WTF::String* + // + // Examples of usage: + // + // WTF::String str("My WTF String"); + // + // // Printing the string. Will print: + // // The wtf string says: "My WTF String" and is NOT EMPTY. + // + // VMInspector::printf("The wtf string says: \"%Js\" and is %s\n", + // &str, str.isEmpty()?"EMPTY":"NOT EMPTY"); + // + // // Printing the string with verbose mode. Will print: + // // <WTF::String "My WTF String"> + // + // VMInspector::printf("<%J+s>\n", &str); + // + // Also added some convenience non-JS formats: + // + // %b - boolean (va_args will look for an int). + // Prints TRUE if non-zero, else prints FALSE. + // + // Caution: the user is expected to pass the correctly matched arguments + // to pair with the corresponding % fomatting. + + static JS_EXPORT_PRIVATE void fprintf(FILE*, const char* format, ...); + static JS_EXPORT_PRIVATE void printf(const char* format, ...); + static JS_EXPORT_PRIVATE void sprintf(char*, const char* format, ...); + static JS_EXPORT_PRIVATE void snprintf(char*, size_t, const char* format, ...); +}; + +#endif // ENABLE(VMINSPECTOR) + +} // namespace JSC + +#endif // VMInspector.h |
