diff options
Diffstat (limited to 'src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp')
-rw-r--r-- | src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp | 6108 |
1 files changed, 6108 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp b/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp new file mode 100644 index 0000000..0f9ea01 --- /dev/null +++ b/src/3rdparty/webkit/JavaScriptCore/interpreter/Interpreter.cpp @@ -0,0 +1,6108 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Interpreter.h" + +#include "Arguments.h" +#include "BatchedTransitionOptimizer.h" +#include "CodeBlock.h" +#include "DebuggerCallFrame.h" +#include "EvalCodeCache.h" +#include "ExceptionHelpers.h" +#include "CallFrame.h" +#include "GlobalEvalFunction.h" +#include "JSActivation.h" +#include "JSArray.h" +#include "JSByteArray.h" +#include "JSFunction.h" +#include "JSNotAnObject.h" +#include "JSPropertyNameIterator.h" +#include "JSStaticScopeObject.h" +#include "JSString.h" +#include "ObjectPrototype.h" +#include "Parser.h" +#include "Profiler.h" +#include "RegExpObject.h" +#include "RegExpPrototype.h" +#include "Register.h" +#include "Collector.h" +#include "Debugger.h" +#include "Operations.h" +#include "SamplingTool.h" +#include <stdio.h> + +#if ENABLE(JIT) +#include "JIT.h" +#endif + +#if ENABLE(ASSEMBLER) +#include "AssemblerBuffer.h" +#endif + +#if PLATFORM(DARWIN) +#include <mach/mach.h> +#endif + +#if HAVE(SYS_TIME_H) +#include <sys/time.h> +#endif + +#if PLATFORM(WIN_OS) +#include <windows.h> +#endif + +#if PLATFORM(QT) +#include <QDateTime> +#endif + +using namespace std; + +namespace JSC { + +// Preferred number of milliseconds between each timeout check +static const int preferredScriptCheckTimeInterval = 1000; + +static ALWAYS_INLINE unsigned bytecodeOffsetForPC(CodeBlock* codeBlock, void* pc) +{ +#if ENABLE(JIT) + return codeBlock->getBytecodeIndex(pc); +#else + return static_cast<Instruction*>(pc) - codeBlock->instructions().begin(); +#endif +} + +// 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(); +} + +// FIXME: This operation should be called "getNumber", not "isNumber" (as it is in JSValue.h). +// FIXME: There's no need to have a "slow" version of this. All versions should be fast. +static ALWAYS_INLINE bool fastIsNumber(JSValuePtr value, double& arg) +{ + if (JSImmediate::isNumber(value)) + arg = JSImmediate::getTruncatedInt32(value); + else if (LIKELY(!JSImmediate::isImmediate(value)) && LIKELY(Heap::isNumber(asCell(value)))) + arg = asNumberCell(value)->value(); + else + return false; + return true; +} + +// FIXME: Why doesn't JSValuePtr::toInt32 have the Heap::isNumber optimization? +static bool fastToInt32(JSValuePtr value, int32_t& arg) +{ + if (JSImmediate::isNumber(value)) + arg = JSImmediate::getTruncatedInt32(value); + else if (LIKELY(!JSImmediate::isImmediate(value)) && LIKELY(Heap::isNumber(asCell(value)))) + arg = asNumberCell(value)->toInt32(); + else + return false; + return true; +} + +static ALWAYS_INLINE bool fastToUInt32(JSValuePtr value, uint32_t& arg) +{ + if (JSImmediate::isNumber(value)) { + if (JSImmediate::getTruncatedUInt32(value, arg)) + return true; + bool scratch; + arg = toUInt32SlowCase(JSImmediate::getTruncatedInt32(value), scratch); + return true; + } else if (!JSImmediate::isImmediate(value) && Heap::isNumber(asCell(value))) + arg = asNumberCell(value)->toUInt32(); + else + return false; + return true; +} + +static inline bool jsLess(CallFrame* callFrame, JSValuePtr v1, JSValuePtr v2) +{ + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return JSImmediate::getTruncatedInt32(v1) < JSImmediate::getTruncatedInt32(v2); + + double n1; + double n2; + if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2)) + return n1 < n2; + + Interpreter* interpreter = callFrame->interpreter(); + if (interpreter->isJSString(v1) && interpreter->isJSString(v2)) + return asString(v1)->value() < asString(v2)->value(); + + JSValuePtr p1; + JSValuePtr p2; + bool wasNotString1 = v1->getPrimitiveNumber(callFrame, n1, p1); + bool wasNotString2 = v2->getPrimitiveNumber(callFrame, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 < n2; + + return asString(p1)->value() < asString(p2)->value(); +} + +static inline bool jsLessEq(CallFrame* callFrame, JSValuePtr v1, JSValuePtr v2) +{ + if (JSImmediate::areBothImmediateNumbers(v1, v2)) + return JSImmediate::getTruncatedInt32(v1) <= JSImmediate::getTruncatedInt32(v2); + + double n1; + double n2; + if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2)) + return n1 <= n2; + + Interpreter* interpreter = callFrame->interpreter(); + if (interpreter->isJSString(v1) && interpreter->isJSString(v2)) + return !(asString(v2)->value() < asString(v1)->value()); + + JSValuePtr p1; + JSValuePtr p2; + bool wasNotString1 = v1->getPrimitiveNumber(callFrame, n1, p1); + bool wasNotString2 = v2->getPrimitiveNumber(callFrame, n2, p2); + + if (wasNotString1 | wasNotString2) + return n1 <= n2; + + return !(asString(p2)->value() < asString(p1)->value()); +} + +static NEVER_INLINE JSValuePtr jsAddSlowCase(CallFrame* callFrame, JSValuePtr v1, JSValuePtr v2) +{ + // exception for the Date exception in defaultValue() + JSValuePtr p1 = v1->toPrimitive(callFrame); + JSValuePtr p2 = v2->toPrimitive(callFrame); + + if (p1->isString() || p2->isString()) { + RefPtr<UString::Rep> value = concatenate(p1->toString(callFrame).rep(), p2->toString(callFrame).rep()); + if (!value) + return throwOutOfMemoryError(callFrame); + return jsString(callFrame, value.release()); + } + + return jsNumber(callFrame, p1->toNumber(callFrame) + p2->toNumber(callFrame)); +} + +// Fast-path choices here are based on frequency data from SunSpider: +// <times> Add case: <t1> <t2> +// --------------------------- +// 5626160 Add case: 3 3 (of these, 3637690 are for immediate values) +// 247412 Add case: 5 5 +// 20900 Add case: 5 6 +// 13962 Add case: 5 3 +// 4000 Add case: 3 5 + +static ALWAYS_INLINE JSValuePtr jsAdd(CallFrame* callFrame, JSValuePtr v1, JSValuePtr v2) +{ + double left; + double right = 0.0; + + bool rightIsNumber = fastIsNumber(v2, right); + if (rightIsNumber && fastIsNumber(v1, left)) + return jsNumber(callFrame, left + right); + + bool leftIsString = v1->isString(); + if (leftIsString && v2->isString()) { + RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); + if (!value) + return throwOutOfMemoryError(callFrame); + return jsString(callFrame, value.release()); + } + + if (rightIsNumber & leftIsString) { + RefPtr<UString::Rep> value = JSImmediate::isImmediate(v2) ? + concatenate(asString(v1)->value().rep(), JSImmediate::getTruncatedInt32(v2)) : + concatenate(asString(v1)->value().rep(), right); + + if (!value) + return throwOutOfMemoryError(callFrame); + return jsString(callFrame, value.release()); + } + + // All other cases are pretty uncommon + return jsAddSlowCase(callFrame, v1, v2); +} + +static JSValuePtr jsTypeStringForValue(CallFrame* callFrame, JSValuePtr v) +{ + if (v->isUndefined()) + return jsNontrivialString(callFrame, "undefined"); + if (v->isBoolean()) + return jsNontrivialString(callFrame, "boolean"); + if (v->isNumber()) + return jsNontrivialString(callFrame, "number"); + if (v->isString()) + return jsNontrivialString(callFrame, "string"); + if (v->isObject()) { + // Return "undefined" for objects that should be treated + // as null when doing comparisons. + if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined()) + return jsNontrivialString(callFrame, "undefined"); + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return jsNontrivialString(callFrame, "function"); + } + return jsNontrivialString(callFrame, "object"); +} + +static bool jsIsObjectType(JSValuePtr v) +{ + if (JSImmediate::isImmediate(v)) + return v->isNull(); + + JSType type = asCell(v)->structure()->typeInfo().type(); + if (type == NumberType || type == StringType) + return false; + if (type == ObjectType) { + if (asObject(v)->structure()->typeInfo().masqueradesAsUndefined()) + return false; + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return false; + } + return true; +} + +static bool jsIsFunctionType(JSValuePtr v) +{ + if (v->isObject()) { + CallData callData; + if (asObject(v)->getCallData(callData) != CallTypeNone) + return true; + } + return false; +} + +NEVER_INLINE bool Interpreter::resolve(CallFrame* callFrame, Instruction* vPC, JSValuePtr& 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)) { + JSValuePtr result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame[dst] = JSValuePtr(result); + return true; + } + } while (++iter != end); + exceptionValue = createUndefinedVariableError(callFrame, ident, vPC - codeBlock->instructions().begin(), codeBlock); + return false; +} + +NEVER_INLINE bool Interpreter::resolveSkip(CallFrame* callFrame, Instruction* vPC, JSValuePtr& exceptionValue) +{ + CodeBlock* codeBlock = callFrame->codeBlock(); + + int dst = (vPC + 1)->u.operand; + int property = (vPC + 2)->u.operand; + int skip = (vPC + 3)->u.operand + codeBlock->needsFullScopeChain(); + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + while (skip--) { + ++iter; + ASSERT(iter != end); + } + Identifier& ident = codeBlock->identifier(property); + do { + JSObject* o = *iter; + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame[dst] = JSValuePtr(result); + return true; + } + } while (++iter != end); + exceptionValue = createUndefinedVariableError(callFrame, ident, vPC - codeBlock->instructions().begin(), codeBlock); + return false; +} + +NEVER_INLINE bool Interpreter::resolveGlobal(CallFrame* callFrame, Instruction* vPC, JSValuePtr& exceptionValue) +{ + int dst = (vPC + 1)->u.operand; + JSGlobalObject* globalObject = static_cast<JSGlobalObject*>((vPC + 2)->u.jsCell); + ASSERT(globalObject->isGlobalObject()); + int property = (vPC + 3)->u.operand; + Structure* structure = (vPC + 4)->u.structure; + int offset = (vPC + 5)->u.operand; + + if (structure == globalObject->structure()) { + callFrame[dst] = JSValuePtr(globalObject->getDirectOffset(offset)); + return true; + } + + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& ident = codeBlock->identifier(property); + PropertySlot slot(globalObject); + if (globalObject->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + if (slot.isCacheable() && !globalObject->structure()->isDictionary()) { + if (vPC[4].u.structure) + vPC[4].u.structure->deref(); + globalObject->structure()->ref(); + vPC[4] = globalObject->structure(); + vPC[5] = slot.cachedOffset(); + callFrame[dst] = JSValuePtr(result); + return true; + } + + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame[dst] = JSValuePtr(result); + return true; + } + + exceptionValue = createUndefinedVariableError(callFrame, ident, vPC - codeBlock->instructions().begin(), codeBlock); + return false; +} + +static ALWAYS_INLINE JSValuePtr inlineResolveBase(CallFrame* callFrame, Identifier& property, ScopeChainNode* scopeChain) +{ + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator next = iter; + ++next; + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + + PropertySlot slot; + JSObject* base; + while (true) { + base = *iter; + if (next == end || base->getPropertySlot(callFrame, property, slot)) + return base; + + iter = next; + ++next; + } + + ASSERT_NOT_REACHED(); + return noValue(); +} + +NEVER_INLINE void Interpreter::resolveBase(CallFrame* callFrame, Instruction* vPC) +{ + int dst = (vPC + 1)->u.operand; + int property = (vPC + 2)->u.operand; + callFrame[dst] = JSValuePtr(inlineResolveBase(callFrame, callFrame->codeBlock()->identifier(property), callFrame->scopeChain())); +} + +NEVER_INLINE bool Interpreter::resolveBaseAndProperty(CallFrame* callFrame, Instruction* vPC, JSValuePtr& 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)) { + JSValuePtr result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + callFrame[propDst] = JSValuePtr(result); + callFrame[baseDst] = JSValuePtr(base); + return true; + } + ++iter; + } while (iter != end); + + exceptionValue = createUndefinedVariableError(callFrame, ident, vPC - codeBlock->instructions().begin(), codeBlock); + return false; +} + +NEVER_INLINE bool Interpreter::resolveBaseAndFunc(CallFrame* callFrame, Instruction* vPC, JSValuePtr& exceptionValue) +{ + int baseDst = (vPC + 1)->u.operand; + int funcDst = (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)) { + // ECMA 11.2.3 says that if we hit an activation the this value should be null. + // However, section 10.2.3 says that in the case where the value provided + // by the caller is null, the global object should be used. It also says + // that the section does not apply to internal functions, but for simplicity + // of implementation we use the global object anyway here. This guarantees + // that in host objects you always get a valid object for this. + // We also handle wrapper substitution for the global object at the same time. + JSObject* thisObj = base->toThisObject(callFrame); + JSValuePtr result = slot.getValue(callFrame, ident); + exceptionValue = callFrame->globalData().exception; + if (exceptionValue) + return false; + + callFrame[baseDst] = JSValuePtr(thisObj); + callFrame[funcDst] = JSValuePtr(result); + return true; + } + ++iter; + } while (iter != end); + + exceptionValue = createUndefinedVariableError(callFrame, ident, vPC - codeBlock->instructions().begin(), codeBlock); + return false; +} + +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); +} + +static NEVER_INLINE bool isNotObject(CallFrame* callFrame, bool forInstanceOf, CodeBlock* codeBlock, const Instruction* vPC, JSValuePtr value, JSValuePtr& exceptionData) +{ + if (value->isObject()) + return false; + exceptionData = createInvalidParamError(callFrame, forInstanceOf ? "instanceof" : "in" , value, vPC - codeBlock->instructions().begin(), codeBlock); + return true; +} + +NEVER_INLINE JSValuePtr Interpreter::callEval(CallFrame* callFrame, RegisterFile* registerFile, Register* argv, int argc, int registerOffset, JSValuePtr& exceptionValue) +{ + if (argc < 2) + return jsUndefined(); + + JSValuePtr program = argv[1].jsValue(callFrame); + + if (!program->isString()) + return program; + + UString programSource = asString(program)->value(); + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + CodeBlock* codeBlock = callFrame->codeBlock(); + RefPtr<EvalNode> evalNode = codeBlock->evalCodeCache().get(callFrame, programSource, scopeChain, exceptionValue); + + JSValuePtr result = jsUndefined(); + if (evalNode) + result = callFrame->globalData().interpreter->execute(evalNode.get(), callFrame, callFrame->thisValue()->toThisObject(callFrame), callFrame->registers() - registerFile->start() + registerOffset, scopeChain, &exceptionValue); + + return result; +} + +Interpreter::Interpreter() + : m_sampler(0) +#if ENABLE(JIT) + , m_ctiArrayLengthTrampoline(0) + , m_ctiStringLengthTrampoline(0) + , m_ctiVirtualCallPreLink(0) + , m_ctiVirtualCallLink(0) + , m_ctiVirtualCall(0) +#endif + , m_reentryDepth(0) + , m_timeoutTime(0) + , m_timeAtLastCheckTimeout(0) + , m_timeExecuting(0) + , m_timeoutCheckCount(0) + , m_ticksUntilNextTimeoutCheck(initialTickCountThreshold) +{ + initTimeout(); + privateExecute(InitializeAndReturn, 0, 0, 0); + + // Bizarrely, calling fastMalloc here is faster than allocating space on the stack. + void* storage = fastMalloc(sizeof(CollectorBlock)); + + JSCell* jsArray = new (storage) JSArray(JSArray::createStructure(jsNull())); + m_jsArrayVptr = jsArray->vptr(); + jsArray->~JSCell(); + + JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack); + m_jsByteArrayVptr = jsByteArray->vptr(); + jsByteArray->~JSCell(); + + JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack); + m_jsStringVptr = jsString->vptr(); + jsString->~JSCell(); + + JSCell* jsFunction = new (storage) JSFunction(JSFunction::createStructure(jsNull())); + m_jsFunctionVptr = jsFunction->vptr(); + jsFunction->~JSCell(); + + fastFree(storage); +} + +void Interpreter::initialize(JSGlobalData* globalData) +{ +#if ENABLE(JIT) + JIT::compileCTIMachineTrampolines(globalData); +#else + UNUSED_PARAM(globalData); +#endif +} + +Interpreter::~Interpreter() +{ +} + +#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; + + if (codeBlock->codeType() == GlobalCode) { + it = registerFile->lastGlobal(); + end = it + registerFile->numGlobals(); + while (it != end) { + printf("[global var] | %10p | %10p \n", it, (*it).v()); + ++it; + } + printf("----------------------------------------------------\n"); + } + + it = callFrame->registers() - RegisterFile::CallFrameHeaderSize - codeBlock->m_numParameters; + printf("[this] | %10p | %10p \n", it, (*it).v()); ++it; + end = it + max(codeBlock->m_numParameters - 1, 0); // - 1 to skip "this" + if (it != end) { + do { + printf("[param] | %10p | %10p \n", it, (*it).v()); + ++it; + } while (it != end); + } + printf("----------------------------------------------------\n"); + + printf("[CodeBlock] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[ScopeChain] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[CallerRegisters] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[ReturnPC] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[ReturnValueRegister] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[ArgumentCount] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[Callee] | %10p | %10p \n", it, (*it).v()); ++it; + printf("[OptionalCalleeArguments] | %10p | %10p \n", it, (*it).v()); ++it; + printf("----------------------------------------------------\n"); + + int registerCount = 0; + + end = it + codeBlock->m_numVars; + if (it != end) { + do { + printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v()); + ++it; + ++registerCount; + } while (it != end); + } + printf("----------------------------------------------------\n"); + + end = it + codeBlock->m_numConstants; + if (it != end) { + do { + printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v()); + ++it; + ++registerCount; + } while (it != end); + } + printf("----------------------------------------------------\n"); + + end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numConstants - codeBlock->m_numVars; + if (it != end) { + do { + printf("[r%2d] | %10p | %10p \n", registerCount, it, (*it).v()); + ++it; + ++registerCount; + } while (it != end); + } + printf("----------------------------------------------------\n"); +} + +#endif + +bool Interpreter::isOpcode(Opcode opcode) +{ +#if HAVE(COMPUTED_GOTO) + return opcode != HashTraits<Opcode>::emptyValue() + && !HashTraits<Opcode>::isDeletedValue(opcode) + && m_opcodeIDTable.contains(opcode); +#else + return opcode >= 0 && opcode <= op_end; +#endif +} + +NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValuePtr 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->ownerNode()->sourceID(), codeBlock->ownerNode()->lastLine()); + else + debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->lastLine()); + } + + if (Profiler* profiler = *Profiler::enabledProfilerReference()) { + if (callFrame->callee()) + profiler->didExecute(callFrame, callFrame->callee()); + else + profiler->didExecute(callFrame, codeBlock->ownerNode()->sourceURL(), codeBlock->ownerNode()->lineNo()); + } + + // If this call frame created an activation or an 'arguments' object, tear it off. + if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsFullScopeChain()) { + while (!scopeChain->object->isObject(&JSActivation::info)) + scopeChain = scopeChain->pop(); + static_cast<JSActivation*>(scopeChain->object)->copyRegisters(callFrame->optionalCalleeArguments()); + } else if (Arguments* arguments = callFrame->optionalCalleeArguments()) { + if (!arguments->isTornOff()) + arguments->copyRegisters(); + } + + if (oldCodeBlock->needsFullScopeChain()) + scopeChain->deref(); + + void* returnPC = callFrame->returnPC(); + callFrame = callFrame->callerFrame(); + if (callFrame->hasHostCallFrameFlag()) + return false; + + codeBlock = callFrame->codeBlock(); + bytecodeOffset = bytecodeOffsetForPC(codeBlock, returnPC); + return true; +} + +NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValuePtr& exceptionValue, unsigned bytecodeOffset, bool explicitThrow) +{ + // Set up the exception object + + CodeBlock* codeBlock = callFrame->codeBlock(); + if (exceptionValue->isObject()) { + JSObject* exception = asObject(exceptionValue); + if (exception->isNotAnObjectErrorStub()) { + exception = createNotAnObjectError(callFrame, static_cast<JSNotAnObjectErrorStub*>(exception), bytecodeOffset, codeBlock); + exceptionValue = exception; + } else { + if (!exception->hasProperty(callFrame, Identifier(callFrame, "line")) && + !exception->hasProperty(callFrame, Identifier(callFrame, "sourceId")) && + !exception->hasProperty(callFrame, Identifier(callFrame, "sourceURL")) && + !exception->hasProperty(callFrame, Identifier(callFrame, expressionBeginOffsetPropertyName)) && + !exception->hasProperty(callFrame, Identifier(callFrame, expressionCaretOffsetPropertyName)) && + !exception->hasProperty(callFrame, Identifier(callFrame, expressionEndOffsetPropertyName))) { + if (explicitThrow) { + int startOffset = 0; + int endOffset = 0; + int divotPoint = 0; + int line = codeBlock->expressionRangeForBytecodeOffset(callFrame, bytecodeOffset, divotPoint, startOffset, endOffset); + exception->putWithAttributes(callFrame, Identifier(callFrame, "line"), jsNumber(callFrame, line), ReadOnly | DontDelete); + + // We only hit this path for error messages and throw statements, which don't have a specific failure position + // So we just give the full range of the error/throw statement. + exception->putWithAttributes(callFrame, Identifier(callFrame, expressionBeginOffsetPropertyName), jsNumber(callFrame, divotPoint - startOffset), ReadOnly | DontDelete); + exception->putWithAttributes(callFrame, Identifier(callFrame, expressionEndOffsetPropertyName), jsNumber(callFrame, divotPoint + endOffset), ReadOnly | DontDelete); + } else + exception->putWithAttributes(callFrame, Identifier(callFrame, "line"), jsNumber(callFrame, codeBlock->lineNumberForBytecodeOffset(callFrame, bytecodeOffset)), ReadOnly | DontDelete); + exception->putWithAttributes(callFrame, Identifier(callFrame, "sourceId"), jsNumber(callFrame, codeBlock->ownerNode()->sourceID()), ReadOnly | DontDelete); + exception->putWithAttributes(callFrame, Identifier(callFrame, "sourceURL"), jsOwnedString(callFrame, codeBlock->ownerNode()->sourceURL()), ReadOnly | DontDelete); + } + + if (exception->isWatchdogException()) { + while (unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) { + // Don't need handler checks or anything, we just want to unroll all the JS callframes possible. + } + return 0; + } + } + } + + if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) { + DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue); + debugger->exception(debuggerCallFrame, codeBlock->ownerNode()->sourceID(), codeBlock->lineNumberForBytecodeOffset(callFrame, bytecodeOffset)); + } + + // If we throw in the middle of a call instruction, we need to notify + // the profiler manually that the call instruction has returned, since + // we'll never reach the relevant op_profile_did_call. + if (Profiler* profiler = *Profiler::enabledProfilerReference()) { +#if !ENABLE(JIT) + if (isCallBytecode(codeBlock->instructions()[bytecodeOffset].u.opcode)) + profiler->didExecute(callFrame, callFrame[codeBlock->instructions()[bytecodeOffset + 2].u.operand].jsValue(callFrame)); + else if (codeBlock->instructions()[bytecodeOffset + 8].u.opcode == getOpcode(op_construct)) + profiler->didExecute(callFrame, callFrame[codeBlock->instructions()[bytecodeOffset + 10].u.operand].jsValue(callFrame)); +#else + int functionRegisterIndex; + if (codeBlock->functionRegisterForBytecodeOffset(bytecodeOffset, functionRegisterIndex)) + profiler->didExecute(callFrame, callFrame[functionRegisterIndex].jsValue(callFrame)); +#endif + } + + // Calculate an exception handler vPC, unwinding call frames as necessary. + + HandlerInfo* handler = 0; + while (!(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { + if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) + return 0; + } + + // Now unwind the scope chain within the exception handler's call frame. + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChain sc(scopeChain); + int scopeDelta = depth(codeBlock, sc) - handler->scopeDepth; + ASSERT(scopeDelta >= 0); + while (scopeDelta--) + scopeChain = scopeChain->pop(); + callFrame->setScopeChain(scopeChain); + + return handler; +} + +JSValuePtr Interpreter::execute(ProgramNode* programNode, CallFrame* callFrame, ScopeChainNode* scopeChain, JSObject* thisObj, JSValuePtr* exception) +{ + ASSERT(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxReentryDepth) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + CodeBlock* codeBlock = &programNode->bytecode(scopeChain); + + Register* oldEnd = m_registerFile.end(); + Register* newEnd = oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize + codeBlock->m_numCalleeRegisters; + if (!m_registerFile.grow(newEnd)) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + DynamicGlobalObjectScope globalObjectScope(callFrame, scopeChain->globalObject()); + + JSGlobalObject* lastGlobalObject = m_registerFile.globalObject(); + JSGlobalObject* globalObject = callFrame->dynamicGlobalObject(); + globalObject->copyGlobalsTo(m_registerFile); + + CallFrame* newCallFrame = CallFrame::create(oldEnd + codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize); + newCallFrame[codeBlock->thisRegister()] = JSValuePtr(thisObj); + newCallFrame->init(codeBlock, 0, scopeChain, CallFrame::noCaller(), 0, 0, 0); + + if (codeBlock->needsFullScopeChain()) + scopeChain->ref(); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(newCallFrame, programNode->sourceURL(), programNode->lineNo()); + + JSValuePtr result; + { + SamplingTool::CallRecord callRecord(m_sampler); + + m_reentryDepth++; +#if ENABLE(JIT) + if (!codeBlock->jitCode()) + JIT::compile(scopeChain->globalData, codeBlock); + result = JIT::execute(codeBlock->jitCode(), &m_registerFile, newCallFrame, scopeChain->globalData, exception); +#else + result = privateExecute(Normal, &m_registerFile, newCallFrame, exception); +#endif + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, programNode->sourceURL(), programNode->lineNo()); + + if (m_reentryDepth && lastGlobalObject && globalObject != lastGlobalObject) + lastGlobalObject->copyGlobalsTo(m_registerFile); + + m_registerFile.shrink(oldEnd); + + return result; +} + +JSValuePtr Interpreter::execute(FunctionBodyNode* functionBodyNode, CallFrame* callFrame, JSFunction* function, JSObject* thisObj, const ArgList& args, ScopeChainNode* scopeChain, JSValuePtr* exception) +{ + ASSERT(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxReentryDepth) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + Register* oldEnd = m_registerFile.end(); + int argc = 1 + args.size(); // implicit "this" parameter + + if (!m_registerFile.grow(oldEnd + argc)) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + DynamicGlobalObjectScope globalObjectScope(callFrame, callFrame->globalData().dynamicGlobalObject ? callFrame->globalData().dynamicGlobalObject : scopeChain->globalObject()); + + CallFrame* newCallFrame = CallFrame::create(oldEnd); + size_t dst = 0; + newCallFrame[0] = JSValuePtr(thisObj); + ArgList::const_iterator end = args.end(); + for (ArgList::const_iterator it = args.begin(); it != end; ++it) + newCallFrame[++dst] = *it; + + CodeBlock* codeBlock = &functionBodyNode->bytecode(scopeChain); + newCallFrame = slideRegisterWindowForCall(codeBlock, &m_registerFile, newCallFrame, argc + RegisterFile::CallFrameHeaderSize, argc); + if (UNLIKELY(!newCallFrame)) { + *exception = createStackOverflowError(callFrame); + m_registerFile.shrink(oldEnd); + return jsNull(); + } + // a 0 codeBlock indicates a built-in caller + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), 0, argc, function); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(newCallFrame, function); + + JSValuePtr result; + { + SamplingTool::CallRecord callRecord(m_sampler); + + m_reentryDepth++; +#if ENABLE(JIT) + if (!codeBlock->jitCode()) + JIT::compile(scopeChain->globalData, codeBlock); + result = JIT::execute(codeBlock->jitCode(), &m_registerFile, newCallFrame, scopeChain->globalData, exception); +#else + result = privateExecute(Normal, &m_registerFile, newCallFrame, exception); +#endif + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(newCallFrame, function); + + m_registerFile.shrink(oldEnd); + return result; +} + +JSValuePtr Interpreter::execute(EvalNode* evalNode, CallFrame* callFrame, JSObject* thisObj, ScopeChainNode* scopeChain, JSValuePtr* exception) +{ + return execute(evalNode, callFrame, thisObj, m_registerFile.size() + evalNode->bytecode(scopeChain).m_numParameters + RegisterFile::CallFrameHeaderSize, scopeChain, exception); +} + +JSValuePtr Interpreter::execute(EvalNode* evalNode, CallFrame* callFrame, JSObject* thisObj, int globalRegisterOffset, ScopeChainNode* scopeChain, JSValuePtr* exception) +{ + ASSERT(!scopeChain->globalData->exception); + + if (m_reentryDepth >= MaxReentryDepth) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + DynamicGlobalObjectScope globalObjectScope(callFrame, callFrame->globalData().dynamicGlobalObject ? callFrame->globalData().dynamicGlobalObject : scopeChain->globalObject()); + + EvalCodeBlock* codeBlock = &evalNode->bytecode(scopeChain); + + JSVariableObject* variableObject; + for (ScopeChainNode* node = scopeChain; ; node = node->next) { + ASSERT(node); + if (node->object->isVariableObject()) { + variableObject = static_cast<JSVariableObject*>(node->object); + break; + } + } + + { // Scope for BatchedTransitionOptimizer + + BatchedTransitionOptimizer optimizer(variableObject); + + const DeclarationStacks::VarStack& varStack = codeBlock->ownerNode()->varStack(); + DeclarationStacks::VarStack::const_iterator varStackEnd = varStack.end(); + for (DeclarationStacks::VarStack::const_iterator it = varStack.begin(); it != varStackEnd; ++it) { + const Identifier& ident = (*it).first; + if (!variableObject->hasProperty(callFrame, ident)) { + PutPropertySlot slot; + variableObject->put(callFrame, ident, jsUndefined(), slot); + } + } + + const DeclarationStacks::FunctionStack& functionStack = codeBlock->ownerNode()->functionStack(); + DeclarationStacks::FunctionStack::const_iterator functionStackEnd = functionStack.end(); + for (DeclarationStacks::FunctionStack::const_iterator it = functionStack.begin(); it != functionStackEnd; ++it) { + PutPropertySlot slot; + variableObject->put(callFrame, (*it)->m_ident, (*it)->makeFunction(callFrame, scopeChain), slot); + } + + } + + Register* oldEnd = m_registerFile.end(); + Register* newEnd = m_registerFile.start() + globalRegisterOffset + codeBlock->m_numCalleeRegisters; + if (!m_registerFile.grow(newEnd)) { + *exception = createStackOverflowError(callFrame); + return jsNull(); + } + + CallFrame* newCallFrame = CallFrame::create(m_registerFile.start() + globalRegisterOffset); + + // a 0 codeBlock indicates a built-in caller + newCallFrame[codeBlock->thisRegister()] = JSValuePtr(thisObj); + newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), 0, 0, 0); + + if (codeBlock->needsFullScopeChain()) + scopeChain->ref(); + + Profiler** profiler = Profiler::enabledProfilerReference(); + if (*profiler) + (*profiler)->willExecute(newCallFrame, evalNode->sourceURL(), evalNode->lineNo()); + + JSValuePtr result; + { + SamplingTool::CallRecord callRecord(m_sampler); + + m_reentryDepth++; +#if ENABLE(JIT) + if (!codeBlock->jitCode()) + JIT::compile(scopeChain->globalData, codeBlock); + result = JIT::execute(codeBlock->jitCode(), &m_registerFile, newCallFrame, scopeChain->globalData, exception); +#else + result = privateExecute(Normal, &m_registerFile, newCallFrame, exception); +#endif + m_reentryDepth--; + } + + if (*profiler) + (*profiler)->didExecute(callFrame, evalNode->sourceURL(), evalNode->lineNo()); + + m_registerFile.shrink(oldEnd); + return 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()->ownerNode()->sourceID(), firstLine); + return; + case WillLeaveCallFrame: + debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerNode()->sourceID(), lastLine); + return; + case WillExecuteStatement: + debugger->atStatement(callFrame, callFrame->codeBlock()->ownerNode()->sourceID(), firstLine); + return; + case WillExecuteProgram: + debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerNode()->sourceID(), firstLine); + return; + case DidExecuteProgram: + debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerNode()->sourceID(), lastLine); + return; + case DidReachBreakpoint: + debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerNode()->sourceID(), lastLine); + return; + } +} + +void Interpreter::resetTimeoutCheck() +{ + m_ticksUntilNextTimeoutCheck = initialTickCountThreshold; + m_timeAtLastCheckTimeout = 0; + m_timeExecuting = 0; +} + +// Returns the time the current thread has spent executing, in milliseconds. +static inline unsigned getCPUTime() +{ +#if PLATFORM(DARWIN) + mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t info; + + // Get thread information + mach_port_t threadPort = mach_thread_self(); + thread_info(threadPort, THREAD_BASIC_INFO, reinterpret_cast<thread_info_t>(&info), &infoCount); + mach_port_deallocate(mach_task_self(), threadPort); + + unsigned time = info.user_time.seconds * 1000 + info.user_time.microseconds / 1000; + time += info.system_time.seconds * 1000 + info.system_time.microseconds / 1000; + + return time; +#elif HAVE(SYS_TIME_H) + // FIXME: This should probably use getrusage with the RUSAGE_THREAD flag. + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +#elif PLATFORM(QT) + QDateTime t = QDateTime::currentDateTime(); + return t.toTime_t() * 1000 + t.time().msec(); +#elif PLATFORM(WIN_OS) + union { + FILETIME fileTime; + unsigned long long fileTimeAsLong; + } userTime, kernelTime; + + // GetThreadTimes won't accept NULL arguments so we pass these even though + // they're not used. + FILETIME creationTime, exitTime; + + GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime.fileTime, &userTime.fileTime); + + return userTime.fileTimeAsLong / 10000 + kernelTime.fileTimeAsLong / 10000; +#else +#error Platform does not have getCurrentTime function +#endif +} + +// We have to return a JSValue here, gcc seems to produce worse code if +// we attempt to return a bool +ALWAYS_INLINE bool Interpreter::checkTimeout(JSGlobalObject* globalObject) +{ + unsigned currentTime = getCPUTime(); + + if (!m_timeAtLastCheckTimeout) { + // Suspicious amount of looping in a script -- start timing it + m_timeAtLastCheckTimeout = currentTime; + return false; + } + + unsigned timeDiff = currentTime - m_timeAtLastCheckTimeout; + + if (timeDiff == 0) + timeDiff = 1; + + m_timeExecuting += timeDiff; + m_timeAtLastCheckTimeout = currentTime; + + // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in + // preferredScriptCheckTimeInterval + m_ticksUntilNextTimeoutCheck = static_cast<unsigned>((static_cast<float>(preferredScriptCheckTimeInterval) / timeDiff) * m_ticksUntilNextTimeoutCheck); + // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the + // preferred script check time interval. + if (m_ticksUntilNextTimeoutCheck == 0) + m_ticksUntilNextTimeoutCheck = initialTickCountThreshold; + + if (m_timeoutTime && m_timeExecuting > m_timeoutTime) { + if (globalObject->shouldInterruptScript()) + return true; + + resetTimeoutCheck(); + } + + return false; +} + +NEVER_INLINE ScopeChainNode* Interpreter::createExceptionScope(CallFrame* callFrame, const Instruction* vPC) +{ + int dst = (++vPC)->u.operand; + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& property = codeBlock->identifier((++vPC)->u.operand); + JSValuePtr value = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSObject* scope = new (callFrame) JSStaticScopeObject(callFrame, property, value, DontDelete); + callFrame[dst] = JSValuePtr(scope); + + return callFrame->scopeChain()->push(scope); +} + +static StructureChain* cachePrototypeChain(CallFrame* callFrame, Structure* structure) +{ + JSValuePtr prototype = structure->prototypeForLookup(callFrame); + if (JSImmediate::isImmediate(prototype)) + return 0; + RefPtr<StructureChain> chain = StructureChain::create(asObject(prototype)->structure()); + structure->setCachedPrototypeChain(chain.release()); + return structure->cachedPrototypeChain(); +} + +NEVER_INLINE void Interpreter::tryCachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValuePtr baseValue, const PutPropertySlot& slot) +{ + // Recursive invocation may already have specialized this instruction. + if (vPC[0].u.opcode != getOpcode(op_put_by_id)) + return; + + if (JSImmediate::isImmediate(baseValue)) + return; + + // Uncacheable: give up. + if (!slot.isCacheable()) { + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + + JSCell* baseCell = asCell(baseValue); + Structure* structure = baseCell->structure(); + + if (structure->isDictionary()) { + 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) { + vPC[0] = getOpcode(op_put_by_id_transition); + vPC[4] = structure->previousID(); + vPC[5] = structure; + StructureChain* chain = structure->cachedPrototypeChain(); + if (!chain) { + chain = cachePrototypeChain(callFrame, structure); + if (!chain) { + // This happens if someone has manually inserted null into the prototype chain + vPC[0] = getOpcode(op_put_by_id_generic); + return; + } + } + vPC[6] = chain; + 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; +} + +static size_t countPrototypeChainEntriesAndCheckForProxies(CallFrame* callFrame, JSValuePtr baseValue, const PropertySlot& slot) +{ + JSCell* cell = asCell(baseValue); + size_t count = 0; + + while (slot.slotBase() != cell) { + JSValuePtr v = cell->structure()->prototypeForLookup(callFrame); + + // If we didn't find slotBase in baseValue's prototype chain, then baseValue + // must be a proxy for another object. + + if (v->isNull()) + return 0; + + cell = asCell(v); + + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (cell->structure()->isDictionary()) { + RefPtr<Structure> transition = Structure::fromDictionaryTransition(cell->structure()); + asObject(cell)->setStructure(transition.release()); + cell->structure()->setCachedPrototypeChain(0); + } + + ++count; + } + + ASSERT(count); + return count; +} + +NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, Instruction* vPC, JSValuePtr 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 (JSImmediate::isImmediate(baseValue)) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + if (isJSArray(baseValue) && propertyName == callFrame->propertyNames().length) { + vPC[0] = getOpcode(op_get_array_length); + return; + } + + if (isJSString(baseValue) && propertyName == callFrame->propertyNames().length) { + vPC[0] = getOpcode(op_get_string_length); + return; + } + + // Uncacheable: give up. + if (!slot.isCacheable()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + Structure* structure = asCell(baseValue)->structure(); + + if (structure->isDictionary()) { + 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) { + vPC[0] = getOpcode(op_get_by_id_self); + vPC[5] = slot.cachedOffset(); + + codeBlock->refStructures(vPC); + return; + } + + if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { + ASSERT(slot.slotBase()->isObject()); + + JSObject* baseObject = asObject(slot.slotBase()); + + // 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()) { + RefPtr<Structure> transition = Structure::fromDictionaryTransition(baseObject->structure()); + baseObject->setStructure(transition.release()); + asCell(baseValue)->structure()->setCachedPrototypeChain(0); + } + + vPC[0] = getOpcode(op_get_by_id_proto); + vPC[5] = baseObject->structure(); + vPC[6] = slot.cachedOffset(); + + codeBlock->refStructures(vPC); + return; + } + + size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot); + if (!count) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + + StructureChain* chain = structure->cachedPrototypeChain(); + if (!chain) + chain = cachePrototypeChain(callFrame, structure); + ASSERT(chain); + + vPC[0] = getOpcode(op_get_by_id_chain); + vPC[4] = structure; + vPC[5] = chain; + vPC[6] = count; + vPC[7] = slot.cachedOffset(); + 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; +} + +JSValuePtr Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFile, CallFrame* callFrame, JSValuePtr* exception) +{ + // 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 (flag == InitializeAndReturn) { + #if HAVE(COMPUTED_GOTO) + #define ADD_BYTECODE(id, length) m_opcodeTable[id] = &&id; + FOR_EACH_OPCODE_ID(ADD_BYTECODE); + #undef ADD_BYTECODE + + #define ADD_OPCODE_ID(id, length) m_opcodeIDTable.add(&&id, id); + FOR_EACH_OPCODE_ID(ADD_OPCODE_ID); + #undef ADD_OPCODE_ID + ASSERT(m_opcodeIDTable.size() == numOpcodeIDs); + #endif // HAVE(COMPUTED_GOTO) + return noValue(); + } + +#if ENABLE(JIT) + // Currently with CTI enabled we never interpret functions + ASSERT_NOT_REACHED(); +#endif + + JSGlobalData* globalData = &callFrame->globalData(); + JSValuePtr exceptionValue = noValue(); + HandlerInfo* handler = 0; + + Instruction* vPC = callFrame->codeBlock()->instructions().begin(); + Profiler** enabledProfilerReference = Profiler::enabledProfilerReference(); + unsigned tickCount = m_ticksUntilNextTimeoutCheck + 1; + +#define CHECK_FOR_EXCEPTION() \ + do { \ + if (UNLIKELY(globalData->exception != noValue())) { \ + exceptionValue = globalData->exception; \ + goto vm_throw; \ + } \ + } while (0) + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + +#define CHECK_FOR_TIMEOUT() \ + if (!--tickCount) { \ + if (checkTimeout(callFrame->dynamicGlobalObject())) { \ + exceptionValue = jsNull(); \ + goto vm_throw; \ + } \ + tickCount = m_ticksUntilNextTimeoutCheck; \ + } + +#if ENABLE(OPCODE_SAMPLING) + #define SAMPLE(codeBlock, vPC) m_sampler->sample(codeBlock, vPC) + #define CTI_SAMPLER ARG_globalData->interpreter->sampler() +#else + #define SAMPLE(codeBlock, vPC) + #define CTI_SAMPLER 0 +#endif + +#if HAVE(COMPUTED_GOTO) + #define NEXT_INSTRUCTION() SAMPLE(callFrame->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(callFrame->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)->u.operand; + callFrame[dst] = JSValuePtr(constructEmptyObject(callFrame)); + + ++vPC; + 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)->u.operand; + int firstArg = (++vPC)->u.operand; + int argCount = (++vPC)->u.operand; + ArgList args(callFrame->registers() + firstArg, argCount); + callFrame[dst] = JSValuePtr(constructArray(callFrame, args)); + + ++vPC; + 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)->u.operand; + int regExp = (++vPC)->u.operand; + callFrame[dst] = JSValuePtr(new (globalData) RegExpObject(callFrame->scopeChain()->globalObject()->regExpStructure(), callFrame->codeBlock()->regexp(regExp))); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_mov) { + /* mov dst(r) src(r) + + Copies register src to register dst. + */ + int dst = (++vPC)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = callFrame[src]; + + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (JSImmediate::areBothImmediateNumbers(src1, src2)) + callFrame[dst] = jsBoolean(src1 == src2); + else { + JSValuePtr result = jsBoolean(equalSlowCase(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr src = callFrame[(++vPC)->u.operand].jsValue(callFrame); + + if (src->isUndefinedOrNull()) { + callFrame[dst] = jsBoolean(true); + ++vPC; + NEXT_INSTRUCTION(); + } + + callFrame[dst] = jsBoolean(!JSImmediate::isImmediate(src) && src->asCell()->structure()->typeInfo().masqueradesAsUndefined()); + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (JSImmediate::areBothImmediateNumbers(src1, src2)) + callFrame[dst] = jsBoolean(src1 != src2); + else { + JSValuePtr result = jsBoolean(!equalSlowCase(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr src = callFrame[(++vPC)->u.operand].jsValue(callFrame); + + if (src->isUndefinedOrNull()) { + callFrame[dst] = jsBoolean(false); + ++vPC; + NEXT_INSTRUCTION(); + } + + callFrame[dst] = jsBoolean(JSImmediate::isImmediate(src) || !asCell(src)->structure()->typeInfo().masqueradesAsUndefined()); + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (JSImmediate::areBothImmediate(src1, src2)) + callFrame[dst] = jsBoolean(src1 == src2); + else if (JSImmediate::isEitherImmediate(src1, src2) & (src1 != JSImmediate::zeroImmediate()) & (src2 != JSImmediate::zeroImmediate())) + callFrame[dst] = jsBoolean(false); + else + callFrame[dst] = jsBoolean(strictEqualSlowCase(src1, src2)); + + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + + if (JSImmediate::areBothImmediate(src1, src2)) + callFrame[dst] = jsBoolean(src1 != src2); + else if (JSImmediate::isEitherImmediate(src1, src2) & (src1 != JSImmediate::zeroImmediate()) & (src2 != JSImmediate::zeroImmediate())) + callFrame[dst] = jsBoolean(true); + else + callFrame[dst] = jsBoolean(!strictEqualSlowCase(src1, src2)); + + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr result = jsBoolean(jsLess(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr result = jsBoolean(jsLessEq(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + + ++vPC; + 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)->u.operand; + JSValuePtr v = callFrame[srcDst].jsValue(callFrame); + if (JSImmediate::canDoFastAdditiveOperations(v)) + callFrame[srcDst] = JSValuePtr(JSImmediate::incImmediateNumber(v)); + else { + JSValuePtr result = jsNumber(callFrame, v->toNumber(callFrame) + 1); + CHECK_FOR_EXCEPTION(); + callFrame[srcDst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr v = callFrame[srcDst].jsValue(callFrame); + if (JSImmediate::canDoFastAdditiveOperations(v)) + callFrame[srcDst] = JSValuePtr(JSImmediate::decImmediateNumber(v)); + else { + JSValuePtr result = jsNumber(callFrame, v->toNumber(callFrame) - 1); + CHECK_FOR_EXCEPTION(); + callFrame[srcDst] = result; + } + + ++vPC; + 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)->u.operand; + int srcDst = (++vPC)->u.operand; + JSValuePtr v = callFrame[srcDst].jsValue(callFrame); + if (JSImmediate::canDoFastAdditiveOperations(v)) { + callFrame[dst] = v; + callFrame[srcDst] = JSValuePtr(JSImmediate::incImmediateNumber(v)); + } else { + JSValuePtr number = callFrame[srcDst].jsValue(callFrame)->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = number; + callFrame[srcDst] = JSValuePtr(jsNumber(callFrame, number->uncheckedGetNumber() + 1)); + } + + ++vPC; + 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)->u.operand; + int srcDst = (++vPC)->u.operand; + JSValuePtr v = callFrame[srcDst].jsValue(callFrame); + if (JSImmediate::canDoFastAdditiveOperations(v)) { + callFrame[dst] = v; + callFrame[srcDst] = JSValuePtr(JSImmediate::decImmediateNumber(v)); + } else { + JSValuePtr number = callFrame[srcDst].jsValue(callFrame)->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = number; + callFrame[srcDst] = JSValuePtr(jsNumber(callFrame, number->uncheckedGetNumber() - 1)); + } + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + + JSValuePtr srcVal = callFrame[src].jsValue(callFrame); + + if (LIKELY(srcVal->isNumber())) + callFrame[dst] = callFrame[src]; + else { + JSValuePtr result = srcVal->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr src = callFrame[(++vPC)->u.operand].jsValue(callFrame); + ++vPC; + double v; + if (fastIsNumber(src, v)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, -v)); + else { + JSValuePtr result = jsNumber(callFrame, -src->toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (JSImmediate::canDoFastAdditiveOperations(src1) && JSImmediate::canDoFastAdditiveOperations(src2)) + callFrame[dst] = JSValuePtr(JSImmediate::addImmediateNumbers(src1, src2)); + else { + JSValuePtr result = jsAdd(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + vPC += 2; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + double left; + double right; + if (JSImmediate::areBothImmediateNumbers(src1, src2)) { + int32_t left = JSImmediate::getTruncatedInt32(src1); + int32_t right = JSImmediate::getTruncatedInt32(src2); + if ((left | right) >> 15 == 0) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left * right)); + else + callFrame[dst] = JSValuePtr(jsNumber(callFrame, static_cast<double>(left) * static_cast<double>(right))); + } else if (fastIsNumber(src1, left) && fastIsNumber(src2, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left * right)); + else { + JSValuePtr result = jsNumber(callFrame, src1->toNumber(callFrame) * src2->toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + vPC += 2; + 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)->u.operand; + JSValuePtr dividend = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr divisor = callFrame[(++vPC)->u.operand].jsValue(callFrame); + double left; + double right; + if (fastIsNumber(dividend, left) && fastIsNumber(divisor, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left / right)); + else { + JSValuePtr result = jsNumber(callFrame, dividend->toNumber(callFrame) / divisor->toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + ++vPC; + 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)->u.operand; + int dividend = (++vPC)->u.operand; + int divisor = (++vPC)->u.operand; + + JSValuePtr dividendValue = callFrame[dividend].jsValue(callFrame); + JSValuePtr divisorValue = callFrame[divisor].jsValue(callFrame); + + if (JSImmediate::areBothImmediateNumbers(dividendValue, divisorValue) && divisorValue != JSImmediate::from(0)) { + callFrame[dst] = JSValuePtr(JSImmediate::from(JSImmediate::getTruncatedInt32(dividendValue) % JSImmediate::getTruncatedInt32(divisorValue))); + ++vPC; + NEXT_INSTRUCTION(); + } + + double d = dividendValue->toNumber(callFrame); + JSValuePtr result = jsNumber(callFrame, fmod(d, divisorValue->toNumber(callFrame))); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + double left; + double right; + if (JSImmediate::canDoFastAdditiveOperations(src1) && JSImmediate::canDoFastAdditiveOperations(src2)) + callFrame[dst] = JSValuePtr(JSImmediate::subImmediateNumbers(src1, src2)); + else if (fastIsNumber(src1, left) && fastIsNumber(src2, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left - right)); + else { + JSValuePtr result = jsNumber(callFrame, src1->toNumber(callFrame) - src2->toNumber(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + vPC += 2; + 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)->u.operand; + JSValuePtr val = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr shift = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t left; + uint32_t right; + if (JSImmediate::areBothImmediateNumbers(val, shift)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, JSImmediate::getTruncatedInt32(val) << (JSImmediate::getTruncatedUInt32(shift) & 0x1f))); + else if (fastToInt32(val, left) && fastToUInt32(shift, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left << (right & 0x1f))); + else { + JSValuePtr result = jsNumber(callFrame, (val->toInt32(callFrame)) << (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr val = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr shift = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t left; + uint32_t right; + if (JSImmediate::areBothImmediateNumbers(val, shift)) + callFrame[dst] = JSValuePtr(JSImmediate::rightShiftImmediateNumbers(val, shift)); + else if (fastToInt32(val, left) && fastToUInt32(shift, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left >> (right & 0x1f))); + else { + JSValuePtr result = jsNumber(callFrame, (val->toInt32(callFrame)) >> (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr val = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr shift = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (JSImmediate::areBothImmediateNumbers(val, shift) && !JSImmediate::isNegative(val)) + callFrame[dst] = JSValuePtr(JSImmediate::rightShiftImmediateNumbers(val, shift)); + else { + JSValuePtr result = jsNumber(callFrame, (val->toUInt32(callFrame)) >> (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + ++vPC; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t left; + int32_t right; + if (JSImmediate::areBothImmediateNumbers(src1, src2)) + callFrame[dst] = JSValuePtr(JSImmediate::andImmediateNumbers(src1, src2)); + else if (fastToInt32(src1, left) && fastToInt32(src2, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left & right)); + else { + JSValuePtr result = jsNumber(callFrame, src1->toInt32(callFrame) & src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + vPC += 2; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t left; + int32_t right; + if (JSImmediate::areBothImmediateNumbers(src1, src2)) + callFrame[dst] = JSValuePtr(JSImmediate::xorImmediateNumbers(src1, src2)); + else if (fastToInt32(src1, left) && fastToInt32(src2, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left ^ right)); + else { + JSValuePtr result = jsNumber(callFrame, src1->toInt32(callFrame) ^ src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + vPC += 2; + 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)->u.operand; + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t left; + int32_t right; + if (JSImmediate::areBothImmediateNumbers(src1, src2)) + callFrame[dst] = JSValuePtr(JSImmediate::orImmediateNumbers(src1, src2)); + else if (fastToInt32(src1, left) && fastToInt32(src2, right)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, left | right)); + else { + JSValuePtr result = jsNumber(callFrame, src1->toInt32(callFrame) | src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + + vPC += 2; + 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)->u.operand; + JSValuePtr src = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int32_t value; + if (fastToInt32(src, value)) + callFrame[dst] = JSValuePtr(jsNumber(callFrame, ~value)); + else { + JSValuePtr result = jsNumber(callFrame, ~src->toInt32(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + } + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + JSValuePtr result = jsBoolean(!callFrame[src].jsValue(callFrame)->toBoolean(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + + ++vPC; + 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; + + JSValuePtr baseVal = callFrame[base].jsValue(callFrame); + + if (isNotObject(callFrame, true, callFrame->codeBlock(), vPC, baseVal, exceptionValue)) + goto vm_throw; + + JSObject* baseObj = asObject(baseVal); + callFrame[dst] = jsBoolean(baseObj->structure()->typeInfo().implementsHasInstance() ? baseObj->hasInstance(callFrame, callFrame[value].jsValue(callFrame), callFrame[baseProto].jsValue(callFrame)) : false); + + vPC += 5; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = JSValuePtr(jsTypeStringForValue(callFrame, callFrame[src].jsValue(callFrame))); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + JSValuePtr v = callFrame[src].jsValue(callFrame); + callFrame[dst] = jsBoolean(JSImmediate::isImmediate(v) ? v->isUndefined() : v->asCell()->structure()->typeInfo().masqueradesAsUndefined()); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = jsBoolean(callFrame[src].jsValue(callFrame)->isBoolean()); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = jsBoolean(callFrame[src].jsValue(callFrame)->isNumber()); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = jsBoolean(callFrame[src].jsValue(callFrame)->isString()); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = jsBoolean(jsIsObjectType(callFrame[src].jsValue(callFrame))); + + ++vPC; + 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)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = jsBoolean(jsIsFunctionType(callFrame[src].jsValue(callFrame))); + + ++vPC; + 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)->u.operand; + int property = (++vPC)->u.operand; + int base = (++vPC)->u.operand; + + JSValuePtr baseVal = callFrame[base].jsValue(callFrame); + if (isNotObject(callFrame, false, callFrame->codeBlock(), vPC, baseVal, exceptionValue)) + goto vm_throw; + + JSObject* baseObj = asObject(baseVal); + + JSValuePtr propName = callFrame[property].jsValue(callFrame); + + uint32_t i; + if (propName->getUInt32(i)) + callFrame[dst] = jsBoolean(baseObj->hasProperty(callFrame, i)); + else { + Identifier property(callFrame, propName->toString(callFrame)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = jsBoolean(baseObj->hasProperty(callFrame, property)); + } + + ++vPC; + 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 += 3; + 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 += 4; + + 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 += 6; + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_global_var) { + /* get_global_var dst(r) globalObject(c) index(n) nop(n) nop(n) + + Gets the global var at global slot index and places it in register dst. + */ + int dst = vPC[1].u.operand; + JSGlobalObject* scope = static_cast<JSGlobalObject*>(vPC[2].u.jsCell); + ASSERT(scope->isGlobalObject()); + int index = vPC[3].u.operand; + + callFrame[dst] = scope->registerAt(index); + + vPC += OPCODE_LENGTH(op_resolve_global); + 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 = static_cast<JSGlobalObject*>((++vPC)->u.jsCell); + ASSERT(scope->isGlobalObject()); + int index = (++vPC)->u.operand; + int value = (++vPC)->u.operand; + + scope->registerAt(index) = JSValuePtr(callFrame[value].jsValue(callFrame)); + ++vPC; + 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)->u.operand; + int index = (++vPC)->u.operand; + int skip = (++vPC)->u.operand + callFrame->codeBlock()->needsFullScopeChain(); + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + while (skip--) { + ++iter; + ASSERT(iter != end); + } + + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast<JSVariableObject*>(*iter); + callFrame[dst] = scope->registerAt(index); + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_put_scoped_var) { + /* put_scoped_var index(n) skip(n) value(r) + + */ + int index = (++vPC)->u.operand; + int skip = (++vPC)->u.operand + callFrame->codeBlock()->needsFullScopeChain(); + int value = (++vPC)->u.operand; + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + while (skip--) { + ++iter; + ASSERT(iter != end); + } + + ASSERT((*iter)->isVariableObject()); + JSVariableObject* scope = static_cast<JSVariableObject*>(*iter); + scope->registerAt(index) = JSValuePtr(callFrame[value].jsValue(callFrame)); + ++vPC; + 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 += 3; + 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 += 4; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_resolve_func) { + /* resolve_func baseDst(r) funcDst(r) property(id) + + Searches the scope chain for an object containing + identifier property, and if one is found, writes the + appropriate object to use as "this" when calling its + properties to register baseDst; and the retrieved property + value to register propDst. If the property is not found, + raises an exception. + + This differs from resolve_with_base, because the + global this value will be substituted for activations or + the global object, which is the right behavior for function + calls but not for other property lookup. + */ + if (UNLIKELY(!resolveBaseAndFunc(callFrame, vPC, exceptionValue))) + goto vm_throw; + + vPC += 4; + 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; + + CodeBlock* codeBlock = callFrame->codeBlock(); + Identifier& ident = codeBlock->identifier(property); + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + CHECK_FOR_EXCEPTION(); + + tryCacheGetByID(callFrame, codeBlock, vPC, baseValue, ident, slot); + + callFrame[dst] = result; + vPC += 8; + 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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + + if (LIKELY(!JSImmediate::isImmediate(baseValue))) { + JSCell* baseCell = asCell(baseValue); + 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, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + callFrame[dst] = JSValuePtr(baseObject->getDirectOffset(offset)); + + vPC += 8; + NEXT_INSTRUCTION(); + } + } + + uncacheGetByID(callFrame->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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + + if (LIKELY(!JSImmediate::isImmediate(baseValue))) { + JSCell* baseCell = asCell(baseValue); + 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, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); + callFrame[dst] = JSValuePtr(protoObject->getDirectOffset(offset)); + + vPC += 8; + NEXT_INSTRUCTION(); + } + } + } + + uncacheGetByID(callFrame->codeBlock(), vPC); + NEXT_INSTRUCTION(); + } + 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 += 8; + 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 += 8; + 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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + + if (LIKELY(!JSImmediate::isImmediate(baseValue))) { + JSCell* baseCell = asCell(baseValue); + Structure* structure = vPC[4].u.structure; + + if (LIKELY(baseCell->structure() == structure)) { + RefPtr<Structure>* it = vPC[5].u.structureChain->head(); + size_t count = vPC[6].u.operand; + RefPtr<Structure>* end = it + count; + + JSObject* baseObject = asObject(baseCell); + while (1) { + baseObject = asObject(baseObject->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, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + callFrame[dst] = JSValuePtr(baseObject->getDirectOffset(offset)); + + vPC += 8; + NEXT_INSTRUCTION(); + } + } + } + } + + uncacheGetByID(callFrame->codeBlock(), vPC); + NEXT_INSTRUCTION(); + } + 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 = callFrame->codeBlock()->identifier(property); + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + CHECK_FOR_EXCEPTION(); + + callFrame[dst] = result; + vPC += 8; + NEXT_INSTRUCTION(); + } + 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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + if (LIKELY(isJSArray(baseValue))) { + int dst = vPC[1].u.operand; + callFrame[dst] = JSValuePtr(jsNumber(callFrame, asArray(baseValue)->length())); + vPC += 8; + NEXT_INSTRUCTION(); + } + + uncacheGetByID(callFrame->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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + if (LIKELY(isJSString(baseValue))) { + int dst = vPC[1].u.operand; + callFrame[dst] = JSValuePtr(jsNumber(callFrame, asString(baseValue)->value().size())); + vPC += 8; + NEXT_INSTRUCTION(); + } + + uncacheGetByID(callFrame->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) + + 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; + + CodeBlock* codeBlock = callFrame->codeBlock(); + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + Identifier& ident = codeBlock->identifier(property); + PutPropertySlot slot; + baseValue->put(callFrame, ident, callFrame[value].jsValue(callFrame), slot); + CHECK_FOR_EXCEPTION(); + + tryCachePutByID(callFrame, codeBlock, vPC, baseValue, slot); + + vPC += 8; + 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) + + 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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + + if (LIKELY(!JSImmediate::isImmediate(baseValue))) { + JSCell* baseCell = asCell(baseValue); + Structure* oldStructure = vPC[4].u.structure; + Structure* newStructure = vPC[5].u.structure; + + if (LIKELY(baseCell->structure() == oldStructure)) { + ASSERT(baseCell->isObject()); + JSObject* baseObject = asObject(baseCell); + + RefPtr<Structure>* it = vPC[6].u.structureChain->head(); + + JSValuePtr proto = baseObject->structure()->prototypeForLookup(callFrame); + while (!proto->isNull()) { + if (UNLIKELY(asObject(proto)->structure() != (*it).get())) { + uncachePutByID(callFrame->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(callFrame->codeBlock()->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(offset, callFrame[value].jsValue(callFrame)); + + vPC += 8; + NEXT_INSTRUCTION(); + } + } + + uncachePutByID(callFrame->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) + + 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; + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + + if (LIKELY(!JSImmediate::isImmediate(baseValue))) { + JSCell* baseCell = asCell(baseValue); + 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(callFrame->codeBlock()->identifier(vPC[2].u.operand))) == offset); + baseObject->putDirectOffset(offset, callFrame[value].jsValue(callFrame)); + + vPC += 8; + NEXT_INSTRUCTION(); + } + } + + uncachePutByID(callFrame->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) + + 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; + + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + Identifier& ident = callFrame->codeBlock()->identifier(property); + PutPropertySlot slot; + baseValue->put(callFrame, ident, callFrame[value].jsValue(callFrame), slot); + CHECK_FOR_EXCEPTION(); + + vPC += 8; + 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)->u.operand; + int base = (++vPC)->u.operand; + int property = (++vPC)->u.operand; + + JSObject* baseObj = callFrame[base].jsValue(callFrame)->toObject(callFrame); + Identifier& ident = callFrame->codeBlock()->identifier(property); + JSValuePtr result = jsBoolean(baseObj->deleteProperty(callFrame, ident)); + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + ++vPC; + NEXT_INSTRUCTION(); + } + 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)->u.operand; + int base = (++vPC)->u.operand; + int property = (++vPC)->u.operand; + + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + JSValuePtr subscript = callFrame[property].jsValue(callFrame); + + JSValuePtr result; + unsigned i; + + bool isUInt32 = JSImmediate::getUInt32(subscript, i); + if (LIKELY(isUInt32)) { + if (isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canGetIndex(i)) + result = jsArray->getIndex(i); + else + result = jsArray->JSArray::get(callFrame, i); + } else if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) + result = asString(baseValue)->getIndex(&callFrame->globalData(), i); + else if (isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) + result = asByteArray(baseValue)->getIndex(i); + else + result = baseValue->get(callFrame, i); + } else { + Identifier property(callFrame, subscript->toString(callFrame)); + result = baseValue->get(callFrame, property); + } + + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + ++vPC; + 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)->u.operand; + int property = (++vPC)->u.operand; + int value = (++vPC)->u.operand; + + JSValuePtr baseValue = callFrame[base].jsValue(callFrame); + JSValuePtr subscript = callFrame[property].jsValue(callFrame); + + unsigned i; + + bool isUInt32 = JSImmediate::getUInt32(subscript, i); + if (LIKELY(isUInt32)) { + if (isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canSetIndex(i)) + jsArray->setIndex(i, callFrame[value].jsValue(callFrame)); + else + jsArray->JSArray::put(callFrame, i, callFrame[value].jsValue(callFrame)); + } else if (isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { + JSByteArray* jsByteArray = asByteArray(baseValue); + double dValue = 0; + JSValuePtr jsValue = callFrame[value].jsValue(callFrame); + if (JSImmediate::isNumber(jsValue)) + jsByteArray->setIndex(i, JSImmediate::getTruncatedInt32(jsValue)); + else if (fastIsNumber(jsValue, dValue)) + jsByteArray->setIndex(i, dValue); + else + baseValue->put(callFrame, i, jsValue); + } else + baseValue->put(callFrame, i, callFrame[value].jsValue(callFrame)); + } else { + Identifier property(callFrame, subscript->toString(callFrame)); + if (!globalData->exception) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot; + baseValue->put(callFrame, property, callFrame[value].jsValue(callFrame), slot); + } + } + + CHECK_FOR_EXCEPTION(); + ++vPC; + 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)->u.operand; + int base = (++vPC)->u.operand; + int property = (++vPC)->u.operand; + + JSObject* baseObj = callFrame[base].jsValue(callFrame)->toObject(callFrame); // may throw + + JSValuePtr subscript = callFrame[property].jsValue(callFrame); + JSValuePtr result; + uint32_t i; + if (subscript->getUInt32(i)) + result = jsBoolean(baseObj->deleteProperty(callFrame, i)); + else { + CHECK_FOR_EXCEPTION(); + Identifier property(callFrame, subscript->toString(callFrame)); + CHECK_FOR_EXCEPTION(); + result = jsBoolean(baseObj->deleteProperty(callFrame, property)); + } + + CHECK_FOR_EXCEPTION(); + callFrame[dst] = result; + ++vPC; + 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)->u.operand; + unsigned property = (++vPC)->u.operand; + int value = (++vPC)->u.operand; + + callFrame[base].jsValue(callFrame)->put(callFrame, property, callFrame[value].jsValue(callFrame)); + + ++vPC; + 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)->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)->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)->u.operand; + int target = (++vPC)->u.operand; + if (callFrame[cond].jsValue(callFrame)->toBoolean(callFrame)) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + ++vPC; + 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)->u.operand; + int target = (++vPC)->u.operand; + if (callFrame[cond].jsValue(callFrame)->toBoolean(callFrame)) { + vPC += target; + NEXT_INSTRUCTION(); + } + + ++vPC; + 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)->u.operand; + int target = (++vPC)->u.operand; + if (!callFrame[cond].jsValue(callFrame)->toBoolean(callFrame)) { + vPC += target; + NEXT_INSTRUCTION(); + } + + ++vPC; + 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)->u.operand; + int target = (++vPC)->u.operand; + JSValuePtr srcValue = callFrame[src].jsValue(callFrame); + + if (srcValue->isUndefinedOrNull() || (!JSImmediate::isImmediate(srcValue) && srcValue->asCell()->structure()->typeInfo().masqueradesAsUndefined())) { + vPC += target; + NEXT_INSTRUCTION(); + } + + ++vPC; + 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)->u.operand; + int target = (++vPC)->u.operand; + JSValuePtr srcValue = callFrame[src].jsValue(callFrame); + + if (!srcValue->isUndefinedOrNull() || (!JSImmediate::isImmediate(srcValue) && !srcValue->asCell()->structure()->typeInfo().masqueradesAsUndefined())) { + vPC += target; + NEXT_INSTRUCTION(); + } + + ++vPC; + 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. + */ + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int target = (++vPC)->u.operand; + + bool result = jsLess(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + ++vPC; + 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. + */ + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int target = (++vPC)->u.operand; + + bool result = jsLessEq(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + ++vPC; + 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. + */ + JSValuePtr src1 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + JSValuePtr src2 = callFrame[(++vPC)->u.operand].jsValue(callFrame); + int target = (++vPC)->u.operand; + + bool result = jsLess(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (!result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + ++vPC; + 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)->u.operand; + int defaultOffset = (++vPC)->u.operand; + JSValuePtr scrutinee = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (!JSImmediate::isNumber(scrutinee)) + vPC += defaultOffset; + else { + int32_t value = JSImmediate::getTruncatedInt32(scrutinee); + vPC += callFrame->codeBlock()->immediateSwitchJumpTable(tableIndex).offsetForValue(value, 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)->u.operand; + int defaultOffset = (++vPC)->u.operand; + JSValuePtr scrutinee = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (!scrutinee->isString()) + vPC += defaultOffset; + else { + UString::Rep* value = asString(scrutinee)->value().rep(); + if (value->size() != 1) + vPC += defaultOffset; + else + vPC += callFrame->codeBlock()->characterSwitchJumpTable(tableIndex).offsetForValue(value->data()[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)->u.operand; + int defaultOffset = (++vPC)->u.operand; + JSValuePtr scrutinee = callFrame[(++vPC)->u.operand].jsValue(callFrame); + if (!scrutinee->isString()) + vPC += defaultOffset; + else + vPC += callFrame->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(asString(scrutinee)->value().rep(), 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)->u.operand; + int func = (++vPC)->u.operand; + + callFrame[dst] = callFrame->codeBlock()->function(func)->makeFunction(callFrame, callFrame->scopeChain()); + + ++vPC; + 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)->u.operand; + int func = (++vPC)->u.operand; + + callFrame[dst] = callFrame->codeBlock()->functionExpression(func)->makeFunction(callFrame, callFrame->scopeChain()); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_call_eval) { + /* call_eval dst(r) 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 dst = vPC[1].u.operand; + int func = vPC[2].u.operand; + int argCount = vPC[3].u.operand; + int registerOffset = vPC[4].u.operand; + + JSValuePtr funcVal = callFrame[func].jsValue(callFrame); + + Register* newCallFrame = callFrame->registers() + registerOffset; + Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount; + JSValuePtr thisValue = argv[0].jsValue(callFrame); + JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject(); + + if (thisValue == globalObject && funcVal == globalObject->evalFunction()) { + JSValuePtr result = callEval(callFrame, registerFile, argv, argCount, registerOffset, exceptionValue); + if (exceptionValue) + goto vm_throw; + callFrame[dst] = result; + + vPC += 5; + 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 dst(r) 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 dst = vPC[1].u.operand; + int func = vPC[2].u.operand; + int argCount = vPC[3].u.operand; + int registerOffset = vPC[4].u.operand; + + JSValuePtr v = callFrame[func].jsValue(callFrame); + + CallData callData; + CallType callType = v->getCallData(callData); + + if (callType == CallTypeJS) { + ScopeChainNode* callDataScopeChain = callData.js.scopeChain; + FunctionBodyNode* functionBodyNode = callData.js.functionBody; + CodeBlock* newCodeBlock = &functionBodyNode->bytecode(callDataScopeChain); + + CallFrame* previousCallFrame = callFrame; + + callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); + if (UNLIKELY(!callFrame)) { + callFrame = previousCallFrame; + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + callFrame->init(newCodeBlock, vPC + 5, callDataScopeChain, previousCallFrame, dst, argCount, asFunction(v)); + vPC = newCodeBlock->instructions().begin(); + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + + NEXT_INSTRUCTION(); + } + + if (callType == CallTypeHost) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); + newCallFrame->init(0, vPC + 5, scopeChain, callFrame, dst, argCount, 0); + + Register* thisRegister = newCallFrame->registers() - RegisterFile::CallFrameHeaderSize - argCount; + ArgList args(thisRegister + 1, argCount - 1); + + // FIXME: All host methods should be calling toThisObject, but this is not presently the case. + JSValuePtr thisValue = thisRegister->jsValue(callFrame); + if (thisValue == jsNull()) + thisValue = callFrame->globalThisValue(); + + JSValuePtr returnValue; + { + SamplingTool::HostCallRecord callRecord(m_sampler); + returnValue = callData.native.function(newCallFrame, asObject(v), thisValue, args); + } + CHECK_FOR_EXCEPTION(); + + callFrame[dst] = JSValuePtr(returnValue); + + vPC += 5; + NEXT_INSTRUCTION(); + } + + ASSERT(callType == CallTypeNone); + + exceptionValue = createNotAFunctionError(callFrame, v, vPC - callFrame->codeBlock()->instructions().begin(), callFrame->codeBlock()); + goto vm_throw; + } + DEFINE_OPCODE(op_tear_off_activation) { + /* tear_off_activation activation(r) + + Copy all locals and parameters to new memory allocated on + the heap, and make the passed activation use this memory + in the future when looking up entries in the symbol table. + If there is an 'arguments' object, then it will also use + this memory for storing the named parameters, but not any + extra arguments. + + This opcode should only be used immediately before op_ret. + */ + + int src = (++vPC)->u.operand; + ASSERT(callFrame->codeBlock()->needsFullScopeChain()); + + asActivation(callFrame[src].getJSValue())->copyRegisters(callFrame->optionalCalleeArguments()); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_tear_off_arguments) { + /* tear_off_arguments + + Copy all arguments to new memory allocated on the heap, + and make the 'arguments' object use this memory in the + future when looking up named parameters, but not any + extra arguments. If an activation object exists for the + current function context, then the tear_off_activation + opcode should be used instead. + + This opcode should only be used immediately before op_ret. + */ + + ASSERT(callFrame->codeBlock()->usesArguments() && !callFrame->codeBlock()->needsFullScopeChain()); + + callFrame->optionalCalleeArguments()->copyRegisters(); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_ret) { + /* 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)->u.operand; + + if (callFrame->codeBlock()->needsFullScopeChain()) + callFrame->scopeChain()->deref(); + + JSValuePtr returnValue = callFrame[result].jsValue(callFrame); + + vPC = callFrame->returnPC(); + int dst = callFrame->returnValueRegister(); + callFrame = callFrame->callerFrame(); + + if (callFrame->hasHostCallFrameFlag()) + return returnValue; + + callFrame[dst] = JSValuePtr(returnValue); + + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_enter) { + /* enter + + Initializes local variables to undefined and fills constant + registers with their values. If the code block requires an + activation, enter_with_activation should be used instead. + + This opcode should only be used at the beginning of a code + block. + */ + + size_t i = 0; + CodeBlock* codeBlock = callFrame->codeBlock(); + + for (size_t count = codeBlock->m_numVars; i < count; ++i) + callFrame[i] = jsUndefined(); + + for (size_t count = codeBlock->numberOfConstantRegisters(), j = 0; j < count; ++i, ++j) + callFrame[i] = codeBlock->constantRegister(j); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_enter_with_activation) { + /* enter_with_activation dst(r) + + Initializes local variables to undefined, fills constant + registers with their values, creates an activation object, + and places the new activation both in dst and at the top + of the scope chain. If the code block does not require an + activation, enter should be used instead. + + This opcode should only be used at the beginning of a code + block. + */ + + size_t i = 0; + CodeBlock* codeBlock = callFrame->codeBlock(); + + for (size_t count = codeBlock->m_numVars; i < count; ++i) + callFrame[i] = jsUndefined(); + + for (size_t count = codeBlock->numberOfConstantRegisters(), j = 0; j < count; ++i, ++j) + callFrame[i] = codeBlock->constantRegister(j); + + int dst = (++vPC)->u.operand; + JSActivation* activation = new (globalData) JSActivation(callFrame, static_cast<FunctionBodyNode*>(codeBlock->ownerNode())); + callFrame[dst] = activation; + callFrame->setScopeChain(callFrame->scopeChain()->copy()->push(activation)); + + ++vPC; + 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)->u.operand; + JSValuePtr thisVal = callFrame[thisRegister].getJSValue(); + if (thisVal->needsThisConversion()) + callFrame[thisRegister] = JSValuePtr(thisVal->toThisObject(callFrame)); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_create_arguments) { + /* create_arguments + + Creates the 'arguments' object and places it in both the + 'arguments' call frame slot and the local 'arguments' + register. + + This opcode should only be used at the beginning of a code + block. + */ + + Arguments* arguments = new (globalData) Arguments(callFrame); + callFrame->setCalleeArguments(arguments); + callFrame[RegisterFile::ArgumentsRegister] = arguments; + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_construct) { + /* construct dst(r) 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 dst = vPC[1].u.operand; + int func = vPC[2].u.operand; + int argCount = vPC[3].u.operand; + int registerOffset = vPC[4].u.operand; + int proto = vPC[5].u.operand; + int thisRegister = vPC[6].u.operand; + + JSValuePtr v = callFrame[func].jsValue(callFrame); + + ConstructData constructData; + ConstructType constructType = v->getConstructData(constructData); + + if (constructType == ConstructTypeJS) { + ScopeChainNode* callDataScopeChain = constructData.js.scopeChain; + FunctionBodyNode* functionBodyNode = constructData.js.functionBody; + CodeBlock* newCodeBlock = &functionBodyNode->bytecode(callDataScopeChain); + + Structure* structure; + JSValuePtr prototype = callFrame[proto].jsValue(callFrame); + if (prototype->isObject()) + structure = asObject(prototype)->inheritorID(); + else + structure = callDataScopeChain->globalObject()->emptyObjectStructure(); + JSObject* newObject = new (globalData) JSObject(structure); + + callFrame[thisRegister] = JSValuePtr(newObject); // "this" value + + CallFrame* previousCallFrame = callFrame; + + callFrame = slideRegisterWindowForCall(newCodeBlock, registerFile, callFrame, registerOffset, argCount); + if (UNLIKELY(!callFrame)) { + callFrame = previousCallFrame; + exceptionValue = createStackOverflowError(callFrame); + goto vm_throw; + } + + callFrame->init(newCodeBlock, vPC + 7, callDataScopeChain, previousCallFrame, dst, argCount, asFunction(v)); + vPC = newCodeBlock->instructions().begin(); + +#if ENABLE(OPCODE_STATS) + OpcodeStats::resetLastInstruction(); +#endif + + NEXT_INSTRUCTION(); + } + + if (constructType == ConstructTypeHost) { + ArgList args(callFrame->registers() + thisRegister + 1, argCount - 1); + + ScopeChainNode* scopeChain = callFrame->scopeChain(); + CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + registerOffset); + newCallFrame->init(0, vPC + 7, scopeChain, callFrame, dst, argCount, 0); + + JSValuePtr returnValue; + { + SamplingTool::HostCallRecord callRecord(m_sampler); + returnValue = constructData.native.function(newCallFrame, asObject(v), args); + } + CHECK_FOR_EXCEPTION(); + callFrame[dst] = JSValuePtr(returnValue); + + vPC += 7; + NEXT_INSTRUCTION(); + } + + ASSERT(constructType == ConstructTypeNone); + + exceptionValue = createNotAConstructorError(callFrame, v, vPC - callFrame->codeBlock()->instructions().begin(), callFrame->codeBlock()); + goto vm_throw; + } + DEFINE_OPCODE(op_construct_verify) { + /* construct_verify dst(r) override(r) + + Verifies that register dst holds an object. If not, moves + the object in register override to register dst. + */ + + int dst = vPC[1].u.operand;; + if (LIKELY(callFrame[dst].jsValue(callFrame)->isObject())) { + vPC += 3; + NEXT_INSTRUCTION(); + } + + int override = vPC[2].u.operand; + callFrame[dst] = callFrame[override]; + + vPC += 3; + 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)->u.operand; + JSValuePtr v = callFrame[scope].jsValue(callFrame); + JSObject* o = v->toObject(callFrame); + CHECK_FOR_EXCEPTION(); + + callFrame[scope] = JSValuePtr(o); + callFrame->setScopeChain(callFrame->scopeChain()->push(o)); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_pop_scope) { + /* pop_scope + + Removes the top item from the current scope chain. + */ + callFrame->setScopeChain(callFrame->scopeChain()->pop()); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_get_pnames) { + /* get_pnames dst(r) base(r) + + Creates a property name list for register base and puts it + in register dst. This is not a true JavaScript value, just + a synthetic value used to keep the iteration state in a + register. + */ + int dst = (++vPC)->u.operand; + int base = (++vPC)->u.operand; + + callFrame[dst] = JSPropertyNameIterator::create(callFrame, callFrame[base].jsValue(callFrame)); + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_next_pname) { + /* next_pname dst(r) iter(r) target(offset) + + Tries to copies the next name from property name list in + register iter. If there are names left, then copies one to + register dst, and jumps to offset target. If there are none + left, invalidates the iterator and continues to the next + instruction. + */ + int dst = (++vPC)->u.operand; + int iter = (++vPC)->u.operand; + int target = (++vPC)->u.operand; + + JSPropertyNameIterator* it = callFrame[iter].propertyNameIterator(); + if (JSValuePtr temp = it->next(callFrame)) { + CHECK_FOR_TIMEOUT(); + callFrame[dst] = JSValuePtr(temp); + vPC += target; + NEXT_INSTRUCTION(); + } + it->invalidate(); + + ++vPC; + 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)->u.operand; + int target = (++vPC)->u.operand; + + ScopeChainNode* tmp = callFrame->scopeChain(); + while (count--) + tmp = tmp->pop(); + callFrame->setScopeChain(tmp); + + vPC += target; + NEXT_INSTRUCTION(); + } +#if HAVE(COMPUTED_GOTO) + // 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 += 4; + NEXT_INSTRUCTION(); + } +#if HAVE(COMPUTED_GOTO) + skip_new_scope: +#endif + DEFINE_OPCODE(op_catch) { + /* catch ex(r) + + Retrieves the VMs 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)->u.operand; + callFrame[ex] = exceptionValue; + exceptionValue = noValue(); + + ++vPC; + 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)->u.operand; + exceptionValue = callFrame[ex].jsValue(callFrame); + + handler = throwException(callFrame, exceptionValue, vPC - callFrame->codeBlock()->instructions().begin(), true); + if (!handler) { + *exception = exceptionValue; + return jsNull(); + } + + vPC = callFrame->codeBlock()->instructions().begin() + handler->target; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_unexpected_load) { + /* unexpected_load load dst(r) src(k) + + Copies constant src to register dst. + */ + int dst = (++vPC)->u.operand; + int src = (++vPC)->u.operand; + callFrame[dst] = JSValuePtr(callFrame->codeBlock()->unexpectedConstant(src)); + + ++vPC; + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(op_new_error) { + /* new_error dst(r) type(n) message(k) + + Constructs a new Error instance using the original + constructor, using immediate number n as the type and + constant message as the message string. The result is + written to register dst. + */ + int dst = (++vPC)->u.operand; + int type = (++vPC)->u.operand; + int message = (++vPC)->u.operand; + + CodeBlock* codeBlock = callFrame->codeBlock(); + callFrame[dst] = JSValuePtr(Error::create(callFrame, (ErrorType)type, codeBlock->unexpectedConstant(message)->toString(callFrame), codeBlock->lineNumberForBytecodeOffset(callFrame, vPC - codeBlock->instructions().begin()), codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL())); + + ++vPC; + NEXT_INSTRUCTION(); + } + 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 (callFrame->codeBlock()->needsFullScopeChain()) { + ScopeChainNode* scopeChain = callFrame->scopeChain(); + ASSERT(scopeChain->refCount > 1); + scopeChain->deref(); + } + int result = (++vPC)->u.operand; + return callFrame[result].jsValue(callFrame); + } + 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)->u.operand; + int property = (++vPC)->u.operand; + int function = (++vPC)->u.operand; + + ASSERT(callFrame[base].jsValue(callFrame)->isObject()); + JSObject* baseObj = asObject(callFrame[base].jsValue(callFrame)); + Identifier& ident = callFrame->codeBlock()->identifier(property); + ASSERT(callFrame[function].jsValue(callFrame)->isObject()); + baseObj->defineGetter(callFrame, ident, asObject(callFrame[function].jsValue(callFrame))); + + ++vPC; + 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)->u.operand; + int property = (++vPC)->u.operand; + int function = (++vPC)->u.operand; + + ASSERT(callFrame[base].jsValue(callFrame)->isObject()); + JSObject* baseObj = asObject(callFrame[base].jsValue(callFrame)); + Identifier& ident = callFrame->codeBlock()->identifier(property); + ASSERT(callFrame[function].jsValue(callFrame)->isObject()); + baseObj->defineSetter(callFrame, ident, asObject(callFrame[function].jsValue(callFrame))); + + ++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)->u.operand; + int target = (++vPC)->u.operand; + callFrame[retAddrDst] = vPC + 1; + + 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)->u.operand; + vPC = callFrame[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)->u.operand; + int firstLine = (++vPC)->u.operand; + int lastLine = (++vPC)->u.operand; + + debug(callFrame, static_cast<DebugHookID>(debugHookID), firstLine, lastLine); + + ++vPC; + 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[function].jsValue(callFrame)); + + vPC += 2; + 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[function].jsValue(callFrame)); + + vPC += 2; + NEXT_INSTRUCTION(); + } + vm_throw: { + globalData->exception = noValue(); + 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 - callFrame->codeBlock()->instructions().begin(), false); + if (!handler) { + *exception = exceptionValue; + return jsNull(); + } + + vPC = callFrame->codeBlock()->instructions().begin() + handler->target; + NEXT_INSTRUCTION(); + } + } +#if !HAVE(COMPUTED_GOTO) + } // iterator loop ends +#endif + #undef NEXT_INSTRUCTION + #undef DEFINE_OPCODE + #undef CHECK_FOR_EXCEPTION + #undef CHECK_FOR_TIMEOUT +} + +JSValuePtr 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); + SymbolTable& symbolTable = codeBlock->symbolTable(); + int argumentsIndex = symbolTable.get(functionCallFrame->propertyNames().arguments.ustring().rep()).getIndex(); + return functionCallFrame[argumentsIndex].jsValue(callFrame); + } + + Arguments* arguments = functionCallFrame->optionalCalleeArguments(); + if (!arguments) { + arguments = new (functionCallFrame) Arguments(functionCallFrame); + arguments->copyRegisters(); + callFrame->setCalleeArguments(arguments); + } + + return arguments; +} + +JSValuePtr Interpreter::retrieveCaller(CallFrame* callFrame, InternalFunction* function) const +{ + CallFrame* functionCallFrame = findFunctionCallFrame(callFrame, function); + if (!functionCallFrame) + return jsNull(); + + CallFrame* callerFrame = functionCallFrame->callerFrame(); + if (callerFrame->hasHostCallFrameFlag()) + return jsNull(); + + JSValuePtr caller = callerFrame->callee(); + if (!caller) + return jsNull(); + + return caller; +} + +void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, UString& sourceURL, JSValuePtr& function) const +{ + function = noValue(); + lineNumber = -1; + sourceURL = UString(); + + CallFrame* callerFrame = callFrame->callerFrame(); + if (callerFrame->hasHostCallFrameFlag()) + return; + + CodeBlock* callerCodeBlock = callerFrame->codeBlock(); + if (!callerCodeBlock) + return; + + unsigned bytecodeOffset = bytecodeOffsetForPC(callerCodeBlock, callFrame->returnPC()); + lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(callerFrame, bytecodeOffset - 1); + sourceID = callerCodeBlock->ownerNode()->sourceID(); + sourceURL = callerCodeBlock->ownerNode()->sourceURL(); + function = callerFrame->callee(); +} + +CallFrame* Interpreter::findFunctionCallFrame(CallFrame* callFrame, InternalFunction* function) +{ + for (CallFrame* candidate = callFrame; candidate; candidate = candidate->callerFrame()->removeHostCallFrameFlag()) { + if (candidate->callee() == function) + return candidate; + } + return 0; +} + +#if ENABLE(JIT) + +#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + +NEVER_INLINE void Interpreter::tryCTICachePutByID(CallFrame* callFrame, CodeBlock* codeBlock, void* returnAddress, JSValuePtr baseValue, const PutPropertySlot& slot) +{ + // The interpreter checks for recursion here; I do not believe this can occur in CTI. + + if (JSImmediate::isImmediate(baseValue)) + return; + + // Uncacheable: give up. + if (!slot.isCacheable()) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_put_by_id_generic)); + return; + } + + JSCell* baseCell = asCell(baseValue); + Structure* structure = baseCell->structure(); + + if (structure->isDictionary()) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_put_by_id_generic)); + return; + } + + // If baseCell != base, then baseCell must be a proxy for another object. + if (baseCell != slot.base()) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_put_by_id_generic)); + return; + } + + StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress); + + // Cache hit: Specialize instruction and ref Structures. + + // Structure transition, cache transition info + if (slot.type() == PutPropertySlot::NewProperty) { + StructureChain* chain = structure->cachedPrototypeChain(); + if (!chain) { + chain = cachePrototypeChain(callFrame, structure); + if (!chain) { + // This happens if someone has manually inserted null into the prototype chain + stubInfo->opcodeID = op_put_by_id_generic; + return; + } + } + stubInfo->initPutByIdTransition(structure->previousID(), structure, chain); + JIT::compilePutByIdTransition(callFrame->scopeChain()->globalData, codeBlock, stubInfo, structure->previousID(), structure, slot.cachedOffset(), chain, returnAddress); + return; + } + + stubInfo->initPutByIdReplace(structure); + +#if USE(CTI_REPATCH_PIC) + UNUSED_PARAM(callFrame); + JIT::patchPutByIdReplace(stubInfo, structure, slot.cachedOffset(), returnAddress); +#else + JIT::compilePutByIdReplace(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress); +#endif +} + +NEVER_INLINE void Interpreter::tryCTICacheGetByID(CallFrame* callFrame, CodeBlock* codeBlock, void* returnAddress, JSValuePtr baseValue, const Identifier& propertyName, const PropertySlot& slot) +{ + // FIXME: Write a test that proves we need to check for recursion here just + // like the interpreter does, then add a check for recursion. + + // FIXME: Cache property access for immediates. + if (JSImmediate::isImmediate(baseValue)) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_get_by_id_generic)); + return; + } + + if (isJSArray(baseValue) && propertyName == callFrame->propertyNames().length) { +#if USE(CTI_REPATCH_PIC) + JIT::compilePatchGetArrayLength(callFrame->scopeChain()->globalData, codeBlock, returnAddress); +#else + ctiPatchCallByReturnAddress(returnAddress, m_ctiArrayLengthTrampoline); +#endif + return; + } + if (isJSString(baseValue) && propertyName == callFrame->propertyNames().length) { + // The tradeoff of compiling an patched inline string length access routine does not seem + // to pay off, so we currently only do this for arrays. + ctiPatchCallByReturnAddress(returnAddress, m_ctiStringLengthTrampoline); + return; + } + + // Uncacheable: give up. + if (!slot.isCacheable()) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_get_by_id_generic)); + return; + } + + JSCell* baseCell = asCell(baseValue); + Structure* structure = baseCell->structure(); + + if (structure->isDictionary()) { + ctiPatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(cti_op_get_by_id_generic)); + return; + } + + // In the interpreter the last structure is trapped here; in CTI we use the + // *_second method to achieve a similar (but not quite the same) effect. + + StructureStubInfo* stubInfo = &codeBlock->getStubInfo(returnAddress); + + // Cache hit: Specialize instruction and ref Structures. + + if (slot.slotBase() == baseValue) { + // set this up, so derefStructures can do it's job. + stubInfo->initGetByIdSelf(structure); + +#if USE(CTI_REPATCH_PIC) + JIT::patchGetByIdSelf(stubInfo, structure, slot.cachedOffset(), returnAddress); +#else + JIT::compileGetByIdSelf(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress); +#endif + return; + } + + if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { + ASSERT(slot.slotBase()->isObject()); + + JSObject* slotBaseObject = asObject(slot.slotBase()); + + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (slotBaseObject->structure()->isDictionary()) { + RefPtr<Structure> transition = Structure::fromDictionaryTransition(slotBaseObject->structure()); + slotBaseObject->setStructure(transition.release()); + asCell(baseValue)->structure()->setCachedPrototypeChain(0); + } + + stubInfo->initGetByIdProto(structure, slotBaseObject->structure()); + + JIT::compileGetByIdProto(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slotBaseObject->structure(), slot.cachedOffset(), returnAddress); + return; + } + + size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot); + if (!count) { + stubInfo->opcodeID = op_get_by_id_generic; + return; + } + + StructureChain* chain = structure->cachedPrototypeChain(); + if (!chain) + chain = cachePrototypeChain(callFrame, structure); + ASSERT(chain); + + stubInfo->initGetByIdChain(structure, chain); + + JIT::compileGetByIdChain(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, chain, count, slot.cachedOffset(), returnAddress); +} + +#endif + +#if USE(JIT_STUB_ARGUMENT_VA_LIST) +#define SETUP_VA_LISTL_ARGS va_list vl_args; va_start(vl_args, args) +#else // JIT_STUB_ARGUMENT_REGISTER or JIT_STUB_ARGUMENT_STACK +#define SETUP_VA_LISTL_ARGS +#endif + +#ifndef NDEBUG + +extern "C" { + +static void jscGeneratedNativeCode() +{ + // When executing a CTI function (which might do an allocation), we hack the return address + // to pretend to be executing this function, to keep stack logging tools from blowing out + // memory. +} + +} + +struct StackHack { + ALWAYS_INLINE StackHack(void** location) + { + returnAddressLocation = location; + savedReturnAddress = *returnAddressLocation; + ctiSetReturnAddress(returnAddressLocation, reinterpret_cast<void*>(jscGeneratedNativeCode)); + } + ALWAYS_INLINE ~StackHack() + { + ctiSetReturnAddress(returnAddressLocation, savedReturnAddress); + } + + void** returnAddressLocation; + void* savedReturnAddress; +}; + +#define BEGIN_STUB_FUNCTION() SETUP_VA_LISTL_ARGS; StackHack stackHack(&STUB_RETURN_ADDRESS_SLOT) +#define STUB_SET_RETURN_ADDRESS(address) stackHack.savedReturnAddress = address +#define STUB_RETURN_ADDRESS stackHack.savedReturnAddress + +#else + +#define BEGIN_STUB_FUNCTION() SETUP_VA_LISTL_ARGS +#define STUB_SET_RETURN_ADDRESS(address) ctiSetReturnAddress(&STUB_RETURN_ADDRESS_SLOT, address); +#define STUB_RETURN_ADDRESS STUB_RETURN_ADDRESS_SLOT + +#endif + +// The reason this is not inlined is to avoid having to do a PIC branch +// to get the address of the ctiVMThrowTrampoline function. It's also +// good to keep the code size down by leaving as much of the exception +// handling code out of line as possible. +static NEVER_INLINE void returnToThrowTrampoline(JSGlobalData* globalData, void* exceptionLocation, void*& returnAddressSlot) +{ + ASSERT(globalData->exception); + globalData->exceptionLocation = exceptionLocation; + ctiSetReturnAddress(&returnAddressSlot, reinterpret_cast<void*>(ctiVMThrowTrampoline)); +} + +static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalData* globalData, void* exceptionLocation, void*& returnAddressSlot) +{ + globalData->exception = createStackOverflowError(callFrame); + returnToThrowTrampoline(globalData, exceptionLocation, returnAddressSlot); +} + +#define VM_THROW_EXCEPTION() \ + do { \ + VM_THROW_EXCEPTION_AT_END(); \ + return 0; \ + } while (0) +#define VM_THROW_EXCEPTION_2() \ + do { \ + VM_THROW_EXCEPTION_AT_END(); \ + RETURN_PAIR(0, 0); \ + } while (0) +#define VM_THROW_EXCEPTION_AT_END() \ + returnToThrowTrampoline(ARG_globalData, STUB_RETURN_ADDRESS, STUB_RETURN_ADDRESS) + +#define CHECK_FOR_EXCEPTION() \ + do { \ + if (UNLIKELY(ARG_globalData->exception != noValue())) \ + VM_THROW_EXCEPTION(); \ + } while (0) +#define CHECK_FOR_EXCEPTION_AT_END() \ + do { \ + if (UNLIKELY(ARG_globalData->exception != noValue())) \ + VM_THROW_EXCEPTION_AT_END(); \ + } while (0) +#define CHECK_FOR_EXCEPTION_VOID() \ + do { \ + if (UNLIKELY(ARG_globalData->exception != noValue())) { \ + VM_THROW_EXCEPTION_AT_END(); \ + return; \ + } \ + } while (0) + +JSObject* Interpreter::cti_op_convert_this(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v1 = ARG_src1; + CallFrame* callFrame = ARG_callFrame; + + JSObject* result = v1->toThisObject(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +void Interpreter::cti_op_end(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ScopeChainNode* scopeChain = ARG_callFrame->scopeChain(); + ASSERT(scopeChain->refCount > 1); + scopeChain->deref(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_add(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v1 = ARG_src1; + JSValuePtr v2 = ARG_src2; + + double left; + double right = 0.0; + + bool rightIsNumber = fastIsNumber(v2, right); + if (rightIsNumber && fastIsNumber(v1, left)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left + right)); + + CallFrame* callFrame = ARG_callFrame; + + bool leftIsString = v1->isString(); + if (leftIsString && v2->isString()) { + RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); + if (UNLIKELY(!value)) { + throwOutOfMemoryError(callFrame); + VM_THROW_EXCEPTION(); + } + + return JSValuePtr::encode(jsString(ARG_globalData, value.release())); + } + + if (rightIsNumber & leftIsString) { + RefPtr<UString::Rep> value = JSImmediate::isImmediate(v2) ? + concatenate(asString(v1)->value().rep(), JSImmediate::getTruncatedInt32(v2)) : + concatenate(asString(v1)->value().rep(), right); + + if (UNLIKELY(!value)) { + throwOutOfMemoryError(callFrame); + VM_THROW_EXCEPTION(); + } + return JSValuePtr::encode(jsString(ARG_globalData, value.release())); + } + + // All other cases are pretty uncommon + JSValuePtr result = jsAddSlowCase(callFrame, v1, v2); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_pre_inc(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, v->toNumber(callFrame) + 1); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +int Interpreter::cti_timeout_check(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + Interpreter* interpreter = ARG_globalData->interpreter; + + if (interpreter->checkTimeout(ARG_callFrame->dynamicGlobalObject())) { + ARG_globalData->exception = createInterruptedExecutionException(ARG_globalData); + VM_THROW_EXCEPTION_AT_END(); + } + + return interpreter->m_ticksUntilNextTimeoutCheck; +} + +void Interpreter::cti_register_file_check(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + if (LIKELY(ARG_registerFile->grow(ARG_callFrame + ARG_callFrame->codeBlock()->m_numCalleeRegisters))) + return; + + // Rewind to the previous call frame because op_call already optimistically + // moved the call frame forward. + CallFrame* oldCallFrame = ARG_callFrame->callerFrame(); + ARG_setCallFrame(oldCallFrame); + throwStackOverflowError(oldCallFrame, ARG_globalData, oldCallFrame->returnPC(), STUB_RETURN_ADDRESS); +} + +int Interpreter::cti_op_loop_if_less(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + CallFrame* callFrame = ARG_callFrame; + + bool result = jsLess(callFrame, src1, src2); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +int Interpreter::cti_op_loop_if_lesseq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + CallFrame* callFrame = ARG_callFrame; + + bool result = jsLessEq(callFrame, src1, src2); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +JSObject* Interpreter::cti_op_new_object(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return constructEmptyObject(ARG_callFrame); +} + +void Interpreter::cti_op_put_by_id_generic(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + PutPropertySlot slot; + ARG_src1->put(ARG_callFrame, *ARG_id2, ARG_src3, slot); + CHECK_FOR_EXCEPTION_AT_END(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_generic(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS) + +void Interpreter::cti_op_put_by_id(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + PutPropertySlot slot; + ARG_src1->put(callFrame, ident, ARG_src3, slot); + + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_put_by_id_second)); + + CHECK_FOR_EXCEPTION_AT_END(); +} + +void Interpreter::cti_op_put_by_id_second(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + PutPropertySlot slot; + ARG_src1->put(ARG_callFrame, *ARG_id2, ARG_src3, slot); + ARG_globalData->interpreter->tryCTICachePutByID(ARG_callFrame, ARG_callFrame->codeBlock(), STUB_RETURN_ADDRESS, ARG_src1, slot); + CHECK_FOR_EXCEPTION_AT_END(); +} + +void Interpreter::cti_op_put_by_id_fail(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + PutPropertySlot slot; + ARG_src1->put(callFrame, ident, ARG_src3, slot); + + CHECK_FOR_EXCEPTION_AT_END(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_second)); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_second(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + + ARG_globalData->interpreter->tryCTICacheGetByID(callFrame, callFrame->codeBlock(), STUB_RETURN_ADDRESS, baseValue, ident, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_self_fail(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Identifier& ident = *ARG_id2; + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, ident, slot); + + CHECK_FOR_EXCEPTION(); + + if (!JSImmediate::isImmediate(baseValue) + && slot.isCacheable() + && !asCell(baseValue)->structure()->isDictionary() + && slot.slotBase() == baseValue) { + + CodeBlock* codeBlock = callFrame->codeBlock(); + StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS); + + ASSERT(slot.slotBase()->isObject()); + + PolymorphicAccessStructureList* polymorphicStructureList; + int listIndex = 1; + + if (stubInfo->opcodeID == op_get_by_id_self) { + ASSERT(!stubInfo->stubRoutine); + polymorphicStructureList = new PolymorphicAccessStructureList(0, stubInfo->u.getByIdSelf.baseObjectStructure); + stubInfo->initGetByIdSelfList(polymorphicStructureList, 2); + } else { + polymorphicStructureList = stubInfo->u.getByIdSelfList.structureList; + listIndex = stubInfo->u.getByIdSelfList.listSize; + stubInfo->u.getByIdSelfList.listSize++; + } + + JIT::compileGetByIdSelfList(callFrame->scopeChain()->globalData, codeBlock, stubInfo, polymorphicStructureList, listIndex, asCell(baseValue)->structure(), slot.cachedOffset()); + + if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1)) + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_generic)); + } else { + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_generic)); + } + return JSValuePtr::encode(result); +} + +static PolymorphicAccessStructureList* getPolymorphicAccessStructureListSlot(StructureStubInfo* stubInfo, int& listIndex) +{ + PolymorphicAccessStructureList* prototypeStructureList = 0; + listIndex = 1; + + switch (stubInfo->opcodeID) { + case op_get_by_id_proto: + prototypeStructureList = new PolymorphicAccessStructureList(stubInfo->stubRoutine, stubInfo->u.getByIdProto.baseObjectStructure, stubInfo->u.getByIdProto.prototypeStructure); + stubInfo->stubRoutine = 0; + stubInfo->initGetByIdProtoList(prototypeStructureList, 2); + break; + case op_get_by_id_chain: + prototypeStructureList = new PolymorphicAccessStructureList(stubInfo->stubRoutine, stubInfo->u.getByIdChain.baseObjectStructure, stubInfo->u.getByIdChain.chain); + stubInfo->stubRoutine = 0; + stubInfo->initGetByIdProtoList(prototypeStructureList, 2); + break; + case op_get_by_id_proto_list: + prototypeStructureList = stubInfo->u.getByIdProtoList.structureList; + listIndex = stubInfo->u.getByIdProtoList.listSize; + stubInfo->u.getByIdProtoList.listSize++; + break; + default: + ASSERT_NOT_REACHED(); + } + + ASSERT(listIndex < POLYMORPHIC_LIST_CACHE_SIZE); + return prototypeStructureList; +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_proto_list(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(callFrame, *ARG_id2, slot); + + CHECK_FOR_EXCEPTION(); + + if (JSImmediate::isImmediate(baseValue) || !slot.isCacheable() || asCell(baseValue)->structure()->isDictionary()) { + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail)); + return JSValuePtr::encode(result); + } + + Structure* structure = asCell(baseValue)->structure(); + CodeBlock* codeBlock = callFrame->codeBlock(); + StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS); + + ASSERT(slot.slotBase()->isObject()); + JSObject* slotBaseObject = asObject(slot.slotBase()); + + if (slot.slotBase() == baseValue) + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail)); + else if (slot.slotBase() == asCell(baseValue)->structure()->prototypeForLookup(callFrame)) { + // Since we're accessing a prototype in a loop, it's a good bet that it + // should not be treated as a dictionary. + if (slotBaseObject->structure()->isDictionary()) { + RefPtr<Structure> transition = Structure::fromDictionaryTransition(slotBaseObject->structure()); + slotBaseObject->setStructure(transition.release()); + asCell(baseValue)->structure()->setCachedPrototypeChain(0); + } + + int listIndex; + PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex); + + JIT::compileGetByIdProtoList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, slotBaseObject->structure(), slot.cachedOffset()); + + if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1)) + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full)); + } else if (size_t count = countPrototypeChainEntriesAndCheckForProxies(callFrame, baseValue, slot)) { + StructureChain* chain = structure->cachedPrototypeChain(); + if (!chain) + chain = cachePrototypeChain(callFrame, structure); + ASSERT(chain); + + int listIndex; + PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex); + + JIT::compileGetByIdChainList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, chain, count, slot.cachedOffset()); + + if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1)) + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full)); + } else + ctiPatchCallByReturnAddress(STUB_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail)); + + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_proto_list_full(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(ARG_callFrame, *ARG_id2, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_proto_fail(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(ARG_callFrame, *ARG_id2, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_array_fail(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(ARG_callFrame, *ARG_id2, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_id_string_fail(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr baseValue = ARG_src1; + PropertySlot slot(baseValue); + JSValuePtr result = baseValue->get(ARG_callFrame, *ARG_id2, slot); + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +#endif + +JSValueEncodedAsPointer* Interpreter::cti_op_instanceof(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr value = ARG_src1; + JSValuePtr baseVal = ARG_src2; + JSValuePtr proto = ARG_src3; + + // at least one of these checks must have failed to get to the slow case + ASSERT(JSImmediate::isAnyImmediate(value, baseVal, proto) + || !value->isObject() || !baseVal->isObject() || !proto->isObject() + || (asObject(baseVal)->structure()->typeInfo().flags() & (ImplementsHasInstance | OverridesHasInstance)) != ImplementsHasInstance); + + if (!baseVal->isObject()) { + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createInvalidParamError(callFrame, "instanceof", baseVal, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); + } + + if (!asObject(baseVal)->structure()->typeInfo().implementsHasInstance()) + return JSValuePtr::encode(jsBoolean(false)); + + if (!proto->isObject()) { + throwError(callFrame, TypeError, "instanceof called on an object with an invalid prototype property."); + VM_THROW_EXCEPTION(); + } + + if (!value->isObject()) + return JSValuePtr::encode(jsBoolean(false)); + + JSValuePtr result = jsBoolean(asObject(baseVal)->hasInstance(callFrame, value, proto)); + CHECK_FOR_EXCEPTION_AT_END(); + + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_del_by_id(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + JSObject* baseObj = ARG_src1->toObject(callFrame); + + JSValuePtr result = jsBoolean(baseObj->deleteProperty(callFrame, *ARG_id2)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_mul(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + double left; + double right; + if (fastIsNumber(src1, left) && fastIsNumber(src2, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left * right)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, src1->toNumber(callFrame) * src2->toNumber(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSObject* Interpreter::cti_op_new_func(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return ARG_func1->makeFunction(ARG_callFrame, ARG_callFrame->scopeChain()); +} + +void* Interpreter::cti_op_call_JSFunction(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + +#ifndef NDEBUG + CallData callData; + ASSERT(ARG_src1->getCallData(callData) == CallTypeJS); +#endif + + ScopeChainNode* callDataScopeChain = asFunction(ARG_src1)->m_scopeChain.node(); + CodeBlock* newCodeBlock = &asFunction(ARG_src1)->body()->bytecode(callDataScopeChain); + + if (!newCodeBlock->jitCode()) + JIT::compile(ARG_globalData, newCodeBlock); + + return newCodeBlock; +} + +VoidPtrPair Interpreter::cti_op_call_arityCheck(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + CodeBlock* newCodeBlock = ARG_codeBlock4; + int argCount = ARG_int3; + + ASSERT(argCount != newCodeBlock->m_numParameters); + + CallFrame* oldCallFrame = callFrame->callerFrame(); + + if (argCount > newCodeBlock->m_numParameters) { + size_t numParameters = newCodeBlock->m_numParameters; + Register* r = callFrame->registers() + numParameters; + + Register* argv = r - RegisterFile::CallFrameHeaderSize - numParameters - argCount; + for (size_t i = 0; i < numParameters; ++i) + argv[i + argCount] = argv[i]; + + callFrame = CallFrame::create(r); + callFrame->setCallerFrame(oldCallFrame); + } else { + size_t omittedArgCount = newCodeBlock->m_numParameters - argCount; + Register* r = callFrame->registers() + omittedArgCount; + Register* newEnd = r + newCodeBlock->m_numCalleeRegisters; + if (!ARG_registerFile->grow(newEnd)) { + // Rewind to the previous call frame because op_call already optimistically + // moved the call frame forward. + ARG_setCallFrame(oldCallFrame); + throwStackOverflowError(oldCallFrame, ARG_globalData, ARG_returnAddress2, STUB_RETURN_ADDRESS); + RETURN_PAIR(0, 0); + } + + Register* argv = r - RegisterFile::CallFrameHeaderSize - omittedArgCount; + for (size_t i = 0; i < omittedArgCount; ++i) + argv[i] = jsUndefined(); + + callFrame = CallFrame::create(r); + callFrame->setCallerFrame(oldCallFrame); + } + + RETURN_PAIR(newCodeBlock, callFrame); +} + +void* Interpreter::cti_vm_dontLazyLinkCall(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSFunction* callee = asFunction(ARG_src1); + CodeBlock* codeBlock = &callee->body()->bytecode(callee->m_scopeChain.node()); + if (!codeBlock->jitCode()) + JIT::compile(ARG_globalData, codeBlock); + + ctiPatchCallByReturnAddress(ARG_returnAddress2, ARG_globalData->interpreter->m_ctiVirtualCallLink); + + return codeBlock->jitCode(); +} + +void* Interpreter::cti_vm_lazyLinkCall(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSFunction* callee = asFunction(ARG_src1); + CodeBlock* codeBlock = &callee->body()->bytecode(callee->m_scopeChain.node()); + if (!codeBlock->jitCode()) + JIT::compile(ARG_globalData, codeBlock); + + CallLinkInfo* callLinkInfo = &ARG_callFrame->callerFrame()->codeBlock()->getCallLinkInfo(ARG_returnAddress2); + JIT::linkCall(callee, codeBlock, codeBlock->jitCode(), callLinkInfo, ARG_int3); + + return codeBlock->jitCode(); +} + +JSObject* Interpreter::cti_op_push_activation(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSActivation* activation = new (ARG_globalData) JSActivation(ARG_callFrame, static_cast<FunctionBodyNode*>(ARG_callFrame->codeBlock()->ownerNode())); + ARG_callFrame->setScopeChain(ARG_callFrame->scopeChain()->copy()->push(activation)); + return activation; +} + +JSValueEncodedAsPointer* Interpreter::cti_op_call_NotJSFunction(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr funcVal = ARG_src1; + + CallData callData; + CallType callType = funcVal->getCallData(callData); + + ASSERT(callType != CallTypeJS); + + if (callType == CallTypeHost) { + int registerOffset = ARG_int2; + int argCount = ARG_int3; + CallFrame* previousCallFrame = ARG_callFrame; + CallFrame* callFrame = CallFrame::create(previousCallFrame->registers() + registerOffset); + + callFrame->init(0, static_cast<Instruction*>(STUB_RETURN_ADDRESS), previousCallFrame->scopeChain(), previousCallFrame, 0, argCount, 0); + ARG_setCallFrame(callFrame); + + Register* argv = ARG_callFrame->registers() - RegisterFile::CallFrameHeaderSize - argCount; + ArgList argList(argv + 1, argCount - 1); + + JSValuePtr returnValue; + { + SamplingTool::HostCallRecord callRecord(CTI_SAMPLER); + + // FIXME: All host methods should be calling toThisObject, but this is not presently the case. + JSValuePtr thisValue = argv[0].jsValue(callFrame); + if (thisValue == jsNull()) + thisValue = callFrame->globalThisValue(); + + returnValue = callData.native.function(callFrame, asObject(funcVal), thisValue, argList); + } + ARG_setCallFrame(previousCallFrame); + CHECK_FOR_EXCEPTION(); + + return JSValuePtr::encode(returnValue); + } + + ASSERT(callType == CallTypeNone); + + CodeBlock* codeBlock = ARG_callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createNotAFunctionError(ARG_callFrame, funcVal, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); +} + +void Interpreter::cti_op_create_arguments(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + Arguments* arguments = new (ARG_globalData) Arguments(ARG_callFrame); + ARG_callFrame->setCalleeArguments(arguments); + ARG_callFrame[RegisterFile::ArgumentsRegister] = arguments; +} + +void Interpreter::cti_op_create_arguments_no_params(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + Arguments* arguments = new (ARG_globalData) Arguments(ARG_callFrame, Arguments::NoParameters); + ARG_callFrame->setCalleeArguments(arguments); + ARG_callFrame[RegisterFile::ArgumentsRegister] = arguments; +} + +void Interpreter::cti_op_tear_off_activation(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ASSERT(ARG_callFrame->codeBlock()->needsFullScopeChain()); + asActivation(ARG_src1)->copyRegisters(ARG_callFrame->optionalCalleeArguments()); +} + +void Interpreter::cti_op_tear_off_arguments(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ASSERT(ARG_callFrame->codeBlock()->usesArguments() && !ARG_callFrame->codeBlock()->needsFullScopeChain()); + ARG_callFrame->optionalCalleeArguments()->copyRegisters(); +} + +void Interpreter::cti_op_profile_will_call(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ASSERT(*ARG_profilerReference); + (*ARG_profilerReference)->willExecute(ARG_callFrame, ARG_src1); +} + +void Interpreter::cti_op_profile_did_call(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ASSERT(*ARG_profilerReference); + (*ARG_profilerReference)->didExecute(ARG_callFrame, ARG_src1); +} + +void Interpreter::cti_op_ret_scopeChain(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ASSERT(ARG_callFrame->codeBlock()->needsFullScopeChain()); + ARG_callFrame->scopeChain()->deref(); +} + +JSObject* Interpreter::cti_op_new_array(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ArgList argList(&ARG_callFrame->registers()[ARG_int1], ARG_int2); + return constructArray(ARG_callFrame, argList); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_resolve(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + ScopeChainNode* scopeChain = callFrame->scopeChain(); + + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + + Identifier& ident = *ARG_id1; + do { + JSObject* o = *iter; + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); + } + } while (++iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createUndefinedVariableError(callFrame, ident, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); +} + +JSObject* Interpreter::cti_op_construct_JSConstruct(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + +#ifndef NDEBUG + ConstructData constructData; + ASSERT(asFunction(ARG_src1)->getConstructData(constructData) == ConstructTypeJS); +#endif + + Structure* structure; + if (ARG_src4->isObject()) + structure = asObject(ARG_src4)->inheritorID(); + else + structure = asFunction(ARG_src1)->m_scopeChain.node()->globalObject()->emptyObjectStructure(); + return new (ARG_globalData) JSObject(structure); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_construct_NotJSConstruct(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr constrVal = ARG_src1; + int argCount = ARG_int3; + int thisRegister = ARG_int5; + + ConstructData constructData; + ConstructType constructType = constrVal->getConstructData(constructData); + + if (constructType == ConstructTypeHost) { + ArgList argList(callFrame->registers() + thisRegister + 1, argCount - 1); + + JSValuePtr returnValue; + { + SamplingTool::HostCallRecord callRecord(CTI_SAMPLER); + returnValue = constructData.native.function(callFrame, asObject(constrVal), argList); + } + CHECK_FOR_EXCEPTION(); + + return JSValuePtr::encode(returnValue); + } + + ASSERT(constructType == ConstructTypeNone); + + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createNotAConstructorError(callFrame, constrVal, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_get_by_val(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Interpreter* interpreter = ARG_globalData->interpreter; + + JSValuePtr baseValue = ARG_src1; + JSValuePtr subscript = ARG_src2; + + JSValuePtr result; + unsigned i; + + bool isUInt32 = JSImmediate::getUInt32(subscript, i); + if (LIKELY(isUInt32)) { + if (interpreter->isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canGetIndex(i)) + result = jsArray->getIndex(i); + else + result = jsArray->JSArray::get(callFrame, i); + } else if (interpreter->isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) + return JSValuePtr::encode(asString(baseValue)->getIndex(ARG_globalData, i)); + else if (interpreter->isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) + return JSValuePtr::encode(asByteArray(baseValue)->getIndex(i)); + else + result = baseValue->get(callFrame, i); + } else { + Identifier property(callFrame, subscript->toString(callFrame)); + result = baseValue->get(callFrame, property); + } + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +VoidPtrPair Interpreter::cti_op_resolve_func(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + ScopeChainNode* scopeChain = callFrame->scopeChain(); + + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + + // FIXME: add scopeDepthIsZero optimization + + ASSERT(iter != end); + + Identifier& ident = *ARG_id1; + JSObject* base; + do { + base = *iter; + PropertySlot slot(base); + if (base->getPropertySlot(callFrame, ident, slot)) { + // ECMA 11.2.3 says that if we hit an activation the this value should be null. + // However, section 10.2.3 says that in the case where the value provided + // by the caller is null, the global object should be used. It also says + // that the section does not apply to internal functions, but for simplicity + // of implementation we use the global object anyway here. This guarantees + // that in host objects you always get a valid object for this. + // We also handle wrapper substitution for the global object at the same time. + JSObject* thisObj = base->toThisObject(callFrame); + JSValuePtr result = slot.getValue(callFrame, ident); + CHECK_FOR_EXCEPTION_AT_END(); + + RETURN_PAIR(thisObj, JSValuePtr::encode(result)); + } + ++iter; + } while (iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createUndefinedVariableError(callFrame, ident, vPCIndex, codeBlock); + VM_THROW_EXCEPTION_2(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_sub(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + double left; + double right; + if (fastIsNumber(src1, left) && fastIsNumber(src2, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left - right)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, src1->toNumber(callFrame) - src2->toNumber(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +void Interpreter::cti_op_put_by_val(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + Interpreter* interpreter = ARG_globalData->interpreter; + + JSValuePtr baseValue = ARG_src1; + JSValuePtr subscript = ARG_src2; + JSValuePtr value = ARG_src3; + + unsigned i; + + bool isUInt32 = JSImmediate::getUInt32(subscript, i); + if (LIKELY(isUInt32)) { + if (interpreter->isJSArray(baseValue)) { + JSArray* jsArray = asArray(baseValue); + if (jsArray->canSetIndex(i)) + jsArray->setIndex(i, value); + else + jsArray->JSArray::put(callFrame, i, value); + } else if (interpreter->isJSByteArray(baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { + JSByteArray* jsByteArray = asByteArray(baseValue); + double dValue = 0; + if (JSImmediate::isNumber(value)) { + jsByteArray->setIndex(i, JSImmediate::getTruncatedInt32(value)); + return; + } else if (fastIsNumber(value, dValue)) { + jsByteArray->setIndex(i, dValue); + return; + } else + baseValue->put(callFrame, i, value); + } else + baseValue->put(callFrame, i, value); + } else { + Identifier property(callFrame, subscript->toString(callFrame)); + if (!ARG_globalData->exception) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot; + baseValue->put(callFrame, property, value, slot); + } + } + + CHECK_FOR_EXCEPTION_AT_END(); +} + +void Interpreter::cti_op_put_by_val_array(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr baseValue = ARG_src1; + int i = ARG_int2; + JSValuePtr value = ARG_src3; + + ASSERT(ARG_globalData->interpreter->isJSArray(baseValue)); + + if (LIKELY(i >= 0)) + asArray(baseValue)->JSArray::put(callFrame, i, value); + else { + Identifier property(callFrame, JSImmediate::from(i)->toString(callFrame)); + // FIXME: can toString throw an exception here? + if (!ARG_globalData->exception) { // Don't put to an object if toString threw an exception. + PutPropertySlot slot; + baseValue->put(callFrame, property, value, slot); + } + } + + CHECK_FOR_EXCEPTION_AT_END(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_lesseq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsBoolean(jsLessEq(callFrame, ARG_src1, ARG_src2)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +int Interpreter::cti_op_loop_if_true(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + + bool result = src1->toBoolean(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +JSValueEncodedAsPointer* Interpreter::cti_op_negate(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src = ARG_src1; + + double v; + if (fastIsNumber(src, v)) + return JSValuePtr::encode(jsNumber(ARG_globalData, -v)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, -src->toNumber(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_resolve_base(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(inlineResolveBase(ARG_callFrame, *ARG_id1, ARG_callFrame->scopeChain())); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_resolve_skip(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + ScopeChainNode* scopeChain = callFrame->scopeChain(); + + int skip = ARG_int2; + + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + ASSERT(iter != end); + while (skip--) { + ++iter; + ASSERT(iter != end); + } + Identifier& ident = *ARG_id1; + do { + JSObject* o = *iter; + PropertySlot slot(o); + if (o->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); + } + } while (++iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createUndefinedVariableError(callFrame, ident, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_resolve_global(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + JSGlobalObject* globalObject = asGlobalObject(ARG_src1); + Identifier& ident = *ARG_id2; + unsigned globalResolveInfoIndex = ARG_int3; + ASSERT(globalObject->isGlobalObject()); + + PropertySlot slot(globalObject); + if (globalObject->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + if (slot.isCacheable() && !globalObject->structure()->isDictionary()) { + GlobalResolveInfo& globalResolveInfo = callFrame->codeBlock()->globalResolveInfo(globalResolveInfoIndex); + if (globalResolveInfo.structure) + globalResolveInfo.structure->deref(); + globalObject->structure()->ref(); + globalResolveInfo.structure = globalObject->structure(); + globalResolveInfo.offset = slot.cachedOffset(); + return JSValuePtr::encode(result); + } + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); + } + + unsigned vPCIndex = ARG_callFrame->codeBlock()->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createUndefinedVariableError(callFrame, ident, vPCIndex, callFrame->codeBlock()); + VM_THROW_EXCEPTION(); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_div(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + double left; + double right; + if (fastIsNumber(src1, left) && fastIsNumber(src2, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left / right)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, src1->toNumber(callFrame) / src2->toNumber(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_pre_dec(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, v->toNumber(callFrame) - 1); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +int Interpreter::cti_op_jless(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + CallFrame* callFrame = ARG_callFrame; + + bool result = jsLess(callFrame, src1, src2); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +JSValueEncodedAsPointer* Interpreter::cti_op_not(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr result = jsBoolean(!src->toBoolean(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +int Interpreter::cti_op_jtrue(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + + bool result = src1->toBoolean(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + return result; +} + +VoidPtrPair Interpreter::cti_op_post_inc(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr number = v->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + + RETURN_PAIR(JSValuePtr::encode(number), JSValuePtr::encode(jsNumber(ARG_globalData, number->uncheckedGetNumber() + 1))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_eq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + CallFrame* callFrame = ARG_callFrame; + + ASSERT(!JSImmediate::areBothImmediateNumbers(src1, src2)); + JSValuePtr result = jsBoolean(equalSlowCaseInline(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_lshift(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr val = ARG_src1; + JSValuePtr shift = ARG_src2; + + int32_t left; + uint32_t right; + if (JSImmediate::areBothImmediateNumbers(val, shift)) + return JSValuePtr::encode(jsNumber(ARG_globalData, JSImmediate::getTruncatedInt32(val) << (JSImmediate::getTruncatedUInt32(shift) & 0x1f))); + if (fastToInt32(val, left) && fastToUInt32(shift, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left << (right & 0x1f))); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, (val->toInt32(callFrame)) << (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_bitand(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + int32_t left; + int32_t right; + if (fastToInt32(src1, left) && fastToInt32(src2, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left & right)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, src1->toInt32(callFrame) & src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_rshift(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr val = ARG_src1; + JSValuePtr shift = ARG_src2; + + int32_t left; + uint32_t right; + if (JSImmediate::areBothImmediateNumbers(val, shift)) + return JSValuePtr::encode(JSImmediate::rightShiftImmediateNumbers(val, shift)); + if (fastToInt32(val, left) && fastToUInt32(shift, right)) + return JSValuePtr::encode(jsNumber(ARG_globalData, left >> (right & 0x1f))); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, (val->toInt32(callFrame)) >> (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_bitnot(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src = ARG_src1; + + int value; + if (fastToInt32(src, value)) + return JSValuePtr::encode(jsNumber(ARG_globalData, ~value)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsNumber(ARG_globalData, ~src->toInt32(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +VoidPtrPair Interpreter::cti_op_resolve_with_base(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + ScopeChainNode* scopeChain = callFrame->scopeChain(); + + ScopeChainIterator iter = scopeChain->begin(); + ScopeChainIterator end = scopeChain->end(); + + // FIXME: add scopeDepthIsZero optimization + + ASSERT(iter != end); + + Identifier& ident = *ARG_id1; + JSObject* base; + do { + base = *iter; + PropertySlot slot(base); + if (base->getPropertySlot(callFrame, ident, slot)) { + JSValuePtr result = slot.getValue(callFrame, ident); + CHECK_FOR_EXCEPTION_AT_END(); + + RETURN_PAIR(base, JSValuePtr::encode(result)); + } + ++iter; + } while (iter != end); + + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createUndefinedVariableError(callFrame, ident, vPCIndex, codeBlock); + VM_THROW_EXCEPTION_2(); +} + +JSObject* Interpreter::cti_op_new_func_exp(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return ARG_funcexp1->makeFunction(ARG_callFrame, ARG_callFrame->scopeChain()); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_mod(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr dividendValue = ARG_src1; + JSValuePtr divisorValue = ARG_src2; + + CallFrame* callFrame = ARG_callFrame; + double d = dividendValue->toNumber(callFrame); + JSValuePtr result = jsNumber(ARG_globalData, fmod(d, divisorValue->toNumber(callFrame))); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_less(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsBoolean(jsLess(callFrame, ARG_src1, ARG_src2)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_neq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + ASSERT(!JSImmediate::areBothImmediateNumbers(src1, src2)); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr result = jsBoolean(!equalSlowCaseInline(callFrame, src1, src2)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +VoidPtrPair Interpreter::cti_op_post_dec(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v = ARG_src1; + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr number = v->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + + RETURN_PAIR(JSValuePtr::encode(number), JSValuePtr::encode(jsNumber(ARG_globalData, number->uncheckedGetNumber() - 1))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_urshift(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr val = ARG_src1; + JSValuePtr shift = ARG_src2; + + CallFrame* callFrame = ARG_callFrame; + + if (JSImmediate::areBothImmediateNumbers(val, shift) && !JSImmediate::isNegative(val)) + return JSValuePtr::encode(JSImmediate::rightShiftImmediateNumbers(val, shift)); + else { + JSValuePtr result = jsNumber(ARG_globalData, (val->toUInt32(callFrame)) >> (shift->toUInt32(callFrame) & 0x1f)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); + } +} + +JSValueEncodedAsPointer* Interpreter::cti_op_bitxor(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr result = jsNumber(ARG_globalData, src1->toInt32(callFrame) ^ src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSObject* Interpreter::cti_op_new_regexp(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return new (ARG_globalData) RegExpObject(ARG_callFrame->lexicalGlobalObject()->regExpStructure(), ARG_regexp1); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_bitor(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr result = jsNumber(ARG_globalData, src1->toInt32(callFrame) | src2->toInt32(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_call_eval(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + RegisterFile* registerFile = ARG_registerFile; + + Interpreter* interpreter = ARG_globalData->interpreter; + + JSValuePtr funcVal = ARG_src1; + int registerOffset = ARG_int2; + int argCount = ARG_int3; + + Register* newCallFrame = callFrame->registers() + registerOffset; + Register* argv = newCallFrame - RegisterFile::CallFrameHeaderSize - argCount; + JSValuePtr thisValue = argv[0].jsValue(callFrame); + JSGlobalObject* globalObject = callFrame->scopeChain()->globalObject(); + + if (thisValue == globalObject && funcVal == globalObject->evalFunction()) { + JSValuePtr exceptionValue = noValue(); + JSValuePtr result = interpreter->callEval(callFrame, registerFile, argv, argCount, registerOffset, exceptionValue); + if (UNLIKELY(exceptionValue != noValue())) { + ARG_globalData->exception = exceptionValue; + VM_THROW_EXCEPTION_AT_END(); + } + return JSValuePtr::encode(result); + } + + return JSValuePtr::encode(JSImmediate::impossibleValue()); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_throw(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + + JSValuePtr exceptionValue = ARG_src1; + ASSERT(exceptionValue); + + HandlerInfo* handler = ARG_globalData->interpreter->throwException(callFrame, exceptionValue, vPCIndex, true); + + if (!handler) { + *ARG_exception = exceptionValue; + return JSValuePtr::encode(JSImmediate::nullImmediate()); + } + + ARG_setCallFrame(callFrame); + void* catchRoutine = handler->nativeCode; + ASSERT(catchRoutine); + STUB_SET_RETURN_ADDRESS(catchRoutine); + return JSValuePtr::encode(exceptionValue); +} + +JSPropertyNameIterator* Interpreter::cti_op_get_pnames(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSPropertyNameIterator::create(ARG_callFrame, ARG_src1); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_next_pname(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSPropertyNameIterator* it = ARG_pni1; + JSValuePtr temp = it->next(ARG_callFrame); + if (!temp) + it->invalidate(); + return JSValuePtr::encode(temp); +} + +JSObject* Interpreter::cti_op_push_scope(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSObject* o = ARG_src1->toObject(ARG_callFrame); + CHECK_FOR_EXCEPTION(); + ARG_callFrame->setScopeChain(ARG_callFrame->scopeChain()->push(o)); + return o; +} + +void Interpreter::cti_op_pop_scope(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + ARG_callFrame->setScopeChain(ARG_callFrame->scopeChain()->pop()); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_typeof(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsTypeStringForValue(ARG_callFrame, ARG_src1)); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_undefined(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr v = ARG_src1; + return JSValuePtr::encode(jsBoolean(JSImmediate::isImmediate(v) ? v->isUndefined() : v->asCell()->structure()->typeInfo().masqueradesAsUndefined())); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_boolean(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsBoolean(ARG_src1->isBoolean())); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_number(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsBoolean(ARG_src1->isNumber())); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_string(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsBoolean(ARG_globalData->interpreter->isJSString(ARG_src1))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_object(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsBoolean(jsIsObjectType(ARG_src1))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_is_function(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + return JSValuePtr::encode(jsBoolean(jsIsFunctionType(ARG_src1))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_stricteq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + // handled inline as fast cases + ASSERT(!JSImmediate::areBothImmediate(src1, src2)); + ASSERT(!(JSImmediate::isEitherImmediate(src1, src2) & (src1 != JSImmediate::zeroImmediate()) & (src2 != JSImmediate::zeroImmediate()))); + + return JSValuePtr::encode(jsBoolean(strictEqualSlowCaseInline(src1, src2))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_nstricteq(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src1 = ARG_src1; + JSValuePtr src2 = ARG_src2; + + // handled inline as fast cases + ASSERT(!JSImmediate::areBothImmediate(src1, src2)); + ASSERT(!(JSImmediate::isEitherImmediate(src1, src2) & (src1 != JSImmediate::zeroImmediate()) & (src2 != JSImmediate::zeroImmediate()))); + + return JSValuePtr::encode(jsBoolean(!strictEqualSlowCaseInline(src1, src2))); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_to_jsnumber(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr src = ARG_src1; + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr result = src->toJSNumber(callFrame); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +JSValueEncodedAsPointer* Interpreter::cti_op_in(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + JSValuePtr baseVal = ARG_src2; + + if (!baseVal->isObject()) { + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned vPCIndex = codeBlock->getBytecodeIndex(STUB_RETURN_ADDRESS); + ARG_globalData->exception = createInvalidParamError(callFrame, "in", baseVal, vPCIndex, codeBlock); + VM_THROW_EXCEPTION(); + } + + JSValuePtr propName = ARG_src1; + JSObject* baseObj = asObject(baseVal); + + uint32_t i; + if (propName->getUInt32(i)) + return JSValuePtr::encode(jsBoolean(baseObj->hasProperty(callFrame, i))); + + Identifier property(callFrame, propName->toString(callFrame)); + CHECK_FOR_EXCEPTION(); + return JSValuePtr::encode(jsBoolean(baseObj->hasProperty(callFrame, property))); +} + +JSObject* Interpreter::cti_op_push_new_scope(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSObject* scope = new (ARG_globalData) JSStaticScopeObject(ARG_callFrame, *ARG_id1, ARG_src2, DontDelete); + + CallFrame* callFrame = ARG_callFrame; + callFrame->setScopeChain(callFrame->scopeChain()->push(scope)); + return scope; +} + +void Interpreter::cti_op_jmp_scopes(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + unsigned count = ARG_int1; + CallFrame* callFrame = ARG_callFrame; + + ScopeChainNode* tmp = callFrame->scopeChain(); + while (count--) + tmp = tmp->pop(); + callFrame->setScopeChain(tmp); +} + +void Interpreter::cti_op_put_by_index(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + unsigned property = ARG_int2; + + ARG_src1->put(callFrame, property, ARG_src3); +} + +void* Interpreter::cti_op_switch_imm(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr scrutinee = ARG_src1; + unsigned tableIndex = ARG_int2; + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + + if (JSImmediate::isNumber(scrutinee)) { + int32_t value = JSImmediate::getTruncatedInt32(scrutinee); + return codeBlock->immediateSwitchJumpTable(tableIndex).ctiForValue(value); + } + + return codeBlock->immediateSwitchJumpTable(tableIndex).ctiDefault; +} + +void* Interpreter::cti_op_switch_char(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr scrutinee = ARG_src1; + unsigned tableIndex = ARG_int2; + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + + void* result = codeBlock->characterSwitchJumpTable(tableIndex).ctiDefault; + + if (scrutinee->isString()) { + UString::Rep* value = asString(scrutinee)->value().rep(); + if (value->size() == 1) + result = codeBlock->characterSwitchJumpTable(tableIndex).ctiForValue(value->data()[0]); + } + + return result; +} + +void* Interpreter::cti_op_switch_string(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + JSValuePtr scrutinee = ARG_src1; + unsigned tableIndex = ARG_int2; + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + + void* result = codeBlock->stringSwitchJumpTable(tableIndex).ctiDefault; + + if (scrutinee->isString()) { + UString::Rep* value = asString(scrutinee)->value().rep(); + result = codeBlock->stringSwitchJumpTable(tableIndex).ctiForValue(value); + } + + return result; +} + +JSValueEncodedAsPointer* Interpreter::cti_op_del_by_val(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + JSValuePtr baseValue = ARG_src1; + JSObject* baseObj = baseValue->toObject(callFrame); // may throw + + JSValuePtr subscript = ARG_src2; + JSValuePtr result; + uint32_t i; + if (subscript->getUInt32(i)) + result = jsBoolean(baseObj->deleteProperty(callFrame, i)); + else { + CHECK_FOR_EXCEPTION(); + Identifier property(callFrame, subscript->toString(callFrame)); + CHECK_FOR_EXCEPTION(); + result = jsBoolean(baseObj->deleteProperty(callFrame, property)); + } + + CHECK_FOR_EXCEPTION_AT_END(); + return JSValuePtr::encode(result); +} + +void Interpreter::cti_op_put_getter(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + ASSERT(ARG_src1->isObject()); + JSObject* baseObj = asObject(ARG_src1); + ASSERT(ARG_src3->isObject()); + baseObj->defineGetter(callFrame, *ARG_id2, asObject(ARG_src3)); +} + +void Interpreter::cti_op_put_setter(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + ASSERT(ARG_src1->isObject()); + JSObject* baseObj = asObject(ARG_src1); + ASSERT(ARG_src3->isObject()); + baseObj->defineSetter(callFrame, *ARG_id2, asObject(ARG_src3)); +} + +JSObject* Interpreter::cti_op_new_error(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + unsigned type = ARG_int1; + JSValuePtr message = ARG_src2; + unsigned bytecodeOffset = ARG_int3; + + unsigned lineNumber = codeBlock->lineNumberForBytecodeOffset(callFrame, bytecodeOffset); + return Error::create(callFrame, static_cast<ErrorType>(type), message->toString(callFrame), lineNumber, codeBlock->ownerNode()->sourceID(), codeBlock->ownerNode()->sourceURL()); +} + +void Interpreter::cti_op_debug(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + + int debugHookID = ARG_int1; + int firstLine = ARG_int2; + int lastLine = ARG_int3; + + ARG_globalData->interpreter->debug(callFrame, static_cast<DebugHookID>(debugHookID), firstLine, lastLine); +} + +JSValueEncodedAsPointer* Interpreter::cti_vm_throw(STUB_ARGS) +{ + BEGIN_STUB_FUNCTION(); + + CallFrame* callFrame = ARG_callFrame; + CodeBlock* codeBlock = callFrame->codeBlock(); + JSGlobalData* globalData = ARG_globalData; + + unsigned vPCIndex = codeBlock->getBytecodeIndex(globalData->exceptionLocation); + + JSValuePtr exceptionValue = globalData->exception; + ASSERT(exceptionValue); + globalData->exception = noValue(); + + HandlerInfo* handler = globalData->interpreter->throwException(callFrame, exceptionValue, vPCIndex, false); + + if (!handler) { + *ARG_exception = exceptionValue; + return JSValuePtr::encode(JSImmediate::nullImmediate()); + } + + ARG_setCallFrame(callFrame); + void* catchRoutine = handler->nativeCode; + ASSERT(catchRoutine); + STUB_SET_RETURN_ADDRESS(catchRoutine); + return JSValuePtr::encode(exceptionValue); +} + +#undef STUB_RETURN_ADDRESS +#undef STUB_SET_RETURN_ADDRESS +#undef BEGIN_STUB_FUNCTION +#undef CHECK_FOR_EXCEPTION +#undef CHECK_FOR_EXCEPTION_AT_END +#undef CHECK_FOR_EXCEPTION_VOID +#undef VM_THROW_EXCEPTION +#undef VM_THROW_EXCEPTION_2 +#undef VM_THROW_EXCEPTION_AT_END + +#endif // ENABLE(JIT) + +} // namespace JSC |