blob: 87bcfec519de536a5d928eb66aeae42264f36089 [file] [log] [blame]
/*
* Copyright (C) 2008 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 "Machine.h"
#include "CodeBlock.h"
#include "DebuggerCallFrame.h"
#include "ExceptionHelpers.h"
#include "ExecState.h"
#include "JSActivation.h"
#include "JSLock.h"
#include "JSPropertyNameIterator.h"
#include "Parser.h"
#include "Register.h"
#include "array_object.h"
#include "debugger.h"
#include "function.h"
#include "internal.h"
#include "object_object.h"
#include "operations.h"
#include "operations.h"
#include "regexp_object.h"
#if COMPILER(GCC)
#define UNLIKELY(x) \
__builtin_expect ((x), 0)
#else
#define UNLIKELY(x) x
#endif
namespace KJS {
#if HAVE(COMPUTED_GOTO)
static void* op_throw_end_indirect;
static void* op_call_indirect;
#endif
// Retrieves the offset of a calling function within the current register file.
bool getCallerFunctionOffset(Register** registerBase, int callOffset, int& callerOffset)
{
Register* callFrame = (*registerBase) + callOffset;
CodeBlock* callerCodeBlock = callFrame[Machine::CallerCodeBlock].u.codeBlock;
if (!callerCodeBlock) // test for top frame of re-entrant function call
return false;
callerOffset = callFrame[Machine::CallerRegisterOffset].u.i - callerCodeBlock->numLocals - Machine::CallFrameHeaderSize;
if (callerOffset < 0) // test for global frame
return false;
Register* callerCallFrame = (*registerBase) + callerOffset;
if (!callerCallFrame[Machine::CallerCodeBlock].u.codeBlock) // test for eval frame
return false;
return true;
}
// Returns the depth of the scope chain within a given call frame.
static int depth(ScopeChain& sc)
{
int scopeDepth = 0;
ScopeChainIterator iter = sc.begin();
ScopeChainIterator end = sc.end();
while (!(*iter)->isVariableObject()) {
++iter;
++scopeDepth;
}
return scopeDepth;
}
static inline bool jsLess(ExecState* exec, JSValue* v1, JSValue* v2)
{
double n1;
double n2;
JSValue* p1;
JSValue* p2;
bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);
if (wasNotString1 | wasNotString2)
return n1 < n2;
return static_cast<const StringImp*>(p1)->value() < static_cast<const StringImp*>(p2)->value();
}
static inline bool jsLessEq(ExecState* exec, JSValue* v1, JSValue* v2)
{
double n1;
double n2;
JSValue* p1;
JSValue* p2;
bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);
if (wasNotString1 | wasNotString2)
return n1 <= n2;
return !(static_cast<const StringImp*>(p2)->value() < static_cast<const StringImp*>(p1)->value());
}
static JSValue* jsAddSlowCase(ExecState* exec, JSValue* v1, JSValue* v2)
{
// exception for the Date exception in defaultValue()
JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType);
JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType);
if (p1->isString() || p2->isString()) {
UString value = p1->toString(exec) + p2->toString(exec);
if (value.isNull())
return throwOutOfMemoryError(exec);
return jsString(value);
}
return jsNumber(p1->toNumber(exec) + p2->toNumber(exec));
}
// Fast-path choices here are based on frequency data from SunSpider:
// <times> Add case: <t1> <t2>
// ---------------------------
// 5627160 Add case: 1 1
// 247427 Add case: 5 5
// 20901 Add case: 5 6
// 13978 Add case: 5 1
// 4000 Add case: 1 5
// 1 Add case: 3 5
static inline JSValue* jsAdd(ExecState* exec, JSValue* v1, JSValue* v2)
{
JSType t1 = v1->type();
JSType t2 = v2->type();
const unsigned bothTypes = (t1 << 3) | t2;
if (bothTypes == ((NumberType << 3) | NumberType))
return jsNumber(v1->uncheckedGetNumber() + v2->uncheckedGetNumber());
if (bothTypes == ((StringType << 3) | StringType)) {
UString value = static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value();
if (value.isNull())
return throwOutOfMemoryError(exec);
return jsString(value);
}
// All other cases are pretty uncommon
return jsAddSlowCase(exec, v1, v2);
}
static JSValue* jsTypeStringForValue(JSValue* v)
{
switch (v->type()) {
case UndefinedType:
return jsString("undefined");
case NullType:
return jsString("object");
case BooleanType:
return jsString("boolean");
case NumberType:
return jsString("number");
case StringType:
return jsString("string");
default:
if (v->isObject()) {
// Return "undefined" for objects that should be treated
// as null when doing comparisons.
if (static_cast<JSObject*>(v)->masqueradeAsUndefined())
return jsString("undefined");
else if (static_cast<JSObject*>(v)->implementsCall())
return jsString("function");
}
return jsString("object");
}
}
static bool NEVER_INLINE resolve(ExecState* exec, Instruction* vPC, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue*& exceptionValue)
{
int dst = (vPC + 1)->u.operand;
int property = (vPC + 2)->u.operand;
ScopeChainIterator iter = scopeChain->begin();
ScopeChainIterator end = scopeChain->end();
ASSERT(iter != end);
PropertySlot slot;
Identifier& ident = codeBlock->identifiers[property];
do {
JSObject* o = *iter;
if (o->getPropertySlot(exec, ident, slot)) {
JSValue* result = slot.getValue(exec, o, ident);
exceptionValue = exec->exception();
if (exceptionValue)
return false;
r[dst].u.jsValue = result;
return true;
}
} while (++iter != end);
exceptionValue = createUndefinedVariableError(exec, ident);
return false;
}
static bool NEVER_INLINE resolve_skip(ExecState* exec, Instruction* vPC, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue*& exceptionValue)
{
int dst = (vPC + 1)->u.operand;
int property = (vPC + 2)->u.operand;
int skip = (vPC + 3)->u.operand + codeBlock->needsFullScopeChain;
ScopeChainIterator iter = scopeChain->begin();
ScopeChainIterator end = scopeChain->end();
ASSERT(iter != end);
while (skip--) {
++iter;
ASSERT(iter != end);
}
PropertySlot slot;
Identifier& ident = codeBlock->identifiers[property];
do {
JSObject* o = *iter;
if (o->getPropertySlot(exec, ident, slot)) {
JSValue* result = slot.getValue(exec, o, ident);
exceptionValue = exec->exception();
if (exceptionValue)
return false;
r[dst].u.jsValue = result;
return true;
}
} while (++iter != end);
exceptionValue = createUndefinedVariableError(exec, ident);
return false;
}
static void NEVER_INLINE resolveBase(ExecState* exec, Instruction* vPC, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock)
{
int dst = (vPC + 1)->u.operand;
int property = (vPC + 2)->u.operand;
ScopeChainIterator iter = scopeChain->begin();
ScopeChainIterator end = scopeChain->end();
ASSERT(iter != end);
PropertySlot slot;
Identifier& ident = codeBlock->identifiers[property];
JSObject* base;
do {
base = *iter;
if (base->getPropertySlot(exec, ident, slot)) {
r[dst].u.jsValue = base;
return;
}
} while (++iter != end);
r[dst].u.jsValue = base;
}
static bool NEVER_INLINE resolveBaseAndProperty(ExecState* exec, Instruction* vPC, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue*& exceptionValue)
{
int baseDst = (vPC + 1)->u.operand;
int propDst = (vPC + 2)->u.operand;
int property = (vPC + 3)->u.operand;
ScopeChainIterator iter = scopeChain->begin();
ScopeChainIterator end = scopeChain->end();
// FIXME: add scopeDepthIsZero optimization
ASSERT(iter != end);
PropertySlot slot;
Identifier& ident = codeBlock->identifiers[property];
JSObject* base;
do {
base = *iter;
if (base->getPropertySlot(exec, ident, slot)) {
JSValue* result = slot.getValue(exec, base, ident);
exceptionValue = exec->exception();
if (exceptionValue)
return false;
r[propDst].u.jsValue = result;
r[baseDst].u.jsValue = base;
return true;
}
++iter;
} while (iter != end);
exceptionValue = createUndefinedVariableError(exec, ident);
return false;
}
static bool NEVER_INLINE resolveBaseAndFunc(ExecState* exec, Instruction* vPC, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue*& exceptionValue)
{
int baseDst = (vPC + 1)->u.operand;
int funcDst = (vPC + 2)->u.operand;
int property = (vPC + 3)->u.operand;
ScopeChainIterator iter = scopeChain->begin();
ScopeChainIterator end = scopeChain->end();
// FIXME: add scopeDepthIsZero optimization
ASSERT(iter != end);
PropertySlot slot;
Identifier& ident = codeBlock->identifiers[property];
JSObject* base;
do {
base = *iter;
if (base->getPropertySlot(exec, 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(exec);
JSValue* result = slot.getValue(exec, base, ident);
exceptionValue = exec->exception();
if (exceptionValue)
return false;
r[baseDst].u.jsValue = thisObj;
r[funcDst].u.jsValue = result;
return true;
}
++iter;
} while (iter != end);
exceptionValue = createUndefinedVariableError(exec, ident);
return false;
}
ALWAYS_INLINE void initializeCallFrame(Register* callFrame, CodeBlock* codeBlock, Instruction* vPC, ScopeChainNode* scopeChain, int registerOffset, int returnValueRegister, int argv, int argc, int calledAsConstructor, JSValue* function)
{
callFrame[Machine::CallerCodeBlock].u.codeBlock = codeBlock;
callFrame[Machine::ReturnVPC].u.vPC = vPC + 1;
callFrame[Machine::CallerScopeChain].u.scopeChain = scopeChain;
callFrame[Machine::CallerRegisterOffset].u.i = registerOffset;
callFrame[Machine::ReturnValueRegister].u.i = returnValueRegister;
callFrame[Machine::ArgumentStartRegister].u.i = argv; // original argument vector (for the sake of the "arguments" object)
callFrame[Machine::ArgumentCount].u.i = argc; // original argument count (for the sake of the "arguments" object)
callFrame[Machine::CalledAsConstructor].u.i = calledAsConstructor;
callFrame[Machine::Callee].u.jsValue = function;
callFrame[Machine::OptionalCalleeActivation].u.jsValue = 0;
}
ALWAYS_INLINE Register* slideRegisterWindowForCall(ExecState* exec, CodeBlock* newCodeBlock, RegisterFile* registerFile, Register** registerBase, int registerOffset, int argv, int argc, JSValue*& exceptionValue)
{
Register* r = 0;
int oldOffset = registerOffset;
registerOffset += argv + newCodeBlock->numLocals;
size_t size = registerOffset + newCodeBlock->numTemporaries;
if (argc == newCodeBlock->numParameters) { // correct number of arguments
if (!registerFile->grow(size)) {
exceptionValue = createStackOverflowError(exec);
return *registerBase + oldOffset;
}
r = (*registerBase) + registerOffset;
} else if (argc < newCodeBlock->numParameters) { // too few arguments -- fill in the blanks
if (!registerFile->grow(size)) {
exceptionValue = createStackOverflowError(exec);
return *registerBase + oldOffset;
}
r = (*registerBase) + registerOffset;
int omittedArgCount = newCodeBlock->numParameters - argc;
Register* endOfParams = r - newCodeBlock->numVars;
for (Register* it = endOfParams - omittedArgCount; it != endOfParams; ++it)
(*it).u.jsValue = jsUndefined();
} else { // too many arguments -- copy return info and expected arguments, leaving the extra arguments behind
int shift = argc + Machine::CallFrameHeaderSize;
registerOffset += shift;
size += shift;
if (!registerFile->grow(size)) {
exceptionValue = createStackOverflowError(exec);
return *registerBase + oldOffset;
}
r = (*registerBase) + registerOffset;
Register* it = r - newCodeBlock->numLocals - Machine::CallFrameHeaderSize - shift;
Register* end = it + Machine::CallFrameHeaderSize + newCodeBlock->numParameters;
for ( ; it != end; ++it)
*(it + shift) = *it;
}
return r;
}
ALWAYS_INLINE ScopeChainNode* scopeChainForCall(FunctionBodyNode* functionBodyNode, CodeBlock* newCodeBlock, ScopeChainNode* callDataScopeChain, Register** registerBase, Register* r)
{
if (newCodeBlock->needsFullScopeChain) {
JSActivation* activation = new JSActivation(functionBodyNode, registerBase, r - (*registerBase));
r[Machine::OptionalCalleeActivation - Machine::CallFrameHeaderSize - newCodeBlock->numLocals].u.jsValue = activation;
return callDataScopeChain->copy()->push(activation);
}
return callDataScopeChain;
}
static NEVER_INLINE bool isNotObject(ExecState* exec, const Instruction*, CodeBlock*, JSValue* value, JSValue*& exceptionData)
{
if (value->isObject())
return false;
exceptionData = createNotAnObjectError(exec, value, 0);
return true;
}
static NEVER_INLINE JSValue* eval(ExecState* exec, JSObject* thisObj, ScopeChainNode* scopeChain, RegisterFile* registerFile, Register* r, int argv, int argc, JSValue*& exceptionValue)
{
JSValue* x = argc >= 2 ? r[argv + 1].u.jsValue : jsUndefined();
if (!x->isString())
return x;
UString s = x->toString(exec);
if (exec->hadException()) {
exceptionValue = exec->exception();
exec->clearException();
return 0;
}
int sourceId;
int errLine;
UString errMsg;
RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(exec, UString(), 0, UStringSourceProvider::create(s), &sourceId, &errLine, &errMsg);
if (!evalNode) {
exceptionValue = Error::create(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
return 0;
}
return machine().execute(evalNode.get(), exec, thisObj, registerFile, r - (*registerFile->basePointer()) + argv + argc, scopeChain, &exceptionValue);
}
Machine& machine()
{
ASSERT(JSLock::currentThreadIsHoldingLock());
static Machine machine;
return machine;
}
Machine::Machine()
: m_reentryDepth(0)
{
privateExecute(InitializeAndReturn);
}
void Machine::dumpCallFrame(const CodeBlock* codeBlock, ScopeChainNode* scopeChain, RegisterFile* registerFile, const Register* r)
{
ScopeChain sc(scopeChain);
JSGlobalObject* globalObject = static_cast<JSGlobalObject*>(sc.bottom());
codeBlock->dump(globalObject->globalExec());
dumpRegisters(codeBlock, registerFile, r);
}
void Machine::dumpRegisters(const CodeBlock* codeBlock, RegisterFile* registerFile, const Register* r)
{
printf("Register frame: \n\n");
printf("----------------------------------------\n");
printf(" use | address | value \n");
printf("----------------------------------------\n");
const Register* it;
const Register* end;
if (isGlobalCallFrame(registerFile->basePointer(), r)) {
it = r - registerFile->numGlobalSlots();
end = r;
if (it != end) {
do {
printf("[global var] | %10p | %10p \n", it, (*it).u.jsValue);
++it;
} while (it != end);
printf("----------------------------------------\n");
}
} else {
it = r - codeBlock->numLocals - CallFrameHeaderSize;
end = it + CallFrameHeaderSize;
if (it != end) {
do {
printf("[call frame] | %10p | %10p \n", it, (*it).u.jsValue);
++it;
} while (it != end);
printf("----------------------------------------\n");
}
end = it + codeBlock->numParameters;
if (it != end) {
do {
printf("[param] | %10p | %10p \n", it, (*it).u.jsValue);
++it;
} while (it != end);
printf("----------------------------------------\n");
}
end = it + codeBlock->numVars;
if (it != end) {
do {
printf("[var] | %10p | %10p \n", it, (*it).u.jsValue);
++it;
} while (it != end);
printf("----------------------------------------\n");
}
}
end = it + codeBlock->numTemporaries;
if (it != end) {
do {
printf("[temp] | %10p | %10p \n", it, (*it).u.jsValue);
++it;
} while (it != end);
}
}
bool Machine::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 Machine::unwindCallFrame(ExecState* exec, JSValue* exceptionValue, Register** registerBase, const Instruction*& vPC, CodeBlock*& codeBlock, JSValue**& k, ScopeChainNode*& scopeChain, Register*& r)
{
CodeBlock* oldCodeBlock = codeBlock;
if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
if (!isGlobalCallFrame(registerBase, r)) {
DebuggerCallFrame debuggerCallFrame(this, exec->dynamicGlobalObject(), codeBlock, scopeChain, exceptionValue, registerBase, r - *registerBase);
debugger->returnEvent(debuggerCallFrame, codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->lastLine());
}
}
if (oldCodeBlock->needsFullScopeChain)
scopeChain->deref();
if (isGlobalCallFrame(registerBase, r))
return false;
Register* callFrame = r - oldCodeBlock->numLocals - CallFrameHeaderSize;
codeBlock = callFrame[CallerCodeBlock].u.codeBlock;
if (!codeBlock)
return false;
// If this call frame created an activation, tear it off.
if (JSActivation* activation = static_cast<JSActivation*>(callFrame[OptionalCalleeActivation].u.jsValue)) {
ASSERT(activation->isActivationObject());
activation->copyRegisters();
}
k = codeBlock->jsValues.data();
scopeChain = callFrame[CallerScopeChain].u.scopeChain;
int callerRegisterOffset = callFrame[CallerRegisterOffset].u.i;
r = (*registerBase) + callerRegisterOffset;
exec->m_callFrameOffset = callerRegisterOffset - codeBlock->numLocals - CallFrameHeaderSize;
vPC = callFrame[ReturnVPC].u.vPC;
return true;
}
NEVER_INLINE Instruction* Machine::throwException(ExecState* exec, JSValue* exceptionValue, Register** registerBase, const Instruction* vPC, CodeBlock*& codeBlock, JSValue**& k, ScopeChainNode*& scopeChain, Register*& r)
{
// Set up the exception object
if (exceptionValue->isObject()) {
JSObject* exception = static_cast<JSObject*>(exceptionValue);
if (!exception->hasProperty(exec, "line") && !exception->hasProperty(exec, "sourceURL")) {
exception->put(exec, "line", jsNumber(codeBlock->lineNumberForVPC(vPC)));
exception->put(exec, "sourceURL", jsOwnedString(codeBlock->ownerNode->sourceURL()));
}
}
if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
DebuggerCallFrame debuggerCallFrame(this, exec->dynamicGlobalObject(), codeBlock, scopeChain, exceptionValue, registerBase, r - *registerBase);
debugger->exception(debuggerCallFrame, codeBlock->ownerNode->sourceId(), codeBlock->lineNumberForVPC(vPC));
}
// Calculate an exception handler vPC, unwinding call frames as necessary.
int scopeDepth;
Instruction* handlerVPC;
while (!codeBlock->getHandlerForVPC(vPC, handlerVPC, scopeDepth))
if (!unwindCallFrame(exec, exceptionValue, registerBase, vPC, codeBlock, k, scopeChain, r))
return 0;
// Now unwind the scope chain within the exception handler's call frame.
ScopeChain sc(scopeChain);
int scopeDelta = depth(sc) - scopeDepth;
ASSERT(scopeDelta >= 0);
while (scopeDelta--)
sc.pop();
setScopeChain(exec, scopeChain, sc.node());
return handlerVPC;
}
JSValue* Machine::execute(ProgramNode* programNode, ExecState* exec, ScopeChainNode* scopeChain, JSObject* thisObj, RegisterFileStack* registerFileStack, JSValue** exception)
{
if (m_reentryDepth >= MaxReentryDepth) {
*exception = createStackOverflowError(exec);
return 0;
}
RegisterFile* registerFile = registerFileStack->pushGlobalRegisterFile();
ASSERT(registerFile->numGlobalSlots());
CodeBlock* codeBlock = &programNode->code(scopeChain, !registerFileStack->inImplicitCall());
registerFile->addGlobalSlots(codeBlock->numVars);
registerFile->uncheckedGrow(codeBlock->numTemporaries);
Register* r = (*registerFile->basePointer());
r[ProgramCodeThisRegister].u.jsValue = thisObj;
if (codeBlock->needsFullScopeChain)
scopeChain = scopeChain->copy();
ExecState newExec(exec, this, registerFile, scopeChain, -1);
m_reentryDepth++;
JSValue* result = privateExecute(Normal, &newExec, registerFile, r, scopeChain, codeBlock, exception);
m_reentryDepth--;
registerFileStack->popGlobalRegisterFile();
return result;
}
JSValue* Machine::execute(FunctionBodyNode* functionBodyNode, ExecState* exec, FunctionImp* function, JSObject* thisObj, const List& args, RegisterFileStack* registerFileStack, ScopeChainNode* scopeChain, JSValue** exception)
{
if (m_reentryDepth >= MaxReentryDepth) {
*exception = createStackOverflowError(exec);
return 0;
}
RegisterFile* registerFile = registerFileStack->current();
int argv = CallFrameHeaderSize;
int argc = args.size() + 1; // implicit "this" parameter
size_t oldSize = registerFile->size();
if (!registerFile->grow(oldSize + CallFrameHeaderSize + argc)) {
*exception = createStackOverflowError(exec);
return 0;
}
Register** registerBase = registerFile->basePointer();
int registerOffset = oldSize;
int callFrameOffset = registerOffset;
Register* callFrame = (*registerBase) + callFrameOffset;
// put args in place, including "this"
Register* dst = callFrame + CallFrameHeaderSize;
(*dst).u.jsValue = thisObj;
List::const_iterator end = args.end();
for (List::const_iterator it = args.begin(); it != end; ++it)
(*++dst).u.jsValue = *it;
// put call frame in place, using a 0 codeBlock to indicate a built-in caller
initializeCallFrame(callFrame, 0, 0, 0, registerOffset, 0, argv, argc, 0, function);
CodeBlock* newCodeBlock = &functionBodyNode->code(scopeChain);
Register* r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, registerOffset, argv, argc, *exception);
if (*exception) {
registerFile->shrink(oldSize);
return 0;
}
scopeChain = scopeChainForCall(functionBodyNode, newCodeBlock, scopeChain, registerBase, r);
ExecState newExec(exec, this, registerFile, scopeChain, callFrameOffset);
m_reentryDepth++;
JSValue* result = privateExecute(Normal, &newExec, registerFile, r, scopeChain, newCodeBlock, exception);
m_reentryDepth--;
registerFile->shrink(oldSize);
return result;
}
JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, RegisterFile* registerFile, int registerOffset, ScopeChainNode* scopeChain, JSValue** exception)
{
if (m_reentryDepth >= MaxReentryDepth) {
*exception = createStackOverflowError(exec);
return 0;
}
EvalCodeBlock* codeBlock = &evalNode->code(scopeChain);
JSVariableObject* variableObject;
for (ScopeChainNode* node = scopeChain; ; node = node->next) {
ASSERT(node);
if (node->object->isVariableObject()) {
variableObject = static_cast<JSVariableObject*>(node->object);
break;
}
}
for (Vector<Identifier>::const_iterator iter = codeBlock->declaredVariableNames.begin(); iter != codeBlock->declaredVariableNames.end(); ++iter) {
Identifier ident = *iter;
if (!variableObject->hasProperty(exec, ident))
variableObject->put(exec, ident, jsUndefined());
}
ASSERT(codeBlock->functions.size() == codeBlock->declaredFunctionNames.size());
for (size_t i = 0; i < codeBlock->functions.size(); ++i)
variableObject->put(exec, codeBlock->declaredFunctionNames[i], codeBlock->functions[i]->makeFunction(exec, scopeChain));
size_t oldSize = registerFile->size();
size_t newSize = registerOffset + codeBlock->numVars + codeBlock->numTemporaries + CallFrameHeaderSize;
if (!registerFile->grow(newSize)) {
*exception = createStackOverflowError(exec);
return 0;
}
Register* callFrame = *registerFile->basePointer() + registerOffset;
// put call frame in place, using a 0 codeBlock to indicate a built-in caller
initializeCallFrame(callFrame, 0, 0, 0, registerOffset, 0, 0, 0, 0, 0);
Register* r = callFrame + CallFrameHeaderSize + codeBlock->numVars;
r[ProgramCodeThisRegister].u.jsValue = thisObj;
if (codeBlock->needsFullScopeChain)
scopeChain = scopeChain->copy();
ExecState newExec(exec, this, registerFile, scopeChain, -1);
m_reentryDepth++;
JSValue* result = privateExecute(Normal, &newExec, registerFile, r, scopeChain, codeBlock, exception);
m_reentryDepth--;
registerFile->shrink(oldSize);
return result;
}
JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj, RegisterFileStack* registerFileStack, ScopeChainNode* scopeChain, JSValue** exception)
{
RegisterFile* registerFile = registerFileStack->current();
if (registerFile->safeForReentry())
return Machine::execute(evalNode, exec, thisObj, registerFile, registerFile->size(), scopeChain, exception);
registerFile = registerFileStack->pushFunctionRegisterFile();
JSValue* result = Machine::execute(evalNode, exec, thisObj, registerFile, registerFile->size(), scopeChain, exception);
registerFileStack->popFunctionRegisterFile();
return result;
}
ALWAYS_INLINE void Machine::setScopeChain(ExecState* exec, ScopeChainNode*& scopeChain, ScopeChainNode* newScopeChain)
{
scopeChain = newScopeChain;
exec->m_scopeChain = newScopeChain;
}
NEVER_INLINE void Machine::debug(ExecState* exec, const Instruction* vPC, const CodeBlock* codeBlock, ScopeChainNode* scopeChain, Register** registerBase, Register* r)
{
int debugHookID = (++vPC)->u.operand;
int firstLine = (++vPC)->u.operand;
int lastLine = (++vPC)->u.operand;
Debugger* debugger = exec->dynamicGlobalObject()->debugger();
if (!debugger)
return;
DebuggerCallFrame debuggerCallFrame(this, exec->dynamicGlobalObject(), codeBlock, scopeChain, 0, registerBase, r - *registerBase);
switch((DebugHookID)debugHookID) {
case DidEnterCallFrame: {
debugger->callEvent(debuggerCallFrame, codeBlock->ownerNode->sourceId(), firstLine);
return;
}
case WillLeaveCallFrame: {
debugger->returnEvent(debuggerCallFrame, codeBlock->ownerNode->sourceId(), lastLine);
return;
}
case WillExecuteStatement: {
debugger->atStatement(debuggerCallFrame, codeBlock->ownerNode->sourceId(), firstLine);
return;
}
}
}
JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFile* registerFile, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue** 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_OPCODE(id) m_opcodeTable[id] = &&id;
FOR_EACH_OPCODE_ID(ADD_OPCODE);
#undef ADD_OPCODE
#define ADD_OPCODE_ID(id) m_opcodeIDTable.add(&&id, id);
FOR_EACH_OPCODE_ID(ADD_OPCODE_ID);
#undef ADD_OPCODE
ASSERT(m_opcodeIDTable.size() == numOpcodeIDs);
op_throw_end_indirect = &&op_throw_end;
op_call_indirect = &&op_call;
#endif // HAVE(COMPUTED_GOTO)
return 0;
}
JSValue* exceptionValue = 0;
Instruction* handlerVPC = 0;
Register** registerBase = registerFile->basePointer();
Instruction* vPC = codeBlock->instructions.begin();
JSValue** k = codeBlock->jsValues.data();
registerFile->setSafeForReentry(false);
#define VM_CHECK_EXCEPTION() \
do { \
if (UNLIKELY(exec->hadException())) { \
exceptionValue = exec->exception(); \
goto vm_throw; \
} \
} while (0)
#if HAVE(COMPUTED_GOTO)
#define NEXT_OPCODE goto *vPC->u.opcode
#define BEGIN_OPCODE(opcode) opcode:
NEXT_OPCODE;
#else
#define NEXT_OPCODE continue
#define BEGIN_OPCODE(opcode) case opcode:
while(1) // iterator loop begins
switch (vPC->u.opcode)
#endif
{
BEGIN_OPCODE(op_load) {
/* load dst(r) src(k)
Copies constant src to register dst.
*/
int dst = (++vPC)->u.operand;
int src = (++vPC)->u.operand;
r[dst].u.jsValue = k[src];
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = scopeChain->globalObject()->objectConstructor()->construct(exec, exec->emptyList());
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_new_array) {
/* new_array dst(r)
Constructs a new empty Array instance using the original
constructor, and puts the result in register dst.
*/
int dst = (++vPC)->u.operand;
r[dst].u.jsValue = scopeChain->globalObject()->arrayConstructor()->construct(exec, exec->emptyList());
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = new RegExpImp(scopeChain->globalObject()->regExpPrototype(), codeBlock->regexps[regExp]);
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_mov) {
/* mov dst(r) src(r)
Copies register src to register dst.
*/
int dst = (++vPC)->u.operand;
int src = (++vPC)->u.operand;
r[dst] = r[src];
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsBoolean(equal(exec, r[src1].u.jsValue, r[src2].u.jsValue));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsBoolean(!equal(exec, r[src1].u.jsValue, r[src2].u.jsValue));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
r[dst].u.jsValue = jsBoolean(strictEqual(r[src1].u.jsValue, r[src2].u.jsValue));
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
r[dst].u.jsValue = jsBoolean(!strictEqual(r[src1].u.jsValue, r[src2].u.jsValue));
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsBoolean(jsLess(exec, r[src1].u.jsValue, r[src2].u.jsValue));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsBoolean(jsLessEq(exec, r[src1].u.jsValue, r[src2].u.jsValue));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* result = jsNumber(r[srcDst].u.jsValue->toNumber(exec) + 1);
VM_CHECK_EXCEPTION();
r[srcDst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* result = jsNumber(r[srcDst].u.jsValue->toNumber(exec) - 1);
VM_CHECK_EXCEPTION();
r[srcDst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* number = r[srcDst].u.jsValue->toJSNumber(exec);
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = number;
r[srcDst].u.jsValue = jsNumber(number->uncheckedGetNumber() + 1);
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* number = r[srcDst].u.jsValue->toJSNumber(exec);
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = number;
r[srcDst].u.jsValue = jsNumber(number->uncheckedGetNumber() - 1);
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* result = r[src].u.jsValue->toJSNumber(exec);
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src = (++vPC)->u.operand;
JSValue* result = jsNumber(-r[src].u.jsValue->toNumber(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsAdd(exec, r[src1].u.jsValue, r[src2].u.jsValue);
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsNumber(r[src1].u.jsValue->toNumber(exec) * r[src2].u.jsValue->toNumber(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int dividend = (++vPC)->u.operand;
int divisor = (++vPC)->u.operand;
JSValue* result = jsNumber(r[dividend].u.jsValue->toNumber(exec) / r[divisor].u.jsValue->toNumber(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
double d = r[dividend].u.jsValue->toNumber(exec);
JSValue* result = jsNumber(fmod(d, r[divisor].u.jsValue->toNumber(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsNumber(r[src1].u.jsValue->toNumber(exec) - r[src2].u.jsValue->toNumber(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int val = (++vPC)->u.operand;
int shift = (++vPC)->u.operand;
JSValue* result = jsNumber((r[val].u.jsValue->toInt32(exec)) << (r[shift].u.jsValue->toUInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int val = (++vPC)->u.operand;
int shift = (++vPC)->u.operand;
JSValue* result = jsNumber((r[val].u.jsValue->toInt32(exec)) >> (r[shift].u.jsValue->toUInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int val = (++vPC)->u.operand;
int shift = (++vPC)->u.operand;
JSValue* result = jsNumber((r[val].u.jsValue->toUInt32(exec)) >> (r[shift].u.jsValue->toUInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsNumber((r[src1].u.jsValue->toInt32(exec)) & (r[src2].u.jsValue->toInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsNumber((r[src1].u.jsValue->toInt32(exec)) ^ (r[src2].u.jsValue->toInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src1 = (++vPC)->u.operand;
int src2 = (++vPC)->u.operand;
JSValue* result = jsNumber((r[src1].u.jsValue->toInt32(exec)) | (r[src2].u.jsValue->toInt32(exec)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
int src = (++vPC)->u.operand;
JSValue* result = jsNumber(~r[src].u.jsValue->toInt32(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_not) {
/* not dst(r) src1(r) src2(r)
Computes logical NOT of register src1 (converted to
boolean), and puts the result in register dst.
*/
int dst = (++vPC)->u.operand;
int src = (++vPC)->u.operand;
JSValue* result = jsBoolean(!r[src].u.jsValue->toBoolean(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_instanceof) {
/* instanceof dst(r) value(r) constructor(r)
Tests whether register value is an instance of register
constructor, and puts the boolean result in register dst.
Raises an exception if register constructor is not an
object.
*/
int dst = (++vPC)->u.operand;
int value = (++vPC)->u.operand;
int base = (++vPC)->u.operand;
JSValue* baseVal = r[base].u.jsValue;
if (isNotObject(exec, vPC, codeBlock, baseVal, exceptionValue))
goto vm_throw;
JSObject* baseObj = static_cast<JSObject*>(baseVal);
r[dst].u.jsValue = jsBoolean(baseObj->implementsHasInstance() ? baseObj->hasInstance(exec, r[value].u.jsValue) : false);
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = jsTypeStringForValue(r[src].u.jsValue);
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSValue* baseVal = r[base].u.jsValue;
if (isNotObject(exec, vPC, codeBlock, baseVal, exceptionValue))
goto vm_throw;
JSObject* baseObj = static_cast<JSObject*>(baseVal);
JSValue* propName = r[property].u.jsValue;
uint32_t i;
if (propName->getUInt32(i))
r[dst].u.jsValue = jsBoolean(baseObj->hasProperty(exec, i));
else {
Identifier property(propName->toString(exec));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = jsBoolean(baseObj->hasProperty(exec, property));
}
++vPC;
NEXT_OPCODE;
}
BEGIN_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(exec, vPC, r, scopeChain, codeBlock, exceptionValue)))
goto vm_throw;
vPC += 3;
NEXT_OPCODE;
}
BEGIN_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(!resolve_skip(exec, vPC, r, scopeChain, codeBlock, exceptionValue)))
goto vm_throw;
vPC += 4;
NEXT_OPCODE;
}
BEGIN_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 + codeBlock->needsFullScopeChain;
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);
r[dst].u.jsValue = scope->valueAt(index);
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_put_scoped_var) {
/* put_scoped_var index(n) skip(n) value(r)
*/
int index = (++vPC)->u.operand;
int skip = (++vPC)->u.operand + codeBlock->needsFullScopeChain;
int value = (++vPC)->u.operand;
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->valueAt(index) = r[value].u.jsValue;
++vPC;
NEXT_OPCODE;
}
BEGIN_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(exec, vPC, r, scopeChain, codeBlock);
vPC += 3;
NEXT_OPCODE;
}
BEGIN_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(exec, vPC, r, scopeChain, codeBlock, exceptionValue)))
goto vm_throw;
vPC += 4;
NEXT_OPCODE;
}
BEGIN_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(exec, vPC, r, scopeChain, codeBlock, exceptionValue)))
goto vm_throw;
vPC += 4;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_get_by_id) {
/* get_by_id dst(r) base(r) property(id)
Converts register base to Object, gets the property
named by identifier property from the object, and puts the
result in register dst.
*/
int dst = (++vPC)->u.operand;
int base = (++vPC)->u.operand;
int property = (++vPC)->u.operand;
#ifndef NDEBUG
int registerOffset = r - (*registerBase);
#endif
JSObject* baseObj = r[base].u.jsValue->toObject(exec);
Identifier& ident = codeBlock->identifiers[property];
JSValue *result = baseObj->get(exec, ident);
ASSERT(registerOffset == (r - (*registerBase)));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_put_by_id) {
/* put_by_id base(r) property(id) value(r)
Sets register value on register base as the property named
by identifier property. Base is converted to object first.
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;
#ifndef NDEBUG
int registerOffset = r - (*registerBase);
#endif
JSObject* baseObj = r[base].u.jsValue->toObject(exec);
Identifier& ident = codeBlock->identifiers[property];
baseObj->put(exec, ident, r[value].u.jsValue);
ASSERT(registerOffset == (r - (*registerBase)));
VM_CHECK_EXCEPTION();
++vPC;
NEXT_OPCODE;
}
BEGIN_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 = r[base].u.jsValue->toObject(exec);
Identifier& ident = codeBlock->identifiers[property];
JSValue* result = jsBoolean(baseObj->deleteProperty(exec, ident));
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSObject* baseObj = r[base].u.jsValue->toObject(exec); // may throw
JSValue* subscript = r[property].u.jsValue;
JSValue* result;
uint32_t i;
if (subscript->getUInt32(i))
result = baseObj->get(exec, i);
else {
Identifier property;
if (subscript->isObject()) {
VM_CHECK_EXCEPTION(); // If toObject threw, we must not call toString, which may execute arbitrary code
property = Identifier(subscript->toString(exec));
} else
property = Identifier(subscript->toString(exec));
VM_CHECK_EXCEPTION(); // This check is needed to prevent us from incorrectly calling a getter after an exception is thrown
result = baseObj->get(exec, property);
}
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
JSObject* baseObj = r[base].u.jsValue->toObject(exec);
JSValue* subscript = r[property].u.jsValue;
uint32_t i;
if (subscript->getUInt32(i))
baseObj->put(exec, i, r[value].u.jsValue);
else {
Identifier property;
if (subscript->isObject()) {
VM_CHECK_EXCEPTION(); // If toObject threw, we must not call toString, which may execute arbitrary code
property = Identifier(subscript->toString(exec));
} else
property = Identifier(subscript->toString(exec));
VM_CHECK_EXCEPTION(); // This check is needed to prevent us from incorrectly calling a setter after an exception is thrown
baseObj->put(exec, property, r[value].u.jsValue);
}
VM_CHECK_EXCEPTION();
++vPC;
NEXT_OPCODE;
}
BEGIN_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 = r[base].u.jsValue->toObject(exec); // may throw
JSValue* subscript = r[property].u.jsValue;
JSValue* result;
uint32_t i;
if (subscript->getUInt32(i))
result = jsBoolean(baseObj->deleteProperty(exec, i));
else {
VM_CHECK_EXCEPTION(); // If toObject threw, we must not call toString, which may execute arbitrary code
Identifier property(subscript->toString(exec));
VM_CHECK_EXCEPTION();
result = jsBoolean(baseObj->deleteProperty(exec, property));
}
VM_CHECK_EXCEPTION();
r[dst].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
BEGIN_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. 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.
This opcode is mainly used to initialize array literals.
*/
int base = (++vPC)->u.operand;
unsigned property = (++vPC)->u.operand;
int value = (++vPC)->u.operand;
r[base].u.jsObject->put(exec, property, r[value].u.jsValue);
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_jmp) {
/* jmp target(offset)
Jumps unconditionally to offset target from the current
instruction.
*/
int target = (++vPC)->u.operand;
vPC += target;
NEXT_OPCODE;
}
BEGIN_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 (r[cond].u.jsValue->toBoolean(exec)) {
vPC += target;
NEXT_OPCODE;
}
++vPC;
NEXT_OPCODE;
}
BEGIN_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 (!r[cond].u.jsValue->toBoolean(exec)) {
vPC += target;
NEXT_OPCODE;
}
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = codeBlock->functions[func]->makeFunction(exec, scopeChain);
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = codeBlock->functionExpressions[func]->makeFunction(exec, scopeChain);
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_call_eval) {
int r0 = (++vPC)->u.operand;
int r1 = (++vPC)->u.operand;
int r2 = (++vPC)->u.operand;
int argv = (++vPC)->u.operand;
int argc = (++vPC)->u.operand;
JSValue* v = r[r1].u.jsValue;
JSValue* base = r[r2].u.jsValue;
if (base == scopeChain->globalObject() && v == scopeChain->globalObject()->evalFunction()) {
int registerOffset = r - (*registerBase);
JSObject* thisObject = r[codeBlock->thisRegister].u.jsObject;
registerFile->setSafeForReentry(true);
JSValue* result = eval(exec, thisObject, scopeChain, registerFile, r, argv, argc, exceptionValue);
registerFile->setSafeForReentry(false);
r = (*registerBase) + registerOffset;
if (exceptionValue)
goto vm_throw;
r[r0].u.jsValue = result;
++vPC;
NEXT_OPCODE;
}
// We didn't find the blessed version of eval, so reset vPC and process
// this instruction as a normal function call, supplying the proper 'this'
// value.
vPC -= 5;
r[r2].u.jsValue = base->toObject(exec)->toThisObject(exec);
#if HAVE(COMPUTED_GOTO)
// Hack around gcc performance quirk by performing an indirect goto
// in order to set the vPC -- attempting to do so directly results in a
// significant regression.
goto *op_call_indirect; // indirect goto -> op_call
#endif
// fall through to op_call
}
BEGIN_OPCODE(op_call) {
int r0 = (++vPC)->u.operand;
int r1 = (++vPC)->u.operand;
int r2 = (++vPC)->u.operand;
int argv = (++vPC)->u.operand;
int argc = (++vPC)->u.operand;
JSValue* v = r[r1].u.jsValue;
CallData callData;
CallType callType = v->getCallData(callData);
if (callType == CallTypeJS) {
int registerOffset = r - (*registerBase);
Register* callFrame = r + argv - CallFrameHeaderSize;
int callFrameOffset = registerOffset + argv - CallFrameHeaderSize;
r[argv].u.jsValue = r2 == missingThisObjectMarker() ? exec->globalThisValue() : r[r2].u.jsValue; // "this" value
initializeCallFrame(callFrame, codeBlock, vPC, scopeChain, registerOffset, r0, argv, argc, 0, v);
ScopeChainNode* callDataScopeChain = callData.js.scopeChain;
FunctionBodyNode* functionBodyNode = callData.js.functionBody;
CodeBlock* newCodeBlock = &functionBodyNode->code(callDataScopeChain);
r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, registerOffset, argv, argc, exceptionValue);
if (UNLIKELY(exceptionValue != 0))
goto vm_throw;
codeBlock = newCodeBlock;
exec->m_callFrameOffset = callFrameOffset;
setScopeChain(exec, scopeChain, scopeChainForCall(functionBodyNode, codeBlock, callDataScopeChain, registerBase, r));
k = codeBlock->jsValues.data();
vPC = codeBlock->instructions.begin();
NEXT_OPCODE;
}
if (callType == CallTypeNative) {
int registerOffset = r - (*registerBase);
r[argv].u.jsValue = r2 == missingThisObjectMarker() ? exec->globalThisValue() : (r[r2].u.jsValue)->toObject(exec); // "this" value
JSObject* thisObj = static_cast<JSObject*>(r[argv].u.jsValue);
List args(&r[argv + 1].u.jsValue, argc - 1);
registerFile->setSafeForReentry(true);
JSValue* returnValue = static_cast<JSObject*>(v)->callAsFunction(exec, thisObj, args);
registerFile->setSafeForReentry(false);
r = (*registerBase) + registerOffset;
r[r0].u.jsValue = returnValue;
VM_CHECK_EXCEPTION();
++vPC;
NEXT_OPCODE;
}
ASSERT(callType == CallTypeNone);
exceptionValue = createNotAFunctionError(exec, v, 0);
goto vm_throw;
}
BEGIN_OPCODE(op_ret) {
int r1 = (++vPC)->u.operand;
CodeBlock* oldCodeBlock = codeBlock;
Register* callFrame = r - oldCodeBlock->numLocals - CallFrameHeaderSize;
JSValue* returnValue = r[r1].u.jsValue;
if (JSActivation* activation = static_cast<JSActivation*>(callFrame[OptionalCalleeActivation].u.jsValue)) {
ASSERT(!codeBlock->needsFullScopeChain || scopeChain->object == activation);
ASSERT(activation->isActivationObject());
activation->copyRegisters();
}
if (codeBlock->needsFullScopeChain)
scopeChain->deref();
if (callFrame[CalledAsConstructor].u.i && !returnValue->isObject()) {
JSValue* thisObject = callFrame[CallFrameHeaderSize].u.jsValue;
returnValue = thisObject;
}
codeBlock = callFrame[CallerCodeBlock].u.codeBlock;
if (!codeBlock)
return returnValue;
k = codeBlock->jsValues.data();
vPC = callFrame[ReturnVPC].u.vPC;
setScopeChain(exec, scopeChain, callFrame[CallerScopeChain].u.scopeChain);
int callerRegisterOffset = callFrame[CallerRegisterOffset].u.i;
r = (*registerBase) + callerRegisterOffset;
exec->m_callFrameOffset = callerRegisterOffset - codeBlock->numLocals - CallFrameHeaderSize;
int r0 = callFrame[ReturnValueRegister].u.i;
r[r0].u.jsValue = returnValue;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_construct) {
int r0 = (++vPC)->u.operand;
int r1 = (++vPC)->u.operand;
int argv = (++vPC)->u.operand;
int argc = (++vPC)->u.operand;
JSValue* v = r[r1].u.jsValue;
ConstructData constructData;
ConstructType constructType = v->getConstructData(constructData);
// Removing this line of code causes a measurable regression on squirrelfish.
JSObject* constructor = static_cast<JSObject*>(v);
if (constructType == ConstructTypeJS) {
int registerOffset = r - (*registerBase);
Register* callFrame = r + argv - CallFrameHeaderSize;
int callFrameOffset = registerOffset + argv - CallFrameHeaderSize;
JSObject* prototype;
JSValue* p = constructor->get(exec, exec->propertyNames().prototype);
if (p->isObject())
prototype = static_cast<JSObject*>(p);
else
prototype = scopeChain->globalObject()->objectPrototype();
JSObject* newObject = new JSObject(prototype);
r[argv].u.jsValue = newObject; // "this" value
initializeCallFrame(callFrame, codeBlock, vPC, scopeChain, registerOffset, r0, argv, argc, 1, constructor);
ScopeChainNode* callDataScopeChain = constructData.js.scopeChain;
FunctionBodyNode* functionBodyNode = constructData.js.functionBody;
CodeBlock* newCodeBlock = &functionBodyNode->code(callDataScopeChain);
r = slideRegisterWindowForCall(exec, newCodeBlock, registerFile, registerBase, registerOffset, argv, argc, exceptionValue);
if (exceptionValue)
goto vm_throw;
codeBlock = newCodeBlock;
exec->m_callFrameOffset = callFrameOffset;
setScopeChain(exec, scopeChain, scopeChainForCall(functionBodyNode, codeBlock, callDataScopeChain, registerBase, r));
k = codeBlock->jsValues.data();
vPC = codeBlock->instructions.begin();
NEXT_OPCODE;
}
if (constructType == ConstructTypeNative) {
int registerOffset = r - (*registerBase);
List args(&r[argv + 1].u.jsValue, argc - 1);
registerFile->setSafeForReentry(true);
JSValue* returnValue = constructor->construct(exec, args);
registerFile->setSafeForReentry(false);
r = (*registerBase) + registerOffset;
VM_CHECK_EXCEPTION();
r[r0].u.jsValue = returnValue;
++vPC;
NEXT_OPCODE;
}
ASSERT(constructType == ConstructTypeNone);
exceptionValue = createNotAConstructorError(exec, v, 0);
goto vm_throw;
}
BEGIN_OPCODE(op_push_scope) {
/* push_scope scope(r)
Converts register scope to object, and pushes it onto the top
of the current scope chain.
*/
int scope = (++vPC)->u.operand;
JSValue* v = r[scope].u.jsValue;
JSObject* o = v->toObject(exec);
VM_CHECK_EXCEPTION();
setScopeChain(exec, scopeChain, scopeChain->push(o));
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_pop_scope) {
/* pop_scope
Removes the top item from the current scope chain.
*/
setScopeChain(exec, scopeChain, scopeChain->pop());
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsPropertyNameIterator = JSPropertyNameIterator::create(exec, r[base].u.jsValue);
++vPC;
NEXT_OPCODE;
}
BEGIN_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 = r[iter].u.jsPropertyNameIterator;
if (JSValue* temp = it->next(exec)) {
r[dst].u.jsValue = temp;
vPC += target;
NEXT_OPCODE;
}
it->invalidate();
++vPC;
NEXT_OPCODE;
}
BEGIN_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 = scopeChain;
while (count--)
tmp = tmp->pop();
setScopeChain(exec, scopeChain, tmp);
vPC += target;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_catch) {
ASSERT(exceptionValue);
ASSERT(!exec->hadException());
int r0 = (++vPC)->u.operand;
r[r0].u.jsValue = exceptionValue;
exceptionValue = 0;
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_throw) {
int e = (++vPC)->u.operand;
exceptionValue = r[e].u.jsValue;
handlerVPC = throwException(exec, exceptionValue, registerBase, vPC, codeBlock, k, scopeChain, r);
if (!handlerVPC) {
*exception = exceptionValue;
return jsNull();
}
#if HAVE(COMPUTED_GOTO)
// Hack around gcc performance quirk by performing an indirect goto
// in order to set the vPC -- attempting to do so directly results in a
// significant regression.
goto *op_throw_end_indirect; // indirect goto -> op_throw_end
}
op_throw_end: {
#endif
vPC = handlerVPC;
NEXT_OPCODE;
}
BEGIN_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;
r[dst].u.jsValue = Error::create(exec, (ErrorType)type, k[message]->toString(exec), codeBlock->lineNumberForVPC(vPC), codeBlock->ownerNode->sourceId(), codeBlock->ownerNode->sourceURL());
++vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_end) {
if (codeBlock->needsFullScopeChain) {
ASSERT(scopeChain->refCount > 1);
scopeChain->deref();
}
int r0 = (++vPC)->u.operand;
return r[r0].u.jsValue;
}
BEGIN_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(r[base].u.jsValue->isObject());
JSObject* baseObj = static_cast<JSObject*>(r[base].u.jsValue);
Identifier& ident = codeBlock->identifiers[property];
ASSERT(r[function].u.jsValue->isObject());
baseObj->defineGetter(exec, ident, static_cast<JSObject* >(r[function].u.jsValue));
++vPC;
NEXT_OPCODE;
}
BEGIN_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(r[base].u.jsValue->isObject());
JSObject* baseObj = static_cast<JSObject*>(r[base].u.jsValue);
Identifier& ident = codeBlock->identifiers[property];
ASSERT(r[function].u.jsValue->isObject());
baseObj->defineSetter(exec, ident, static_cast<JSObject* >(r[function].u.jsValue));
++vPC;
NEXT_OPCODE;
}
BEGIN_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;
r[retAddrDst].u.vPC = vPC + 1;
vPC += target;
NEXT_OPCODE;
}
BEGIN_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 = r[retAddrSrc].u.vPC;
NEXT_OPCODE;
}
BEGIN_OPCODE(op_debug) {
/* debug debugHookID(n) firstLine(n) lastLine(n)
Notifies the debugger of the current state of execution:
didEnterCallFrame; willLeaveCallFrame; or willExecuteStatement.
This opcode is only generated while the debugger is attached.
*/
int registerOffset = r - (*registerBase);
registerFile->setSafeForReentry(true);
debug(exec, vPC, codeBlock, scopeChain, registerBase, r);
registerFile->setSafeForReentry(false);
r = (*registerBase) + registerOffset;
vPC += 4;
NEXT_OPCODE;
}
vm_throw: {
exec->clearException();
handlerVPC = throwException(exec, exceptionValue, registerBase, vPC, codeBlock, k, scopeChain, r);
if (!handlerVPC) {
*exception = exceptionValue;
return jsNull();
}
vPC = handlerVPC;
NEXT_OPCODE;
}
}
#undef NEXT_OPCODE
#undef BEGIN_OPCODE
#undef VM_CHECK_EXCEPTION
}
JSValue* Machine::retrieveArguments(ExecState* exec, FunctionImp* function) const
{
Register** registerBase;
int callFrameOffset;
if (!getCallFrame(exec, function, registerBase, callFrameOffset))
return jsNull();
Register* callFrame = (*registerBase) + callFrameOffset;
JSActivation* activation = static_cast<JSActivation*>(callFrame[OptionalCalleeActivation].u.jsValue);
if (!activation) {
CodeBlock* codeBlock = &function->body->generatedCode();
activation = new JSActivation(function->body, registerBase, callFrameOffset + CallFrameHeaderSize + codeBlock->numLocals);
callFrame[OptionalCalleeActivation].u.jsValue = activation;
}
return activation->get(exec, exec->propertyNames().arguments);
}
JSValue* Machine::retrieveCaller(ExecState* exec, FunctionImp* function) const
{
Register** registerBase;
int callFrameOffset;
if (!getCallFrame(exec, function, registerBase, callFrameOffset))
return jsNull();
int callerFrameOffset;
if (!getCallerFunctionOffset(registerBase, callFrameOffset, callerFrameOffset))
return jsNull();
Register* callerFrame = (*registerBase) + callerFrameOffset;
ASSERT(callerFrame[Callee].u.jsValue);
return callerFrame[Callee].u.jsValue;
}
bool Machine::getCallFrame(ExecState* exec, FunctionImp* function, Register**& registerBase, int& callFrameOffset) const
{
callFrameOffset = exec->m_callFrameOffset;
while (1) {
while (callFrameOffset < 0) {
exec = exec->m_prev;
if (!exec)
return false;
callFrameOffset = exec->m_callFrameOffset;
}
registerBase = exec->m_registerFile->basePointer();
Register* callFrame = (*registerBase) + callFrameOffset;
if (callFrame[Callee].u.jsValue == function)
return true;
if (!getCallerFunctionOffset(registerBase, callFrameOffset, callFrameOffset))
callFrameOffset = -1;
}
}
void Machine::getFunctionAndArguments(Register** registerBase, Register* callFrame, FunctionImp*& function, Register*& argv, int& argc)
{
function = static_cast<FunctionImp*>(callFrame[Callee].u.jsValue);
ASSERT(function->inherits(&FunctionImp::info));
argv = (*registerBase) + callFrame[CallerRegisterOffset].u.i + callFrame[ArgumentStartRegister].u.i + 1; // skip "this"
argc = callFrame[ArgumentCount].u.i - 1; // skip "this"
}
} // namespace KJS