diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/interpreter | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
19 files changed, 2205 insertions, 2174 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp index 8600b7228..09b24d5dc 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.cpp +++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp @@ -27,9 +27,9 @@ #include "AbstractPC.h" #include "CallFrame.h" -#include "VM.h" #include "JSObject.h" - +#include "JSCInlines.h" +#include "VM.h" namespace JSC { diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h index c30027d9e..ac2aaa7ad 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.h +++ b/Source/JavaScriptCore/interpreter/AbstractPC.h @@ -27,7 +27,6 @@ #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 ddf9a40ad..30cad2a14 100644 --- a/Source/JavaScriptCore/interpreter/CachedCall.h +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -27,9 +27,12 @@ #define CachedCall_h #include "CallFrameClosure.h" +#include "ExceptionHelpers.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Interpreter.h" +#include "ProtoCallFrame.h" +#include "VMEntryScope.h" namespace JSC { class CachedCall { @@ -38,10 +41,14 @@ namespace JSC { CachedCall(CallFrame* callFrame, JSFunction* function, int argumentCount) : m_valid(false) , m_interpreter(callFrame->interpreter()) - , m_globalObjectScope(callFrame->vm(), function->scope()->globalObject()) + , m_entryScope(callFrame->vm(), function->scope()->globalObject()) { - ASSERT(!function->isHostFunction()); - m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, function, argumentCount + 1, function->scope()); + ASSERT(!function->isHostFunctionNonInline()); + 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()); + } else + throwStackOverflowError(callFrame); m_valid = !callFrame->hadException(); } @@ -50,26 +57,15 @@ namespace JSC { ASSERT(m_valid); return m_interpreter->execute(m_closure); } - void setThis(JSValue v) { m_closure.setThis(v); } - void setArgument(int n, JSValue v) { m_closure.setArgument(n, v); } + void setThis(JSValue v) { m_protoCallFrame.setThisValue(v); } + void setArgument(int n, JSValue v) { m_protoCallFrame.setArgument(n, v); } - CallFrame* newCallFrame(ExecState* exec) - { - CallFrame* callFrame = m_closure.newCallFrame; - callFrame->setScope(exec->scope()); - return callFrame; - } - - ~CachedCall() - { - if (m_valid) - m_interpreter->endRepeatCall(m_closure); - } - private: bool m_valid; Interpreter* m_interpreter; - DynamicGlobalObjectScope m_globalObjectScope; + VMEntryScope m_entryScope; + ProtoCallFrame m_protoCallFrame; + Vector<JSValue> m_arguments; CallFrameClosure m_closure; }; } diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp index bb61020ce..3d3897b6b 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2013, 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 @@ -27,23 +27,74 @@ #include "CallFrame.h" #include "CodeBlock.h" +#include "InlineCallFrame.h" #include "Interpreter.h" -#include "Operations.h" +#include "JSLexicalEnvironment.h" +#include "JSCInlines.h" +#include "VMEntryScope.h" +#include <wtf/StringPrintStream.h> namespace JSC { -#ifndef NDEBUG -void CallFrame::dumpCaller() +bool CallFrame::callSiteBitsAreBytecodeOffset() const { - int signedLineNumber; - intptr_t sourceID; - String urlString; - JSValue function; - - interpreter()->retrieveLastCaller(this, signedLineNumber, sourceID, urlString, function); - dataLogF("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber); + ASSERT(codeBlock()); + switch (codeBlock()->jitType()) { + case JITCode::InterpreterThunk: + case JITCode::BaselineJIT: + return true; + case JITCode::None: + case JITCode::HostCallThunk: + RELEASE_ASSERT_NOT_REACHED(); + return false; + default: + return false; + } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +bool CallFrame::callSiteBitsAreCodeOriginIndex() const +{ + ASSERT(codeBlock()); + switch (codeBlock()->jitType()) { + case JITCode::DFGJIT: + case JITCode::FTLJIT: + return true; + case JITCode::None: + case JITCode::HostCallThunk: + RELEASE_ASSERT_NOT_REACHED(); + return false; + default: + return false; + } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +unsigned CallFrame::callSiteAsRawBits() const +{ + return this[JSStack::ArgumentCount].tag(); +} + +SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const +{ + return this[JSStack::ArgumentCount].unsafeTag(); +} + +CallSiteIndex CallFrame::callSiteIndex() const +{ + return CallSiteIndex(callSiteAsRawBits()); +} + +SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const +{ + return CallSiteIndex(unsafeCallSiteAsRawBits()); } +#ifndef NDEBUG JSStack* CallFrame::stack() { return &interpreter()->stack(); @@ -52,170 +103,175 @@ JSStack* CallFrame::stack() #endif #if USE(JSVALUE32_64) -unsigned CallFrame::bytecodeOffsetForNonDFGCode() const +Instruction* CallFrame::currentVPC() const { - ASSERT(codeBlock()); - return currentVPC() - codeBlock()->instructions().begin(); + return bitwise_cast<Instruction*>(callSiteIndex().bits()); } -void CallFrame::setBytecodeOffsetForNonDFGCode(unsigned offset) +void CallFrame::setCurrentVPC(Instruction* vpc) +{ + CallSiteIndex callSite(vpc); + this[JSStack::ArgumentCount].tag() = callSite.bits(); +} + +unsigned CallFrame::callSiteBitsAsBytecodeOffset() const { ASSERT(codeBlock()); - setCurrentVPC(codeBlock()->instructions().begin() + offset); + ASSERT(callSiteBitsAreBytecodeOffset()); + return currentVPC() - codeBlock()->instructions().begin(); } -#else + +#else // USE(JSVALUE32_64) Instruction* CallFrame::currentVPC() const { - return codeBlock()->instructions().begin() + bytecodeOffsetForNonDFGCode(); + ASSERT(callSiteBitsAreBytecodeOffset()); + return codeBlock()->instructions().begin() + callSiteBitsAsBytecodeOffset(); } + void CallFrame::setCurrentVPC(Instruction* vpc) { - setBytecodeOffsetForNonDFGCode(vpc - codeBlock()->instructions().begin()); + CallSiteIndex callSite(vpc - codeBlock()->instructions().begin()); + this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(callSite.bits()); } -#endif - -#if ENABLE(DFG_JIT) -bool CallFrame::isInlineCallFrameSlow() + +unsigned CallFrame::callSiteBitsAsBytecodeOffset() const { - if (!callee()) - return false; - JSCell* calleeAsFunctionCell = getJSFunction(callee()); - if (!calleeAsFunctionCell) - return false; - JSFunction* calleeAsFunction = jsCast<JSFunction*>(calleeAsFunctionCell); - return calleeAsFunction->executable() != codeBlock()->ownerExecutable(); + ASSERT(codeBlock()); + ASSERT(callSiteBitsAreBytecodeOffset()); + return callSiteIndex().bits(); } -CallFrame* CallFrame::trueCallFrame(AbstractPC pc) -{ - // Am I an inline call frame? If so, we're done. - if (isInlineCallFrame()) - return this; - - // If I don't have a code block, then I'm not DFG code, so I'm the true call frame. - CodeBlock* machineCodeBlock = codeBlock(); - if (!machineCodeBlock) - return this; - - // If the code block does not have any code origins, then there was no inlining, so - // I'm done. - if (!machineCodeBlock->hasCodeOrigins()) - return this; - - // At this point the PC must be due either to the DFG, or it must be unset. - ASSERT(pc.hasJITReturnAddress() || !pc); +#endif - // Try to determine the CodeOrigin. If we don't have a pc set then the only way - // that this makes sense is if the CodeOrigin index was set in the call frame. - // FIXME: Note that you will see "Not currently in inlined code" comments below. - // Currently, we do not record code origins for code that is not inlined, because - // the only thing that we use code origins for is determining the inline stack. - // But in the future, we'll want to use this same functionality (having a code - // origin mapping for any calls out of JIT code) to determine the PC at any point - // in the stack even if not in inlined code. When that happens, the code below - // will have to change the way it detects the presence of inlining: it will always - // get a code origin, but sometimes, that code origin will not have an inline call - // frame. In that case, this method should bail and return this. - CodeOrigin codeOrigin; - if (pc.isSet()) { - ReturnAddressPtr currentReturnPC = pc.jitReturnAddress(); - - bool hasCodeOrigin = machineCodeBlock->codeOriginForReturn(currentReturnPC, codeOrigin); - ASSERT(hasCodeOrigin); - if (!hasCodeOrigin) { - // In release builds, if we find ourselves in a situation where the return PC doesn't - // correspond to a valid CodeOrigin, we return zero instead of continuing. Some of - // the callers of trueCallFrame() will be able to recover and do conservative things, - // while others will crash. - return 0; - } - } else { - unsigned index = codeOriginIndexForDFG(); - ASSERT(machineCodeBlock->canGetCodeOrigin(index)); - if (!machineCodeBlock->canGetCodeOrigin(index)) { - // See above. In release builds, we try to protect ourselves from crashing even - // though stack walking will be goofed up. - return 0; +unsigned CallFrame::bytecodeOffset() +{ + if (!codeBlock()) + return 0; +#if ENABLE(DFG_JIT) + if (callSiteBitsAreCodeOriginIndex()) { + ASSERT(codeBlock()); + CodeOrigin codeOrigin = this->codeOrigin(); + for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { + codeOrigin = inlineCallFrame->directCaller; + inlineCallFrame = codeOrigin.inlineCallFrame; } - codeOrigin = machineCodeBlock->codeOrigin(index); + return codeOrigin.bytecodeIndex; } +#endif + ASSERT(callSiteBitsAreBytecodeOffset()); + return callSiteBitsAsBytecodeOffset(); +} - if (!codeOrigin.inlineCallFrame) - return this; // Not currently in inlined code. - - for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { - InlineCallFrame* nextInlineCallFrame = inlineCallFrame->caller.inlineCallFrame; - - CallFrame* inlinedCaller = this + inlineCallFrame->stackOffset; - - JSFunction* calleeAsFunction = inlineCallFrame->callee.get(); - - // Fill in the inlinedCaller - inlinedCaller->setCodeBlock(machineCodeBlock); - if (calleeAsFunction) - inlinedCaller->setScope(calleeAsFunction->scope()); - if (nextInlineCallFrame) - inlinedCaller->setCallerFrame(this + nextInlineCallFrame->stackOffset); - else - inlinedCaller->setCallerFrame(this); - - inlinedCaller->setInlineCallFrame(inlineCallFrame); - inlinedCaller->setArgumentCountIncludingThis(inlineCallFrame->arguments.size()); - if (calleeAsFunction) - inlinedCaller->setCallee(calleeAsFunction); - - inlineCallFrame = nextInlineCallFrame; +CodeOrigin CallFrame::codeOrigin() +{ + if (!codeBlock()) + return CodeOrigin(0); +#if ENABLE(DFG_JIT) + if (callSiteBitsAreCodeOriginIndex()) { + CallSiteIndex index = callSiteIndex(); + ASSERT(codeBlock()->canGetCodeOrigin(index)); + return codeBlock()->codeOrigin(index); } - - return this + codeOrigin.inlineCallFrame->stackOffset; +#endif + return CodeOrigin(callSiteBitsAsBytecodeOffset()); } - -CallFrame* CallFrame::trueCallerFrame() + +Register* CallFrame::topOfFrameInternal() { - if (!codeBlock()) - return callerFrame()->removeHostCallFrameFlag(); - - // this -> The callee; this is either an inlined callee in which case it already has - // a pointer to the true caller. Otherwise it contains current PC in the machine - // caller. - // - // machineCaller -> The caller according to the machine, which may be zero or - // more frames above the true caller due to inlining. - - // Am I an inline call frame? If so, we're done. - if (isInlineCallFrame()) - return callerFrame()->removeHostCallFrameFlag(); - - // I am a machine call frame, so the question is: is my caller a machine call frame - // that has inlines or a machine call frame that doesn't? - CallFrame* machineCaller = callerFrame()->removeHostCallFrameFlag(); - if (!machineCaller) - return 0; - ASSERT(!machineCaller->isInlineCallFrame()); - - // Figure out how we want to get the current code location. - if (!hasReturnPC() || returnAddressIsInCtiTrampoline(returnPC())) - return machineCaller->trueCallFrameFromVMCode()->removeHostCallFrameFlag(); - - return machineCaller->trueCallFrame(returnPC())->removeHostCallFrameFlag(); + CodeBlock* codeBlock = this->codeBlock(); + ASSERT(codeBlock); + return registers() + codeBlock->stackPointerOffset(); } -CodeBlock* CallFrame::someCodeBlockForPossiblyInlinedCode() +JSGlobalObject* CallFrame::vmEntryGlobalObject() { - if (!isInlineCallFrame()) - return codeBlock(); - - return jsCast<FunctionExecutable*>(inlineCallFrame()->executable.get())->baselineCodeBlockFor( - inlineCallFrame()->isCall ? CodeForCall : CodeForConstruct); + if (this == lexicalGlobalObject()->globalExec()) + return lexicalGlobalObject(); + + // For any ExecState that's not a globalExec, the + // dynamic global object must be set since code is running + ASSERT(vm().entryScope); + return vm().entryScope->globalObject(); } -#endif +CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame) +{ + if (callerFrameOrVMEntryFrame() == currVMEntryFrame) { + VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); + currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame(); + return currVMEntryRecord->prevTopCallFrame(); + } + return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); +} -Register* CallFrame::frameExtentInternal() +SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(VMEntryFrame*& currVMEntryFrame) +{ + if (unsafeCallerFrameOrVMEntryFrame() == currVMEntryFrame) { + VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); + currVMEntryFrame = currVMEntryRecord->unsafePrevTopVMEntryFrame(); + return currVMEntryRecord->unsafePrevTopCallFrame(); + } + return static_cast<CallFrame*>(unsafeCallerFrameOrVMEntryFrame()); +} + +String CallFrame::friendlyFunctionName() { CodeBlock* codeBlock = this->codeBlock(); - ASSERT(codeBlock); - return registers() + codeBlock->m_numCalleeRegisters; + if (!codeBlock) + return emptyString(); + + switch (codeBlock->codeType()) { + case EvalCode: + return ASCIILiteral("eval code"); + case ModuleCode: + return ASCIILiteral("module code"); + case GlobalCode: + return ASCIILiteral("global code"); + case FunctionCode: + if (callee()) + return getCalculatedDisplayName(this, callee()); + return emptyString(); + } + + ASSERT_NOT_REACHED(); + return emptyString(); +} + +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 c09132c57..980b92fb2 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 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2011, 2013-2015 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,36 +24,62 @@ #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 JSActivation; class Interpreter; class JSScope; + struct CallSiteIndex { + CallSiteIndex() + : m_bits(UINT_MAX) + { + } + + explicit CallSiteIndex(uint32_t bits) + : m_bits(bits) + { } +#if USE(JSVALUE32_64) + explicit CallSiteIndex(Instruction* instruction) + : m_bits(bitwise_cast<uint32_t>(instruction)) + { } +#endif + + explicit operator bool() const { return m_bits != UINT_MAX; } + + inline uint32_t bits() const { return m_bits; } + + private: + uint32_t m_bits; + }; + // Represents the current state of script execution. // Passed as the first argument to most functions. class ExecState : private Register { public: JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); } - JSObject* callee() const { return this[JSStack::Callee].function(); } + JSObject* callee() const { return this[JSStack::Callee].object(); } + SUPPRESS_ASAN JSValue unsafeCallee() const { return this[JSStack::Callee].asanUnsafeJSValue(); } CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } - JSScope* scope() const + SUPPRESS_ASAN CodeBlock* unsafeCodeBlock() const { return this[JSStack::CodeBlock].Register::asanUnsafeCodeBlock(); } + JSScope* scope(int scopeRegisterOffset) const { - ASSERT(this[JSStack::ScopeChain].Register::scope()); - return this[JSStack::ScopeChain].Register::scope(); + ASSERT(this[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); } // Global object in which execution began. - JSGlobalObject* dynamicGlobalObject(); + JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject(); // Global object in which the currently executing code was defined. - // Differs from dynamicGlobalObject() during function calls across web browser frames. + // Differs from vmEntryGlobalObject() during function calls across web browser frames. JSGlobalObject* lexicalGlobalObject() const; // Differs from lexicalGlobalObject because this will have DOM window shell rather than @@ -67,132 +93,101 @@ namespace JSC { // pointer, so these are inefficient, and should be used sparingly in new code. // But they're used in many places in legacy code, so they're not going away any time soon. - void clearException() { vm().exception = JSValue(); } - void clearSupplementaryExceptionInfo() - { - vm().clearExceptionStack(); - } + void clearException() { vm().clearException(); } + + Exception* exception() const { return vm().exception(); } + bool hadException() const { return !!vm().exception(); } - JSValue exception() const { return vm().exception; } - bool hadException() const { return vm().exception; } + Exception* lastException() const { return vm().lastException(); } + void clearLastException() { vm().clearLastException(); } + 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(CallFrame* callFrame) { return callFrame->vm().arrayConstructorTable; } - static const HashTable* arrayPrototypeTable(CallFrame* callFrame) { return callFrame->vm().arrayPrototypeTable; } - static const HashTable* booleanPrototypeTable(CallFrame* callFrame) { return callFrame->vm().booleanPrototypeTable; } - static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->vm().dateTable; } - static const HashTable* dateConstructorTable(CallFrame* callFrame) { return callFrame->vm().dateConstructorTable; } - static const HashTable* errorPrototypeTable(CallFrame* callFrame) { return callFrame->vm().errorPrototypeTable; } - static const HashTable* globalObjectTable(CallFrame* callFrame) { return callFrame->vm().globalObjectTable; } - static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->vm().jsonTable; } - static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->vm().mathTable; } - static const HashTable* numberConstructorTable(CallFrame* callFrame) { return callFrame->vm().numberConstructorTable; } - static const HashTable* numberPrototypeTable(CallFrame* callFrame) { return callFrame->vm().numberPrototypeTable; } - static const HashTable* objectConstructorTable(CallFrame* callFrame) { return callFrame->vm().objectConstructorTable; } - static const HashTable* privateNamePrototypeTable(CallFrame* callFrame) { return callFrame->vm().privateNamePrototypeTable; } - static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->vm().regExpTable; } - static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->vm().regExpConstructorTable; } - static const HashTable* regExpPrototypeTable(CallFrame* callFrame) { return callFrame->vm().regExpPrototypeTable; } - static const HashTable* stringConstructorTable(CallFrame* callFrame) { return callFrame->vm().stringConstructorTable; } + 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 this[JSStack::CallerFrame].callFrame(); } -#if ENABLE(JIT) || ENABLE(LLINT) - ReturnAddressPtr returnPC() const { return ReturnAddressPtr(this[JSStack::ReturnPC].vPC()); } - bool hasReturnPC() const { return !!this[JSStack::ReturnPC].vPC(); } - void clearReturnPC() { registers()[JSStack::ReturnPC] = static_cast<Instruction*>(0); } -#endif + CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); } + void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + SUPPRESS_ASAN void* unsafeCallerFrameOrVMEntryFrame() const { return unsafeCallerFrameAndPC().callerFrame; } + + CallFrame* unsafeCallerFrame(VMEntryFrame*&); + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); + + static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); } + + ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); } + bool hasReturnPC() const { return !!callerFrameAndPC().pc; } + void clearReturnPC() { callerFrameAndPC().pc = 0; } + static ptrdiff_t returnPCOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, pc); } AbstractPC abstractReturnPC(VM& vm) { return AbstractPC(vm, this); } -#if USE(JSVALUE32_64) - unsigned bytecodeOffsetForNonDFGCode() const; - void setBytecodeOffsetForNonDFGCode(unsigned offset); -#else - unsigned bytecodeOffsetForNonDFGCode() const - { - ASSERT(codeBlock()); - return this[JSStack::ArgumentCount].tag(); - } + + bool callSiteBitsAreBytecodeOffset() const; + bool callSiteBitsAreCodeOriginIndex() const; + + unsigned callSiteAsRawBits() const; + unsigned unsafeCallSiteAsRawBits() const; + CallSiteIndex callSiteIndex() const; + CallSiteIndex unsafeCallSiteIndex() const; + private: + unsigned callSiteBitsAsBytecodeOffset() const; + public: + + // This will try to get you the bytecode offset, but you should be aware that + // this bytecode offset may be bogus in the presence of inlining. This will + // also return 0 if the call frame has no notion of bytecode offsets (for + // example if it's native code). + // https://bugs.webkit.org/show_bug.cgi?id=121754 + unsigned bytecodeOffset(); - void setBytecodeOffsetForNonDFGCode(unsigned offset) - { - ASSERT(codeBlock()); - this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(offset); - } -#endif + // This will get you a CodeOrigin. It will always succeed. May return + // CodeOrigin(0) if we're in native code. + CodeOrigin codeOrigin(); - Register* frameExtent() + Register* topOfFrame() { if (!codeBlock()) return registers(); - return frameExtentInternal(); + return topOfFrameInternal(); } - Register* frameExtentInternal(); - -#if ENABLE(DFG_JIT) - InlineCallFrame* inlineCallFrame() const { return this[JSStack::ReturnPC].asInlineCallFrame(); } - unsigned codeOriginIndexForDFG() const { return this[JSStack::ArgumentCount].tag(); } -#else - // This will never be called if !ENABLE(DFG_JIT) since all calls should be guarded by - // isInlineCallFrame(). But to make it easier to write code without having a bunch of - // #if's, we make a dummy implementation available anyway. - InlineCallFrame* inlineCallFrame() const - { - RELEASE_ASSERT_NOT_REACHED(); - return 0; - } -#endif -#if USE(JSVALUE32_64) - Instruction* currentVPC() const - { - return bitwise_cast<Instruction*>(this[JSStack::ArgumentCount].tag()); - } - void setCurrentVPC(Instruction* vpc) - { - this[JSStack::ArgumentCount].tag() = bitwise_cast<int32_t>(vpc); - } -#else - Instruction* currentVPC() const; + Instruction* currentVPC() const; // This only makes sense in the LLInt and baseline. void setCurrentVPC(Instruction* vpc); -#endif - void setCallerFrame(CallFrame* callerFrame) { static_cast<Register*>(this)[JSStack::CallerFrame] = callerFrame; } - void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; } + void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } + void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; } - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope, - CallFrame* callerFrame, int argc, JSObject* callee) - { - ASSERT(callerFrame); // Use noCaller() rather than 0 for the outer host call frame caller. - ASSERT(callerFrame == noCaller() || callerFrame->removeHostCallFrameFlag()->stack()->end() >= 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); + 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); } // Read a register from the codeframe (or constant from the CodeBlock). Register& r(int); - // Read a register for a non-constant + 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; } size_t argumentCountIncludingThis() const { return this[JSStack::ArgumentCount].payload(); } - static int argumentOffset(int argument) { return s_firstArgumentOffset - argument; } - static int argumentOffsetIncludingThis(int argument) { return s_thisArgumentOffset - argument; } + static int argumentOffset(int argument) { return (JSStack::FirstArgument + argument); } + static int argumentOffsetIncludingThis(int argument) { return (JSStack::ThisArgument + argument); } // In the following (argument() and setArgument()), the 'argument' // parameter is the index of the arguments of the target function of @@ -207,85 +202,69 @@ namespace JSC { { if (argument >= argumentCount()) return jsUndefined(); - return this[argumentOffset(argument)].jsValue(); + return getArgumentUnsafe(argument); + } + JSValue uncheckedArgument(size_t argument) + { + ASSERT(argument < argumentCount()); + return getArgumentUnsafe(argument); } void setArgument(size_t argument, JSValue value) { 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; } - JSValue argumentAfterCapture(size_t argument); - - static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::CallFrameHeaderSize; } + // 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(); } - // FIXME: Remove these. - int hostThisRegister() { return thisArgumentOffset(); } - JSValue hostThisValue() { return thisValue(); } + JSValue argumentAfterCapture(size_t argument); - static CallFrame* noCaller() { return reinterpret_cast<CallFrame*>(HostCallFrameFlag); } + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } - bool hasHostCallFrameFlag() const { return reinterpret_cast<intptr_t>(this) & HostCallFrameFlag; } - CallFrame* addHostCallFrameFlag() const { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) | HostCallFrameFlag); } - CallFrame* removeHostCallFrameFlag() { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) & ~HostCallFrameFlag); } + static CallFrame* noCaller() { return 0; } void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[JSStack::ArgumentCount].payload() = count; } - void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); } + void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = callee; } void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; } - void setReturnPC(void* value) { static_cast<Register*>(this)[JSStack::ReturnPC] = (Instruction*)value; } - -#if ENABLE(DFG_JIT) - bool isInlineCallFrame(); - - void setInlineCallFrame(InlineCallFrame* inlineCallFrame) { static_cast<Register*>(this)[JSStack::ReturnPC] = inlineCallFrame; } - - // Call this to get the semantically correct JS CallFrame* for the - // currently executing function. - CallFrame* trueCallFrame(AbstractPC); - - // Call this to get the semantically correct JS CallFrame* corresponding - // to the caller. This resolves issues surrounding inlining and the - // HostCallFrameFlag stuff. - CallFrame* trueCallerFrame(); - - CodeBlock* someCodeBlockForPossiblyInlinedCode(); -#else - bool isInlineCallFrame() { return false; } - - CallFrame* trueCallFrame(AbstractPC) { return this; } - CallFrame* trueCallerFrame() { return callerFrame()->removeHostCallFrameFlag(); } - - CodeBlock* someCodeBlockForPossiblyInlinedCode() { return codeBlock(); } -#endif - CallFrame* callerFrameNoFlags() { return callerFrame()->removeHostCallFrameFlag(); } - - // Call this to get the true call frame (accounted for inlining and any - // other optimizations), when you have entered into VM code through one - // of the "blessed" entrypoints (JITStubs or DFGOperations). This means - // that if you're pretty much anywhere in the VM you can safely call this; - // though if you were to magically get an ExecState* by, say, interrupting - // a thread that is running JS code and brutishly scraped the call frame - // register, calling this method would probably lead to horrible things - // happening. - CallFrame* trueCallFrameFromVMCode() { return trueCallFrame(AbstractPC()); } + void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); } + + String friendlyFunctionName(); + + // CallFrame::iterate() expects a Functor that implements the following method: + // StackVisitor::Status operator()(StackVisitor&); + + template <typename Functor> void iterate(Functor& functor) + { + StackVisitor::visit<Functor>(this, functor); + } + + void dump(PrintStream&); + JS_EXPORT_PRIVATE const char* describeFrame(); private: - static const intptr_t HostCallFrameFlag = 1; - static const int s_thisArgumentOffset = -1 - JSStack::CallFrameHeaderSize; - static const int s_firstArgumentOffset = s_thisArgumentOffset - 1; #ifndef NDEBUG JSStack* stack(); #endif -#if ENABLE(DFG_JIT) - bool isInlineCallFrameSlow(); -#endif 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: @@ -299,24 +278,18 @@ namespace JSC { int offset = reg - this->registers(); // The offset is defined (based on argumentOffset()) to be: - // offset = s_firstArgumentOffset - argIndex; + // offset = JSStack::FirstArgument - argIndex; // Hence: - // argIndex = s_firstArgumentOffset - offset; - size_t argIndex = s_firstArgumentOffset - offset; + // argIndex = JSStack::FirstArgument - offset; + size_t argIndex = offset - JSStack::FirstArgument; return argIndex; } - 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); } + SUPPRESS_ASAN const CallerFrameAndPC& unsafeCallerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } friend class JSStack; - friend class VMInspector; }; } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h index 7ae1e6fdf..2c03b7452 100644 --- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -26,11 +26,13 @@ #ifndef CallFrameClosure_h #define CallFrameClosure_h +#include "ProtoCallFrame.h" + namespace JSC { struct CallFrameClosure { CallFrame* oldCallFrame; - CallFrame* newCallFrame; + ProtoCallFrame* protoCallFrame; JSFunction* function; FunctionExecutable* functionExecutable; VM* vm; @@ -40,23 +42,12 @@ struct CallFrameClosure { void setThis(JSValue value) { - newCallFrame->setThisValue(value); + protoCallFrame->setThisValue(value); } void setArgument(int argument, JSValue value) { - newCallFrame->setArgument(argument, value); - } - - void resetCallFrame() - { - newCallFrame->setScope(scope); - // setArgument() takes an arg index that starts from 0 for the first - // argument after the 'this' value. Since both argumentCountIncludingThis - // and parameterCountIncludingThis includes the 'this' value, we need to - // subtract 1 from them to make i a valid argument index for setArgument(). - for (int i = argumentCountIncludingThis-1; i < parameterCountIncludingThis-1; ++i) - newCallFrame->setArgument(i, jsUndefined()); + protoCallFrame->setArgument(argument, value); } }; diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 844f5f252..18a90760d 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2012-2015 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 Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple 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,44 +30,53 @@ #include "config.h" #include "Interpreter.h" -#include "Arguments.h" #include "BatchedTransitionOptimizer.h" -#include "CallFrame.h" #include "CallFrameClosure.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 "JSNameScope.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" +#include "JSModuleEnvironment.h" #include "JSNotAnObject.h" -#include "JSPropertyNameIterator.h" #include "JSStackInlines.h" #include "JSString.h" #include "JSWithScope.h" #include "LLIntCLoop.h" +#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 "VMStackBounds.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> @@ -77,36 +86,53 @@ #include "JIT.h" #endif -#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__)) - using namespace std; namespace JSC { -Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec) - : m_interpreter(*exec->interpreter()) +String StackFrame::friendlySourceURL() const { - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().enableErrorStackReserve(); - m_interpreter.m_errorHandlingModeReentry++; + String traceLine; + + switch (codeType) { + case StackFrameEvalCode: + case StackFrameModuleCode: + case StackFrameFunctionCode: + case StackFrameGlobalCode: + if (!sourceURL.isEmpty()) + traceLine = sourceURL.impl(); + break; + case StackFrameNativeCode: + traceLine = "[native code]"; + break; + } + return traceLine.isNull() ? emptyString() : traceLine; } -Interpreter::ErrorHandlingMode::~ErrorHandlingMode() +String StackFrame::friendlyFunctionName(CallFrame* callFrame) const { - m_interpreter.m_errorHandlingModeReentry--; - ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().disableErrorStackReserve(); -} - -static CallFrame* getCallerInfo(VM*, CallFrame*, unsigned& bytecodeOffset, CodeBlock*& callerOut); - -// Returns the depth of the scope chain within a given call frame. -static int depth(CodeBlock* codeBlock, JSScope* sc) -{ - if (!codeBlock->needsFullScopeChain()) - return 0; - return sc->localDepth(); + String traceLine; + JSObject* stackFrameCallee = callee.get(); + + switch (codeType) { + case StackFrameEvalCode: + traceLine = "eval code"; + break; + case StackFrameModuleCode: + traceLine = "module 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; } JSValue eval(CallFrame* callFrame) @@ -117,21 +143,27 @@ JSValue eval(CallFrame* callFrame) JSValue program = callFrame->argument(0); if (!program.isString()) return program; - + TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); + JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); + if (!globalObject->evalEnabled()) { + callFrame->vm().throwException(callFrame, createEvalError(callFrame, globalObject->evalDisabledErrorMessage())); + return jsUndefined(); + } String programSource = asString(program)->value(callFrame); if (callFrame->hadException()) return JSValue(); CallFrame* callerFrame = callFrame->callerFrame(); CodeBlock* callerCodeBlock = callerFrame->codeBlock(); - JSScope* callerScopeChain = callerFrame->scope(); - EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope(); + UnlinkedCodeBlock* callerUnlinkedCodeBlock = callerCodeBlock->unlinkedCodeBlock(); + + bool isArrowFunctionContext = callerUnlinkedCodeBlock->isArrowFunction() || callerUnlinkedCodeBlock->isArrowFunctionContext(); + EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, isArrowFunctionContext, 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()) @@ -144,102 +176,124 @@ JSValue eval(CallFrame* callFrame) } // If the literal parser bailed, it should not have thrown exceptions. - ASSERT(!callFrame->vm().exception); + ASSERT(!callFrame->vm().exception()); - JSValue exceptionValue; - eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->unlinkedCodeBlock()->codeCacheForEval().get(), callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue); - - ASSERT(!eval == exceptionValue); - if (UNLIKELY(!eval)) - return throwError(callFrame, exceptionValue); + ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded; + if (callerUnlinkedCodeBlock->constructorKind() == ConstructorKind::Derived) + thisTDZMode = ThisTDZMode::AlwaysCheck; + + eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock, callerCodeBlock->isStrictMode(), thisTDZMode, callerCodeBlock->unlinkedCodeBlock()->derivedContextType(), callerCodeBlock->unlinkedCodeBlock()->isArrowFunction(), programSource, callerScopeChain); + if (!eval) + return jsUndefined(); } JSValue thisValue = callerFrame->thisValue(); - ASSERT(isValidThisObject(thisValue, callFrame)); Interpreter* interpreter = callFrame->vm().interpreter; return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); } -CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue, JSValue arguments, int firstFreeRegister) +unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset) { - if (!arguments) { // f.apply(x, arguments), with arguments unmodified. - unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); - if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) { - callFrame->vm().exception = createStackOverflowError(callFrame); + if (UNLIKELY(!arguments.isCell())) { + if (arguments.isUndefinedOrNull()) return 0; - } - - newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); - newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < callFrame->argumentCount(); ++i) - newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i)); - return newCallFrame; + + callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); + return 0; } - - if (arguments.isUndefinedOrNull()) { - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + 1 + JSStack::CallFrameHeaderSize); - if (!stack->grow(newCallFrame->registers())) { - callFrame->vm().exception = createStackOverflowError(callFrame); - return 0; - } - newCallFrame->setArgumentCountIncludingThis(1); - newCallFrame->setThisValue(thisValue); - 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)); + 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; +} - if (!arguments.isObject()) { - callFrame->vm().exception = createInvalidParameterError(callFrame, "Function.prototype.apply", arguments); +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); return 0; } + + return length; +} - if (asObject(arguments)->classInfo() == &Arguments::s_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().exception = createStackOverflowError(callFrame); - return 0; +void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length) +{ + if (UNLIKELY(!arguments.isCell()) || !length) + return; + + JSCell* cell = arguments.asCell(); + switch (cell->type()) { + case DirectArgumentsType: + jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); + 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; } - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - argsObject->copyToArguments(callFrame, newCallFrame, argCount); - return newCallFrame; - } + 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); + return; + } } +} - 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().exception = createStackOverflowError(callFrame); - return 0; - } - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - array->copyToArguments(callFrame, newCallFrame, argCount); - return newCallFrame; - } +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); + + newCallFrame->setArgumentCountIncludingThis(length + 1); +} - 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().exception = createStackOverflowError(callFrame); - return 0; - } - newCallFrame->setArgumentCountIncludingThis(argCount + 1); +void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length) +{ + setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length); 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 0; - } - return newCallFrame; } Interpreter::Interpreter(VM& vm) : m_sampleEntryDepth(0) + , m_vm(vm) , m_stack(vm) , m_errorHandlingModeReentry(0) #if !ASSERT_DISABLED @@ -252,11 +306,9 @@ Interpreter::~Interpreter() { } -void Interpreter::initialize(bool canUseJIT) +void Interpreter::initialize() { - UNUSED_PARAM(canUseJIT); - -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) m_opcodeTable = LLInt::opcodeMap(); for (int i = 0; i < numOpcodeIDs; ++i) m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); @@ -285,6 +337,34 @@ void Interpreter::dumpCallFrame(CallFrame* callFrame) dumpRegisters(callFrame); } +class DumpRegisterFunctor { +public: + DumpRegisterFunctor(const Register*& it) + : m_hasSkippedFirstFrame(false) + , m_it(it) + { + } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + unsigned line = 0; + unsigned unusedColumn = 0; + visitor->computeLineAndColumn(line, unusedColumn); + dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", m_it, visitor->bytecodeOffset(), line); + --m_it; + return StackVisitor::Done; + } + +private: + bool m_hasSkippedFirstFrame; + const Register*& m_it; +}; + void Interpreter::dumpRegisters(CallFrame* callFrame) { dataLogF("Register frame: \n\n"); @@ -296,63 +376,57 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) const Register* it; const Register* end; - it = callFrame->registers() - JSStack::CallFrameHeaderSize - callFrame->argumentCountIncludingThis(); - end = callFrame->registers() - JSStack::CallFrameHeaderSize; - while (it < end) { + it = callFrame->registers() + JSStack::ThisArgument + callFrame->argumentCount(); + end = callFrame->registers() + JSStack::ThisArgument - 1; + while (it > end) { JSValue v = it->jsValue(); int registerNumber = it - callFrame->registers(); - String name = codeBlock->nameForRegister(registerNumber); + String name = codeBlock->nameForRegister(VirtualRegister(registerNumber)); dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); - it++; + --it; } dataLogF("-----------------------------------------------------------------------------\n"); dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); - ++it; + --it; dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); - ++it; + --it; dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); - ++it; - dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); - ++it; + --it; + // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header + --it; #if ENABLE(JIT) AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); if (pc.hasJITReturnAddress()) dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); #endif - unsigned bytecodeOffset = 0; - int line = 0; - CodeBlock* callerCodeBlock = 0; - getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, callerCodeBlock); - line = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset); - dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line); - ++it; + + DumpRegisterFunctor functor(it); + callFrame->iterate(functor); + dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); - ++it; + --it; dataLogF("-----------------------------------------------------------------------------\n"); - int registerCount = 0; - - end = it + codeBlock->m_numVars; + end = it - codeBlock->m_numVars; if (it != end) { do { JSValue v = it->jsValue(); int registerNumber = it - callFrame->registers(); - String name = codeBlock->nameForRegister(registerNumber); + String name = codeBlock->nameForRegister(VirtualRegister(registerNumber)); dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v)); - ++it; - ++registerCount; + --it; } while (it != end); } dataLogF("-----------------------------------------------------------------------------\n"); - end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars; + end = it - codeBlock->m_numCalleeLocals + codeBlock->m_numVars; if (it != end) { do { JSValue v = (*it).jsValue(); - dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerCount, it, toCString(v).data(), (long long)JSValue::encode(v)); - ++it; - ++registerCount; + int registerNumber = it - callFrame->registers(); + dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerNumber, it, toCString(v).data(), (long long)JSValue::encode(v)); + --it; } while (it != end); } dataLogF("-----------------------------------------------------------------------------\n"); @@ -363,212 +437,28 @@ 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 } -NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue exceptionValue, unsigned& bytecodeOffset, CodeBlock*& codeBlock) -{ - CodeBlock* oldCodeBlock = codeBlock; - JSScope* scope = callFrame->scope(); - - if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { - DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); - if (callFrame->callee()) - debugger->returnEvent(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0); - else - debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0); - } - - JSValue activation; - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) { - activation = callFrame->uncheckedR(oldCodeBlock->activationRegister()).jsValue(); - if (activation) - jsCast<JSActivation*>(activation)->tearOff(*scope->vm()); - } - - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) { - if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) { - if (activation) - jsCast<Arguments*>(arguments)->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); - else - jsCast<Arguments*>(arguments)->tearOff(callFrame); - } - } - - CallFrame* callerFrame = callFrame->callerFrame(); - callFrame->vm().topCallFrame = callerFrame; - if (callerFrame->hasHostCallFrameFlag()) - return false; - callFrame = getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, codeBlock); - return true; -} - -static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset) -{ - exception->clearAppendSourceToMessage(); - - if (!callFrame->codeBlock()->hasExpressionInfo()) - return; - - int startOffset = 0; - int endOffset = 0; - int divotPoint = 0; - unsigned line = 0; - unsigned column = 0; - - CodeBlock* codeBlock = callFrame->codeBlock(); - codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column); - - int expressionStart = divotPoint - startOffset; - int expressionStop = divotPoint + endOffset; - - const String& sourceString = codeBlock->source()->source(); - if (!expressionStop || expressionStart > static_cast<int>(sourceString.length())) - return; - - VM* vm = &callFrame->vm(); - JSValue jsMessage = exception->getDirect(*vm, vm->propertyNames->message); - if (!jsMessage || !jsMessage.isString()) - return; - - String message = asString(jsMessage)->value(callFrame); - - if (expressionStart < expressionStop) - message = makeString(message, " (evaluating '", codeBlock->source()->getRange(expressionStart, expressionStop), "')"); - else { - // No range information, so give a few characters of context - const StringImpl* data = sourceString.impl(); - int dataLength = sourceString.length(); - int start = expressionStart; - int stop = expressionStart; - // Get up to 20 characters of context to the left and right of the divot, clamping to the line. - // then strip whitespace. - while (start > 0 && (expressionStart - start < 20) && (*data)[start - 1] != '\n') - start--; - while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start])) - start++; - while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n') - stop++; - while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1])) - stop--; - message = makeString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')"); - } - - exception->putDirect(*vm, vm->propertyNames->message, jsString(vm, message)); -} - -static unsigned getBytecodeOffsetForCallFrame(CallFrame* callFrame) -{ - callFrame = callFrame->removeHostCallFrameFlag(); - CodeBlock* codeBlock = callFrame->codeBlock(); - if (!codeBlock) - return 0; -#if ENABLE(DFG_JIT) - if (codeBlock->getJITType() == JITCode::DFGJIT) - return codeBlock->codeOrigin(callFrame->codeOriginIndexForDFG()).bytecodeIndex; -#endif - return callFrame->bytecodeOffsetForNonDFGCode(); -} - -static CallFrame* getCallerInfo(VM* vm, CallFrame* callFrame, unsigned& bytecodeOffset, CodeBlock*& caller) +static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) { - ASSERT_UNUSED(vm, vm); - bytecodeOffset = 0; - ASSERT(!callFrame->hasHostCallFrameFlag()); - CallFrame* trueCallerFrame = callFrame->trueCallerFrame(); - bool wasCalledByHost = callFrame->callerFrame()->hasHostCallFrameFlag(); - ASSERT(!trueCallerFrame->hasHostCallFrameFlag()); - - if (trueCallerFrame == CallFrame::noCaller() || !trueCallerFrame || !trueCallerFrame->codeBlock()) { - caller = 0; - return trueCallerFrame; - } - - CodeBlock* callerCodeBlock = trueCallerFrame->codeBlock(); - - if (!callFrame->hasReturnPC()) - wasCalledByHost = true; - - if (wasCalledByHost) { -#if ENABLE(DFG_JIT) - if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) { - unsigned codeOriginIndex = callFrame->callerFrame()->removeHostCallFrameFlag()->codeOriginIndexForDFG(); - CodeOrigin origin = callerCodeBlock->codeOrigin(codeOriginIndex); - bytecodeOffset = origin.bytecodeIndex; - if (InlineCallFrame* inlineCallFrame = origin.inlineCallFrame) - callerCodeBlock = inlineCallFrame->baselineCodeBlock(); - } else -#endif - bytecodeOffset = trueCallerFrame->bytecodeOffsetForNonDFGCode(); - } else { -#if ENABLE(DFG_JIT) - if (callFrame->isInlineCallFrame()) { - InlineCallFrame* icf = callFrame->inlineCallFrame(); - bytecodeOffset = icf->caller.bytecodeIndex; - if (InlineCallFrame* parentCallFrame = icf->caller.inlineCallFrame) { - FunctionExecutable* executable = static_cast<FunctionExecutable*>(parentCallFrame->executable.get()); - CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(parentCallFrame->isCall ? CodeForCall : CodeForConstruct); - ASSERT(newCodeBlock); - ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); - callerCodeBlock = newCodeBlock; - } - } else if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) { - CodeOrigin origin; - if (!callerCodeBlock->codeOriginForReturn(callFrame->returnPC(), origin)) { - // This should not be possible, but we're seeing cases where it does happen - // CallFrame already has robustness against bogus stack walks, so - // we'll extend that to here as well. - ASSERT_NOT_REACHED(); - caller = 0; - return 0; - } - bytecodeOffset = origin.bytecodeIndex; - if (InlineCallFrame* icf = origin.inlineCallFrame) { - FunctionExecutable* executable = static_cast<FunctionExecutable*>(icf->executable.get()); - CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(icf->isCall ? CodeForCall : CodeForConstruct); - ASSERT(newCodeBlock); - ASSERT(newCodeBlock->instructionCount() > bytecodeOffset); - callerCodeBlock = newCodeBlock; - } - } else -#endif - { - RELEASE_ASSERT(callerCodeBlock); - bytecodeOffset = callerCodeBlock->bytecodeOffset(trueCallerFrame, callFrame->returnPC()); - } - } - - RELEASE_ASSERT(callerCodeBlock); - caller = callerCodeBlock; - return trueCallerFrame; -} - -static ALWAYS_INLINE const String getSourceURLFromCallFrame(CallFrame* callFrame) -{ - ASSERT(!callFrame->hasHostCallFrameFlag()); - return callFrame->codeBlock()->ownerExecutable()->sourceURL(); -} - -static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame) -{ - ASSERT(!callFrame->hasHostCallFrameFlag()); - - switch (callFrame->codeBlock()->codeType()) { - case EvalCode: + switch (visitor->codeType()) { + case StackVisitor::Frame::Eval: return StackFrameEvalCode; - case FunctionCode: + case StackVisitor::Frame::Module: + return StackFrameModuleCode; + case StackVisitor::Frame::Function: return StackFrameFunctionCode; - case GlobalCode: + case StackVisitor::Frame::Global: return StackFrameGlobalCode; + case StackVisitor::Frame::Native: + ASSERT_NOT_REACHED(); + return StackFrameNativeCode; } RELEASE_ASSERT_NOT_REACHED(); return StackFrameGlobalCode; @@ -591,6 +481,9 @@ 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) @@ -623,147 +516,286 @@ String StackFrame::toString(CallFrame* callFrame) return traceBuild.toString().impl(); } -void Interpreter::getStackTrace(VM* vm, Vector<StackFrame>& results, size_t maxStackSize) +static inline bool isWebAssemblyExecutable(ExecutableBase* executable) { - CallFrame* callFrame = vm->topCallFrame->removeHostCallFrameFlag(); - if (!callFrame || callFrame == CallFrame::noCaller()) - return; - unsigned bytecodeOffset = getBytecodeOffsetForCallFrame(callFrame); - callFrame = callFrame->trueCallFrameFromVMCode(); - if (!callFrame) - return; - CodeBlock* callerCodeBlock = callFrame->codeBlock(); - - while (callFrame && callFrame != CallFrame::noCaller() && maxStackSize--) { - String sourceURL; - if (callerCodeBlock) { - sourceURL = getSourceURLFromCallFrame(callFrame); - StackFrame s = { - Strong<JSObject>(*vm, callFrame->callee()), - getStackFrameCodeType(callFrame), - Strong<ExecutableBase>(*vm, callerCodeBlock->ownerExecutable()), - Strong<UnlinkedCodeBlock>(*vm, callerCodeBlock->unlinkedCodeBlock()), - callerCodeBlock->source(), - callerCodeBlock->ownerExecutable()->lineNo(), - callerCodeBlock->firstLineColumnOffset(), - callerCodeBlock->sourceOffset(), - bytecodeOffset, - sourceURL - }; - - results.append(s); - } else { - StackFrame s = { Strong<JSObject>(*vm, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; - results.append(s); - } - callFrame = getCallerInfo(vm, callFrame, bytecodeOffset, callerCodeBlock); - } +#if !ENABLE(WEBASSEMBLY) + UNUSED_PARAM(executable); + return false; +#else + return executable->isWebAssemblyExecutable(); +#endif } -void Interpreter::addStackTraceIfNecessary(CallFrame* callFrame, JSValue error) -{ - VM* vm = &callFrame->vm(); - ASSERT(callFrame == vm->topCallFrame || callFrame == callFrame->lexicalGlobalObject()->globalExec() || callFrame == callFrame->dynamicGlobalObject()->globalExec()); - - if (error.isObject()) { - if (asObject(error)->hasProperty(callFrame, vm->propertyNames->stack)) - return; +class GetStackTraceFunctor { +public: + GetStackTraceFunctor(VM& vm, Vector<StackFrame>& results, size_t remainingCapacity) + : m_vm(vm) + , m_results(results) + , m_remainingCapacityForFrameCapture(remainingCapacity) + { } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + VM& vm = m_vm; + if (m_remainingCapacityForFrameCapture) { + if (visitor->isJSFrame() + && !isWebAssemblyExecutable(visitor->codeBlock()->ownerExecutable()) + && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { + CodeBlock* codeBlock = visitor->codeBlock(); + StackFrame s = { + Strong<JSObject>(vm, visitor->callee()), + getStackFrameCodeType(visitor), + Strong<ScriptExecutable>(vm, codeBlock->ownerScriptExecutable()), + Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), + codeBlock->source(), + codeBlock->ownerScriptExecutable()->firstLine(), + codeBlock->firstLineColumnOffset(), + codeBlock->sourceOffset(), + visitor->bytecodeOffset(), + visitor->sourceURL() + }; + m_results.append(s); + } else { + StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ScriptExecutable>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; + m_results.append(s); + } - Vector<StackFrame> stackTrace; - getStackTrace(&callFrame->vm(), stackTrace); - vm->exceptionStack() = RefCountedArray<StackFrame>(stackTrace); - if (stackTrace.isEmpty() || !error.isObject()) + m_remainingCapacityForFrameCapture--; + return StackVisitor::Continue; + } + return StackVisitor::Done; + } + +private: + VM& m_vm; + Vector<StackFrame>& m_results; + size_t m_remainingCapacityForFrameCapture; +}; + +void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize) +{ + VM& vm = m_vm; + CallFrame* callFrame = vm.topCallFrame; + if (!callFrame) return; - JSObject* errorObject = asObject(error); - JSGlobalObject* globalObject = 0; - if (isTerminatedExecutionException(error)) - globalObject = vm->dynamicGlobalObject; - else - globalObject = errorObject->globalObject(); + GetStackTraceFunctor functor(vm, results, maxStackSize); + callFrame->iterate(functor); +} +JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> stackTrace) +{ // FIXME: JSStringJoiner could be more efficient than StringBuilder here. StringBuilder builder; for (unsigned i = 0; i < stackTrace.size(); i++) { - builder.append(String(stackTrace[i].toString(globalObject->globalExec()).impl())); + builder.append(String(stackTrace[i].toString(exec))); if (i != stackTrace.size() - 1) builder.append('\n'); } - - errorObject->putDirect(*vm, vm->propertyNames->stack, jsString(vm, builder.toString()), ReadOnly | DontDelete); + return jsString(&exec->vm(), builder.toString()); } -NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) +ALWAYS_INLINE static HandlerInfo* findExceptionHandler(StackVisitor& visitor, CodeBlock* codeBlock, CodeBlock::RequiredHandler requiredHandler) { - CodeBlock* codeBlock = callFrame->codeBlock(); - bool isTermination = false; + ASSERT(codeBlock); +#if ENABLE(DFG_JIT) + ASSERT(!visitor->isInlinedFrame()); +#endif - ASSERT(!exceptionValue.isEmpty()); - ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); - // This shouldn't be possible (hence the assertions), but we're already in the slowest of - // slow cases, so let's harden against it anyway to be safe. - if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) - exceptionValue = jsNull(); + CallFrame* callFrame = visitor->callFrame(); + unsigned exceptionHandlerIndex; + if (JITCode::isOptimizingJIT(codeBlock->jitType())) + exceptionHandlerIndex = callFrame->callSiteIndex().bits(); + else + exceptionHandlerIndex = callFrame->bytecodeOffset(); + + return codeBlock->handlerForIndex(exceptionHandlerIndex, requiredHandler); +} + +class GetCatchHandlerFunctor { +public: + GetCatchHandlerFunctor() + : m_handler(0) + { + } + + HandlerInfo* handler() { return m_handler; } - // Set up the exception object - if (exceptionValue.isObject()) { - JSObject* exception = asObject(exceptionValue); + StackVisitor::Status operator()(StackVisitor& visitor) + { + visitor.unwindToMachineCodeBlockFrame(); + + CodeBlock* codeBlock = visitor->codeBlock(); + if (!codeBlock) + return StackVisitor::Continue; + + m_handler = findExceptionHandler(visitor, codeBlock, CodeBlock::RequiredHandler::CatchHandler); + if (m_handler) + return StackVisitor::Done; + + return StackVisitor::Continue; + } + +private: + HandlerInfo* m_handler; +}; + +ALWAYS_INLINE static void notifyDebuggerOfUnwinding(CallFrame* callFrame) +{ + if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { + SuspendExceptionScope scope(&callFrame->vm()); + if (jsDynamicCast<JSFunction*>(callFrame->callee())) + debugger->returnEvent(callFrame); + else + debugger->didExecuteProgram(callFrame); + ASSERT(!callFrame->hadException()); + } +} - if (exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage()) - appendSourceToError(callFrame, static_cast<ErrorInstance*>(exception), bytecodeOffset); +class UnwindFunctor { +public: + UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) + : m_callFrame(callFrame) + , m_isTermination(isTermination) + , m_codeBlock(codeBlock) + , m_handler(handler) + { + } - if (!hasErrorInfo(callFrame, exception)) { - // FIXME: should only really be adding these properties to VM generated exceptions, - // but the inspector currently requires these for all thrown objects. - addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); + StackVisitor::Status operator()(StackVisitor& visitor) + { + visitor.unwindToMachineCodeBlockFrame(); + VM& vm = m_callFrame->vm(); + m_callFrame = visitor->callFrame(); + m_codeBlock = visitor->codeBlock(); + + m_handler = nullptr; + if (!m_isTermination) { + if (m_codeBlock && !isWebAssemblyExecutable(m_codeBlock->ownerExecutable())) + m_handler = findExceptionHandler(visitor, m_codeBlock, CodeBlock::RequiredHandler::AnyHandler); } - isTermination = isTerminatedExecutionException(exception); - } else { - if (!callFrame->vm().exceptionStack().size()) { - Vector<StackFrame> stack; - Interpreter::getStackTrace(&callFrame->vm(), stack); - callFrame->vm().exceptionStack() = RefCountedArray<StackFrame>(stack); + if (m_handler) + return StackVisitor::Done; + + notifyDebuggerOfUnwinding(m_callFrame); + + bool shouldStopUnwinding = visitor->callerIsVMEntryFrame(); + if (shouldStopUnwinding) { + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->exceptionUnwind(m_callFrame); + + copyCalleeSavesToVMCalleeSavesBuffer(visitor); + + return StackVisitor::Done; } - } - if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { - DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); - bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); - debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), 0, hasHandler); + copyCalleeSavesToVMCalleeSavesBuffer(visitor); + + return StackVisitor::Continue; } - // Calculate an exception handler vPC, unwinding call frames as necessary. - HandlerInfo* handler = 0; - while (isTermination || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { - if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { - if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler()) - profiler->exceptionUnwind(callFrame); - return 0; +private: + void copyCalleeSavesToVMCalleeSavesBuffer(StackVisitor& visitor) + { +#if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0 + + if (!visitor->isJSFrame()) + return; + +#if ENABLE(DFG_JIT) + if (visitor->inlineCallFrame()) + return; +#endif + RegisterAtOffsetList* currentCalleeSaves = m_codeBlock ? m_codeBlock->calleeSaveRegisters() : nullptr; + + if (!currentCalleeSaves) + return; + + VM& vm = m_callFrame->vm(); + RegisterAtOffsetList* allCalleeSaves = vm.getAllCalleeSaveRegisterOffsets(); + RegisterSet dontCopyRegisters = RegisterSet::stackRegisters(); + intptr_t* frame = reinterpret_cast<intptr_t*>(m_callFrame->registers()); + + unsigned registerCount = currentCalleeSaves->size(); + for (unsigned i = 0; i < registerCount; i++) { + RegisterAtOffset currentEntry = currentCalleeSaves->at(i); + if (dontCopyRegisters.get(currentEntry.reg())) + continue; + RegisterAtOffset* vmCalleeSavesEntry = allCalleeSaves->find(currentEntry.reg()); + + vm.calleeSaveRegistersBuffer[vmCalleeSavesEntry->offsetAsIndex()] = *(frame + currentEntry.offsetAsIndex()); } +#else + UNUSED_PARAM(visitor); +#endif } - if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler()) - profiler->exceptionUnwind(callFrame); - - // Unwind the scope chain within the exception handler's call frame. - JSScope* scope = callFrame->scope(); - int scopeDelta = 0; - if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode - || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) { - int currentDepth = depth(codeBlock, scope); - int targetDepth = handler->scopeDepth; - scopeDelta = currentDepth - targetDepth; - RELEASE_ASSERT(scopeDelta >= 0); + CallFrame*& m_callFrame; + bool m_isTermination; + CodeBlock*& m_codeBlock; + HandlerInfo*& m_handler; +}; + +NEVER_INLINE HandlerInfo* Interpreter::unwind(VM& vm, CallFrame*& callFrame, Exception* exception, UnwindStart unwindStart) +{ + if (unwindStart == UnwindFromCallerFrame) { + if (callFrame->callerFrameOrVMEntryFrame() == vm.topVMEntryFrame) + return nullptr; + + callFrame = callFrame->callerFrame(); + vm.topCallFrame = callFrame; } - while (scopeDelta--) - scope = scope->next(); - callFrame->setScope(scope); + + CodeBlock* codeBlock = callFrame->codeBlock(); + + 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 + // slow cases, so let's harden against it anyway to be safe. + if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) + exceptionValue = jsNull(); + + ASSERT(vm.exception() && vm.exception()->stack().size()); + + // Calculate an exception handler vPC, unwinding call frames as necessary. + HandlerInfo* handler = nullptr; + UnwindFunctor functor(callFrame, isTerminatedExecutionException(exception), codeBlock, handler); + callFrame->iterate(functor); + if (!handler) + return nullptr; return handler; } +void Interpreter::notifyDebuggerOfExceptionToBeThrown(CallFrame* callFrame, Exception* exception) +{ + Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); + if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) { + // 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 isTermination = isTerminatedExecutionException(exception); + if (isTermination) + hasCatchHandler = false; + else { + GetCatchHandlerFunctor functor; + callFrame->iterate(functor); + HandlerInfo* handler = functor.handler(); + ASSERT(!handler || handler->isCatchHandler()); + hasCatchHandler = !!handler; + } + + debugger->exception(callFrame, exception->value(), hasCatchHandler); + } + exception->setDidNotifyInspectorOfThrow(); +} + static inline JSValue checkedReturn(JSValue returnValue) { ASSERT(returnValue); @@ -794,28 +826,26 @@ private: JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) { SamplingScope samplingScope(this); - - JSScope* scope = callFrame->scope(); + + JSScope* scope = thisObj->globalObject()->globalScope(); VM& vm = *scope->vm(); - ASSERT(isValidThisObject(thisObj, callFrame)); - ASSERT(!vm.exception); + ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); - StackStats::CheckPoint stackCheckPoint; - const VMStackBounds vmStackBounds(vm, wtfThreadData().stack()); - if (!vmStackBounds.isSafeToRecurse()) + if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); // First check if the "program" is actually just a JSON object. If so, // we'll handle the JSON object here. Else, we'll handle real JS code // below at failedJSONP. - DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); + Vector<JSONPData> JSONPData; bool parseResult; - const String programSource = program->source().toString(); + StringView programSource = program->source().view(); if (programSource.isNull()) return jsUndefined(); if (programSource.is8Bit()) { @@ -834,12 +864,9 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J JSONPPath.swap(JSONPData[entry].m_path); JSValue JSONPValue = JSONPData[entry].m_value.get(); if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) { - if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) { - PutPropertySlot slot; - globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); - } else - globalObject->methodTable()->putDirectVirtual(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete); - // var declarations return undefined + globalObject->addVar(callFrame, JSONPPath[0].m_pathEntryName); + PutPropertySlot slot(globalObject); + globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); result = jsUndefined(); continue; } @@ -849,10 +876,10 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J switch (JSONPPath[i].m_type) { case JSONPPathEntryTypeDot: { if (i == 0) { - PropertySlot slot(globalObject); + PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get); if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { if (entry) - return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); + return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName)); goto failedJSONP; } baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); @@ -873,7 +900,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J return jsUndefined(); } } - PutPropertySlot slot; + PutPropertySlot slot(baseObject); switch (JSONPPath.last().m_type) { case JSONPPathEntryTypeCall: { JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); @@ -882,7 +909,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J CallData callData; CallType callType = getCallData(function, callData); if (callType == CallTypeNone) - return throwError(callFrame, createNotAFunctionError(callFrame, function)); + return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function)); MarkedArgumentBuffer jsonArg; jsonArg.append(JSONPValue); JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject; @@ -915,47 +942,37 @@ 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(throwError(callFrame, error)); + return checkedReturn(callFrame->vm().throwException(callFrame, error)); - if (JSObject* error = program->compile(callFrame, scope)) - return checkedReturn(throwError(callFrame, error)); + if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall)) + return checkedReturn(callFrame->vm().throwException(callFrame, error)); - ProgramCodeBlock* codeBlock = &program->generatedBytecode(); + ProgramCodeBlock* codeBlock = program->codeBlock(); - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - // Push the call frame for this invocation: ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); - if (UNLIKELY(!newCallFrame)) - return checkedReturn(throwStackOverflowError(callFrame)); - // Set the arguments for the callee: - newCallFrame->setThisValue(thisObj); + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); + profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - -#if ENABLE(LLINT_C_LOOP) - result = LLInt::CLoop::execute(newCallFrame, llint_program_prologue); -#elif ENABLE(JIT) - result = program->generatedJITCode().execute(&m_stack, newCallFrame, &vm); -#endif // ENABLE(JIT) + result = program->generatedJITCode()->execute(&vm, &protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); - - m_stack.popFrame(newCallFrame); + profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); return checkedReturn(result); } @@ -963,52 +980,47 @@ failedJSONP: JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) { VM& vm = callFrame->vm(); - ASSERT(isValidThisObject(thisValue, callFrame)); ASSERT(!callFrame->hadException()); ASSERT(!vm.isCollectorBusy()); if (vm.isCollectorBusy()) return jsNull(); - StackStats::CheckPoint stackCheckPoint; - const VMStackBounds vmStackBounds(vm, wtfThreadData().stack()); - if (!vmStackBounds.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); - bool isJSCall = (callType == CallTypeJS); - JSScope* scope; + JSScope* scope = nullptr; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - if (isJSCall) + JSGlobalObject* globalObject; + + if (isJSCall) { scope = callData.js.scope; - else { + globalObject = scope->globalObject(); + } else { ASSERT(callType == CallTypeHost); - scope = callFrame->scope(); + globalObject = function->globalObject(); } - DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); + + VMEntryScope entryScope(vm, globalObject); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); if (isJSCall) { // Compile the callee: - JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, scope); + JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall); if (UNLIKELY(!!compileError)) { - return checkedReturn(throwError(callFrame, compileError)); + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } - newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); + newCodeBlock = callData.js.functionExecutable->codeBlockForCall(); ASSERT(!!newCodeBlock); + newCodeBlock->m_shouldAlwaysBeInlined = false; } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); - if (UNLIKELY(!newCallFrame)) - return checkedReturn(throwStackOverflowError(callFrame)); - - // Set the arguments for the callee: - newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < args.size(); ++i) - newCallFrame->setArgument(i, args.at(i)); + ProtoCallFrame protoCallFrame; + protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, function); @@ -1016,27 +1028,24 @@ 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) { -#if ENABLE(LLINT_C_LOOP) - result = LLInt::CLoop::execute(newCallFrame, llint_function_for_call_prologue); -#elif ENABLE(JIT) - result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_stack, newCallFrame, &vm); -#endif // ENABLE(JIT) - } else - result = JSValue::decode(callData.native.function(newCallFrame)); + 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(); + } } if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->didExecute(callFrame, function); - m_stack.popFrame(newCallFrame); return checkedReturn(result); } -JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) { VM& vm = callFrame->vm(); ASSERT(!callFrame->hadException()); @@ -1046,47 +1055,42 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc if (vm.isCollectorBusy()) return checkedReturn(throwStackOverflowError(callFrame)); - StackStats::CheckPoint stackCheckPoint; - const VMStackBounds vmStackBounds(vm, wtfThreadData().stack()); - if (!vmStackBounds.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); - bool isJSConstruct = (constructType == ConstructTypeJS); - JSScope* scope; + JSScope* scope = nullptr; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - if (isJSConstruct) + JSGlobalObject* globalObject; + + if (isJSConstruct) { scope = constructData.js.scope; - else { + globalObject = scope->globalObject(); + } else { ASSERT(constructType == ConstructTypeHost); - scope = callFrame->scope(); + globalObject = constructor->globalObject(); } - DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); + VMEntryScope entryScope(vm, globalObject); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); if (isJSConstruct) { // Compile the callee: - JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, scope); + JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct); if (UNLIKELY(!!compileError)) { - return checkedReturn(throwError(callFrame, compileError)); + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } - newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); + newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct(); ASSERT(!!newCodeBlock); + newCodeBlock->m_shouldAlwaysBeInlined = false; } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, constructor); - if (UNLIKELY(!newCallFrame)) - return checkedReturn(throwStackOverflowError(callFrame)); - - // Set the arguments for the callee: - newCallFrame->setThisValue(jsUndefined()); - for (size_t i = 0; i < args.size(); ++i) - newCallFrame->setArgument(i, args.at(i)); + ProtoCallFrame protoCallFrame; + protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, constructor); @@ -1094,68 +1098,49 @@ 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) { -#if ENABLE(LLINT_C_LOOP) - result = LLInt::CLoop::execute(newCallFrame, llint_function_for_construct_prologue); -#elif ENABLE(JIT) - result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, &vm); -#endif // ENABLE(JIT) - } else - result = JSValue::decode(constructData.native.function(newCallFrame)); + if (isJSConstruct) + result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame); + else { + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame)); + + if (!callFrame->hadException()) + RELEASE_ASSERT(result.isObject()); + } } if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->didExecute(callFrame, constructor); - m_stack.popFrame(newCallFrame); - if (callFrame->hadException()) return 0; ASSERT(result.isObject()); return checkedReturn(asObject(result)); } -CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope) +CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, JSValue* args) { VM& vm = *scope->vm(); - ASSERT(!vm.exception); + ASSERT(!vm.exception()); if (vm.isCollectorBusy()) return CallFrameClosure(); - StackStats::CheckPoint stackCheckPoint; - const VMStackBounds vmStackBounds(vm, wtfThreadData().stack()); - if (!vmStackBounds.isSafeToRecurse()) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - // Compile the callee: - JSObject* error = functionExecutable->compileForCall(callFrame, scope); + JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall); if (error) { - throwError(callFrame, error); + callFrame->vm().throwException(callFrame, error); return CallFrameClosure(); } - CodeBlock* newCodeBlock = &functionExecutable->generatedBytecodeForCall(); + CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall(); + newCodeBlock->m_shouldAlwaysBeInlined = false; size_t argsCount = argumentCountIncludingThis; - CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function); - if (UNLIKELY(!newCallFrame)) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - - if (UNLIKELY(!newCallFrame)) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - + protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args); // Return the successful closure: - CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; + CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; return result; } @@ -1165,253 +1150,226 @@ JSValue Interpreter::execute(CallFrameClosure& closure) SamplingScope samplingScope(this); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); StackStats::CheckPoint stackCheckPoint; - m_stack.validateFence(closure.newCallFrame, "BEFORE"); - closure.resetCallFrame(); - m_stack.validateFence(closure.newCallFrame, "STEP 1"); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(closure.oldCallFrame, closure.function); - if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame))) return throwTerminatedExecutionException(closure.oldCallFrame); - // The code execution below may push more frames and point the topCallFrame - // to those newer frames, or it may pop to the top frame to the caller of - // the current repeat frame, or it may leave the top frame pointing to the - // current repeat frame. - // - // Hence, we need to preserve the topCallFrame here ourselves before - // repeating this call on a second callback function. - - TopCallFrameSetter topCallFrame(vm, closure.newCallFrame); - // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - -#if ENABLE(LLINT_C_LOOP) - result = LLInt::CLoop::execute(closure.newCallFrame, llint_function_for_call_prologue); -#elif ENABLE(JIT) - result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, &vm); -#endif // ENABLE(JIT) + result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->didExecute(closure.oldCallFrame, closure.function); - m_stack.validateFence(closure.newCallFrame, "AFTER"); return checkedReturn(result); } -void Interpreter::endRepeatCall(CallFrameClosure& closure) -{ - m_stack.popFrame(closure.newCallFrame); -} - JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) { VM& vm = *scope->vm(); SamplingScope samplingScope(this); ASSERT(scope->vm() == &callFrame->vm()); - ASSERT(isValidThisObject(thisValue, callFrame)); - ASSERT(!vm.exception); + ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); - DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject()); + VMEntryScope entryScope(vm, scope->globalObject()); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); - StackStats::CheckPoint stackCheckPoint; - const VMStackBounds vmStackBounds(vm, wtfThreadData().stack()); - if (!vmStackBounds.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); + unsigned numVariables = eval->numVariables(); + int numFunctions = eval->numberOfFunctionDecls(); - // Compile the callee: - JSObject* compileError = eval->compile(callFrame, scope); + JSScope* variableObject; + if ((numVariables || numFunctions) && eval->isStrictMode()) { + scope = StrictEvalActivation::create(callFrame, scope); + variableObject = scope; + } else { + for (JSScope* node = scope; ; node = node->next()) { + RELEASE_ASSERT(node); + if (node->isGlobalObject()) { + variableObject = node; + break; + } + if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) { + if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) { + variableObject = node; + break; + } + } + } + } + + JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall); if (UNLIKELY(!!compileError)) - return checkedReturn(throwError(callFrame, compileError)); - EvalCodeBlock* codeBlock = &eval->generatedBytecode(); - - JSObject* variableObject; - for (JSScope* node = scope; ; node = node->next()) { - RELEASE_ASSERT(node); - if (node->isVariableObject() && !node->isNameScopeObject()) { - variableObject = node; - break; + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + EvalCodeBlock* codeBlock = eval->codeBlock(); + + // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval. + if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numFunctions)) { + JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalObject*>(variableObject)->globalLexicalEnvironment(); + for (unsigned i = 0; i < numVariables; ++i) { + const Identifier& ident = codeBlock->variable(i); + PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry); + if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, ident, slot)) { + return checkedReturn(callFrame->vm().throwException(callFrame, + createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(ident.impl()), "'")))); + } + } + + for (int i = 0; i < numFunctions; ++i) { + FunctionExecutable* function = codeBlock->functionDecl(i); + PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry); + if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, function->name(), slot)) { + return checkedReturn(callFrame->vm().throwException(callFrame, + createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(function->name().impl()), "'")))); + } } } - unsigned numVariables = codeBlock->numVariables(); - int numFunctions = codeBlock->numberOfFunctionDecls(); if (numVariables || numFunctions) { - if (codeBlock->isStrictMode()) { - scope = StrictEvalActivation::create(callFrame); - variableObject = scope; - } - // Scope for BatchedTransitionOptimizer BatchedTransitionOptimizer optimizer(vm, variableObject); + if (variableObject->next()) + variableObject->globalObject()->varInjectionWatchpoint()->fireAll("Executed eval, fired VarInjection watchpoint"); for (unsigned i = 0; i < numVariables; ++i) { const Identifier& ident = codeBlock->variable(i); if (!variableObject->hasProperty(callFrame, ident)) { - PutPropertySlot slot; + PutPropertySlot slot(variableObject); variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); } } for (int i = 0; i < numFunctions; ++i) { FunctionExecutable* function = codeBlock->functionDecl(i); - PutPropertySlot slot; - variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(callFrame, function, scope), slot); + PutPropertySlot slot(variableObject); + variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot); } } - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - // Push the frame: ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0); - if (UNLIKELY(!newCallFrame)) - return checkedReturn(throwStackOverflowError(callFrame)); - // Set the arguments for the callee: - newCallFrame->setThisValue(thisValue); + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); + profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - -#if ENABLE(LLINT_C_LOOP) - result = LLInt::CLoop::execute(newCallFrame, llint_eval_prologue); -#elif ENABLE(JIT) - result = eval->generatedJITCode().execute(&m_stack, newCallFrame, &vm); -#endif // ENABLE(JIT) + result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); + profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); - m_stack.popFrame(newCallFrame); return checkedReturn(result); } -NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID, int firstLine, int lastLine, int column) +JSValue Interpreter::execute(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope) { - Debugger* debugger = callFrame->dynamicGlobalObject()->debugger(); - if (!debugger) - return; + VM& vm = *scope->vm(); + SamplingScope samplingScope(this); - switch (debugHookID) { - case DidEnterCallFrame: - debugger->callEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); - return; - case WillLeaveCallFrame: - debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); - return; - case WillExecuteStatement: - debugger->atStatement(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); - return; - case WillExecuteProgram: - debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column); - return; - case DidExecuteProgram: - debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); - return; - case DidReachBreakpoint: - debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column); - return; - } -} - -JSValue Interpreter::retrieveArgumentsFromVMCode(CallFrame* callFrame, JSFunction* function) const -{ - CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function); - if (!functionCallFrame) + ASSERT(scope->vm() == &callFrame->vm()); + ASSERT(!vm.exception()); + ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusy()) return jsNull(); - Arguments* arguments = Arguments::create(functionCallFrame->vm(), functionCallFrame); - arguments->tearOff(functionCallFrame); - return JSValue(arguments); -} + VMEntryScope entryScope(vm, scope->globalObject()); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); -JSValue Interpreter::retrieveCallerFromVMCode(CallFrame* callFrame, JSFunction* function) const -{ - CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function); + JSObject* compileError = executable->prepareForExecution(callFrame, nullptr, scope, CodeForCall); + if (UNLIKELY(!!compileError)) + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + ModuleProgramCodeBlock* codeBlock = executable->codeBlock(); - if (!functionCallFrame) - return jsNull(); - - unsigned bytecodeOffset; - CodeBlock* unusedCallerCodeBlock = 0; - CallFrame* callerFrame = getCallerInfo(&callFrame->vm(), functionCallFrame, bytecodeOffset, unusedCallerCodeBlock); - if (!callerFrame) - return jsNull(); - JSValue caller = callerFrame->callee(); - if (!caller) - return jsNull(); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + + // The |this| of the module is always `undefined`. + // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-hasthisbinding + // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-getthisbinding + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), jsUndefined(), 1); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn()); - // Skip over function bindings. - ASSERT(caller.isObject()); - while (asObject(caller)->inherits(&JSBoundFunction::s_info)) { - callerFrame = getCallerInfo(&callFrame->vm(), callerFrame, bytecodeOffset, unusedCallerCodeBlock); - if (!callerFrame) - return jsNull(); - caller = callerFrame->callee(); - if (!caller) - return jsNull(); + // Execute the code: + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + result = executable->generatedJITCode()->execute(&vm, &protoCallFrame); } - return caller; + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn()); + + return checkedReturn(result); } -void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, String& sourceURL, JSValue& function) const +NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID) { - function = JSValue(); - lineNumber = -1; - sourceURL = String(); - - CallFrame* callerFrame = callFrame->callerFrame(); - if (callerFrame->hasHostCallFrameFlag()) + Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); + if (!debugger) return; - CodeBlock* callerCodeBlock = callerFrame->codeBlock(); - if (!callerCodeBlock) - return; - unsigned bytecodeOffset = 0; - bytecodeOffset = callerCodeBlock->bytecodeOffset(callerFrame, callFrame->returnPC()); - lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); - sourceID = callerCodeBlock->ownerExecutable()->sourceID(); - sourceURL = callerCodeBlock->ownerExecutable()->sourceURL(); - function = callerFrame->callee(); -} + ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); + ASSERT(!callFrame->hadException()); -CallFrame* Interpreter::findFunctionCallFrameFromVMCode(CallFrame* callFrame, JSFunction* function) -{ - for (CallFrame* candidate = callFrame->trueCallFrameFromVMCode(); candidate; candidate = candidate->trueCallerFrame()) { - if (candidate->callee() == function) - return candidate; + switch (debugHookID) { + case DidEnterCallFrame: + debugger->callEvent(callFrame); + break; + case WillLeaveCallFrame: + debugger->returnEvent(callFrame); + break; + case WillExecuteStatement: + debugger->atStatement(callFrame); + break; + case WillExecuteProgram: + debugger->willExecuteProgram(callFrame); + break; + case DidExecuteProgram: + debugger->didExecuteProgram(callFrame); + break; + case DidReachBreakpoint: + debugger->didReachBreakpoint(callFrame); + break; } - return 0; -} + ASSERT(!callFrame->hadException()); +} void Interpreter::enableSampler() { #if ENABLE(OPCODE_SAMPLING) if (!m_sampler) { - m_sampler = adoptPtr(new SamplingTool(this)); + m_sampler = std::make_unique<SamplingTool>(this); m_sampler->setup(); } #endif diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h index e5eca0e1c..aba745f17 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.h +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013, 2015 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 Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple 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,11 +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> @@ -49,16 +50,23 @@ namespace JSC { class ExecutableBase; class FunctionExecutable; class VM; + class JSFunction; class JSGlobalObject; + class JSModuleEnvironment; + class JSModuleRecord; class LLIntOffsetsExtractor; class ProgramExecutable; + class ModuleProgramExecutable; class Register; class JSScope; class SamplingTool; struct CallFrameClosure; struct HandlerInfo; struct Instruction; - + struct ProtoCallFrame; + + enum UnwindStart { UnwindFromCurrentFrame, UnwindFromCallerFrame }; + enum DebugHookID { WillExecuteProgram, DidExecuteProgram, @@ -71,6 +79,7 @@ namespace JSC { enum StackFrameCodeType { StackFrameGlobalCode, StackFrameEvalCode, + StackFrameModuleCode, StackFrameFunctionCode, StackFrameNativeCode }; @@ -78,7 +87,7 @@ namespace JSC { struct StackFrame { Strong<JSObject> callee; StackFrameCodeType codeType; - Strong<ExecutableBase> executable; + Strong<ScriptExecutable> executable; Strong<UnlinkedCodeBlock> codeBlock; RefPtr<SourceProvider> code; int lineOffset; @@ -87,58 +96,38 @@ namespace JSC { unsigned bytecodeOffset; String sourceURL; JS_EXPORT_PRIVATE String toString(CallFrame*); - 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; - } + String friendlySourceURL() const; + String friendlyFunctionName(CallFrame*) const; JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); private: void expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); }; + class SuspendExceptionScope { + public: + SuspendExceptionScope(VM* vm) + : m_vm(vm) + { + oldException = vm->exception(); + vm->clearException(); + } + ~SuspendExceptionScope() + { + m_vm->restorePreviousException(oldException); + } + private: + Exception* oldException; + VM* m_vm; + }; + class TopCallFrameSetter { public: - TopCallFrameSetter(VM& global, CallFrame* callFrame) - : vm(global) - , oldCallFrame(global.topCallFrame) + TopCallFrameSetter(VM& currentVM, CallFrame* callFrame) + : vm(currentVM) + , oldCallFrame(currentVM.topCallFrame) { - global.topCallFrame = callFrame; + currentVM.topCallFrame = callFrame; } ~TopCallFrameSetter() @@ -152,33 +141,52 @@ namespace JSC { class NativeCallFrameTracer { public: - ALWAYS_INLINE NativeCallFrameTracer(VM* global, CallFrame* callFrame) + ALWAYS_INLINE NativeCallFrameTracer(VM* vm, CallFrame* callFrame) { - ASSERT(global); + ASSERT(vm); ASSERT(callFrame); - global->topCallFrame = callFrame; + ASSERT(callFrame < vm->topVMEntryFrame); + 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; friend class LLIntOffsetsExtractor; friend class JIT; + friend class VM; public: - class ErrorHandlingMode { - public: - JS_EXPORT_PRIVATE ErrorHandlingMode(ExecState*); - JS_EXPORT_PRIVATE ~ErrorHandlingMode(); - private: - Interpreter& m_interpreter; - }; - Interpreter(VM &); ~Interpreter(); - void initialize(bool canUseJIT); + void initialize(); JSStack& stack() { return m_stack; } @@ -195,7 +203,7 @@ namespace JSC { OpcodeID getOpcodeID(Opcode opcode) { ASSERT(m_initialized); -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) ASSERT(isOpcode(opcode)); return m_opcodeIDTable.get(opcode); #else @@ -207,24 +215,23 @@ 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&); + JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*); + JSValue execute(ModuleProgramExecutable*, CallFrame*, JSModuleEnvironment*); - JSValue retrieveArgumentsFromVMCode(CallFrame*, JSFunction*) const; - JSValue retrieveCallerFromVMCode(CallFrame*, JSFunction*) const; - JS_EXPORT_PRIVATE void retrieveLastCaller(CallFrame*, int& lineNumber, intptr_t& sourceID, String& sourceURL, JSValue& function) const; - void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); SamplingTool* sampler() { return m_sampler.get(); } - bool isInErrorHandlingMode() { return m_errorHandlingModeReentry; } + NEVER_INLINE HandlerInfo* unwind(VM&, CallFrame*&, Exception*, UnwindStart); + void notifyDebuggerOfExceptionToBeThrown(CallFrame*, Exception*); + NEVER_INLINE void debug(CallFrame*, DebugHookID); + JSString* stackTraceAsString(ExecState*, Vector<StackFrame>); - NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset); - NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine, int column); - static const String getTraceLine(CallFrame*, StackFrameCodeType, const String&, int); - JS_EXPORT_PRIVATE static void getStackTrace(VM*, Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max()); - static void addStackTraceIfNecessary(CallFrame*, JSValue error); + static EncodedJSValue JSC_HOST_CALL constructWithErrorConstructor(ExecState*); + static EncodedJSValue JSC_HOST_CALL callErrorConstructor(ExecState*); + static EncodedJSValue JSC_HOST_CALL constructWithNativeErrorConstructor(ExecState*); + static EncodedJSValue JSC_HOST_CALL callNativeErrorConstructor(ExecState*); void dumpSampleData(ExecState* exec); void startSampling(); @@ -232,29 +239,30 @@ 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 }; - CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*); - void endRepeatCall(CallFrameClosure&); + CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, ProtoCallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*, JSValue*); + JSValue execute(CallFrameClosure&); - NEVER_INLINE bool unwindCallFrame(CallFrame*&, JSValue, unsigned& bytecodeOffset, CodeBlock*&); - static CallFrame* findFunctionCallFrameFromVMCode(CallFrame*, JSFunction*); void dumpRegisters(CallFrame*); - bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval); } + bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval) || opcode == getOpcode(op_tail_call); } void enableSampler(); int m_sampleEntryDepth; - OwnPtr<SamplingTool> m_sampler; + std::unique_ptr<SamplingTool> m_sampler; + VM& m_vm; JSStack m_stack; int m_errorHandlingModeReentry; -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling #endif @@ -264,15 +272,30 @@ namespace JSC { #endif }; - // This value must not be an object that would require this conversion (WebCore's global object). - inline bool isValidThisObject(JSValue thisValue, ExecState* exec) + JSValue eval(CallFrame*); + + inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis) { - return !thisValue.isObject() || thisValue.toThisObject(exec) == thisValue; + // We want the new frame to be allocated on a stack aligned offset with a stack + // aligned size. Align the size here. + argumentCountIncludingThis = WTF::roundUpToMultipleOf( + stackAlignmentRegisters(), + argumentCountIncludingThis + JSStack::CallFrameHeaderSize) - JSStack::CallFrameHeaderSize; + + // Align the frame offset here. + unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf( + stackAlignmentRegisters(), + numUsedStackSlots + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); + return CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); } - JSValue eval(CallFrame*); - CallFrame* loadVarargs(CallFrame*, JSStack*, JSValue thisValue, JSValue arguments, int firstFreeRegister); - + 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); + } // namespace JSC #endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp index ec2962a92..6e441dfd5 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.cpp +++ b/Source/JavaScriptCore/interpreter/JSStack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013, 2014, 2015 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 Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple 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. * @@ -27,125 +27,158 @@ */ #include "config.h" -#include "JSStack.h" #include "JSStackInlines.h" #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 Mutex& stackStatisticsMutex() +static size_t commitSize() { - DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); - return staticMutex; -} + static size_t size = std::max<size_t>(16 * 1024, pageSize()); + return size; +} + +static StaticLock stackStatisticsMutex; +#endif // !ENABLE(JIT) -JSStack::JSStack(VM& vm, size_t capacity) - : m_end(0) +JSStack::JSStack(VM& vm) + : m_vm(vm) , m_topCallFrame(vm.topCallFrame) +#if !ENABLE(JIT) + , m_end(0) + , m_reservedZoneSizeInRegisters(0) +#endif { +#if !ENABLE(JIT) + size_t capacity = Options::maxPerThreadStackUsage(); ASSERT(capacity && isPageAligned(capacity)); - m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); - m_end = static_cast<Register*>(m_reservation.base()); - m_commitEnd = static_cast<Register*>(m_reservation.base()); - - disableErrorStackReserve(); + m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize(), capacity), OSAllocator::JSVMStackPages); + setStackLimit(highAddress()); + m_commitTop = highAddress(); + + m_lastStackTop = baseOfStack(); +#endif // !ENABLE(JIT) m_topCallFrame = 0; } +#if !ENABLE(JIT) JSStack::~JSStack() { - void* base = m_reservation.base(); - m_reservation.decommit(base, reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base)); - addToCommittedByteCount(-(reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base))); + ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop); + m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit); + addToCommittedByteCount(-sizeToDecommit); m_reservation.deallocate(); } -bool JSStack::growSlowCase(Register* newEnd) +bool JSStack::growSlowCase(Register* newTopOfStack) { + Register* newTopOfStackWithReservedZone = newTopOfStack - m_reservedZoneSizeInRegisters; + // If we have already committed enough memory to satisfy this request, // just update the end pointer and return. - if (newEnd <= m_commitEnd) { - m_end = newEnd; + if (newTopOfStackWithReservedZone >= m_commitTop) { + setStackLimit(newTopOfStack); 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. - long delta = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize); - if (reinterpret_cast<char*>(m_commitEnd) + delta > reinterpret_cast<char*>(m_useableEnd)) + 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()) return false; - // Otherwise, the growth is still within our budget. Go ahead and commit - // it and return true. - m_reservation.commit(m_commitEnd, delta); + // Otherwise, the growth is still within our budget. Commit it and return true. + m_reservation.commit(newCommitTop, delta); addToCommittedByteCount(delta); - m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + delta); - m_end = newEnd; + m_commitTop = newCommitTop; + setStackLimit(newTopOfStack); return true; } -void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots) +void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) { - conservativeRoots.add(begin(), getTopOfStack()); + conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks); } -void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, DFGCodeBlocks& dfgCodeBlocks) +void JSStack::sanitizeStack() { - conservativeRoots.add(begin(), getTopOfStack(), jitStubRoutines, dfgCodeBlocks); +#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 } void JSStack::releaseExcessCapacity() { - ptrdiff_t delta = reinterpret_cast<uintptr_t>(m_commitEnd) - reinterpret_cast<uintptr_t>(m_reservation.base()); - m_reservation.decommit(m_reservation.base(), delta); + Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters; + ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop); + m_reservation.decommit(m_commitTop, delta); addToCommittedByteCount(-delta); - m_commitEnd = static_cast<Register*>(m_reservation.base()); + m_commitTop = highAddressWithReservedZone; } -void JSStack::initializeThreading() +void JSStack::addToCommittedByteCount(long byteCount) { - stackStatisticsMutex(); + LockHolder locker(stackStatisticsMutex); + ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); + committedBytesCount += byteCount; } -size_t JSStack::committedByteCount() +void JSStack::setReservedZoneSize(size_t reservedZoneSize) { - MutexLocker locker(stackStatisticsMutex()); - return committedBytesCount; + m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register); + if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters) + growSlowCase(m_end + 1); } +#endif // !ENABLE(JIT) -void JSStack::addToCommittedByteCount(long byteCount) +#if ENABLE(JIT) +Register* JSStack::lowAddress() const { - MutexLocker locker(stackStatisticsMutex()); - ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); - committedBytesCount += byteCount; + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(m_vm.stackLimit()); } -void JSStack::enableErrorStackReserve() +Register* JSStack::highAddress() const { - m_useableEnd = reservationEnd(); + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(wtfThreadData().stack().origin()); } +#endif // ENABLE(JIT) -void JSStack::disableErrorStackReserve() +size_t JSStack::committedByteCount() { - 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); - } +#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 } } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h index fe4012d83..770db0920 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.h +++ b/Source/JavaScriptCore/interpreter/JSStack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2013, 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 @@ -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 Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple 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,124 +35,117 @@ #include <wtf/PageReservation.h> #include <wtf/VMTags.h> -#if !defined(NDEBUG) && !defined(ENABLE_DEBUG_JSSTACK) -#define ENABLE_DEBUG_JSSTACK 1 -#endif - namespace JSC { + class CodeBlockSet; class ConservativeRoots; - class DFGCodeBlocks; class ExecState; class JITStubRoutineSet; class VM; class LLIntOffsetsExtractor; + struct Instruction; + typedef ExecState CallFrame; + + struct CallerFrameAndPC { + CallFrame* callerFrame; + Instruction* pc; + }; + class JSStack { WTF_MAKE_NONCOPYABLE(JSStack); public: enum CallFrameHeaderEntry { - CallFrameHeaderSize = 6, - - ArgumentCount = -6, - CallerFrame = -5, - Callee = -4, - ScopeChain = -3, - ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. - CodeBlock = -1, + CallerFrameAndPCSize = sizeof(CallerFrameAndPC) / sizeof(Register), + CodeBlock = CallerFrameAndPCSize, + Callee, + ArgumentCount, + CallFrameHeaderSize, + + // The following entries are not part of the CallFrameHeader but are provided here as a convenience: + ThisArgument = CallFrameHeaderSize, + 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&, size_t capacity = defaultCapacity); - ~JSStack(); + JSStack(VM&); - void gatherConservativeRoots(ConservativeRoots&); - void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, DFGCodeBlocks&); - - Register* begin() const { return static_cast<Register*>(m_reservation.base()); } - Register* end() const { return m_end; } - size_t size() const { return end() - begin(); } + bool ensureCapacityFor(Register* newTopOfStack); - bool grow(Register*); - + bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); } static size_t committedByteCount(); - static void initializeThreading(); - Register* const * addressOfEnd() const - { - return &m_end; - } +#if ENABLE(JIT) + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { } + void sanitizeStack() { } +#else + ~JSStack(); - Register* getTopOfFrame(CallFrame*); - Register* getStartOfFrame(CallFrame*); - Register* getTopOfStack(); + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&); + void sanitizeStack(); - CallFrame* pushFrame(CallFrame* callerFrame, class CodeBlock*, - JSScope*, int argsCount, JSObject* callee); + Register* baseOfStack() const + { + return highAddress() - 1; + } - void popFrame(CallFrame*); + size_t size() const { return highAddress() - lowAddress(); } - void enableErrorStackReserve(); - void disableErrorStackReserve(); + void setReservedZoneSize(size_t); -#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) + inline Register* topOfStack(); +#endif // ENABLE(JIT) private: - Register* reservationEnd() const + +#if !ENABLE(JIT) + Register* lowAddress() const { - char* base = static_cast<char*>(m_reservation.base()); - char* reservationEnd = base + m_reservation.size(); - return reinterpret_cast_ptr<Register*>(reservationEnd); + return m_end + 1; } -#if ENABLE(DEBUG_JSSTACK) - static JSValue generateFenceValue(size_t argIndex); - void installTrapsAfterFrame(CallFrame*); + Register* highAddress() const + { + return reinterpret_cast_ptr<Register*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); + } #else - void installTrapsAfterFrame(CallFrame*) { } -#endif + Register* lowAddress() const; + Register* highAddress() const; +#endif // !ENABLE(JIT) + +#if !ENABLE(JIT) + inline Register* topOfFrameFor(CallFrame*); + + Register* reservationTop() const + { + char* reservationTop = static_cast<char*>(m_reservation.base()); + return reinterpret_cast_ptr<Register*>(reservationTop); + } - bool growSlowCase(Register*); - void shrink(Register*); + bool grow(Register* newTopOfStack); + bool growSlowCase(Register* newTopOfStack); + void shrink(Register* newTopOfStack); void releaseExcessCapacity(); void addToCommittedByteCount(long); + void setStackLimit(Register* newTopOfStack); +#endif // !ENABLE(JIT) + + VM& m_vm; + CallFrame*& m_topCallFrame; +#if !ENABLE(JIT) Register* m_end; - Register* m_commitEnd; - Register* m_useableEnd; + Register* m_commitTop; PageReservation m_reservation; - CallFrame*& m_topCallFrame; + Register* m_lastStackTop; + ptrdiff_t m_reservedZoneSizeInRegisters; +#endif // !ENABLE(JIT) friend class LLIntOffsetsExtractor; }; - inline void JSStack::shrink(Register* newEnd) - { - if (newEnd >= m_end) - return; - m_end = newEnd; - if (m_end == m_reservation.base() && (m_commitEnd - begin()) >= maxExcessCapacity) - releaseExcessCapacity(); - } - - inline bool JSStack::grow(Register* newEnd) - { - if (newEnd <= m_end) - return true; - return growSlowCase(newEnd); - } - } // namespace JSC #endif // JSStack_h diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h index d316b5992..69508ab5d 100644 --- a/Source/JavaScriptCore/interpreter/JSStackInlines.h +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 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 @@ -29,202 +29,65 @@ #include "CallFrame.h" #include "CodeBlock.h" #include "JSStack.h" +#include "VM.h" namespace JSC { -inline Register* JSStack::getTopOfFrame(CallFrame* frame) +inline bool JSStack::ensureCapacityFor(Register* newTopOfStack) { - if (UNLIKELY(!frame)) - return begin(); - return frame->frameExtent(); -} - -inline Register* JSStack::getTopOfStack() -{ - return getTopOfFrame(m_topCallFrame); +#if !ENABLE(JIT) + return grow(newTopOfStack); +#else + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return newTopOfStack >= m_vm.stackLimit(); +#endif } -inline Register* JSStack::getStartOfFrame(CallFrame* frame) -{ - CallFrame* callerFrame = frame->callerFrameNoFlags(); - return getTopOfFrame(callerFrame); -} +#if !ENABLE(JIT) -inline CallFrame* JSStack::pushFrame(CallFrame* callerFrame, - class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) +inline Register* JSStack::topOfFrameFor(CallFrame* frame) { - 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 + JSStack::CallFrameHeaderSize; -#if ENABLE(DEBUG_JSSTACK) - newCallFrameSlot += JSStack::FenceSize; -#endif - Register* newEnd = newCallFrameSlot; - if (!!codeBlock) - newEnd += codeBlock->m_numCalleeRegisters; - - // Ensure that we have the needed stack capacity to push the new frame: - if (!grow(newEnd)) - return 0; - - // 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. - callerFrame = m_topCallFrame; - - // Initialize the frame header: - newCallFrame->init(codeBlock, 0, scope, - callerFrame->addHostCallFrameFlag(), 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; + if (UNLIKELY(!frame)) + return baseOfStack(); + return frame->topOfFrame() - 1; } -inline void JSStack::popFrame(CallFrame* frame) +inline Register* JSStack::topOfStack() { - validateFence(frame, __FUNCTION__, __LINE__); - CallFrame* callerFrame = frame->callerFrameNoFlags(); - - // 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(begin()); - - installTrapsAfterFrame(callerFrame); + return topOfFrameFor(m_topCallFrame); } - -#if ENABLE(DEBUG_JSSTACK) -inline JSValue JSStack::generateFenceValue(size_t argIndex) +inline void JSStack::shrink(Register* newTopOfStack) { - unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); - JSValue fenceValue = JSValue(fenceBits); - return fenceValue; + 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) + releaseExcessCapacity(); } -// 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 *** | -// |--------------------------------------| -// | 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) +inline bool JSStack::grow(Register* newTopOfStack) { - 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); - } + Register* newEnd = newTopOfStack - 1; + if (newEnd >= m_end) + return true; + return growSlowCase(newTopOfStack); } -inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) +inline void JSStack::setStackLimit(Register* newTopOfStack) { - 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); - } + Register* newEnd = newTopOfStack - 1; + m_end = newEnd; + m_vm.setJSStackLimit(newTopOfStack); } -// 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) +#endif // !ENABLE(JIT) } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp new file mode 100644 index 000000000..eb80b2c23 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp @@ -0,0 +1,53 @@ +/* + * 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 + * 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 "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) +{ + this->args = otherArgs; + this->setCodeBlock(codeBlock); + 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); + this->clearCurrentVPC(); + this->setThisValue(thisValue); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h new file mode 100644 index 000000000..af33a3072 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h @@ -0,0 +1,76 @@ +/* + * 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 + * 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 ProtoCallFrame_h +#define ProtoCallFrame_h + +#include "Register.h" + +namespace JSC { + +struct ProtoCallFrame { + Register codeBlockValue; + Register calleeValue; + Register argCountAndCodeOriginValue; + Register thisArg; + uint32_t paddedArgCount; + bool arityMissMatch; + JSValue *args; + + void init(CodeBlock*, 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; } + + 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 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()); + return args[argumentIndex]; + } + void setArgument(size_t argumentIndex, JSValue value) + { + ASSERT(static_cast<int>(argumentIndex) < argumentCount()); + args[argumentIndex] = value; + } +}; + +} // namespace JSC + +#endif // ProtoCallFrame_h diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h index bc2335689..c0396c966 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 Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple 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,14 +37,10 @@ namespace JSC { class CodeBlock; class ExecState; - class JSActivation; + class JSLexicalEnvironment; class JSObject; - class JSPropertyNameIterator; class JSScope; - struct InlineCallFrame; - struct Instruction; - typedef ExecState CallFrame; class Register { @@ -55,28 +51,29 @@ 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=(Instruction*); - Register& operator=(InlineCallFrame*); + Register& operator=(JSObject*); int32_t i() const; - JSActivation* activation() const; CallFrame* callFrame() const; CodeBlock* codeBlock() const; - JSObject* function() const; - JSPropertyNameIterator* propertyNameIterator() const; + CodeBlock* asanUnsafeCodeBlock() const; + JSObject* object() const; JSScope* scope() const; - Instruction* vPC() const; - InlineCallFrame* asInlineCallFrame() const; int32_t unboxedInt32() const; + int64_t unboxedInt52() const; + int64_t unboxedStrictInt52() const; bool unboxedBoolean() const; + double unboxedDouble() const; JSCell* unboxedCell() const; int32_t payload() const; int32_t tag() const; + int32_t unsafeTag() const; int32_t& payload(); int32_t& tag(); @@ -86,16 +83,14 @@ namespace JSC { return r; } - static Register withCallee(JSObject* callee); - private: union { EncodedJSValue value; CallFrame* callFrame; CodeBlock* codeBlock; - Instruction* vPC; - InlineCallFrame* inlineCallFrame; EncodedValueDescriptor encodedValue; + double number; + int64_t integer; } u; }; @@ -117,6 +112,12 @@ 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); @@ -141,18 +142,6 @@ namespace JSC { return *this; } - ALWAYS_INLINE Register& Register::operator=(Instruction* vPC) - { - u.vPC = vPC; - return *this; - } - - ALWAYS_INLINE Register& Register::operator=(InlineCallFrame* inlineCallFrame) - { - u.inlineCallFrame = inlineCallFrame; - return *this; - } - ALWAYS_INLINE int32_t Register::i() const { return jsValue().asInt32(); @@ -168,26 +157,36 @@ namespace JSC { return u.codeBlock; } - ALWAYS_INLINE Instruction* Register::vPC() const + SUPPRESS_ASAN ALWAYS_INLINE CodeBlock* Register::asanUnsafeCodeBlock() const { - return u.vPC; + return u.codeBlock; } - ALWAYS_INLINE InlineCallFrame* Register::asInlineCallFrame() const - { - return u.inlineCallFrame; - } - ALWAYS_INLINE int32_t Register::unboxedInt32() const { return payload(); } + ALWAYS_INLINE int64_t Register::unboxedInt52() const + { + return u.integer >> JSValue::int52ShiftAmount; + } + + ALWAYS_INLINE int64_t Register::unboxedStrictInt52() const + { + return u.integer; + } + ALWAYS_INLINE bool Register::unboxedBoolean() const { return !!payload(); } + ALWAYS_INLINE double Register::unboxedDouble() const + { + return u.number; + } + ALWAYS_INLINE JSCell* Register::unboxedCell() const { #if USE(JSVALUE64) @@ -207,6 +206,11 @@ namespace JSC { return u.encodedValue.asBits.tag; } + SUPPRESS_ASAN ALWAYS_INLINE int32_t Register::unsafeTag() const + { + return u.encodedValue.asBits.tag; + } + ALWAYS_INLINE int32_t& Register::payload() { return u.encodedValue.asBits.payload; diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp new file mode 100644 index 000000000..c0c03d280 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2013, 2015 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 "StackVisitor.h" + +#include "ClonedArguments.h" +#include "Executable.h" +#include "InlineCallFrame.h" +#include "Interpreter.h" +#include "JSCInlines.h" +#include <wtf/DataLog.h> + +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(); +} + +void StackVisitor::gotoNextFrame() +{ +#if ENABLE(DFG_JIT) + if (m_frame.isInlinedFrame()) { + InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); + CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls(); + if (!callerCodeOrigin) { + while (inlineCallFrame) { + readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller); + inlineCallFrame = m_frame.inlineCallFrame(); + } + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); + } else + readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); + return; + } +#endif // ENABLE(DFG_JIT) + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); +} + +void StackVisitor::unwindToMachineCodeBlockFrame() +{ +#if ENABLE(DFG_JIT) + while (m_frame.isInlinedFrame()) + gotoNextFrame(); +#endif +} + +void StackVisitor::readFrame(CallFrame* callFrame) +{ + if (!callFrame) { + m_frame.setToEnd(); + return; + } + +#if !ENABLE(DFG_JIT) + readNonInlinedFrame(callFrame); + +#else // !ENABLE(DFG_JIT) + // If the frame doesn't have a code block, then it's not a DFG frame. + // Hence, we're not at an inlined frame. + CodeBlock* codeBlock = callFrame->codeBlock(); + if (!codeBlock) { + readNonInlinedFrame(callFrame); + return; + } + + // If the code block does not have any code origins, then there's no + // inlining. Hence, we're not at an inlined frame. + if (!codeBlock->hasCodeOrigins()) { + readNonInlinedFrame(callFrame); + return; + } + + CallSiteIndex index = callFrame->callSiteIndex(); + ASSERT(codeBlock->canGetCodeOrigin(index)); + if (!codeBlock->canGetCodeOrigin(index)) { + // See assertion above. In release builds, we try to protect ourselves + // from crashing even though stack walking will be goofed up. + m_frame.setToEnd(); + return; + } + + CodeOrigin codeOrigin = codeBlock->codeOrigin(index); + if (!codeOrigin.inlineCallFrame) { + readNonInlinedFrame(callFrame, &codeOrigin); + return; + } + + readInlinedFrame(callFrame, &codeOrigin); +#endif // !ENABLE(DFG_JIT) +} + +void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) +{ + 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_callee = callFrame->callee(); + m_frame.m_codeBlock = callFrame->codeBlock(); + m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 + : codeOrigin ? codeOrigin->bytecodeIndex + : callFrame->bytecodeOffset(); +#if ENABLE(DFG_JIT) + m_frame.m_inlineCallFrame = 0; +#endif +} + +#if ENABLE(DFG_JIT) +static int inlinedFrameOffset(CodeOrigin* codeOrigin) +{ + InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; + int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0; + return frameOffset; +} + +void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) +{ + ASSERT(codeOrigin); + + int frameOffset = inlinedFrameOffset(codeOrigin); + bool isInlined = !!frameOffset; + if (isInlined) { + InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame; + + 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_codeBlock = inlineCallFrame->baselineCodeBlock.get(); + m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; + + JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); + m_frame.m_callee = callee; + ASSERT(m_frame.callee()); + + // The callerFrame just needs to be non-null to indicate that we + // haven't reached the last frame yet. Setting it to the root + // frame (i.e. the callFrame that this inlined frame is called from) + // would work just fine. + m_frame.m_callerFrame = callFrame; + return; + } + + readNonInlinedFrame(callFrame, codeOrigin); +} +#endif // ENABLE(DFG_JIT) + +StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const +{ + if (!isJSFrame()) + return CodeType::Native; + + switch (codeBlock()->codeType()) { + case EvalCode: + return CodeType::Eval; + case ModuleCode: + return CodeType::Module; + case FunctionCode: + return CodeType::Function; + case GlobalCode: + return CodeType::Global; + } + RELEASE_ASSERT_NOT_REACHED(); + return CodeType::Global; +} + +String StackVisitor::Frame::functionName() +{ + String traceLine; + JSObject* callee = this->callee(); + + switch (codeType()) { + case CodeType::Eval: + traceLine = ASCIILiteral("eval code"); + break; + case CodeType::Module: + traceLine = ASCIILiteral("module code"); + break; + case CodeType::Native: + if (callee) + traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + break; + case CodeType::Function: + traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); + break; + case CodeType::Global: + traceLine = ASCIILiteral("global code"); + break; + } + return traceLine.isNull() ? emptyString() : traceLine; +} + +String StackVisitor::Frame::sourceURL() +{ + String traceLine; + + switch (codeType()) { + case CodeType::Eval: + case CodeType::Module: + case CodeType::Function: + case CodeType::Global: { + String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL(); + if (!sourceURL.isEmpty()) + traceLine = sourceURL.impl(); + break; + } + case CodeType::Native: + traceLine = ASCIILiteral("[native code]"); + break; + } + return traceLine.isNull() ? emptyString() : traceLine; +} + +String StackVisitor::Frame::toString() +{ + StringBuilder traceBuild; + String functionName = this->functionName(); + String sourceURL = this->sourceURL(); + traceBuild.append(functionName); + if (!sourceURL.isEmpty()) { + if (!functionName.isEmpty()) + traceBuild.append('@'); + traceBuild.append(sourceURL); + if (isJSFrame()) { + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + traceBuild.append(':'); + traceBuild.appendNumber(line); + traceBuild.append(':'); + traceBuild.appendNumber(column); + } + } + return traceBuild.toString().impl(); +} + +ClonedArguments* StackVisitor::Frame::createArguments() +{ + ASSERT(m_callFrame); + CallFrame* physicalFrame = m_callFrame; + ClonedArguments* arguments; + ArgumentsMode mode; + if (Options::useFunctionDotArguments()) + mode = ArgumentsMode::Cloned; + else + mode = ArgumentsMode::FakeValues; +#if ENABLE(DFG_JIT) + if (isInlinedFrame()) { + ASSERT(m_inlineCallFrame); + arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode); + } else +#endif + arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode); + return arguments; +} + +void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) +{ + CodeBlock* codeBlock = this->codeBlock(); + if (!codeBlock) { + line = 0; + column = 0; + return; + } + + int divot = 0; + int unusedStartOffset = 0; + int unusedEndOffset = 0; + unsigned divotLine = 0; + unsigned divotColumn = 0; + retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); + + line = divotLine + codeBlock->ownerScriptExecutable()->firstLine(); + column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); + + if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber()) + line = codeBlock->ownerScriptExecutable()->overrideLineNumber(); +} + +void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) +{ + CodeBlock* codeBlock = this->codeBlock(); + codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column); + divot += codeBlock->sourceOffset(); +} + +void StackVisitor::Frame::setToEnd() +{ + m_callFrame = 0; +#if ENABLE(DFG_JIT) + m_inlineCallFrame = 0; +#endif +} + +static void printIndents(int levels) +{ + while (levels--) + dataLogFString(" "); +} + +template<typename... Types> +void log(unsigned indent, const Types&... values) +{ + printIndents(indent); + dataLog(values...); +} + +template<typename... Types> +void logF(unsigned indent, const char* format, const Types&... values) +{ + printIndents(indent); + +#if COMPILER(GCC_OR_CLANG) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wmissing-format-attribute" +#endif + + dataLogF(format, values...); + +#if COMPILER(GCC_OR_CLANG) +#pragma GCC diagnostic pop +#endif +} + +void StackVisitor::Frame::print(int indent) +{ + if (!this->callFrame()) { + log(indent, "frame 0x0\n"); + return; + } + + CodeBlock* codeBlock = this->codeBlock(); + logF(indent, "frame %p {\n", this->callFrame()); + + { + indent++; + + 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"); + + bool isInlined = false; +#if ENABLE(DFG_JIT) + isInlined = isInlinedFrame(); + log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); + if (isInlinedFrame()) + logF(indent, "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->callSiteAsRawBits(); + 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->callSiteBitsAreBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->bytecodeOffset(); + log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); +#if ENABLE(DFG_JIT) + } else { + log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); + if (codeBlock->hasCodeOrigins()) { + CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); + log(indent, "callSiteIndex: ", callSiteIndex.bits(), " 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()); + } + } +#endif + } + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + log(indent, "line: ", line, "\n"); + log(indent, "column: ", column, "\n"); + + indent--; + } + indent--; + } + log(indent, "}\n"); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.h b/Source/JavaScriptCore/interpreter/StackVisitor.h new file mode 100644 index 000000000..bbf37fe9c --- /dev/null +++ b/Source/JavaScriptCore/interpreter/StackVisitor.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2013, 2015 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 StackVisitor_h +#define StackVisitor_h + +#include "VMEntryRecord.h" +#include <wtf/text/WTFString.h> + +namespace JSC { + +struct CodeOrigin; +struct InlineCallFrame; + +class CodeBlock; +class ExecState; +class JSFunction; +class JSObject; +class JSScope; +class ClonedArguments; +class Register; + +typedef ExecState CallFrame; + +class StackVisitor { +public: + class Frame { + public: + enum CodeType { + Global, + Eval, + Function, + Module, + Native + }; + + 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; } + CodeBlock* codeBlock() const { return m_codeBlock; } + unsigned bytecodeOffset() const { return m_bytecodeOffset; } +#if ENABLE(DFG_JIT) + InlineCallFrame* inlineCallFrame() const { return m_inlineCallFrame; } +#endif + + bool isJSFrame() const { return !!codeBlock(); } +#if ENABLE(DFG_JIT) + bool isInlinedFrame() const { return !!m_inlineCallFrame; } +#endif + + JS_EXPORT_PRIVATE String functionName(); + JS_EXPORT_PRIVATE String sourceURL(); + JS_EXPORT_PRIVATE String toString(); + + CodeType codeType() const; + JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); + + ClonedArguments* createArguments(); + VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; } + CallFrame* callFrame() const { return m_callFrame; } + + JS_EXPORT_PRIVATE void print(int indentLevel); + + private: + Frame() { } + ~Frame() { } + + void retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); + void setToEnd(); + + size_t m_index; + size_t m_argumentCountIncludingThis; + VMEntryFrame* m_VMEntryFrame; + VMEntryFrame* m_CallerVMEntryFrame; + CallFrame* m_callerFrame; + JSObject* m_callee; + CodeBlock* m_codeBlock; + unsigned m_bytecodeOffset; + bool m_callerIsVMEntryFrame; +#if ENABLE(DFG_JIT) + InlineCallFrame* m_inlineCallFrame; +#endif + CallFrame* m_callFrame; + + friend class StackVisitor; + }; + + enum Status { + Continue = 0, + Done = 1 + }; + + // StackVisitor::visit() expects a Functor that implements the following method: + // Status operator()(StackVisitor&); + + template <typename Functor> + static void visit(CallFrame* startFrame, Functor& functor) + { + StackVisitor visitor(startFrame); + while (visitor->callFrame()) { + Status status = functor(visitor); + if (status != Continue) + break; + visitor.gotoNextFrame(); + } + } + + Frame& operator*() { return m_frame; } + ALWAYS_INLINE Frame* operator->() { return &m_frame; } + void unwindToMachineCodeBlockFrame(); + +private: + JS_EXPORT_PRIVATE StackVisitor(CallFrame* startFrame); + + JS_EXPORT_PRIVATE void gotoNextFrame(); + + void readFrame(CallFrame*); + void readNonInlinedFrame(CallFrame*, CodeOrigin* = 0); +#if ENABLE(DFG_JIT) + void readInlinedFrame(CallFrame*, CodeOrigin*); +#endif + + 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 new file mode 100644 index 000000000..9726538fb --- /dev/null +++ b/Source/JavaScriptCore/interpreter/VMEntryRecord.h @@ -0,0 +1,56 @@ +/* + * 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; } + SUPPRESS_ASAN ExecState* unsafePrevTopCallFrame() { return m_prevTopCallFrame; } + + VMEntryFrame* prevTopVMEntryFrame() { return m_prevTopVMEntryFrame; } + SUPPRESS_ASAN VMEntryFrame* unsafePrevTopVMEntryFrame() { 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 deleted file mode 100644 index 58bc15075..000000000 --- a/Source/JavaScriptCore/interpreter/VMInspector.cpp +++ /dev/null @@ -1,573 +0,0 @@ -/* - * 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 <stdio.h> -#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->hasHostCallFrameFlag()) { - 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 deleted file mode 100644 index 6623068dc..000000000 --- a/Source/JavaScriptCore/interpreter/VMInspector.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 |