blob: 212bc46d126c8e99d9cf9f933bd3ac3559c09f98 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Ion.h"
#include "IonCompartment.h"
#include "jit/BaselineFrame-inl.h"
#include "jit/BaselineIC.h"
#include "jit/IonFrames.h"
#include "vm/Debugger.h"
#include "vm/Interpreter.h"
#include "vm/StringObject-inl.h"
#include "builtin/ParallelArray.h"
#include "frontend/BytecodeCompiler.h"
#include "jsboolinlines.h"
#include "jit/IonFrames-inl.h" // for GetTopIonJSScript
#include "vm/Interpreter-inl.h"
#include "vm/StringObject-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
// Don't explicitly initialize, it's not guaranteed that this initializer will
// run before the constructors for static VMFunctions.
/* static */ VMFunction *VMFunction::functions;
void
VMFunction::addToFunctions()
{
static bool initialized = false;
if (!initialized) {
initialized = true;
functions = NULL;
}
this->next = functions;
functions = this;
}
bool
InvokeFunction(JSContext *cx, HandleFunction fun0, uint32_t argc, Value *argv, Value *rval)
{
RootedFunction fun(cx, fun0);
if (fun->isInterpreted()) {
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return false;
// Clone function at call site if needed.
if (fun->nonLazyScript()->shouldCloneAtCallsite) {
RootedScript script(cx);
jsbytecode *pc;
types::TypeScript::GetPcScript(cx, script.address(), &pc);
fun = CloneFunctionAtCallsite(cx, fun0, script, pc);
if (!fun)
return false;
}
}
// Data in the argument vector is arranged for a JIT -> JIT call.
Value thisv = argv[0];
Value *argvWithoutThis = argv + 1;
// For constructing functions, |this| is constructed at caller side and we can just call Invoke.
// When creating this failed / is impossible at caller site, i.e. MagicValue(JS_IS_CONSTRUCTING),
// we use InvokeConstructor that creates it at the callee side.
if (thisv.isMagic(JS_IS_CONSTRUCTING))
return InvokeConstructor(cx, ObjectValue(*fun), argc, argvWithoutThis, rval);
return Invoke(cx, thisv, ObjectValue(*fun), argc, argvWithoutThis, rval);
}
JSObject *
NewGCThing(JSContext *cx, gc::AllocKind allocKind, size_t thingSize)
{
return gc::NewGCThing<JSObject, CanGC>(cx, allocKind, thingSize, gc::DefaultHeap);
}
bool
CheckOverRecursed(JSContext *cx)
{
// IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
// want to trigger an operation callback, we set the ionStackLimit to NULL,
// which causes the stack limit check to fail.
//
// There are two states we're concerned about here:
// (1) The interrupt bit is set, and we need to fire the interrupt callback.
// (2) The stack limit has been exceeded, and we need to throw an error.
//
// Note that we can reach here if ionStackLimit is MAXADDR, but interrupt
// has not yet been set to 1. That's okay; it will be set to 1 very shortly,
// and in the interim we might just fire a few useless calls to
// CheckOverRecursed.
JS_CHECK_RECURSION(cx, return false);
if (cx->runtime()->interrupt)
return InterruptCheck(cx);
return true;
}
bool
DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
{
// Given the ScopeChain, extract the VarObj.
RootedObject obj(cx, scopeChain);
while (!obj->isVarObj())
obj = obj->enclosingScope();
return DefVarOrConstOperation(cx, obj, dn, attrs);
}
bool
SetConst(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval)
{
// Given the ScopeChain, extract the VarObj.
RootedObject obj(cx, scopeChain);
while (!obj->isVarObj())
obj = obj->enclosingScope();
return SetConstOperation(cx, obj, name, rval);
}
bool
InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value)
{
// Copy the incoming value. This may be overwritten; the return value is discarded.
RootedValue rval(cx, value);
RootedId id(cx, NameToId(name));
if (name == cx->names().proto)
return baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, false);
return DefineNativeProperty(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0);
}
template<bool Equal>
bool
LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool equal;
if (!js::LooselyEqual(cx, lhs, rhs, &equal))
return false;
*res = (equal == Equal);
return true;
}
template bool LooselyEqual<true>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
template bool LooselyEqual<false>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
template<bool Equal>
bool
StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool equal;
if (!js::StrictlyEqual(cx, lhs, rhs, &equal))
return false;
*res = (equal == Equal);
return true;
}
template bool StrictlyEqual<true>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
template bool StrictlyEqual<false>(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res);
bool
LessThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool cond;
if (!LessThanOperation(cx, lhs, rhs, &cond))
return false;
*res = cond;
return true;
}
bool
LessThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool cond;
if (!LessThanOrEqualOperation(cx, lhs, rhs, &cond))
return false;
*res = cond;
return true;
}
bool
GreaterThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool cond;
if (!GreaterThanOperation(cx, lhs, rhs, &cond))
return false;
*res = cond;
return true;
}
bool
GreaterThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
bool cond;
if (!GreaterThanOrEqualOperation(cx, lhs, rhs, &cond))
return false;
*res = cond;
return true;
}
template<bool Equal>
bool
StringsEqual(JSContext *cx, HandleString lhs, HandleString rhs, JSBool *res)
{
bool equal;
if (!js::EqualStrings(cx, lhs, rhs, &equal))
return false;
*res = (equal == Equal);
return true;
}
template bool StringsEqual<true>(JSContext *cx, HandleString lhs, HandleString rhs, JSBool *res);
template bool StringsEqual<false>(JSContext *cx, HandleString lhs, HandleString rhs, JSBool *res);
JSBool
ObjectEmulatesUndefined(JSObject *obj)
{
return EmulatesUndefined(obj);
}
bool
IteratorMore(JSContext *cx, HandleObject obj, JSBool *res)
{
RootedValue tmp(cx);
if (!js_IteratorMore(cx, obj, &tmp))
return false;
*res = tmp.toBoolean();
return true;
}
JSObject *
NewInitParallelArray(JSContext *cx, HandleObject templateObject)
{
JS_ASSERT(templateObject->getClass() == &ParallelArrayObject::class_);
JS_ASSERT(!templateObject->hasSingletonType());
RootedObject obj(cx, ParallelArrayObject::newInstance(cx, TenuredObject));
if (!obj)
return NULL;
obj->setType(templateObject->type());
return obj;
}
JSObject*
NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg)
{
RootedTypeObject type(cx, typeArg);
NewObjectKind newKind = !type ? SingletonObject : GenericObject;
RootedObject obj(cx, NewDenseAllocatedArray(cx, count, NULL, newKind));
if (!obj)
return NULL;
if (!type)
types::TypeScript::Monitor(cx, ObjectValue(*obj));
else
obj->setType(type);
return obj;
}
JSObject*
NewInitObject(JSContext *cx, HandleObject templateObject)
{
NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject;
RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind));
if (!obj)
return NULL;
if (templateObject->hasSingletonType())
types::TypeScript::Monitor(cx, ObjectValue(*obj));
else
obj->setType(templateObject->type());
return obj;
}
JSObject *
NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject)
{
JS_ASSERT(!templateObject->hasSingletonType());
JSObject *obj = NewObjectWithGivenProto(cx,
templateObject->getClass(),
templateObject->getProto(),
cx->global());
if (!obj)
return NULL;
obj->setType(templateObject->type());
return obj;
}
bool
ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval)
{
JS_ASSERT(obj->isArray());
AutoDetectInvalidation adi(cx, rval.address());
Value argv[] = { UndefinedValue(), ObjectValue(*obj) };
AutoValueArray ava(cx, argv, 2);
if (!js::array_pop(cx, 0, argv))
return false;
// If the result is |undefined|, the array was probably empty and we
// have to monitor the return value.
rval.set(argv[0]);
if (rval.isUndefined())
types::TypeScript::Monitor(cx, rval);
return true;
}
bool
ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length)
{
JS_ASSERT(obj->isArray());
Value argv[] = { UndefinedValue(), ObjectValue(*obj), v };
AutoValueArray ava(cx, argv, 3);
if (!js::array_push(cx, 1, argv))
return false;
*length = argv[0].toInt32();
return true;
}
bool
ArrayShiftDense(JSContext *cx, HandleObject obj, MutableHandleValue rval)
{
JS_ASSERT(obj->isArray());
AutoDetectInvalidation adi(cx, rval.address());
Value argv[] = { UndefinedValue(), ObjectValue(*obj) };
AutoValueArray ava(cx, argv, 2);
if (!js::array_shift(cx, 0, argv))
return false;
// If the result is |undefined|, the array was probably empty and we
// have to monitor the return value.
rval.set(argv[0]);
if (rval.isUndefined())
types::TypeScript::Monitor(cx, rval);
return true;
}
JSObject *
ArrayConcatDense(JSContext *cx, HandleObject obj1, HandleObject obj2, HandleObject res)
{
JS_ASSERT(obj1->isArray());
JS_ASSERT(obj2->isArray());
JS_ASSERT_IF(res, res->isArray());
if (res) {
// Fast path if we managed to allocate an object inline.
if (!js::array_concat_dense(cx, obj1, obj2, res))
return NULL;
return res;
}
Value argv[] = { UndefinedValue(), ObjectValue(*obj1), ObjectValue(*obj2) };
AutoValueArray ava(cx, argv, 3);
if (!js::array_concat(cx, 1, argv))
return NULL;
return &argv[0].toObject();
}
bool
CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code)
{
jschar c;
if (!str->getChar(cx, index, &c))
return false;
*code = c;
return true;
}
JSFlatString *
StringFromCharCode(JSContext *cx, int32_t code)
{
jschar c = jschar(code);
if (StaticStrings::hasUnit(c))
return cx->runtime()->staticStrings.getUnit(c);
return js_NewStringCopyN<CanGC>(cx, &c, 1);
}
bool
SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value,
bool strict, int jsop)
{
RootedValue v(cx, value);
RootedId id(cx, NameToId(name));
if (jsop == JSOP_SETALIASEDVAR) {
// Aliased var assigns ignore readonly attributes on the property, as
// required for initializing 'const' closure variables.
Shape *shape = obj->nativeLookup(cx, name);
JS_ASSERT(shape && shape->hasSlot());
JSObject::nativeSetSlotWithType(cx, obj, shape, value);
return true;
}
if (JS_LIKELY(!obj->getOps()->setProperty)) {
unsigned defineHow = (jsop == JSOP_SETNAME || jsop == JSOP_SETGNAME) ? DNP_UNQUALIFIED : 0;
return baseops::SetPropertyHelper(cx, obj, obj, id, defineHow, &v, strict);
}
return JSObject::setGeneric(cx, obj, obj, id, &v, strict);
}
bool
InterruptCheck(JSContext *cx)
{
gc::MaybeVerifyBarriers(cx);
return !!js_HandleExecutionInterrupt(cx);
}
HeapSlot *
NewSlots(JSRuntime *rt, unsigned nslots)
{
JS_STATIC_ASSERT(sizeof(Value) == sizeof(HeapSlot));
Value *slots = reinterpret_cast<Value *>(rt->malloc_(nslots * sizeof(Value)));
if (!slots)
return NULL;
for (unsigned i = 0; i < nslots; i++)
slots[i] = UndefinedValue();
return reinterpret_cast<HeapSlot *>(slots);
}
JSObject *
NewCallObject(JSContext *cx, HandleScript script,
HandleShape shape, HandleTypeObject type, HeapSlot *slots)
{
return CallObject::create(cx, script, shape, type, slots);
}
JSObject *
NewStringObject(JSContext *cx, HandleString str)
{
return StringObject::create(cx, str);
}
bool
SPSEnter(JSContext *cx, HandleScript script)
{
return cx->runtime()->spsProfiler.enter(cx, script, script->function());
}
bool
SPSExit(JSContext *cx, HandleScript script)
{
cx->runtime()->spsProfiler.exit(cx, script, script->function());
return true;
}
bool
OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, JSBool *out)
{
RootedId id(cx);
if (!ValueToId<CanGC>(cx, key, &id))
return false;
RootedObject obj2(cx);
RootedShape prop(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
return false;
*out = !!prop;
return true;
}
bool
OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, JSBool *out)
{
RootedValue key(cx, Int32Value(index));
return OperatorIn(cx, key, obj, out);
}
bool
GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval)
{
if (!cx->global()->getIntrinsicValue(cx, name, rval))
return false;
// This function is called when we try to compile a cold getintrinsic
// op. MCallGetIntrinsicValue has an AliasSet of None for optimization
// purposes, as its side effect is not observable from JS. We are
// guaranteed to bail out after this function, but because of its AliasSet,
// type info will not be reflowed. Manually monitor here.
types::TypeScript::Monitor(cx, rval);
return true;
}
bool
CreateThis(JSContext *cx, HandleObject callee, MutableHandleValue rval)
{
rval.set(MagicValue(JS_IS_CONSTRUCTING));
if (callee->is<JSFunction>()) {
JSFunction *fun = &callee->as<JSFunction>();
if (fun->isInterpreted()) {
JSScript *script = fun->getOrCreateScript(cx);
if (!script || !script->ensureHasTypes(cx))
return false;
JSObject *thisObj = CreateThisForFunction(cx, callee, false);
if (!thisObj)
return false;
rval.set(ObjectValue(*thisObj));
}
}
return true;
}
void
GetDynamicName(JSContext *cx, JSObject *scopeChain, JSString *str, Value *vp)
{
// Lookup a string on the scope chain, returning either the value found or
// undefined through rval. This function is infallible, and cannot GC or
// invalidate.
JSAtom *atom;
if (str->isAtom()) {
atom = &str->asAtom();
} else {
atom = AtomizeString<NoGC>(cx, str);
if (!atom) {
vp->setUndefined();
return;
}
}
if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) {
vp->setUndefined();
return;
}
Shape *shape = NULL;
JSObject *scope = NULL, *pobj = NULL;
if (LookupNameNoGC(cx, atom->asPropertyName(), scopeChain, &scope, &pobj, &shape)) {
if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp)))
return;
}
vp->setUndefined();
}
JSBool
FilterArguments(JSContext *cx, JSString *str)
{
// getChars() is fallible, but cannot GC: it can only allocate a character
// for the flattened string. If this call fails then the calling Ion code
// will bailout, resume in the interpreter and likely fail again when
// trying to flatten the string and unwind the stack.
const jschar *chars = str->getChars(cx);
if (!chars)
return false;
static const jschar arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'};
return !StringHasPattern(chars, str->length(), arguments, mozilla::ArrayLength(arguments));
}
#ifdef JSGC_GENERATIONAL
void
PostWriteBarrier(JSRuntime *rt, JSObject *obj)
{
JS_ASSERT(!IsInsideNursery(rt, obj));
rt->gcStoreBuffer.putWholeCell(obj);
}
#endif
uint32_t
GetIndexFromString(JSString *str)
{
// Masks the return value UINT32_MAX as failure to get the index.
// I.e. it is impossible to distinguish between failing to get the index
// or the actual index UINT32_MAX.
if (!str->isAtom())
return UINT32_MAX;
uint32_t index;
JSAtom *atom = &str->asAtom();
if (!atom->isIndex(&index))
return UINT32_MAX;
return index;
}
bool
DebugPrologue(JSContext *cx, BaselineFrame *frame, JSBool *mustReturn)
{
*mustReturn = false;
JSTrapStatus status = ScriptDebugPrologue(cx, frame);
switch (status) {
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
// The script is going to return immediately, so we have to call the
// debug epilogue handler as well.
JS_ASSERT(frame->hasReturnValue());
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, true);
case JSTRAP_THROW:
case JSTRAP_ERROR:
return false;
default:
JS_NOT_REACHED("Invalid trap status");
}
}
bool
DebugEpilogue(JSContext *cx, BaselineFrame *frame, JSBool ok)
{
// Unwind scope chain to stack depth 0.
UnwindScope(cx, frame, 0);
// If ScriptDebugEpilogue returns |true| we have to return the frame's
// return value. If it returns |false|, the debugger threw an exception.
// In both cases we have to pop debug scopes.
ok = ScriptDebugEpilogue(cx, frame, ok);
if (frame->isNonEvalFunctionFrame()) {
JS_ASSERT_IF(ok, frame->hasReturnValue());
DebugScopes::onPopCall(frame, cx);
} else if (frame->isStrictEvalFrame()) {
JS_ASSERT_IF(frame->hasCallObj(), frame->scopeChain()->as<CallObject>().isForEval());
DebugScopes::onPopStrictEvalScope(frame);
}
// If the frame has a pushed SPS frame, make sure to pop it.
if (frame->hasPushedSPSFrame()) {
cx->runtime()->spsProfiler.exit(cx, frame->script(), frame->maybeFun());
// Unset the pushedSPSFrame flag because DebugEpilogue may get called before
// Probes::exitScript in baseline during exception handling, and we don't
// want to double-pop SPS frames.
frame->unsetPushedSPSFrame();
}
if (!ok) {
// Pop this frame by updating ionTop, so that the exception handling
// code will start at the previous frame.
IonJSFrameLayout *prefix = frame->framePrefix();
EnsureExitFrame(prefix);
cx->mainThread().ionTop = (uint8_t *)prefix;
}
return ok;
}
bool
StrictEvalPrologue(JSContext *cx, BaselineFrame *frame)
{
return frame->strictEvalPrologue(cx);
}
bool
HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame)
{
return frame->heavyweightFunPrologue(cx);
}
bool
NewArgumentsObject(JSContext *cx, BaselineFrame *frame, MutableHandleValue res)
{
ArgumentsObject *obj = ArgumentsObject::createExpected(cx, frame);
if (!obj)
return false;
res.setObject(*obj);
return true;
}
JSObject *
InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
HandleObject res)
{
if (res) {
JS_ASSERT(res->isArray());
JS_ASSERT(!res->getDenseInitializedLength());
JS_ASSERT(res->type() == templateObj->type());
// Fast path: we managed to allocate the array inline; initialize the
// slots.
if (length > 0) {
if (!res->ensureElements(cx, length))
return NULL;
res->setDenseInitializedLength(length);
res->initDenseElements(0, rest, length);
res->setArrayLengthInt32(length);
// Ensure that values in the rest array are represented in the
// type of the array.
for (unsigned i = 0; i < length; i++)
types::AddTypePropertyId(cx, res, JSID_VOID, rest[i]);
}
return res;
}
JSObject *obj = NewDenseCopiedArray(cx, length, rest, NULL);
if (!obj)
return NULL;
obj->setType(templateObj->type());
for (unsigned i = 0; i < length; i++)
types::AddTypePropertyId(cx, obj, JSID_VOID, rest[i]);
return obj;
}
bool
HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, JSBool *mustReturn)
{
*mustReturn = false;
RootedScript script(cx, frame->script());
jsbytecode *pc = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script);
JS_ASSERT(cx->compartment()->debugMode());
JS_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc));
RootedValue rval(cx);
JSTrapStatus status = JSTRAP_CONTINUE;
JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook;
if (hook || script->stepModeEnabled()) {
if (hook)
status = hook(cx, script, pc, rval.address(), cx->runtime()->debugHooks.interruptHookData);
if (status == JSTRAP_CONTINUE && script->stepModeEnabled())
status = Debugger::onSingleStep(cx, &rval);
}
if (status == JSTRAP_CONTINUE && script->hasBreakpointsAt(pc))
status = Debugger::onTrap(cx, &rval);
switch (status) {
case JSTRAP_CONTINUE:
break;
case JSTRAP_ERROR:
return false;
case JSTRAP_RETURN:
*mustReturn = true;
frame->setReturnValue(rval);
return jit::DebugEpilogue(cx, frame, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:
JS_NOT_REACHED("Invalid trap status");
}
return true;
}
bool
OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, JSBool *mustReturn)
{
*mustReturn = false;
RootedScript script(cx, frame->script());
JSTrapStatus status = JSTRAP_CONTINUE;
RootedValue rval(cx);
if (JSDebuggerHandler handler = cx->runtime()->debugHooks.debuggerHandler)
status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.debuggerHandlerData);
if (status == JSTRAP_CONTINUE)
status = Debugger::onDebuggerStatement(cx, &rval);
switch (status) {
case JSTRAP_ERROR:
return false;
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
frame->setReturnValue(rval);
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, true);
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:
JS_NOT_REACHED("Invalid trap status");
}
}
bool
EnterBlock(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
{
return frame->pushBlock(cx, block);
}
bool
LeaveBlock(JSContext *cx, BaselineFrame *frame)
{
frame->popBlock(cx);
return true;
}
bool
InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
{
return frame->initForOsr(interpFrame, numStackValues);
}
} // namespace jit
} // namespace js