From cad810f21b803229eb11403f9209855525a25d57 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Fri, 6 May 2011 11:45:16 +0100 Subject: Merge WebKit at r75315: Initial merge by git. Change-Id: I570314b346ce101c935ed22a626b48c2af266b84 --- Source/JavaScriptCore/interpreter/CachedCall.h | 76 + Source/JavaScriptCore/interpreter/CallFrame.cpp | 53 + Source/JavaScriptCore/interpreter/CallFrame.h | 167 + .../JavaScriptCore/interpreter/CallFrameClosure.h | 60 + Source/JavaScriptCore/interpreter/Interpreter.cpp | 4921 ++++++++++++++++++++ Source/JavaScriptCore/interpreter/Interpreter.h | 170 + Source/JavaScriptCore/interpreter/Register.h | 218 + Source/JavaScriptCore/interpreter/RegisterFile.cpp | 93 + Source/JavaScriptCore/interpreter/RegisterFile.h | 211 + 9 files changed, 5969 insertions(+) create mode 100644 Source/JavaScriptCore/interpreter/CachedCall.h create mode 100644 Source/JavaScriptCore/interpreter/CallFrame.cpp create mode 100644 Source/JavaScriptCore/interpreter/CallFrame.h create mode 100644 Source/JavaScriptCore/interpreter/CallFrameClosure.h create mode 100644 Source/JavaScriptCore/interpreter/Interpreter.cpp create mode 100644 Source/JavaScriptCore/interpreter/Interpreter.h create mode 100644 Source/JavaScriptCore/interpreter/Register.h create mode 100644 Source/JavaScriptCore/interpreter/RegisterFile.cpp create mode 100644 Source/JavaScriptCore/interpreter/RegisterFile.h (limited to 'Source/JavaScriptCore/interpreter') diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h new file mode 100644 index 0000000..dfbe658 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -0,0 +1,76 @@ +/* + * 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 : public Noncopyable { + public: + CachedCall(CallFrame* callFrame, JSFunction* function, int argCount) + : m_valid(false) + , m_interpreter(callFrame->interpreter()) + , m_globalObjectScope(callFrame, function->scope().globalObject()) + { + ASSERT(!function->isHostFunction()); + m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, function, argCount, function->scope().node()); + m_valid = !callFrame->hadException(); + } + + JSValue call() + { + ASSERT(m_valid); + return m_interpreter->execute(m_closure); + } + void setThis(JSValue v) { m_closure.setArgument(0, v); } + void setArgument(int n, JSValue v) { m_closure.setArgument(n + 1, 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 0000000..5819875 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -0,0 +1,53 @@ +/* + * 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 + +} diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h new file mode 100644 index 0000000..2797ef3 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (C) 2003, 2007, 2008 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" +#include "ScopeChain.h" + +namespace JSC { + + class Arguments; + class JSActivation; + class Interpreter; + + // Represents the current state of script execution. + // Passed as the first argument to most functions. + class ExecState : private Register { + public: + 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. + JSGlobalObject* lexicalGlobalObject() const + { + return scopeChain()->globalObject; + } + + // Differs from lexicalGlobalObject because this will have DOM window shell rather than + // the actual DOM window, which can't be "this" for security reasons. + JSObject* globalThisValue() const + { + return scopeChain()->globalThis; + } + + JSGlobalData& globalData() const + { + ASSERT(scopeChain()->globalData); + return *scopeChain()->globalData; + } + + // 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* arrayTable(CallFrame* callFrame) { return callFrame->globalData().arrayTable; } + static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->globalData().dateTable; } + static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->globalData().jsonTable; } + static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->globalData().mathTable; } + static const HashTable* numberTable(CallFrame* callFrame) { return callFrame->globalData().numberTable; } + static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; } + static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->globalData().regExpConstructorTable; } + static const HashTable* stringTable(CallFrame* callFrame) { return callFrame->globalData().stringTable; } + + static CallFrame* create(Register* callFrameBase) { return static_cast(callFrameBase); } + Register* registers() { return this; } + + CallFrame& operator=(const Register& r) { *static_cast(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(INTERPRETER) + Instruction* returnVPC() const { return this[RegisterFile::ReturnPC].vPC(); } +#endif + + void setCallerFrame(CallFrame* callerFrame) { static_cast(this)[RegisterFile::CallerFrame] = callerFrame; } + void setScopeChain(ScopeChainNode* scopeChain) { static_cast(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. + int hostThisRegister() { return -RegisterFile::CallFrameHeaderSize - argumentCountIncludingThis(); } + JSValue hostThisValue() { return this[hostThisRegister()].jsValue(); } + size_t argumentCount() const { return argumentCountIncludingThis() - 1; } + size_t argumentCountIncludingThis() const { return this[RegisterFile::ArgumentCount].i(); } + JSValue argument(int argumentNumber) + { + int argumentIndex = -RegisterFile::CallFrameHeaderSize - this[RegisterFile::ArgumentCount].i() + argumentNumber + 1; + if (argumentIndex >= -RegisterFile::CallFrameHeaderSize) + return jsUndefined(); + return this[argumentIndex].jsValue(); + } + + static CallFrame* noCaller() { return reinterpret_cast(HostCallFrameFlag); } + + bool hasHostCallFrameFlag() const { return reinterpret_cast(this) & HostCallFrameFlag; } + CallFrame* addHostCallFrameFlag() const { return reinterpret_cast(reinterpret_cast(this) | HostCallFrameFlag); } + CallFrame* removeHostCallFrameFlag() { return reinterpret_cast(reinterpret_cast(this) & ~HostCallFrameFlag); } + + void setArgumentCountIncludingThis(int count) { static_cast(this)[RegisterFile::ArgumentCount] = Register::withInt(count); } + void setCallee(JSObject* callee) { static_cast(this)[RegisterFile::Callee] = Register::withCallee(callee); } + void setCodeBlock(CodeBlock* codeBlock) { static_cast(this)[RegisterFile::CodeBlock] = codeBlock; } + void setReturnPC(void* value) { static_cast(this)[RegisterFile::ReturnPC] = (Instruction*)value; } + + private: + static const intptr_t HostCallFrameFlag = 1; +#ifndef NDEBUG + RegisterFile* registerFile(); +#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 0000000..b4b7efd --- /dev/null +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -0,0 +1,60 @@ +/* + * 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 expectedParams; + int providedParams; + + void setArgument(int arg, JSValue value) + { + if (arg < expectedParams) + newCallFrame[arg - RegisterFile::CallFrameHeaderSize - expectedParams] = value; + else + newCallFrame[arg - RegisterFile::CallFrameHeaderSize - expectedParams - providedParams] = value; + } + + void resetCallFrame() + { + newCallFrame->setScopeChain(scopeChain); + for (int i = providedParams; i < expectedParams; ++i) + newCallFrame[i - RegisterFile::CallFrameHeaderSize - expectedParams] = jsUndefined(); + } +}; + +} + +#endif diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp new file mode 100644 index 0000000..57c068a --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -0,0 +1,4921 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich + * + * 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 "Collector.h" +#include "Debugger.h" +#include "DebuggerCallFrame.h" +#include "ErrorInstance.h" +#include "EvalCodeCache.h" +#include "ExceptionHelpers.h" +#include "GetterSetter.h" +#include "GlobalEvalFunction.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 +#include +#include + +#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, ScopeChain& 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; + 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; + 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; + 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) { + if (vPC[3].u.structure) + vPC[3].u.structure->deref(); + globalObject->structure()->ref(); + vPC[3] = 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; + 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; + 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; + ++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) { + if (vPC[3].u.structure) + vPC[3].u.structure->deref(); + globalObject->structure()->ref(); + vPC[3] = 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; + 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; +} + +#endif // ENABLE(INTERPRETER) + +ALWAYS_INLINE CallFrame* Interpreter::slideRegisterWindowForCall(CodeBlock* newCodeBlock, RegisterFile* registerFile, CallFrame* callFrame, size_t registerOffset, int argc) +{ + Register* r = callFrame->registers(); + Register* newEnd = r + registerOffset + newCodeBlock->m_numCalleeRegisters; + + if (LIKELY(argc == newCodeBlock->m_numParameters)) { // correct number of arguments + if (UNLIKELY(!registerFile->grow(newEnd))) + return 0; + r += registerOffset; + } else if (argc < newCodeBlock->m_numParameters) { // too few arguments -- fill in the blanks + size_t omittedArgCount = newCodeBlock->m_numParameters - argc; + registerOffset += omittedArgCount; + newEnd += omittedArgCount; + if (!registerFile->grow(newEnd)) + return 0; + r += registerOffset; + + Register* argv = r - RegisterFile::CallFrameHeaderSize - omittedArgCount; + for (size_t i = 0; i < omittedArgCount; ++i) + argv[i] = jsUndefined(); + } else { // too many arguments -- copy expected arguments, leaving the extra arguments behind + size_t numParameters = newCodeBlock->m_numParameters; + registerOffset += numParameters; + newEnd += numParameters; + + if (!registerFile->grow(newEnd)) + return 0; + r += registerOffset; + + Register* argv = r - RegisterFile::CallFrameHeaderSize - numParameters - argc; + for (size_t i = 0; i < numParameters; ++i) + argv[i + argc] = argv[i]; + } + + return CallFrame::create(r); +} + +#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 + +NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* registerFile, Register* argv, int argc, int registerOffset) +{ + if (argc < 2) + return jsUndefined(); + + JSValue program = argv[1].jsValue(); + + if (!program.isString()) + return program; + + UString programSource = asString(program)->value(callFrame); + if (callFrame->hadException()) + return JSValue(); + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (!codeBlock->isStrictMode()) { + // FIXME: We can use the preparser in strict mode, we just need additional logic + // to prevent duplicates. + LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON); + if (JSValue parsedObject = preparser.tryLiteralParse()) + return parsedObject; + } + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + JSValue exceptionValue; + RefPtr eval = codeBlock->evalCodeCache().get(callFrame, codeBlock->isStrictMode(), programSource, scopeChain, exceptionValue); + + ASSERT(!eval == exceptionValue); + if (UNLIKELY(!eval)) + return throwError(callFrame, exceptionValue); + + return callFrame->globalData().interpreter->execute(eval.get(), callFrame, callFrame->uncheckedR(codeBlock->thisRegister()).jsValue().toThisObject(callFrame), callFrame->registers() - registerFile->start() + registerOffset, scopeChain); +} + +Interpreter::Interpreter() + : m_sampleEntryDepth(0) + , m_reentryDepth(0) +{ +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + privateExecute(InitializeAndReturn, 0, 0); + + for (int i = 0; i < numOpcodeIDs; ++i) + m_opcodeIDTable.add(m_opcodeTable[i], static_cast(i)); +#endif // ENABLE(COMPUTED_GOTO_INTERPRETER) + +#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(); + RegisterFile* registerFile = &callFrame->scopeChain()->globalObject->globalData().interpreter->registerFile(); + const Register* it; + const Register* end; + JSValue v; + + if (codeBlock->codeType() == GlobalCode) { + it = registerFile->lastGlobal(); + end = it + registerFile->numGlobals(); + while (it != end) { + v = (*it).jsValue(); +#if USE(JSVALUE32_64) + printf("[global var] | %10p | %-16s 0x%llx \n", it, v.description(), JSValue::encode(v)); +#else + printf("[global var] | %10p | %-16s %p \n", it, v.description(), JSValue::encode(v)); +#endif + ++it; + } + printf("-----------------------------------------------------------------------------\n"); + } + + 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::emptyValue() + && !HashTraits::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::info)) + scopeChain = scopeChain->pop(); + JSActivation* activation = asActivation(scopeChain->object); + activation->copyRegisters(); + if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) { + if (!oldCodeBlock->isStrictMode()) + asArguments(arguments)->setActivation(activation); + } + } else if (oldCodeBlock->usesArguments() && !oldCodeBlock->isStrictMode()) { + if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) + asArguments(arguments)->copyRegisters(); + } + + if (oldCodeBlock->needsFullScopeChain()) + scopeChain->deref(); + + CallFrame* callerFrame = callFrame->callerFrame(); + if (callerFrame->hasHostCallFrameFlag()) + return false; + + codeBlock = callerFrame->codeBlock(); +#if ENABLE(JIT) && ENABLE(INTERPRETER) + if (callerFrame->globalData().canUseJIT()) + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); + else + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()); +#elif ENABLE(JIT) + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnPC()); +#else + bytecodeOffset = codeBlock->bytecodeOffset(callFrame->returnVPC()); +#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->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 UChar* 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])) + stop--; + message = makeUString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')"); + } + + exception->putDirect(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(exception)->appendSourceToMessage()) + appendSourceToError(callFrame, static_cast(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()); + } + + ComplType exceptionType = exception->exceptionType(); + isInterrupt = exceptionType == Interrupted || exceptionType == Terminated; + } + + 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(); + ScopeChain sc(scopeChain); + int scopeDelta = 0; + if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode + || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) + scopeDelta = depth(codeBlock, sc) - 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(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + 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)); + + JSGlobalObject* lastGlobalObject = m_registerFile.globalObject(); + JSGlobalObject* globalObject = callFrame->dynamicGlobalObject(); + globalObject->copyGlobalsTo(m_registerFile); + + 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->uncheckedR(newCallFrame->hostThisRegister()) = JSValue(thisObj); + + if (codeBlock->needsFullScopeChain()) + scopeChain->ref(); + + DynamicGlobalObjectScope globalObjectScope(callFrame, scopeChain->globalObject); + + 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()); + + if (m_reentryDepth && lastGlobalObject && globalObject != lastGlobalObject) + lastGlobalObject->copyGlobalsTo(m_registerFile); + + 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(!callFrame->hadException()); + + 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); + size_t dst = 0; + newCallFrame->uncheckedR(0) = thisValue; + ArgList::const_iterator end = args.end(); + for (ArgList::const_iterator it = args.begin(); it != end; ++it) + newCallFrame->uncheckedR(++dst) = *it; + + if (callType == CallTypeJS) { + ScopeChainNode* callDataScopeChain = callData.js.scopeChain; + + 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, registerOffset, argCount); + if (UNLIKELY(!newCallFrame)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + newCallFrame->init(newCodeBlock, 0, callDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, function); + + DynamicGlobalObjectScope globalObjectScope(newCallFrame, callDataScopeChain->globalObject); + + 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 = CallFrame::create(newCallFrame->registers() + registerOffset); + newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, function); + + DynamicGlobalObjectScope globalObjectScope(newCallFrame, scopeChain->globalObject); + + 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()); + + 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); + size_t dst = 0; + ArgList::const_iterator end = args.end(); + for (ArgList::const_iterator it = args.begin(); it != end; ++it) + newCallFrame->uncheckedR(++dst) = *it; + + if (constructType == ConstructTypeJS) { + ScopeChainNode* constructDataScopeChain = constructData.js.scopeChain; + + 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, registerOffset, argCount); + if (UNLIKELY(!newCallFrame)) { + m_registerFile.shrink(oldEnd); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + newCallFrame->init(newCodeBlock, 0, constructDataScopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); + + DynamicGlobalObjectScope globalObjectScope(newCallFrame, constructDataScopeChain->globalObject); + + 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 = CallFrame::create(newCallFrame->registers() + registerOffset); + newCallFrame->init(0, 0, scopeChain, callFrame->addHostCallFrameFlag(), argCount, constructor); + + DynamicGlobalObjectScope globalObjectScope(newCallFrame, scopeChain->globalObject); + + 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 argCount, ScopeChainNode* scopeChain) +{ + ASSERT(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth) { + if (m_reentryDepth >= callFrame->globalData().maxReentryDepth) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + } + + Register* oldEnd = m_registerFile.end(); + int argc = 1 + argCount; // implicit "this" parameter + + if (!m_registerFile.grow(oldEnd + argc)) { + throwStackOverflowError(callFrame); + return CallFrameClosure(); + } + + CallFrame* newCallFrame = CallFrame::create(oldEnd); + size_t dst = 0; + for (int i = 0; i < argc; ++i) + newCallFrame->uncheckedR(++dst) = jsUndefined(); + + 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, argc + RegisterFile::CallFrameHeaderSize, argc); + if (UNLIKELY(!newCallFrame)) { + throwStackOverflowError(callFrame); + m_registerFile.shrink(oldEnd); + return CallFrameClosure(); + } + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), argc, function); + CallFrameClosure result = { callFrame, newCallFrame, function, FunctionExecutable, scopeChain->globalData, oldEnd, scopeChain, codeBlock->m_numParameters, argc }; + return result; +} + +JSValue Interpreter::execute(CallFrameClosure& closure) +{ + closure.resetCallFrame(); + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(closure.oldCallFrame, closure.function); + + 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) +{ + m_registerFile.shrink(closure.oldEnd); +} + +JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObject* thisObj, ScopeChainNode* scopeChain) +{ + JSObject* compileError = eval->compile(callFrame, scopeChain); + if (UNLIKELY(!!compileError)) + return checkedReturn(throwError(callFrame, compileError)); + return execute(eval, callFrame, thisObj, m_registerFile.size() + eval->generatedBytecode().m_numParameters + RegisterFile::CallFrameHeaderSize, scopeChain); +} + +JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObject* thisObj, int globalRegisterOffset, ScopeChainNode* scopeChain) +{ + ASSERT(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxSmallThreadReentryDepth && m_reentryDepth >= callFrame->globalData().maxReentryDepth) + return checkedReturn(throwStackOverflowError(callFrame)); + + DynamicGlobalObjectScope globalObjectScope(callFrame, scopeChain->globalObject); + + 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) { + ASSERT(node); + if (node->object->isVariableObject()) { + variableObject = static_cast(node->object); + break; + } + } + + unsigned numVariables = codeBlock->numVariables(); + int numFunctions = codeBlock->numberOfFunctionDecls(); + bool pushedScope = false; + if (numVariables || numFunctions) { + if (codeBlock->isStrictMode()) { + variableObject = new (callFrame) StrictEvalActivation(callFrame); + scopeChain = scopeChain->push(variableObject); + pushedScope = true; + } + // Scope for BatchedTransitionOptimizer + BatchedTransitionOptimizer optimizer(variableObject); + + for (unsigned i = 0; i < numVariables; ++i) { + const Identifier& ident = codeBlock->variable(i); + if (!variableObject->hasProperty(callFrame, ident)) { + PutPropertySlot slot; + variableObject->put(callFrame, ident, jsUndefined(), slot); + } + } + + for (int i = 0; i < numFunctions; ++i) { + FunctionExecutable* function = codeBlock->functionDecl(i); + PutPropertySlot slot; + variableObject->put(callFrame, function->name(), function->make(callFrame, scopeChain), slot); + } + } + + Register* oldEnd = m_registerFile.end(); + Register* newEnd = m_registerFile.start() + globalRegisterOffset + codeBlock->m_numCalleeRegisters; + if (!m_registerFile.grow(newEnd)) { + if (pushedScope) + scopeChain->pop(); + return checkedReturn(throwStackOverflowError(callFrame)); + } + + CallFrame* newCallFrame = CallFrame::create(m_registerFile.start() + globalRegisterOffset); + + ASSERT(codeBlock->m_numParameters == 1); // 1 parameter for 'this'. + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), codeBlock->m_numParameters, 0); + newCallFrame->uncheckedR(newCallFrame->hostThisRegister()) = JSValue(thisObj); + + if (codeBlock->needsFullScopeChain()) + scopeChain->ref(); + + 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 = new (callFrame) JSStaticScopeObject(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()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + // Cache miss: record Structure to compare against next time. + Structure* lastStructure = vPC[4].u.structure; + if (structure != lastStructure) { + // First miss: record Structure to compare against next time. + if (!lastStructure) { + vPC[4] = 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); + + vPC[0] = getOpcode(op_put_by_id_transition); + vPC[4] = structure->previousID(); + vPC[5] = structure; + vPC[6] = structure->prototypeChain(callFrame); + vPC[7] = slot.cachedOffset(); + codeBlock->refStructures(vPC); + return; + } + + vPC[0] = getOpcode(op_put_by_id_replace); + vPC[5] = slot.cachedOffset(); + codeBlock->refStructures(vPC); +} + +NEVER_INLINE void Interpreter::uncachePutByID(CodeBlock* codeBlock, Instruction* vPC) +{ + codeBlock->derefStructures(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; + } + + JSGlobalData* globalData = &callFrame->globalData(); + if (isJSArray(globalData, baseValue) && propertyName == callFrame->propertyNames().length) { + vPC[0] = getOpcode(op_get_array_length); + return; + } + + if (isJSString(globalData, 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()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + // Cache miss + Structure* lastStructure = vPC[4].u.structure; + if (structure != lastStructure) { + // First miss: record Structure to compare against next time. + if (!lastStructure) { + vPC[4] = 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; + } + + codeBlock->refStructures(vPC); + 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(); + offset = baseObject->structure()->get(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] = baseObject->structure(); + + codeBlock->refStructures(vPC); + 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] = structure; + vPC[5] = structure->prototypeChain(callFrame); + vPC[6] = count; + codeBlock->refStructures(vPC); +} + +NEVER_INLINE void Interpreter::uncacheGetByID(CodeBlock* codeBlock, Instruction* vPC) +{ + codeBlock->derefStructures(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(); + } + +#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 + + JSGlobalData* globalData = &callFrame->globalData(); + JSValue exceptionValue; + HandlerInfo* handler = 0; + + 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; + ArgList args(callFrame->registers() + firstArg, argCount); + callFrame->uncheckedR(dst) = JSValue(constructArray(callFrame, args)); + + 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; + int regExp = vPC[2].u.operand; + callFrame->uncheckedR(dst) = JSValue(new (globalData) RegExpObject(callFrame->lexicalGlobalObject(), callFrame->scopeChain()->globalObject->regExpStructure(), codeBlock->regexp(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(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(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + + vPC += OPCODE_LENGTH(op_lesseq); + 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 { + JSValue number = callFrame->r(srcDst).jsValue().toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = jsNumber(number.uncheckedGetNumber() + 1); + callFrame->uncheckedR(dst) = 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 { + JSValue number = callFrame->r(srcDst).jsValue().toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(srcDst) = jsNumber(number.uncheckedGetNumber() - 1); + callFrame->uncheckedR(dst) = 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 { + JSValue result = srcVal.toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(dst) = result; + } + + 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)->hasInstance(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); + 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) = JSValue(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(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(iter != end); + } + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast(*iter); + callFrame->uncheckedR(dst) = scope->registerAt(index); + 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(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(iter != end); + } + + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast(*iter); + ASSERT(callFrame->r(value).jsValue()); + scope->registerAt(index) = JSValue(callFrame->r(value).jsValue()); + vPC += OPCODE_LENGTH(op_put_scoped_var); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_base) { + /* resolve_base dst(r) property(id) + + Searches the scope chain for an object containing + identifier property, and if one is found, writes it to + register dst. If none is found, the outermost scope (which + will be the global object) is stored in register dst. + */ + resolveBase(callFrame, vPC); + + 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; + } + 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_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; + + 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; + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure; + + 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; + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure; + + 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->getCallData(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; + + if (LIKELY(baseCell->structure() == structure)) { + ASSERT(structure->prototypeForLookup(callFrame).isObject()); + JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); + Structure* prototypeStructure = vPC[5].u.structure; + + 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 + DEFINE_OPCODE(op_get_by_id_self_list) { + // Polymorphic self access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_self_list); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_proto_list) { + // Polymorphic prototype access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_proto_list); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_getter_self_list) { + // Polymorphic self access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_self_list); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_getter_proto_list) { + // Polymorphic prototype access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_proto_list); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_custom_self_list) { + // Polymorphic self access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_custom_self_list); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_by_id_custom_proto_list) { + // Polymorphic prototype access caching currently only supported when JITting. + ASSERT_NOT_REACHED(); + // This case of the switch must not be empty, else (op_get_by_id_proto_list == op_get_by_id_chain)! + vPC += OPCODE_LENGTH(op_get_by_id_proto_list); + NEXT_INSTRUCTION(); + } + 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; + + if (LIKELY(baseCell->structure() == structure)) { + RefPtr* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + RefPtr* 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) + 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; + + 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->getCallData(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; + + 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; + + if (LIKELY(baseCell->structure() == structure)) { + RefPtr* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + RefPtr* 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->getCallData(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; + + if (LIKELY(baseCell->structure() == structure)) { + RefPtr* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + RefPtr* 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: +#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(globalData, 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(); + } + 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(globalData, 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(); + } + 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); + ASSERT(slot.base() == baseValue); + } 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(); + } + 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; + Structure* newStructure = vPC[5].u.structure; + + if (LIKELY(baseCell->structure() == oldStructure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + int direct = vPC[8].u.operand; + + if (!direct) { + RefPtr* 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(newStructure); + + int value = vPC[3].u.operand; + unsigned offset = vPC[7].u.operand; + ASSERT(baseObject->offsetForLocation(baseObject->getDirectLocation(codeBlock->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(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; + + 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(codeBlock->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(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); + ASSERT(slot.base() == baseValue); + } 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->deleteProperty(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()) { + unsigned arg = subscript.asUInt32() + 1; + unsigned numParameters = callFrame->codeBlock()->m_numParameters; + if (arg < numParameters) + callFrame->uncheckedR(dst) = callFrame->r(arg - RegisterFile::CallFrameHeaderSize - numParameters); + else + callFrame->uncheckedR(dst) = callFrame->r(arg - RegisterFile::CallFrameHeaderSize - numParameters - callFrame->argumentCount() - 1); + vPC += OPCODE_LENGTH(op_get_argument_by_val); + NEXT_INSTRUCTION(); + } + if (!arguments) { + Arguments* arguments = new (globalData) Arguments(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(globalData, baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canGetIndex(i)) + result = jsArray->getIndex(i); + else + result = jsArray->JSArray::get(callFrame, i); + } else if (isJSString(globalData, baseValue) && asString(baseValue)->canGetIndex(i)) + result = asString(baseValue)->getIndex(callFrame, i); + else if (isJSByteArray(globalData, 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(globalData, baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canSetIndex(i)) + jsArray->setIndex(i, callFrame->r(value).jsValue()); + else + jsArray->JSArray::put(callFrame, i, callFrame->r(value).jsValue()); + } else if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { + JSByteArray* jsByteArray = asByteArray(baseValue); + double dValue = 0; + JSValue jsValue = callFrame->r(value).jsValue(); + if (jsValue.isInt32()) + jsByteArray->setIndex(i, jsValue.asInt32()); + else if (jsValue.getNumber(dValue)) + jsByteArray->setIndex(i, dValue); + 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->deleteProperty(callFrame, i); + else { + CHECK_FOR_EXCEPTION(); + Identifier property(callFrame, subscript.toString(callFrame)); + CHECK_FOR_EXCEPTION(); + result = baseObj->deleteProperty(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_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; + JSValue ptr = JSValue(vPC[2].u.jsCell); + int target = vPC[3].u.operand; + JSValue srcValue = callFrame->r(src).jsValue(); + if (srcValue != ptr) { + 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(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(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_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(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jnless); + 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(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jless); + 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(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jnlesseq); + 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(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jlesseq); + 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 { + double value; + int32_t intValue; + if (scrutinee.getNumber(value) && ((intValue = static_cast(value)) == value)) + vPC += codeBlock->immediateSwitchJumpTable(tableIndex).offsetForValue(intValue, 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->characters()[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 = new (callFrame) JSStaticScopeObject(callFrame, function->name(), func, ReadOnly | DontDelete); + 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(); + + Register* newCallFrame = callFrame->registers() + registerOffset; + Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount; + JSValue thisValue = argv[0].jsValue(); + JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject; + + if (thisValue == globalObject && funcVal == globalObject->evalFunction()) { + JSValue result = callEval(callFrame, registerFile, argv, argCount, registerOffset); + 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()); + 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); + if (!registerFile->grow(newCallFrame->registers())) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call), scopeChain, callFrame, argCount, asObject(v)); + + JSValue returnValue; + { + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(callData.native.function(newCallFrame)); + } + 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_load_varargs) { + int argCountDst = vPC[1].u.operand; + int argsOffset = vPC[2].u.operand; + + JSValue arguments = callFrame->r(argsOffset).jsValue(); + uint32_t argCount = 0; + if (!arguments) { + argCount = (uint32_t)(callFrame->argumentCount()); + argCount = min(argCount, Arguments::MaxArguments); + int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; + Register* newEnd = callFrame->registers() + sizeDelta; + if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + ASSERT(!asFunction(callFrame->callee())->isHostFunction()); + int32_t expectedParams = asFunction(callFrame->callee())->jsExecutable()->parameterCount(); + int32_t inplaceArgs = min(static_cast(argCount), expectedParams); + int32_t i = 0; + Register* argStore = callFrame->registers() + argsOffset; + + // First step is to copy the "expected" parameters from their normal location relative to the callframe + for (; i < inplaceArgs; i++) + argStore[i] = callFrame->registers()[i - RegisterFile::CallFrameHeaderSize - expectedParams]; + // Then we copy any additional arguments that may be further up the stack ('-1' to account for 'this') + for (; i < static_cast(argCount); i++) + argStore[i] = callFrame->registers()[i - RegisterFile::CallFrameHeaderSize - expectedParams - static_cast(argCount) - 1]; + } else if (!arguments.isUndefinedOrNull()) { + if (!arguments.isObject()) { + exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); + goto vm_throw; + } + if (asObject(arguments)->classInfo() == &Arguments::info) { + Arguments* args = asArguments(arguments); + argCount = args->numProvidedArguments(callFrame); + argCount = min(argCount, Arguments::MaxArguments); + int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; + Register* newEnd = callFrame->registers() + sizeDelta; + if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + args->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount); + } else if (isJSArray(&callFrame->globalData(), arguments)) { + JSArray* array = asArray(arguments); + argCount = array->length(); + argCount = min(argCount, Arguments::MaxArguments); + int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; + Register* newEnd = callFrame->registers() + sizeDelta; + if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + array->copyToRegisters(callFrame, callFrame->registers() + argsOffset, argCount); + } else if (asObject(arguments)->inherits(&JSArray::info)) { + JSObject* argObject = asObject(arguments); + argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + argCount = min(argCount, Arguments::MaxArguments); + int32_t sizeDelta = argsOffset + argCount + RegisterFile::CallFrameHeaderSize; + Register* newEnd = callFrame->registers() + sizeDelta; + if (!registerFile->grow(newEnd) || ((newEnd - callFrame->registers()) != sizeDelta)) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + Register* argsBuffer = callFrame->registers() + argsOffset; + for (uint32_t i = 0; i < argCount; ++i) { + argsBuffer[i] = asObject(arguments)->get(callFrame, i); + CHECK_FOR_EXCEPTION(); + } + } else { + exceptionValue = createInvalidParamError(callFrame, "Function.prototype.apply", arguments); + goto vm_throw; + } + } + CHECK_FOR_EXCEPTION(); + callFrame->uncheckedR(argCountDst) = Register::withInt(argCount + 1); + vPC += OPCODE_LENGTH(op_load_varargs); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_call_varargs) { + /* call_varargs func(r) argCountReg(r) baseRegisterOffset(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. + */ + + int func = vPC[1].u.operand; + int argCountReg = vPC[2].u.operand; + int registerOffset = vPC[3].u.operand; + + JSValue v = callFrame->r(func).jsValue(); + int argCount = callFrame->r(argCountReg).i(); + registerOffset += argCount; + 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_varargs), callDataScopeChain, previousCallFrame, argCount, asFunction(v)); + codeBlock = newCodeBlock; + ASSERT(codeBlock == callFrame->codeBlock()); + 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); + if (!registerFile->grow(newCallFrame->registers())) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_call_varargs), scopeChain, callFrame, argCount, asObject(v)); + + JSValue returnValue; + { + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(callData.native.function(newCallFrame)); + } + 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)->copyRegisters(); + + if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { + if (!codeBlock->isStrictMode()) + asArguments(argumentsValue)->setActivation(asActivation(activationValue)); + } + } else if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue()) { + if (!codeBlock->isStrictMode()) + asArguments(argumentsValue)->copyRegisters(); + } + + 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)->copyRegisters(); + + 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; + + if (callFrame->codeBlock()->needsFullScopeChain() && callFrame->r(codeBlock->activationRegister()).jsValue()) + callFrame->scopeChain()->deref(); + + JSValue returnValue = callFrame->r(result).jsValue(); + + vPC = callFrame->returnVPC(); + callFrame = callFrame->callerFrame(); + + if (callFrame->hasHostCallFrameFlag()) + return returnValue; + + 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; + + if (codeBlock->needsFullScopeChain() && callFrame->r(codeBlock->activationRegister()).jsValue()) + callFrame->scopeChain()->deref(); + + 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; + + 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 = new (globalData) JSActivation(callFrame, static_cast(codeBlock->ownerExecutable())); + callFrame->r(activationReg) = JSValue(activation); + callFrame->setScopeChain(callFrame->scopeChain()->copy()->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->getConstructData(constructData) == ConstructTypeJS); +#endif + + Structure* structure; + JSValue proto = callFrame->r(protoRegister).jsValue(); + if (proto.isObject()) + structure = asObject(proto)->inheritorID(); + else + structure = constructor->scope().node()->globalObject->emptyObjectStructure(); + callFrame->uncheckedR(thisRegister) = JSValue(new (&callFrame->globalData()) JSObject(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.needsThisConversion()) + callFrame->uncheckedR(thisRegister) = JSValue(thisVal.toThisObject(callFrame)); + + vPC += OPCODE_LENGTH(op_convert_this); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_convert_this_strict) { + /* convert_this_strict this(r) + + Takes the value in the 'this' register, and converts it to + its "this" form if (and only if) "this" is an object with a + custom this conversion + + 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.isObject() && thisVal.needsThisConversion()) + callFrame->uncheckedR(thisRegister) = JSValue(thisVal.toStrictThisObject(callFrame)); + + vPC += OPCODE_LENGTH(op_convert_this_strict); + 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 = new (globalData) Arguments(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; + 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); + if (!registerFile->grow(newCallFrame->registers())) { + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + newCallFrame->init(0, vPC + OPCODE_LENGTH(op_construct), scopeChain, callFrame, argCount, asObject(v)); + + JSValue returnValue; + { + SamplingTool::HostCallRecord callRecord(m_sampler.get()); + returnValue = JSValue::decode(constructData.native.function(newCallFrame)); + } + 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_throw_syntax_error) { + /* op_throw_syntax_error message(k) + + Constructs a new syntax 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(createSyntaxError(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. + */ + + if (codeBlock->needsFullScopeChain()) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ASSERT(scopeChain->refCount > 1); + scopeChain->deref(); + } + 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->defineGetter(callFrame, ident, asObject(callFrame->r(function).jsValue())); + + 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->defineSetter(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), 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); + } + 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(); + } + } +#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(); + if (!functionCallFrame->r(argumentsRegister).jsValue()) { + JSValue arguments = JSValue(new (callFrame) Arguments(functionCallFrame)); + functionCallFrame->r(argumentsRegister) = arguments; + functionCallFrame->r(unmodifiedArgumentsRegister(argumentsRegister)) = arguments; + } + return functionCallFrame->r(argumentsRegister).jsValue(); + } + + Arguments* arguments = new (functionCallFrame) Arguments(functionCallFrame); + arguments->copyRegisters(); + return 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->callerFrame()->removeHostCallFrameFlag()) { + if (candidate->callee() == function) + return candidate; + } + return 0; +} + +void Interpreter::enableSampler() +{ +#if ENABLE(OPCODE_SAMPLING) + if (!m_sampler) { + m_sampler.set(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 0000000..47790cf --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -0,0 +1,170 @@ +/* + * 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 "FastAllocBase.h" +#include "JSCell.h" +#include "JSValue.h" +#include "JSObject.h" +#include "Opcode.h" +#include "RegisterFile.h" + +#include + +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 + }; + + enum { MaxLargeThreadReentryDepth = 256, MaxSmallThreadReentryDepth = 32 }; + + class Interpreter : public FastAllocBase { + friend class JIT; + friend class CachedCall; + public: + Interpreter(); + + RegisterFile& registerFile() { return m_registerFile; } + + Opcode getOpcode(OpcodeID id) + { + #if ENABLE(COMPUTED_GOTO_INTERPRETER) + return m_opcodeTable[id]; + #else + return id; + #endif + } + + OpcodeID getOpcodeID(Opcode opcode) + { + #if ENABLE(COMPUTED_GOTO_INTERPRETER) + ASSERT(isOpcode(opcode)); + 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* evalNode, CallFrame* exec, JSObject* thisObj, ScopeChainNode* scopeChain); + + JSValue retrieveArguments(CallFrame*, JSFunction*) const; + JSValue retrieveCaller(CallFrame*, JSFunction*) const; + 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 JSValue callEval(CallFrame*, RegisterFile*, Register* argv, int argc, int registerOffset); + 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 argCount, ScopeChainNode*); + void endRepeatCall(CallFrameClosure&); + JSValue execute(CallFrameClosure&); + + JSValue execute(EvalExecutable*, CallFrame*, JSObject* thisObject, int globalRegisterOffset, ScopeChainNode*); + +#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 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 m_sampler; + + int m_reentryDepth; + + RegisterFile m_registerFile; + +#if ENABLE(COMPUTED_GOTO_INTERPRETER) + Opcode m_opcodeTable[numOpcodeIDs]; // Maps OpcodeID => Opcode for compiling + HashMap m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling +#endif + }; + +} // namespace JSC + +#endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h new file mode 100644 index 0000000..38d1647 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/Register.h @@ -0,0 +1,218 @@ +/* + * 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 +#include +#include + +namespace JSC { + + class CodeBlock; + class ExecState; + class JSActivation; + class JSObject; + class JSPropertyNameIterator; + class ScopeChainNode; + + struct Instruction; + + typedef ExecState CallFrame; + + class Register : public WTF::FastAllocBase { + public: + Register(); + + Register(const JSValue&); + Register& operator=(const JSValue&); + JSValue jsValue() const; + + Register& operator=(JSActivation*); + Register& operator=(CallFrame*); + Register& operator=(CodeBlock*); + Register& operator=(JSPropertyNameIterator*); + Register& operator=(ScopeChainNode*); + Register& operator=(Instruction*); + + 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; + + static Register withInt(int32_t i) + { + Register r; + r.u.i = i; + return r; + } + + static Register withCallee(JSObject* callee) + { + Register r; + r.u.function = callee; + return r; + } + + private: + union { + int32_t i; + EncodedJSValue value; + + JSActivation* activation; + CallFrame* callFrame; + CodeBlock* codeBlock; + JSObject* function; + JSPropertyNameIterator* propertyNameIterator; + ScopeChainNode* scopeChain; + Instruction* vPC; + } u; + }; + + ALWAYS_INLINE Register::Register() + { +#ifndef NDEBUG + *this = JSValue(); +#endif + } + + ALWAYS_INLINE Register::Register(const JSValue& v) + { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + u.value = JSValue::encode(v); + } + + ALWAYS_INLINE Register& Register::operator=(const JSValue& v) + { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + u.value = JSValue::encode(v); + return *this; + } + + ALWAYS_INLINE JSValue Register::jsValue() const + { + return JSValue::decode(u.value); + } + + // Interpreter functions + + ALWAYS_INLINE Register& Register::operator=(JSActivation* activation) + { + u.activation = activation; + return *this; + } + + 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=(ScopeChainNode* scopeChain) + { + u.scopeChain = scopeChain; + return *this; + } + + ALWAYS_INLINE Register& Register::operator=(JSPropertyNameIterator* propertyNameIterator) + { + u.propertyNameIterator = propertyNameIterator; + return *this; + } + + ALWAYS_INLINE int32_t Register::i() const + { + return u.i; + } + + ALWAYS_INLINE JSActivation* Register::activation() const + { + return u.activation; + } + + ALWAYS_INLINE CallFrame* Register::callFrame() const + { + return u.callFrame; + } + + ALWAYS_INLINE CodeBlock* Register::codeBlock() const + { + return u.codeBlock; + } + + ALWAYS_INLINE JSObject* Register::function() const + { + return u.function; + } + + ALWAYS_INLINE JSPropertyNameIterator* Register::propertyNameIterator() const + { + return u.propertyNameIterator; + } + + ALWAYS_INLINE ScopeChainNode* Register::scopeChain() const + { + return u.scopeChain; + } + + ALWAYS_INLINE Instruction* Register::vPC() const + { + return u.vPC; + } + +} // namespace JSC + +namespace WTF { + + template<> struct VectorTraits : VectorTraitsBase { }; + +} // namespace WTF + +#endif // Register_h diff --git a/Source/JavaScriptCore/interpreter/RegisterFile.cpp b/Source/JavaScriptCore/interpreter/RegisterFile.cpp new file mode 100644 index 0000000..4c37676 --- /dev/null +++ b/Source/JavaScriptCore/interpreter/RegisterFile.cpp @@ -0,0 +1,93 @@ +/* + * 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 "JSGlobalObject.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(m_commitEnd) - reinterpret_cast(base)); + addToCommittedByteCount(-(reinterpret_cast(m_commitEnd) - reinterpret_cast(base))); + m_reservation.deallocate(); +} + +void RegisterFile::releaseExcessCapacity() +{ + m_reservation.decommit(m_start, reinterpret_cast(m_commitEnd) - reinterpret_cast(m_start)); + addToCommittedByteCount(-(reinterpret_cast(m_commitEnd) - reinterpret_cast(m_start))); + m_commitEnd = m_start; + m_maxUsed = m_start; +} + +void RegisterFile::setGlobalObject(JSGlobalObject* globalObject) +{ + m_globalObject = globalObject; +} + +bool RegisterFile::clearGlobalObject(JSGlobalObject* globalObject) +{ + return m_globalObject.clear(globalObject); +} + +JSGlobalObject* RegisterFile::globalObject() +{ + return m_globalObject.get(); +} + +void RegisterFile::initializeThreading() +{ + registerFileStatisticsMutex(); +} + +size_t RegisterFile::committedByteCount() +{ + MutexLocker locker(registerFileStatisticsMutex()); + return committedBytesCount; +} + +void RegisterFile::addToCommittedByteCount(long byteCount) +{ + MutexLocker locker(registerFileStatisticsMutex()); + ASSERT(static_cast(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 0000000..51a5bdf --- /dev/null +++ b/Source/JavaScriptCore/interpreter/RegisterFile.h @@ -0,0 +1,211 @@ +/* + * 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 "Collector.h" +#include "ExecutableAllocator.h" +#include "Register.h" +#include "WeakGCPtr.h" +#include +#include +#include +#include + +namespace JSC { + +/* + A register file is a stack of register frames. We represent a register + frame by its offset from "base", the logical first entry in the register + file. The bottom-most register frame's offset from base is 0. + + In a program where function "a" calls function "b" (global code -> a -> b), + the register file might look like this: + + | global frame | call frame | call frame | spare capacity | + ----------------------------------------------------------------------------------------------------- + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | | | | | | <-- index in buffer + ----------------------------------------------------------------------------------------------------- + | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | | | | | <-- index relative to base + ----------------------------------------------------------------------------------------------------- + | <-globals | temps-> | <-vars | temps-> | <-vars | + ^ ^ ^ ^ + | | | | + buffer base (frame 0) frame 1 frame 2 + + Since all variables, including globals, are accessed by negative offsets + from their register frame pointers, to keep old global offsets correct, new + globals must appear at the beginning of the register file, shifting base + to the right. + + If we added one global variable to the register file depicted above, it + would look like this: + + | global frame |< > + -------------------------------> < + | 0 | 1 | 2 | 3 | 4 | 5 |< >snip< > <-- index in buffer + -------------------------------> < + | -4 | -3 | -2 | -1 | 0 | 1 |< > <-- index relative to base + -------------------------------> < + | <-globals | temps-> | + ^ ^ + | | + buffer base (frame 0) + + As you can see, global offsets relative to base have stayed constant, + but base itself has moved. To keep up with possible changes to base, + clients keep an indirect pointer, so their calculations update + automatically when base changes. + + For client simplicity, the RegisterFile measures size and capacity from + "base", not "buffer". +*/ + + class JSGlobalObject; + + class RegisterFile : public Noncopyable { + friend class JIT; + 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, + }; + + enum { ProgramCodeThisRegister = -CallFrameHeaderSize - 1 }; + + static const size_t defaultCapacity = 512 * 1024; + static const size_t defaultMaxGlobals = 8 * 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, size_t maxGlobals = defaultMaxGlobals); + ~RegisterFile(); + + Register* start() const { return m_start; } + Register* end() const { return m_end; } + size_t size() const { return m_end - m_start; } + + void setGlobalObject(JSGlobalObject*); + bool clearGlobalObject(JSGlobalObject*); + JSGlobalObject* globalObject(); + + bool grow(Register* newEnd); + void shrink(Register* newEnd); + + void setNumGlobals(size_t numGlobals) { m_numGlobals = numGlobals; } + int numGlobals() const { return m_numGlobals; } + size_t maxGlobals() const { return m_maxGlobals; } + + Register* lastGlobal() const { return m_start - m_numGlobals; } + + void markGlobals(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, lastGlobal(), m_start); } + void markCallFrames(MarkStack& markStack, Heap* heap) { heap->markConservatively(markStack, m_start, m_end); } + + static size_t committedByteCount(); + static void initializeThreading(); + + private: + void releaseExcessCapacity(); + void addToCommittedByteCount(long); + size_t m_numGlobals; + const size_t m_maxGlobals; + Register* m_start; + Register* m_end; + Register* m_max; + Register* m_maxUsed; + Register* m_commitEnd; + PageReservation m_reservation; + + WeakGCPtr m_globalObject; // The global object whose vars are currently stored in the register file. + }; + + inline RegisterFile::RegisterFile(size_t capacity, size_t maxGlobals) + : m_numGlobals(0) + , m_maxGlobals(maxGlobals) + , m_start(0) + , m_end(0) + , m_max(0) + { + ASSERT(maxGlobals && isPageAligned(maxGlobals)); + ASSERT(capacity && isPageAligned(capacity)); + + size_t bufferLength = (capacity + maxGlobals) * sizeof(Register); + m_reservation = PageReservation::reserve(roundUpAllocationSize(bufferLength, commitSize), OSAllocator::JSVMStackPages); + void* base = m_reservation.base(); + size_t committedSize = roundUpAllocationSize(maxGlobals * sizeof(Register), commitSize); + m_reservation.commit(base, committedSize); + addToCommittedByteCount(static_cast(committedSize)); + m_commitEnd = reinterpret_cast_ptr(reinterpret_cast(base) + committedSize); + m_start = static_cast(base) + maxGlobals; + m_end = m_start; + m_maxUsed = m_end; + m_max = m_start + capacity; + } + + inline void RegisterFile::shrink(Register* newEnd) + { + if (newEnd >= m_end) + return; + m_end = newEnd; + if (m_end == m_start && (m_maxUsed - m_start) > maxExcessCapacity) + releaseExcessCapacity(); + } + + inline bool RegisterFile::grow(Register* newEnd) + { + if (newEnd < m_end) + return true; + + if (newEnd > m_max) + return false; + + if (newEnd > m_commitEnd) { + size_t size = roundUpAllocationSize(reinterpret_cast(newEnd) - reinterpret_cast(m_commitEnd), commitSize); + m_reservation.commit(m_commitEnd, size); + addToCommittedByteCount(static_cast(size)); + m_commitEnd = reinterpret_cast_ptr(reinterpret_cast(m_commitEnd) + size); + } + + if (newEnd > m_maxUsed) + m_maxUsed = newEnd; + + m_end = newEnd; + return true; + } + +} // namespace JSC + +#endif // RegisterFile_h -- cgit v1.1