text: /* -*- 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 "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 {
  
  // Don't explicitly initialize, it's not guaranteed that this initializer will
  // run before the constructors for static VMFunctions.
  /* static */ VMFunction* VMFunction::functions;
  
  AutoDetectInvalidation::AutoDetectInvalidation(JSContext* cx, MutableHandleValue rval)
    : cx_(cx),
      ionScript_(GetTopJitJSScript(cx)->ionScript()),
      rval_(rval),
      disabled_(false)
  { }
  
  void
  VMFunction::addToFunctions()
  {
      static bool initialized = false;
      if (!initialized) {
          initialized = true;
          functions = nullptr;
      }
      this->next = functions;
      functions = this;
  }
  
  bool
  InvokeFunction(JSContext* cx, HandleObject obj, bool constructing, uint32_t argc, Value* argv,
                 MutableHandleValue rval)
  {
      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(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, we can use normal construction code.
          if (thisv.isMagic(JS_IS_CONSTRUCTING))
              return Construct(cx, fval, cargs, newTarget, rval);
  
          // 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);
      }
  
      return Invoke(cx, thisv, fval, argc, argvWithoutThis, 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 ealyCheck=false, after the scope 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);
  }
  
  bool
  DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
  {
      // Given the ScopeChain, extract the VarObj.
      RootedObject obj(cx, scopeChain);
      while (!obj->isQualifiedVarObj())
          obj = obj->enclosingScope();
  
      return DefVarOperation(cx, obj, dn, attrs);
  }
  
  bool
  DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
  {
      // Find the extensible lexical scope.
      Rooted<ClonedBlockObject*> lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain));
  
      // Find the variables object.
      RootedObject varObj(cx, scopeChain);
      while (!varObj->isQualifiedVarObj())
          varObj = varObj->enclosingScope();
  
      return DefLexicalOperation(cx, lexical, varObj, dn, attrs);
  }
  
  bool
  DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs)
  {
      Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
      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;
  }
  
  JSObject*
  ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes)
  {
      if (objRes) {
          // Fast path if we managed to allocate an object inline.
          if (!js::array_concat_dense(cx, obj1, obj2, objRes))
              return nullptr;
          return objRes;
      }
  
      JS::AutoValueArray<3> argv(cx);
      argv[0].setUndefined();
      argv[1].setObject(*obj1);
      argv[2].setObject(*obj2);
      if (!js::array_concat(cx, 1, argv.begin()))
          return nullptr;
      return &argv[0].toObject();
  }
  
  JSString*
  ArrayJoin(JSContext* cx, HandleObject array, HandleString sep)
  {
      // The annotations in this function follow the first steps of join
      // specified in ES5.
  
      // Step 1
      RootedObject obj(cx, array);
      if (!obj)
          return nullptr;
  
      AutoCycleDetector detector(cx, obj);
      if (!detector.init())
          return nullptr;
  
      if (detector.foundCycle())
          return nullptr;
  
      // Steps 2 and 3
      uint32_t length;
      if (!GetLengthProperty(cx, obj, &length))
          return nullptr;
  
      // Steps 4 and 5
      RootedLinearString sepstr(cx);
      if (sep) {
          sepstr = sep->ensureLinear(cx);
          if (!sepstr)
              return nullptr;
      } else {
          sepstr = cx->names().comma;
      }
  
      // Step 6 to 11
      return js::ArrayJoin<false>(cx, obj, sepstr, length);
  }
  
  
  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);
  }
  
  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->getOps()->setProperty)) {
          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);
  
      {
          JitRuntime* jrt = cx->runtime()->jitRuntime();
          JitRuntime::AutoMutateBackedges amb(jrt);
          jrt->patchIonBackedges(cx->runtime(), 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, uint32_t lexicalBegin)
  {
      JSObject* obj = CallObject::create(cx, shape, group, lexicalBegin);
      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, uint32_t lexicalBegin)
  {
      JSObject* obj = CallObject::createSingleton(cx, shape, lexicalBegin);
      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>()) {
          JSFunction* fun = &callee->as<JSFunction>();
          if (fun->isInterpreted() && fun->isConstructor()) {
              JSScript* script = fun->getOrCreateScript(cx);
              if (!script || !script->ensureHasTypes(cx))
                  return false;
              if (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* 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(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(), scopeChain, &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);
  }
  
  void
  PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj)
  {
      MOZ_ASSERT(obj->is<GlobalObject>());
      if (!obj->compartment()->globalWriteBarriered) {
          PostWriteBarrier(rt, obj);
          obj->compartment()->globalWriteBarriered = true;
      }
  }
  
  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, ok);
  
      // Unwind to the outermost scope and set pc to the end of the script,
      // regardless of error.
      ScopeIter si(cx, frame, pc);
      UnwindAllScopesInFrame(cx, si);
      JSScript* script = frame->script();
      frame->setOverridePc(script->lastPC());
  
      if (frame->isNonEvalFunctionFrame()) {
          MOZ_ASSERT_IF(ok, frame->hasReturnValue());
          DebugScopes::onPopCall(frame, cx);
      } else if (frame->isStrictEvalFrame()) {
          MOZ_ASSERT_IF(frame->hasCallObj(), frame->scopeChain()->as<CallObject>().isForEval());
          DebugScopes::onPopStrictEvalScope(frame);
      }
  
      if (!ok) {
          // Pop this frame by updating jitTop, so that the exception handling
          // code will start at the previous frame.
  
          JitFrameLayout* prefix = frame->framePrefix();
          EnsureExitFrame(prefix);
          cx->runtime()->jitTop = (uint8_t*)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)) {
          // 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>());
  
      InvokeArgs args(cx);
      if (!args.init(3))
          return false;
  
      args.setCallee(selfHostedFun);
      args.setThis(UndefinedValue());
  
      args[0].setObject(*obj);
      args[1].set(val);
      args[2].setString(kind);
  
      if (!Invoke(cx, args))
          return false;
  
      rval.set(args.rval());
      return true;
  }
  
  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
  InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
  {
      RootedScript script(cx, frame->script());
      RootedObject varObj(cx, frame->scopeChain());
      while (!varObj->isQualifiedVarObj())
          varObj = varObj->enclosingScope();
  
      if (script->isForEval()) {
          // Strict eval needs its own call object.
          //
          // Non-strict eval may introduce 'var' bindings that conflict with
          // lexical bindings in an enclosing lexical scope.
          if (script->strict()) {
              if (!frame->initStrictEvalScopeObjects(cx))
                  return false;
          } else {
              RootedObject scopeChain(cx, frame->scopeChain());
              if (!CheckEvalDeclarationConflicts(cx, script, scopeChain, varObj))
                  return false;
          }
      } else {
          Rooted<ClonedBlockObject*> lexicalScope(cx,
              &NearestEnclosingExtensibleLexicalScope(frame->scopeChain()));
          if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj))
              return false;
      }
  
      return true;
  }
  
  bool
  GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script)
  {
      Rooted<ClonedBlockObject*> lexicalScope(cx, &cx->global()->lexicalScope());
      return CheckGlobalDeclarationConflicts(cx, script, lexicalScope, cx->global());
  }
  
  bool
  InitFunctionScopeObjects(JSContext* cx, BaselineFrame* frame)
  {
      return frame->initFunctionScopeObjects(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
  PushBlockScope(JSContext* cx, BaselineFrame* frame, Handle<StaticBlockObject*> block)
  {
      return frame->pushBlock(cx, block);
  }
  
  bool
  PopBlockScope(JSContext* cx, BaselineFrame* frame)
  {
      frame->popBlock(cx);
      return true;
  }
  
  bool
  DebugLeaveThenPopBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
  {
      MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc));
      frame->popBlock(cx);
      return true;
  }
  
  bool
  FreshenBlockScope(JSContext* cx, BaselineFrame* frame)
  {
      return frame->freshenBlock(cx);
  }
  
  bool
  DebugLeaveThenFreshenBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
  {
      MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc));
      return frame->freshenBlock(cx);
  }
  
  bool
  DebugLeaveBlock(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
  {
      MOZ_ASSERT(frame->script()->baselineScript()->hasDebugInstrumentation());
      if (cx->compartment()->isDebuggee())
          DebugScopes::onPopBlock(cx, frame, pc);
      return true;
  }
  
  bool
  EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val, Handle<StaticWithObject*> templ)
  {
      return EnterWithOperation(cx, frame, val, templ);
  }
  
  bool
  LeaveWith(JSContext* cx, BaselineFrame* frame)
  {
      frame->popWith(cx);
      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*
  RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp, HandleString repl)
  {
      MOZ_ASSERT(string);
      MOZ_ASSERT(repl);
  
      return str_replace_regexp_raw(cx, string, regexp.as<RegExpObject>(), repl);
  }
  
  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;
  
      bool isConstructing = iter.isConstructing();
      RootedScript script(cx, iter.script());
      MOZ_ASSERT(script->hasIonScript());
  
      if (!IsIonEnabled(cx))
          return true;
  
      MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing, 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);
      else if (str->isExternal())
          MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
      else if (str->isAtom() || str->isFlat())
          MOZ_ASSERT(kind == gc::AllocKind::STRING || kind == gc::AllocKind::FAT_INLINE_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();
  }
  
  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
  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, false));
      return false;
  }
  
  bool
  BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res)
  {
      return GetFunctionThis(cx, frame, res);
  }
  
  } // namespace jit
  } // namespace js
  