summaryrefslogtreecommitdiffstats
path: root/js/src/jit/VMFunctions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/VMFunctions.cpp')
-rw-r--r--js/src/jit/VMFunctions.cpp1361
1 files changed, 1361 insertions, 0 deletions
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
new file mode 100644
index 000000000..628b31fae
--- /dev/null
+++ b/js/src/jit/VMFunctions.cpp
@@ -0,0 +1,1361 @@
+/* -*- 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 "jit/VMFunctions.h"
+
+#include "jsgc.h"
+
+#include "builtin/TypedObject.h"
+#include "frontend/BytecodeCompiler.h"
+#include "jit/arm/Simulator-arm.h"
+#include "jit/BaselineIC.h"
+#include "jit/JitCompartment.h"
+#include "jit/JitFrames.h"
+#include "jit/mips32/Simulator-mips32.h"
+#include "jit/mips64/Simulator-mips64.h"
+#include "vm/ArrayObject.h"
+#include "vm/Debugger.h"
+#include "vm/Interpreter.h"
+#include "vm/TraceLogging.h"
+
+#include "jit/BaselineFrame-inl.h"
+#include "jit/JitFrames-inl.h"
+#include "vm/Debugger-inl.h"
+#include "vm/Interpreter-inl.h"
+#include "vm/NativeObject-inl.h"
+#include "vm/StringObject-inl.h"
+#include "vm/TypeInference-inl.h"
+#include "vm/UnboxedObject-inl.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+// Statics are initialized to null.
+/* static */ VMFunction* VMFunction::functions;
+
+AutoDetectInvalidation::AutoDetectInvalidation(JSContext* cx, MutableHandleValue rval)
+ : cx_(cx),
+ ionScript_(GetTopJitJSScript(cx)->ionScript()),
+ rval_(rval),
+ disabled_(false)
+{ }
+
+void
+VMFunction::addToFunctions()
+{
+ this->next = functions;
+ functions = this;
+}
+
+bool
+InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv,
+ MutableHandleValue rval)
+{
+ TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
+ TraceLogStartEvent(logger, TraceLogger_Call);
+
+ AutoArrayRooter argvRoot(cx, argc + 1 + constructing, argv);
+
+ // Data in the argument vector is arranged for a JIT -> JIT call.
+ RootedValue thisv(cx, argv[0]);
+ Value* argvWithoutThis = argv + 1;
+
+ RootedValue fval(cx, ObjectValue(*obj));
+ if (constructing) {
+ if (!IsConstructor(fval)) {
+ ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
+ return false;
+ }
+
+ ConstructArgs cargs(cx);
+ if (!cargs.init(cx, argc))
+ return false;
+
+ for (uint32_t i = 0; i < argc; i++)
+ cargs[i].set(argvWithoutThis[i]);
+
+ RootedValue newTarget(cx, argvWithoutThis[argc]);
+
+ // If |this| hasn't been created, or is JS_UNINITIALIZED_LEXICAL,
+ // we can use normal construction code without creating an extraneous
+ // object.
+ if (thisv.isMagic()) {
+ MOZ_ASSERT(thisv.whyMagic() == JS_IS_CONSTRUCTING ||
+ thisv.whyMagic() == JS_UNINITIALIZED_LEXICAL);
+
+ RootedObject obj(cx);
+ if (!Construct(cx, fval, cargs, newTarget, &obj))
+ return false;
+
+ rval.setObject(*obj);
+ return true;
+ }
+
+ // Otherwise the default |this| has already been created. We could
+ // almost perform a *call* at this point, but we'd break |new.target|
+ // in the function. So in this one weird case we call a one-off
+ // construction path that *won't* set |this| to JS_IS_CONSTRUCTING.
+ return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget, rval);
+ }
+
+ InvokeArgs args(cx);
+ if (!args.init(cx, argc))
+ return false;
+
+ for (size_t i = 0; i < argc; i++)
+ args[i].set(argvWithoutThis[i]);
+
+ return Call(cx, fval, thisv, args, rval);
+}
+
+bool
+InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
+ uint32_t numFormalArgs, Value* argv, MutableHandleValue rval)
+{
+ MOZ_ASSERT(numFormalArgs > numActualArgs);
+ argv[1 + numActualArgs] = argv[1 + numFormalArgs];
+ return InvokeFunction(cx, obj, true, numActualArgs, argv, rval);
+}
+
+bool
+CheckOverRecursed(JSContext* cx)
+{
+ // We just failed the jitStackLimit check. There are two possible reasons:
+ // - jitStackLimit was the real stack limit and we're over-recursed
+ // - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
+ // and we need to call JSRuntime::handleInterrupt.
+#ifdef JS_SIMULATOR
+ JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
+#else
+ JS_CHECK_RECURSION(cx, return false);
+#endif
+ gc::MaybeVerifyBarriers(cx);
+ return cx->runtime()->handleInterrupt(cx);
+}
+
+// This function can get called in two contexts. In the usual context, it's
+// called with earlyCheck=false, after the env chain has been initialized on
+// a baseline frame. In this case, it's ok to throw an exception, so a failed
+// stack check returns false, and a successful stack check promps a check for
+// an interrupt from the runtime, which may also cause a false return.
+//
+// In the second case, it's called with earlyCheck=true, prior to frame
+// initialization. An exception cannot be thrown in this instance, so instead
+// an error flag is set on the frame and true returned.
+bool
+CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
+ uint32_t extra, uint32_t earlyCheck)
+{
+ MOZ_ASSERT_IF(earlyCheck, !frame->overRecursed());
+
+ // See |CheckOverRecursed| above. This is a variant of that function which
+ // accepts an argument holding the extra stack space needed for the Baseline
+ // frame that's about to be pushed.
+ uint8_t spDummy;
+ uint8_t* checkSp = (&spDummy) - extra;
+ if (earlyCheck) {
+#ifdef JS_SIMULATOR
+ (void)checkSp;
+ JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, frame->setOverRecursed());
+#else
+ JS_CHECK_RECURSION_WITH_SP(cx, checkSp, frame->setOverRecursed());
+#endif
+ return true;
+ }
+
+ // The OVERRECURSED flag may have already been set on the frame by an
+ // early over-recursed check. If so, throw immediately.
+ if (frame->overRecursed())
+ return false;
+
+#ifdef JS_SIMULATOR
+ JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, return false);
+#else
+ JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
+#endif
+
+ gc::MaybeVerifyBarriers(cx);
+ return cx->runtime()->handleInterrupt(cx);
+}
+
+JSObject*
+BindVar(JSContext* cx, HandleObject envChain)
+{
+ JSObject* obj = envChain;
+ while (!obj->isQualifiedVarObj())
+ obj = obj->enclosingEnvironment();
+ MOZ_ASSERT(obj);
+ return obj;
+}
+
+bool
+DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject envChain)
+{
+ // Given the ScopeChain, extract the VarObj.
+ RootedObject obj(cx, BindVar(cx, envChain));
+ return DefVarOperation(cx, obj, dn, attrs);
+}
+
+bool
+DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject envChain)
+{
+ // Find the extensible lexical scope.
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx,
+ &NearestEnclosingExtensibleLexicalEnvironment(envChain));
+
+ // Find the variables object.
+ RootedObject varObj(cx, BindVar(cx, envChain));
+ return DefLexicalOperation(cx, lexicalEnv, varObj, dn, attrs);
+}
+
+bool
+DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs)
+{
+ Rooted<LexicalEnvironmentObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
+ return DefLexicalOperation(cx, globalLexical, cx->global(), dn, attrs);
+}
+
+bool
+MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value)
+{
+ if (!value.isObjectOrNull())
+ return true;
+
+ RootedObject newProto(cx, value.toObjectOrNull());
+ return SetPrototype(cx, obj, newProto);
+}
+
+bool
+InitProp(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
+ jsbytecode* pc)
+{
+ RootedId id(cx, NameToId(name));
+ return InitPropertyOperation(cx, JSOp(*pc), obj, id, value);
+}
+
+template<bool Equal>
+bool
+LooselyEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ if (!js::LooselyEqual(cx, lhs, rhs, res))
+ return false;
+ if (!Equal)
+ *res = !*res;
+ return true;
+}
+
+template bool LooselyEqual<true>(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
+template bool LooselyEqual<false>(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
+
+template<bool Equal>
+bool
+StrictlyEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ if (!js::StrictlyEqual(cx, lhs, rhs, res))
+ return false;
+ if (!Equal)
+ *res = !*res;
+ return true;
+}
+
+template bool StrictlyEqual<true>(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
+template bool StrictlyEqual<false>(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
+
+bool
+LessThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ return LessThanOperation(cx, lhs, rhs, res);
+}
+
+bool
+LessThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ return LessThanOrEqualOperation(cx, lhs, rhs, res);
+}
+
+bool
+GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ return GreaterThanOperation(cx, lhs, rhs, res);
+}
+
+bool
+GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res)
+{
+ return GreaterThanOrEqualOperation(cx, lhs, rhs, res);
+}
+
+template<bool Equal>
+bool
+StringsEqual(JSContext* cx, HandleString lhs, HandleString rhs, bool* res)
+{
+ if (!js::EqualStrings(cx, lhs, rhs, res))
+ return false;
+ if (!Equal)
+ *res = !*res;
+ return true;
+}
+
+template bool StringsEqual<true>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
+template bool StringsEqual<false>(JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
+
+bool
+ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount)
+{
+ JS::AutoValueArray<4> argv(cx);
+ argv[0].setUndefined();
+ argv[1].setObject(*obj);
+ argv[2].set(Int32Value(start));
+ argv[3].set(Int32Value(deleteCount));
+
+ return js::array_splice_impl(cx, 2, argv.begin(), false);
+}
+
+bool
+ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
+{
+ MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+
+ AutoDetectInvalidation adi(cx, rval);
+
+ JS::AutoValueArray<2> argv(cx);
+ argv[0].setUndefined();
+ argv[1].setObject(*obj);
+ if (!js::array_pop(cx, 0, argv.begin()))
+ 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())
+ TypeScript::Monitor(cx, rval);
+ return true;
+}
+
+bool
+ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
+{
+ *length = GetAnyBoxedOrUnboxedArrayLength(obj);
+ DenseElementResult result =
+ SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1,
+ ShouldUpdateTypes::DontUpdate);
+ if (result != DenseElementResult::Incomplete) {
+ (*length)++;
+ return result == DenseElementResult::Success;
+ }
+
+ JS::AutoValueArray<3> argv(cx);
+ argv[0].setUndefined();
+ argv[1].setObject(*obj);
+ argv[2].set(v);
+ if (!js::array_push(cx, 1, argv.begin()))
+ return false;
+
+ *length = argv[0].toInt32();
+ return true;
+}
+
+bool
+ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
+{
+ MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
+
+ AutoDetectInvalidation adi(cx, rval);
+
+ JS::AutoValueArray<2> argv(cx);
+ argv[0].setUndefined();
+ argv[1].setObject(*obj);
+ if (!js::array_shift(cx, 0, argv.begin()))
+ 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())
+ TypeScript::Monitor(cx, rval);
+ return true;
+}
+
+JSString*
+ArrayJoin(JSContext* cx, HandleObject array, HandleString sep)
+{
+ JS::AutoValueArray<3> argv(cx);
+ argv[0].setUndefined();
+ argv[1].setObject(*array);
+ argv[2].setString(sep);
+ if (!js::array_join(cx, 1, argv.begin()))
+ return nullptr;
+ return argv[0].toString();
+}
+
+bool
+CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code)
+{
+ char16_t c;
+ if (!str->getChar(cx, index, &c))
+ return false;
+ *code = c;
+ return true;
+}
+
+JSFlatString*
+StringFromCharCode(JSContext* cx, int32_t code)
+{
+ char16_t c = char16_t(code);
+
+ if (StaticStrings::hasUnit(c))
+ return cx->staticStrings().getUnit(c);
+
+ return NewStringCopyN<CanGC>(cx, &c, 1);
+}
+
+JSString*
+StringFromCodePoint(JSContext* cx, int32_t codePoint)
+{
+ RootedValue rval(cx, Int32Value(codePoint));
+ if (!str_fromCodePoint_one_arg(cx, rval, &rval))
+ return nullptr;
+
+ return rval.toString();
+}
+
+bool
+SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
+ bool strict, jsbytecode* pc)
+{
+ RootedId id(cx, NameToId(name));
+
+ JSOp op = JSOp(*pc);
+
+ if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL) {
+ // Aliased var assigns ignore readonly attributes on the property, as
+ // required for initializing 'const' closure variables.
+ Shape* shape = obj->as<NativeObject>().lookup(cx, name);
+ MOZ_ASSERT(shape && shape->hasSlot());
+ obj->as<NativeObject>().setSlotWithType(cx, shape, value);
+ return true;
+ }
+
+ RootedValue receiver(cx, ObjectValue(*obj));
+ ObjectOpResult result;
+ if (MOZ_LIKELY(!obj->getOpsSetProperty())) {
+ if (!NativeSetProperty(
+ cx, obj.as<NativeObject>(), id, value, receiver,
+ (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
+ op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME)
+ ? Unqualified
+ : Qualified,
+ result))
+ {
+ return false;
+ }
+ } else {
+ if (!SetProperty(cx, obj, id, value, receiver, result))
+ return false;
+ }
+ return result.checkStrictErrorOrWarning(cx, obj, id, strict);
+}
+
+bool
+InterruptCheck(JSContext* cx)
+{
+ gc::MaybeVerifyBarriers(cx);
+
+ {
+ JSRuntime* rt = cx->runtime();
+ JitRuntime::AutoPreventBackedgePatching apbp(rt);
+ rt->jitRuntime()->patchIonBackedges(rt, JitRuntime::BackedgeLoopHeader);
+ }
+
+ return CheckForInterrupt(cx);
+}
+
+void*
+MallocWrapper(JSRuntime* rt, size_t nbytes)
+{
+ return rt->pod_malloc<uint8_t>(nbytes);
+}
+
+JSObject*
+NewCallObject(JSContext* cx, HandleShape shape, HandleObjectGroup group)
+{
+ JSObject* obj = CallObject::create(cx, shape, group);
+ if (!obj)
+ return nullptr;
+
+ // The JIT creates call objects in the nursery, so elides barriers for
+ // the initializing writes. The interpreter, however, may have allocated
+ // the call object tenured, so barrier as needed before re-entering.
+ if (!IsInsideNursery(obj))
+ cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+
+ return obj;
+}
+
+JSObject*
+NewSingletonCallObject(JSContext* cx, HandleShape shape)
+{
+ JSObject* obj = CallObject::createSingleton(cx, shape);
+ if (!obj)
+ return nullptr;
+
+ // The JIT creates call objects in the nursery, so elides barriers for
+ // the initializing writes. The interpreter, however, may have allocated
+ // the call object tenured, so barrier as needed before re-entering.
+ MOZ_ASSERT(!IsInsideNursery(obj),
+ "singletons are created in the tenured heap");
+ cx->runtime()->gc.storeBuffer.putWholeCell(obj);
+
+ return obj;
+}
+
+JSObject*
+NewStringObject(JSContext* cx, HandleString str)
+{
+ return StringObject::create(cx, str);
+}
+
+bool
+OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out)
+{
+ RootedId id(cx);
+ return ToPropertyKey(cx, key, &id) &&
+ HasProperty(cx, obj, id, out);
+}
+
+bool
+OperatorInI(JSContext* cx, uint32_t index, HandleObject obj, bool* out)
+{
+ RootedValue key(cx, Int32Value(index));
+ return OperatorIn(cx, key, obj, out);
+}
+
+bool
+GetIntrinsicValue(JSContext* cx, HandlePropertyName name, MutableHandleValue rval)
+{
+ if (!GlobalObject::getIntrinsicValue(cx, cx->global(), 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.
+ TypeScript::Monitor(cx, rval);
+
+ return true;
+}
+
+bool
+CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval)
+{
+ rval.set(MagicValue(JS_IS_CONSTRUCTING));
+
+ if (callee->is<JSFunction>()) {
+ RootedFunction fun(cx, &callee->as<JSFunction>());
+ if (fun->isInterpreted() && fun->isConstructor()) {
+ JSScript* script = fun->getOrCreateScript(cx);
+ if (!script || !script->ensureHasTypes(cx))
+ return false;
+ if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
+ rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
+ } else {
+ JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject);
+ if (!thisObj)
+ return false;
+ rval.set(ObjectValue(*thisObj));
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+GetDynamicName(JSContext* cx, JSObject* envChain, JSString* str, Value* vp)
+{
+ // Lookup a string on the env 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(cx, str);
+ if (!atom) {
+ vp->setUndefined();
+ return;
+ }
+ }
+
+ if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) {
+ vp->setUndefined();
+ return;
+ }
+
+ Shape* shape = nullptr;
+ JSObject* scope = nullptr;
+ JSObject* pobj = nullptr;
+ if (LookupNameNoGC(cx, atom->asPropertyName(), envChain, &scope, &pobj, &shape)) {
+ if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp)))
+ return;
+ }
+
+ vp->setUndefined();
+}
+
+void
+PostWriteBarrier(JSRuntime* rt, JSObject* obj)
+{
+ MOZ_ASSERT(!IsInsideNursery(obj));
+ rt->gc.storeBuffer.putWholeCell(obj);
+}
+
+static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
+
+void
+PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index)
+{
+ MOZ_ASSERT(!IsInsideNursery(obj));
+ if (obj->is<NativeObject>() &&
+ !obj->as<NativeObject>().isInWholeCellBuffer() &&
+ uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength() &&
+ (obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
+#ifdef JS_GC_ZEAL
+ || rt->hasZealMode(gc::ZealMode::ElementsBarrier)
+#endif
+ ))
+ {
+ rt->gc.storeBuffer.putSlot(&obj->as<NativeObject>(), HeapSlot::Element, index, 1);
+ return;
+ }
+
+ rt->gc.storeBuffer.putWholeCell(obj);
+}
+
+void
+PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
+{
+ MOZ_ASSERT(obj->is<GlobalObject>());
+ if (!obj->compartment()->globalWriteBarriered) {
+ PostWriteBarrier(rt, obj);
+ obj->compartment()->globalWriteBarriered = 1;
+ }
+}
+
+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, jsbytecode* pc, bool* mustReturn)
+{
+ *mustReturn = false;
+
+ switch (Debugger::onEnterFrame(cx, frame)) {
+ 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.
+ MOZ_ASSERT(frame->hasReturnValue());
+ *mustReturn = true;
+ return jit::DebugEpilogue(cx, frame, pc, true);
+
+ case JSTRAP_THROW:
+ case JSTRAP_ERROR:
+ return false;
+
+ default:
+ MOZ_CRASH("bad Debugger::onEnterFrame status");
+ }
+}
+
+bool
+DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+ if (!DebugEpilogue(cx, frame, pc, true)) {
+ // DebugEpilogue popped the frame by updating jitTop, so run the stop event
+ // here before we enter the exception handler.
+ TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
+ TraceLogStopEvent(logger, TraceLogger_Baseline);
+ TraceLogStopEvent(logger, TraceLogger_Scripts);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool ok)
+{
+ // If Debugger::onLeaveFrame 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 = Debugger::onLeaveFrame(cx, frame, pc, ok);
+
+ // Unwind to the outermost environment and set pc to the end of the
+ // script, regardless of error.
+ EnvironmentIter ei(cx, frame, pc);
+ UnwindAllEnvironmentsInFrame(cx, ei);
+ JSScript* script = frame->script();
+ frame->setOverridePc(script->lastPC());
+
+ if (!ok) {
+ // Pop this frame by updating jitTop, so that the exception handling
+ // code will start at the previous frame.
+
+ JitFrameLayout* prefix = frame->framePrefix();
+ EnsureBareExitFrame(cx, prefix);
+ return false;
+ }
+
+ // Clear the override pc. This is not necessary for correctness: the frame
+ // will return immediately, but this simplifies the check we emit in debug
+ // builds after each callVM, to ensure this flag is not set.
+ frame->clearOverridePc();
+ return true;
+}
+
+void
+FrameIsDebuggeeCheck(BaselineFrame* frame)
+{
+ if (frame->script()->isDebuggee())
+ frame->setIsDebuggee();
+}
+
+JSObject*
+CreateGenerator(JSContext* cx, BaselineFrame* frame)
+{
+ return GeneratorObject::create(cx, frame);
+}
+
+bool
+NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* pc,
+ uint32_t stackDepth)
+{
+ MOZ_ASSERT(*pc == JSOP_YIELD);
+
+ // Return value is still on the stack.
+ MOZ_ASSERT(stackDepth >= 1);
+
+ // The expression stack slots are stored on the stack in reverse order, so
+ // we copy them to a Vector and pass a pointer to that instead. We use
+ // stackDepth - 1 because we don't want to include the return value.
+ AutoValueVector exprStack(cx);
+ if (!exprStack.reserve(stackDepth - 1))
+ return false;
+
+ size_t firstSlot = frame->numValueSlots() - stackDepth;
+ for (size_t i = 0; i < stackDepth - 1; i++)
+ exprStack.infallibleAppend(*frame->valueSlot(firstSlot + i));
+
+ MOZ_ASSERT(exprStack.length() == stackDepth - 1);
+
+ return GeneratorObject::normalSuspend(cx, obj, frame, pc, exprStack.begin(), stackDepth - 1);
+}
+
+bool
+FinalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* pc)
+{
+ MOZ_ASSERT(*pc == JSOP_FINALYIELDRVAL);
+
+ if (!GeneratorObject::finalSuspend(cx, obj)) {
+
+ TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
+ TraceLogStopEvent(logger, TraceLogger_Engine);
+ TraceLogStopEvent(logger, TraceLogger_Scripts);
+
+ // Leave this frame and propagate the exception to the caller.
+ return DebugEpilogue(cx, frame, pc, /* ok = */ false);
+ }
+
+ return true;
+}
+
+bool
+InterpretResume(JSContext* cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
+ MutableHandleValue rval)
+{
+ MOZ_ASSERT(obj->is<GeneratorObject>());
+
+ RootedValue selfHostedFun(cx);
+ if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().InterpretGeneratorResume,
+ &selfHostedFun))
+ {
+ return false;
+ }
+
+ MOZ_ASSERT(selfHostedFun.toObject().is<JSFunction>());
+
+ FixedInvokeArgs<3> args(cx);
+
+ args[0].setObject(*obj);
+ args[1].set(val);
+ args[2].setString(kind);
+
+ return Call(cx, selfHostedFun, UndefinedHandleValue, args, rval);
+}
+
+bool
+DebugAfterYield(JSContext* cx, BaselineFrame* frame)
+{
+ // The BaselineFrame has just been constructed by JSOP_RESUME in the
+ // caller. We need to set its debuggee flag as necessary.
+ if (frame->script()->isDebuggee())
+ frame->setIsDebuggee();
+ return true;
+}
+
+bool
+GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handle<GeneratorObject*> genObj,
+ HandleValue arg, uint32_t resumeKind)
+{
+ // Set the frame's pc to the current resume pc, so that frame iterators
+ // work. This function always returns false, so we're guaranteed to enter
+ // the exception handler where we will clear the pc.
+ JSScript* script = frame->script();
+ uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
+ frame->setOverridePc(script->offsetToPC(offset));
+
+ MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame));
+ MOZ_ALWAYS_FALSE(js::GeneratorThrowOrClose(cx, frame, genObj, arg, resumeKind));
+ return false;
+}
+
+bool
+CheckGlobalOrEvalDeclarationConflicts(JSContext* cx, BaselineFrame* frame)
+{
+ RootedScript script(cx, frame->script());
+ RootedObject envChain(cx, frame->environmentChain());
+ RootedObject varObj(cx, BindVar(cx, envChain));
+
+ if (script->isForEval()) {
+ // Strict eval and eval in parameter default expressions have their
+ // own call objects.
+ //
+ // Non-strict eval may introduce 'var' bindings that conflict with
+ // lexical bindings in an enclosing lexical scope.
+ if (!script->bodyScope()->hasEnvironment()) {
+ MOZ_ASSERT(!script->strict() &&
+ (!script->enclosingScope()->is<FunctionScope>() ||
+ !script->enclosingScope()->as<FunctionScope>().hasParameterExprs()));
+ if (!CheckEvalDeclarationConflicts(cx, script, envChain, varObj))
+ return false;
+ }
+ } else {
+ Rooted<LexicalEnvironmentObject*> lexicalEnv(cx,
+ &NearestEnclosingExtensibleLexicalEnvironment(envChain));
+ if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv, varObj))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script)
+{
+ Rooted<LexicalEnvironmentObject*> globalLexical(cx, &cx->global()->lexicalEnvironment());
+ return CheckGlobalDeclarationConflicts(cx, script, globalLexical, cx->global());
+}
+
+bool
+InitFunctionEnvironmentObjects(JSContext* cx, BaselineFrame* frame)
+{
+ return frame->initFunctionEnvironmentObjects(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 objRes)
+{
+ if (objRes) {
+ Rooted<ArrayObject*> arrRes(cx, &objRes->as<ArrayObject>());
+
+ MOZ_ASSERT(!arrRes->getDenseInitializedLength());
+ MOZ_ASSERT(arrRes->group() == templateObj->group());
+
+ // Fast path: we managed to allocate the array inline; initialize the
+ // slots.
+ if (length > 0) {
+ if (!arrRes->ensureElements(cx, length))
+ return nullptr;
+ arrRes->setDenseInitializedLength(length);
+ arrRes->initDenseElements(0, rest, length);
+ arrRes->setLengthInt32(length);
+ }
+ return arrRes;
+ }
+
+ NewObjectKind newKind = templateObj->group()->shouldPreTenure()
+ ? TenuredObject
+ : GenericObject;
+ ArrayObject* arrRes = NewDenseCopiedArray(cx, length, rest, nullptr, newKind);
+ if (arrRes)
+ arrRes->setGroup(templateObj->group());
+ return arrRes;
+}
+
+bool
+HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr, bool* mustReturn)
+{
+ *mustReturn = false;
+
+ RootedScript script(cx, frame->script());
+ jsbytecode* pc = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script);
+
+ MOZ_ASSERT(frame->isDebuggee());
+ MOZ_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc));
+
+ RootedValue rval(cx);
+ JSTrapStatus status = JSTRAP_CONTINUE;
+
+ if (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, pc, true);
+
+ case JSTRAP_THROW:
+ cx->setPendingException(rval);
+ return false;
+
+ default:
+ MOZ_CRASH("Invalid trap status");
+ }
+
+ return true;
+}
+
+bool
+OnDebuggerStatement(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn)
+{
+ *mustReturn = false;
+
+ switch (Debugger::onDebuggerStatement(cx, frame)) {
+ case JSTRAP_ERROR:
+ return false;
+
+ case JSTRAP_CONTINUE:
+ return true;
+
+ case JSTRAP_RETURN:
+ *mustReturn = true;
+ return jit::DebugEpilogue(cx, frame, pc, true);
+
+ case JSTRAP_THROW:
+ return false;
+
+ default:
+ MOZ_CRASH("Invalid trap status");
+ }
+}
+
+bool
+GlobalHasLiveOnDebuggerStatement(JSContext* cx)
+{
+ return cx->compartment()->isDebuggee() &&
+ Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
+}
+
+bool
+PushLexicalEnv(JSContext* cx, BaselineFrame* frame, Handle<LexicalScope*> scope)
+{
+ return frame->pushLexicalEnvironment(cx, scope);
+}
+
+bool
+PopLexicalEnv(JSContext* cx, BaselineFrame* frame)
+{
+ frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
+ return true;
+}
+
+bool
+DebugLeaveThenPopLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+ MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
+ frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
+ return true;
+}
+
+bool
+FreshenLexicalEnv(JSContext* cx, BaselineFrame* frame)
+{
+ return frame->freshenLexicalEnvironment(cx);
+}
+
+bool
+DebugLeaveThenFreshenLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+ MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
+ return frame->freshenLexicalEnvironment(cx);
+}
+
+bool
+RecreateLexicalEnv(JSContext* cx, BaselineFrame* frame)
+{
+ return frame->recreateLexicalEnvironment(cx);
+}
+
+bool
+DebugLeaveThenRecreateLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+ MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
+ return frame->recreateLexicalEnvironment(cx);
+}
+
+bool
+DebugLeaveLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+ MOZ_ASSERT(frame->script()->baselineScript()->hasDebugInstrumentation());
+ if (cx->compartment()->isDebuggee())
+ DebugEnvironments::onPopLexical(cx, frame, pc);
+ return true;
+}
+
+bool
+PushVarEnv(JSContext* cx, BaselineFrame* frame, HandleScope scope)
+{
+ return frame->pushVarEnvironment(cx, scope);
+}
+
+bool
+PopVarEnv(JSContext* cx, BaselineFrame* frame)
+{
+ frame->popOffEnvironmentChain<VarEnvironmentObject>();
+ return true;
+}
+
+bool
+EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val, Handle<WithScope*> templ)
+{
+ return EnterWithOperation(cx, frame, val, templ);
+}
+
+bool
+LeaveWith(JSContext* cx, BaselineFrame* frame)
+{
+ if (MOZ_UNLIKELY(frame->isDebuggee()))
+ DebugEnvironments::onPopWith(frame);
+ frame->popOffEnvironmentChain<WithEnvironmentObject>();
+ return true;
+}
+
+bool
+InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
+ uint32_t numStackValues)
+{
+ return frame->initForOsr(interpFrame, numStackValues);
+}
+
+JSObject*
+CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
+ HandleObject owner, int32_t offset)
+{
+ MOZ_ASSERT(descr->is<TypeDescr>());
+ MOZ_ASSERT(owner->is<TypedObject>());
+ Rooted<TypeDescr*> descr1(cx, &descr->as<TypeDescr>());
+ Rooted<TypedObject*> owner1(cx, &owner->as<TypedObject>());
+ return OutlineTypedObject::createDerived(cx, descr1, owner1, offset);
+}
+
+JSString*
+StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl)
+{
+ MOZ_ASSERT(string);
+ MOZ_ASSERT(pattern);
+ MOZ_ASSERT(repl);
+
+ return str_replace_string_raw(cx, string, pattern, repl);
+}
+
+bool
+RecompileImpl(JSContext* cx, bool force)
+{
+ MOZ_ASSERT(cx->currentlyRunningInJit());
+ JitActivationIterator activations(cx->runtime());
+ JitFrameIterator iter(activations);
+
+ MOZ_ASSERT(iter.type() == JitFrame_Exit);
+ ++iter;
+
+ RootedScript script(cx, iter.script());
+ MOZ_ASSERT(script->hasIonScript());
+
+ if (!IsIonEnabled(cx))
+ return true;
+
+ MethodStatus status = Recompile(cx, script, nullptr, nullptr, force);
+ if (status == Method_Error)
+ return false;
+
+ return true;
+}
+
+bool
+ForcedRecompile(JSContext* cx)
+{
+ return RecompileImpl(cx, /* force = */ true);
+}
+
+bool
+Recompile(JSContext* cx)
+{
+ return RecompileImpl(cx, /* force = */ false);
+}
+
+bool
+SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
+ HandleValue value, bool strict)
+{
+ // This function is called from Ion code for StoreElementHole's OOL path.
+ // In this case we know the object is native or an unboxed array and that
+ // no type changes are needed.
+
+ DenseElementResult result =
+ SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1,
+ ShouldUpdateTypes::DontUpdate);
+ if (result != DenseElementResult::Incomplete)
+ return result == DenseElementResult::Success;
+
+ RootedValue indexVal(cx, Int32Value(index));
+ return SetObjectElement(cx, obj, indexVal, value, strict);
+}
+
+void
+AutoDetectInvalidation::setReturnOverride()
+{
+ cx_->runtime()->jitRuntime()->setIonReturnOverride(rval_.get());
+}
+
+void
+AssertValidObjectPtr(JSContext* cx, JSObject* obj)
+{
+#ifdef DEBUG
+ // Check what we can, so that we'll hopefully assert/crash if we get a
+ // bogus object (pointer).
+ MOZ_ASSERT(obj->compartment() == cx->compartment());
+ MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
+
+ MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
+ obj->group()->clasp() == obj->maybeShape()->getObjectClass());
+
+ if (obj->isTenured()) {
+ MOZ_ASSERT(obj->isAligned());
+ gc::AllocKind kind = obj->asTenured().getAllocKind();
+ MOZ_ASSERT(gc::IsObjectAllocKind(kind));
+ MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
+ }
+#endif
+}
+
+void
+AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj)
+{
+ if (obj)
+ AssertValidObjectPtr(cx, obj);
+}
+
+void
+AssertValidStringPtr(JSContext* cx, JSString* str)
+{
+#ifdef DEBUG
+ // We can't closely inspect strings from another runtime.
+ if (str->runtimeFromAnyThread() != cx->runtime()) {
+ MOZ_ASSERT(str->isPermanentAtom());
+ return;
+ }
+
+ if (str->isAtom())
+ MOZ_ASSERT(str->zone()->isAtomsZone());
+ else
+ MOZ_ASSERT(str->zone() == cx->zone());
+
+ MOZ_ASSERT(str->isAligned());
+ MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
+
+ gc::AllocKind kind = str->getAllocKind();
+ if (str->isFatInline()) {
+ MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING ||
+ kind == gc::AllocKind::FAT_INLINE_ATOM);
+ } else if (str->isExternal()) {
+ MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
+ } else if (str->isAtom()) {
+ MOZ_ASSERT(kind == gc::AllocKind::ATOM);
+ } else if (str->isFlat()) {
+ MOZ_ASSERT(kind == gc::AllocKind::STRING ||
+ kind == gc::AllocKind::FAT_INLINE_STRING ||
+ kind == gc::AllocKind::EXTERNAL_STRING);
+ } else {
+ MOZ_ASSERT(kind == gc::AllocKind::STRING);
+ }
+#endif
+}
+
+void
+AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym)
+{
+ // We can't closely inspect symbols from another runtime.
+ if (sym->runtimeFromAnyThread() != cx->runtime()) {
+ MOZ_ASSERT(sym->isWellKnownSymbol());
+ return;
+ }
+
+ MOZ_ASSERT(sym->zone()->isAtomsZone());
+ MOZ_ASSERT(sym->isAligned());
+ if (JSString* desc = sym->description()) {
+ MOZ_ASSERT(desc->isAtom());
+ AssertValidStringPtr(cx, desc);
+ }
+
+ MOZ_ASSERT(sym->getAllocKind() == gc::AllocKind::SYMBOL);
+}
+
+void
+AssertValidValue(JSContext* cx, Value* v)
+{
+ if (v->isObject())
+ AssertValidObjectPtr(cx, &v->toObject());
+ else if (v->isString())
+ AssertValidStringPtr(cx, v->toString());
+ else if (v->isSymbol())
+ AssertValidSymbolPtr(cx, v->toSymbol());
+}
+
+bool
+ObjectIsCallable(JSObject* obj)
+{
+ return obj->isCallable();
+}
+
+bool
+ObjectIsConstructor(JSObject* obj)
+{
+ return obj->isConstructor();
+}
+
+void
+MarkValueFromIon(JSRuntime* rt, Value* vp)
+{
+ TraceManuallyBarrieredEdge(&rt->gc.marker, vp, "write barrier");
+}
+
+void
+MarkStringFromIon(JSRuntime* rt, JSString** stringp)
+{
+ if (*stringp)
+ TraceManuallyBarrieredEdge(&rt->gc.marker, stringp, "write barrier");
+}
+
+void
+MarkObjectFromIon(JSRuntime* rt, JSObject** objp)
+{
+ if (*objp)
+ TraceManuallyBarrieredEdge(&rt->gc.marker, objp, "write barrier");
+}
+
+void
+MarkShapeFromIon(JSRuntime* rt, Shape** shapep)
+{
+ TraceManuallyBarrieredEdge(&rt->gc.marker, shapep, "write barrier");
+}
+
+void
+MarkObjectGroupFromIon(JSRuntime* rt, ObjectGroup** groupp)
+{
+ TraceManuallyBarrieredEdge(&rt->gc.marker, groupp, "write barrier");
+}
+
+bool
+ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber)
+{
+ ScriptFrameIter iter(cx);
+ RootedScript script(cx, iter.script());
+ ReportRuntimeLexicalError(cx, errorNumber, script, iter.pc());
+ return false;
+}
+
+bool
+ThrowReadOnlyError(JSContext* cx, int32_t index)
+{
+ RootedValue val(cx, Int32Value(index));
+ ReportValueError(cx, JSMSG_READ_ONLY, JSDVG_IGNORE_STACK, val, nullptr);
+ return false;
+}
+
+bool
+ThrowBadDerivedReturn(JSContext* cx, HandleValue v)
+{
+ ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v, nullptr);
+ return false;
+}
+
+bool
+BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame)
+{
+ return ThrowUninitializedThis(cx, frame);
+}
+
+
+bool
+ThrowObjectCoercible(JSContext* cx, HandleValue v)
+{
+ MOZ_ASSERT(v.isUndefined() || v.isNull());
+ MOZ_ALWAYS_FALSE(ToObjectSlow(cx, v, true));
+ return false;
+}
+
+bool
+BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res)
+{
+ return GetFunctionThis(cx, frame, res);
+}
+
+} // namespace jit
+} // namespace js