tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WasmPI.cpp (51583B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 *
      4 * Copyright 2016 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #include "wasm/WasmPI.h"
     20 
     21 #include "builtin/Promise.h"
     22 #include "debugger/DebugAPI.h"
     23 #include "debugger/Debugger.h"
     24 #include "jit/MIRGenerator.h"
     25 #include "js/CallAndConstruct.h"
     26 #include "js/Printf.h"
     27 #include "vm/Iteration.h"
     28 #include "vm/JSContext.h"
     29 #include "vm/JSObject.h"
     30 #include "vm/NativeObject.h"
     31 #include "vm/PromiseObject.h"
     32 #include "wasm/WasmConstants.h"
     33 #include "wasm/WasmContext.h"
     34 #include "wasm/WasmFeatures.h"
     35 #include "wasm/WasmGenerator.h"
     36 #include "wasm/WasmIonCompile.h"  // IonPlatformSupport
     37 #include "wasm/WasmValidate.h"
     38 
     39 #include "vm/JSObject-inl.h"
     40 #include "wasm/WasmGcObject-inl.h"
     41 #include "wasm/WasmInstance-inl.h"
     42 
     43 using namespace js;
     44 using namespace js::jit;
     45 
     46 #ifdef ENABLE_WASM_JSPI
     47 namespace js::wasm {
     48 
     49 void SuspenderObject::releaseStackMemory() {
     50  void* memory = stackMemory();
     51  MOZ_ASSERT(isMoribund() == !memory);
     52  if (memory) {
     53    js_free(memory);
     54    setStackMemory(nullptr);
     55    setState(SuspenderState::Moribund);
     56  }
     57 }
     58 
     59 // Slots that used in various JSFunctionExtended below.
     60 const size_t SUSPENDER_SLOT = 0;
     61 const size_t WRAPPED_FN_SLOT = 1;
     62 const size_t CONTINUE_ON_SUSPENDABLE_SLOT = 1;
     63 const size_t PROMISE_SLOT = 2;
     64 
     65 static JitActivation* FindSuspendableStackActivation(
     66    JSTracer* trc, SuspenderObject* suspender) {
     67  // The jitActivation.refNoCheck() can be used since during trace/marking
     68  // the main thread will be paused.
     69  JitActivation* activation =
     70      trc->runtime()->mainContextFromAnyThread()->jitActivation.refNoCheck();
     71  while (activation) {
     72    // Skip activations without Wasm exit FP -- they are mostly debugger
     73    // related.
     74    if (activation->hasWasmExitFP()) {
     75      // Scan all JitActivations to find one that starts with suspended stack
     76      // frame pointer.
     77      WasmFrameIter iter(activation);
     78      if (!iter.done() && suspender->hasStackAddress(iter.frame())) {
     79        return activation;
     80      }
     81    }
     82    activation = activation->prevJitActivation();
     83  }
     84  MOZ_CRASH("Suspendable stack activation not found");
     85 }
     86 
     87 void TraceSuspendableStack(JSTracer* trc, SuspenderObject* suspender) {
     88  MOZ_ASSERT(suspender->isTraceable());
     89  void* exitFP = suspender->suspendableExitFP();
     90 
     91  // Create and iterator for wasm frames:
     92  //  - If a stack entry for suspended stack exists, the
     93  //  suspender->suspendableFP()
     94  //    and suspender->suspendedReturnAddress() provide start of the frames.
     95  //  - Otherwise, the stack is the part of the main stack, the context
     96  //    JitActivation frames will be used to trace.
     97  WasmFrameIter iter =
     98      suspender->isSuspended()
     99          ? WasmFrameIter(
    100                static_cast<FrameWithInstances*>(suspender->suspendableFP()),
    101                suspender->suspendedReturnAddress())
    102          : WasmFrameIter(FindSuspendableStackActivation(trc, suspender));
    103  MOZ_ASSERT_IF(suspender->isSuspended(), iter.currentFrameStackSwitched());
    104  uintptr_t highestByteVisitedInPrevWasmFrame = 0;
    105  while (true) {
    106    MOZ_ASSERT(!iter.done());
    107    uint8_t* nextPC = iter.resumePCinCurrentFrame();
    108    Instance* instance = iter.instance();
    109    TraceInstanceEdge(trc, instance, "WasmFrameIter instance");
    110    highestByteVisitedInPrevWasmFrame = instance->traceFrame(
    111        trc, iter, nextPC, highestByteVisitedInPrevWasmFrame);
    112    if (iter.frame() == exitFP) {
    113      break;
    114    }
    115    ++iter;
    116    if (iter.currentFrameStackSwitched()) {
    117      highestByteVisitedInPrevWasmFrame = 0;
    118    }
    119  }
    120 }
    121 
    122 static_assert(JS_STACK_GROWTH_DIRECTION < 0,
    123              "JS-PI implemented only for native stacks that grows towards 0");
    124 
    125 SuspenderObject* SuspenderObject::create(JSContext* cx) {
    126  if (cx->wasm().suspenders_.count() >= SuspendableStacksMaxCount) {
    127    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    128                              JSMSG_JSPI_SUSPENDER_LIMIT);
    129    return nullptr;
    130  }
    131 
    132  Rooted<SuspenderObject*> suspender(
    133      cx, NewBuiltinClassInstance<SuspenderObject>(cx));
    134  if (!suspender) {
    135    return nullptr;
    136  }
    137 
    138  // Initialize all of the slots
    139  suspender->initFixedSlot(StateSlot, Int32Value(SuspenderState::Moribund));
    140  suspender->initFixedSlot(PromisingPromiseSlot, NullValue());
    141  suspender->initFixedSlot(SuspendingReturnTypeSlot,
    142                           Int32Value(int32_t(ReturnType::Unknown)));
    143  suspender->initFixedSlot(StackMemorySlot, PrivateValue(nullptr));
    144  suspender->initFixedSlot(MainFPSlot, PrivateValue(nullptr));
    145  suspender->initFixedSlot(MainSPSlot, PrivateValue(nullptr));
    146  suspender->initFixedSlot(SuspendableFPSlot, PrivateValue(nullptr));
    147  suspender->initFixedSlot(SuspendableSPSlot, PrivateValue(nullptr));
    148  suspender->initFixedSlot(SuspendableExitFPSlot, PrivateValue(nullptr));
    149  suspender->initFixedSlot(SuspendedRASlot, PrivateValue(nullptr));
    150  suspender->initFixedSlot(MainExitFPSlot, PrivateValue(nullptr));
    151 
    152  void* stackMemory = js_malloc(SuspendableStackPlusRedZoneSize);
    153  if (!stackMemory) {
    154    ReportOutOfMemory(cx);
    155    return nullptr;
    156  }
    157 
    158  if (!cx->wasm().suspenders_.putNew(suspender)) {
    159    js_free(stackMemory);
    160    ReportOutOfMemory(cx);
    161    return nullptr;
    162  }
    163 
    164  // We are now fully constructed and can transition states
    165  suspender->setStackMemory(stackMemory);
    166  suspender->setFixedSlot(SuspendableSPSlot,
    167                          PrivateValue(static_cast<uint8_t*>(stackMemory) +
    168                                       SuspendableStackPlusRedZoneSize));
    169  suspender->setState(SuspenderState::Initial);
    170 
    171  return suspender;
    172 }
    173 
    174 const JSClass SuspenderObject::class_ = {
    175    "SuspenderObject",
    176    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_FOREGROUND_FINALIZE,
    177    &SuspenderObject::classOps_,
    178    nullptr,
    179    &SuspenderObject::classExt_,
    180 };
    181 
    182 const JSClassOps SuspenderObject::classOps_ = {
    183    nullptr,   // addProperty
    184    nullptr,   // delProperty
    185    nullptr,   // enumerate
    186    nullptr,   // newEnumerate
    187    nullptr,   // resolve
    188    nullptr,   // mayResolve
    189    finalize,  // finalize
    190    nullptr,   // call
    191    nullptr,   // construct
    192    trace,     // trace
    193 };
    194 
    195 const ClassExtension SuspenderObject::classExt_ = {
    196    .objectMovedOp = SuspenderObject::moved,
    197 };
    198 
    199 /* static */
    200 void SuspenderObject::finalize(JS::GCContext* gcx, JSObject* obj) {
    201  SuspenderObject& suspender = obj->as<SuspenderObject>();
    202  if (!suspender.isMoribund()) {
    203    gcx->runtime()->mainContextFromOwnThread()->wasm().suspenders_.remove(
    204        &suspender);
    205  }
    206  suspender.releaseStackMemory();
    207  MOZ_ASSERT(suspender.isMoribund());
    208 }
    209 
    210 /* static */
    211 void SuspenderObject::trace(JSTracer* trc, JSObject* obj) {
    212  SuspenderObject& suspender = obj->as<SuspenderObject>();
    213  // The SuspenderObject refers stacks frames that need to be traced
    214  // only during major GC to determine if SuspenderObject content is
    215  // reachable from JS.
    216  if (!suspender.isTraceable() || trc->isTenuringTracer()) {
    217    return;
    218  }
    219  TraceSuspendableStack(trc, &suspender);
    220 }
    221 
    222 /* static */
    223 size_t SuspenderObject::moved(JSObject* obj, JSObject* old) {
    224  wasm::Context& context =
    225      obj->runtimeFromMainThread()->mainContextFromOwnThread()->wasm();
    226  context.suspenders_.rekeyIfMoved(&old->as<SuspenderObject>(),
    227                                   &obj->as<SuspenderObject>());
    228  return 0;
    229 }
    230 
    231 void SuspenderObject::setMoribund(JSContext* cx) {
    232  MOZ_ASSERT(state() == SuspenderState::Active);
    233  cx->wasm().leaveSuspendableStack(cx);
    234  if (!this->isMoribund()) {
    235    cx->wasm().suspenders_.remove(this);
    236  }
    237  this->releaseStackMemory();
    238  MOZ_ASSERT(this->isMoribund());
    239 }
    240 
    241 void SuspenderObject::setActive(JSContext* cx) {
    242  this->setState(SuspenderState::Active);
    243  cx->wasm().enterSuspendableStack(cx, this);
    244 }
    245 
    246 void SuspenderObject::setSuspended(JSContext* cx) {
    247  this->setState(SuspenderState::Suspended);
    248  cx->wasm().leaveSuspendableStack(cx);
    249 }
    250 
    251 void SuspenderObject::enter(JSContext* cx) {
    252  // We can enter a suspender normally from Initial, or through unwinding when
    253  // are in the 'CalledOnMain' or 'Suspended' states.
    254  MOZ_ASSERT(state() == SuspenderState::Initial ||
    255             state() == SuspenderState::CalledOnMain ||
    256             state() == SuspenderState::Suspended);
    257  setActive(cx);
    258 }
    259 
    260 void SuspenderObject::suspend(JSContext* cx) {
    261  MOZ_ASSERT(state() == SuspenderState::Active);
    262  setSuspended(cx);
    263 
    264  if (cx->realm()->isDebuggee()) {
    265    WasmFrameIter iter(cx->activation()->asJit());
    266    while (true) {
    267      MOZ_ASSERT(!iter.done());
    268      if (iter.debugEnabled()) {
    269        DebugAPI::onSuspendWasmFrame(cx, iter.debugFrame());
    270      }
    271      ++iter;
    272      if (iter.currentFrameStackSwitched()) {
    273        break;
    274      }
    275    }
    276  }
    277 }
    278 
    279 void SuspenderObject::resume(JSContext* cx) {
    280  MOZ_ASSERT(state() == SuspenderState::Suspended);
    281  setActive(cx);
    282  // Use barrier because object is being removed from the suspendable stack
    283  // from roots.
    284  gc::PreWriteBarrier(this);
    285 
    286  if (cx->realm()->isDebuggee()) {
    287    for (FrameIter iter(cx);; ++iter) {
    288      MOZ_RELEASE_ASSERT(!iter.done(), "expecting stackSwitched()");
    289      if (iter.isWasm()) {
    290        WasmFrameIter& wasmIter = iter.wasmFrame();
    291        if (wasmIter.currentFrameStackSwitched()) {
    292          break;
    293        }
    294        if (wasmIter.debugEnabled()) {
    295          DebugAPI::onResumeWasmFrame(cx, iter);
    296        }
    297      }
    298    }
    299  }
    300 }
    301 
    302 void SuspenderObject::leave(JSContext* cx) {
    303  // We are exiting suspended stack if state is active,
    304  // otherwise the stack was just suspended.
    305  switch (state()) {
    306    case SuspenderState::Active: {
    307      setMoribund(cx);
    308      break;
    309    }
    310    case SuspenderState::Suspended: {
    311      MOZ_ASSERT(!cx->wasm().onSuspendableStack());
    312      break;
    313    }
    314    case SuspenderState::Initial:
    315    case SuspenderState::Moribund:
    316    case SuspenderState::CalledOnMain:
    317      MOZ_CRASH();
    318  }
    319 }
    320 
    321 void SuspenderObject::unwind(JSContext* cx) {
    322  switch (state()) {
    323    case SuspenderState::Suspended:
    324    case SuspenderState::CalledOnMain: {
    325      cx->wasm().suspenders_.remove(this);
    326      this->releaseStackMemory();
    327      MOZ_ASSERT(this->isMoribund());
    328      break;
    329    }
    330    case SuspenderState::Active:
    331    case SuspenderState::Initial:
    332    case SuspenderState::Moribund:
    333      MOZ_CRASH();
    334  }
    335 }
    336 
    337 void SuspenderObject::forwardToSuspendable() {
    338  // Injecting suspendable stack back into main one at the exit frame.
    339  uint8_t* mainExitFP = (uint8_t*)this->mainExitFP();
    340  *reinterpret_cast<void**>(mainExitFP + Frame::callerFPOffset()) =
    341      this->suspendableFP();
    342  *reinterpret_cast<void**>(mainExitFP + Frame::returnAddressOffset()) =
    343      this->suspendedReturnAddress();
    344 }
    345 
    346 // Suspending
    347 
    348 // Builds a wasm module with following structure:
    349 // (module
    350 //   (type $params (struct (field ..)*)))
    351 //   (type $results (struct (field ..)*)))
    352 //   (import "" "" (func $suspending.wrappedfn ..))
    353 //   (func $suspending.exported .. )
    354 //   (func $suspending.trampoline ..)
    355 //   (func $suspending.continue-on-suspendable ..)
    356 //   (export "" (func $suspending.exported))
    357 // )
    358 //
    359 // The module provides logic for the state transitions (see the SMDOC):
    360 //  - Invoke Suspending Import via $suspending.exported
    361 //  - Suspending Function Returns a Promise via $suspending.trampoline
    362 //  - Promise Resolved transitions via $suspending.continue-on-suspendable
    363 //
    364 class SuspendingFunctionModuleFactory {
    365 public:
    366  enum TypeIdx {
    367    ParamsTypeIndex,
    368    ResultsTypeIndex,
    369  };
    370 
    371  enum FnIdx {
    372    WrappedFnIndex,
    373    ExportedFnIndex,
    374    TrampolineFnIndex,
    375    ContinueOnSuspendableFnIndex
    376  };
    377 
    378 private:
    379  // Builds function that will be imported to wasm module:
    380  // (func $suspending.exported
    381  //   (param ..)* (result ..)*
    382  //   (local $suspender externref)
    383  //   (local $results (ref $results))
    384  //   call $builtin.current-suspender
    385  //   local.tee $suspender
    386  //   ref.func $suspending.trampoline
    387  //   local.get $i*
    388  //   stuct.new $param-type
    389  //   stack-switch SwitchToMain ;; <- (suspender,fn,data)
    390  //   local.get $suspender
    391  //   call $builtin.get-suspending-promise-result
    392  //   ref.cast $results-type
    393  //   local.set $results
    394  //   (struct.get $results (local.get $results))*
    395  // )
    396  bool encodeExportedFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
    397                              uint32_t resultSize, uint32_t paramsOffset,
    398                              RefType resultType, Bytes& bytecode) {
    399    Encoder encoder(bytecode, *codeMeta.types);
    400    ValTypeVector locals;
    401    if (!locals.emplaceBack(RefType::extern_())) {
    402      return false;
    403    }
    404    if (!locals.emplaceBack(resultType)) {
    405      return false;
    406    }
    407    if (!EncodeLocalEntries(encoder, locals)) {
    408      return false;
    409    }
    410 
    411    const int suspenderIndex = paramsSize;
    412    if (!encoder.writeOp(Op::I32Const) || !encoder.writeVarU32(0)) {
    413      return false;
    414    }
    415    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    416        !encoder.writeVarU32((uint32_t)BuiltinModuleFuncId::CurrentSuspender)) {
    417      return false;
    418    }
    419    if (!encoder.writeOp(Op::LocalTee) ||
    420        !encoder.writeVarU32(suspenderIndex)) {
    421      return false;
    422    }
    423 
    424    // Results local is located after all params and suspender.
    425    const int resultsIndex = paramsSize + 1;
    426 
    427    if (!encoder.writeOp(Op::RefFunc) ||
    428        !encoder.writeVarU32(TrampolineFnIndex)) {
    429      return false;
    430    }
    431    for (uint32_t i = 0; i < paramsSize; i++) {
    432      if (!encoder.writeOp(Op::LocalGet) ||
    433          !encoder.writeVarU32(i + paramsOffset)) {
    434        return false;
    435      }
    436    }
    437    if (!encoder.writeOp(GcOp::StructNew) ||
    438        !encoder.writeVarU32(ParamsTypeIndex)) {
    439      return false;
    440    }
    441 
    442    if (!encoder.writeOp(MozOp::StackSwitch) ||
    443        !encoder.writeVarU32(uint32_t(StackSwitchKind::SwitchToMain))) {
    444      return false;
    445    }
    446 
    447    if (!encoder.writeOp(Op::LocalGet) ||
    448        !encoder.writeVarU32(suspenderIndex)) {
    449      return false;
    450    }
    451    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    452        !encoder.writeVarU32(
    453            (uint32_t)BuiltinModuleFuncId::GetSuspendingPromiseResult)) {
    454      return false;
    455    }
    456    if (!encoder.writeOp(GcOp::RefCast) ||
    457        !encoder.writeVarU32(ResultsTypeIndex) ||
    458        !encoder.writeOp(Op::LocalSet) || !encoder.writeVarU32(resultsIndex)) {
    459      return false;
    460    }
    461    for (uint32_t i = 0; i < resultSize; i++) {
    462      if (!encoder.writeOp(Op::LocalGet) ||
    463          !encoder.writeVarU32(resultsIndex) ||
    464          !encoder.writeOp(GcOp::StructGet) ||
    465          !encoder.writeVarU32(ResultsTypeIndex) || !encoder.writeVarU32(i)) {
    466        return false;
    467      }
    468    }
    469    return encoder.writeOp(Op::End);
    470  }
    471 
    472  // Builds function that is called on main stack:
    473  // (func $suspending.trampoline
    474  //   (param $params (ref $suspender)) (param $param (ref $param-type))
    475  //   (result anyref)
    476  //   local.get $suspender ;; for $builtin.forward-exn-to-suspended below
    477  //   block (result exnref)
    478  //    try_table (catch_all_ref 0)
    479  //     local.get $suspender ;; for call $add-promise-reactions
    480  //     (struct.get $param-type $i (local.get $param))*
    481  //     call $suspending.wrappedfn
    482  //     ref.func $suspending.continue-on-suspendable
    483  //     call $builtin.add-promise-reactions
    484  //     return
    485  //    end
    486  //    unreachable
    487  //   end
    488  //   call $builtin.forward-exn-to-suspended
    489  // )
    490  // The function calls suspending import and returns into the
    491  // $promising.exported function because that was the top function
    492  // on the main stack.
    493  bool encodeTrampolineFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
    494                                Bytes& bytecode) {
    495    Encoder encoder(bytecode, *codeMeta.types);
    496    if (!EncodeLocalEntries(encoder, ValTypeVector())) {
    497      return false;
    498    }
    499    const uint32_t SuspenderIndex = 0;
    500    const uint32_t ParamsIndex = 1;
    501 
    502    if (!encoder.writeOp(Op::LocalGet) ||
    503        !encoder.writeVarU32(SuspenderIndex)) {
    504      return false;
    505    }
    506 
    507    if (!encoder.writeOp(Op::Block) ||
    508        !encoder.writeFixedU8(uint8_t(TypeCode::ExnRef))) {
    509      return false;
    510    }
    511 
    512    if (!encoder.writeOp(Op::TryTable) ||
    513        !encoder.writeFixedU8(uint8_t(TypeCode::BlockVoid)) ||
    514        !encoder.writeVarU32(1) ||
    515        !encoder.writeFixedU8(/* catch_all_ref = */ 0x03) ||
    516        !encoder.writeVarU32(0)) {
    517      return false;
    518    }
    519 
    520    // For AddPromiseReactions call below.
    521    if (!encoder.writeOp(Op::LocalGet) ||
    522        !encoder.writeVarU32(SuspenderIndex)) {
    523      return false;
    524    }
    525 
    526    for (uint32_t i = 0; i < paramsSize; i++) {
    527      if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ParamsIndex)) {
    528        return false;
    529      }
    530      if (!encoder.writeOp(GcOp::StructGet) ||
    531          !encoder.writeVarU32(ParamsTypeIndex) || !encoder.writeVarU32(i)) {
    532        return false;
    533      }
    534    }
    535    if (!encoder.writeOp(Op::Call) || !encoder.writeVarU32(WrappedFnIndex)) {
    536      return false;
    537    }
    538    if (!encoder.writeOp(Op::RefFunc) ||
    539        !encoder.writeVarU32(ContinueOnSuspendableFnIndex)) {
    540      return false;
    541    }
    542 
    543    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    544        !encoder.writeVarU32(
    545            (uint32_t)BuiltinModuleFuncId::AddPromiseReactions)) {
    546      return false;
    547    }
    548 
    549    if (!encoder.writeOp(Op::Return) || !encoder.writeOp(Op::End) ||
    550        !encoder.writeOp(Op::Unreachable) || !encoder.writeOp(Op::End)) {
    551      return false;
    552    }
    553 
    554    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    555        !encoder.writeVarU32(
    556            (uint32_t)BuiltinModuleFuncId::ForwardExceptionToSuspended)) {
    557      return false;
    558    }
    559 
    560    return encoder.writeOp(Op::End);
    561  }
    562 
    563  // Builds function that is called on main stack:
    564  // (func $suspending.continue-on-suspendable
    565  //   (param $params (ref $suspender)) (param $results externref)
    566  //   (result externref)
    567  //   local.get $suspender
    568  //   ref.null funcref
    569  //   local.get $results
    570  //   any.convert_extern
    571  //   stack-switch ContinueOnSuspendable
    572  // )
    573  bool encodeContinueOnSuspendableFunction(CodeMetadata& codeMeta,
    574                                           uint32_t resultsSize,
    575                                           Bytes& bytecode) {
    576    Encoder encoder(bytecode, *codeMeta.types);
    577    if (!EncodeLocalEntries(encoder, ValTypeVector())) {
    578      return false;
    579    }
    580 
    581    const uint32_t SuspenderIndex = 0;
    582    const uint32_t ResultsIndex = 1;
    583 
    584    if (!encoder.writeOp(Op::LocalGet) ||
    585        !encoder.writeVarU32(SuspenderIndex)) {
    586      return false;
    587    }
    588    if (!encoder.writeOp(Op::RefNull) ||
    589        !encoder.writeValType(ValType(RefType::func()))) {
    590      return false;
    591    }
    592    if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ResultsIndex) ||
    593        !encoder.writeOp(GcOp::AnyConvertExtern)) {
    594      return false;
    595    }
    596 
    597    if (!encoder.writeOp(MozOp::StackSwitch) ||
    598        !encoder.writeVarU32(
    599            uint32_t(StackSwitchKind::ContinueOnSuspendable))) {
    600      return false;
    601    }
    602 
    603    return encoder.writeOp(Op::End);
    604  }
    605 
    606 public:
    607  SharedModule build(JSContext* cx, HandleObject func, ValTypeVector&& params,
    608                     ValTypeVector&& results) {
    609    FeatureOptions options;
    610    options.isBuiltinModule = true;
    611 
    612    ScriptedCaller scriptedCaller;
    613    SharedCompileArgs compileArgs =
    614        CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
    615    if (!compileArgs) {
    616      return nullptr;
    617    }
    618 
    619    MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
    620    if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
    621      return nullptr;
    622    }
    623    MutableCodeMetadata codeMeta = moduleMeta->codeMeta;
    624 
    625    MOZ_ASSERT(IonPlatformSupport());
    626    CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
    627                                    DebugEnabled::False);
    628    compilerEnv.computeParameters();
    629 
    630    RefType suspenderType = RefType::extern_();
    631    RefType promiseType = RefType::extern_();
    632 
    633    ValTypeVector paramsWithoutSuspender;
    634 
    635    const size_t resultsSize = results.length();
    636    const size_t paramsSize = params.length();
    637    const size_t paramsOffset = 0;
    638    if (!paramsWithoutSuspender.append(params.begin(), params.end())) {
    639      ReportOutOfMemory(cx);
    640      return nullptr;
    641    }
    642 
    643    ValTypeVector resultsRef;
    644    if (!resultsRef.emplaceBack(promiseType)) {
    645      ReportOutOfMemory(cx);
    646      return nullptr;
    647    }
    648 
    649    StructType boxedParamsStruct;
    650    if (!StructType::createImmutable(paramsWithoutSuspender,
    651                                     &boxedParamsStruct)) {
    652      ReportOutOfMemory(cx);
    653      return nullptr;
    654    }
    655    MOZ_ASSERT(codeMeta->types->length() == ParamsTypeIndex);
    656    if (!codeMeta->types->addType(std::move(boxedParamsStruct))) {
    657      return nullptr;
    658    }
    659 
    660    StructType boxedResultType;
    661    if (!StructType::createImmutable(results, &boxedResultType)) {
    662      ReportOutOfMemory(cx);
    663      return nullptr;
    664    }
    665    MOZ_ASSERT(codeMeta->types->length() == ResultsTypeIndex);
    666    if (!codeMeta->types->addType(std::move(boxedResultType))) {
    667      return nullptr;
    668    }
    669 
    670    MOZ_ASSERT(codeMeta->funcs.length() == WrappedFnIndex);
    671    if (!moduleMeta->addDefinedFunc(std::move(paramsWithoutSuspender),
    672                                    std::move(resultsRef))) {
    673      return nullptr;
    674    }
    675 
    676    // Imports names are not important, declare functions above as imports.
    677    codeMeta->numFuncImports = codeMeta->funcs.length();
    678 
    679    // We will be looking up and using the exports function by index so
    680    // the name doesn't matter.
    681    MOZ_ASSERT(codeMeta->funcs.length() == ExportedFnIndex);
    682    if (!moduleMeta->addDefinedFunc(std::move(params), std::move(results),
    683                                    /*declareForRef = */ true,
    684                                    mozilla::Some(CacheableName()))) {
    685      return nullptr;
    686    }
    687 
    688    ValTypeVector paramsTrampoline, resultsTrampoline;
    689    if (!paramsTrampoline.emplaceBack(suspenderType) ||
    690        !paramsTrampoline.emplaceBack(RefType::fromTypeDef(
    691            &(*codeMeta->types)[ParamsTypeIndex], false)) ||
    692        !resultsTrampoline.emplaceBack(RefType::any())) {
    693      ReportOutOfMemory(cx);
    694      return nullptr;
    695    }
    696    MOZ_ASSERT(codeMeta->funcs.length() == TrampolineFnIndex);
    697    if (!moduleMeta->addDefinedFunc(std::move(paramsTrampoline),
    698                                    std::move(resultsTrampoline),
    699                                    /*declareForRef = */ true)) {
    700      return nullptr;
    701    }
    702 
    703    ValTypeVector paramsContinueOnSuspendable, resultsContinueOnSuspendable;
    704    if (!paramsContinueOnSuspendable.emplaceBack(suspenderType) ||
    705        !paramsContinueOnSuspendable.emplaceBack(RefType::extern_())) {
    706      ReportOutOfMemory(cx);
    707      return nullptr;
    708    }
    709    MOZ_ASSERT(codeMeta->funcs.length() == ContinueOnSuspendableFnIndex);
    710    if (!moduleMeta->addDefinedFunc(std::move(paramsContinueOnSuspendable),
    711                                    std::move(resultsContinueOnSuspendable),
    712                                    /*declareForRef = */ true)) {
    713      return nullptr;
    714    }
    715 
    716    if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
    717      return nullptr;
    718    }
    719 
    720    ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(),
    721                       nullptr, nullptr, nullptr);
    722    if (!mg.initializeCompleteTier()) {
    723      return nullptr;
    724    }
    725    // Build functions and keep bytecodes around until the end.
    726    uint32_t funcBytecodeOffset = CallSite::FIRST_VALID_BYTECODE_OFFSET;
    727    Bytes bytecode;
    728    if (!encodeExportedFunction(
    729            *codeMeta, paramsSize, resultsSize, paramsOffset,
    730            RefType::fromTypeDef(&(*codeMeta->types)[ResultsTypeIndex], false),
    731            bytecode)) {
    732      ReportOutOfMemory(cx);
    733      return nullptr;
    734    }
    735    if (!mg.compileFuncDef(ExportedFnIndex, funcBytecodeOffset,
    736                           bytecode.begin(),
    737                           bytecode.begin() + bytecode.length())) {
    738      return nullptr;
    739    }
    740    funcBytecodeOffset += bytecode.length();
    741 
    742    Bytes bytecode2;
    743    if (!encodeTrampolineFunction(*codeMeta, paramsSize, bytecode2)) {
    744      ReportOutOfMemory(cx);
    745      return nullptr;
    746    }
    747    if (!mg.compileFuncDef(TrampolineFnIndex, funcBytecodeOffset,
    748                           bytecode2.begin(),
    749                           bytecode2.begin() + bytecode2.length())) {
    750      return nullptr;
    751    }
    752    funcBytecodeOffset += bytecode2.length();
    753 
    754    Bytes bytecode3;
    755    if (!encodeContinueOnSuspendableFunction(*codeMeta, paramsSize,
    756                                             bytecode3)) {
    757      ReportOutOfMemory(cx);
    758      return nullptr;
    759    }
    760    if (!mg.compileFuncDef(ContinueOnSuspendableFnIndex, funcBytecodeOffset,
    761                           bytecode3.begin(),
    762                           bytecode3.begin() + bytecode3.length())) {
    763      return nullptr;
    764    }
    765    funcBytecodeOffset += bytecode3.length();
    766 
    767    if (!mg.finishFuncDefs()) {
    768      return nullptr;
    769    }
    770 
    771    return mg.finishModule(BytecodeBufferOrSource(), *moduleMeta,
    772                           /*maybeCompleteTier2Listener=*/nullptr);
    773  }
    774 };
    775 
    776 // Reaction on resolved/rejected suspending promise.
    777 static bool WasmPISuspendTaskContinue(JSContext* cx, unsigned argc, Value* vp) {
    778  CallArgs args = CallArgsFromVp(argc, vp);
    779  // The arg[0] has result of resolved promise, or rejection reason.
    780  Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
    781  RootedValue suspender(cx, callee->getExtendedSlot(SUSPENDER_SLOT));
    782  RootedValue suspendingPromise(cx, callee->getExtendedSlot(PROMISE_SLOT));
    783 
    784  // Convert result of the promise into the parameters/arguments for the
    785  // $suspending.continue-on-suspendable.
    786  RootedFunction continueOnSuspendable(
    787      cx, &callee->getExtendedSlot(CONTINUE_ON_SUSPENDABLE_SLOT)
    788               .toObject()
    789               .as<JSFunction>());
    790  JS::RootedValueArray<2> argv(cx);
    791  argv[0].set(suspender);
    792  argv[1].set(suspendingPromise);
    793 
    794  JS::Rooted<JS::Value> rval(cx);
    795  if (Call(cx, UndefinedHandleValue, continueOnSuspendable, argv, &rval)) {
    796    return true;
    797  }
    798 
    799  // The stack was unwound during exception.
    800  MOZ_RELEASE_ASSERT(!cx->wasm().activeSuspender());
    801  MOZ_RELEASE_ASSERT(
    802      suspender.toObject().as<wasm::SuspenderObject>().isMoribund());
    803 
    804  if (cx->isThrowingOutOfMemory()) {
    805    return false;
    806  }
    807  Rooted<PromiseObject*> promise(
    808      cx, suspender.toObject().as<SuspenderObject>().promisingPromise());
    809  return RejectPromiseWithPendingError(cx, promise);
    810 }
    811 
    812 // Wraps original import to catch all exceptions and convert result to a
    813 // promise.
    814 // Seen as $suspending.wrappedfn in wasm.
    815 static bool WasmPIWrapSuspendingImport(JSContext* cx, unsigned argc,
    816                                       Value* vp) {
    817  CallArgs args = CallArgsFromVp(argc, vp);
    818  Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
    819  RootedValue originalImportFunc(cx, callee->getExtendedSlot(WRAPPED_FN_SLOT));
    820 
    821  // Catching exceptions here.
    822  RootedValue rval(cx);
    823  if (Call(cx, UndefinedHandleValue, originalImportFunc, args, &rval)) {
    824    // Convert the result to a resolved promise later in AddPromiseReactions.
    825    args.rval().set(rval);
    826    return true;
    827  }
    828 
    829  // Deferring pending exception to the handler in the
    830  // $suspending.trampoline.
    831  return false;
    832 }
    833 
    834 JSFunction* WasmSuspendingFunctionCreate(JSContext* cx, HandleObject func,
    835                                         ValTypeVector&& params,
    836                                         ValTypeVector&& results) {
    837  MOZ_ASSERT(IsCallable(ObjectValue(*func)) &&
    838             !IsCrossCompartmentWrapper(func));
    839 
    840  SuspendingFunctionModuleFactory moduleFactory;
    841  SharedModule module =
    842      moduleFactory.build(cx, func, std::move(params), std::move(results));
    843  if (!module) {
    844    return nullptr;
    845  }
    846 
    847  // Instantiate the module.
    848  Rooted<ImportValues> imports(cx);
    849 
    850  // Add $suspending.wrappedfn to imports.
    851  RootedFunction funcWrapper(
    852      cx, NewNativeFunction(cx, WasmPIWrapSuspendingImport, 0, nullptr,
    853                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
    854  if (!funcWrapper) {
    855    return nullptr;
    856  }
    857  funcWrapper->initExtendedSlot(WRAPPED_FN_SLOT, ObjectValue(*func));
    858  if (!imports.get().funcs.append(funcWrapper)) {
    859    ReportOutOfMemory(cx);
    860    return nullptr;
    861  }
    862 
    863  Rooted<WasmInstanceObject*> instance(cx);
    864  if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
    865    // Can also trap on invalid input function.
    866    return nullptr;
    867  }
    868 
    869  // Returns the $suspending.exported function.
    870  RootedFunction wasmFunc(cx);
    871  if (!WasmInstanceObject::getExportedFunction(
    872          cx, instance, SuspendingFunctionModuleFactory::ExportedFnIndex,
    873          &wasmFunc)) {
    874    return nullptr;
    875  }
    876  return wasmFunc;
    877 }
    878 
    879 JSFunction* WasmSuspendingFunctionCreate(JSContext* cx, HandleObject func,
    880                                         const FuncType& type) {
    881  ValTypeVector params, results;
    882  if (!params.append(type.args().begin(), type.args().end()) ||
    883      !results.append(type.results().begin(), type.results().end())) {
    884    ReportOutOfMemory(cx);
    885    return nullptr;
    886  }
    887  return WasmSuspendingFunctionCreate(cx, func, std::move(params),
    888                                      std::move(results));
    889 }
    890 
    891 // Promising
    892 
    893 // Builds a wasm module with following structure:
    894 // (module
    895 //   (type $params (struct (field ..)*))
    896 //   (type $results (struct (field ..)*))
    897 //   (type $create-suspender-result (struct (field externref externref)))
    898 //   (import "" "" (func $promising.wrappedfn ..))
    899 //   (func $promising.exported .. )
    900 //   (func $promising.trampoline ..)
    901 //   (export "" (func $promising.exported))
    902 // )
    903 //
    904 // The module provides logic for the Invoke Promising Import state transition
    905 // via $promising.exported and $promising.trampoline (see the SMDOC).
    906 //
    907 class PromisingFunctionModuleFactory {
    908 public:
    909  enum TypeIdx {
    910    ParamsTypeIndex,
    911    ResultsTypeIndex,
    912  };
    913 
    914  enum FnIdx {
    915    WrappedFnIndex,
    916    ExportedFnIndex,
    917    TrampolineFnIndex,
    918  };
    919 
    920 private:
    921  // Builds function that will be exported for JS:
    922  // (func $promising.exported
    923  //   (param ..)* (result externref)
    924  //   (local $suspender externref)
    925  //   call $builtin.create-suspender
    926  //   local.tee $suspender
    927  //   call $builtin.create-promising-promise ;; -> (promise)
    928  //   local.get $suspender
    929  //   ref.func $promising.trampoline
    930  //   local.get $i*
    931  //   stuct.new $param-type
    932  //   stack-switch SwitchToSuspendable ;; <- (suspender,fn,data)
    933  // )
    934  bool encodeExportedFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
    935                              Bytes& bytecode) {
    936    Encoder encoder(bytecode, *codeMeta.types);
    937    ValTypeVector locals;
    938    if (!locals.emplaceBack(RefType::extern_())) {
    939      return false;
    940    }
    941    if (!EncodeLocalEntries(encoder, locals)) {
    942      return false;
    943    }
    944 
    945    const uint32_t SuspenderIndex = paramsSize;
    946    if (!encoder.writeOp(Op::I32Const) || !encoder.writeVarU32(0)) {
    947      return false;
    948    }
    949    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    950        !encoder.writeVarU32((uint32_t)BuiltinModuleFuncId::CreateSuspender)) {
    951      return false;
    952    }
    953 
    954    if (!encoder.writeOp(Op::LocalTee) ||
    955        !encoder.writeVarU32(SuspenderIndex)) {
    956      return false;
    957    }
    958    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
    959        !encoder.writeVarU32(
    960            (uint32_t)BuiltinModuleFuncId::CreatePromisingPromise)) {
    961      return false;
    962    }
    963 
    964    if (!encoder.writeOp(Op::LocalGet) ||
    965        !encoder.writeVarU32(SuspenderIndex)) {
    966      return false;
    967    }
    968    if (!encoder.writeOp(Op::RefFunc) ||
    969        !encoder.writeVarU32(TrampolineFnIndex)) {
    970      return false;
    971    }
    972    for (uint32_t i = 0; i < paramsSize; i++) {
    973      if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(i)) {
    974        return false;
    975      }
    976    }
    977    if (!encoder.writeOp(GcOp::StructNew) ||
    978        !encoder.writeVarU32(ParamsTypeIndex)) {
    979      return false;
    980    }
    981    if (!encoder.writeOp(MozOp::StackSwitch) ||
    982        !encoder.writeVarU32(uint32_t(StackSwitchKind::SwitchToSuspendable))) {
    983      return false;
    984    }
    985 
    986    return encoder.writeOp(Op::End);
    987  }
    988 
    989  // Builds function that is called on alternative stack:
    990  // (func $promising.trampoline
    991  //   (param $suspender externref) (param $params (ref $param-type))
    992  //   (result externref)
    993  //   local.get $suspender ;; for call $set-results
    994  //   (local.get $suspender)?
    995  //   (struct.get $param-type $i (local.get $param))*
    996  //   (local.get $suspender)?
    997  //   call $promising.wrappedfn
    998  //   struct.new $result-type
    999  //   call $builtin.set-promising-promise-results
   1000  // )
   1001  bool encodeTrampolineFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
   1002                                Bytes& bytecode) {
   1003    Encoder encoder(bytecode, *codeMeta.types);
   1004    if (!EncodeLocalEntries(encoder, ValTypeVector())) {
   1005      return false;
   1006    }
   1007    const uint32_t SuspenderIndex = 0;
   1008    const uint32_t ParamsIndex = 1;
   1009 
   1010    // Reserved for SetResultsFnIndex call at the end
   1011    if (!encoder.writeOp(Op::LocalGet) ||
   1012        !encoder.writeVarU32(SuspenderIndex)) {
   1013      return false;
   1014    }
   1015 
   1016    for (uint32_t i = 0; i < paramsSize; i++) {
   1017      if (!encoder.writeOp(Op::LocalGet) || !encoder.writeVarU32(ParamsIndex)) {
   1018        return false;
   1019      }
   1020      if (!encoder.writeOp(GcOp::StructGet) ||
   1021          !encoder.writeVarU32(ParamsTypeIndex) || !encoder.writeVarU32(i)) {
   1022        return false;
   1023      }
   1024    }
   1025    if (!encoder.writeOp(Op::Call) || !encoder.writeVarU32(WrappedFnIndex)) {
   1026      return false;
   1027    }
   1028 
   1029    if (!encoder.writeOp(GcOp::StructNew) ||
   1030        !encoder.writeVarU32(ResultsTypeIndex)) {
   1031      return false;
   1032    }
   1033    if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
   1034        !encoder.writeVarU32(
   1035            (uint32_t)BuiltinModuleFuncId::SetPromisingPromiseResults)) {
   1036      return false;
   1037    }
   1038 
   1039    return encoder.writeOp(Op::End);
   1040  }
   1041 
   1042 public:
   1043  SharedModule build(JSContext* cx, HandleFunction fn, ValTypeVector&& params,
   1044                     ValTypeVector&& results) {
   1045    const FuncType& fnType = fn->wasmTypeDef()->funcType();
   1046    size_t paramsSize = params.length();
   1047 
   1048    RefType suspenderType = RefType::extern_();
   1049 
   1050    FeatureOptions options;
   1051    options.isBuiltinModule = true;
   1052 
   1053    ScriptedCaller scriptedCaller;
   1054    SharedCompileArgs compileArgs =
   1055        CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
   1056    if (!compileArgs) {
   1057      return nullptr;
   1058    }
   1059 
   1060    MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
   1061    if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
   1062      return nullptr;
   1063    }
   1064    MutableCodeMetadata codeMeta = moduleMeta->codeMeta;
   1065 
   1066    MOZ_ASSERT(IonPlatformSupport());
   1067    CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
   1068                                    DebugEnabled::False);
   1069    compilerEnv.computeParameters();
   1070 
   1071    StructType boxedParamsStruct;
   1072    if (!StructType::createImmutable(params, &boxedParamsStruct)) {
   1073      ReportOutOfMemory(cx);
   1074      return nullptr;
   1075    }
   1076    MOZ_ASSERT(codeMeta->types->length() == ParamsTypeIndex);
   1077    if (!codeMeta->types->addType(std::move(boxedParamsStruct))) {
   1078      return nullptr;
   1079    }
   1080 
   1081    StructType boxedResultType;
   1082    if (!StructType::createImmutable(fnType.results(), &boxedResultType)) {
   1083      ReportOutOfMemory(cx);
   1084      return nullptr;
   1085    }
   1086    MOZ_ASSERT(codeMeta->types->length() == ResultsTypeIndex);
   1087    if (!codeMeta->types->addType(std::move(boxedResultType))) {
   1088      return nullptr;
   1089    }
   1090 
   1091    ValTypeVector paramsForWrapper, resultsForWrapper;
   1092    if (!paramsForWrapper.append(fnType.args().begin(), fnType.args().end()) ||
   1093        !resultsForWrapper.append(fnType.results().begin(),
   1094                                  fnType.results().end())) {
   1095      ReportOutOfMemory(cx);
   1096      return nullptr;
   1097    }
   1098    MOZ_ASSERT(codeMeta->funcs.length() == WrappedFnIndex);
   1099    if (!moduleMeta->addDefinedFunc(std::move(paramsForWrapper),
   1100                                    std::move(resultsForWrapper))) {
   1101      return nullptr;
   1102    }
   1103 
   1104    // Imports names are not important, declare functions above as imports.
   1105    codeMeta->numFuncImports = codeMeta->funcs.length();
   1106 
   1107    // We will be looking up and using the exports function by index so
   1108    // the name doesn't matter.
   1109    MOZ_ASSERT(codeMeta->funcs.length() == ExportedFnIndex);
   1110    if (!moduleMeta->addDefinedFunc(std::move(params), std::move(results),
   1111                                    /* declareFoRef = */ true,
   1112                                    mozilla::Some(CacheableName()))) {
   1113      return nullptr;
   1114    }
   1115 
   1116    ValTypeVector paramsTrampoline, resultsTrampoline;
   1117    if (!paramsTrampoline.emplaceBack(suspenderType) ||
   1118        !paramsTrampoline.emplaceBack(RefType::fromTypeDef(
   1119            &(*codeMeta->types)[ParamsTypeIndex], false))) {
   1120      ReportOutOfMemory(cx);
   1121      return nullptr;
   1122    }
   1123    MOZ_ASSERT(codeMeta->funcs.length() == TrampolineFnIndex);
   1124    if (!moduleMeta->addDefinedFunc(std::move(paramsTrampoline),
   1125                                    std::move(resultsTrampoline),
   1126                                    /* declareFoRef = */ true)) {
   1127      return nullptr;
   1128    }
   1129 
   1130    if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
   1131      return nullptr;
   1132    }
   1133 
   1134    ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(),
   1135                       nullptr, nullptr, nullptr);
   1136    if (!mg.initializeCompleteTier()) {
   1137      return nullptr;
   1138    }
   1139    // Build functions and keep bytecodes around until the end.
   1140    Bytes bytecode;
   1141    uint32_t funcBytecodeOffset = CallSite::FIRST_VALID_BYTECODE_OFFSET;
   1142    if (!encodeExportedFunction(*codeMeta, paramsSize, bytecode)) {
   1143      ReportOutOfMemory(cx);
   1144      return nullptr;
   1145    }
   1146    if (!mg.compileFuncDef(ExportedFnIndex, funcBytecodeOffset,
   1147                           bytecode.begin(),
   1148                           bytecode.begin() + bytecode.length())) {
   1149      return nullptr;
   1150    }
   1151    funcBytecodeOffset += bytecode.length();
   1152 
   1153    Bytes bytecode2;
   1154    if (!encodeTrampolineFunction(*codeMeta, paramsSize, bytecode2)) {
   1155      ReportOutOfMemory(cx);
   1156      return nullptr;
   1157    }
   1158    if (!mg.compileFuncDef(TrampolineFnIndex, funcBytecodeOffset,
   1159                           bytecode2.begin(),
   1160                           bytecode2.begin() + bytecode2.length())) {
   1161      return nullptr;
   1162    }
   1163    funcBytecodeOffset += bytecode2.length();
   1164 
   1165    if (!mg.finishFuncDefs()) {
   1166      return nullptr;
   1167    }
   1168 
   1169    return mg.finishModule(BytecodeBufferOrSource(), *moduleMeta,
   1170                           /*maybeCompleteTier2Listener=*/nullptr);
   1171  }
   1172 };
   1173 
   1174 // Wraps call to wasm $promising.exported function to catch an exception and
   1175 // return a promise instead.
   1176 static bool WasmPIPromisingFunction(JSContext* cx, unsigned argc, Value* vp) {
   1177  CallArgs args = CallArgsFromVp(argc, vp);
   1178  Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
   1179  RootedFunction fn(
   1180      cx,
   1181      &callee->getExtendedSlot(WRAPPED_FN_SLOT).toObject().as<JSFunction>());
   1182 
   1183  // Catching exceptions here.
   1184  if (Call(cx, UndefinedHandleValue, fn, args, args.rval())) {
   1185    return true;
   1186  }
   1187 
   1188  // The stack was unwound during exception. There should be no active
   1189  // suspender.
   1190  MOZ_RELEASE_ASSERT(!cx->wasm().activeSuspender());
   1191 
   1192  if (cx->isThrowingOutOfMemory()) {
   1193    return false;
   1194  }
   1195 
   1196  RootedObject promiseObject(cx, NewPromiseObject(cx, nullptr));
   1197  if (!promiseObject) {
   1198    return false;
   1199  }
   1200  args.rval().setObject(*promiseObject);
   1201 
   1202  Rooted<PromiseObject*> promise(cx, &promiseObject->as<PromiseObject>());
   1203  return RejectPromiseWithPendingError(cx, promise);
   1204 }
   1205 
   1206 JSFunction* WasmPromisingFunctionCreate(JSContext* cx, HandleObject func,
   1207                                        ValTypeVector&& params,
   1208                                        ValTypeVector&& results) {
   1209  RootedFunction wrappedWasmFunc(cx, &func->as<JSFunction>());
   1210  MOZ_ASSERT(wrappedWasmFunc->isWasm());
   1211  const FuncType& wrappedWasmFuncType =
   1212      wrappedWasmFunc->wasmTypeDef()->funcType();
   1213 
   1214  MOZ_ASSERT(results.length() == 0 && params.length() == 0);
   1215  if (!results.append(RefType::extern_())) {
   1216    ReportOutOfMemory(cx);
   1217    return nullptr;
   1218  }
   1219  if (!params.append(wrappedWasmFuncType.args().begin(),
   1220                     wrappedWasmFuncType.args().end())) {
   1221    ReportOutOfMemory(cx);
   1222    return nullptr;
   1223  }
   1224 
   1225  PromisingFunctionModuleFactory moduleFactory;
   1226  SharedModule module = moduleFactory.build(
   1227      cx, wrappedWasmFunc, std::move(params), std::move(results));
   1228  // Instantiate the module.
   1229  Rooted<ImportValues> imports(cx);
   1230 
   1231  // Add wrapped function ($promising.wrappedfn) to imports.
   1232  if (!imports.get().funcs.append(func)) {
   1233    ReportOutOfMemory(cx);
   1234    return nullptr;
   1235  }
   1236 
   1237  Rooted<WasmInstanceObject*> instance(cx);
   1238  if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
   1239    MOZ_ASSERT(cx->isThrowingOutOfMemory());
   1240    return nullptr;
   1241  }
   1242 
   1243  // Wrap $promising.exported function for exceptions/traps handling.
   1244  RootedFunction wasmFunc(cx);
   1245  if (!WasmInstanceObject::getExportedFunction(
   1246          cx, instance, PromisingFunctionModuleFactory::ExportedFnIndex,
   1247          &wasmFunc)) {
   1248    return nullptr;
   1249  }
   1250 
   1251  RootedFunction wasmFuncWrapper(
   1252      cx, NewNativeFunction(cx, WasmPIPromisingFunction, 0, nullptr,
   1253                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
   1254  if (!wasmFuncWrapper) {
   1255    return nullptr;
   1256  }
   1257  wasmFuncWrapper->initExtendedSlot(WRAPPED_FN_SLOT, ObjectValue(*wasmFunc));
   1258  return wasmFuncWrapper;
   1259 }
   1260 
   1261 // Gets active suspender.
   1262 // The reserved parameter is a workaround for limitation in the
   1263 // WasmBuiltinModule.yaml generator to always have params.
   1264 // Seen as $builtin.current-suspender to wasm.
   1265 SuspenderObject* CurrentSuspender(Instance* instance, int32_t reserved) {
   1266  MOZ_ASSERT(SASigCurrentSuspender.failureMode == FailureMode::FailOnNullPtr);
   1267  JSContext* cx = instance->cx();
   1268  SuspenderObject* suspender = cx->wasm().activeSuspender();
   1269  if (!suspender) {
   1270    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1271                              JSMSG_JSPI_INVALID_STATE);
   1272    return nullptr;
   1273  }
   1274  return suspender;
   1275 }
   1276 
   1277 // Creates a suspender and promise (that will be returned to JS code).
   1278 // Seen as $builtin.create-suspender to wasm.
   1279 SuspenderObject* CreateSuspender(Instance* instance, int32_t reserved) {
   1280  MOZ_ASSERT(SASigCreateSuspender.failureMode == FailureMode::FailOnNullPtr);
   1281  JSContext* cx = instance->cx();
   1282  return SuspenderObject::create(cx);
   1283 }
   1284 
   1285 // Creates a promise that will be returned at promising call.
   1286 // Seen as $builtin.create-promising-promise to wasm.
   1287 PromiseObject* CreatePromisingPromise(Instance* instance,
   1288                                      SuspenderObject* suspender) {
   1289  MOZ_ASSERT(SASigCreatePromisingPromise.failureMode ==
   1290             FailureMode::FailOnNullPtr);
   1291  JSContext* cx = instance->cx();
   1292 
   1293  Rooted<SuspenderObject*> suspenderObject(cx, suspender);
   1294  RootedObject promiseObject(cx, NewPromiseObject(cx, nullptr));
   1295  if (!promiseObject) {
   1296    return nullptr;
   1297  }
   1298 
   1299  Rooted<PromiseObject*> promise(cx, &promiseObject->as<PromiseObject>());
   1300  suspenderObject->setPromisingPromise(promise);
   1301  return promise.get();
   1302 }
   1303 
   1304 // Converts promise results into actual function result, or exception/trap
   1305 // if rejected.
   1306 // Seen as $builtin.get-suspending-promise-result to wasm.
   1307 JSObject* GetSuspendingPromiseResult(Instance* instance, void* result,
   1308                                     SuspenderObject* suspender) {
   1309  MOZ_ASSERT(SASigGetSuspendingPromiseResult.failureMode ==
   1310             FailureMode::FailOnNullPtr);
   1311  JSContext* cx = instance->cx();
   1312  Rooted<SuspenderObject*> suspenderObject(cx, suspender);
   1313  RootedAnyRef resultRef(cx, AnyRef::fromCompiledCode(result));
   1314 
   1315  SuspenderObject::ReturnType returnType =
   1316      suspenderObject->suspendingReturnType();
   1317  MOZ_ASSERT(returnType != SuspenderObject::ReturnType::Unknown);
   1318  Rooted<PromiseObject*> promise(
   1319      cx, returnType == SuspenderObject::ReturnType::Promise
   1320              ? &resultRef.toJSObject().as<PromiseObject>()
   1321              : nullptr);
   1322 
   1323 #  ifdef DEBUG
   1324  auto resetReturnType = mozilla::MakeScopeExit([&suspenderObject]() {
   1325    suspenderObject->setSuspendingReturnType(
   1326        SuspenderObject::ReturnType::Unknown);
   1327  });
   1328 #  endif
   1329 
   1330  if (promise ? promise->state() == JS::PromiseState::Rejected
   1331              : returnType == SuspenderObject::ReturnType::Exception) {
   1332    // Promise was rejected or an exception was thrown, set pending exception
   1333    // and fail.
   1334    RootedValue reason(
   1335        cx, promise ? promise->reason() : resultRef.get().toJSValue());
   1336    cx->setPendingException(reason, ShouldCaptureStack::Maybe);
   1337    return nullptr;
   1338  }
   1339 
   1340  // The exception and rejection are handled above -- expect resolved promise.
   1341  MOZ_ASSERT(promise->state() == JS::PromiseState::Fulfilled);
   1342  RootedValue jsValue(cx, promise->value());
   1343 
   1344  // Construct the results object.
   1345  Rooted<WasmStructObject*> results(
   1346      cx, instance->constantStructNewDefault(
   1347              cx, SuspendingFunctionModuleFactory::ResultsTypeIndex));
   1348  const FieldTypeVector& fields = results->typeDef().structType().fields_;
   1349 
   1350  if (fields.length() > 0) {
   1351    // The struct object is constructed based on returns of exported function.
   1352    // It is the only way we can get ValType for Val::fromJSValue call.
   1353    const wasm::FuncType& sig = instance->codeMeta().getFuncType(
   1354        SuspendingFunctionModuleFactory::ExportedFnIndex);
   1355 
   1356    if (fields.length() == 1) {
   1357      RootedVal val(cx);
   1358      MOZ_ASSERT(sig.result(0).storageType() == fields[0].type);
   1359      if (!Val::fromJSValue(cx, sig.result(0), jsValue, &val)) {
   1360        return nullptr;
   1361      }
   1362      results->storeVal(val, 0);
   1363    } else {
   1364      // The multi-value result is wrapped into ArrayObject/Iterable.
   1365      Rooted<ArrayObject*> array(cx);
   1366      if (!IterableToArray(cx, jsValue, &array)) {
   1367        return nullptr;
   1368      }
   1369      if (fields.length() != array->length()) {
   1370        UniqueChars expected(JS_smprintf("%zu", fields.length()));
   1371        UniqueChars got(JS_smprintf("%u", array->length()));
   1372        if (!expected || !got) {
   1373          ReportOutOfMemory(cx);
   1374          return nullptr;
   1375        }
   1376 
   1377        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1378                                 JSMSG_WASM_WRONG_NUMBER_OF_VALUES,
   1379                                 expected.get(), got.get());
   1380        return nullptr;
   1381      }
   1382 
   1383      for (size_t i = 0; i < fields.length(); i++) {
   1384        RootedVal val(cx);
   1385        RootedValue v(cx, array->getDenseElement(i));
   1386        MOZ_ASSERT(sig.result(i).storageType() == fields[i].type);
   1387        if (!Val::fromJSValue(cx, sig.result(i), v, &val)) {
   1388          return nullptr;
   1389        }
   1390        results->storeVal(val, i);
   1391      }
   1392    }
   1393  }
   1394  return results;
   1395 }
   1396 
   1397 // Collects returned suspending promising, and registers callbacks to
   1398 // react on it using WasmPISuspendTaskContinue.
   1399 // Seen as $builtin.add-promise-reactions to wasm.
   1400 void* AddPromiseReactions(Instance* instance, SuspenderObject* suspender,
   1401                          void* result, JSFunction* continueOnSuspendable) {
   1402  MOZ_ASSERT(SASigAddPromiseReactions.failureMode ==
   1403             FailureMode::FailOnInvalidRef);
   1404  JSContext* cx = instance->cx();
   1405 
   1406  RootedAnyRef resultRef(cx, AnyRef::fromCompiledCode(result));
   1407  RootedValue resultValue(cx, resultRef.get().toJSValue());
   1408  Rooted<SuspenderObject*> suspenderObject(cx, suspender);
   1409  RootedFunction fn(cx, continueOnSuspendable);
   1410 
   1411  // Wrap a promise.
   1412  RootedObject promiseConstructor(cx, GetPromiseConstructor(cx));
   1413  RootedObject promiseObj(cx,
   1414                          PromiseResolve(cx, promiseConstructor, resultValue));
   1415  if (!promiseObj) {
   1416    return AnyRef::invalid().forCompiledCode();
   1417  }
   1418  Rooted<PromiseObject*> promiseObject(cx, &promiseObj->as<PromiseObject>());
   1419 
   1420  suspenderObject->setSuspendingReturnType(
   1421      SuspenderObject::ReturnType::Promise);
   1422 
   1423  // Add promise reactions
   1424  RootedFunction then_(
   1425      cx, NewNativeFunction(cx, WasmPISuspendTaskContinue, 1, nullptr,
   1426                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
   1427  then_->initExtendedSlot(SUSPENDER_SLOT, ObjectValue(*suspenderObject));
   1428  then_->initExtendedSlot(CONTINUE_ON_SUSPENDABLE_SLOT, ObjectValue(*fn));
   1429  then_->initExtendedSlot(PROMISE_SLOT, ObjectValue(*promiseObject));
   1430  if (!JS::AddPromiseReactions(cx, promiseObject, then_, then_)) {
   1431    return AnyRef::invalid().forCompiledCode();
   1432  }
   1433  return AnyRef::fromJSObject(*promiseObject).forCompiledCode();
   1434 }
   1435 
   1436 // Changes exit stack frame pointers to suspendable stack and recast exception
   1437 // to wasm reference. Seen as $builtin.forward-exn-to-suspended to wasm.
   1438 void* ForwardExceptionToSuspended(Instance* instance,
   1439                                  SuspenderObject* suspender, void* exception) {
   1440  MOZ_ASSERT(SASigForwardExceptionToSuspended.failureMode ==
   1441             FailureMode::Infallible);
   1442 
   1443  suspender->forwardToSuspendable();
   1444  suspender->setSuspendingReturnType(SuspenderObject::ReturnType::Exception);
   1445  return exception;
   1446 }
   1447 
   1448 // Resolves the promise using results packed by wasm.
   1449 // Seen as $builtin.set-promising-promise-results to wasm.
   1450 int32_t SetPromisingPromiseResults(Instance* instance,
   1451                                   SuspenderObject* suspender,
   1452                                   WasmStructObject* results) {
   1453  MOZ_ASSERT(SASigSetPromisingPromiseResults.failureMode ==
   1454             FailureMode::FailOnNegI32);
   1455  JSContext* cx = instance->cx();
   1456  Rooted<WasmStructObject*> res(cx, results);
   1457  Rooted<SuspenderObject*> suspenderObject(cx, suspender);
   1458  RootedObject promise(cx, suspenderObject->promisingPromise());
   1459 
   1460  const StructType& resultType = res->typeDef().structType();
   1461  RootedValue val(cx);
   1462  // Unbox the result value from the struct, if any.
   1463  switch (resultType.fields_.length()) {
   1464    case 0:
   1465      break;
   1466    case 1: {
   1467      if (!res->getField(cx, /*index=*/0, &val)) {
   1468        return false;
   1469      }
   1470    } break;
   1471    default: {
   1472      Rooted<ArrayObject*> array(cx, NewDenseEmptyArray(cx));
   1473      if (!array) {
   1474        return false;
   1475      }
   1476      for (size_t i = 0; i < resultType.fields_.length(); i++) {
   1477        RootedValue item(cx);
   1478        if (!res->getField(cx, i, &item)) {
   1479          return false;
   1480        }
   1481        if (!NewbornArrayPush(cx, array, item)) {
   1482          return false;
   1483        }
   1484      }
   1485      val.setObject(*array);
   1486    } break;
   1487  }
   1488  ResolvePromise(cx, promise, val);
   1489  return 0;
   1490 }
   1491 
   1492 void UpdateSuspenderState(Instance* instance, SuspenderObject* suspender,
   1493                          UpdateSuspenderStateAction action) {
   1494  MOZ_ASSERT(SASigUpdateSuspenderState.failureMode == FailureMode::Infallible);
   1495 
   1496  JSContext* cx = instance->cx();
   1497  switch (action) {
   1498    case UpdateSuspenderStateAction::Enter:
   1499      suspender->enter(cx);
   1500      break;
   1501    case UpdateSuspenderStateAction::Suspend:
   1502      suspender->suspend(cx);
   1503      break;
   1504    case UpdateSuspenderStateAction::Resume:
   1505      suspender->resume(cx);
   1506      break;
   1507    case UpdateSuspenderStateAction::Leave:
   1508      suspender->leave(cx);
   1509      break;
   1510    default:
   1511      MOZ_CRASH();
   1512  }
   1513 }
   1514 
   1515 }  // namespace js::wasm
   1516 #endif  // ENABLE_WASM_JSPI