diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
-rw-r--r-- | Source/JavaScriptCore/interpreter/CachedCall.h | 77 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/CallFrame.cpp | 123 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/CallFrame.h | 214 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/CallFrameClosure.h | 62 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/Interpreter.cpp | 5224 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/Interpreter.h | 221 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/Register.h | 228 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/RegisterFile.cpp | 107 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/RegisterFile.h | 120 |
9 files changed, 6376 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h new file mode 100644 index 000000000..ea55d90e5 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 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. + * + * 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 "JSFunction.h" +#include "JSGlobalObject.h" +#include "Interpreter.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_globalObjectScope(callFrame->globalData(), function->scope()->globalObject.get()) + { + ASSERT(!function->isHostFunction()); + m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, function, argumentCount + 1, function->scope()); + m_valid = !callFrame->hadException(); + } + + JSValue call() + { + ASSERT(m_valid); + return m_interpreter->execute(m_closure); + } + void setThis(JSValue v) { m_closure.setThis(v); } + void setArgument(int n, JSValue v) { m_closure.setArgument(n, v); } + + CallFrame* newCallFrame(ExecState* exec) + { + CallFrame* callFrame = m_closure.newCallFrame; + callFrame->setScopeChain(exec->scopeChain()); + return callFrame; + } + + ~CachedCall() + { + if (m_valid) + m_interpreter->endRepeatCall(m_closure); + } + + private: + bool m_valid; + Interpreter* m_interpreter; + DynamicGlobalObjectScope m_globalObjectScope; + CallFrameClosure m_closure; + }; +} + +#endif diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp new file mode 100644 index 000000000..6dba77898 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 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 "CodeBlock.h" +#include "Interpreter.h" + +namespace JSC { + +#ifndef NDEBUG +void CallFrame::dumpCaller() +{ + int signedLineNumber; + intptr_t sourceID; + UString urlString; + JSValue function; + + interpreter()->retrieveLastCaller(this, signedLineNumber, sourceID, urlString, function); + printf("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber); +} + +RegisterFile* CallFrame::registerFile() +{ + return &interpreter()->registerFile(); +} + +#endif + +#if ENABLE(DFG_JIT) +bool CallFrame::isInlineCallFrameSlow() +{ + if (!callee()) + return false; + JSCell* calleeAsFunctionCell = getJSFunction(callee()); + if (!calleeAsFunctionCell) + return false; + JSFunction* calleeAsFunction = asFunction(calleeAsFunctionCell); + return calleeAsFunction->executable() != codeBlock()->ownerExecutable(); +} + +CallFrame* CallFrame::trueCallerFrame() +{ + // this -> The callee; this is either an inlined callee in which case it already has + // a pointer to the true caller. Otherwise it contains current PC in the machine + // caller. + // + // machineCaller -> The caller according to the machine, which may be zero or + // more frames above the true caller due to inlining. + // + // trueCaller -> The real caller. + + // Am I an inline call frame? If so, we're done. + if (isInlineCallFrame()) + return callerFrame(); + + // I am a machine call frame, so the question is: is my caller a machine call frame + // that has inlines or a machine call frame that doesn't? + CallFrame* machineCaller = callerFrame()->removeHostCallFrameFlag(); + if (!machineCaller) + return 0; + ASSERT(!machineCaller->isInlineCallFrame()); + if (!machineCaller->codeBlock()) + return machineCaller; + if (!machineCaller->codeBlock()->hasCodeOrigins()) + return machineCaller; // No inlining, so machineCaller == trueCaller + + // Figure out where the caller frame would have gone relative to the machine + // caller, and rematerialize it. Do so for the entire inline stack. + + CodeOrigin codeOrigin = machineCaller->codeBlock()->codeOriginForReturn(returnPC()); + + for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { + InlineCallFrame* nextInlineCallFrame = inlineCallFrame->caller.inlineCallFrame; + + CallFrame* inlinedCaller = machineCaller + inlineCallFrame->stackOffset; + + JSFunction* calleeAsFunction = inlineCallFrame->callee.get(); + + // Fill in the inlinedCaller + inlinedCaller->setCodeBlock(machineCaller->codeBlock()); + + inlinedCaller->setScopeChain(calleeAsFunction->scope()); + if (nextInlineCallFrame) + inlinedCaller->setCallerFrame(machineCaller + nextInlineCallFrame->stackOffset); + else + inlinedCaller->setCallerFrame(machineCaller); + + inlinedCaller->setInlineCallFrame(inlineCallFrame); + inlinedCaller->setArgumentCountIncludingThis(inlineCallFrame->arguments.size()); + inlinedCaller->setCallee(calleeAsFunction); + + inlineCallFrame = nextInlineCallFrame; + } + + return machineCaller + codeOrigin.inlineCallFrame->stackOffset; +} +#endif + +} diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h new file mode 100644 index 000000000..d5037b794 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008, 2011 Apple Inc. All rights reserved. + * + * 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 "JSGlobalData.h" +#include "MacroAssemblerCodeRef.h" +#include "RegisterFile.h" + +namespace JSC { + + class Arguments; + class JSActivation; + class Interpreter; + class ScopeChainNode; + + // 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[RegisterFile::Callee].jsValue(); } + JSObject* callee() const { return this[RegisterFile::Callee].function(); } + CodeBlock* codeBlock() const { return this[RegisterFile::CodeBlock].Register::codeBlock(); } + ScopeChainNode* scopeChain() const + { + ASSERT(this[RegisterFile::ScopeChain].Register::scopeChain()); + return this[RegisterFile::ScopeChain].Register::scopeChain(); + } + + // Global object in which execution began. + JSGlobalObject* dynamicGlobalObject(); + + // Global object in which the currently executing code was defined. + // Differs from dynamicGlobalObject() during function calls across web browser frames. + inline 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. + inline JSObject* globalThisValue() const; + + inline JSGlobalData& globalData() 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() { globalData().exception = JSValue(); } + JSValue exception() const { return globalData().exception; } + bool hadException() const { return globalData().exception; } + + const CommonIdentifiers& propertyNames() const { return *globalData().propertyNames; } + const MarkedArgumentBuffer& emptyList() const { return *globalData().emptyList; } + Interpreter* interpreter() { return globalData().interpreter; } + Heap* heap() { return &globalData().heap; } +#ifndef NDEBUG + void dumpCaller(); +#endif + static const HashTable* arrayConstructorTable(CallFrame* callFrame) { return callFrame->globalData().arrayConstructorTable; } + static const HashTable* arrayPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().arrayPrototypeTable; } + static const HashTable* booleanPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().booleanPrototypeTable; } + static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->globalData().dateTable; } + static const HashTable* dateConstructorTable(CallFrame* callFrame) { return callFrame->globalData().dateConstructorTable; } + static const HashTable* errorPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().errorPrototypeTable; } + static const HashTable* globalObjectTable(CallFrame* callFrame) { return callFrame->globalData().globalObjectTable; } + static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->globalData().jsonTable; } + static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->globalData().mathTable; } + static const HashTable* numberConstructorTable(CallFrame* callFrame) { return callFrame->globalData().numberConstructorTable; } + static const HashTable* numberPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().numberPrototypeTable; } + static const HashTable* objectConstructorTable(CallFrame* callFrame) { return callFrame->globalData().objectConstructorTable; } + static const HashTable* objectPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().objectPrototypeTable; } + static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; } + static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->globalData().regExpConstructorTable; } + static const HashTable* regExpPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().regExpPrototypeTable; } + static const HashTable* stringTable(CallFrame* callFrame) { return callFrame->globalData().stringTable; } + static const HashTable* stringConstructorTable(CallFrame* callFrame) { return callFrame->globalData().stringConstructorTable; } + + static CallFrame* create(Register* callFrameBase) { return static_cast<CallFrame*>(callFrameBase); } + Register* registers() { return this; } + + CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; } + + CallFrame* callerFrame() const { return this[RegisterFile::CallerFrame].callFrame(); } +#if ENABLE(JIT) + ReturnAddressPtr returnPC() const { return ReturnAddressPtr(this[RegisterFile::ReturnPC].vPC()); } +#endif +#if ENABLE(DFG_JIT) + InlineCallFrame* inlineCallFrame() const { return this[RegisterFile::ReturnPC].asInlineCallFrame(); } +#else + // This will never be called if !ENABLE(DFG_JIT) since all calls should be guarded by + // isInlineCallFrame(). But to make it easier to write code without having a bunch of + // #if's, we make a dummy implementation available anyway. + InlineCallFrame* inlineCallFrame() const + { + ASSERT_NOT_REACHED(); + return 0; + } +#endif +#if ENABLE(INTERPRETER) + Instruction* returnVPC() const { return this[RegisterFile::ReturnPC].vPC(); } +#endif + + void setCallerFrame(CallFrame* callerFrame) { static_cast<Register*>(this)[RegisterFile::CallerFrame] = callerFrame; } + void setScopeChain(ScopeChainNode* scopeChain) { static_cast<Register*>(this)[RegisterFile::ScopeChain] = scopeChain; } + + ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, + CallFrame* callerFrame, int argc, JSObject* callee) + { + ASSERT(callerFrame); // Use noCaller() rather than 0 for the outer host call frame caller. + ASSERT(callerFrame == noCaller() || callerFrame->removeHostCallFrameFlag()->registerFile()->end() >= this); + + setCodeBlock(codeBlock); + setScopeChain(scopeChain); + 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). + inline Register& r(int); + // Read a register for a non-constant + inline Register& uncheckedR(int); + + // Access to arguments. + size_t argumentCount() const { return argumentCountIncludingThis() - 1; } + size_t argumentCountIncludingThis() const { return this[RegisterFile::ArgumentCount].payload(); } + static int argumentOffset(size_t argument) { return s_firstArgumentOffset - argument; } + static int argumentOffsetIncludingThis(size_t argument) { return s_thisArgumentOffset - argument; } + + JSValue argument(size_t argument) + { + if (argument >= argumentCount()) + return jsUndefined(); + return this[argumentOffset(argument)].jsValue(); + } + void setArgument(size_t argument, JSValue value) + { + this[argumentOffset(argument)] = value; + } + + static int thisArgumentOffset() { return argumentOffsetIncludingThis(0); } + JSValue thisValue() { return this[thisArgumentOffset()].jsValue(); } + void setThisValue(JSValue value) { this[thisArgumentOffset()] = value; } + + static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + RegisterFile::CallFrameHeaderSize; } + + // FIXME: Remove these. + int hostThisRegister() { return thisArgumentOffset(); } + JSValue hostThisValue() { return thisValue(); } + + static CallFrame* noCaller() { return reinterpret_cast<CallFrame*>(HostCallFrameFlag); } + + bool hasHostCallFrameFlag() const { return reinterpret_cast<intptr_t>(this) & HostCallFrameFlag; } + CallFrame* addHostCallFrameFlag() const { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) | HostCallFrameFlag); } + CallFrame* removeHostCallFrameFlag() { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) & ~HostCallFrameFlag); } + + void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[RegisterFile::ArgumentCount].payload() = count; } + void setCallee(JSObject* callee) { static_cast<Register*>(this)[RegisterFile::Callee] = Register::withCallee(callee); } + void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[RegisterFile::CodeBlock] = codeBlock; } + void setReturnPC(void* value) { static_cast<Register*>(this)[RegisterFile::ReturnPC] = (Instruction*)value; } + +#if ENABLE(DFG_JIT) + bool isInlineCallFrame(); + + void setInlineCallFrame(InlineCallFrame* inlineCallFrame) { static_cast<Register*>(this)[RegisterFile::ReturnPC] = inlineCallFrame; } + + // Call this to get the semantically correct JS CallFrame*. This resolves issues + // surrounding inlining and the HostCallFrameFlag stuff. + CallFrame* trueCallerFrame(); +#else + bool isInlineCallFrame() { return false; } + + CallFrame* trueCallerFrame() { return callerFrame()->removeHostCallFrameFlag(); } +#endif + + private: + static const intptr_t HostCallFrameFlag = 1; + static const int s_thisArgumentOffset = -1 - RegisterFile::CallFrameHeaderSize; + static const int s_firstArgumentOffset = s_thisArgumentOffset - 1; + +#ifndef NDEBUG + RegisterFile* registerFile(); +#endif +#if ENABLE(DFG_JIT) + bool isInlineCallFrameSlow(); +#endif + ExecState(); + ~ExecState(); + }; + +} // 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..2a841511a --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 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. + * + * 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 + +namespace JSC { + +struct CallFrameClosure { + CallFrame* oldCallFrame; + CallFrame* newCallFrame; + JSFunction* function; + FunctionExecutable* functionExecutable; + JSGlobalData* globalData; + Register* oldEnd; + ScopeChainNode* scopeChain; + int parameterCountIncludingThis; + int argumentCountIncludingThis; + + void setThis(JSValue value) + { + newCallFrame->setThisValue(value); + } + + void setArgument(int argument, JSValue value) + { + newCallFrame->setArgument(argument, value); + } + + void resetCallFrame() + { + newCallFrame->setScopeChain(scopeChain); + for (int i = argumentCountIncludingThis; i < parameterCountIncludingThis; ++i) + newCallFrame->setArgument(i, jsUndefined()); + } +}; + +} + +#endif diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp new file mode 100644 index 000000000..38befcd4f --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -0,0 +1,5224 @@ +/* + * Copyright (C) 2008, 2009, 2010 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 Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 "Arguments.h" +#include "BatchedTransitionOptimizer.h" +#include "CallFrame.h" +#include "CallFrameClosure.h" +#include "CodeBlock.h" +#include "Heap.h" +#include "Debugger.h" +#include "DebuggerCallFrame.h" +#include "ErrorInstance.h" +#include "EvalCodeCache.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "JSActivation.h" +#include "JSArray.h" +#include "JSByteArray.h" +#include "JSFunction.h" +#include "JSNotAnObject.h" +#include "JSPropertyNameIterator.h" +#include "LiteralParser.h" +#include "JSStaticScopeObject.h" +#include "JSString.h" +#include "ObjectPrototype.h" +#include "Operations.h" +#include "Parser.h" +#include "Profiler.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "Register.h" +#include "SamplingTool.h" +#include "StrictEvalActivation.h" +#include "UStringConcatenate.h" +#include <limits.h> +#include <stdio.h> +#include <wtf/Threading.h> + +#if ENABLE(JIT) +#include "JIT.h" +#endif + +#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(COMPUTED_GOTO_INTERPRETER) && !defined(__llvm__)) + +using namespace std; + +namespace JSC { + +// Returns the depth of the scope chain within a given call frame. +static int depth(CodeBlock* codeBlock, ScopeChainNode* sc) +{ + if (!codeBlock->needsFullScopeChain()) + return 0; + return sc->localDepth(); +} + +#if ENABLE(INTERPRETER) +static NEVER_INLINE JSValue concatenateStrings(ExecState* exec, Register* strings, unsigned count) +{ + return jsString(exec, strings, count); +} + +NEVER_INLINE bool Interpreter::resolve(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + int dst = vPC[1].u.operand; + int property = vPC[2].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& ident = codeBlock->identifier(property); + do { + JSObject* o = iter->get(); + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + } while (++iter != end); + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +NEVER_INLINE bool Interpreter::resolveSkip(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + CodeBlock* codeBlock = callFrame->codeBlock(); + + int dst = vPC[1].u.operand; + int property = vPC[2].u.operand; + int skip = vPC[3].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) + ++iter; + } + while (skip--) { + ++iter; + ASSERT(iter != end); + } + Identifier& ident = codeBlock->identifier(property); + do { + JSObject* o = iter->get(); + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + ASSERT(result); + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + } while (++iter != end); + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +NEVER_INLINE bool Interpreter::resolveGlobal(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + int dst = vPC[1].u.operand; + CodeBlock* codeBlock = callFrame->codeBlock(); + JSGlobalObject* globalObject = codeBlock->globalObject(); + ASSERT(globalObject->isGlobalObject()); + int property = vPC[2].u.operand; + Structure* structure = vPC[3].u.structure.get(); + int offset = vPC[4].u.operand; + + if (structure == globalObject->structure()) { + callFrame->uncheckedR(dst) = JSValue(globalObject->getDirectOffset(offset)); + return true; + } + + Identifier& ident = codeBlock->identifier(property); + PropertySlot slot(globalObject); + if (globalObject->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) { + vPC[3].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), globalObject->structure()); + vPC[4] = slot.cachedOffset(); + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +NEVER_INLINE bool Interpreter::resolveGlobalDynamic(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + int dst = vPC[1].u.operand; + CodeBlock* codeBlock = callFrame->codeBlock(); + JSGlobalObject* globalObject = codeBlock->globalObject(); + ASSERT(globalObject->isGlobalObject()); + int property = vPC[2].u.operand; + Structure* structure = vPC[3].u.structure.get(); + int offset = vPC[4].u.operand; + int skip = vPC[5].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) + ++iter; + } + while (skip--) { + JSObject* o = iter->get(); + if (o->hasCustomProperties()) { + Identifier& ident = codeBlock->identifier(property); + do { + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + ASSERT(result); + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + if (iter == end) + break; + o = iter->get(); + ++iter; + } while (true); + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; + } + ++iter; + } + + if (structure == globalObject->structure()) { + callFrame->uncheckedR(dst) = JSValue(globalObject->getDirectOffset(offset)); + ASSERT(callFrame->uncheckedR(dst).jsValue()); + return true; + } + + Identifier& ident = codeBlock->identifier(property); + PropertySlot slot(globalObject); + if (globalObject->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) { + vPC[3].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), globalObject->structure()); + vPC[4] = slot.cachedOffset(); + ASSERT(result); + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + ASSERT(result); + callFrame->uncheckedR(dst) = JSValue(result); + return true; + } + + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +NEVER_INLINE void Interpreter::resolveBase(CallFrame* callFrame, Instruction* vPC) +{ + int dst = vPC[1].u.operand; + int property = vPC[2].u.operand; + bool isStrictPut = vPC[3].u.operand; + Identifier ident = callFrame->codeBlock()->identifier(property); + JSValue result = JSC::resolveBase(callFrame, ident, callFrame->scopeChain(), isStrictPut); + if (result) { + callFrame->uncheckedR(dst) = result; + ASSERT(callFrame->uncheckedR(dst).jsValue()); + } else + callFrame->globalData().exception = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring()); +} + +NEVER_INLINE bool Interpreter::resolveBaseAndProperty(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + int baseDst = vPC[1].u.operand; + int propDst = vPC[2].u.operand; + int property = vPC[3].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + + // FIXME: add scopeDepthIsZero optimization + + ASSERT(iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& ident = codeBlock->identifier(property); + JSObject* base; + do { + base = iter->get(); + PropertySlot slot(base); + if (base->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame->uncheckedR(propDst) = JSValue(result); + callFrame->uncheckedR(baseDst) = JSValue(base); + return true; + } + ++iter; + } while (iter != end); + + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +NEVER_INLINE bool Interpreter::resolveThisAndProperty(CallFrame* callFrame, Instruction* vPC, JSValue& exceptionValue) +{ + int thisDst = vPC[1].u.operand; + int propDst = vPC[2].u.operand; + int property = vPC[3].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + + // FIXME: add scopeDepthIsZero optimization + + ASSERT(iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& ident = codeBlock->identifier(property); + JSObject* base; + do { + base = iter->get(); + ++iter; + PropertySlot slot(base); + if (base->getPropertySlot(callFrame, ident, slot)) { + JSValue result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame->uncheckedR(propDst) = JSValue(result); + // All entries on the scope chain should be EnvironmentRecords (activations etc), + // other then 'with' object, which are directly referenced from the scope chain, + // and the global object. If we hit either an EnvironmentRecord or a global + // object at the end of the scope chain, this is undefined. If we hit a non- + // EnvironmentRecord within the scope chain, pass the base as the this value. + if (iter == end || base->structure()->typeInfo().isEnvironmentRecord()) + callFrame->uncheckedR(thisDst) = jsUndefined(); + else + callFrame->uncheckedR(thisDst) = JSValue(base); + return true; + } + } while (iter != end); + + exceptionValue = createUndefinedVariableError(callFrame, ident); + return false; +} + +#endif // ENABLE(INTERPRETER) + +ALWAYS_INLINE CallFrame* Interpreter::slideRegisterWindowForCall(CodeBlock* newCodeBlock, RegisterFile* registerFile, CallFrame* callFrame, size_t registerOffset, int argumentCountIncludingThis) +{ + // This ensures enough space for the worst case scenario of zero arguments passed by the caller. + if (!registerFile->grow(callFrame->registers() + registerOffset + newCodeBlock->m_numParameters + newCodeBlock->m_numCalleeRegisters)) + return 0; + + if (argumentCountIncludingThis >= newCodeBlock->m_numParameters) { + Register* newCallFrame = callFrame->registers() + registerOffset; + return CallFrame::create(newCallFrame); + } + + // Too few arguments -- copy arguments, then fill in missing arguments with undefined. + size_t delta = newCodeBlock->m_numParameters - argumentCountIncludingThis; + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset + delta); + + Register* dst = &newCallFrame->uncheckedR(CallFrame::thisArgumentOffset()); + Register* end = dst - argumentCountIncludingThis; + for ( ; dst != end; --dst) + *dst = *(dst - delta); + + end -= delta; + for ( ; dst != end; --dst) + *dst = jsUndefined(); + + return newCallFrame; +} + +#if ENABLE(INTERPRETER) +static NEVER_INLINE bool isInvalidParamForIn(CallFrame* callFrame, JSValue value, JSValue& exceptionData) +{ + if (value.isObject()) + return false; + exceptionData = createInvalidParamError(callFrame, "in" , value); + return true; +} + +static NEVER_INLINE bool isInvalidParamForInstanceOf(CallFrame* callFrame, JSValue value, JSValue& exceptionData) +{ + if (value.isObject() && asObject(value)->structure()->typeInfo().implementsHasInstance()) + return false; + exceptionData = createInvalidParamError(callFrame, "instanceof" , value); + return true; +} +#endif + +JSValue eval(CallFrame* callFrame) +{ + if (!callFrame->argumentCount()) + return jsUndefined(); + + JSValue program = callFrame->argument(0); + if (!program.isString()) + return program; + + UString programSource = asString(program)->value(callFrame); + if (callFrame->hadException()) + return JSValue(); + + CallFrame* callerFrame = callFrame->callerFrame(); + CodeBlock* callerCodeBlock = callerFrame->codeBlock(); + ScopeChainNode* callerScopeChain = callerFrame->scopeChain(); + EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + + if (!eval) { + if (!callerCodeBlock->isStrictMode()) { + // FIXME: We can use the preparser in strict mode, we just need additional logic + // to prevent duplicates. + if (programSource.is8Bit()) { + LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return parsedObject; + } else { + LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return parsedObject; + } + } + + JSValue exceptionValue; + eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue); + + ASSERT(!eval == exceptionValue); + if (UNLIKELY(!eval)) + return throwError(callFrame, exceptionValue); + } + + JSValue thisValue = callerFrame->thisValue(); + ASSERT(isValidThisObject(thisValue, callFrame)); + Interpreter* interpreter = callFrame->globalData().interpreter; + return interpreter->execute(eval, callFrame, thisValue, callerScopeChain, callFrame->registers() - interpreter->registerFile().begin() + 1 + RegisterFile::CallFrameHeaderSize); +} + +CallFrame* loadVarargs(CallFrame* callFrame, RegisterFile* registerFile, JSValue thisValue, JSValue arguments, int firstFreeRegister) +{ + if (!arguments) { // f.apply(x, arguments), with arguments unmodified. + unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + argumentCountIncludingThis + RegisterFile::CallFrameHeaderSize); + if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !registerFile->grow(newCallFrame->registers())) { + callFrame->globalData().exception = createStackOverflowError(callFrame); + return 0; + } + + newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); + newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < callFrame->argumentCount(); ++i) + newCallFrame->setArgument(i, callFrame->argument(i)); + return newCallFrame; + } + + if (arguments.isUndefinedOrNull()) { + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + 1 + RegisterFile::CallFrameHeaderSize); + if (!registerFile->grow(newCallFrame->registers())) { + callFrame->globalData().exception = createStackOverflowError(callFrame); + return 0; + } + newCallFrame->setArgumentCountIncludingThis(1); + newCallFrame->setThisValue(thisValue); + return newCallFrame; + } + + if (!arguments.isObject()) { + callFrame->globalData().exception = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); + return 0; + } + + if (asObject(arguments)->classInfo() == &Arguments::s_info) { + Arguments* argsObject = asArguments(arguments); + unsigned argCount = argsObject->length(callFrame); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !registerFile->grow(newCallFrame->registers())) { + callFrame->globalData().exception = createStackOverflowError(callFrame); + return 0; + } + newCallFrame->setArgumentCountIncludingThis(argCount + 1); + newCallFrame->setThisValue(thisValue); + argsObject->copyToArguments(callFrame, newCallFrame, argCount); + return newCallFrame; + } + + if (isJSArray(arguments)) { + JSArray* array = asArray(arguments); + unsigned argCount = array->length(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !registerFile->grow(newCallFrame->registers())) { + callFrame->globalData().exception = createStackOverflowError(callFrame); + return 0; + } + newCallFrame->setArgumentCountIncludingThis(argCount + 1); + newCallFrame->setThisValue(thisValue); + array->copyToArguments(callFrame, newCallFrame, argCount); + return newCallFrame; + } + + JSObject* argObject = asObject(arguments); + unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + CallFrame::offsetFor(argCount + 1)); + if (argCount > Arguments::MaxArguments || !registerFile->grow(newCallFrame->registers())) { + callFrame->globalData().exception = createStackOverflowError(callFrame); + return 0; + } + newCallFrame->setArgumentCountIncludingThis(argCount + 1); + newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < argCount; ++i) { + newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i)); + if (UNLIKELY(callFrame->globalData().exception)) + return 0; + } + return newCallFrame; +} + +Interpreter::Interpreter() + : m_sampleEntryDepth(0) + , m_reentryDepth(0) +#if !ASSERT_DISABLED + , m_initialized(false) +#endif + , m_enabled(false) +{ +} + +void Interpreter::initialize(bool canUseJIT) +{ +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + if (canUseJIT) { + // If the JIT is present, don't use jump destinations for opcodes. + + for (int i = 0; i < numOpcodeIDs; ++i) { + Opcode opcode = bitwise_cast<void*>(static_cast<uintptr_t>(i)); + m_opcodeTable[i] = opcode; + m_opcodeIDTable.add(opcode, static_cast<OpcodeID>(i)); + } + } else { + privateExecute(InitializeAndReturn, 0, 0); + + for (int i = 0; i < numOpcodeIDs; ++i) + m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); + + m_enabled = true; + } +#else + UNUSED_PARAM(canUseJIT); +#if ENABLE(INTERPRETER) + m_enabled = true; +#else + m_enabled = false; +#endif +#endif // ENABLE(COMPUTED_GOTO_INTERPRETER) +#if !ASSERT_DISABLED + m_initialized = true; +#endif + +#if ENABLE(OPCODE_SAMPLING) + enableSampler(); +#endif +} + +#ifndef NDEBUG + +void Interpreter::dumpCallFrame(CallFrame* callFrame) +{ + callFrame->codeBlock()->dump(callFrame); + dumpRegisters(callFrame); +} + +void Interpreter::dumpRegisters(CallFrame* callFrame) +{ + printf("Register frame: \n\n"); + printf("-----------------------------------------------------------------------------\n"); + printf(" use | address | value \n"); + printf("-----------------------------------------------------------------------------\n"); + + CodeBlock* codeBlock = callFrame->codeBlock(); + const Register* it; + const Register* end; + JSValue v; + + it = callFrame->registers() - RegisterFile::CallFrameHeaderSize - codeBlock->m_numParameters; + v = (*it).jsValue(); +#if USE(JSVALUE32_64) + printf("[this] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); ++it; +#else + printf("[this] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); ++it; +#endif + end = it + max(codeBlock->m_numParameters - 1, 0); // - 1 to skip "this" + if (it != end) { + do { + v = (*it).jsValue(); +#if USE(JSVALUE32_64) + printf("[param] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); +#else + printf("[param] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); +#endif + ++it; + } while (it != end); + } + printf("-----------------------------------------------------------------------------\n"); + printf("[CodeBlock] | %10p | %p \n", it, (*it).codeBlock()); ++it; + printf("[ScopeChain] | %10p | %p \n", it, (*it).scopeChain()); ++it; + printf("[CallerRegisters] | %10p | %d \n", it, (*it).i()); ++it; + printf("[ReturnPC] | %10p | %p \n", it, (*it).vPC()); ++it; + printf("[ArgumentCount] | %10p | %d \n", it, (*it).i()); ++it; + printf("[Callee] | %10p | %p \n", it, (*it).function()); ++it; + printf("-----------------------------------------------------------------------------\n"); + + int registerCount = 0; + + end = it + codeBlock->m_numVars; + if (it != end) { + do { + v = (*it).jsValue(); +#if USE(JSVALUE32_64) + printf("[r%2d] | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v)); +#else + printf("[r%2d] | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v)); +#endif + ++it; + ++registerCount; + } while (it != end); + } + printf("-----------------------------------------------------------------------------\n"); + + end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars; + if (it != end) { + do { + v = (*it).jsValue(); +#if USE(JSVALUE32_64) + printf("[r%2d] | %10p | %-16s 0x%llx \n", registerCount, it, v.description(), JSValue::encode(v)); +#else + printf("[r%2d] | %10p | %-16s %p \n", registerCount, it, v.description(), JSValue::encode(v)); +#endif + ++it; + ++registerCount; + } while (it != end); + } + printf("-----------------------------------------------------------------------------\n"); +} + +#endif + +bool Interpreter::isOpcode(Opcode opcode) +{ +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + return opcode != HashTraits<Opcode>::emptyValue() + && !HashTraits<Opcode>::isDeletedValue(opcode) + && m_opcodeIDTable.contains(opcode); +#else + return opcode >= 0 && opcode <= op_end; +#endif +} + +NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue exceptionValue, unsigned& bytecodeOffset, CodeBlock*& codeBlock) +{ + CodeBlock* oldCodeBlock = codeBlock; + ScopeChainNode* scopeChain = callFrame->scopeChain(); + + if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { + DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); + if (callFrame->callee()) + debugger->returnEvent(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine()); + else + debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine()); + } + + // If this call frame created an activation or an 'arguments' object, tear it off. + if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsFullScopeChain()) { + if (!callFrame->uncheckedR(oldCodeBlock->activationRegister()).jsValue()) { + oldCodeBlock->createActivation(callFrame); + scopeChain = callFrame->scopeChain(); + } + while (!scopeChain->object->inherits(&JSActivation::s_info)) + scopeChain = scopeChain->pop(); + + callFrame->setScopeChain(scopeChain); + JSActivation* activation = asActivation(scopeChain->object.get()); + activation->tearOff(*scopeChain->globalData); + if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) + asArguments(arguments)->didTearOffActivation(callFrame->globalData(), activation); + } else if (oldCodeBlock->usesArguments() && !oldCodeBlock->isStrictMode()) { + if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) + asArguments(arguments)->tearOff(callFrame); + } + + CallFrame* callerFrame = callFrame->callerFrame(); + callFrame->globalData().topCallFrame = callerFrame; + if (callerFrame->hasHostCallFrameFlag()) + return false; + + codeBlock = callerFrame->codeBlock(); + + // Because of how the JIT records call site->bytecode offset + // information the JIT reports the bytecodeOffset for the returnPC + // to be at the beginning of the opcode that has caused the call. + // In the interpreter we have an actual return address, which is + // the beginning of next instruction to execute. To get an offset + // inside the call instruction that triggered the exception we + // have to subtract 1. +#if ENABLE(JIT) && ENABLE(INTERPRETER) + if (callerFrame->globalData().canUseJIT()) + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); + else + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()) - 1; +#elif ENABLE(JIT) + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); +#else + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()) - 1; +#endif + + callFrame = callerFrame; + return true; +} + +static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset) +{ + exception->clearAppendSourceToMessage(); + + if (!callFrame->codeBlock()->hasExpressionInfo()) + return; + + int startOffset = 0; + int endOffset = 0; + int divotPoint = 0; + + CodeBlock* codeBlock = callFrame->codeBlock(); + codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset); + + int expressionStart = divotPoint - startOffset; + int expressionStop = divotPoint + endOffset; + + if (!expressionStop || expressionStart > codeBlock->source()->length()) + return; + + JSGlobalData* globalData = &callFrame->globalData(); + JSValue jsMessage = exception->getDirect(*globalData, globalData->propertyNames->message); + if (!jsMessage || !jsMessage.isString()) + return; + + UString message = asString(jsMessage)->value(callFrame); + + if (expressionStart < expressionStop) + message = makeUString(message, " (evaluating '", codeBlock->source()->getRange(expressionStart, expressionStop), "')"); + else { + // No range information, so give a few characters of context + const StringImpl* data = codeBlock->source()->data(); + int dataLength = codeBlock->source()->length(); + int start = expressionStart; + int stop = expressionStart; + // Get up to 20 characters of context to the left and right of the divot, clamping to the line. + // then strip whitespace. + while (start > 0 && (expressionStart - start < 20) && (*data)[start - 1] != '\n') + start--; + while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start])) + start++; + while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n') + stop++; + while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1])) + stop--; + message = makeUString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')"); + } + + exception->putDirect(*globalData, globalData->propertyNames->message, jsString(globalData, message)); +} + +NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset) +{ + CodeBlock* codeBlock = callFrame->codeBlock(); + bool isInterrupt = false; + + // Set up the exception object + if (exceptionValue.isObject()) { + JSObject* exception = asObject(exceptionValue); + + if (exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage()) + appendSourceToError(callFrame, static_cast<ErrorInstance*>(exception), bytecodeOffset); + + // Using hasExpressionInfo to imply we are interested in rich exception info. + if (codeBlock->hasExpressionInfo() && !hasErrorInfo(callFrame, exception)) { + ASSERT(codeBlock->hasLineInfo()); + + // FIXME: should only really be adding these properties to VM generated exceptions, + // but the inspector currently requires these for all thrown objects. + addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source()); + } + + isInterrupt = isInterruptedExecutionException(exception) || isTerminatedExecutionException(exception); + } + + if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { + DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); + bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); + debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), hasHandler); + } + + // Calculate an exception handler vPC, unwinding call frames as necessary. + HandlerInfo* handler = 0; + while (isInterrupt || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { + if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { + if (Profiler* profiler = *Profiler::enabledProfilerReference()) + profiler->exceptionUnwind(callFrame); + return 0; + } + } + + if (Profiler* profiler = *Profiler::enabledProfilerReference()) + profiler->exceptionUnwind(callFrame); + + // Shrink the JS stack, in case stack overflow made it huge. + Register* highWaterMark = 0; + for (CallFrame* callerFrame = callFrame; callerFrame; callerFrame = callerFrame->callerFrame()->removeHostCallFrameFlag()) { + CodeBlock* codeBlock = callerFrame->codeBlock(); + if (!codeBlock) + continue; + Register* callerHighWaterMark = callerFrame->registers() + codeBlock->m_numCalleeRegisters; + highWaterMark = max(highWaterMark, callerHighWaterMark); + } + m_registerFile.shrink(highWaterMark); + + // Unwind the scope chain within the exception handler's call frame. + ScopeChainNode* scopeChain = callFrame->scopeChain(); + int scopeDelta = 0; + if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode + || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) + scopeDelta = depth(codeBlock, scopeChain) - handler->scopeDepth; + ASSERT(scopeDelta >= 0); + while (scopeDelta--) + scopeChain = scopeChain->pop(); + callFrame->setScopeChain(scopeChain); + + return handler; +} + +static inline JSValue checkedReturn(JSValue returnValue) +{ + ASSERT(returnValue); + return returnValue; +} + +static inline JSObject* checkedReturn(JSObject* returnValue) +{ + ASSERT(returnValue); + return returnValue; +} + +JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, ScopeChainNode* scopeChain, JSObject* thisObj) +{ + ASSERT(isValidThisObject(thisObj, callFrame)); + ASSERT(!scopeChain->globalData->exception); + ASSERT(!callFrame->globalData().isCollectorBusy()); + if (callFrame->globalData().isCollectorBusy()) + return jsNull(); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); + Vector<JSONPData> JSONPData; + bool parseResult; + const UString programSource = program->source().toString(); + if (programSource.is8Bit()) { + LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP); + parseResult = literalParser.tryJSONPParse(JSONPData, scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())); + } else { + LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP); + parseResult = literalParser.tryJSONPParse(JSONPData, scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())); + } + + if (parseResult) { + JSGlobalObject* globalObject = scopeChain->globalObject.get(); + 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) { + if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) { + PutPropertySlot slot; + globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot); + } else + globalObject->methodTable()->putWithAttributes(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete); + // var declarations return undefined + 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 throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), 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: + ASSERT_NOT_REACHED(); + return jsUndefined(); + } + } + PutPropertySlot slot; + 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 throwError(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.put(callFrame, JSONPPath.last().m_pathIndex, JSONPValue); + if (callFrame->hadException()) + return jsUndefined(); + break; + } + default: + ASSERT_NOT_REACHED(); + return jsUndefined(); + } + result = JSONPValue; + } + return result; + } +failedJSONP: + JSObject* error = program->compile(callFrame, scopeChain); + if (error) + return checkedReturn(throwError(callFrame, error)); + CodeBlock* codeBlock = &program->generatedBytecode(); + + Register* oldEnd = m_registerFile.end(); + Register* newEnd = oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize + codeBlock->m_numCalleeRegisters; + if (!m_registerFile.grow(newEnd)) + return checkedReturn(throwStackOverflowError(callFrame)); + + CallFrame* newCallFrame = CallFrame::create(oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize); + ASSERT(codeBlock->m_numParameters == 1); // 1 parameter for 'this'. + newCallFrame->init(codeBlock, 0, scopeChain, CallFrame::noCaller(), codeBlock->m_numParameters, 0); + newCallFrame->setThisValue(thisObj); + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, program->sourceURL(), program->lineNo()); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + + m_reentryDepth++; +#if ENABLE(JIT) + if (callFrame->globalData().canUseJIT()) + result = program->generatedJITCode().execute(&m_registerFile, newCallFrame, scopeChain->globalData); + else +#endif + result = privateExecute(Normal, &m_registerFile, newCallFrame); + + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, program->sourceURL(), program->lineNo()); + + m_registerFile.shrink(oldEnd); + + return checkedReturn(result); +} + +JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args) +{ + ASSERT(isValidThisObject(thisValue, callFrame)); + ASSERT(!callFrame->hadException()); + ASSERT(!callFrame->globalData().isCollectorBusy()); + if (callFrame->globalData().isCollectorBusy()) + return jsNull(); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + Register* oldEnd = m_registerFile.end(); + int argCount = 1 + args.size(); // implicit "this" parameter + size_t registerOffset = argCount + RegisterFile::CallFrameHeaderSize; + + CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); + if (!m_registerFile.grow(newCallFrame->registers())) + return checkedReturn(throwStackOverflowError(callFrame)); + + newCallFrame->setThisValue(thisValue); + for (size_t i = 0; i < args.size(); ++i) + newCallFrame->setArgument(i, args.at(i)); + + if (callType == CallTypeJS) { + ScopeChainNode* callDataScopeChain = callData.js.scopeChain; + + DynamicGlobalObjectScope globalObjectScope(*callDataScopeChain->globalData, callDataScopeChain->globalObject.get()); + + JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); + if (UNLIKELY(!!compileError)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwError(callFrame, compileError)); + } + + CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); + newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_registerFile, newCallFrame, 0, argCount); + if (UNLIKELY(!newCallFrame)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + newCallFrame->init(newCodeBlock, 0, callDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, function); + + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, function); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + + m_reentryDepth++; +#if ENABLE(JIT) + if (callFrame->globalData().canUseJIT()) + result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_registerFile, newCallFrame, callDataScopeChain->globalData); + else +#endif + result = privateExecute(Normal, &m_registerFile, newCallFrame); + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, function); + + m_registerFile.shrink(oldEnd); + return checkedReturn(result); + } + + ASSERT(callType == CallTypeHost); + ScopeChainNode* scopeChain = callFrame->scopeChain(); + newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, function); + + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, function); + + JSValue result; + { + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + result = JSValue::decode(callData.native.function(newCallFrame)); + } + + if (*profiler) + (*profiler)->didExecute(callFrame, function); + + m_registerFile.shrink(oldEnd); + return checkedReturn(result); +} + +JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +{ + ASSERT(!callFrame->hadException()); + ASSERT(!callFrame->globalData().isCollectorBusy()); + // We throw in this case because we have to return something "valid" but we're + // already in an invalid state. + if (callFrame->globalData().isCollectorBusy()) + return checkedReturn(throwStackOverflowError(callFrame)); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + Register* oldEnd = m_registerFile.end(); + int argCount = 1 + args.size(); // implicit "this" parameter + size_t registerOffset = argCount + RegisterFile::CallFrameHeaderSize; + + if (!m_registerFile.grow(oldEnd + registerOffset)) + return checkedReturn(throwStackOverflowError(callFrame)); + + CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); + newCallFrame->setThisValue(jsUndefined()); + for (size_t i = 0; i < args.size(); ++i) + newCallFrame->setArgument(i, args.at(i)); + + if (constructType == ConstructTypeJS) { + ScopeChainNode* constructDataScopeChain = constructData.js.scopeChain; + + DynamicGlobalObjectScope globalObjectScope(*constructDataScopeChain->globalData, constructDataScopeChain->globalObject.get()); + + JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, constructDataScopeChain); + if (UNLIKELY(!!compileError)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwError(callFrame, compileError)); + } + + CodeBlock* newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); + newCallFrame = slideRegisterWindowForCall(newCodeBlock, &m_registerFile, newCallFrame, 0, argCount); + if (UNLIKELY(!newCallFrame)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + newCallFrame->init(newCodeBlock, 0, constructDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); + + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, constructor); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + + m_reentryDepth++; +#if ENABLE(JIT) + if (callFrame->globalData().canUseJIT()) + result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_registerFile, newCallFrame, constructDataScopeChain->globalData); + else +#endif + result = privateExecute(Normal, &m_registerFile, newCallFrame); + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, constructor); + + m_registerFile.shrink(oldEnd); + if (callFrame->hadException()) + return 0; + ASSERT(result.isObject()); + return checkedReturn(asObject(result)); + } + + ASSERT(constructType == ConstructTypeHost); + ScopeChainNode* scopeChain = callFrame->scopeChain(); + newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); + + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, constructor); + + JSValue result; + { + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + result = JSValue::decode(constructData.native.function(newCallFrame)); + } + + if (*profiler) + (*profiler)->didExecute(callFrame, constructor); + + m_registerFile.shrink(oldEnd); + if (callFrame->hadException()) + return 0; + ASSERT(result.isObject()); + return checkedReturn(asObject(result)); +} + +CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, JSFunction* function, int argumentCountIncludingThis, ScopeChainNode* scopeChain) +{ + ASSERT(!scopeChain->globalData->exception); + + if (callFrame->globalData().isCollectorBusy()) + return CallFrameClosure(); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + + Register* oldEnd = m_registerFile.end(); + size_t registerOffset = argumentCountIncludingThis + RegisterFile::CallFrameHeaderSize; + + CallFrame* newCallFrame = CallFrame::create(oldEnd + registerOffset); + if (!m_registerFile.grow(newCallFrame->registers())) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + + JSObject* error = functionExecutable->compileForCall(callFrame, scopeChain); + if (error) { + throwError(callFrame, error); + m_registerFile.shrink(oldEnd); + return CallFrameClosure(); + } + CodeBlock* codeBlock = &functionExecutable->generatedBytecodeForCall(); + + newCallFrame = slideRegisterWindowForCall(codeBlock, &m_registerFile, newCallFrame, 0, argumentCountIncludingThis); + if (UNLIKELY(!newCallFrame)) { + throwStackOverflowError(callFrame); + m_registerFile.shrink(oldEnd); + return CallFrameClosure(); + } + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), argumentCountIncludingThis, function); + scopeChain->globalData->topCallFrame = newCallFrame; + CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, scopeChain->globalData, oldEnd, scopeChain, codeBlock->m_numParameters, argumentCountIncludingThis }; + return result; +} + +JSValue Interpreter::execute(CallFrameClosure& closure) +{ + ASSERT(!closure.oldCallFrame->globalData().isCollectorBusy()); + if (closure.oldCallFrame->globalData().isCollectorBusy()) + return jsNull(); + closure.resetCallFrame(); + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(closure.oldCallFrame, closure.function); + + TopCallFrameSetter topCallFrame(*closure.globalData, closure.newCallFrame); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + + m_reentryDepth++; +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (closure.newCallFrame->globalData().canUseJIT()) +#endif + result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_registerFile, closure.newCallFrame, closure.globalData); +#if ENABLE(INTERPRETER) + else +#endif +#endif +#if ENABLE(INTERPRETER) + result = privateExecute(Normal, &m_registerFile, closure.newCallFrame); +#endif + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(closure.oldCallFrame, closure.function); + return checkedReturn(result); +} + +void Interpreter::endRepeatCall(CallFrameClosure& closure) +{ + closure.globalData->topCallFrame = closure.oldCallFrame; + m_registerFile.shrink(closure.oldEnd); +} + +JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, ScopeChainNode* scopeChain, int globalRegisterOffset) +{ + ASSERT(isValidThisObject(thisValue, callFrame)); + ASSERT(!scopeChain->globalData->exception); + ASSERT(!callFrame->globalData().isCollectorBusy()); + if (callFrame->globalData().isCollectorBusy()) + return jsNull(); + + DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get()); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + JSObject* compileError = eval->compile(callFrame, scopeChain); + if (UNLIKELY(!!compileError)) + return checkedReturn(throwError(callFrame, compileError)); + EvalCodeBlock* codeBlock = &eval->generatedBytecode(); + + JSObject* variableObject; + for (ScopeChainNode* node = scopeChain; ; node = node->next.get()) { + ASSERT(node); + if (node->object->isVariableObject()) { + variableObject = static_cast<JSVariableObject*>(node->object.get()); + break; + } + } + + unsigned numVariables = codeBlock->numVariables(); + int numFunctions = codeBlock->numberOfFunctionDecls(); + bool pushedScope = false; + if (numVariables || numFunctions) { + if (codeBlock->isStrictMode()) { + variableObject = StrictEvalActivation::create(callFrame); + scopeChain = scopeChain->push(variableObject); + pushedScope = true; + } + // Scope for BatchedTransitionOptimizer + BatchedTransitionOptimizer optimizer(callFrame->globalData(), variableObject); + + for (unsigned i = 0; i < numVariables; ++i) { + const Identifier& ident = codeBlock->variable(i); + if (!variableObject->hasProperty(callFrame, ident)) { + PutPropertySlot slot; + variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); + } + } + + for (int i = 0; i < numFunctions; ++i) { + FunctionExecutable* function = codeBlock->functionDecl(i); + PutPropertySlot slot; + variableObject->methodTable()->put(variableObject, callFrame, function->name(), function->make(callFrame, scopeChain), slot); + } + } + + Register* oldEnd = m_registerFile.end(); + Register* newEnd = m_registerFile.begin() + globalRegisterOffset + codeBlock->m_numCalleeRegisters; + if (!m_registerFile.grow(newEnd)) { + if (pushedScope) + scopeChain->pop(); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + CallFrame* newCallFrame = CallFrame::create(m_registerFile.begin() + globalRegisterOffset); + + ASSERT(codeBlock->m_numParameters == 1); // 1 parameter for 'this'. + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), codeBlock->m_numParameters, 0); + newCallFrame->setThisValue(thisValue); + + TopCallFrameSetter topCallFrame(callFrame->globalData(), newCallFrame); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); + + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + + m_reentryDepth++; + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + if (callFrame->globalData().canUseJIT()) +#endif + result = eval->generatedJITCode().execute(&m_registerFile, newCallFrame, scopeChain->globalData); +#if ENABLE(INTERPRETER) + else +#endif +#endif +#if ENABLE(INTERPRETER) + result = privateExecute(Normal, &m_registerFile, newCallFrame); +#endif + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); + + m_registerFile.shrink(oldEnd); + if (pushedScope) + scopeChain->pop(); + return checkedReturn(result); +} + +NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID, int firstLine, int lastLine) +{ + Debugger* debugger = callFrame->dynamicGlobalObject()->debugger(); + if (!debugger) + return; + + switch (debugHookID) { + case DidEnterCallFrame: + debugger->callEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); + return; + case WillLeaveCallFrame: + debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); + return; + case WillExecuteStatement: + debugger->atStatement(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); + return; + case WillExecuteProgram: + debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine); + return; + case DidExecuteProgram: + debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); + return; + case DidReachBreakpoint: + debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine); + return; + } +} + +#if ENABLE(INTERPRETER) +NEVER_INLINE ScopeChainNode* Interpreter::createExceptionScope(CallFrame* callFrame, const Instruction* vPC) +{ + int dst = vPC[1].u.operand; + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& property = codeBlock->identifier(vPC[2].u.operand); + JSValue value = callFrame->r(vPC[3].u.operand).jsValue(); + JSObject* scope = JSStaticScopeObject::create(callFrame, property, value, DontDelete); + callFrame->uncheckedR(dst) = JSValue(scope); + + return callFrame->scopeChain()->push(scope); +} + +NEVER_INLINE void Interpreter::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValue baseValue, const PutPropertySlot& slot) +{ + // Recursive invocation may already have specialized this instruction. + if (vPC[0].u.opcode != getOpcode(op_put_by_id)) + return; + + if (!baseValue.isCell()) + return; + + // Uncacheable: give up. + if (!slot.isCacheable()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + JSCell* baseCell = baseValue.asCell(); + Structure* structure = baseCell->structure(); + + if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + // Cache miss: record Structure to compare against next time. + Structure* lastStructure = vPC[4].u.structure.get(); + if (structure != lastStructure) { + // First miss: record Structure to compare against next time. + if (!lastStructure) { + vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); + return; + } + + // Second miss: give up. + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + // Cache hit: Specialize instruction and ref Structures. + + // If baseCell != slot.base(), then baseCell must be a proxy for another object. + if (baseCell != slot.base()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + // Structure transition, cache transition info + if (slot.type() == PutPropertySlot::NewProperty) { + if (structure->isDictionary()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + // put_by_id_transition checks the prototype chain for setters. + normalizePrototypeChain(callFrame, baseCell); + JSCell* owner = codeBlock->ownerExecutable(); + JSGlobalData& globalData = callFrame->globalData(); + // Get the prototype here because the call to prototypeChain could cause a + // GC allocation, which we don't want to happen while we're in the middle of + // initializing the union. + StructureChain* prototypeChain = structure->prototypeChain(callFrame); + vPC[0] = getOpcode(op_put_by_id_transition); + vPC[4].u.structure.set(globalData, owner, structure->previousID()); + vPC[5].u.structure.set(globalData, owner, structure); + vPC[6].u.structureChain.set(callFrame->globalData(), codeBlock->ownerExecutable(), prototypeChain); + ASSERT(vPC[6].u.structureChain); + vPC[7] = slot.cachedOffset(); + return; + } + + vPC[0] = getOpcode(op_put_by_id_replace); + vPC[5] = slot.cachedOffset(); +} + +NEVER_INLINE void Interpreter::uncachePutByID(CodeBlock*, Instruction* vPC) +{ + vPC[0] = getOpcode(op_put_by_id); + vPC[4] = 0; +} + +NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot) +{ + // Recursive invocation may already have specialized this instruction. + if (vPC[0].u.opcode != getOpcode(op_get_by_id)) + return; + + // FIXME: Cache property access for immediates. + if (!baseValue.isCell()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + if (isJSArray(baseValue) && propertyName == callFrame->propertyNames().length) { + vPC[0] = getOpcode(op_get_array_length); + return; + } + + if (isJSString(baseValue) && propertyName == callFrame->propertyNames().length) { + vPC[0] = getOpcode(op_get_string_length); + return; + } + + // Uncacheable: give up. + if (!slot.isCacheable()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + Structure* structure = baseValue.asCell()->structure(); + + if (structure->isUncacheableDictionary() || structure->typeInfo().prohibitsPropertyCaching()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + // Cache miss + Structure* lastStructure = vPC[4].u.structure.get(); + if (structure != lastStructure) { + // First miss: record Structure to compare against next time. + if (!lastStructure) { + vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); + return; + } + + // Second miss: give up. + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + // Cache hit: Specialize instruction and ref Structures. + + if (slot.slotBase() == baseValue) { + switch (slot.cachedPropertyType()) { + case PropertySlot::Getter: + vPC[0] = getOpcode(op_get_by_id_getter_self); + vPC[5] = slot.cachedOffset(); + break; + case PropertySlot::Custom: + vPC[0] = getOpcode(op_get_by_id_custom_self); + vPC[5] = slot.customGetter(); + break; + default: + vPC[0] = getOpcode(op_get_by_id_self); + vPC[5] = slot.cachedOffset(); + break; + } + return; + } + + if (structure->isDictionary()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { + ASSERT(slot.slotBase().isObject()); + + JSObject* baseObject = asObject(slot.slotBase()); + size_t offset = slot.cachedOffset(); + + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (baseObject->structure()->isDictionary()) { + baseObject->flattenDictionaryObject(callFrame->globalData()); + offset = baseObject->structure()->get(callFrame->globalData(), propertyName); + } + + ASSERT(!baseObject->structure()->isUncacheableDictionary()); + + switch (slot.cachedPropertyType()) { + case PropertySlot::Getter: + vPC[0] = getOpcode(op_get_by_id_getter_proto); + vPC[6] = offset; + break; + case PropertySlot::Custom: + vPC[0] = getOpcode(op_get_by_id_custom_proto); + vPC[6] = slot.customGetter(); + break; + default: + vPC[0] = getOpcode(op_get_by_id_proto); + vPC[6] = offset; + break; + } + vPC[5].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), baseObject->structure()); + return; + } + + size_t offset = slot.cachedOffset(); + size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase(), propertyName, offset); + if (!count) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + + switch (slot.cachedPropertyType()) { + case PropertySlot::Getter: + vPC[0] = getOpcode(op_get_by_id_getter_chain); + vPC[7] = offset; + break; + case PropertySlot::Custom: + vPC[0] = getOpcode(op_get_by_id_custom_chain); + vPC[7] = slot.customGetter(); + break; + default: + vPC[0] = getOpcode(op_get_by_id_chain); + vPC[7] = offset; + break; + } + vPC[4].u.structure.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure); + vPC[5].u.structureChain.set(callFrame->globalData(), codeBlock->ownerExecutable(), structure->prototypeChain(callFrame)); + vPC[6] = count; +} + +NEVER_INLINE void Interpreter::uncacheGetByID(CodeBlock*, Instruction* vPC) +{ + vPC[0] = getOpcode(op_get_by_id); + vPC[4] = 0; +} + +#endif // ENABLE(INTERPRETER) + +JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFile, CallFrame* callFrame) +{ + // One-time initialization of our address tables. We have to put this code + // here because our labels are only in scope inside this function. + if (UNLIKELY(flag == InitializeAndReturn)) { + #if ENABLE(COMPUTED_GOTO_INTERPRETER) + #define LIST_OPCODE_LABEL(id, length) &&id, + static Opcode labels[] = { FOR_EACH_OPCODE_ID(LIST_OPCODE_LABEL) }; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(labels); ++i) + m_opcodeTable[i] = labels[i]; + #undef LIST_OPCODE_LABEL + #endif // ENABLE(COMPUTED_GOTO_INTERPRETER) + return JSValue(); + } + + ASSERT(m_initialized); + ASSERT(m_enabled); + +#if ENABLE(JIT) +#if ENABLE(INTERPRETER) + // Mixing Interpreter + JIT is not supported. + if (callFrame->globalData().canUseJIT()) +#endif + ASSERT_NOT_REACHED(); +#endif + +#if !ENABLE(INTERPRETER) + UNUSED_PARAM(registerFile); + UNUSED_PARAM(callFrame); + return JSValue(); +#else + + ASSERT(callFrame->globalData().topCallFrame == callFrame); + + JSGlobalData* globalData = &callFrame->globalData(); + JSValue exceptionValue; + HandlerInfo* handler = 0; + CallFrame** topCallFrameSlot = &globalData->topCallFrame; + + CodeBlock* codeBlock = callFrame->codeBlock(); + Instruction* vPC = codeBlock->instructions().begin(); + Profiler** enabledProfilerReference = Profiler::enabledProfilerReference(); + unsigned tickCount = globalData->timeoutChecker.ticksUntilNextCheck(); + JSValue functionReturnValue; + +#define CHECK_FOR_EXCEPTION() \ + do { \ + if (UNLIKELY(globalData->exception != JSValue())) { \ + exceptionValue = globalData->exception; \ + goto vm_throw; \ + } \ + } while (0) + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + +#define CHECK_FOR_TIMEOUT() \ + if (!--tickCount) { \ + if (globalData->terminator.shouldTerminate() || globalData->timeoutChecker.didTimeOut(callFrame)) { \ + exceptionValue = jsNull(); \ + goto vm_throw; \ + } \ + tickCount = globalData->timeoutChecker.ticksUntilNextCheck(); \ + } + +#if ENABLE(OPCODE_SAMPLING) + #define SAMPLE(codeBlock, vPC) m_sampler->sample(codeBlock, vPC) +#else + #define SAMPLE(codeBlock, vPC) +#endif + +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto *vPC->u.opcode +#if ENABLE(OPCODE_STATS) + #define DEFINE_OPCODE(opcode) opcode: OpcodeStats::recordInstruction(opcode); +#else + #define DEFINE_OPCODE(opcode) opcode: +#endif + NEXT_INSTRUCTION(); +#else + #define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto interpreterLoopStart +#if ENABLE(OPCODE_STATS) + #define DEFINE_OPCODE(opcode) case opcode: OpcodeStats::recordInstruction(opcode); +#else + #define DEFINE_OPCODE(opcode) case opcode: +#endif + while (1) { // iterator loop begins + interpreterLoopStart:; + switch (vPC->u.opcode) +#endif + { + DEFINE_OPCODE(op_new_object) { + /* new_object dst(r) + + Constructs a new empty Object instance using the original + constructor, and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + callFrame->uncheckedR(dst) = JSValue(constructEmptyObject(callFrame)); + + vPC += OPCODE_LENGTH(op_new_object); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_array) { + /* new_array dst(r) firstArg(r) argCount(n) + + Constructs a new Array instance using the original + constructor, and puts the result in register dst. + The array will contain argCount elements with values + taken from registers starting at register firstArg. + */ + int dst = vPC[1].u.operand; + int firstArg = vPC[2].u.operand; + int argCount = vPC[3].u.operand; + callFrame->uncheckedR(dst) = JSValue(constructArray(callFrame, reinterpret_cast<JSValue*>(&callFrame->registers()[firstArg]), argCount)); + + vPC += OPCODE_LENGTH(op_new_array); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_array_buffer) { + /* new_array_buffer dst(r) index(n) argCount(n) + + Constructs a new Array instance using the original + constructor, and puts the result in register dst. + The array be initialized with the values from constantBuffer[index] + */ + int dst = vPC[1].u.operand; + int firstArg = vPC[2].u.operand; + int argCount = vPC[3].u.operand; + callFrame->uncheckedR(dst) = JSValue(constructArray(callFrame, codeBlock->constantBuffer(firstArg), argCount)); + + vPC += OPCODE_LENGTH(op_new_array); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_regexp) { + /* new_regexp dst(r) regExp(re) + + Constructs a new RegExp instance using the original + constructor from regexp regExp, and puts the result in + register dst. + */ + int dst = vPC[1].u.operand; + RegExp* regExp = codeBlock->regexp(vPC[2].u.operand); + if (!regExp->isValid()) { + exceptionValue = createSyntaxError(callFrame, "Invalid flags supplied to RegExp constructor."); + goto vm_throw; + } + callFrame->uncheckedR(dst) = JSValue(RegExpObject::create(*globalData, callFrame->lexicalGlobalObject(), callFrame->scopeChain()->globalObject->regExpStructure(), regExp)); + + vPC += OPCODE_LENGTH(op_new_regexp); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_mov) { + /* mov dst(r) src(r) + + Copies register src to register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + + callFrame->uncheckedR(dst) = callFrame->r(src); + + vPC += OPCODE_LENGTH(op_mov); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_eq) { + /* eq dst(r) src1(r) src2(r) + + Checks whether register src1 and register src2 are equal, + as with the ECMAScript '==' operator, and puts the result + as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32()) + callFrame->uncheckedR(dst) = jsBoolean(src1.asInt32() == src2.asInt32()); + else { + JSValue result = jsBoolean(JSValue::equalSlowCase(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_eq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_eq_null) { + /* eq_null dst(r) src(r) + + Checks whether register src is null, as with the ECMAScript '!=' + operator, and puts the result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); + + if (src.isUndefinedOrNull()) { + callFrame->uncheckedR(dst) = jsBoolean(true); + vPC += OPCODE_LENGTH(op_eq_null); + NEXT_INSTRUCTION(); + } + + callFrame->uncheckedR(dst) = jsBoolean(src.isCell() && src.asCell()->structure()->typeInfo().masqueradesAsUndefined()); + vPC += OPCODE_LENGTH(op_eq_null); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_neq) { + /* neq dst(r) src1(r) src2(r) + + Checks whether register src1 and register src2 are not + equal, as with the ECMAScript '!=' operator, and puts the + result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32()) + callFrame->uncheckedR(dst) = jsBoolean(src1.asInt32() != src2.asInt32()); + else { + JSValue result = jsBoolean(!JSValue::equalSlowCase(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_neq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_neq_null) { + /* neq_null dst(r) src(r) + + Checks whether register src is not null, as with the ECMAScript '!=' + operator, and puts the result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); + + if (src.isUndefinedOrNull()) { + callFrame->uncheckedR(dst) = jsBoolean(false); + vPC += OPCODE_LENGTH(op_neq_null); + NEXT_INSTRUCTION(); + } + + callFrame->uncheckedR(dst) = jsBoolean(!src.isCell() || !src.asCell()->structure()->typeInfo().masqueradesAsUndefined()); + vPC += OPCODE_LENGTH(op_neq_null); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_stricteq) { + /* stricteq dst(r) src1(r) src2(r) + + Checks whether register src1 and register src2 are strictly + equal, as with the ECMAScript '===' operator, and puts the + result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + bool result = JSValue::strictEqual(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(result); + + vPC += OPCODE_LENGTH(op_stricteq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_nstricteq) { + /* nstricteq dst(r) src1(r) src2(r) + + Checks whether register src1 and register src2 are not + strictly equal, as with the ECMAScript '!==' operator, and + puts the result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + bool result = !JSValue::strictEqual(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(result); + + vPC += OPCODE_LENGTH(op_nstricteq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_less) { + /* less dst(r) src1(r) src2(r) + + Checks whether register src1 is less than register src2, as + with the ECMAScript '<' operator, and puts the result as + a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + JSValue result = jsBoolean(jsLess<true>(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_less); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_lesseq) { + /* lesseq dst(r) src1(r) src2(r) + + Checks whether register src1 is less than or equal to + register src2, as with the ECMAScript '<=' operator, and + puts the result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + JSValue result = jsBoolean(jsLessEq<true>(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_lesseq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_greater) { + /* greater dst(r) src1(r) src2(r) + + Checks whether register src1 is greater than register src2, as + with the ECMAScript '>' operator, and puts the result as + a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + JSValue result = jsBoolean(jsLess<false>(callFrame, src2, src1)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_greater); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_greatereq) { + /* greatereq dst(r) src1(r) src2(r) + + Checks whether register src1 is greater than or equal to + register src2, as with the ECMAScript '>=' operator, and + puts the result as a boolean in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + JSValue result = jsBoolean(jsLessEq<false>(callFrame, src2, src1)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_greatereq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_pre_inc) { + /* pre_inc srcDst(r) + + Converts register srcDst to number, adds one, and puts the result + back in register srcDst. + */ + int srcDst = vPC[1].u.operand; + JSValue v = callFrame->r(srcDst).jsValue(); + if (v.isInt32() && v.asInt32() < INT_MAX) + callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() + 1); + else { + JSValue result = jsNumber(v.toNumber(callFrame) + 1); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = result; + } + + vPC += OPCODE_LENGTH(op_pre_inc); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_pre_dec) { + /* pre_dec srcDst(r) + + Converts register srcDst to number, subtracts one, and puts the result + back in register srcDst. + */ + int srcDst = vPC[1].u.operand; + JSValue v = callFrame->r(srcDst).jsValue(); + if (v.isInt32() && v.asInt32() > INT_MIN) + callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() - 1); + else { + JSValue result = jsNumber(v.toNumber(callFrame) - 1); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = result; + } + + vPC += OPCODE_LENGTH(op_pre_dec); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_post_inc) { + /* post_inc dst(r) srcDst(r) + + Converts register srcDst to number. The number itself is + written to register dst, and the number plus one is written + back to register srcDst. + */ + int dst = vPC[1].u.operand; + int srcDst = vPC[2].u.operand; + JSValue v = callFrame->r(srcDst).jsValue(); + if (v.isInt32() && v.asInt32() < INT_MAX) { + callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() + 1); + callFrame->uncheckedR(dst) = v; + } else { + double number = callFrame->r(srcDst).jsValue().toNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = jsNumber(number + 1); + callFrame->uncheckedR(dst) = jsNumber(number); + } + + vPC += OPCODE_LENGTH(op_post_inc); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_post_dec) { + /* post_dec dst(r) srcDst(r) + + Converts register srcDst to number. The number itself is + written to register dst, and the number minus one is written + back to register srcDst. + */ + int dst = vPC[1].u.operand; + int srcDst = vPC[2].u.operand; + JSValue v = callFrame->r(srcDst).jsValue(); + if (v.isInt32() && v.asInt32() > INT_MIN) { + callFrame->uncheckedR(srcDst) = jsNumber(v.asInt32() - 1); + callFrame->uncheckedR(dst) = v; + } else { + double number = callFrame->r(srcDst).jsValue().toNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = jsNumber(number - 1); + callFrame->uncheckedR(dst) = jsNumber(number); + } + + vPC += OPCODE_LENGTH(op_post_dec); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_to_jsnumber) { + /* to_jsnumber dst(r) src(r) + + Converts register src to number, and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + + JSValue srcVal = callFrame->r(src).jsValue(); + + if (LIKELY(srcVal.isNumber())) + callFrame->uncheckedR(dst) = callFrame->r(src); + else { + double number = srcVal.toNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsNumber(number); + } + + vPC += OPCODE_LENGTH(op_to_jsnumber); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_negate) { + /* negate dst(r) src(r) + + Converts register src to number, negates it, and puts the + result in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); + if (src.isInt32() && (src.asInt32() & 0x7fffffff)) // non-zero and no overflow + callFrame->uncheckedR(dst) = jsNumber(-src.asInt32()); + else { + JSValue result = jsNumber(-src.toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_negate); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_add) { + /* add dst(r) src1(r) src2(r) + + Adds register src1 and register src2, and puts the result + in register dst. (JS add may be string concatenation or + numeric add, depending on the types of the operands.) + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() + src2.asInt32()); + else { + JSValue result = jsAdd(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + vPC += OPCODE_LENGTH(op_add); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_mul) { + /* mul dst(r) src1(r) src2(r) + + Multiplies register src1 and register src2 (converted to + numbers), and puts the product in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | src2.asInt32() >> 15)) // no overflow + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() * src2.asInt32()); + else { + JSValue result = jsNumber(src1.toNumber(callFrame) * src2.toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_mul); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_div) { + /* div dst(r) dividend(r) divisor(r) + + Divides register dividend (converted to number) by the + register divisor (converted to number), and puts the + quotient in register dst. + */ + int dst = vPC[1].u.operand; + JSValue dividend = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue divisor = callFrame->r(vPC[3].u.operand).jsValue(); + + JSValue result = jsNumber(dividend.toNumber(callFrame) / divisor.toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_div); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_mod) { + /* mod dst(r) dividend(r) divisor(r) + + Divides register dividend (converted to number) by + register divisor (converted to number), and puts the + remainder in register dst. + */ + int dst = vPC[1].u.operand; + JSValue dividend = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue divisor = callFrame->r(vPC[3].u.operand).jsValue(); + + if (dividend.isInt32() && divisor.isInt32() && divisor.asInt32() != 0) { + JSValue result = jsNumber(dividend.asInt32() % divisor.asInt32()); + ASSERT(result); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_mod); + NEXT_INSTRUCTION(); + } + + // Conversion to double must happen outside the call to fmod since the + // order of argument evaluation is not guaranteed. + double d1 = dividend.toNumber(callFrame); + double d2 = divisor.toNumber(callFrame); + JSValue result = jsNumber(fmod(d1, d2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_mod); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_sub) { + /* sub dst(r) src1(r) src2(r) + + Subtracts register src2 (converted to number) from register + src1 (converted to number), and puts the difference in + register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() - src2.asInt32()); + else { + JSValue result = jsNumber(src1.toNumber(callFrame) - src2.toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + vPC += OPCODE_LENGTH(op_sub); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_lshift) { + /* lshift dst(r) val(r) shift(r) + + Performs left shift of register val (converted to int32) by + register shift (converted to uint32), and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); + + if (val.isInt32() && shift.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(val.asInt32() << (shift.asInt32() & 0x1f)); + else { + JSValue result = jsNumber((val.toInt32(callFrame)) << (shift.toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_lshift); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_rshift) { + /* rshift dst(r) val(r) shift(r) + + Performs arithmetic right shift of register val (converted + to int32) by register shift (converted to + uint32), and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); + + if (val.isInt32() && shift.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(val.asInt32() >> (shift.asInt32() & 0x1f)); + else { + JSValue result = jsNumber((val.toInt32(callFrame)) >> (shift.toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_rshift); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_urshift) { + /* rshift dst(r) val(r) shift(r) + + Performs logical right shift of register val (converted + to uint32) by register shift (converted to + uint32), and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + JSValue val = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue shift = callFrame->r(vPC[3].u.operand).jsValue(); + if (val.isUInt32() && shift.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(val.asInt32() >> (shift.asInt32() & 0x1f)); + else { + JSValue result = jsNumber((val.toUInt32(callFrame)) >> (shift.toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_urshift); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_bitand) { + /* bitand dst(r) src1(r) src2(r) + + Computes bitwise AND of register src1 (converted to int32) + and register src2 (converted to int32), and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() & src2.asInt32()); + else { + JSValue result = jsNumber(src1.toInt32(callFrame) & src2.toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_bitand); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_bitxor) { + /* bitxor dst(r) src1(r) src2(r) + + Computes bitwise XOR of register src1 (converted to int32) + and register src2 (converted to int32), and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() ^ src2.asInt32()); + else { + JSValue result = jsNumber(src1.toInt32(callFrame) ^ src2.toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_bitxor); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_bitor) { + /* bitor dst(r) src1(r) src2(r) + + Computes bitwise OR of register src1 (converted to int32) + and register src2 (converted to int32), and puts the + result in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); + if (src1.isInt32() && src2.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(src1.asInt32() | src2.asInt32()); + else { + JSValue result = jsNumber(src1.toInt32(callFrame) | src2.toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + vPC += OPCODE_LENGTH(op_bitor); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_bitnot) { + /* bitnot dst(r) src(r) + + Computes bitwise NOT of register src1 (converted to int32), + and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + JSValue src = callFrame->r(vPC[2].u.operand).jsValue(); + if (src.isInt32()) + callFrame->uncheckedR(dst) = jsNumber(~src.asInt32()); + else { + JSValue result = jsNumber(~src.toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + vPC += OPCODE_LENGTH(op_bitnot); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_not) { + /* not dst(r) src(r) + + Computes logical NOT of register src (converted to + boolean), and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + JSValue result = jsBoolean(!callFrame->r(src).jsValue().toBoolean(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_not); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_check_has_instance) { + /* check_has_instance constructor(r) + + Check 'constructor' is an object with the internal property + [HasInstance] (i.e. is a function ... *shakes head sadly at + JSC API*). Raises an exception if register constructor is not + an valid parameter for instanceof. + */ + int base = vPC[1].u.operand; + JSValue baseVal = callFrame->r(base).jsValue(); + + if (isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue)) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_check_has_instance); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_instanceof) { + /* instanceof dst(r) value(r) constructor(r) constructorProto(r) + + Tests whether register value is an instance of register + constructor, and puts the boolean result in register + dst. Register constructorProto must contain the "prototype" + property (not the actual prototype) of the object in + register constructor. This lookup is separated so that + polymorphic inline caching can apply. + + Raises an exception if register constructor is not an + object. + */ + int dst = vPC[1].u.operand; + int value = vPC[2].u.operand; + int base = vPC[3].u.operand; + int baseProto = vPC[4].u.operand; + + JSValue baseVal = callFrame->r(base).jsValue(); + + ASSERT(!isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue)); + + bool result = asObject(baseVal)->methodTable()->hasInstance(asObject(baseVal), callFrame, callFrame->r(value).jsValue(), callFrame->r(baseProto).jsValue()); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(result); + + vPC += OPCODE_LENGTH(op_instanceof); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_typeof) { + /* typeof dst(r) src(r) + + Determines the type string for src according to ECMAScript + rules, and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = JSValue(jsTypeStringForValue(callFrame, callFrame->r(src).jsValue())); + + vPC += OPCODE_LENGTH(op_typeof); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_undefined) { + /* is_undefined dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "undefined", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + JSValue v = callFrame->r(src).jsValue(); + callFrame->uncheckedR(dst) = jsBoolean(v.isCell() ? v.asCell()->structure()->typeInfo().masqueradesAsUndefined() : v.isUndefined()); + + vPC += OPCODE_LENGTH(op_is_undefined); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_boolean) { + /* is_boolean dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "boolean", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isBoolean()); + + vPC += OPCODE_LENGTH(op_is_boolean); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_number) { + /* is_number dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "number", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isNumber()); + + vPC += OPCODE_LENGTH(op_is_number); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_string) { + /* is_string dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "string", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = jsBoolean(callFrame->r(src).jsValue().isString()); + + vPC += OPCODE_LENGTH(op_is_string); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_object) { + /* is_object dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "object", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = jsBoolean(jsIsObjectType(callFrame->r(src).jsValue())); + + vPC += OPCODE_LENGTH(op_is_object); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_is_function) { + /* is_function dst(r) src(r) + + Determines whether the type string for src according to + the ECMAScript rules is "function", and puts the result + in register dst. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + callFrame->uncheckedR(dst) = jsBoolean(jsIsFunctionType(callFrame->r(src).jsValue())); + + vPC += OPCODE_LENGTH(op_is_function); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_in) { + /* in dst(r) property(r) base(r) + + Tests whether register base has a property named register + property, and puts the boolean result in register dst. + + Raises an exception if register constructor is not an + object. + */ + int dst = vPC[1].u.operand; + int property = vPC[2].u.operand; + int base = vPC[3].u.operand; + + JSValue baseVal = callFrame->r(base).jsValue(); + if (isInvalidParamForIn(callFrame, baseVal, exceptionValue)) + goto vm_throw; + + JSObject* baseObj = asObject(baseVal); + + JSValue propName = callFrame->r(property).jsValue(); + + uint32_t i; + if (propName.getUInt32(i)) + callFrame->uncheckedR(dst) = jsBoolean(baseObj->hasProperty(callFrame, i)); + else { + Identifier property(callFrame, propName.toString(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(baseObj->hasProperty(callFrame, property)); + } + + vPC += OPCODE_LENGTH(op_in); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve) { + /* resolve dst(r) property(id) + + Looks up the property named by identifier property in the + scope chain, and writes the resulting value to register + dst. If the property is not found, raises an exception. + */ + if (UNLIKELY(!resolve(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_skip) { + /* resolve_skip dst(r) property(id) skip(n) + + Looks up the property named by identifier property in the + scope chain skipping the top 'skip' levels, and writes the resulting + value to register dst. If the property is not found, raises an exception. + */ + if (UNLIKELY(!resolveSkip(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve_skip); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_global) { + /* resolve_skip dst(r) globalObject(c) property(id) structure(sID) offset(n) + + Performs a dynamic property lookup for the given property, on the provided + global object. If structure matches the Structure of the global then perform + a fast lookup using the case offset, otherwise fall back to a full resolve and + cache the new structure and offset + */ + if (UNLIKELY(!resolveGlobal(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve_global); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_global_dynamic) { + /* resolve_skip dst(r) globalObject(c) property(id) structure(sID) offset(n), depth(n) + + Performs a dynamic property lookup for the given property, on the provided + global object. If structure matches the Structure of the global then perform + a fast lookup using the case offset, otherwise fall back to a full resolve and + cache the new structure and offset. + + This walks through n levels of the scope chain to verify that none of those levels + in the scope chain include dynamically added properties. + */ + if (UNLIKELY(!resolveGlobalDynamic(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve_global_dynamic); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_global_var) { + /* get_global_var dst(r) globalObject(c) index(n) + + Gets the global var at global slot index and places it in register dst. + */ + int dst = vPC[1].u.operand; + JSGlobalObject* scope = codeBlock->globalObject(); + ASSERT(scope->isGlobalObject()); + int index = vPC[2].u.operand; + + callFrame->uncheckedR(dst) = scope->registerAt(index).get(); + vPC += OPCODE_LENGTH(op_get_global_var); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_global_var) { + /* put_global_var globalObject(c) index(n) value(r) + + Puts value into global slot index. + */ + JSGlobalObject* scope = codeBlock->globalObject(); + ASSERT(scope->isGlobalObject()); + int index = vPC[1].u.operand; + int value = vPC[2].u.operand; + + scope->registerAt(index).set(*globalData, scope, callFrame->r(value).jsValue()); + vPC += OPCODE_LENGTH(op_put_global_var); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_scoped_var) { + /* get_scoped_var dst(r) index(n) skip(n) + + Loads the contents of the index-th local from the scope skip nodes from + the top of the scope chain, and places it in register dst. + */ + int dst = vPC[1].u.operand; + int index = vPC[2].u.operand; + int skip = vPC[3].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT_UNUSED(end, iter != end); + ASSERT(codeBlock == callFrame->codeBlock()); + bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + if (callFrame->r(codeBlock->activationRegister()).jsValue()) + ++iter; + } + while (skip--) { + ++iter; + ASSERT_UNUSED(end, iter != end); + } + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast<JSVariableObject*>(iter->get()); + callFrame->uncheckedR(dst) = scope->registerAt(index).get(); + ASSERT(callFrame->r(dst).jsValue()); + vPC += OPCODE_LENGTH(op_get_scoped_var); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_scoped_var) { + /* put_scoped_var index(n) skip(n) value(r) + + */ + int index = vPC[1].u.operand; + int skip = vPC[2].u.operand; + int value = vPC[3].u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(codeBlock == callFrame->codeBlock()); + ASSERT_UNUSED(end, iter != end); + bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain(); + ASSERT(skip || !checkTopLevel); + if (checkTopLevel && skip--) { + if (callFrame->r(codeBlock->activationRegister()).jsValue()) + ++iter; + } + while (skip--) { + ++iter; + ASSERT_UNUSED(end, iter != end); + } + + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast<JSVariableObject*>(iter->get()); + ASSERT(callFrame->r(value).jsValue()); + scope->registerAt(index).set(*globalData, scope, callFrame->r(value).jsValue()); + vPC += OPCODE_LENGTH(op_put_scoped_var); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_base) { + /* resolve_base dst(r) property(id) isStrict(bool) + + Searches the scope chain for an object containing + identifier property, and if one is found, writes it to + register dst. If none is found and isStrict is false, the + outermost scope (which will be the global object) is + stored in register dst. + */ + resolveBase(callFrame, vPC); + CHECK_FOR_EXCEPTION(); + + vPC += OPCODE_LENGTH(op_resolve_base); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_ensure_property_exists) { + /* ensure_property_exists base(r) property(id) + + Throws an exception if property does not exist on base + */ + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + Identifier& ident = codeBlock->identifier(property); + + JSValue baseVal = callFrame->r(base).jsValue(); + JSObject* baseObject = asObject(baseVal); + PropertySlot slot(baseVal); + if (!baseObject->getPropertySlot(callFrame, ident, slot)) { + exceptionValue = createErrorForInvalidGlobalAssignment(callFrame, ident.ustring()); + goto vm_throw; + } + + vPC += OPCODE_LENGTH(op_ensure_property_exists); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_with_base) { + /* resolve_with_base baseDst(r) propDst(r) property(id) + + Searches the scope chain for an object containing + identifier property, and if one is found, writes it to + register srcDst, and the retrieved property value to register + propDst. If the property is not found, raises an exception. + + This is more efficient than doing resolve_base followed by + resolve, or resolve_base followed by get_by_id, as it + avoids duplicate hash lookups. + */ + if (UNLIKELY(!resolveBaseAndProperty(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve_with_base); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_with_this) { + /* resolve_with_this thisDst(r) propDst(r) property(id) + + Searches the scope chain for an object containing + identifier property, and if one is found, writes the + retrieved property value to register propDst, and the + this object to pass in a call to thisDst. + + If the property is not found, raises an exception. + */ + if (UNLIKELY(!resolveThisAndProperty(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += OPCODE_LENGTH(op_resolve_with_this); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id) { + /* get_by_id dst(r) base(r) property(id) structure(sID) nop(n) nop(n) nop(n) + + Generic property access: Gets the property named by identifier + property from the value base, and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + + Identifier& ident = codeBlock->identifier(property); + JSValue baseValue = callFrame->r(base).jsValue(); + PropertySlot slot(baseValue); + JSValue result = baseValue.get(callFrame, ident, slot); + CHECK_FOR_EXCEPTION(); + + tryCacheGetByID(callFrame, codeBlock, vPC, baseValue, ident, slot); + + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_id); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_self) { + /* op_get_by_id_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) + + Cached property access: Attempts to get a cached property from the + value base. If the cache misses, op_get_by_id_self reverts to + op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + int dst = vPC[1].u.operand; + int offset = vPC[5].u.operand; + + ASSERT(baseObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + callFrame->uncheckedR(dst) = JSValue(baseObject->getDirectOffset(offset)); + + vPC += OPCODE_LENGTH(op_get_by_id_self); + NEXT_INSTRUCTION(); + } + } + + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_proto) { + /* op_get_by_id_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) + + Cached property access: Attempts to get a cached property from the + value base's prototype. If the cache misses, op_get_by_id_proto + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure.get(); + + if (LIKELY(protoObject->structure() == prototypeStructure)) { + int dst = vPC[1].u.operand; + int offset = vPC[6].u.operand; + + ASSERT(protoObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); + ASSERT(baseValue.get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); + callFrame->uncheckedR(dst) = JSValue(protoObject->getDirectOffset(offset)); + + vPC += OPCODE_LENGTH(op_get_by_id_proto); + NEXT_INSTRUCTION(); + } + } + } + + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_id_getter_proto); +#endif + DEFINE_OPCODE(op_get_by_id_getter_proto) { + /* op_get_by_id_getter_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) + + Cached property access: Attempts to get a cached getter property from the + value base's prototype. If the cache misses, op_get_by_id_getter_proto + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure.get(); + + if (LIKELY(protoObject->structure() == prototypeStructure)) { + int dst = vPC[1].u.operand; + int offset = vPC[6].u.operand; + if (GetterSetter* getterSetter = asGetterSetter(protoObject->getDirectOffset(offset).asCell())) { + JSObject* getter = getterSetter->getter(); + CallData callData; + CallType callType = getter->methodTable()->getCallData(getter, callData); + JSValue result = call(callFrame, getter, callType, callData, asObject(baseCell), ArgList()); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } else + callFrame->uncheckedR(dst) = jsUndefined(); + vPC += OPCODE_LENGTH(op_get_by_id_getter_proto); + NEXT_INSTRUCTION(); + } + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_id_getter_proto: +#endif +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_id_custom_proto); +#endif + DEFINE_OPCODE(op_get_by_id_custom_proto) { + /* op_get_by_id_custom_proto dst(r) base(r) property(id) structure(sID) prototypeStructure(sID) offset(n) nop(n) + + Cached property access: Attempts to use a cached named property getter + from the value base's prototype. If the cache misses, op_get_by_id_custom_proto + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure.get(); + + if (LIKELY(protoObject->structure() == prototypeStructure)) { + int dst = vPC[1].u.operand; + int property = vPC[3].u.operand; + Identifier& ident = codeBlock->identifier(property); + + PropertySlot::GetValueFunc getter = vPC[6].u.getterFunc; + JSValue result = getter(callFrame, protoObject, ident); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_id_custom_proto); + NEXT_INSTRUCTION(); + } + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_id_custom_proto: +#endif +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_get_by_id_chain); +#endif + DEFINE_OPCODE(op_get_by_id_chain) { + /* op_get_by_id_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) + + Cached property access: Attempts to get a cached property from the + value base's prototype chain. If the cache misses, op_get_by_id_chain + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + WriteBarrier<Structure>* end = it + count; + + while (true) { + JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); + + if (UNLIKELY(baseObject->structure() != (*it).get())) + break; + + if (++it == end) { + int dst = vPC[1].u.operand; + int offset = vPC[7].u.operand; + + ASSERT(baseObject->get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + ASSERT(baseValue.get(callFrame, codeBlock->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + callFrame->uncheckedR(dst) = JSValue(baseObject->getDirectOffset(offset)); + + vPC += OPCODE_LENGTH(op_get_by_id_chain); + NEXT_INSTRUCTION(); + } + + // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. + baseCell = baseObject; + } + } + } + + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_get_by_id_chain: + goto *(&&skip_id_getter_self); +#endif + DEFINE_OPCODE(op_get_by_id_getter_self) { + /* op_get_by_id_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) + + Cached property access: Attempts to get a cached property from the + value base. If the cache misses, op_get_by_id_getter_self reverts to + op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + int dst = vPC[1].u.operand; + int offset = vPC[5].u.operand; + + if (GetterSetter* getterSetter = asGetterSetter(baseObject->getDirectOffset(offset).asCell())) { + JSObject* getter = getterSetter->getter(); + CallData callData; + CallType callType = getter->methodTable()->getCallData(getter, callData); + JSValue result = call(callFrame, getter, callType, callData, baseObject, ArgList()); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } else + callFrame->uncheckedR(dst) = jsUndefined(); + + vPC += OPCODE_LENGTH(op_get_by_id_getter_self); + NEXT_INSTRUCTION(); + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_id_getter_self: +#endif +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_id_custom_self); +#endif + DEFINE_OPCODE(op_get_by_id_custom_self) { + /* op_get_by_id_custom_self dst(r) base(r) property(id) structure(sID) offset(n) nop(n) nop(n) + + Cached property access: Attempts to use a cached named property getter + from the value base. If the cache misses, op_get_by_id_custom_self reverts to + op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(baseCell->isObject()); + int dst = vPC[1].u.operand; + int property = vPC[3].u.operand; + Identifier& ident = codeBlock->identifier(property); + + PropertySlot::GetValueFunc getter = vPC[5].u.getterFunc; + JSValue result = getter(callFrame, baseValue, ident); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_id_custom_self); + NEXT_INSTRUCTION(); + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) +skip_id_custom_self: +#endif + DEFINE_OPCODE(op_get_by_id_generic) { + /* op_get_by_id_generic dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) + + Generic property access: Gets the property named by identifier + property from the value base, and puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + + Identifier& ident = codeBlock->identifier(property); + JSValue baseValue = callFrame->r(base).jsValue(); + PropertySlot slot(baseValue); + JSValue result = baseValue.get(callFrame, ident, slot); + CHECK_FOR_EXCEPTION(); + + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_id_generic); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_id_getter_chain); +#endif + DEFINE_OPCODE(op_get_by_id_getter_chain) { + /* op_get_by_id_getter_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) + + Cached property access: Attempts to get a cached property from the + value base's prototype chain. If the cache misses, op_get_by_id_getter_chain + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + WriteBarrier<Structure>* end = it + count; + + while (true) { + JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); + + if (UNLIKELY(baseObject->structure() != (*it).get())) + break; + + if (++it == end) { + int dst = vPC[1].u.operand; + int offset = vPC[7].u.operand; + if (GetterSetter* getterSetter = asGetterSetter(baseObject->getDirectOffset(offset).asCell())) { + JSObject* getter = getterSetter->getter(); + CallData callData; + CallType callType = getter->methodTable()->getCallData(getter, callData); + JSValue result = call(callFrame, getter, callType, callData, baseValue, ArgList()); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } else + callFrame->uncheckedR(dst) = jsUndefined(); + vPC += OPCODE_LENGTH(op_get_by_id_getter_chain); + NEXT_INSTRUCTION(); + } + + // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. + baseCell = baseObject; + } + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_id_getter_chain: +#endif +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + goto *(&&skip_id_custom_chain); +#endif + DEFINE_OPCODE(op_get_by_id_custom_chain) { + /* op_get_by_id_custom_chain dst(r) base(r) property(id) structure(sID) structureChain(chain) count(n) offset(n) + + Cached property access: Attempts to use a cached named property getter on the + value base's prototype chain. If the cache misses, op_get_by_id_custom_chain + reverts to op_get_by_id. + */ + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + WriteBarrier<Structure>* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + WriteBarrier<Structure>* end = it + count; + + while (true) { + JSObject* baseObject = asObject(baseCell->structure()->prototypeForLookup(callFrame)); + + if (UNLIKELY(baseObject->structure() != (*it).get())) + break; + + if (++it == end) { + int dst = vPC[1].u.operand; + int property = vPC[3].u.operand; + Identifier& ident = codeBlock->identifier(property); + + PropertySlot::GetValueFunc getter = vPC[7].u.getterFunc; + JSValue result = getter(callFrame, baseObject, ident); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_id_custom_chain); + NEXT_INSTRUCTION(); + } + + // Update baseCell, so that next time around the loop we'll pick up the prototype's prototype. + baseCell = baseObject; + } + } + } + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_id_custom_chain: + goto *(&&skip_get_array_length); +#endif + DEFINE_OPCODE(op_get_array_length) { + /* op_get_array_length dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) + + Cached property access: Gets the length of the array in register base, + and puts the result in register dst. If register base does not hold + an array, op_get_array_length reverts to op_get_by_id. + */ + + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + if (LIKELY(isJSArray(baseValue))) { + int dst = vPC[1].u.operand; + callFrame->uncheckedR(dst) = jsNumber(asArray(baseValue)->length()); + vPC += OPCODE_LENGTH(op_get_array_length); + NEXT_INSTRUCTION(); + } + + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_get_array_length: + goto *(&&skip_get_string_length); +#endif + DEFINE_OPCODE(op_get_string_length) { + /* op_get_string_length dst(r) base(r) property(id) nop(sID) nop(n) nop(n) nop(n) + + Cached property access: Gets the length of the string in register base, + and puts the result in register dst. If register base does not hold + a string, op_get_string_length reverts to op_get_by_id. + */ + + int base = vPC[2].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + if (LIKELY(isJSString(baseValue))) { + int dst = vPC[1].u.operand; + callFrame->uncheckedR(dst) = jsNumber(asString(baseValue)->length()); + vPC += OPCODE_LENGTH(op_get_string_length); + NEXT_INSTRUCTION(); + } + + uncacheGetByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_get_string_length: + goto *(&&skip_put_by_id); +#endif + DEFINE_OPCODE(op_put_by_id) { + /* put_by_id base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n) direct(b) + + Generic property access: Sets the property named by identifier + property, belonging to register base, to register value. + + Unlike many opcodes, this one does not write any output to + the register file. + + The "direct" flag should only be set this put_by_id is to initialize + an object literal. + */ + + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + int value = vPC[3].u.operand; + int direct = vPC[8].u.operand; + + JSValue baseValue = callFrame->r(base).jsValue(); + Identifier& ident = codeBlock->identifier(property); + PutPropertySlot slot(codeBlock->isStrictMode()); + if (direct) + baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot); + else + baseValue.put(callFrame, ident, callFrame->r(value).jsValue(), slot); + CHECK_FOR_EXCEPTION(); + + tryCachePutByID(callFrame, codeBlock, vPC, baseValue, slot); + + vPC += OPCODE_LENGTH(op_put_by_id); + NEXT_INSTRUCTION(); + } +#if USE(GCC_COMPUTED_GOTO_WORKAROUND) + skip_put_by_id: +#endif + DEFINE_OPCODE(op_put_by_id_transition) { + /* op_put_by_id_transition base(r) property(id) value(r) oldStructure(sID) newStructure(sID) structureChain(chain) offset(n) direct(b) + + Cached property access: Attempts to set a new property with a cached transition + property named by identifier property, belonging to register base, + to register value. If the cache misses, op_put_by_id_transition + reverts to op_put_by_id_generic. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* oldStructure = vPC[4].u.structure.get(); + Structure* newStructure = vPC[5].u.structure.get(); + + if (LIKELY(baseCell->structure() == oldStructure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + int direct = vPC[8].u.operand; + + if (!direct) { + WriteBarrier<Structure>* it = vPC[6].u.structureChain->head(); + + JSValue proto = baseObject->structure()->prototypeForLookup(callFrame); + while (!proto.isNull()) { + if (UNLIKELY(asObject(proto)->structure() != (*it).get())) { + uncachePutByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } + ++it; + proto = asObject(proto)->structure()->prototypeForLookup(callFrame); + } + } + baseObject->transitionTo(*globalData, newStructure); + + int value = vPC[3].u.operand; + unsigned offset = vPC[7].u.operand; + ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(*globalData, codeBlock->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(callFrame->globalData(), offset, callFrame->r(value).jsValue()); + + vPC += OPCODE_LENGTH(op_put_by_id_transition); + NEXT_INSTRUCTION(); + } + } + + uncachePutByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_by_id_replace) { + /* op_put_by_id_replace base(r) property(id) value(r) structure(sID) offset(n) nop(n) nop(n) direct(b) + + Cached property access: Attempts to set a pre-existing, cached + property named by identifier property, belonging to register base, + to register value. If the cache misses, op_put_by_id_replace + reverts to op_put_by_id. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + JSValue baseValue = callFrame->r(base).jsValue(); + + if (LIKELY(baseValue.isCell())) { + JSCell* baseCell = baseValue.asCell(); + Structure* structure = vPC[4].u.structure.get(); + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + int value = vPC[3].u.operand; + unsigned offset = vPC[5].u.operand; + + ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(*globalData, codeBlock->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(callFrame->globalData(), offset, callFrame->r(value).jsValue()); + + vPC += OPCODE_LENGTH(op_put_by_id_replace); + NEXT_INSTRUCTION(); + } + } + + uncachePutByID(codeBlock, vPC); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_by_id_generic) { + /* op_put_by_id_generic base(r) property(id) value(r) nop(n) nop(n) nop(n) nop(n) direct(b) + + Generic property access: Sets the property named by identifier + property, belonging to register base, to register value. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + int value = vPC[3].u.operand; + int direct = vPC[8].u.operand; + + JSValue baseValue = callFrame->r(base).jsValue(); + Identifier& ident = codeBlock->identifier(property); + PutPropertySlot slot(codeBlock->isStrictMode()); + if (direct) + baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot); + else + baseValue.put(callFrame, ident, callFrame->r(value).jsValue(), slot); + CHECK_FOR_EXCEPTION(); + + vPC += OPCODE_LENGTH(op_put_by_id_generic); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_del_by_id) { + /* del_by_id dst(r) base(r) property(id) + + Converts register base to Object, deletes the property + named by identifier property from the object, and writes a + boolean indicating success (if true) or failure (if false) + to register dst. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + + JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame); + Identifier& ident = codeBlock->identifier(property); + bool result = baseObj->methodTable()->deleteProperty(baseObj, callFrame, ident); + if (!result && codeBlock->isStrictMode()) { + exceptionValue = createTypeError(callFrame, "Unable to delete property."); + goto vm_throw; + } + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(result); + vPC += OPCODE_LENGTH(op_del_by_id); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_pname) { + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + int expected = vPC[4].u.operand; + int iter = vPC[5].u.operand; + int i = vPC[6].u.operand; + + JSValue baseValue = callFrame->r(base).jsValue(); + JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator(); + JSValue subscript = callFrame->r(property).jsValue(); + JSValue expectedSubscript = callFrame->r(expected).jsValue(); + int index = callFrame->r(i).i() - 1; + JSValue result; + int offset = 0; + if (subscript == expectedSubscript && baseValue.isCell() && (baseValue.asCell()->structure() == it->cachedStructure()) && it->getOffset(index, offset)) { + callFrame->uncheckedR(dst) = JSValue(asObject(baseValue)->getDirectOffset(offset)); + vPC += OPCODE_LENGTH(op_get_by_pname); + NEXT_INSTRUCTION(); + } + { + Identifier propertyName(callFrame, subscript.toString(callFrame)); + result = baseValue.get(callFrame, propertyName); + } + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_pname); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_arguments_length) { + int dst = vPC[1].u.operand; + int argumentsRegister = vPC[2].u.operand; + int property = vPC[3].u.operand; + JSValue arguments = callFrame->r(argumentsRegister).jsValue(); + if (arguments) { + Identifier& ident = codeBlock->identifier(property); + PropertySlot slot(arguments); + JSValue result = arguments.get(callFrame, ident, slot); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } else + callFrame->uncheckedR(dst) = jsNumber(callFrame->argumentCount()); + + vPC += OPCODE_LENGTH(op_get_arguments_length); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_argument_by_val) { + int dst = vPC[1].u.operand; + int argumentsRegister = vPC[2].u.operand; + int property = vPC[3].u.operand; + JSValue arguments = callFrame->r(argumentsRegister).jsValue(); + JSValue subscript = callFrame->r(property).jsValue(); + if (!arguments && subscript.isUInt32() && subscript.asUInt32() < callFrame->argumentCount()) { + callFrame->uncheckedR(dst) = callFrame->argument(subscript.asUInt32()); + vPC += OPCODE_LENGTH(op_get_argument_by_val); + NEXT_INSTRUCTION(); + } + if (!arguments) { + Arguments* arguments = Arguments::create(*globalData, callFrame); + callFrame->uncheckedR(argumentsRegister) = JSValue(arguments); + callFrame->uncheckedR(unmodifiedArgumentsRegister(argumentsRegister)) = JSValue(arguments); + } + // fallthrough + } + DEFINE_OPCODE(op_get_by_val) { + /* get_by_val dst(r) base(r) property(r) + + Converts register base to Object, gets the property named + by register property from the object, and puts the result + in register dst. property is nominally converted to string + but numbers are treated more efficiently. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + + JSValue baseValue = callFrame->r(base).jsValue(); + JSValue subscript = callFrame->r(property).jsValue(); + + JSValue result; + + if (LIKELY(subscript.isUInt32())) { + uint32_t i = subscript.asUInt32(); + if (isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canGetIndex(i)) + result = jsArray->getIndex(i); + else + result = jsArray->JSArray::get(callFrame, i); + } else if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) + result = asString(baseValue)->getIndex(callFrame, i); + else if (isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) + result = asByteArray(baseValue)->getIndex(callFrame, i); + else + result = baseValue.get(callFrame, i); + } else { + Identifier property(callFrame, subscript.toString(callFrame)); + result = baseValue.get(callFrame, property); + } + + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + vPC += OPCODE_LENGTH(op_get_by_val); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_by_val) { + /* put_by_val base(r) property(r) value(r) + + Sets register value on register base as the property named + by register property. Base is converted to object + first. register property is nominally converted to string + but numbers are treated more efficiently. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + int value = vPC[3].u.operand; + + JSValue baseValue = callFrame->r(base).jsValue(); + JSValue subscript = callFrame->r(property).jsValue(); + + if (LIKELY(subscript.isUInt32())) { + uint32_t i = subscript.asUInt32(); + if (isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canSetIndex(i)) + jsArray->setIndex(*globalData, i, callFrame->r(value).jsValue()); + else + jsArray->JSArray::putByIndex(jsArray, callFrame, i, callFrame->r(value).jsValue()); + } else if (isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { + JSByteArray* jsByteArray = asByteArray(baseValue); + JSValue jsValue = callFrame->r(value).jsValue(); + if (jsValue.isInt32()) + jsByteArray->setIndex(i, jsValue.asInt32()); + else if (jsValue.isDouble()) + jsByteArray->setIndex(i, jsValue.asDouble()); + else + baseValue.put(callFrame, i, jsValue); + } else + baseValue.put(callFrame, i, callFrame->r(value).jsValue()); + } else { + Identifier property(callFrame, subscript.toString(callFrame)); + if (!globalData->exception) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot(codeBlock->isStrictMode()); + baseValue.put(callFrame, property, callFrame->r(value).jsValue(), slot); + } + } + + CHECK_FOR_EXCEPTION(); + vPC += OPCODE_LENGTH(op_put_by_val); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_del_by_val) { + /* del_by_val dst(r) base(r) property(r) + + Converts register base to Object, deletes the property + named by register property from the object, and writes a + boolean indicating success (if true) or failure (if false) + to register dst. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int property = vPC[3].u.operand; + + JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame); // may throw + + JSValue subscript = callFrame->r(property).jsValue(); + bool result; + uint32_t i; + if (subscript.getUInt32(i)) + result = baseObj->methodTable()->deletePropertyByIndex(baseObj, callFrame, i); + else { + CHECK_FOR_EXCEPTION(); + Identifier property(callFrame, subscript.toString(callFrame)); + CHECK_FOR_EXCEPTION(); + result = baseObj->methodTable()->deleteProperty(baseObj, callFrame, property); + } + if (!result && codeBlock->isStrictMode()) { + exceptionValue = createTypeError(callFrame, "Unable to delete property."); + goto vm_throw; + } + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = jsBoolean(result); + vPC += OPCODE_LENGTH(op_del_by_val); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_by_index) { + /* put_by_index base(r) property(n) value(r) + + Sets register value on register base as the property named + by the immediate number property. Base is converted to + object first. + + Unlike many opcodes, this one does not write any output to + the register file. + + This opcode is mainly used to initialize array literals. + */ + int base = vPC[1].u.operand; + unsigned property = vPC[2].u.operand; + int value = vPC[3].u.operand; + + callFrame->r(base).jsValue().put(callFrame, property, callFrame->r(value).jsValue()); + + vPC += OPCODE_LENGTH(op_put_by_index); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop) { + /* loop target(offset) + + Jumps unconditionally to offset target from the current + instruction. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + int target = vPC[1].u.operand; + CHECK_FOR_TIMEOUT(); + vPC += target; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jmp) { + /* jmp target(offset) + + Jumps unconditionally to offset target from the current + instruction. + */ +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + int target = vPC[1].u.operand; + + vPC += target; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_hint) { + // This is a no-op unless we intend on doing OSR from the interpreter. + vPC += OPCODE_LENGTH(op_loop_hint); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_true) { + /* loop_if_true cond(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register cond converts to boolean as true. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + int cond = vPC[1].u.operand; + int target = vPC[2].u.operand; + if (callFrame->r(cond).jsValue().toBoolean(callFrame)) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_true); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_false) { + /* loop_if_true cond(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register cond converts to boolean as false. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + int cond = vPC[1].u.operand; + int target = vPC[2].u.operand; + if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_true); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jtrue) { + /* jtrue cond(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register cond converts to boolean as true. + */ + int cond = vPC[1].u.operand; + int target = vPC[2].u.operand; + if (callFrame->r(cond).jsValue().toBoolean(callFrame)) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jtrue); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jfalse) { + /* jfalse cond(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register cond converts to boolean as false. + */ + int cond = vPC[1].u.operand; + int target = vPC[2].u.operand; + if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jfalse); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jeq_null) { + /* jeq_null src(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register src is null. + */ + int src = vPC[1].u.operand; + int target = vPC[2].u.operand; + JSValue srcValue = callFrame->r(src).jsValue(); + + if (srcValue.isUndefinedOrNull() || (srcValue.isCell() && srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jeq_null); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jneq_null) { + /* jneq_null src(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register src is not null. + */ + int src = vPC[1].u.operand; + int target = vPC[2].u.operand; + JSValue srcValue = callFrame->r(src).jsValue(); + + if (!srcValue.isUndefinedOrNull() && (!srcValue.isCell() || !srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jneq_null); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jneq_ptr) { + /* jneq_ptr src(r) ptr(jsCell) target(offset) + + Jumps to offset target from the current instruction, if the value r is equal + to ptr, using pointer equality. + */ + int src = vPC[1].u.operand; + int target = vPC[3].u.operand; + JSValue srcValue = callFrame->r(src).jsValue(); + if (srcValue != vPC[2].u.jsCell.get()) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jneq_ptr); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_less) { + /* loop_if_less src1(r) src2(r) target(offset) + + Checks whether register src1 is less than register src2, as + with the ECMAScript '<' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is true. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_less); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_lesseq) { + /* loop_if_lesseq src1(r) src2(r) target(offset) + + Checks whether register src1 is less than or equal to register + src2, as with the ECMAScript '<=' operator, and then jumps to + offset target from the current instruction, if and only if the + result of the comparison is true. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_lesseq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_greater) { + /* loop_if_greater src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than register src2, as + with the ECMAScript '>' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is true. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_greater); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_loop_if_greatereq) { + /* loop_if_greatereq src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than or equal to register + src2, as with the ECMAScript '>=' operator, and then jumps to + offset target from the current instruction, if and only if the + result of the comparison is true. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_greatereq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jless) { + /* jless src1(r) src2(r) target(offset) + + Checks whether register src1 is less than register src2, as + with the ECMAScript '<' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is true. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jless); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jlesseq) { + /* jlesseq src1(r) src2(r) target(offset) + + Checks whether register src1 is less than or equal to + register src2, as with the ECMAScript '<=' operator, + and then jumps to offset target from the current instruction, + if and only if the result of the comparison is true. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jlesseq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jgreater) { + /* jgreater src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than register src2, as + with the ECMAScript '>' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is true. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jgreater); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jgreatereq) { + /* jgreatereq src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than or equal to + register src2, as with the ECMAScript '>=' operator, + and then jumps to offset target from the current instruction, + if and only if the result of the comparison is true. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jgreatereq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jnless) { + /* jnless src1(r) src2(r) target(offset) + + Checks whether register src1 is less than register src2, as + with the ECMAScript '<' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is false. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jnless); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jnlesseq) { + /* jnlesseq src1(r) src2(r) target(offset) + + Checks whether register src1 is less than or equal to + register src2, as with the ECMAScript '<=' operator, + and then jumps to offset target from the current instruction, + if and only if theresult of the comparison is false. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<true>(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jnlesseq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jngreater) { + /* jngreater src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than register src2, as + with the ECMAScript '>' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is false. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jngreater); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jngreatereq) { + /* jngreatereq src1(r) src2(r) target(offset) + + Checks whether register src1 is greater than or equal to + register src2, as with the ECMAScript '>=' operator, + and then jumps to offset target from the current instruction, + if and only if theresult of the comparison is false. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLessEq<false>(callFrame, src2, src1); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jngreatereq); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_switch_imm) { + /* switch_imm tableIndex(n) defaultOffset(offset) scrutinee(r) + + Performs a range checked switch on the scrutinee value, using + the tableIndex-th immediate switch jump table. If the scrutinee value + is an immediate number in the range covered by the referenced jump + table, and the value at jumpTable[scrutinee value] is non-zero, then + that value is used as the jump offset, otherwise defaultOffset is used. + */ + int tableIndex = vPC[1].u.operand; + int defaultOffset = vPC[2].u.operand; + JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); + if (scrutinee.isInt32()) + vPC += codeBlock->immediateSwitchJumpTable(tableIndex).offsetForValue(scrutinee.asInt32(), defaultOffset); + else if (scrutinee.isDouble() && scrutinee.asDouble() == static_cast<int32_t>(scrutinee.asDouble())) + vPC += codeBlock->immediateSwitchJumpTable(tableIndex).offsetForValue(static_cast<int32_t>(scrutinee.asDouble()), defaultOffset); + else + vPC += defaultOffset; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_switch_char) { + /* switch_char tableIndex(n) defaultOffset(offset) scrutinee(r) + + Performs a range checked switch on the scrutinee value, using + the tableIndex-th character switch jump table. If the scrutinee value + is a single character string in the range covered by the referenced jump + table, and the value at jumpTable[scrutinee value] is non-zero, then + that value is used as the jump offset, otherwise defaultOffset is used. + */ + int tableIndex = vPC[1].u.operand; + int defaultOffset = vPC[2].u.operand; + JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); + if (!scrutinee.isString()) + vPC += defaultOffset; + else { + StringImpl* value = asString(scrutinee)->value(callFrame).impl(); + if (value->length() != 1) + vPC += defaultOffset; + else + vPC += codeBlock->characterSwitchJumpTable(tableIndex).offsetForValue((*value)[0], defaultOffset); + } + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_switch_string) { + /* switch_string tableIndex(n) defaultOffset(offset) scrutinee(r) + + Performs a sparse hashmap based switch on the value in the scrutinee + register, using the tableIndex-th string switch jump table. If the + scrutinee value is a string that exists as a key in the referenced + jump table, then the value associated with the string is used as the + jump offset, otherwise defaultOffset is used. + */ + int tableIndex = vPC[1].u.operand; + int defaultOffset = vPC[2].u.operand; + JSValue scrutinee = callFrame->r(vPC[3].u.operand).jsValue(); + if (!scrutinee.isString()) + vPC += defaultOffset; + else + vPC += codeBlock->stringSwitchJumpTable(tableIndex).offsetForValue(asString(scrutinee)->value(callFrame).impl(), defaultOffset); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_func) { + /* new_func dst(r) func(f) + + Constructs a new Function instance from function func and + the current scope chain using the original Function + constructor, using the rules for function declarations, and + puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int func = vPC[2].u.operand; + int shouldCheck = vPC[3].u.operand; + ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); + if (!shouldCheck || !callFrame->r(dst).jsValue()) + callFrame->uncheckedR(dst) = JSValue(codeBlock->functionDecl(func)->make(callFrame, callFrame->scopeChain())); + + vPC += OPCODE_LENGTH(op_new_func); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_func_exp) { + /* new_func_exp dst(r) func(f) + + Constructs a new Function instance from function func and + the current scope chain using the original Function + constructor, using the rules for function expressions, and + puts the result in register dst. + */ + int dst = vPC[1].u.operand; + int funcIndex = vPC[2].u.operand; + + ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); + FunctionExecutable* function = codeBlock->functionExpr(funcIndex); + JSFunction* func = function->make(callFrame, callFrame->scopeChain()); + + /* + The Identifier in a FunctionExpression can be referenced from inside + the FunctionExpression's FunctionBody to allow the function to call + itself recursively. However, unlike in a FunctionDeclaration, the + Identifier in a FunctionExpression cannot be referenced from and + does not affect the scope enclosing the FunctionExpression. + */ + if (!function->name().isNull()) { + JSStaticScopeObject* functionScopeObject = JSStaticScopeObject::create(callFrame, function->name(), func, ReadOnly | DontDelete); + func->setScope(*globalData, func->scope()->push(functionScopeObject)); + } + + callFrame->uncheckedR(dst) = JSValue(func); + + vPC += OPCODE_LENGTH(op_new_func_exp); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_call_eval) { + /* call_eval func(r) argCount(n) registerOffset(n) + + Call a function named "eval" with no explicit "this" value + (which may therefore be the eval operator). If register + thisVal is the global object, and register func contains + that global object's original global eval function, then + perform the eval operator in local scope (interpreting + the argument registers as for the "call" + opcode). Otherwise, act exactly as the "call" opcode would. + */ + + int func = vPC[1].u.operand; + int argCount = vPC[2].u.operand; + int registerOffset = vPC[3].u.operand; + + ASSERT(codeBlock->codeType() != FunctionCode || !codeBlock->needsFullScopeChain() || callFrame->r(codeBlock->activationRegister()).jsValue()); + JSValue funcVal = callFrame->r(func).jsValue(); + + if (isHostFunction(funcVal, globalFuncEval)) { + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call_eval), callFrame->scopeChain(), callFrame, argCount, asFunction(funcVal)); + + JSValue result = eval(newCallFrame); + if ((exceptionValue = globalData->exception)) + goto vm_throw; + functionReturnValue = result; + + vPC += OPCODE_LENGTH(op_call_eval); + NEXT_INSTRUCTION(); + } + + // We didn't find the blessed version of eval, so process this + // instruction as a normal function call. + // fall through to op_call + } + DEFINE_OPCODE(op_call) { + /* call func(r) argCount(n) registerOffset(n) + + Perform a function call. + + registerOffset is the distance the callFrame pointer should move + before the VM initializes the new call frame's header. + + dst is where op_ret should store its result. + */ + + int func = vPC[1].u.operand; + int argCount = vPC[2].u.operand; + int registerOffset = vPC[3].u.operand; + + JSValue v = callFrame->r(func).jsValue(); + + CallData callData; + CallType callType = getCallData(v, callData); + + if (callType == CallTypeJS) { + ScopeChainNode* callDataScopeChain = callData.js.scopeChain; + + JSObject* error = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); + if (UNLIKELY(!!error)) { + exceptionValue = error; + goto vm_throw; + } + + CallFrame* previousCallFrame = callFrame; + CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); + callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); + if (UNLIKELY(!callFrame)) { + callFrame = previousCallFrame; + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + callFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_call), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); + codeBlock = newCodeBlock; + ASSERT(codeBlock == callFrame->codeBlock()); + *topCallFrameSlot = callFrame; + vPC = newCodeBlock->instructions().begin(); + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + + NEXT_INSTRUCTION(); + } + + if (callType == CallTypeHost) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call), scopeChain, callFrame, argCount, asObject(v)); + JSValue returnValue; + { + *topCallFrameSlot = newCallFrame; + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(callData.native.function(newCallFrame)); + *topCallFrameSlot = callFrame; + } + CHECK_FOR_EXCEPTION(); + + functionReturnValue = returnValue; + + vPC += OPCODE_LENGTH(op_call); + NEXT_INSTRUCTION(); + } + + ASSERT(callType == CallTypeNone); + + exceptionValue = createNotAFunctionError(callFrame, v); + goto vm_throw; + } + DEFINE_OPCODE(op_call_varargs) { + /* call_varargs callee(r) thisValue(r) arguments(r) firstFreeRegister(n) + + Perform a function call with a dynamic set of arguments. + + registerOffset is the distance the callFrame pointer should move + before the VM initializes the new call frame's header, excluding + space for arguments. + + dst is where op_ret should store its result. + */ + + JSValue v = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue thisValue = callFrame->r(vPC[2].u.operand).jsValue(); + JSValue arguments = callFrame->r(vPC[3].u.operand).jsValue(); + int firstFreeRegister = vPC[4].u.operand; + + CallFrame* newCallFrame = loadVarargs(callFrame, registerFile, thisValue, arguments, firstFreeRegister); + if ((exceptionValue = globalData->exception)) + goto vm_throw; + int argCount = newCallFrame->argumentCountIncludingThis(); + + CallData callData; + CallType callType = getCallData(v, callData); + + if (callType == CallTypeJS) { + ScopeChainNode* callDataScopeChain = callData.js.scopeChain; + + JSObject* error = callData.js.functionExecutable->compileForCall(callFrame, callDataScopeChain); + if (UNLIKELY(!!error)) { + exceptionValue = error; + goto vm_throw; + } + + CodeBlock* newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall(); + newCallFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, newCallFrame, 0, argCount); + if (UNLIKELY(!newCallFrame)) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + newCallFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_call_varargs), callDataScopeChain, callFrame, argCount, asFunction(v)); + codeBlock = newCodeBlock; + callFrame = newCallFrame; + ASSERT(codeBlock == callFrame->codeBlock()); + *topCallFrameSlot = callFrame; + vPC = newCodeBlock->instructions().begin(); + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + + NEXT_INSTRUCTION(); + } + + if (callType == CallTypeHost) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call_varargs), scopeChain, callFrame, argCount, asObject(v)); + + JSValue returnValue; + { + *topCallFrameSlot = newCallFrame; + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(callData.native.function(newCallFrame)); + *topCallFrameSlot = callFrame; + } + CHECK_FOR_EXCEPTION(); + + functionReturnValue = returnValue; + + vPC += OPCODE_LENGTH(op_call_varargs); + NEXT_INSTRUCTION(); + } + + ASSERT(callType == CallTypeNone); + + exceptionValue = createNotAFunctionError(callFrame, v); + goto vm_throw; + } + DEFINE_OPCODE(op_tear_off_activation) { + /* tear_off_activation activation(r) arguments(r) + + Copy locals and named parameters from the register file to the heap. + Point the bindings in 'activation' and 'arguments' to this new backing + store. (Note that 'arguments' may not have been created. If created, + 'arguments' already holds a copy of any extra / unnamed parameters.) + + This opcode appears before op_ret in functions that require full scope chains. + */ + + int activation = vPC[1].u.operand; + int arguments = vPC[2].u.operand; + ASSERT(codeBlock->needsFullScopeChain()); + JSValue activationValue = callFrame->r(activation).jsValue(); + if (activationValue) { + asActivation(activationValue)->tearOff(*globalData); + + if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) + asArguments(argumentsValue)->didTearOffActivation(*globalData, asActivation(activationValue)); + } else if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { + if (!codeBlock->isStrictMode()) + asArguments(argumentsValue)->tearOff(callFrame); + } + + vPC += OPCODE_LENGTH(op_tear_off_activation); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_tear_off_arguments) { + /* tear_off_arguments arguments(r) + + Copy named parameters from the register file to the heap. Point the + bindings in 'arguments' to this new backing store. (Note that + 'arguments' may not have been created. If created, 'arguments' already + holds a copy of any extra / unnamed parameters.) + + This opcode appears before op_ret in functions that don't require full + scope chains, but do use 'arguments'. + */ + + int src1 = vPC[1].u.operand; + ASSERT(!codeBlock->needsFullScopeChain() && codeBlock->ownerExecutable()->usesArguments()); + + if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src1)).jsValue()) + asArguments(arguments)->tearOff(callFrame); + + vPC += OPCODE_LENGTH(op_tear_off_arguments); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_ret) { + /* ret result(r) + + Return register result as the return value of the current + function call, writing it into functionReturnValue. + In addition, unwind one call frame and restore the scope + chain, code block instruction pointer and register base + to those of the calling function. + */ + + int result = vPC[1].u.operand; + + JSValue returnValue = callFrame->r(result).jsValue(); + + vPC = callFrame->returnVPC(); + callFrame = callFrame->callerFrame(); + + if (callFrame->hasHostCallFrameFlag()) + return returnValue; + + *topCallFrameSlot = callFrame; + functionReturnValue = returnValue; + codeBlock = callFrame->codeBlock(); + ASSERT(codeBlock == callFrame->codeBlock()); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_call_put_result) { + /* op_call_put_result result(r) + + Move call result from functionReturnValue to caller's + expected return value register. + */ + + callFrame->uncheckedR(vPC[1].u.operand) = functionReturnValue; + + vPC += OPCODE_LENGTH(op_call_put_result); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_ret_object_or_this) { + /* ret result(r) + + Return register result as the return value of the current + function call, writing it into the caller's expected return + value register. In addition, unwind one call frame and + restore the scope chain, code block instruction pointer and + register base to those of the calling function. + */ + + int result = vPC[1].u.operand; + + JSValue returnValue = callFrame->r(result).jsValue(); + + if (UNLIKELY(!returnValue.isObject())) + returnValue = callFrame->r(vPC[2].u.operand).jsValue(); + + vPC = callFrame->returnVPC(); + callFrame = callFrame->callerFrame(); + + if (callFrame->hasHostCallFrameFlag()) + return returnValue; + + *topCallFrameSlot = callFrame; + functionReturnValue = returnValue; + codeBlock = callFrame->codeBlock(); + ASSERT(codeBlock == callFrame->codeBlock()); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_enter) { + /* enter + + Initializes local variables to undefined. If the code block requires + an activation, enter_with_activation is used instead. + + This opcode appears only at the beginning of a code block. + */ + + size_t i = 0; + for (size_t count = codeBlock->m_numVars; i < count; ++i) + callFrame->uncheckedR(i) = jsUndefined(); + + vPC += OPCODE_LENGTH(op_enter); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_create_activation) { + /* create_activation dst(r) + + If the activation object for this callframe has not yet been created, + this creates it and writes it back to dst. + */ + + int activationReg = vPC[1].u.operand; + if (!callFrame->r(activationReg).jsValue()) { + JSActivation* activation = JSActivation::create(*globalData, callFrame, static_cast<FunctionExecutable*>(codeBlock->ownerExecutable())); + callFrame->r(activationReg) = JSValue(activation); + callFrame->setScopeChain(callFrame->scopeChain()->push(activation)); + } + vPC += OPCODE_LENGTH(op_create_activation); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_callee) { + /* op_get_callee callee(r) + + Move callee into a register. + */ + + callFrame->uncheckedR(vPC[1].u.operand) = JSValue(callFrame->callee()); + + vPC += OPCODE_LENGTH(op_get_callee); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_create_this) { + /* op_create_this this(r) proto(r) + + Allocate an object as 'this', fr use in construction. + + This opcode should only be used at the beginning of a code + block. + */ + + int thisRegister = vPC[1].u.operand; + int protoRegister = vPC[2].u.operand; + + JSFunction* constructor = asFunction(callFrame->callee()); +#if !ASSERT_DISABLED + ConstructData constructData; + ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS); +#endif + + Structure* structure; + JSValue proto = callFrame->r(protoRegister).jsValue(); + if (proto.isObject()) + structure = asObject(proto)->inheritorID(callFrame->globalData()); + else + structure = constructor->scope()->globalObject->emptyObjectStructure(); + callFrame->uncheckedR(thisRegister) = constructEmptyObject(callFrame, structure); + + vPC += OPCODE_LENGTH(op_create_this); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_convert_this) { + /* convert_this this(r) + + Takes the value in the 'this' register, converts it to a + value that is suitable for use as the 'this' value, and + stores it in the 'this' register. This opcode is emitted + to avoid doing the conversion in the caller unnecessarily. + + This opcode should only be used at the beginning of a code + block. + */ + + int thisRegister = vPC[1].u.operand; + JSValue thisVal = callFrame->r(thisRegister).jsValue(); + if (thisVal.isPrimitive()) + callFrame->uncheckedR(thisRegister) = JSValue(thisVal.toThisObject(callFrame)); + + vPC += OPCODE_LENGTH(op_convert_this); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_init_lazy_reg) { + /* init_lazy_reg dst(r) + + Initialises dst(r) to JSValue(). + + This opcode appears only at the beginning of a code block. + */ + int dst = vPC[1].u.operand; + + callFrame->uncheckedR(dst) = JSValue(); + vPC += OPCODE_LENGTH(op_init_lazy_reg); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_create_arguments) { + /* create_arguments dst(r) + + Creates the 'arguments' object and places it in both the + 'arguments' call frame slot and the local 'arguments' + register, if it has not already been initialised. + */ + + int dst = vPC[1].u.operand; + + if (!callFrame->r(dst).jsValue()) { + Arguments* arguments = Arguments::create(*globalData, callFrame); + callFrame->uncheckedR(dst) = JSValue(arguments); + callFrame->uncheckedR(unmodifiedArgumentsRegister(dst)) = JSValue(arguments); + } + vPC += OPCODE_LENGTH(op_create_arguments); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_construct) { + /* construct func(r) argCount(n) registerOffset(n) proto(r) thisRegister(r) + + Invoke register "func" as a constructor. For JS + functions, the calling convention is exactly as for the + "call" opcode, except that the "this" value is a newly + created Object. For native constructors, no "this" + value is passed. In either case, the argCount and registerOffset + registers are interpreted as for the "call" opcode. + + Register proto must contain the prototype property of + register func. This is to enable polymorphic inline + caching of this lookup. + */ + + int func = vPC[1].u.operand; + int argCount = vPC[2].u.operand; + int registerOffset = vPC[3].u.operand; + + JSValue v = callFrame->r(func).jsValue(); + + ConstructData constructData; + ConstructType constructType = getConstructData(v, constructData); + + if (constructType == ConstructTypeJS) { + ScopeChainNode* callDataScopeChain = constructData.js.scopeChain; + + JSObject* error = constructData.js.functionExecutable->compileForConstruct(callFrame, callDataScopeChain); + if (UNLIKELY(!!error)) { + exceptionValue = error; + goto vm_throw; + } + + CallFrame* previousCallFrame = callFrame; + CodeBlock* newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct(); + callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); + if (UNLIKELY(!callFrame)) { + callFrame = previousCallFrame; + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + callFrame->init(newCodeBlock, vPC + OPCODE_LENGTH(op_construct), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); + codeBlock = newCodeBlock; + *topCallFrameSlot = callFrame; + vPC = newCodeBlock->instructions().begin(); +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + + NEXT_INSTRUCTION(); + } + + if (constructType == ConstructTypeHost) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_construct), scopeChain, callFrame, argCount, asObject(v)); + + JSValue returnValue; + { + *topCallFrameSlot = newCallFrame; + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(constructData.native.function(newCallFrame)); + *topCallFrameSlot = callFrame; + } + CHECK_FOR_EXCEPTION(); + functionReturnValue = returnValue; + + vPC += OPCODE_LENGTH(op_construct); + NEXT_INSTRUCTION(); + } + + ASSERT(constructType == ConstructTypeNone); + + exceptionValue = createNotAConstructorError(callFrame, v); + goto vm_throw; + } + DEFINE_OPCODE(op_strcat) { + /* strcat dst(r) src(r) count(n) + + Construct a new String instance using the original + constructor, and puts the result in register dst. + The string will be the result of concatenating count + strings with values taken from registers starting at + register src. + */ + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + int count = vPC[3].u.operand; + + callFrame->uncheckedR(dst) = concatenateStrings(callFrame, &callFrame->registers()[src], count); + CHECK_FOR_EXCEPTION(); + vPC += OPCODE_LENGTH(op_strcat); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_to_primitive) { + int dst = vPC[1].u.operand; + int src = vPC[2].u.operand; + + callFrame->uncheckedR(dst) = callFrame->r(src).jsValue().toPrimitive(callFrame); + vPC += OPCODE_LENGTH(op_to_primitive); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_push_scope) { + /* push_scope scope(r) + + Converts register scope to object, and pushes it onto the top + of the current scope chain. The contents of the register scope + are replaced by the result of toObject conversion of the scope. + */ + int scope = vPC[1].u.operand; + JSValue v = callFrame->r(scope).jsValue(); + JSObject* o = v.toObject(callFrame); + CHECK_FOR_EXCEPTION(); + + callFrame->uncheckedR(scope) = JSValue(o); + callFrame->setScopeChain(callFrame->scopeChain()->push(o)); + + vPC += OPCODE_LENGTH(op_push_scope); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_pop_scope) { + /* pop_scope + + Removes the top item from the current scope chain. + */ + callFrame->setScopeChain(callFrame->scopeChain()->pop()); + + vPC += OPCODE_LENGTH(op_pop_scope); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_pnames) { + /* get_pnames dst(r) base(r) i(n) size(n) breakTarget(offset) + + Creates a property name list for register base and puts it + in register dst, initializing i and size for iteration. If + base is undefined or null, jumps to breakTarget. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int i = vPC[3].u.operand; + int size = vPC[4].u.operand; + int breakTarget = vPC[5].u.operand; + + JSValue v = callFrame->r(base).jsValue(); + if (v.isUndefinedOrNull()) { + vPC += breakTarget; + NEXT_INSTRUCTION(); + } + + JSObject* o = v.toObject(callFrame); + Structure* structure = o->structure(); + JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache(); + if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(callFrame)) + jsPropertyNameIterator = JSPropertyNameIterator::create(callFrame, o); + + callFrame->uncheckedR(dst) = jsPropertyNameIterator; + callFrame->uncheckedR(base) = JSValue(o); + callFrame->uncheckedR(i) = Register::withInt(0); + callFrame->uncheckedR(size) = Register::withInt(jsPropertyNameIterator->size()); + vPC += OPCODE_LENGTH(op_get_pnames); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_next_pname) { + /* next_pname dst(r) base(r) i(n) size(n) iter(r) target(offset) + + Copies the next name from the property name list in + register iter to dst, then jumps to offset target. If there are no + names left, invalidates the iterator and continues to the next + instruction. + */ + int dst = vPC[1].u.operand; + int base = vPC[2].u.operand; + int i = vPC[3].u.operand; + int size = vPC[4].u.operand; + int iter = vPC[5].u.operand; + int target = vPC[6].u.operand; + + JSPropertyNameIterator* it = callFrame->r(iter).propertyNameIterator(); + while (callFrame->r(i).i() != callFrame->r(size).i()) { + JSValue key = it->get(callFrame, asObject(callFrame->r(base).jsValue()), callFrame->r(i).i()); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(i) = Register::withInt(callFrame->r(i).i() + 1); + if (key) { + CHECK_FOR_TIMEOUT(); + callFrame->uncheckedR(dst) = key; + vPC += target; + NEXT_INSTRUCTION(); + } + } + + vPC += OPCODE_LENGTH(op_next_pname); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jmp_scopes) { + /* jmp_scopes count(n) target(offset) + + Removes the a number of items from the current scope chain + specified by immediate number count, then jumps to offset + target. + */ + int count = vPC[1].u.operand; + int target = vPC[2].u.operand; + + ScopeChainNode* tmp = callFrame->scopeChain(); + while (count--) + tmp = tmp->pop(); + callFrame->setScopeChain(tmp); + + vPC += target; + NEXT_INSTRUCTION(); + } +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + // Appease GCC + goto *(&&skip_new_scope); +#endif + DEFINE_OPCODE(op_push_new_scope) { + /* new_scope dst(r) property(id) value(r) + + Constructs a new StaticScopeObject with property set to value. That scope + object is then pushed onto the ScopeChain. The scope object is then stored + in dst for GC. + */ + callFrame->setScopeChain(createExceptionScope(callFrame, vPC)); + + vPC += OPCODE_LENGTH(op_push_new_scope); + NEXT_INSTRUCTION(); + } +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + skip_new_scope: +#endif + DEFINE_OPCODE(op_catch) { + /* catch ex(r) + + Retrieves the VM's current exception and puts it in register + ex. This is only valid after an exception has been raised, + and usually forms the beginning of an exception handler. + */ + ASSERT(exceptionValue); + ASSERT(!globalData->exception); + int ex = vPC[1].u.operand; + callFrame->uncheckedR(ex) = exceptionValue; + exceptionValue = JSValue(); + + vPC += OPCODE_LENGTH(op_catch); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_throw) { + /* throw ex(r) + + Throws register ex as an exception. This involves three + steps: first, it is set as the current exception in the + VM's internal state, then the stack is unwound until an + exception handler or a native code boundary is found, and + then control resumes at the exception handler if any or + else the script returns control to the nearest native caller. + */ + + int ex = vPC[1].u.operand; + exceptionValue = callFrame->r(ex).jsValue(); + + handler = throwException(callFrame, exceptionValue, vPC - codeBlock->instructions().begin()); + if (!handler) + return throwError(callFrame, exceptionValue); + + codeBlock = callFrame->codeBlock(); + vPC = codeBlock->instructions().begin() + handler->target; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_throw_reference_error) { + /* op_throw_reference_error message(k) + + Constructs a new reference Error instance using the + original constructor, using constant message as the + message string. The result is thrown. + */ + UString message = callFrame->r(vPC[1].u.operand).jsValue().toString(callFrame); + exceptionValue = JSValue(createReferenceError(callFrame, message)); + goto vm_throw; + } + DEFINE_OPCODE(op_end) { + /* end result(r) + + Return register result as the value of a global or eval + program. Return control to the calling native code. + */ + + int result = vPC[1].u.operand; + return callFrame->r(result).jsValue(); + } + DEFINE_OPCODE(op_put_getter) { + /* put_getter base(r) property(id) function(r) + + Sets register function on register base as the getter named + by identifier property. Base and function are assumed to be + objects as this op should only be used for getters defined + in object literal form. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + int function = vPC[3].u.operand; + + ASSERT(callFrame->r(base).jsValue().isObject()); + JSObject* baseObj = asObject(callFrame->r(base).jsValue()); + Identifier& ident = codeBlock->identifier(property); + ASSERT(callFrame->r(function).jsValue().isObject()); + baseObj->methodTable()->defineGetter(baseObj, callFrame, ident, asObject(callFrame->r(function).jsValue()), 0); + + vPC += OPCODE_LENGTH(op_put_getter); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_setter) { + /* put_setter base(r) property(id) function(r) + + Sets register function on register base as the setter named + by identifier property. Base and function are assumed to be + objects as this op should only be used for setters defined + in object literal form. + + Unlike many opcodes, this one does not write any output to + the register file. + */ + int base = vPC[1].u.operand; + int property = vPC[2].u.operand; + int function = vPC[3].u.operand; + + ASSERT(callFrame->r(base).jsValue().isObject()); + JSObject* baseObj = asObject(callFrame->r(base).jsValue()); + Identifier& ident = codeBlock->identifier(property); + ASSERT(callFrame->r(function).jsValue().isObject()); + baseObj->methodTable()->defineSetter(baseObj, callFrame, ident, asObject(callFrame->r(function).jsValue()), 0); + + vPC += OPCODE_LENGTH(op_put_setter); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_method_check) { + vPC++; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_jsr) { + /* jsr retAddrDst(r) target(offset) + + Places the address of the next instruction into the retAddrDst + register and jumps to offset target from the current instruction. + */ + int retAddrDst = vPC[1].u.operand; + int target = vPC[2].u.operand; + callFrame->r(retAddrDst) = vPC + OPCODE_LENGTH(op_jsr); + + vPC += target; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_sret) { + /* sret retAddrSrc(r) + + Jumps to the address stored in the retAddrSrc register. This + differs from op_jmp because the target address is stored in a + register, not as an immediate. + */ + int retAddrSrc = vPC[1].u.operand; + vPC = callFrame->r(retAddrSrc).vPC(); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_debug) { + /* debug debugHookID(n) firstLine(n) lastLine(n) + + Notifies the debugger of the current state of execution. This opcode + is only generated while the debugger is attached. + */ + int debugHookID = vPC[1].u.operand; + int firstLine = vPC[2].u.operand; + int lastLine = vPC[3].u.operand; + + debug(callFrame, static_cast<DebugHookID>(debugHookID), firstLine, lastLine); + + vPC += OPCODE_LENGTH(op_debug); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_profile_will_call) { + /* op_profile_will_call function(r) + + Notifies the profiler of the beginning of a function call. This opcode + is only generated if developer tools are enabled. + */ + int function = vPC[1].u.operand; + + if (*enabledProfilerReference) + (*enabledProfilerReference)->willExecute(callFrame, callFrame->r(function).jsValue()); + + vPC += OPCODE_LENGTH(op_profile_will_call); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_profile_did_call) { + /* op_profile_did_call function(r) + + Notifies the profiler of the end of a function call. This opcode + is only generated if developer tools are enabled. + */ + int function = vPC[1].u.operand; + + if (*enabledProfilerReference) + (*enabledProfilerReference)->didExecute(callFrame, callFrame->r(function).jsValue()); + + vPC += OPCODE_LENGTH(op_profile_did_call); + NEXT_INSTRUCTION(); + } + vm_throw: { + globalData->exception = JSValue(); + if (!tickCount) { + // The exceptionValue is a lie! (GCC produces bad code for reasons I + // cannot fathom if we don't assign to the exceptionValue before branching) + exceptionValue = createInterruptedExecutionException(globalData); + } + JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); + handler = throwException(callFrame, exceptionValue, vPC - codeBlock->instructions().begin()); + if (!handler) { + // Can't use the callframe at this point as the scopechain, etc have + // been released. + return throwError(globalObject->globalExec(), exceptionValue); + } + + codeBlock = callFrame->codeBlock(); + vPC = codeBlock->instructions().begin() + handler->target; + NEXT_INSTRUCTION(); + } + } +#if !ENABLE(COMPUTED_GOTO_INTERPRETER) + } // iterator loop ends +#endif + #undef NEXT_INSTRUCTION + #undef DEFINE_OPCODE + #undef CHECK_FOR_EXCEPTION + #undef CHECK_FOR_TIMEOUT +#endif // ENABLE(INTERPRETER) +} + +JSValue Interpreter::retrieveArguments(CallFrame* callFrame, JSFunction* function) const +{ + CallFrame* functionCallFrame = findFunctionCallFrame(callFrame, function); + if (!functionCallFrame) + return jsNull(); + + CodeBlock* codeBlock = functionCallFrame->codeBlock(); + if (codeBlock->usesArguments()) { + ASSERT(codeBlock->codeType() == FunctionCode); + int argumentsRegister = codeBlock->argumentsRegister(); + int realArgumentsRegister = unmodifiedArgumentsRegister(argumentsRegister); + if (JSValue arguments = functionCallFrame->uncheckedR(argumentsRegister).jsValue()) + return arguments; + JSValue arguments = JSValue(Arguments::create(callFrame->globalData(), functionCallFrame)); + functionCallFrame->r(argumentsRegister) = arguments; + functionCallFrame->r(realArgumentsRegister) = arguments; + return arguments; + } + + Arguments* arguments = Arguments::create(functionCallFrame->globalData(), functionCallFrame); + arguments->tearOff(functionCallFrame); + return JSValue(arguments); +} + +JSValue Interpreter::retrieveCaller(CallFrame* callFrame, JSFunction* function) const +{ + CallFrame* functionCallFrame = findFunctionCallFrame(callFrame, function); + if (!functionCallFrame) + return jsNull(); + + CallFrame* callerFrame = functionCallFrame->callerFrame(); + if (callerFrame->hasHostCallFrameFlag()) + return jsNull(); + + JSValue caller = callerFrame->callee(); + if (!caller) + return jsNull(); + + return caller; +} + +void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, UString& sourceURL, JSValue& function) const +{ + function = JSValue(); + lineNumber = -1; + sourceURL = UString(); + + CallFrame* callerFrame = callFrame->callerFrame(); + if (callerFrame->hasHostCallFrameFlag()) + return; + + CodeBlock* callerCodeBlock = callerFrame->codeBlock(); + if (!callerCodeBlock) + return; + unsigned bytecodeOffset = 0; +#if ENABLE(INTERPRETER) + if (!callerFrame->globalData().canUseJIT()) + bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC()); +#if ENABLE(JIT) + else + bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); +#endif +#else + bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC()); +#endif + lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1); + sourceID = callerCodeBlock->ownerExecutable()->sourceID(); + sourceURL = callerCodeBlock->ownerExecutable()->sourceURL(); + function = callerFrame->callee(); +} + +CallFrame* Interpreter::findFunctionCallFrame(CallFrame* callFrame, JSFunction* function) +{ + for (CallFrame* candidate = callFrame; candidate; candidate = candidate->trueCallerFrame()) { + if (candidate->callee() == function) + return candidate; + } + return 0; +} + +void Interpreter::enableSampler() +{ +#if ENABLE(OPCODE_SAMPLING) + if (!m_sampler) { + m_sampler = adoptPtr(new 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..368fa27f2 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2008 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 Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 "JSCell.h" +#include "JSValue.h" +#include "JSObject.h" +#include "Opcode.h" +#include "RegisterFile.h" + +#include <wtf/HashMap.h> + +namespace JSC { + + class CodeBlock; + class EvalExecutable; + class FunctionExecutable; + class JSFunction; + class JSGlobalObject; + class ProgramExecutable; + class Register; + class ScopeChainNode; + class SamplingTool; + struct CallFrameClosure; + struct HandlerInfo; + struct Instruction; + + enum DebugHookID { + WillExecuteProgram, + DidExecuteProgram, + DidEnterCallFrame, + DidReachBreakpoint, + WillLeaveCallFrame, + WillExecuteStatement + }; + + class TopCallFrameSetter { + public: + TopCallFrameSetter(JSGlobalData& global, CallFrame* callFrame) + : globalData(global) + , oldCallFrame(global.topCallFrame) + { + global.topCallFrame = callFrame; + } + + ~TopCallFrameSetter() + { + globalData.topCallFrame = oldCallFrame; + } + private: + JSGlobalData& globalData; + CallFrame* oldCallFrame; + }; + +#if PLATFORM(IOS) + // We use a smaller reentrancy limit on iPhone because of the high amount of + // stack space required on the web thread. + enum { MaxLargeThreadReentryDepth = 93, MaxSmallThreadReentryDepth = 16 }; +#else + enum { MaxLargeThreadReentryDepth = 256, MaxSmallThreadReentryDepth = 16 }; +#endif // PLATFORM(IOS) + + class Interpreter { + WTF_MAKE_FAST_ALLOCATED; + friend class JIT; + friend class CachedCall; + public: + Interpreter(); + + void initialize(bool canUseJIT); + + RegisterFile& registerFile() { return m_registerFile; } + + Opcode getOpcode(OpcodeID id) + { + ASSERT(m_initialized); +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + return m_opcodeTable[id]; +#else + return id; +#endif + } + + OpcodeID getOpcodeID(Opcode opcode) + { + ASSERT(m_initialized); +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + ASSERT(isOpcode(opcode)); + if (!m_enabled) { + OpcodeID result = static_cast<OpcodeID>(bitwise_cast<uintptr_t>(opcode)); + ASSERT(result == m_opcodeIDTable.get(opcode)); + return result; + } + return m_opcodeIDTable.get(opcode); +#else + return opcode; +#endif + } + + bool isOpcode(Opcode); + + JSValue execute(ProgramExecutable*, CallFrame*, ScopeChainNode*, 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 execute(EvalExecutable*, CallFrame*, JSValue thisValue, ScopeChainNode*); + JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, ScopeChainNode*, int globalRegisterOffset); + + JSValue retrieveArguments(CallFrame*, JSFunction*) const; + JS_EXPORT_PRIVATE JSValue retrieveCaller(CallFrame*, JSFunction*) const; + JS_EXPORT_PRIVATE void retrieveLastCaller(CallFrame*, int& lineNumber, intptr_t& sourceID, UString& sourceURL, JSValue& function) const; + + void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); + + SamplingTool* sampler() { return m_sampler.get(); } + + NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset); + NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine); + + void dumpSampleData(ExecState* exec); + void startSampling(); + void stopSampling(); + private: + enum ExecutionFlag { Normal, InitializeAndReturn }; + + CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, JSFunction*, int argumentCountIncludingThis, ScopeChainNode*); + void endRepeatCall(CallFrameClosure&); + JSValue execute(CallFrameClosure&); + +#if ENABLE(INTERPRETER) + NEVER_INLINE bool resolve(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE bool resolveSkip(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE bool resolveGlobal(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE bool resolveGlobalDynamic(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE void resolveBase(CallFrame*, Instruction* vPC); + NEVER_INLINE bool resolveBaseAndProperty(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE bool resolveThisAndProperty(CallFrame*, Instruction*, JSValue& exceptionValue); + NEVER_INLINE ScopeChainNode* createExceptionScope(CallFrame*, const Instruction* vPC); + + void tryCacheGetByID(CallFrame*, CodeBlock*, Instruction*, JSValue baseValue, const Identifier& propertyName, const PropertySlot&); + void uncacheGetByID(CodeBlock*, Instruction* vPC); + void tryCachePutByID(CallFrame*, CodeBlock*, Instruction*, JSValue baseValue, const PutPropertySlot&); + void uncachePutByID(CodeBlock*, Instruction* vPC); +#endif // ENABLE(INTERPRETER) + + NEVER_INLINE bool unwindCallFrame(CallFrame*&, JSValue, unsigned& bytecodeOffset, CodeBlock*&); + + static ALWAYS_INLINE CallFrame* slideRegisterWindowForCall(CodeBlock*, RegisterFile*, CallFrame*, size_t registerOffset, int argc); + + static CallFrame* findFunctionCallFrame(CallFrame*, JSFunction*); + + JSValue privateExecute(ExecutionFlag, RegisterFile*, CallFrame*); + + void dumpCallFrame(CallFrame*); + 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; + OwnPtr<SamplingTool> m_sampler; + + int m_reentryDepth; + + RegisterFile m_registerFile; + +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + Opcode m_opcodeTable[numOpcodeIDs]; // Maps OpcodeID => Opcode for compiling + HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling +#endif + +#if !ASSERT_DISABLED + bool m_initialized; +#endif + bool m_enabled; + }; + + // This value must not be an object that would require this conversion (WebCore's global object). + inline bool isValidThisObject(JSValue thisValue, ExecState* exec) + { + return !thisValue.isObject() || thisValue.toThisObject(exec) == thisValue; + } + + inline JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, ScopeChainNode* scopeChain) + { + return execute(eval, callFrame, thisValue, scopeChain, m_registerFile.size() + 1 + RegisterFile::CallFrameHeaderSize); + } + + JSValue eval(CallFrame*); + CallFrame* loadVarargs(CallFrame*, RegisterFile*, JSValue thisValue, JSValue arguments, int firstFreeRegister); + +} // namespace JSC + +#endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h new file mode 100644 index 000000000..f3b2125cb --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Register.h @@ -0,0 +1,228 @@ +/* + * 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 Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 "JSValue.h" +#include <wtf/Assertions.h> +#include <wtf/VectorTraits.h> + +namespace JSC { + + class CodeBlock; + class ExecState; + class JSActivation; + class JSObject; + class JSPropertyNameIterator; + class ScopeChainNode; + + struct InlineCallFrame; + struct Instruction; + + typedef ExecState CallFrame; + + class Register { + WTF_MAKE_FAST_ALLOCATED; + public: + Register(); + + Register(const JSValue&); + Register& operator=(const JSValue&); + JSValue jsValue() const; + EncodedJSValue encodedJSValue() const; + + Register& operator=(CallFrame*); + Register& operator=(CodeBlock*); + Register& operator=(ScopeChainNode*); + Register& operator=(Instruction*); + Register& operator=(InlineCallFrame*); + + int32_t i() const; + JSActivation* activation() const; + CallFrame* callFrame() const; + CodeBlock* codeBlock() const; + JSObject* function() const; + JSPropertyNameIterator* propertyNameIterator() const; + ScopeChainNode* scopeChain() const; + Instruction* vPC() const; + InlineCallFrame* asInlineCallFrame() const; + int32_t unboxedInt32() const; + bool unboxedBoolean() 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; + } + + static inline Register withCallee(JSObject* callee); + + private: + union { + EncodedJSValue value; + CallFrame* callFrame; + CodeBlock* codeBlock; + Instruction* vPC; + InlineCallFrame* inlineCallFrame; + EncodedValueDescriptor encodedValue; + } 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; + } + + 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 Register& Register::operator=(Instruction* vPC) + { + u.vPC = vPC; + return *this; + } + + ALWAYS_INLINE Register& Register::operator=(InlineCallFrame* inlineCallFrame) + { + u.inlineCallFrame = inlineCallFrame; + return *this; + } + + ALWAYS_INLINE int32_t Register::i() const + { + return jsValue().asInt32(); + } + + ALWAYS_INLINE CallFrame* Register::callFrame() const + { + return u.callFrame; + } + + ALWAYS_INLINE CodeBlock* Register::codeBlock() const + { + return u.codeBlock; + } + + ALWAYS_INLINE Instruction* Register::vPC() const + { + return u.vPC; + } + + ALWAYS_INLINE InlineCallFrame* Register::asInlineCallFrame() const + { + return u.inlineCallFrame; + } + + ALWAYS_INLINE int32_t Register::unboxedInt32() const + { + return payload(); + } + + ALWAYS_INLINE bool Register::unboxedBoolean() const + { + return !!payload(); + } + + 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/RegisterFile.cpp b/Source/JavaScriptCore/interpreter/RegisterFile.cpp new file mode 100644 index 000000000..b72352781 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/RegisterFile.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 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 Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 "RegisterFile.h" + +#include "ConservativeRoots.h" +#include "Interpreter.h" + +namespace JSC { + +static size_t committedBytesCount = 0; + +static Mutex& registerFileStatisticsMutex() +{ + DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); + return staticMutex; +} + +RegisterFile::~RegisterFile() +{ + void* base = m_reservation.base(); + m_reservation.decommit(base, reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base)); + addToCommittedByteCount(-(reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base))); + m_reservation.deallocate(); +} + +bool RegisterFile::growSlowCase(Register* newEnd) +{ + if (newEnd <= m_commitEnd) { + m_end = newEnd; + return true; + } + + long delta = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize); + if (reinterpret_cast<char*>(m_commitEnd) + delta > static_cast<char*>(m_reservation.base()) + m_reservation.size()) + return false; + + m_reservation.commit(m_commitEnd, delta); + addToCommittedByteCount(delta); + m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + delta); + m_end = newEnd; + return true; +} + +void RegisterFile::gatherConservativeRoots(ConservativeRoots& conservativeRoots) +{ + conservativeRoots.add(begin(), end()); +} + +void RegisterFile::gatherConservativeRoots(ConservativeRoots& conservativeRoots, DFGCodeBlocks& dfgCodeBlocks) +{ + conservativeRoots.add(begin(), end(), dfgCodeBlocks); +} + +void RegisterFile::releaseExcessCapacity() +{ + ptrdiff_t delta = reinterpret_cast<uintptr_t>(m_commitEnd) - reinterpret_cast<uintptr_t>(m_reservation.base()); + m_reservation.decommit(m_reservation.base(), delta); + addToCommittedByteCount(-delta); + m_commitEnd = static_cast<Register*>(m_reservation.base()); +} + +void RegisterFile::initializeThreading() +{ + registerFileStatisticsMutex(); +} + +size_t RegisterFile::committedByteCount() +{ + MutexLocker locker(registerFileStatisticsMutex()); + return committedBytesCount; +} + +void RegisterFile::addToCommittedByteCount(long byteCount) +{ + MutexLocker locker(registerFileStatisticsMutex()); + ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); + committedBytesCount += byteCount; +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/RegisterFile.h b/Source/JavaScriptCore/interpreter/RegisterFile.h new file mode 100644 index 000000000..e45b869a1 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/RegisterFile.h @@ -0,0 +1,120 @@ +/* + * 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 Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 RegisterFile_h +#define RegisterFile_h + +#include "ExecutableAllocator.h" +#include "Register.h" +#include <wtf/Noncopyable.h> +#include <wtf/PageReservation.h> +#include <wtf/VMTags.h> + +namespace JSC { + + class ConservativeRoots; + class DFGCodeBlocks; + + class RegisterFile { + WTF_MAKE_NONCOPYABLE(RegisterFile); + public: + enum CallFrameHeaderEntry { + CallFrameHeaderSize = 6, + + ArgumentCount = -6, + CallerFrame = -5, + Callee = -4, + ScopeChain = -3, + ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. + CodeBlock = -1, + }; + + static const size_t defaultCapacity = 512 * 1024; + static const size_t commitSize = 16 * 1024; + // Allow 8k of excess registers before we start trying to reap the registerfile + static const ptrdiff_t maxExcessCapacity = 8 * 1024; + + RegisterFile(size_t capacity = defaultCapacity); + ~RegisterFile(); + + void gatherConservativeRoots(ConservativeRoots&); + void gatherConservativeRoots(ConservativeRoots&, DFGCodeBlocks&); + + Register* begin() const { return static_cast<Register*>(m_reservation.base()); } + Register* end() const { return m_end; } + size_t size() const { return end() - begin(); } + + bool grow(Register*); + void shrink(Register*); + + static size_t committedByteCount(); + static void initializeThreading(); + + Register* const * addressOfEnd() const + { + return &m_end; + } + + private: + bool growSlowCase(Register*); + void releaseExcessCapacity(); + void addToCommittedByteCount(long); + Register* m_end; + Register* m_commitEnd; + PageReservation m_reservation; + }; + + inline RegisterFile::RegisterFile(size_t capacity) + : m_end(0) + { + ASSERT(capacity && isPageAligned(capacity)); + + m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); + m_end = static_cast<Register*>(m_reservation.base()); + m_commitEnd = static_cast<Register*>(m_reservation.base()); + } + + inline void RegisterFile::shrink(Register* newEnd) + { + if (newEnd >= m_end) + return; + m_end = newEnd; + if (m_end == m_reservation.base() && (m_commitEnd - begin()) >= maxExcessCapacity) + releaseExcessCapacity(); + } + + inline bool RegisterFile::grow(Register* newEnd) + { + if (newEnd <= m_end) + return true; + return growSlowCase(newEnd); + } + +} // namespace JSC + +#endif // RegisterFile_h |