diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
commit | e15dd966d523731101f70ccf768bba12435a0208 (patch) | |
tree | ae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/interpreter | |
download | WebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz |
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
18 files changed, 3904 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp new file mode 100644 index 000000000..09b24d5dc --- /dev/null +++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp @@ -0,0 +1,51 @@ +/* + * 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 "AbstractPC.h" + +#include "CallFrame.h" +#include "JSObject.h" +#include "JSCInlines.h" +#include "VM.h" + +namespace JSC { + +AbstractPC::AbstractPC(VM& vm, ExecState* exec) +{ + UNUSED_PARAM(vm); + UNUSED_PARAM(exec); + +#if ENABLE(JIT) + if (vm.canUseJIT()) { + m_pointer = exec->returnPC().value(); + m_mode = JIT; + return; + } +#endif +} + +} // namespace JSC + diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h new file mode 100644 index 000000000..ac2aaa7ad --- /dev/null +++ b/Source/JavaScriptCore/interpreter/AbstractPC.h @@ -0,0 +1,75 @@ +/* + * 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 AbstractPC_h +#define AbstractPC_h + +#include "MacroAssemblerCodeRef.h" + +namespace JSC { + +class VM; +class ExecState; +struct Instruction; + +class AbstractPC { +public: + AbstractPC() + : m_pointer(0) + , m_mode(None) + { + } + + AbstractPC(VM&, ExecState*); + +#if ENABLE(JIT) + AbstractPC(ReturnAddressPtr ptr) + : m_pointer(ptr.value()) + , m_mode(JIT) + { + } + + bool hasJITReturnAddress() const { return m_mode == JIT; } + ReturnAddressPtr jitReturnAddress() const + { + ASSERT(hasJITReturnAddress()); + return ReturnAddressPtr(m_pointer); + } +#endif + + bool isSet() const { return m_mode != None; } + bool operator!() const { return !isSet(); } + +private: + void* m_pointer; + + enum Mode { None, JIT, Interpreter }; + Mode m_mode; +}; + +} // namespace JSC + +#endif // AbstractPC_h + diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h new file mode 100644 index 000000000..30cad2a14 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -0,0 +1,73 @@ +/* + * 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 + * 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 CachedCall_h +#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 { + WTF_MAKE_NONCOPYABLE(CachedCall); WTF_MAKE_FAST_ALLOCATED; + public: + CachedCall(CallFrame* callFrame, JSFunction* function, int argumentCount) + : m_valid(false) + , m_interpreter(callFrame->interpreter()) + , m_entryScope(callFrame->vm(), function->scope()->globalObject()) + { + 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(); + } + + JSValue call() + { + ASSERT(m_valid); + return m_interpreter->execute(m_closure); + } + void setThis(JSValue v) { m_protoCallFrame.setThisValue(v); } + void setArgument(int n, JSValue v) { m_protoCallFrame.setArgument(n, v); } + + private: + bool m_valid; + Interpreter* m_interpreter; + VMEntryScope m_entryScope; + ProtoCallFrame m_protoCallFrame; + Vector<JSValue> m_arguments; + CallFrameClosure m_closure; + }; +} + +#endif diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp new file mode 100644 index 000000000..259061df4 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -0,0 +1,208 @@ +/* + * 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 + * 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 "CallFrame.h" + +#include "CallFrameInlines.h" +#include "CodeBlock.h" +#include "Interpreter.h" +#include "JSLexicalEnvironment.h" +#include "JSCInlines.h" +#include "VMEntryScope.h" +#include <wtf/StringPrintStream.h> + +namespace JSC { + +#ifndef NDEBUG +JSStack* CallFrame::stack() +{ + return &interpreter()->stack(); +} + +#endif + +#if USE(JSVALUE32_64) +unsigned CallFrame::locationAsBytecodeOffset() const +{ + ASSERT(codeBlock()); + ASSERT(hasLocationAsBytecodeOffset()); + return currentVPC() - codeBlock()->instructions().begin(); +} + +void CallFrame::setLocationAsBytecodeOffset(unsigned offset) +{ + ASSERT(codeBlock()); + setCurrentVPC(codeBlock()->instructions().begin() + offset); + ASSERT(hasLocationAsBytecodeOffset()); +} +#else +Instruction* CallFrame::currentVPC() const +{ + return codeBlock()->instructions().begin() + locationAsBytecodeOffset(); +} +void CallFrame::setCurrentVPC(Instruction* vpc) +{ + setLocationAsBytecodeOffset(vpc - codeBlock()->instructions().begin()); +} +#endif + +#if ENABLE(DFG_JIT) +unsigned CallFrame::bytecodeOffsetFromCodeOriginIndex() +{ + ASSERT(hasLocationAsCodeOriginIndex()); + CodeBlock* codeBlock = this->codeBlock(); + ASSERT(codeBlock); + + CodeOrigin codeOrigin; + unsigned index = locationAsCodeOriginIndex(); + ASSERT(codeBlock->canGetCodeOrigin(index)); + codeOrigin = codeBlock->codeOrigin(index); + + for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { + if (inlineCallFrame->baselineCodeBlock() == codeBlock) + return codeOrigin.bytecodeIndex; + + codeOrigin = inlineCallFrame->caller; + inlineCallFrame = codeOrigin.inlineCallFrame; + } + return codeOrigin.bytecodeIndex; +} + +#endif // ENABLE(DFG_JIT) + +unsigned CallFrame::bytecodeOffset() +{ + if (!codeBlock()) + return 0; +#if ENABLE(DFG_JIT) + if (hasLocationAsCodeOriginIndex()) + return bytecodeOffsetFromCodeOriginIndex(); +#endif + return locationAsBytecodeOffset(); +} + +CodeOrigin CallFrame::codeOrigin() +{ + if (!codeBlock()) + return CodeOrigin(0); +#if ENABLE(DFG_JIT) + if (hasLocationAsCodeOriginIndex()) { + unsigned index = locationAsCodeOriginIndex(); + ASSERT(codeBlock()->canGetCodeOrigin(index)); + return codeBlock()->codeOrigin(index); + } +#endif + return CodeOrigin(locationAsBytecodeOffset()); +} + +Register* CallFrame::topOfFrameInternal() +{ + CodeBlock* codeBlock = this->codeBlock(); + ASSERT(codeBlock); + return registers() + codeBlock->stackPointerOffset(); +} + +JSGlobalObject* CallFrame::vmEntryGlobalObject() +{ + 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(); +} + +CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame) +{ + if (callerFrameOrVMEntryFrame() == currVMEntryFrame) { + VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); + currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame(); + return currVMEntryRecord->prevTopCallFrame(); + } + return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); +} + +JSLexicalEnvironment* CallFrame::lexicalEnvironment() const +{ + CodeBlock* codeBlock = this->codeBlock(); + RELEASE_ASSERT(codeBlock->needsActivation()); + VirtualRegister activationRegister = codeBlock->activationRegister(); + return registers()[activationRegister.offset()].Register::lexicalEnvironment(); +} + +JSLexicalEnvironment* CallFrame::lexicalEnvironmentOrNullptr() const +{ + CodeBlock* codeBlock = this->codeBlock(); + return codeBlock->needsActivation() ? lexicalEnvironment() : nullptr; +} + +void CallFrame::setActivation(JSLexicalEnvironment* lexicalEnvironment) +{ + CodeBlock* codeBlock = this->codeBlock(); + RELEASE_ASSERT(codeBlock->needsActivation()); + VirtualRegister activationRegister = codeBlock->activationRegister(); + registers()[activationRegister.offset()] = lexicalEnvironment; +} + +void CallFrame::dump(PrintStream& out) +{ + if (CodeBlock* codeBlock = this->codeBlock()) { + out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), "]"); + + out.print("("); + thisValue().dumpForBacktrace(out); + + for (size_t i = 0; i < argumentCount(); ++i) { + out.print(", "); + JSValue value = argument(i); + value.dumpForBacktrace(out); + } + + out.print(")"); + + return; + } + + out.print(returnPC()); +} + +const char* CallFrame::describeFrame() +{ + const size_t bufferSize = 200; + static char buffer[bufferSize + 1]; + + WTF::StringPrintStream stringStream; + + dump(stringStream); + + strncpy(buffer, stringStream.toCString().data(), bufferSize); + buffer[bufferSize] = '\0'; + + return buffer; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h new file mode 100644 index 000000000..afa8d3ca7 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008, 2011, 2013, 2014 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef CallFrame_h +#define CallFrame_h + +#include "AbstractPC.h" +#include "JSStack.h" +#include "MacroAssemblerCodeRef.h" +#include "Register.h" +#include "StackVisitor.h" +#include "VM.h" +#include "VMEntryRecord.h" + +namespace JSC { + + class Arguments; + class JSLexicalEnvironment; + class Interpreter; + class JSScope; + + // 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].object(); } + CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } + JSScope* scope(int scopeRegisterOffset) const + { + ASSERT(this[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); + } + + bool hasActivation() const; + JSLexicalEnvironment* lexicalEnvironment() const; + JSLexicalEnvironment* lexicalEnvironmentOrNullptr() const; + JSValue uncheckedActivation() const; + + // Global object in which execution began. + JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject(); + + // Global object in which the currently executing code was defined. + // 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 + // the actual DOM window, which can't be "this" for security reasons. + JSObject* globalThisValue() const; + + VM& vm() const; + + // Convenience functions for access to global data. + // It takes a few memory references to get from a call frame to the global data + // 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().clearException(); } + + Exception* 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; } + + + static CallFrame* create(Register* callFrameBase) { return static_cast<CallFrame*>(callFrameBase); } + Register* registers() { return this; } + const Register* registers() const { return this; } + + CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; } + + CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); } + + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); + + 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); } + + class Location { + public: + static inline uint32_t decode(uint32_t bits); + + static inline bool isBytecodeLocation(uint32_t bits); +#if USE(JSVALUE64) + static inline uint32_t encodeAsBytecodeOffset(uint32_t bits); +#else + static inline uint32_t encodeAsBytecodeInstruction(Instruction*); +#endif + + static inline bool isCodeOriginIndex(uint32_t bits); + static inline uint32_t encodeAsCodeOriginIndex(uint32_t bits); + + private: + enum TypeTag { + BytecodeLocationTag = 0, + CodeOriginIndexTag = 1, + }; + + static inline uint32_t encode(TypeTag, uint32_t bits); + + static const uint32_t s_mask = 0x1; +#if USE(JSVALUE64) + static const uint32_t s_shift = 31; + static const uint32_t s_shiftedMask = s_mask << s_shift; +#else + static const uint32_t s_shift = 1; +#endif + }; + + bool hasLocationAsBytecodeOffset() const; + bool hasLocationAsCodeOriginIndex() const; + + unsigned locationAsRawBits() const; + unsigned locationAsBytecodeOffset() const; + unsigned locationAsCodeOriginIndex() const; + + void setLocationAsRawBits(unsigned); + void setLocationAsBytecodeOffset(unsigned); + +#if ENABLE(DFG_JIT) + unsigned bytecodeOffsetFromCodeOriginIndex(); +#endif + + // 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(); + + // This will get you a CodeOrigin. It will always succeed. May return + // CodeOrigin(0) if we're in native code. + CodeOrigin codeOrigin(); + + Register* topOfFrame() + { + if (!codeBlock()) + return registers(); + return topOfFrameInternal(); + } + +#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; + void setCurrentVPC(Instruction* vpc); +#endif + + void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } + void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; } + void setActivation(JSLexicalEnvironment*); + + ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, + CallFrame* callerFrame, int argc, JSObject* callee) + { + ASSERT(callerFrame == noCaller() || callerFrame->stack()->containsAddress(this)); + + setCodeBlock(codeBlock); + setCallerFrame(callerFrame); + setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. + setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) + setCallee(callee); + } + + // Read a register from the codeframe (or constant from the CodeBlock). + Register& r(int); + Register& r(VirtualRegister); + // Read a register for a non-constant + Register& uncheckedR(int); + Register& uncheckedR(VirtualRegister); + + // Access to arguments as passed. (After capture, arguments may move to a different location.) + size_t argumentCount() const { return argumentCountIncludingThis() - 1; } + size_t argumentCountIncludingThis() const { return this[JSStack::ArgumentCount].payload(); } + 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 + // this frame. The index starts at 0 for the first arg, 1 for the + // second, etc. + // + // The arguments (in this case) do not include the 'this' value. + // arguments(0) will not fetch the 'this' value. To get/set 'this', + // use thisValue() and setThisValue() below. + + JSValue argument(size_t argument) + { + if (argument >= argumentCount()) + return jsUndefined(); + 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; } + + // Under the constructor implemented in C++, thisValue holds the newTarget instead of the automatically constructed value. + // The result of this function is only effective under the "construct" context. + JSValue newTarget() { return thisValue(); } + + JSValue argumentAfterCapture(size_t argument); + + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } + + 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] = callee; } + void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; } + void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); } + + // 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: + +#ifndef NDEBUG + JSStack* stack(); +#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: + + size_t argIndexForRegister(Register* reg) + { + // The register at 'offset' number of slots from the frame pointer + // i.e. + // reg = frame[offset]; + // ==> reg = frame + offset; + // ==> offset = reg - frame; + int offset = reg - this->registers(); + + // The offset is defined (based on argumentOffset()) to be: + // offset = JSStack::FirstArgument - argIndex; + // Hence: + // argIndex = JSStack::FirstArgument - offset; + size_t argIndex = offset - JSStack::FirstArgument; + return argIndex; + } + + void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + + CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); } + const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } + + friend class JSStack; + }; + +} // namespace JSC + +#endif // CallFrame_h diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h new file mode 100644 index 000000000..2c03b7452 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -0,0 +1,56 @@ +/* + * 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 + * 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 CallFrameClosure_h +#define CallFrameClosure_h + +#include "ProtoCallFrame.h" + +namespace JSC { + +struct CallFrameClosure { + CallFrame* oldCallFrame; + ProtoCallFrame* protoCallFrame; + JSFunction* function; + FunctionExecutable* functionExecutable; + VM* vm; + JSScope* scope; + int parameterCountIncludingThis; + int argumentCountIncludingThis; + + void setThis(JSValue value) + { + protoCallFrame->setThisValue(value); + } + + void setArgument(int argument, JSValue value) + { + protoCallFrame->setArgument(argument, value); + } +}; + +} + +#endif diff --git a/Source/JavaScriptCore/interpreter/CallFrameInlines.h b/Source/JavaScriptCore/interpreter/CallFrameInlines.h new file mode 100644 index 000000000..3f430ec31 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrameInlines.h @@ -0,0 +1,158 @@ +/* + * 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 CallFrameInlines_h +#define CallFrameInlines_h + +#include "CallFrame.h" +#include "CodeBlock.h" + +namespace JSC { + +inline uint32_t CallFrame::Location::encode(CallFrame::Location::TypeTag tag, uint32_t bits) +{ +#if USE(JSVALUE64) + ASSERT(!(bits & s_shiftedMask)); + ASSERT(!(tag & ~s_mask)); + return bits | (tag << s_shift); +#else + ASSERT(!(tag & ~s_mask)); + if (tag & CodeOriginIndexTag) + bits = (bits << s_shift); + ASSERT(!(bits & s_mask)); + bits |= tag; + return bits; +#endif +} + +inline uint32_t CallFrame::Location::decode(uint32_t bits) +{ +#if USE(JSVALUE64) + return bits & ~s_shiftedMask; +#else + if (isCodeOriginIndex(bits)) + return bits >> s_shift; + return bits & ~s_mask; +#endif +} + +#if USE(JSVALUE64) +inline uint32_t CallFrame::Location::encodeAsBytecodeOffset(uint32_t bits) +{ + uint32_t encodedBits = encode(BytecodeLocationTag, bits); + ASSERT(isBytecodeLocation(encodedBits)); + return encodedBits; +} +#else +inline uint32_t CallFrame::Location::encodeAsBytecodeInstruction(Instruction* instruction) +{ + uint32_t encodedBits = encode(BytecodeLocationTag, reinterpret_cast<uint32_t>(instruction)); + ASSERT(isBytecodeLocation(encodedBits)); + return encodedBits; +} +#endif + +inline uint32_t CallFrame::Location::encodeAsCodeOriginIndex(uint32_t bits) +{ + uint32_t encodedBits = encode(CodeOriginIndexTag, bits); + ASSERT(isCodeOriginIndex(encodedBits)); + return encodedBits; +} + +inline bool CallFrame::Location::isBytecodeLocation(uint32_t bits) +{ + return !isCodeOriginIndex(bits); +} + +inline bool CallFrame::Location::isCodeOriginIndex(uint32_t bits) +{ +#if USE(JSVALUE64) + TypeTag tag = static_cast<TypeTag>(bits >> s_shift); + return !!(tag & CodeOriginIndexTag); +#else + return !!(bits & CodeOriginIndexTag); +#endif +} + +inline bool CallFrame::hasLocationAsBytecodeOffset() const +{ + return Location::isBytecodeLocation(locationAsRawBits()); +} + +inline bool CallFrame::hasLocationAsCodeOriginIndex() const +{ + return Location::isCodeOriginIndex(locationAsRawBits()); +} + +inline unsigned CallFrame::locationAsRawBits() const +{ + return this[JSStack::ArgumentCount].tag(); +} + +inline void CallFrame::setLocationAsRawBits(unsigned bits) +{ + this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(bits); +} + +#if USE(JSVALUE64) +inline unsigned CallFrame::locationAsBytecodeOffset() const +{ + ASSERT(hasLocationAsBytecodeOffset()); + ASSERT(codeBlock()); + return Location::decode(locationAsRawBits()); +} + +inline void CallFrame::setLocationAsBytecodeOffset(unsigned offset) +{ + ASSERT(codeBlock()); + setLocationAsRawBits(Location::encodeAsBytecodeOffset(offset)); + ASSERT(hasLocationAsBytecodeOffset()); +} +#endif // USE(JSVALUE64) + +inline unsigned CallFrame::locationAsCodeOriginIndex() const +{ + ASSERT(hasLocationAsCodeOriginIndex()); + ASSERT(codeBlock()); + return Location::decode(locationAsRawBits()); +} + +inline bool CallFrame::hasActivation() const +{ + JSValue activation = uncheckedActivation(); + return !!activation && activation.isCell(); +} + +inline JSValue CallFrame::uncheckedActivation() const +{ + CodeBlock* codeBlock = this->codeBlock(); + RELEASE_ASSERT(codeBlock->needsActivation()); + VirtualRegister activationRegister = codeBlock->activationRegister(); + return registers()[activationRegister.offset()].jsValue(); +} + +} // namespace JSC + +#endif // CallFrameInlines_h diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp new file mode 100644 index 000000000..518fc0b76 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -0,0 +1,1248 @@ +/* + * 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 + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "Interpreter.h" + +#include "BatchedTransitionOptimizer.h" +#include "CallFrameClosure.h" +#include "CallFrameInlines.h" +#include "ClonedArguments.h" +#include "CodeBlock.h" +#include "DirectArguments.h" +#include "Heap.h" +#include "Debugger.h" +#include "DebuggerCallFrame.h" +#include "ErrorInstance.h" +#include "EvalCodeCache.h" +#include "Exception.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "JSArray.h" +#include "JSBoundFunction.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" +#include "JSNotAnObject.h" +#include "JSStackInlines.h" +#include "JSString.h" +#include "JSWithScope.h" +#include "LLIntCLoop.h" +#include "LLIntThunks.h" +#include "LegacyProfiler.h" +#include "LiteralParser.h" +#include "ObjectPrototype.h" +#include "Parser.h" +#include "ProtoCallFrame.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "Register.h" +#include "SamplingTool.h" +#include "ScopedArguments.h" +#include "StackAlignment.h" +#include "StackVisitor.h" +#include "StrictEvalActivation.h" +#include "StrongInlines.h" +#include "Symbol.h" +#include "VMEntryScope.h" +#include "VMInlines.h" +#include "VirtualRegister.h" + +#include <limits.h> +#include <stdio.h> +#include <wtf/StackStats.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringPrintStream.h> +#include <wtf/Threading.h> +#include <wtf/WTFThreadData.h> +#include <wtf/text/StringBuilder.h> + +#if ENABLE(JIT) +#include "JIT.h" +#endif + +using namespace std; + +namespace JSC { + +String StackFrame::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 StackFrame::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; +} + +JSValue eval(CallFrame* callFrame) +{ + if (!callFrame->argumentCount()) + return jsUndefined(); + + JSValue program = callFrame->argument(0); + if (!program.isString()) + return program; + + TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); + String programSource = asString(program)->value(callFrame); + if (callFrame->hadException()) + return JSValue(); + + CallFrame* callerFrame = callFrame->callerFrame(); + CodeBlock* callerCodeBlock = callerFrame->codeBlock(); + JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope(); + EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + + if (!eval) { + if (!callerCodeBlock->isStrictMode()) { + if (programSource.is8Bit()) { + LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return parsedObject; + } else { + LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return parsedObject; + } + } + + // If the literal parser bailed, it should not have thrown exceptions. + ASSERT(!callFrame->vm().exception()); + + ThisTDZMode thisTDZMode = callerCodeBlock->unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded; + eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), thisTDZMode, programSource, callerScopeChain); + if (!eval) + return jsUndefined(); + } + + JSValue thisValue = callerFrame->thisValue(); + Interpreter* interpreter = callFrame->vm().interpreter; + return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); +} + +unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset) +{ + if (UNLIKELY(!arguments.isCell())) { + if (arguments.isUndefinedOrNull()) + return 0; + + callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); + return 0; + } + + 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; +} + +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; +} + +void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length) +{ + if (UNLIKELY(!arguments.isCell())) + 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; + } + 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; + } } +} + +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); +} + +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); +} + +Interpreter::Interpreter(VM& vm) + : m_sampleEntryDepth(0) + , m_vm(vm) + , m_stack(vm) + , m_errorHandlingModeReentry(0) +#if !ASSERT_DISABLED + , m_initialized(false) +#endif +{ +} + +Interpreter::~Interpreter() +{ +} + +void Interpreter::initialize(bool canUseJIT) +{ + UNUSED_PARAM(canUseJIT); + +#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)); +#endif + +#if !ASSERT_DISABLED + m_initialized = true; +#endif + +#if ENABLE(OPCODE_SAMPLING) + enableSampler(); +#endif +} + +#ifdef NDEBUG + +void Interpreter::dumpCallFrame(CallFrame*) +{ +} + +#else + +void Interpreter::dumpCallFrame(CallFrame* callFrame) +{ + callFrame->codeBlock()->dumpBytecode(); + 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"); + dataLogF("-----------------------------------------------------------------------------\n"); + dataLogF(" use | address | value \n"); + dataLogF("-----------------------------------------------------------------------------\n"); + + CodeBlock* codeBlock = callFrame->codeBlock(); + const Register* it; + const Register* 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(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; + } + + dataLogF("-----------------------------------------------------------------------------\n"); + dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount()); + --it; + dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); + --it; + dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); + --it; + // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header + --it; +#if ENABLE(JIT) + AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); + if (pc.hasJITReturnAddress()) + dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value()); +#endif + + DumpRegisterFunctor functor(it); + callFrame->iterate(functor); + + dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock()); + --it; + dataLogF("-----------------------------------------------------------------------------\n"); + + end = it - codeBlock->m_numVars; + if (it != end) { + do { + JSValue v = it->jsValue(); + int registerNumber = it - callFrame->registers(); + 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; + } while (it != end); + } + dataLogF("-----------------------------------------------------------------------------\n"); + + end = it - codeBlock->m_numCalleeRegisters + codeBlock->m_numVars; + if (it != end) { + do { + JSValue v = (*it).jsValue(); + 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"); +} + +#endif + +bool Interpreter::isOpcode(Opcode opcode) +{ +#if ENABLE(COMPUTED_GOTO_OPCODES) + return opcode != HashTraits<Opcode>::emptyValue() + && !HashTraits<Opcode>::isDeletedValue(opcode) + && m_opcodeIDTable.contains(opcode); +#else + return opcode >= 0 && opcode <= op_end; +#endif +} + +static bool unwindCallFrame(StackVisitor& visitor) +{ + CallFrame* callFrame = visitor->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()); + } + + return !visitor->callerIsVMEntryFrame(); +} + +static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) +{ + switch (visitor->codeType()) { + case StackVisitor::Frame::Eval: + return StackFrameEvalCode; + case StackVisitor::Frame::Function: + return StackFrameFunctionCode; + case StackVisitor::Frame::Global: + return StackFrameGlobalCode; + case StackVisitor::Frame::Native: + ASSERT_NOT_REACHED(); + return StackFrameNativeCode; + } + RELEASE_ASSERT_NOT_REACHED(); + return StackFrameGlobalCode; +} + +void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) +{ + if (!codeBlock) { + line = 0; + column = 0; + return; + } + + int divot = 0; + int unusedStartOffset = 0; + int unusedEndOffset = 0; + unsigned divotLine = 0; + unsigned divotColumn = 0; + expressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); + + 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) +{ + codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column); + divot += characterOffset; +} + +String StackFrame::toString(CallFrame* callFrame) +{ + StringBuilder traceBuild; + String functionName = friendlyFunctionName(callFrame); + String sourceURL = friendlySourceURL(); + traceBuild.append(functionName); + if (!sourceURL.isEmpty()) { + if (!functionName.isEmpty()) + traceBuild.append('@'); + traceBuild.append(sourceURL); + if (codeType != StackFrameNativeCode) { + unsigned line; + unsigned column; + computeLineAndColumn(line, column); + + traceBuild.append(':'); + traceBuild.appendNumber(line); + traceBuild.append(':'); + traceBuild.appendNumber(column); + } + } + return traceBuild.toString().impl(); +} + +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() && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { + CodeBlock* codeBlock = visitor->codeBlock(); + StackFrame s = { + Strong<JSObject>(vm, visitor->callee()), + getStackFrameCodeType(visitor), + Strong<ScriptExecutable>(vm, codeBlock->ownerExecutable()), + Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), + codeBlock->source(), + codeBlock->ownerExecutable()->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); + } + + 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; + + 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(exec))); + if (i != stackTrace.size() - 1) + builder.append('\n'); + } + return jsString(&exec->vm(), builder.toString()); +} + +class GetCatchHandlerFunctor { +public: + GetCatchHandlerFunctor() + : m_handler(0) + { + } + + HandlerInfo* handler() { return m_handler; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + CodeBlock* codeBlock = visitor->codeBlock(); + if (!codeBlock) + return StackVisitor::Continue; + + unsigned bytecodeOffset = visitor->bytecodeOffset(); + m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset, CodeBlock::RequiredHandler::CatchHandler); + if (m_handler) + return StackVisitor::Done; + + return StackVisitor::Continue; + } + +private: + HandlerInfo* m_handler; +}; + +class UnwindFunctor { +public: + UnwindFunctor(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) + : m_vmEntryFrame(vmEntryFrame) + , m_callFrame(callFrame) + , m_isTermination(isTermination) + , m_codeBlock(codeBlock) + , m_handler(handler) + { + } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + VM& vm = m_callFrame->vm(); + m_vmEntryFrame = visitor->vmEntryFrame(); + m_callFrame = visitor->callFrame(); + m_codeBlock = visitor->codeBlock(); + unsigned bytecodeOffset = visitor->bytecodeOffset(); + + if (m_isTermination || !(m_handler = m_codeBlock ? m_codeBlock->handlerForBytecodeOffset(bytecodeOffset) : nullptr)) { + if (!unwindCallFrame(visitor)) { + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->exceptionUnwind(m_callFrame); + return StackVisitor::Done; + } + } else + return StackVisitor::Done; + + return StackVisitor::Continue; + } + +private: + VMEntryFrame*& m_vmEntryFrame; + CallFrame*& m_callFrame; + bool m_isTermination; + CodeBlock*& m_codeBlock; + HandlerInfo*& m_handler; +}; + +NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, Exception* exception) +{ + CodeBlock* codeBlock = callFrame->codeBlock(); + bool isTermination = false; + + JSValue exceptionValue = exception->value(); + ASSERT(!exceptionValue.isEmpty()); + ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); + // This shouldn't be possible (hence the assertions), but we're already in the slowest of + // slow cases, so let's harden against it anyway to be safe. + if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) + exceptionValue = jsNull(); + + if (exceptionValue.isObject()) + isTermination = isTerminatedExecutionException(exception); + + ASSERT(callFrame->vm().exception() && callFrame->vm().exception()->stack().size()); + + Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); + if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) { + // We need to clear the exception here in order to see if a new exception happens. + // Afterwards, the values are put back to continue processing this error. + SuspendExceptionScope scope(&callFrame->vm()); + // This code assumes that if the debugger is enabled then there is no inlining. + // If that assumption turns out to be false then we'll ignore the inlined call + // frames. + // https://bugs.webkit.org/show_bug.cgi?id=121754 + + bool hasCatchHandler; + if (isTermination) + hasCatchHandler = false; + else { + GetCatchHandlerFunctor functor; + callFrame->iterate(functor); + HandlerInfo* handler = functor.handler(); + ASSERT(!handler || handler->isCatchHandler()); + hasCatchHandler = !!handler; + } + + debugger->exception(callFrame, exceptionValue, hasCatchHandler); + ASSERT(!callFrame->hadException()); + } + exception->setDidNotifyInspectorOfThrow(); + + // Calculate an exception handler vPC, unwinding call frames as necessary. + HandlerInfo* handler = 0; + VM& vm = callFrame->vm(); + ASSERT(callFrame == vm.topCallFrame); + UnwindFunctor functor(vmEntryFrame, callFrame, isTermination, codeBlock, handler); + callFrame->iterate(functor); + if (!handler) + return 0; + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->exceptionUnwind(callFrame); + + return handler; +} + +static inline JSValue checkedReturn(JSValue returnValue) +{ + ASSERT(returnValue); + return returnValue; +} + +static inline JSObject* checkedReturn(JSObject* returnValue) +{ + ASSERT(returnValue); + return returnValue; +} + +class SamplingScope { +public: + SamplingScope(Interpreter* interpreter) + : m_interpreter(interpreter) + { + interpreter->startSampling(); + } + ~SamplingScope() + { + m_interpreter->stopSampling(); + } +private: + Interpreter* m_interpreter; +}; + +JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) +{ + SamplingScope samplingScope(this); + + JSScope* scope = thisObj->globalObject(); + VM& vm = *scope->vm(); + + ASSERT(!vm.exception()); + ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusy()) + return jsNull(); + + 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. + + Vector<JSONPData> JSONPData; + bool parseResult; + const String programSource = program->source().toString(); + if (programSource.isNull()) + return jsUndefined(); + if (programSource.is8Bit()) { + LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP); + parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); + } else { + LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP); + parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); + } + + if (parseResult) { + JSGlobalObject* globalObject = scope->globalObject(); + JSValue result; + for (unsigned entry = 0; entry < JSONPData.size(); entry++) { + Vector<JSONPPathEntry> JSONPPath; + JSONPPath.swap(JSONPData[entry].m_path); + JSValue JSONPValue = JSONPData[entry].m_value.get(); + if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) { + globalObject->addVar(callFrame, JSONPPath[0].m_pathEntryName); + PutPropertySlot slot(globalObject); + globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); + result = jsUndefined(); + continue; + } + JSValue baseObject(globalObject); + for (unsigned i = 0; i < JSONPPath.size() - 1; i++) { + ASSERT(JSONPPath[i].m_type != JSONPPathEntryTypeDeclare); + switch (JSONPPath[i].m_type) { + case JSONPPathEntryTypeDot: { + if (i == 0) { + PropertySlot slot(globalObject); + if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { + if (entry) + return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName)); + goto failedJSONP; + } + baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); + } else + baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName); + if (callFrame->hadException()) + return jsUndefined(); + continue; + } + case JSONPPathEntryTypeLookup: { + baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex); + if (callFrame->hadException()) + return jsUndefined(); + continue; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + return jsUndefined(); + } + } + PutPropertySlot slot(baseObject); + switch (JSONPPath.last().m_type) { + case JSONPPathEntryTypeCall: { + JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); + if (callFrame->hadException()) + return jsUndefined(); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function)); + MarkedArgumentBuffer jsonArg; + jsonArg.append(JSONPValue); + JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject; + JSONPValue = JSC::call(callFrame, function, callType, callData, thisValue, jsonArg); + if (callFrame->hadException()) + return jsUndefined(); + break; + } + case JSONPPathEntryTypeDot: { + baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot); + if (callFrame->hadException()) + return jsUndefined(); + break; + } + case JSONPPathEntryTypeLookup: { + baseObject.putByIndex(callFrame, JSONPPath.last().m_pathIndex, JSONPValue, slot.isStrictMode()); + if (callFrame->hadException()) + return jsUndefined(); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + return jsUndefined(); + } + result = JSONPValue; + } + return result; + } +failedJSONP: + // If we get here, then we have already proven that the script is not a JSON + // object. + + VMEntryScope entryScope(vm, scope->globalObject()); + + // Compile source to bytecode if necessary: + if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) + return checkedReturn(callFrame->vm().throwException(callFrame, error)); + + if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall)) + return checkedReturn(callFrame->vm().throwException(callFrame, error)); + + ProgramCodeBlock* codeBlock = program->codeBlock(); + + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); + + // Execute the code: + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + result = program->generatedJITCode()->execute(&vm, &protoCallFrame); + } + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); + + return checkedReturn(result); +} + +JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) +{ + VM& vm = callFrame->vm(); + ASSERT(!callFrame->hadException()); + ASSERT(!vm.isCollectorBusy()); + if (vm.isCollectorBusy()) + return jsNull(); + + bool isJSCall = (callType == CallTypeJS); + JSScope* scope = nullptr; + CodeBlock* newCodeBlock; + size_t argsCount = 1 + args.size(); // implicit "this" parameter + + JSGlobalObject* globalObject; + + if (isJSCall) { + scope = callData.js.scope; + globalObject = scope->globalObject(); + } else { + ASSERT(callType == CallTypeHost); + globalObject = function->globalObject(); + } + + VMEntryScope entryScope(vm, globalObject); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); + + if (isJSCall) { + // Compile the callee: + JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall); + if (UNLIKELY(!!compileError)) { + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + } + newCodeBlock = callData.js.functionExecutable->codeBlockForCall(); + ASSERT(!!newCodeBlock); + newCodeBlock->m_shouldAlwaysBeInlined = false; + } else + newCodeBlock = 0; + + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ProtoCallFrame protoCallFrame; + protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data()); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, function); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); + + // Execute the code: + if (isJSCall) + result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame); + else { + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame)); + if (callFrame->hadException()) + result = jsNull(); + } + } + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(callFrame, function); + + return checkedReturn(result); +} + +JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) +{ + VM& vm = callFrame->vm(); + ASSERT(!callFrame->hadException()); + ASSERT(!vm.isCollectorBusy()); + // We throw in this case because we have to return something "valid" but we're + // already in an invalid state. + if (vm.isCollectorBusy()) + return checkedReturn(throwStackOverflowError(callFrame)); + + bool isJSConstruct = (constructType == ConstructTypeJS); + JSScope* scope = nullptr; + CodeBlock* newCodeBlock; + size_t argsCount = 1 + args.size(); // implicit "this" parameter + + JSGlobalObject* globalObject; + + if (isJSConstruct) { + scope = constructData.js.scope; + globalObject = scope->globalObject(); + } else { + ASSERT(constructType == ConstructTypeHost); + globalObject = constructor->globalObject(); + } + + VMEntryScope entryScope(vm, globalObject); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); + + if (isJSConstruct) { + // Compile the callee: + JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct); + if (UNLIKELY(!!compileError)) { + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + } + newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct(); + ASSERT(!!newCodeBlock); + newCodeBlock->m_shouldAlwaysBeInlined = false; + } else + newCodeBlock = 0; + + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ProtoCallFrame protoCallFrame; + protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data()); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, constructor); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); + + // Execute the code. + 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); + + if (callFrame->hadException()) + return 0; + ASSERT(result.isObject()); + return checkedReturn(asObject(result)); +} + +CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, JSValue* args) +{ + VM& vm = *scope->vm(); + ASSERT(!vm.exception()); + + if (vm.isCollectorBusy()) + return CallFrameClosure(); + + // Compile the callee: + JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall); + if (error) { + callFrame->vm().throwException(callFrame, error); + return CallFrameClosure(); + } + CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall(); + newCodeBlock->m_shouldAlwaysBeInlined = false; + + size_t argsCount = argumentCountIncludingThis; + + protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args); + // Return the successful closure: + CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; + return result; +} + +JSValue Interpreter::execute(CallFrameClosure& closure) +{ + VM& vm = *closure.vm; + SamplingScope samplingScope(this); + + ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusy()) + return jsNull(); + + StackStats::CheckPoint stackCheckPoint; + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(closure.oldCallFrame, closure.function); + + if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame))) + return throwTerminatedExecutionException(closure.oldCallFrame); + + // Execute the code: + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); + } + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(closure.oldCallFrame, closure.function); + + return checkedReturn(result); +} + +JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) +{ + VM& vm = *scope->vm(); + SamplingScope samplingScope(this); + + ASSERT(scope->vm() == &callFrame->vm()); + ASSERT(!vm.exception()); + ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusy()) + return jsNull(); + + VMEntryScope entryScope(vm, scope->globalObject()); + if (!vm.isSafeToRecurse()) + return checkedReturn(throwStackOverflowError(callFrame)); + + unsigned numVariables = eval->numVariables(); + int numFunctions = eval->numberOfFunctionDecls(); + + 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; + } + } + } + ASSERT(!variableObject->isNameScopeObject()); + } + + JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall); + if (UNLIKELY(!!compileError)) + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + EvalCodeBlock* codeBlock = eval->codeBlock(); + + if (numVariables || numFunctions) { + 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(variableObject); + variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); + } + } + + for (int i = 0; i < numFunctions; ++i) { + FunctionExecutable* function = codeBlock->functionDecl(i); + PutPropertySlot slot(variableObject); + variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot); + } + } + + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + + // Execute the code: + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); + } + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + + return checkedReturn(result); +} + +NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID) +{ + Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); + if (!debugger) + return; + + ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); + ASSERT(!callFrame->hadException()); + + 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; + } + ASSERT(!callFrame->hadException()); +} + +void Interpreter::enableSampler() +{ +#if ENABLE(OPCODE_SAMPLING) + if (!m_sampler) { + m_sampler = std::make_unique<SamplingTool>(this); + m_sampler->setup(); + } +#endif +} +void Interpreter::dumpSampleData(ExecState* exec) +{ +#if ENABLE(OPCODE_SAMPLING) + if (m_sampler) + m_sampler->dump(exec); +#else + UNUSED_PARAM(exec); +#endif +} +void Interpreter::startSampling() +{ +#if ENABLE(SAMPLING_THREAD) + if (!m_sampleEntryDepth) + SamplingThread::start(); + + m_sampleEntryDepth++; +#endif +} +void Interpreter::stopSampling() +{ +#if ENABLE(SAMPLING_THREAD) + m_sampleEntryDepth--; + if (!m_sampleEntryDepth) + SamplingThread::stop(); +#endif +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h new file mode 100644 index 000000000..ad9df6fc7 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -0,0 +1,285 @@ +/* + * 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 + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Interpreter_h +#define Interpreter_h + +#include "ArgList.h" +#include "JSCJSValue.h" +#include "JSCell.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> + +namespace JSC { + + class CodeBlock; + class EvalExecutable; + class ExecutableBase; + class FunctionExecutable; + class VM; + class JSFunction; + class JSGlobalObject; + class LLIntOffsetsExtractor; + class ProgramExecutable; + class Register; + class JSScope; + class SamplingTool; + struct CallFrameClosure; + struct HandlerInfo; + struct Instruction; + struct ProtoCallFrame; + + enum DebugHookID { + WillExecuteProgram, + DidExecuteProgram, + DidEnterCallFrame, + DidReachBreakpoint, + WillLeaveCallFrame, + WillExecuteStatement + }; + + enum StackFrameCodeType { + StackFrameGlobalCode, + StackFrameEvalCode, + StackFrameFunctionCode, + StackFrameNativeCode + }; + + struct StackFrame { + Strong<JSObject> callee; + StackFrameCodeType codeType; + Strong<ScriptExecutable> executable; + Strong<UnlinkedCodeBlock> codeBlock; + RefPtr<SourceProvider> code; + int lineOffset; + unsigned firstLineColumnOffset; + unsigned characterOffset; + unsigned bytecodeOffset; + String sourceURL; + JS_EXPORT_PRIVATE String toString(CallFrame*); + 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->setException(oldException); + } + private: + Exception* oldException; + VM* m_vm; + }; + + class TopCallFrameSetter { + public: + TopCallFrameSetter(VM& currentVM, CallFrame* callFrame) + : vm(currentVM) + , oldCallFrame(currentVM.topCallFrame) + { + currentVM.topCallFrame = callFrame; + } + + ~TopCallFrameSetter() + { + vm.topCallFrame = oldCallFrame; + } + private: + VM& vm; + CallFrame* oldCallFrame; + }; + + class NativeCallFrameTracer { + public: + ALWAYS_INLINE NativeCallFrameTracer(VM* vm, CallFrame* callFrame) + { + ASSERT(vm); + ASSERT(callFrame); + 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: + Interpreter(VM &); + ~Interpreter(); + + void initialize(bool canUseJIT); + + JSStack& stack() { return m_stack; } + + Opcode getOpcode(OpcodeID id) + { + ASSERT(m_initialized); +#if ENABLE(COMPUTED_GOTO_OPCODES) + return m_opcodeTable[id]; +#else + return id; +#endif + } + + OpcodeID getOpcodeID(Opcode opcode) + { + ASSERT(m_initialized); +#if ENABLE(COMPUTED_GOTO_OPCODES) + ASSERT(isOpcode(opcode)); + return m_opcodeIDTable.get(opcode); +#else + return opcode; +#endif + } + + bool isOpcode(Opcode); + + JSValue execute(ProgramExecutable*, CallFrame*, JSObject* thisObj); + JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&); + JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); + JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*); + + void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); + + SamplingTool* sampler() { return m_sampler.get(); } + + NEVER_INLINE HandlerInfo* unwind(VMEntryFrame*&, CallFrame*&, Exception*); + NEVER_INLINE void debug(CallFrame*, DebugHookID); + JSString* stackTraceAsString(ExecState*, Vector<StackFrame>); + + 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(); + void stopSampling(); + + 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*, ProtoCallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*, JSValue*); + + JSValue execute(CallFrameClosure&); + + + + void dumpRegisters(CallFrame*); + + bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval); } + + void enableSampler(); + int m_sampleEntryDepth; + std::unique_ptr<SamplingTool> m_sampler; + + VM& m_vm; + JSStack m_stack; + int m_errorHandlingModeReentry; + +#if ENABLE(COMPUTED_GOTO_OPCODES) + Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling + HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling +#endif + +#if !ASSERT_DISABLED + bool m_initialized; +#endif + }; + + JSValue eval(CallFrame*); + + inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis) + { + unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf( + stackAlignmentRegisters(), + numUsedStackSlots + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); + return CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); + } + + unsigned sizeOfVarargs(CallFrame* exec, JSValue arguments, uint32_t firstVarArgOffset); + static const unsigned maxArguments = 0x10000; + unsigned sizeFrameForVarargs(CallFrame* exec, JSStack*, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset); + void loadVarargs(CallFrame* execCaller, VirtualRegister firstElementDest, JSValue source, uint32_t offset, uint32_t length); + void setupVarargsFrame(CallFrame* execCaller, CallFrame* execCallee, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); + void setupVarargsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); + +} // namespace JSC + +#endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp new file mode 100644 index 000000000..d755aaa7d --- /dev/null +++ b/Source/JavaScriptCore/interpreter/JSStack.cpp @@ -0,0 +1,183 @@ +/* + * 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 + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "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 StaticLock stackStatisticsMutex; +#endif // !ENABLE(JIT) + +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(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() +{ + 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* 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 (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. + 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. Commit it and return true. + m_reservation.commit(newCommitTop, delta); + addToCommittedByteCount(delta); + m_commitTop = newCommitTop; + setStackLimit(newTopOfStack); + return true; +} + +void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots) +{ + conservativeRoots.add(topOfStack() + 1, highAddress()); +} + +void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) +{ + conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks); +} + +void JSStack::sanitizeStack() +{ +#if !ASAN_ENABLED + ASSERT(topOfStack() <= baseOfStack()); + + if (m_lastStackTop < topOfStack()) { + char* begin = reinterpret_cast<char*>(m_lastStackTop + 1); + char* end = reinterpret_cast<char*>(topOfStack() + 1); + memset(begin, 0, end - begin); + } + + m_lastStackTop = topOfStack(); +#endif +} + +void JSStack::releaseExcessCapacity() +{ + Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters; + ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop); + m_reservation.decommit(m_commitTop, delta); + addToCommittedByteCount(-delta); + m_commitTop = highAddressWithReservedZone; +} + +void JSStack::addToCommittedByteCount(long byteCount) +{ + LockHolder locker(stackStatisticsMutex); + ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); + committedBytesCount += byteCount; +} + +void JSStack::setReservedZoneSize(size_t reservedZoneSize) +{ + m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register); + if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters) + growSlowCase(m_end + 1); +} +#endif // !ENABLE(JIT) + +#if ENABLE(JIT) +Register* JSStack::lowAddress() const +{ + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(m_vm.stackLimit()); +} + +Register* JSStack::highAddress() const +{ + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(wtfThreadData().stack().origin()); +} +#endif // ENABLE(JIT) + +size_t JSStack::committedByteCount() +{ +#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 new file mode 100644 index 000000000..07cbbe69b --- /dev/null +++ b/Source/JavaScriptCore/interpreter/JSStack.h @@ -0,0 +1,154 @@ +/* + * 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 + * 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 JSStack_h +#define JSStack_h + +#include "ExecutableAllocator.h" +#include "Register.h" +#include <wtf/Noncopyable.h> +#include <wtf/PageReservation.h> +#include <wtf/VMTags.h> + +namespace JSC { + + class CodeBlockSet; + class ConservativeRoots; + 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 { + 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 commitSize = 16 * 1024; + // Allow 8k of excess registers before we start trying to reap the stack + static const ptrdiff_t maxExcessCapacity = 8 * 1024; + + JSStack(VM&); + + bool ensureCapacityFor(Register* newTopOfStack); + + bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); } + static size_t committedByteCount(); + +#if ENABLE(JIT) + void gatherConservativeRoots(ConservativeRoots&) { } + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { } + void sanitizeStack() { } +#else + ~JSStack(); + + void gatherConservativeRoots(ConservativeRoots&); + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&); + void sanitizeStack(); + + Register* baseOfStack() const + { + return highAddress() - 1; + } + + size_t size() const { return highAddress() - lowAddress(); } + + void setReservedZoneSize(size_t); + + inline Register* topOfStack(); +#endif // ENABLE(JIT) + + private: + +#if !ENABLE(JIT) + Register* lowAddress() const + { + return m_end + 1; + } + + Register* highAddress() const + { + return reinterpret_cast_ptr<Register*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); + } +#else + Register* lowAddress() const; + Register* highAddress() const; +#endif // !ENABLE(JIT) + +#if !ENABLE(JIT) + inline Register* topOfFrameFor(CallFrame*); + + Register* reservationTop() const + { + char* reservationTop = static_cast<char*>(m_reservation.base()); + return reinterpret_cast_ptr<Register*>(reservationTop); + } + + 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_commitTop; + PageReservation m_reservation; + Register* m_lastStackTop; + ptrdiff_t m_reservedZoneSizeInRegisters; +#endif // !ENABLE(JIT) + + friend class LLIntOffsetsExtractor; + }; + +} // namespace JSC + +#endif // JSStack_h diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h new file mode 100644 index 000000000..69508ab5d --- /dev/null +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -0,0 +1,94 @@ +/* + * 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 + * 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 JSStackInlines_h +#define JSStackInlines_h + +#include "CallFrame.h" +#include "CodeBlock.h" +#include "JSStack.h" +#include "VM.h" + +namespace JSC { + +inline bool JSStack::ensureCapacityFor(Register* newTopOfStack) +{ +#if !ENABLE(JIT) + return grow(newTopOfStack); +#else + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return newTopOfStack >= m_vm.stackLimit(); +#endif +} + +#if !ENABLE(JIT) + +inline Register* JSStack::topOfFrameFor(CallFrame* frame) +{ + if (UNLIKELY(!frame)) + return baseOfStack(); + return frame->topOfFrame() - 1; +} + +inline Register* JSStack::topOfStack() +{ + return topOfFrameFor(m_topCallFrame); +} + +inline void JSStack::shrink(Register* newTopOfStack) +{ + 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(); +} + +inline bool JSStack::grow(Register* newTopOfStack) +{ + Register* newEnd = newTopOfStack - 1; + if (newEnd >= m_end) + return true; + return growSlowCase(newTopOfStack); +} + +inline void JSStack::setStackLimit(Register* newTopOfStack) +{ + Register* newEnd = newTopOfStack - 1; + m_end = newEnd; + m_vm.setJSStackLimit(newTopOfStack); +} + +#endif // !ENABLE(JIT) + +} // namespace JSC + +#endif // JSStackInlines_h 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 new file mode 100644 index 000000000..71ead7d2f --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Register.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2008, 2009 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. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Register_h +#define Register_h + +#include "JSCJSValue.h" +#include <wtf/Assertions.h> +#include <wtf/VectorTraits.h> + +namespace JSC { + + class CodeBlock; + class ExecState; + class JSLexicalEnvironment; + class JSObject; + class JSScope; + + typedef ExecState CallFrame; + + class Register { + WTF_MAKE_FAST_ALLOCATED; + public: + Register(); + + Register(const JSValue&); + Register& operator=(const JSValue&); + JSValue jsValue() const; + JSValue asanUnsafeJSValue() const; + EncodedJSValue encodedJSValue() const; + + Register& operator=(CallFrame*); + Register& operator=(CodeBlock*); + Register& operator=(JSScope*); + Register& operator=(JSObject*); + + int32_t i() const; + JSLexicalEnvironment* lexicalEnvironment() const; + CallFrame* callFrame() const; + CodeBlock* codeBlock() const; + JSObject* object() const; + JSScope* scope() 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& payload(); + int32_t& tag(); + + static Register withInt(int32_t i) + { + Register r = jsNumber(i); + return r; + } + + private: + union { + EncodedJSValue value; + CallFrame* callFrame; + CodeBlock* codeBlock; + EncodedValueDescriptor encodedValue; + double number; + int64_t integer; + } u; + }; + + ALWAYS_INLINE Register::Register() + { +#ifndef NDEBUG + *this = JSValue(); +#endif + } + + ALWAYS_INLINE Register::Register(const JSValue& v) + { + u.value = JSValue::encode(v); + } + + ALWAYS_INLINE Register& Register::operator=(const JSValue& v) + { + u.value = JSValue::encode(v); + 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); + } + + ALWAYS_INLINE EncodedJSValue Register::encodedJSValue() const + { + return u.value; + } + + // Interpreter functions + + ALWAYS_INLINE Register& Register::operator=(CallFrame* callFrame) + { + u.callFrame = callFrame; + return *this; + } + + ALWAYS_INLINE Register& Register::operator=(CodeBlock* codeBlock) + { + u.codeBlock = codeBlock; + return *this; + } + + ALWAYS_INLINE int32_t Register::i() const + { + return jsValue().asInt32(); + } + + ALWAYS_INLINE CallFrame* Register::callFrame() const + { + return u.callFrame; + } + + ALWAYS_INLINE CodeBlock* Register::codeBlock() const + { + return u.codeBlock; + } + + 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) + return u.encodedValue.ptr; +#else + return bitwise_cast<JSCell*>(payload()); +#endif + } + + ALWAYS_INLINE int32_t Register::payload() const + { + return u.encodedValue.asBits.payload; + } + + ALWAYS_INLINE int32_t Register::tag() const + { + return u.encodedValue.asBits.tag; + } + + ALWAYS_INLINE int32_t& Register::payload() + { + return u.encodedValue.asBits.payload; + } + + ALWAYS_INLINE int32_t& Register::tag() + { + return u.encodedValue.asBits.tag; + } + +} // namespace JSC + +namespace WTF { + + template<> struct VectorTraits<JSC::Register> : VectorTraitsBase<true, JSC::Register> { }; + +} // namespace WTF + +#endif // Register_h diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp new file mode 100644 index 000000000..6fe792b7e --- /dev/null +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -0,0 +1,418 @@ +/* + * 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 "CallFrameInlines.h" +#include "ClonedArguments.h" +#include "Executable.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->caller; + readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); + return; + } +#endif // ENABLE(DFG_JIT) + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); +} + +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; + } + + unsigned index = callFrame->locationAsCodeOriginIndex(); + 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->locationAsBytecodeOffset(); +#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(); + 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 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::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::Function: + case CodeType::Global: { + String sourceURL = codeBlock()->ownerExecutable()->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::enableFunctionDotArguments()) + 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->ownerExecutable()->firstLine(); + column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); + + if (codeBlock->ownerExecutable()->hasOverrideLineNumber()) + line = codeBlock->ownerExecutable()->overrideLineNumber(); +} + +void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) +{ + 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->locationAsRawBits(); + logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits); + logF(indent, "codeBlock: %p ", codeBlock); + if (codeBlock) + dataLog(*codeBlock); + dataLog("\n"); + if (codeBlock && !isInlined) { + indent++; + + if (callFrame->hasLocationAsBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); + log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); +#if ENABLE(DFG_JIT) + } else { + log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); + if (codeBlock->hasCodeOrigins()) { + unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); + log(indent, "codeOriginIndex: ", codeOriginIndex, " of ", codeBlock->codeOrigins().size(), "\n"); + + JITCode::JITType jitType = codeBlock->jitType(); + if (jitType != JITCode::FTLJIT) { + JITCode* jitCode = codeBlock->jitCode().get(); + logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); + } + } +#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..0036a789c --- /dev/null +++ b/Source/JavaScriptCore/interpreter/StackVisitor.h @@ -0,0 +1,177 @@ +/* + * 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, + 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; } + +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..50ac65f78 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/VMEntryRecord.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VMEntryRecord_h +#define VMEntryRecord_h + +namespace JSC { + +typedef void VMEntryFrame; + +class ExecState; +class VM; + +struct VMEntryRecord { + /* + * This record stored in a vmEntryTo{JavaScript,Host} allocated frame. It is allocated on the stack + * after callee save registers where local variables would go. + */ + VM* m_vm; + ExecState* m_prevTopCallFrame; + VMEntryFrame* m_prevTopVMEntryFrame; + + ExecState* prevTopCallFrame() { return m_prevTopCallFrame; } + + VMEntryFrame* prevTopVMEntryFrame() { return m_prevTopVMEntryFrame; } +}; + +extern "C" VMEntryRecord* vmEntryRecord(VMEntryFrame*); + +} // namespace JSC + +#endif // VMEntryRecord_h |