blob: 04e19dcad76cd1c89b4c3868ea5005d0e72c5616 [file] [log] [blame]
/*
* Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "LLIntSlowPaths.h"
#if ENABLE(LLINT)
#include "Arguments.h"
#include "ArrayConstructor.h"
#include "CallFrame.h"
#include "CommonSlowPaths.h"
#include "GetterSetter.h"
#include "HostCallReturnValue.h"
#include "Interpreter.h"
#include "JIT.h"
#include "JITDriver.h"
#include "JSActivation.h"
#include "JSGlobalObjectFunctions.h"
#include "JSNameScope.h"
#include "JSPropertyNameIterator.h"
#include "JSString.h"
#include "JSValue.h"
#include "JSWithScope.h"
#include "LLIntCommon.h"
#include "LLIntExceptions.h"
#include "LowLevelInterpreter.h"
#include "Operations.h"
#include <wtf/StringPrintStream.h>
namespace JSC { namespace LLInt {
#define LLINT_BEGIN_NO_SET_PC() \
JSGlobalData& globalData = exec->globalData(); \
NativeCallFrameTracer tracer(&globalData, exec)
#ifndef NDEBUG
#define LLINT_SET_PC_FOR_STUBS() do { \
exec->codeBlock()->bytecodeOffset(pc); \
exec->setCurrentVPC(pc + 1); \
} while (false)
#else
#define LLINT_SET_PC_FOR_STUBS() do { \
exec->setCurrentVPC(pc + 1); \
} while (false)
#endif
#define LLINT_BEGIN() \
LLINT_BEGIN_NO_SET_PC(); \
LLINT_SET_PC_FOR_STUBS()
#define LLINT_OP(index) (exec->uncheckedR(pc[index].u.operand))
#define LLINT_OP_C(index) (exec->r(pc[index].u.operand))
#define LLINT_RETURN_TWO(first, second) do { \
return encodeResult(first, second); \
} while (false)
#define LLINT_END_IMPL() LLINT_RETURN_TWO(pc, exec)
#define LLINT_THROW(exceptionToThrow) do { \
globalData.exception = (exceptionToThrow); \
pc = returnToThrow(exec, pc); \
LLINT_END_IMPL(); \
} while (false)
#define LLINT_CHECK_EXCEPTION() do { \
if (UNLIKELY(globalData.exception)) { \
pc = returnToThrow(exec, pc); \
LLINT_END_IMPL(); \
} \
} while (false)
#define LLINT_END() do { \
LLINT_CHECK_EXCEPTION(); \
LLINT_END_IMPL(); \
} while (false)
#define LLINT_BRANCH(opcode, condition) do { \
bool __b_condition = (condition); \
LLINT_CHECK_EXCEPTION(); \
if (__b_condition) \
pc += pc[OPCODE_LENGTH(opcode) - 1].u.operand; \
else \
pc += OPCODE_LENGTH(opcode); \
LLINT_END_IMPL(); \
} while (false)
#define LLINT_RETURN(value) do { \
JSValue __r_returnValue = (value); \
LLINT_CHECK_EXCEPTION(); \
LLINT_OP(1) = __r_returnValue; \
LLINT_END_IMPL(); \
} while (false)
#if ENABLE(VALUE_PROFILER)
#define LLINT_RETURN_PROFILED(opcode, value) do { \
JSValue __rp_returnValue = (value); \
LLINT_CHECK_EXCEPTION(); \
LLINT_OP(1) = __rp_returnValue; \
LLINT_PROFILE_VALUE(opcode, __rp_returnValue); \
LLINT_END_IMPL(); \
} while (false)
#define LLINT_PROFILE_VALUE(opcode, value) do { \
pc[OPCODE_LENGTH(opcode) - 1].u.profile->m_buckets[0] = \
JSValue::encode(value); \
} while (false)
#else // ENABLE(VALUE_PROFILER)
#define LLINT_RETURN_PROFILED(opcode, value) LLINT_RETURN(value)
#define LLINT_PROFILE_VALUE(opcode, value) do { } while (false)
#endif // ENABLE(VALUE_PROFILER)
#define LLINT_CALL_END_IMPL(exec, callTarget) LLINT_RETURN_TWO((callTarget), (exec))
#define LLINT_CALL_THROW(exec, pc, exceptionToThrow) do { \
ExecState* __ct_exec = (exec); \
Instruction* __ct_pc = (pc); \
globalData.exception = (exceptionToThrow); \
LLINT_CALL_END_IMPL(__ct_exec, callToThrow(__ct_exec, __ct_pc)); \
} while (false)
#define LLINT_CALL_CHECK_EXCEPTION(exec, pc) do { \
ExecState* __cce_exec = (exec); \
Instruction* __cce_pc = (pc); \
if (UNLIKELY(globalData.exception)) \
LLINT_CALL_END_IMPL(__cce_exec, callToThrow(__cce_exec, __cce_pc)); \
} while (false)
#define LLINT_CALL_RETURN(exec, pc, callTarget) do { \
ExecState* __cr_exec = (exec); \
Instruction* __cr_pc = (pc); \
void* __cr_callTarget = (callTarget); \
LLINT_CALL_CHECK_EXCEPTION(__cr_exec->callerFrame(), __cr_pc); \
LLINT_CALL_END_IMPL(__cr_exec, __cr_callTarget); \
} while (false)
extern "C" SlowPathReturnType llint_trace_operand(ExecState* exec, Instruction* pc, int fromWhere, int operand)
{
LLINT_BEGIN();
dataLogF("%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d\n",
exec->codeBlock(),
exec,
static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()),
exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode),
fromWhere,
operand,
pc[operand].u.operand);
LLINT_END();
}
extern "C" SlowPathReturnType llint_trace_value(ExecState* exec, Instruction* pc, int fromWhere, int operand)
{
JSValue value = LLINT_OP_C(operand).jsValue();
union {
struct {
uint32_t tag;
uint32_t payload;
} bits;
EncodedJSValue asValue;
} u;
u.asValue = JSValue::encode(value);
dataLogF(
"%p / %p: executing bc#%zu, op#%u: Trace(%d): %d: %d: %08x:%08x: %s\n",
exec->codeBlock(),
exec,
static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()),
exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode),
fromWhere,
operand,
pc[operand].u.operand,
u.bits.tag,
u.bits.payload,
toCString(value).data());
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(trace_prologue)
{
dataLogF("%p / %p: in prologue.\n", exec->codeBlock(), exec);
LLINT_END_IMPL();
}
static void traceFunctionPrologue(ExecState* exec, const char* comment, CodeSpecializationKind kind)
{
JSFunction* callee = jsCast<JSFunction*>(exec->callee());
FunctionExecutable* executable = callee->jsExecutable();
CodeBlock* codeBlock = &executable->generatedBytecodeFor(kind);
dataLogF("%p / %p: in %s of function %p, executable %p; numVars = %u, numParameters = %u, numCalleeRegisters = %u, caller = %p.\n",
codeBlock, exec, comment, callee, executable,
codeBlock->m_numVars, codeBlock->numParameters(), codeBlock->m_numCalleeRegisters,
exec->callerFrame());
}
LLINT_SLOW_PATH_DECL(trace_prologue_function_for_call)
{
traceFunctionPrologue(exec, "call prologue", CodeForCall);
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(trace_prologue_function_for_construct)
{
traceFunctionPrologue(exec, "construct prologue", CodeForConstruct);
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(trace_arityCheck_for_call)
{
traceFunctionPrologue(exec, "call arity check", CodeForCall);
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(trace_arityCheck_for_construct)
{
traceFunctionPrologue(exec, "construct arity check", CodeForConstruct);
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(trace)
{
dataLogF("%p / %p: executing bc#%zu, %s, scope %p\n",
exec->codeBlock(),
exec,
static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()),
opcodeNames[exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode)],
exec->scope());
if (exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode) == op_ret) {
dataLogF("Will be returning to %p\n", exec->returnPC().value());
dataLogF("The new cfr will be %p\n", exec->callerFrame());
}
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(special_trace)
{
dataLogF("%p / %p: executing special case bc#%zu, op#%u, return PC is %p\n",
exec->codeBlock(),
exec,
static_cast<intptr_t>(pc - exec->codeBlock()->instructions().begin()),
exec->globalData().interpreter->getOpcodeID(pc[0].u.opcode),
exec->returnPC().value());
LLINT_END_IMPL();
}
#if ENABLE(JIT)
inline bool shouldJIT(ExecState* exec)
{
// You can modify this to turn off JITting without rebuilding the world.
return exec->globalData().canUseJIT();
}
// Returns true if we should try to OSR.
inline bool jitCompileAndSetHeuristics(CodeBlock* codeBlock, ExecState* exec)
{
codeBlock->updateAllValueProfilePredictions();
if (!codeBlock->checkIfJITThresholdReached()) {
#if ENABLE(JIT_VERBOSE_OSR)
dataLogF(" JIT threshold should be lifted.\n");
#endif
return false;
}
CodeBlock::JITCompilationResult result = codeBlock->jitCompile(exec);
switch (result) {
case CodeBlock::AlreadyCompiled:
#if ENABLE(JIT_VERBOSE_OSR)
dataLogF(" Code was already compiled.\n");
#endif
codeBlock->jitSoon();
return true;
case CodeBlock::CouldNotCompile:
#if ENABLE(JIT_VERBOSE_OSR)
dataLogF(" JIT compilation failed.\n");
#endif
codeBlock->dontJITAnytimeSoon();
return false;
case CodeBlock::CompiledSuccessfully:
#if ENABLE(JIT_VERBOSE_OSR)
dataLogF(" JIT compilation successful.\n");
#endif
codeBlock->jitSoon();
return true;
}
ASSERT_NOT_REACHED();
return false;
}
enum EntryKind { Prologue, ArityCheck };
static SlowPathReturnType entryOSR(ExecState* exec, Instruction*, CodeBlock* codeBlock, const char *name, EntryKind kind)
{
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(*codeBlock, ": Entered ", name, " with executeCounter = ", codeBlock->llintExecuteCounter(), "\n");
#else
UNUSED_PARAM(name);
#endif
if (!shouldJIT(exec)) {
codeBlock->dontJITAnytimeSoon();
LLINT_RETURN_TWO(0, exec);
}
if (!jitCompileAndSetHeuristics(codeBlock, exec))
LLINT_RETURN_TWO(0, exec);
if (kind == Prologue)
LLINT_RETURN_TWO(codeBlock->getJITCode().executableAddressAtOffset(0), exec);
ASSERT(kind == ArityCheck);
LLINT_RETURN_TWO(codeBlock->getJITCodeWithArityCheck().executableAddress(), exec);
}
LLINT_SLOW_PATH_DECL(entry_osr)
{
return entryOSR(exec, pc, exec->codeBlock(), "entry_osr", Prologue);
}
LLINT_SLOW_PATH_DECL(entry_osr_function_for_call)
{
return entryOSR(exec, pc, &jsCast<JSFunction*>(exec->callee())->jsExecutable()->generatedBytecodeFor(CodeForCall), "entry_osr_function_for_call", Prologue);
}
LLINT_SLOW_PATH_DECL(entry_osr_function_for_construct)
{
return entryOSR(exec, pc, &jsCast<JSFunction*>(exec->callee())->jsExecutable()->generatedBytecodeFor(CodeForConstruct), "entry_osr_function_for_construct", Prologue);
}
LLINT_SLOW_PATH_DECL(entry_osr_function_for_call_arityCheck)
{
return entryOSR(exec, pc, &jsCast<JSFunction*>(exec->callee())->jsExecutable()->generatedBytecodeFor(CodeForCall), "entry_osr_function_for_call_arityCheck", ArityCheck);
}
LLINT_SLOW_PATH_DECL(entry_osr_function_for_construct_arityCheck)
{
return entryOSR(exec, pc, &jsCast<JSFunction*>(exec->callee())->jsExecutable()->generatedBytecodeFor(CodeForConstruct), "entry_osr_function_for_construct_arityCheck", ArityCheck);
}
LLINT_SLOW_PATH_DECL(loop_osr)
{
CodeBlock* codeBlock = exec->codeBlock();
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(*codeBlock, ": Entered loop_osr with executeCounter = ", codeBlock->llintExecuteCounter(), "\n");
#endif
if (!shouldJIT(exec)) {
codeBlock->dontJITAnytimeSoon();
LLINT_RETURN_TWO(0, exec);
}
if (!jitCompileAndSetHeuristics(codeBlock, exec))
LLINT_RETURN_TWO(0, exec);
ASSERT(codeBlock->getJITType() == JITCode::BaselineJIT);
Vector<BytecodeAndMachineOffset> map;
codeBlock->jitCodeMap()->decode(map);
BytecodeAndMachineOffset* mapping = binarySearch<BytecodeAndMachineOffset, unsigned>(map, map.size(), pc - codeBlock->instructions().begin(), BytecodeAndMachineOffset::getBytecodeIndex);
ASSERT(mapping);
ASSERT(mapping->m_bytecodeIndex == static_cast<unsigned>(pc - codeBlock->instructions().begin()));
void* jumpTarget = codeBlock->getJITCode().executableAddressAtOffset(mapping->m_machineCodeOffset);
ASSERT(jumpTarget);
LLINT_RETURN_TWO(jumpTarget, exec);
}
LLINT_SLOW_PATH_DECL(replace)
{
CodeBlock* codeBlock = exec->codeBlock();
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(*codeBlock, ": Entered replace with executeCounter = ", codeBlock->llintExecuteCounter(), "\n");
#endif
if (shouldJIT(exec))
jitCompileAndSetHeuristics(codeBlock, exec);
else
codeBlock->dontJITAnytimeSoon();
LLINT_END_IMPL();
}
#endif // ENABLE(JIT)
LLINT_SLOW_PATH_DECL(stack_check)
{
LLINT_BEGIN();
#if LLINT_SLOW_PATH_TRACING
dataLogF("Checking stack height with exec = %p.\n", exec);
dataLogF("CodeBlock = %p.\n", exec->codeBlock());
dataLogF("Num callee registers = %u.\n", exec->codeBlock()->m_numCalleeRegisters);
dataLogF("Num vars = %u.\n", exec->codeBlock()->m_numVars);
dataLogF("Current end is at %p.\n", exec->globalData().interpreter->stack().end());
#endif
ASSERT(&exec->registers()[exec->codeBlock()->m_numCalleeRegisters] > exec->globalData().interpreter->stack().end());
if (UNLIKELY(!globalData.interpreter->stack().grow(&exec->registers()[exec->codeBlock()->m_numCalleeRegisters]))) {
ReturnAddressPtr returnPC = exec->returnPC();
exec = exec->callerFrame();
globalData.exception = createStackOverflowError(exec);
interpreterThrowInCaller(exec, returnPC);
pc = returnToThrowForThrownException(exec);
}
LLINT_END_IMPL();
}
LLINT_SLOW_PATH_DECL(slow_path_call_arityCheck)
{
LLINT_BEGIN();
ExecState* newExec = CommonSlowPaths::arityCheckFor(exec, &globalData.interpreter->stack(), CodeForCall);
if (!newExec) {
ReturnAddressPtr returnPC = exec->returnPC();
exec = exec->callerFrame();
globalData.exception = createStackOverflowError(exec);
interpreterThrowInCaller(exec, returnPC);
LLINT_RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec);
}
LLINT_RETURN_TWO(0, newExec);
}
LLINT_SLOW_PATH_DECL(slow_path_construct_arityCheck)
{
LLINT_BEGIN();
ExecState* newExec = CommonSlowPaths::arityCheckFor(exec, &globalData.interpreter->stack(), CodeForConstruct);
if (!newExec) {
ReturnAddressPtr returnPC = exec->returnPC();
exec = exec->callerFrame();
globalData.exception = createStackOverflowError(exec);
interpreterThrowInCaller(exec, returnPC);
LLINT_RETURN_TWO(bitwise_cast<void*>(static_cast<uintptr_t>(1)), exec);
}
LLINT_RETURN_TWO(0, newExec);
}
LLINT_SLOW_PATH_DECL(slow_path_create_activation)
{
LLINT_BEGIN();
#if LLINT_SLOW_PATH_TRACING
dataLogF("Creating an activation, exec = %p!\n", exec);
#endif
JSActivation* activation = JSActivation::create(globalData, exec, exec->codeBlock());
exec->setScope(activation);
LLINT_RETURN(JSValue(activation));
}
LLINT_SLOW_PATH_DECL(slow_path_create_arguments)
{
LLINT_BEGIN();
JSValue arguments = JSValue(Arguments::create(globalData, exec));
LLINT_CHECK_EXCEPTION();
exec->uncheckedR(pc[1].u.operand) = arguments;
exec->uncheckedR(unmodifiedArgumentsRegister(pc[1].u.operand)) = arguments;
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_create_this)
{
LLINT_BEGIN();
JSFunction* constructor = jsCast<JSFunction*>(LLINT_OP(2).jsValue().asCell());
#if !ASSERT_DISABLED
ConstructData constructData;
ASSERT(constructor->methodTable()->getConstructData(constructor, constructData) == ConstructTypeJS);
#endif
Structure* structure = constructor->cachedInheritorID(exec);
LLINT_RETURN(constructEmptyObject(exec, structure));
}
LLINT_SLOW_PATH_DECL(slow_path_convert_this)
{
LLINT_BEGIN();
JSValue v1 = LLINT_OP(1).jsValue();
ASSERT(v1.isPrimitive());
#if ENABLE(VALUE_PROFILER)
pc[OPCODE_LENGTH(op_convert_this) - 1].u.profile->m_buckets[0] =
JSValue::encode(v1.structureOrUndefined());
#endif
LLINT_RETURN(v1.toThisObject(exec));
}
LLINT_SLOW_PATH_DECL(slow_path_new_object)
{
LLINT_BEGIN();
LLINT_RETURN(constructEmptyObject(exec));
}
LLINT_SLOW_PATH_DECL(slow_path_new_array)
{
LLINT_BEGIN();
LLINT_RETURN(constructArray(exec, pc[4].u.arrayAllocationProfile, bitwise_cast<JSValue*>(&LLINT_OP(2)), pc[3].u.operand));
}
LLINT_SLOW_PATH_DECL(slow_path_new_array_with_size)
{
LLINT_BEGIN();
LLINT_RETURN(constructArrayWithSizeQuirk(exec, pc[3].u.arrayAllocationProfile, exec->lexicalGlobalObject(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_new_array_buffer)
{
LLINT_BEGIN();
LLINT_RETURN(constructArray(exec, pc[4].u.arrayAllocationProfile, exec->codeBlock()->constantBuffer(pc[2].u.operand), pc[3].u.operand));
}
LLINT_SLOW_PATH_DECL(slow_path_new_regexp)
{
LLINT_BEGIN();
RegExp* regExp = exec->codeBlock()->regexp(pc[2].u.operand);
if (!regExp->isValid())
LLINT_THROW(createSyntaxError(exec, "Invalid flag supplied to RegExp constructor."));
LLINT_RETURN(RegExpObject::create(globalData, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->regExpStructure(), regExp));
}
LLINT_SLOW_PATH_DECL(slow_path_not)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(!LLINT_OP_C(2).jsValue().toBoolean(exec)));
}
LLINT_SLOW_PATH_DECL(slow_path_eq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(JSValue::equal(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_neq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(!JSValue::equal(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_stricteq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(JSValue::strictEqual(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_nstricteq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(!JSValue::strictEqual(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_less)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsLess<true>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_lesseq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsLessEq<true>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_greater)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsLess<false>(exec, LLINT_OP_C(3).jsValue(), LLINT_OP_C(2).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_greatereq)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsLessEq<false>(exec, LLINT_OP_C(3).jsValue(), LLINT_OP_C(2).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_pre_inc)
{
LLINT_BEGIN();
LLINT_RETURN(jsNumber(LLINT_OP(1).jsValue().toNumber(exec) + 1));
}
LLINT_SLOW_PATH_DECL(slow_path_pre_dec)
{
LLINT_BEGIN();
LLINT_RETURN(jsNumber(LLINT_OP(1).jsValue().toNumber(exec) - 1));
}
LLINT_SLOW_PATH_DECL(slow_path_post_inc)
{
LLINT_BEGIN();
double result = LLINT_OP(2).jsValue().toNumber(exec);
LLINT_OP(2) = jsNumber(result + 1);
LLINT_RETURN(jsNumber(result));
}
LLINT_SLOW_PATH_DECL(slow_path_post_dec)
{
LLINT_BEGIN();
double result = LLINT_OP(2).jsValue().toNumber(exec);
LLINT_OP(2) = jsNumber(result - 1);
LLINT_RETURN(jsNumber(result));
}
LLINT_SLOW_PATH_DECL(slow_path_to_jsnumber)
{
LLINT_BEGIN();
LLINT_RETURN(jsNumber(LLINT_OP_C(2).jsValue().toNumber(exec)));
}
LLINT_SLOW_PATH_DECL(slow_path_negate)
{
LLINT_BEGIN();
LLINT_RETURN(jsNumber(-LLINT_OP_C(2).jsValue().toNumber(exec)));
}
LLINT_SLOW_PATH_DECL(slow_path_add)
{
LLINT_BEGIN();
JSValue v1 = LLINT_OP_C(2).jsValue();
JSValue v2 = LLINT_OP_C(3).jsValue();
#if LLINT_SLOW_PATH_TRACING
dataLog("Trying to add ", v1, " to ", v2, ".\n");
#endif
if (v1.isString() && !v2.isObject())
LLINT_RETURN(jsString(exec, asString(v1), v2.toString(exec)));
if (v1.isNumber() && v2.isNumber())
LLINT_RETURN(jsNumber(v1.asNumber() + v2.asNumber()));
LLINT_RETURN(jsAddSlowCase(exec, v1, v2));
}
// The following arithmetic and bitwise operations need to be sure to run
// toNumber() on their operands in order. (A call to toNumber() is idempotent
// if an exception is already set on the ExecState.)
LLINT_SLOW_PATH_DECL(slow_path_mul)
{
LLINT_BEGIN();
double a = LLINT_OP_C(2).jsValue().toNumber(exec);
double b = LLINT_OP_C(3).jsValue().toNumber(exec);
LLINT_RETURN(jsNumber(a * b));
}
LLINT_SLOW_PATH_DECL(slow_path_sub)
{
LLINT_BEGIN();
double a = LLINT_OP_C(2).jsValue().toNumber(exec);
double b = LLINT_OP_C(3).jsValue().toNumber(exec);
LLINT_RETURN(jsNumber(a - b));
}
LLINT_SLOW_PATH_DECL(slow_path_div)
{
LLINT_BEGIN();
double a = LLINT_OP_C(2).jsValue().toNumber(exec);
double b = LLINT_OP_C(3).jsValue().toNumber(exec);
LLINT_RETURN(jsNumber(a / b));
}
LLINT_SLOW_PATH_DECL(slow_path_mod)
{
LLINT_BEGIN();
double a = LLINT_OP_C(2).jsValue().toNumber(exec);
double b = LLINT_OP_C(3).jsValue().toNumber(exec);
LLINT_RETURN(jsNumber(fmod(a, b)));
}
LLINT_SLOW_PATH_DECL(slow_path_lshift)
{
LLINT_BEGIN();
int32_t a = LLINT_OP_C(2).jsValue().toInt32(exec);
uint32_t b = LLINT_OP_C(3).jsValue().toUInt32(exec);
LLINT_RETURN(jsNumber(a << (b & 31)));
}
LLINT_SLOW_PATH_DECL(slow_path_rshift)
{
LLINT_BEGIN();
int32_t a = LLINT_OP_C(2).jsValue().toInt32(exec);
uint32_t b = LLINT_OP_C(3).jsValue().toUInt32(exec);
LLINT_RETURN(jsNumber(a >> (b & 31)));
}
LLINT_SLOW_PATH_DECL(slow_path_urshift)
{
LLINT_BEGIN();
uint32_t a = LLINT_OP_C(2).jsValue().toUInt32(exec);
uint32_t b = LLINT_OP_C(3).jsValue().toUInt32(exec);
LLINT_RETURN(jsNumber(a >> (b & 31)));
}
LLINT_SLOW_PATH_DECL(slow_path_bitand)
{
LLINT_BEGIN();
int32_t a = LLINT_OP_C(2).jsValue().toInt32(exec);
int32_t b = LLINT_OP_C(3).jsValue().toInt32(exec);
LLINT_RETURN(jsNumber(a & b));
}
LLINT_SLOW_PATH_DECL(slow_path_bitor)
{
LLINT_BEGIN();
int32_t a = LLINT_OP_C(2).jsValue().toInt32(exec);
int32_t b = LLINT_OP_C(3).jsValue().toInt32(exec);
LLINT_RETURN(jsNumber(a | b));
}
LLINT_SLOW_PATH_DECL(slow_path_bitxor)
{
LLINT_BEGIN();
int32_t a = LLINT_OP_C(2).jsValue().toInt32(exec);
int32_t b = LLINT_OP_C(3).jsValue().toInt32(exec);
LLINT_RETURN(jsNumber(a ^ b));
}
LLINT_SLOW_PATH_DECL(slow_path_check_has_instance)
{
LLINT_BEGIN();
JSValue value = LLINT_OP_C(2).jsValue();
JSValue baseVal = LLINT_OP_C(3).jsValue();
if (baseVal.isObject()) {
JSObject* baseObject = asObject(baseVal);
ASSERT(!baseObject->structure()->typeInfo().implementsDefaultHasInstance());
if (baseObject->structure()->typeInfo().implementsHasInstance()) {
pc += pc[4].u.operand;
LLINT_RETURN(jsBoolean(baseObject->methodTable()->customHasInstance(baseObject, exec, value)));
}
}
LLINT_THROW(createInvalidParamError(exec, "instanceof", baseVal));
}
LLINT_SLOW_PATH_DECL(slow_path_instanceof)
{
LLINT_BEGIN();
JSValue value = LLINT_OP_C(2).jsValue();
JSValue proto = LLINT_OP_C(3).jsValue();
ASSERT(!value.isObject() || !proto.isObject());
LLINT_RETURN(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
}
LLINT_SLOW_PATH_DECL(slow_path_typeof)
{
LLINT_BEGIN();
LLINT_RETURN(jsTypeStringForValue(exec, LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_is_object)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsIsObjectType(exec, LLINT_OP_C(2).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_is_function)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(jsIsFunctionType(LLINT_OP_C(2).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_in)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(CommonSlowPaths::opIn(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue())));
}
LLINT_SLOW_PATH_DECL(slow_path_resolve)
{
LLINT_BEGIN();
Identifier ident = exec->codeBlock()->identifier(pc[2].u.operand);
ResolveOperations* operations = exec->codeBlock()->resolveOperations(pc[3].u.operand);
JSValue result = JSScope::resolve(exec, ident, operations);
ASSERT(operations->size());
ASSERT(operations == exec->codeBlock()->resolveOperations(pc[3].u.operand));
switch (operations->data()[0].m_operation) {
case ResolveOperation::GetAndReturnGlobalProperty:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_global_property);
break;
case ResolveOperation::GetAndReturnGlobalVar:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_global_var);
break;
case ResolveOperation::SkipTopScopeNode:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_scoped_var_with_top_scope_check);
break;
case ResolveOperation::SkipScopes:
if (operations->data()[0].m_scopesToSkip)
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_scoped_var);
else
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_scoped_var_on_top_scope);
break;
default:
break;
}
LLINT_RETURN_PROFILED(op_resolve, result);
}
LLINT_SLOW_PATH_DECL(slow_path_put_to_base)
{
LLINT_BEGIN();
PutToBaseOperation* operation = exec->codeBlock()->putToBaseOperation(pc[4].u.operand);
JSScope::resolvePut(exec, LLINT_OP_C(1).jsValue(), exec->codeBlock()->identifier(pc[2].u.operand), LLINT_OP_C(3).jsValue(), operation);
switch (operation->m_kind) {
case PutToBaseOperation::VariablePut:
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_to_base_variable);
break;
default:
break;
}
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_resolve_base)
{
LLINT_BEGIN();
Identifier& ident = exec->codeBlock()->identifier(pc[2].u.operand);
ResolveOperations* operations = exec->codeBlock()->resolveOperations(pc[4].u.operand);
JSValue result;
if (pc[3].u.operand) {
result = JSScope::resolveBase(exec, ident, true, operations, exec->codeBlock()->putToBaseOperation(pc[5].u.operand));
if (!result)
LLINT_THROW(globalData.exception);
} else
result = JSScope::resolveBase(exec, ident, false, operations, exec->codeBlock()->putToBaseOperation(pc[5].u.operand));
ASSERT(operations->size());
switch (operations->data()[0].m_operation) {
case ResolveOperation::ReturnGlobalObjectAsBase:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_base_to_global);
break;
case ResolveOperation::SkipTopScopeNode:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_base_to_scope_with_top_scope_check);
break;
case ResolveOperation::SkipScopes:
pc[0].u.opcode = LLInt::getOpcode(llint_op_resolve_base_to_scope);
break;
default:
break;
}
LLINT_PROFILE_VALUE(op_resolve_base, result);
LLINT_RETURN(result);
}
LLINT_SLOW_PATH_DECL(slow_path_ensure_property_exists)
{
LLINT_BEGIN();
JSObject* object = asObject(LLINT_OP(1).jsValue());
PropertySlot slot(object);
Identifier& ident = exec->codeBlock()->identifier(pc[2].u.operand);
if (!object->getPropertySlot(exec, ident, slot))
LLINT_THROW(createErrorForInvalidGlobalAssignment(exec, ident.string()));
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_resolve_with_base)
{
LLINT_BEGIN();
ResolveOperations* operations = exec->codeBlock()->resolveOperations(pc[4].u.operand);
JSValue result = JSScope::resolveWithBase(exec, exec->codeBlock()->identifier(pc[3].u.operand), &LLINT_OP(1), operations, exec->codeBlock()->putToBaseOperation(pc[5].u.operand));
LLINT_CHECK_EXCEPTION();
LLINT_OP(2) = result;
LLINT_PROFILE_VALUE(op_resolve_with_base, result);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_resolve_with_this)
{
LLINT_BEGIN();
ResolveOperations* operations = exec->codeBlock()->resolveOperations(pc[4].u.operand);
JSValue result = JSScope::resolveWithThis(exec, exec->codeBlock()->identifier(pc[3].u.operand), &LLINT_OP(1), operations);
LLINT_CHECK_EXCEPTION();
LLINT_OP(2) = result;
LLINT_PROFILE_VALUE(op_resolve_with_this, result);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_init_global_const_check)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
symbolTablePut(codeBlock->globalObject(), exec, codeBlock->identifier(pc[4].u.operand), LLINT_OP_C(2).jsValue(), true);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
Identifier& ident = codeBlock->identifier(pc[3].u.operand);
JSValue baseValue = LLINT_OP_C(2).jsValue();
PropertySlot slot(baseValue);
JSValue result = baseValue.get(exec, ident, slot);
LLINT_CHECK_EXCEPTION();
LLINT_OP(1) = result;
if (!LLINT_ALWAYS_ACCESS_SLOW
&& baseValue.isCell()
&& slot.isCacheable()
&& slot.slotBase() == baseValue
&& slot.cachedPropertyType() == PropertySlot::Value) {
JSCell* baseCell = baseValue.asCell();
Structure* structure = baseCell->structure();
if (!structure->isUncacheableDictionary()
&& !structure->typeInfo().prohibitsPropertyCaching()) {
pc[4].u.structure.set(
globalData, codeBlock->ownerExecutable(), structure);
if (isInlineOffset(slot.cachedOffset())) {
pc[0].u.opcode = LLInt::getOpcode(llint_op_get_by_id);
pc[5].u.operand = offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + JSObject::offsetOfInlineStorage();
} else {
pc[0].u.opcode = LLInt::getOpcode(llint_op_get_by_id_out_of_line);
pc[5].u.operand = offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue);
}
}
}
if (!LLINT_ALWAYS_ACCESS_SLOW
&& isJSArray(baseValue)
&& ident == exec->propertyNames().length) {
pc[0].u.opcode = LLInt::getOpcode(llint_op_get_array_length);
#if ENABLE(VALUE_PROFILER)
ArrayProfile* arrayProfile = codeBlock->getOrAddArrayProfile(pc - codeBlock->instructions().begin());
arrayProfile->observeStructure(baseValue.asCell()->structure());
pc[4].u.arrayProfile = arrayProfile;
#endif
}
#if ENABLE(VALUE_PROFILER)
pc[OPCODE_LENGTH(op_get_by_id) - 1].u.profile->m_buckets[0] = JSValue::encode(result);
#endif
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_get_arguments_length)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
Identifier& ident = codeBlock->identifier(pc[3].u.operand);
JSValue baseValue = LLINT_OP(2).jsValue();
PropertySlot slot(baseValue);
LLINT_RETURN(baseValue.get(exec, ident, slot));
}
LLINT_SLOW_PATH_DECL(slow_path_put_by_id)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
Identifier& ident = codeBlock->identifier(pc[2].u.operand);
JSValue baseValue = LLINT_OP_C(1).jsValue();
PutPropertySlot slot(codeBlock->isStrictMode());
if (pc[8].u.operand)
asObject(baseValue)->putDirect(globalData, ident, LLINT_OP_C(3).jsValue(), slot);
else
baseValue.put(exec, ident, LLINT_OP_C(3).jsValue(), slot);
LLINT_CHECK_EXCEPTION();
if (!LLINT_ALWAYS_ACCESS_SLOW
&& baseValue.isCell()
&& slot.isCacheable()) {
JSCell* baseCell = baseValue.asCell();
Structure* structure = baseCell->structure();
if (!structure->isUncacheableDictionary()
&& !structure->typeInfo().prohibitsPropertyCaching()
&& baseCell == slot.base()) {
if (slot.type() == PutPropertySlot::NewProperty) {
if (!structure->isDictionary() && structure->previousID()->outOfLineCapacity() == structure->outOfLineCapacity()) {
ASSERT(structure->previousID()->transitionWatchpointSetHasBeenInvalidated());
// This is needed because some of the methods we call
// below may GC.
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id);
if (normalizePrototypeChain(exec, baseCell) != InvalidPrototypeChain) {
ASSERT(structure->previousID()->isObject());
pc[4].u.structure.set(
globalData, codeBlock->ownerExecutable(), structure->previousID());
if (isInlineOffset(slot.cachedOffset()))
pc[5].u.operand = offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + JSObject::offsetOfInlineStorage();
else
pc[5].u.operand = offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue);
pc[6].u.structure.set(
globalData, codeBlock->ownerExecutable(), structure);
StructureChain* chain = structure->prototypeChain(exec);
ASSERT(chain);
pc[7].u.structureChain.set(
globalData, codeBlock->ownerExecutable(), chain);
if (pc[8].u.operand) {
if (isInlineOffset(slot.cachedOffset()))
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id_transition_direct);
else
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id_transition_direct_out_of_line);
} else {
if (isInlineOffset(slot.cachedOffset()))
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id_transition_normal);
else
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id_transition_normal_out_of_line);
}
}
}
} else {
pc[4].u.structure.set(
globalData, codeBlock->ownerExecutable(), structure);
if (isInlineOffset(slot.cachedOffset())) {
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id);
pc[5].u.operand = offsetInInlineStorage(slot.cachedOffset()) * sizeof(JSValue) + JSObject::offsetOfInlineStorage();
} else {
pc[0].u.opcode = LLInt::getOpcode(llint_op_put_by_id_out_of_line);
pc[5].u.operand = offsetInButterfly(slot.cachedOffset()) * sizeof(JSValue);
}
}
}
}
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_del_by_id)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
JSObject* baseObject = LLINT_OP_C(2).jsValue().toObject(exec);
bool couldDelete = baseObject->methodTable()->deleteProperty(baseObject, exec, codeBlock->identifier(pc[3].u.operand));
LLINT_CHECK_EXCEPTION();
if (!couldDelete && codeBlock->isStrictMode())
LLINT_THROW(createTypeError(exec, "Unable to delete property."));
LLINT_RETURN(jsBoolean(couldDelete));
}
inline JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript)
{
if (LIKELY(baseValue.isCell() && subscript.isString())) {
if (JSValue result = baseValue.asCell()->fastGetOwnProperty(exec, asString(subscript)->value(exec)))
return result;
}
if (subscript.isUInt32()) {
uint32_t i = subscript.asUInt32();
if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i))
return asString(baseValue)->getIndex(exec, i);
return baseValue.get(exec, i);
}
if (isName(subscript))
return baseValue.get(exec, jsCast<NameInstance*>(subscript.asCell())->privateName());
Identifier property(exec, subscript.toString(exec)->value(exec));
return baseValue.get(exec, property);
}
LLINT_SLOW_PATH_DECL(slow_path_get_by_val)
{
LLINT_BEGIN();
LLINT_RETURN_PROFILED(op_get_by_val, getByVal(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_get_argument_by_val)
{
LLINT_BEGIN();
JSValue arguments = LLINT_OP(2).jsValue();
if (!arguments) {
arguments = Arguments::create(globalData, exec);
LLINT_CHECK_EXCEPTION();
LLINT_OP(2) = arguments;
exec->uncheckedR(unmodifiedArgumentsRegister(pc[2].u.operand)) = arguments;
}
LLINT_RETURN_PROFILED(op_get_argument_by_val, getByVal(exec, arguments, LLINT_OP_C(3).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_get_by_pname)
{
LLINT_BEGIN();
LLINT_RETURN(getByVal(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_put_by_val)
{
LLINT_BEGIN();
JSValue baseValue = LLINT_OP_C(1).jsValue();
JSValue subscript = LLINT_OP_C(2).jsValue();
JSValue value = LLINT_OP_C(3).jsValue();
if (LIKELY(subscript.isUInt32())) {
uint32_t i = subscript.asUInt32();
if (baseValue.isObject()) {
JSObject* object = asObject(baseValue);
if (object->canSetIndexQuickly(i))
object->setIndexQuickly(globalData, i, value);
else
object->methodTable()->putByIndex(object, exec, i, value, exec->codeBlock()->isStrictMode());
LLINT_END();
}
baseValue.putByIndex(exec, i, value, exec->codeBlock()->isStrictMode());
LLINT_END();
}
if (isName(subscript)) {
PutPropertySlot slot(exec->codeBlock()->isStrictMode());
baseValue.put(exec, jsCast<NameInstance*>(subscript.asCell())->privateName(), value, slot);
LLINT_END();
}
Identifier property(exec, subscript.toString(exec)->value(exec));
LLINT_CHECK_EXCEPTION();
PutPropertySlot slot(exec->codeBlock()->isStrictMode());
baseValue.put(exec, property, value, slot);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_del_by_val)
{
LLINT_BEGIN();
JSValue baseValue = LLINT_OP_C(2).jsValue();
JSObject* baseObject = baseValue.toObject(exec);
JSValue subscript = LLINT_OP_C(3).jsValue();
bool couldDelete;
uint32_t i;
if (subscript.getUInt32(i))
couldDelete = baseObject->methodTable()->deletePropertyByIndex(baseObject, exec, i);
else if (isName(subscript))
couldDelete = baseObject->methodTable()->deleteProperty(baseObject, exec, jsCast<NameInstance*>(subscript.asCell())->privateName());
else {
LLINT_CHECK_EXCEPTION();
Identifier property(exec, subscript.toString(exec)->value(exec));
LLINT_CHECK_EXCEPTION();
couldDelete = baseObject->methodTable()->deleteProperty(baseObject, exec, property);
}
if (!couldDelete && exec->codeBlock()->isStrictMode())
LLINT_THROW(createTypeError(exec, "Unable to delete property."));
LLINT_RETURN(jsBoolean(couldDelete));
}
LLINT_SLOW_PATH_DECL(slow_path_put_by_index)
{
LLINT_BEGIN();
JSValue arrayValue = LLINT_OP_C(1).jsValue();
ASSERT(isJSArray(arrayValue));
asArray(arrayValue)->putDirectIndex(exec, pc[2].u.operand, LLINT_OP_C(3).jsValue());
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_put_getter_setter)
{
LLINT_BEGIN();
ASSERT(LLINT_OP(1).jsValue().isObject());
JSObject* baseObj = asObject(LLINT_OP(1).jsValue());
GetterSetter* accessor = GetterSetter::create(exec);
LLINT_CHECK_EXCEPTION();
JSValue getter = LLINT_OP(3).jsValue();
JSValue setter = LLINT_OP(4).jsValue();
ASSERT(getter.isObject() || getter.isUndefined());
ASSERT(setter.isObject() || setter.isUndefined());
ASSERT(getter.isObject() || setter.isObject());
if (!getter.isUndefined())
accessor->setGetter(globalData, asObject(getter));
if (!setter.isUndefined())
accessor->setSetter(globalData, asObject(setter));
baseObj->putDirectAccessor(
exec,
exec->codeBlock()->identifier(pc[2].u.operand),
accessor, Accessor);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_jmp_scopes)
{
LLINT_BEGIN();
unsigned count = pc[1].u.operand;
JSScope* tmp = exec->scope();
while (count--)
tmp = tmp->next();
exec->setScope(tmp);
pc += pc[2].u.operand;
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_jtrue)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jtrue, LLINT_OP_C(1).jsValue().toBoolean(exec));
}
LLINT_SLOW_PATH_DECL(slow_path_jfalse)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jfalse, !LLINT_OP_C(1).jsValue().toBoolean(exec));
}
LLINT_SLOW_PATH_DECL(slow_path_jless)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jless, jsLess<true>(exec, LLINT_OP_C(1).jsValue(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jnless)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jnless, !jsLess<true>(exec, LLINT_OP_C(1).jsValue(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jgreater)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jgreater, jsLess<false>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(1).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jngreater)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jngreater, !jsLess<false>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(1).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jlesseq)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jlesseq, jsLessEq<true>(exec, LLINT_OP_C(1).jsValue(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jnlesseq)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jnlesseq, !jsLessEq<true>(exec, LLINT_OP_C(1).jsValue(), LLINT_OP_C(2).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jgreatereq)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jgreatereq, jsLessEq<false>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(1).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_jngreatereq)
{
LLINT_BEGIN();
LLINT_BRANCH(op_jngreatereq, !jsLessEq<false>(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(1).jsValue()));
}
LLINT_SLOW_PATH_DECL(slow_path_switch_imm)
{
LLINT_BEGIN();
JSValue scrutinee = LLINT_OP_C(3).jsValue();
ASSERT(scrutinee.isDouble());
double value = scrutinee.asDouble();
int32_t intValue = static_cast<int32_t>(value);
int defaultOffset = pc[2].u.operand;
if (value == intValue) {
CodeBlock* codeBlock = exec->codeBlock();
pc += codeBlock->immediateSwitchJumpTable(pc[1].u.operand).offsetForValue(intValue, defaultOffset);
} else
pc += defaultOffset;
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_switch_char)
{
LLINT_BEGIN();
JSValue scrutinee = LLINT_OP_C(3).jsValue();
ASSERT(scrutinee.isString());
JSString* string = asString(scrutinee);
ASSERT(string->length() == 1);
int defaultOffset = pc[2].u.operand;
StringImpl* impl = string->value(exec).impl();
CodeBlock* codeBlock = exec->codeBlock();
pc += codeBlock->characterSwitchJumpTable(pc[1].u.operand).offsetForValue((*impl)[0], defaultOffset);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_switch_string)
{
LLINT_BEGIN();
JSValue scrutinee = LLINT_OP_C(3).jsValue();
int defaultOffset = pc[2].u.operand;
if (!scrutinee.isString())
pc += defaultOffset;
else {
CodeBlock* codeBlock = exec->codeBlock();
pc += codeBlock->stringSwitchJumpTable(pc[1].u.operand).offsetForValue(asString(scrutinee)->value(exec).impl(), defaultOffset);
}
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_new_func)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
ASSERT(codeBlock->codeType() != FunctionCode
|| !codeBlock->needsFullScopeChain()
|| exec->uncheckedR(codeBlock->activationRegister()).jsValue());
#if LLINT_SLOW_PATH_TRACING
dataLogF("Creating function!\n");
#endif
LLINT_RETURN(JSFunction::create(exec, codeBlock->functionDecl(pc[2].u.operand), exec->scope()));
}
LLINT_SLOW_PATH_DECL(slow_path_new_func_exp)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
FunctionExecutable* function = codeBlock->functionExpr(pc[2].u.operand);
JSFunction* func = JSFunction::create(exec, function, exec->scope());
LLINT_RETURN(func);
}
static SlowPathReturnType handleHostCall(ExecState* execCallee, Instruction* pc, JSValue callee, CodeSpecializationKind kind)
{
ExecState* exec = execCallee->callerFrame();
JSGlobalData& globalData = exec->globalData();
execCallee->setScope(exec->scope());
execCallee->setCodeBlock(0);
execCallee->clearReturnPC();
if (kind == CodeForCall) {
CallData callData;
CallType callType = getCallData(callee, callData);
ASSERT(callType != CallTypeJS);
if (callType == CallTypeHost) {
NativeCallFrameTracer tracer(&globalData, execCallee);
execCallee->setCallee(asObject(callee));
globalData.hostCallReturnValue = JSValue::decode(callData.native.function(execCallee));
LLINT_CALL_RETURN(execCallee, pc, LLInt::getCodePtr(getHostCallReturnValue));
}
#if LLINT_SLOW_PATH_TRACING
dataLog("Call callee is not a function: ", callee, "\n");
#endif
ASSERT(callType == CallTypeNone);
LLINT_CALL_THROW(exec, pc, createNotAFunctionError(exec, callee));
}
ASSERT(kind == CodeForConstruct);
ConstructData constructData;
ConstructType constructType = getConstructData(callee, constructData);
ASSERT(constructType != ConstructTypeJS);
if (constructType == ConstructTypeHost) {
NativeCallFrameTracer tracer(&globalData, execCallee);
execCallee->setCallee(asObject(callee));
globalData.hostCallReturnValue = JSValue::decode(constructData.native.function(execCallee));
LLINT_CALL_RETURN(execCallee, pc, LLInt::getCodePtr(getHostCallReturnValue));
}
#if LLINT_SLOW_PATH_TRACING
dataLog("Constructor callee is not a function: ", callee, "\n");
#endif
ASSERT(constructType == ConstructTypeNone);
LLINT_CALL_THROW(exec, pc, createNotAConstructorError(exec, callee));
}
inline SlowPathReturnType setUpCall(ExecState* execCallee, Instruction* pc, CodeSpecializationKind kind, JSValue calleeAsValue, LLIntCallLinkInfo* callLinkInfo = 0)
{
#if LLINT_SLOW_PATH_TRACING
dataLogF("Performing call with recorded PC = %p\n", execCallee->callerFrame()->currentVPC());
#endif
JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue);
if (!calleeAsFunctionCell)
return handleHostCall(execCallee, pc, calleeAsValue, kind);
JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
JSScope* scope = callee->scopeUnchecked();
JSGlobalData& globalData = *scope->globalData();
execCallee->setScope(scope);
ExecutableBase* executable = callee->executable();
MacroAssemblerCodePtr codePtr;
CodeBlock* codeBlock = 0;
if (executable->isHostFunction())
codePtr = executable->hostCodeEntryFor(kind);
else {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
JSObject* error = functionExecutable->compileFor(execCallee, callee->scope(), kind);
if (error)
LLINT_CALL_THROW(execCallee->callerFrame(), pc, error);
codeBlock = &functionExecutable->generatedBytecodeFor(kind);
ASSERT(codeBlock);
if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()))
codePtr = functionExecutable->jsCodeWithArityCheckEntryFor(kind);
else
codePtr = functionExecutable->jsCodeEntryFor(kind);
}
if (callLinkInfo) {
if (callLinkInfo->isOnList())
callLinkInfo->remove();
ExecState* execCaller = execCallee->callerFrame();
callLinkInfo->callee.set(globalData, execCaller->codeBlock()->ownerExecutable(), callee);
callLinkInfo->lastSeenCallee.set(globalData, execCaller->codeBlock()->ownerExecutable(), callee);
callLinkInfo->machineCodeTarget = codePtr;
if (codeBlock)
codeBlock->linkIncomingCall(callLinkInfo);
}
LLINT_CALL_RETURN(execCallee, pc, codePtr.executableAddress());
}
inline SlowPathReturnType genericCall(ExecState* exec, Instruction* pc, CodeSpecializationKind kind)
{
// This needs to:
// - Set up a call frame.
// - Figure out what to call and compile it if necessary.
// - If possible, link the call's inline cache.
// - Return a tuple of machine code address to call and the new call frame.
JSValue calleeAsValue = LLINT_OP_C(1).jsValue();
ExecState* execCallee = exec + pc[3].u.operand;
execCallee->setArgumentCountIncludingThis(pc[2].u.operand);
execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
execCallee->setCallerFrame(exec);
ASSERT(pc[4].u.callLinkInfo);
return setUpCall(execCallee, pc, kind, calleeAsValue, pc[4].u.callLinkInfo);
}
LLINT_SLOW_PATH_DECL(slow_path_call)
{
LLINT_BEGIN_NO_SET_PC();
return genericCall(exec, pc, CodeForCall);
}
LLINT_SLOW_PATH_DECL(slow_path_construct)
{
LLINT_BEGIN_NO_SET_PC();
return genericCall(exec, pc, CodeForConstruct);
}
LLINT_SLOW_PATH_DECL(slow_path_call_varargs)
{
LLINT_BEGIN();
// This needs to:
// - Set up a call frame while respecting the variable arguments.
// - Figure out what to call and compile it if necessary.
// - Return a tuple of machine code address to call and the new call frame.
JSValue calleeAsValue = LLINT_OP_C(1).jsValue();
ExecState* execCallee = loadVarargs(
exec, &globalData.interpreter->stack(),
LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue(), pc[4].u.operand);
LLINT_CALL_CHECK_EXCEPTION(exec, pc);
execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
execCallee->setCallerFrame(exec);
exec->setCurrentVPC(pc + OPCODE_LENGTH(op_call_varargs));
return setUpCall(execCallee, pc, CodeForCall, calleeAsValue);
}
LLINT_SLOW_PATH_DECL(slow_path_call_eval)
{
LLINT_BEGIN_NO_SET_PC();
JSValue calleeAsValue = LLINT_OP(1).jsValue();
ExecState* execCallee = exec + pc[3].u.operand;
execCallee->setArgumentCountIncludingThis(pc[2].u.operand);
execCallee->setCallerFrame(exec);
execCallee->uncheckedR(JSStack::Callee) = calleeAsValue;
execCallee->setScope(exec->scope());
execCallee->setReturnPC(LLInt::getCodePtr(llint_generic_return_point));
execCallee->setCodeBlock(0);
exec->setCurrentVPC(pc + OPCODE_LENGTH(op_call_eval));
if (!isHostFunction(calleeAsValue, globalFuncEval))
return setUpCall(execCallee, pc, CodeForCall, calleeAsValue);
globalData.hostCallReturnValue = eval(execCallee);
LLINT_CALL_RETURN(execCallee, pc, LLInt::getCodePtr(getHostCallReturnValue));
}
LLINT_SLOW_PATH_DECL(slow_path_tear_off_activation)
{
LLINT_BEGIN();
ASSERT(exec->codeBlock()->needsFullScopeChain());
jsCast<JSActivation*>(LLINT_OP(1).jsValue())->tearOff(globalData);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_tear_off_arguments)
{
LLINT_BEGIN();
ASSERT(exec->codeBlock()->usesArguments());
Arguments* arguments = jsCast<Arguments*>(exec->uncheckedR(unmodifiedArgumentsRegister(pc[1].u.operand)).jsValue());
if (JSValue activationValue = LLINT_OP_C(2).jsValue())
arguments->didTearOffActivation(exec, jsCast<JSActivation*>(activationValue));
else
arguments->tearOff(exec);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_strcat)
{
LLINT_BEGIN();
LLINT_RETURN(jsString(exec, &LLINT_OP(2), pc[3].u.operand));
}
LLINT_SLOW_PATH_DECL(slow_path_to_primitive)
{
LLINT_BEGIN();
LLINT_RETURN(LLINT_OP_C(2).jsValue().toPrimitive(exec));
}
LLINT_SLOW_PATH_DECL(slow_path_get_pnames)
{
LLINT_BEGIN();
JSValue v = LLINT_OP(2).jsValue();
if (v.isUndefinedOrNull()) {
pc += pc[5].u.operand;
LLINT_END();
}
JSObject* o = v.toObject(exec);
Structure* structure = o->structure();
JSPropertyNameIterator* jsPropertyNameIterator = structure->enumerationCache();
if (!jsPropertyNameIterator || jsPropertyNameIterator->cachedPrototypeChain() != structure->prototypeChain(exec))
jsPropertyNameIterator = JSPropertyNameIterator::create(exec, o);
LLINT_OP(1) = JSValue(jsPropertyNameIterator);
LLINT_OP(2) = JSValue(o);
LLINT_OP(3) = Register::withInt(0);
LLINT_OP(4) = Register::withInt(jsPropertyNameIterator->size());
pc += OPCODE_LENGTH(op_get_pnames);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_next_pname)
{
LLINT_BEGIN();
JSObject* base = asObject(LLINT_OP(2).jsValue());
JSString* property = asString(LLINT_OP(1).jsValue());
if (base->hasProperty(exec, Identifier(exec, property->value(exec)))) {
// Go to target.
pc += pc[6].u.operand;
} // Else, don't change the PC, so the interpreter will reloop.
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_push_with_scope)
{
LLINT_BEGIN();
JSValue v = LLINT_OP_C(1).jsValue();
JSObject* o = v.toObject(exec);
LLINT_CHECK_EXCEPTION();
exec->setScope(JSWithScope::create(exec, o));
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_pop_scope)
{
LLINT_BEGIN();
exec->setScope(exec->scope()->next());
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_push_name_scope)
{
LLINT_BEGIN();
CodeBlock* codeBlock = exec->codeBlock();
JSNameScope* scope = JSNameScope::create(exec, codeBlock->identifier(pc[1].u.operand), LLINT_OP(2).jsValue(), pc[3].u.operand);
exec->setScope(scope);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_throw)
{
LLINT_BEGIN();
LLINT_THROW(LLINT_OP_C(1).jsValue());
}
LLINT_SLOW_PATH_DECL(slow_path_throw_static_error)
{
LLINT_BEGIN();
if (pc[2].u.operand)
LLINT_THROW(createReferenceError(exec, LLINT_OP_C(1).jsValue().toString(exec)->value(exec)));
else
LLINT_THROW(createTypeError(exec, LLINT_OP_C(1).jsValue().toString(exec)->value(exec)));
}
LLINT_SLOW_PATH_DECL(slow_path_debug)
{
LLINT_BEGIN();
int debugHookID = pc[1].u.operand;
int firstLine = pc[2].u.operand;
int lastLine = pc[3].u.operand;
int column = pc[4].u.operand;
globalData.interpreter->debug(exec, static_cast<DebugHookID>(debugHookID), firstLine, lastLine, column);
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_profile_will_call)
{
LLINT_BEGIN();
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->willExecute(exec, LLINT_OP(1).jsValue());
LLINT_END();
}
LLINT_SLOW_PATH_DECL(slow_path_profile_did_call)
{
LLINT_BEGIN();
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(exec, LLINT_OP(1).jsValue());
LLINT_END();
}
LLINT_SLOW_PATH_DECL(throw_from_native_call)
{
LLINT_BEGIN();
ASSERT(globalData.exception);
LLINT_END();
}
} } // namespace JSC::LLInt
#endif // ENABLE(LLINT)