tor-browser

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

WasmJS.cpp (178281B)


      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/WasmJS.h"
     20 
     21 #include "mozilla/Maybe.h"
     22 
     23 #include <algorithm>
     24 #include <cstdint>
     25 
     26 #include "jsapi.h"
     27 #include "jsexn.h"
     28 
     29 #include "ds/IdValuePair.h"            // js::IdValuePair
     30 #include "frontend/FrontendContext.h"  // AutoReportFrontendContext
     31 #include "gc/GCContext.h"
     32 #include "jit/AtomicOperations.h"
     33 #include "jit/FlushICache.h"
     34 #include "jit/JitContext.h"
     35 #include "jit/JitOptions.h"
     36 #include "jit/Simulator.h"
     37 #include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin
     38 #include "js/ForOfIterator.h"
     39 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     40 #include "js/Printf.h"
     41 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetProperty
     42 #include "js/PropertySpec.h"        // JS_{PS,FN}{,_END}
     43 #include "js/Stack.h"               // BuildStackString
     44 #include "js/StreamConsumer.h"
     45 #include "util/StringBuilder.h"
     46 #include "util/Text.h"
     47 #include "vm/ErrorObject.h"
     48 #include "vm/FunctionFlags.h"      // js::FunctionFlags
     49 #include "vm/GlobalObject.h"       // js::GlobalObject
     50 #include "vm/HelperThreadState.h"  // js::PromiseHelperTask
     51 #include "vm/Interpreter.h"
     52 #include "vm/JSFunction.h"
     53 #include "vm/PlainObject.h"    // js::PlainObject
     54 #include "vm/PromiseObject.h"  // js::PromiseObject
     55 #include "vm/SharedArrayObject.h"
     56 #include "vm/StringType.h"
     57 #include "vm/Warnings.h"  // js::WarnNumberASCII
     58 #include "wasm/WasmBaselineCompile.h"
     59 #include "wasm/WasmBuiltinModule.h"
     60 #include "wasm/WasmBuiltins.h"
     61 #include "wasm/WasmCompile.h"
     62 #include "wasm/WasmDebug.h"
     63 #include "wasm/WasmFeatures.h"
     64 #include "wasm/WasmInstance.h"
     65 #include "wasm/WasmIonCompile.h"
     66 #include "wasm/WasmMemory.h"
     67 #include "wasm/WasmModule.h"
     68 #include "wasm/WasmPI.h"
     69 #include "wasm/WasmProcess.h"
     70 #include "wasm/WasmSignalHandlers.h"
     71 #include "wasm/WasmStubs.h"
     72 #include "wasm/WasmValidate.h"
     73 
     74 #include "gc/GCContext-inl.h"
     75 #include "gc/StableCellHasher-inl.h"
     76 #include "vm/ArrayBufferObject-inl.h"
     77 #include "vm/JSObject-inl.h"
     78 #include "vm/NativeObject-inl.h"
     79 #include "wasm/WasmInstance-inl.h"
     80 
     81 /*
     82 * [SMDOC] WebAssembly code rules (evolving)
     83 *
     84 * TlsContext.get() is only to be invoked from functions that have been invoked
     85 *   _directly_ by generated code as cold(!) Builtin calls, from code that is
     86 *   only used by signal handlers, or from helper functions that have been
     87 *   called _directly_ from a simulator.  All other code shall pass in a
     88 *   JSContext* to functions that need it, or an Instance* or Instance* since
     89 * the context is available through them.
     90 *
     91 *   Code that uses TlsContext.get() shall annotate each such call with the
     92 *   reason why the call is OK.
     93 */
     94 
     95 using namespace js;
     96 using namespace js::jit;
     97 using namespace js::wasm;
     98 
     99 using mozilla::Maybe;
    100 using mozilla::Nothing;
    101 using mozilla::Some;
    102 using mozilla::Span;
    103 
    104 static bool ThrowCompileOutOfMemory(JSContext* cx) {
    105  // Most OOMs during compilation are due to large contiguous allocations,
    106  // and future allocations are likely to succeed. Throwing a proper error
    107  // object is nicer for users in these circumstances.
    108  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
    109  return false;
    110 }
    111 
    112 // ============================================================================
    113 // Imports
    114 
    115 static bool ThrowBadImportArg(JSContext* cx) {
    116  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    117                           JSMSG_WASM_BAD_IMPORT_ARG);
    118  return false;
    119 }
    120 
    121 static bool ThrowBadImportType(JSContext* cx, const CacheableName& field,
    122                               const char* str) {
    123  UniqueChars fieldQuoted = field.toQuotedString(cx);
    124  if (!fieldQuoted) {
    125    ReportOutOfMemory(cx);
    126    return false;
    127  }
    128  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    129                           JSMSG_WASM_BAD_IMPORT_TYPE, fieldQuoted.get(), str);
    130  return false;
    131 }
    132 
    133 // For now reject cross-compartment wrappers. These have more complicated realm
    134 // semantics (we use nonCCWRealm in a few places) and may require unwrapping to
    135 // test for specific function types.
    136 static bool IsCallableNonCCW(const Value& v) {
    137  return IsCallable(v) && !IsCrossCompartmentWrapper(&v.toObject());
    138 }
    139 
    140 static bool IsWasmSuspendingWrapper(const Value& v) {
    141  return v.isObject() && js::IsWasmSuspendingObject(&v.toObject());
    142 }
    143 
    144 bool js::wasm::GetImports(JSContext* cx, const Module& module,
    145                          HandleObject importObj, ImportValues* imports) {
    146  const ModuleMetadata& moduleMeta = module.moduleMeta();
    147  const CodeMetadata& codeMeta = module.codeMeta();
    148  const BuiltinModuleIds& builtinModules = codeMeta.features().builtinModules;
    149 
    150  if (!moduleMeta.imports.empty() && !importObj) {
    151    return ThrowBadImportArg(cx);
    152  }
    153 
    154  BuiltinModuleInstances builtinInstances(cx);
    155  RootedValue importModuleValue(cx);
    156  RootedObject importModuleObject(cx);
    157  bool isImportedStringModule = false;
    158  RootedValue importFieldValue(cx);
    159 
    160  uint32_t tagIndex = 0;
    161  const TagDescVector& tags = codeMeta.tags;
    162  uint32_t globalIndex = 0;
    163  const GlobalDescVector& globals = codeMeta.globals;
    164  uint32_t tableIndex = 0;
    165  const TableDescVector& tables = codeMeta.tables;
    166  for (const Import& import : moduleMeta.imports) {
    167    Maybe<BuiltinModuleId> builtinModule =
    168        ImportMatchesBuiltinModule(import.module.utf8Bytes(), builtinModules);
    169    if (builtinModule) {
    170      if (*builtinModule == BuiltinModuleId::JSStringConstants) {
    171        isImportedStringModule = true;
    172        importModuleObject = nullptr;
    173      } else {
    174        MutableHandle<JSObject*> builtinInstance =
    175            builtinInstances[*builtinModule];
    176 
    177        // If this module has not been instantiated yet, do so now.
    178        if (!builtinInstance) {
    179          // Use the first imported memory, if it exists, when compiling the
    180          // builtin module.
    181          const Import* firstMemoryImport =
    182              (codeMeta.memories.empty() ||
    183               codeMeta.memories[0].importIndex.isNothing())
    184                  ? nullptr
    185                  : &moduleMeta.imports[*codeMeta.memories[0].importIndex];
    186 
    187          // Compile and instantiate the builtin module. This uses our module's
    188          // importObj so that it can read the memory import that we provided
    189          // above.
    190          if (!wasm::InstantiateBuiltinModule(cx, *builtinModule,
    191                                              firstMemoryImport, importObj,
    192                                              builtinInstance)) {
    193            return false;
    194          }
    195        }
    196        isImportedStringModule = false;
    197        importModuleObject = builtinInstance;
    198      }
    199    } else {
    200      RootedId moduleName(cx);
    201      if (!import.module.toPropertyKey(cx, &moduleName)) {
    202        return false;
    203      }
    204 
    205      if (!GetProperty(cx, importObj, importObj, moduleName,
    206                       &importModuleValue)) {
    207        return false;
    208      }
    209 
    210      if (!importModuleValue.isObject()) {
    211        UniqueChars moduleQuoted = import.module.toQuotedString(cx);
    212        if (!moduleQuoted) {
    213          ReportOutOfMemory(cx);
    214          return false;
    215        }
    216        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    217                                 JSMSG_WASM_BAD_IMPORT_FIELD,
    218                                 moduleQuoted.get());
    219        return false;
    220      }
    221 
    222      isImportedStringModule = false;
    223      importModuleObject = &importModuleValue.toObject();
    224    }
    225    MOZ_RELEASE_ASSERT(!isImportedStringModule ||
    226                       import.kind == DefinitionKind::Global);
    227 
    228    if (isImportedStringModule) {
    229      RootedString stringConstant(cx, import.field.toJSString(cx));
    230      if (!stringConstant) {
    231        ReportOutOfMemory(cx);
    232        return false;
    233      }
    234      importFieldValue = StringValue(stringConstant);
    235    } else {
    236      RootedId fieldName(cx);
    237      if (!import.field.toPropertyKey(cx, &fieldName)) {
    238        return false;
    239      }
    240      if (!GetProperty(cx, importModuleObject, importModuleObject, fieldName,
    241                       &importFieldValue)) {
    242        return false;
    243      }
    244    }
    245 
    246    switch (import.kind) {
    247      case DefinitionKind::Function: {
    248        if (!IsCallableNonCCW(importFieldValue) &&
    249            !IsWasmSuspendingWrapper(importFieldValue)) {
    250          return ThrowBadImportType(cx, import.field, "Function");
    251        }
    252 
    253        if (!imports->funcs.append(&importFieldValue.toObject())) {
    254          ReportOutOfMemory(cx);
    255          return false;
    256        }
    257 
    258        break;
    259      }
    260      case DefinitionKind::Table: {
    261        const uint32_t index = tableIndex++;
    262        if (!importFieldValue.isObject() ||
    263            !importFieldValue.toObject().is<WasmTableObject>()) {
    264          return ThrowBadImportType(cx, import.field, "Table");
    265        }
    266 
    267        Rooted<WasmTableObject*> obj(
    268            cx, &importFieldValue.toObject().as<WasmTableObject>());
    269        if (obj->table().elemType() != tables[index].elemType) {
    270          JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    271                                   JSMSG_WASM_BAD_TBL_TYPE_LINK);
    272          return false;
    273        }
    274 
    275        if (!imports->tables.append(obj)) {
    276          ReportOutOfMemory(cx);
    277          return false;
    278        }
    279        break;
    280      }
    281      case DefinitionKind::Memory: {
    282        if (!importFieldValue.isObject() ||
    283            !importFieldValue.toObject().is<WasmMemoryObject>()) {
    284          return ThrowBadImportType(cx, import.field, "Memory");
    285        }
    286 
    287        if (!imports->memories.append(
    288                &importFieldValue.toObject().as<WasmMemoryObject>())) {
    289          ReportOutOfMemory(cx);
    290          return false;
    291        }
    292        break;
    293      }
    294      case DefinitionKind::Tag: {
    295        const uint32_t index = tagIndex++;
    296        if (!importFieldValue.isObject() ||
    297            !importFieldValue.toObject().is<WasmTagObject>()) {
    298          return ThrowBadImportType(cx, import.field, "Tag");
    299        }
    300 
    301        Rooted<WasmTagObject*> obj(
    302            cx, &importFieldValue.toObject().as<WasmTagObject>());
    303 
    304        // Checks whether the signature of the imported exception object matches
    305        // the signature declared in the exception import's TagDesc.
    306        if (!TagType::matches(*obj->tagType(), *tags[index].type)) {
    307          UniqueChars fieldQuoted = import.field.toQuotedString(cx);
    308          UniqueChars moduleQuoted = import.module.toQuotedString(cx);
    309          if (!fieldQuoted || !moduleQuoted) {
    310            ReportOutOfMemory(cx);
    311            return false;
    312          }
    313          JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    314                                   JSMSG_WASM_BAD_TAG_SIG, moduleQuoted.get(),
    315                                   fieldQuoted.get());
    316          return false;
    317        }
    318 
    319        if (!imports->tagObjs.append(obj)) {
    320          ReportOutOfMemory(cx);
    321          return false;
    322        }
    323        break;
    324      }
    325      case DefinitionKind::Global: {
    326        const uint32_t index = globalIndex++;
    327        const GlobalDesc& global = globals[index];
    328        MOZ_ASSERT(global.importIndex() == index);
    329 
    330        RootedVal val(cx);
    331        if (importFieldValue.isObject() &&
    332            importFieldValue.toObject().is<WasmGlobalObject>()) {
    333          Rooted<WasmGlobalObject*> obj(
    334              cx, &importFieldValue.toObject().as<WasmGlobalObject>());
    335 
    336          if (obj->isMutable() != global.isMutable()) {
    337            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    338                                     JSMSG_WASM_BAD_GLOB_MUT_LINK);
    339            return false;
    340          }
    341 
    342          bool matches = global.isMutable()
    343                             ? obj->type() == global.type()
    344                             : ValType::isSubTypeOf(obj->type(), global.type());
    345          if (!matches) {
    346            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    347                                     JSMSG_WASM_BAD_GLOB_TYPE_LINK);
    348            return false;
    349          }
    350 
    351          if (imports->globalObjs.length() <= index &&
    352              !imports->globalObjs.resize(index + 1)) {
    353            ReportOutOfMemory(cx);
    354            return false;
    355          }
    356          imports->globalObjs[index] = obj;
    357          val = obj->val();
    358        } else {
    359          if (!global.type().isRefType()) {
    360            if (global.type() == ValType::I64 && !importFieldValue.isBigInt()) {
    361              return ThrowBadImportType(cx, import.field, "BigInt");
    362            }
    363            if (global.type() != ValType::I64 && !importFieldValue.isNumber()) {
    364              return ThrowBadImportType(cx, import.field, "Number");
    365            }
    366          }
    367 
    368          if (global.isMutable()) {
    369            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    370                                     JSMSG_WASM_BAD_GLOB_MUT_LINK);
    371            return false;
    372          }
    373 
    374          if (!Val::fromJSValue(cx, global.type(), importFieldValue, &val)) {
    375            return false;
    376          }
    377        }
    378 
    379        if (!imports->globalValues.append(val)) {
    380          ReportOutOfMemory(cx);
    381          return false;
    382        }
    383 
    384        break;
    385      }
    386    }
    387  }
    388 
    389  MOZ_ASSERT(globalIndex == globals.length() ||
    390             !globals[globalIndex].isImport());
    391 
    392  return true;
    393 }
    394 
    395 static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller,
    396                                   const char* introducer) {
    397  // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
    398  // found, not whether an error was thrown. This wrapper function converts
    399  // back to the more ordinary false-if-error form.
    400 
    401  JS::AutoFilename af;
    402  if (JS::DescribeScriptedCaller(&af, cx, &caller->line)) {
    403    caller->filename =
    404        FormatIntroducedFilename(af.get(), caller->line, introducer);
    405    if (!caller->filename) {
    406      ReportOutOfMemory(cx);
    407      return false;
    408    }
    409  }
    410 
    411  return true;
    412 }
    413 
    414 static SharedCompileArgs InitCompileArgs(JSContext* cx,
    415                                         const FeatureOptions& options,
    416                                         const char* introducer) {
    417  ScriptedCaller scriptedCaller;
    418  if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
    419    return nullptr;
    420  }
    421 
    422  return CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
    423 }
    424 
    425 // ============================================================================
    426 // Testing / Fuzzing support
    427 
    428 bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code,
    429                HandleObject importObj,
    430                MutableHandle<WasmInstanceObject*> instanceObj) {
    431  if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
    432    return false;
    433  }
    434 
    435  FeatureOptions options;
    436  SharedCompileArgs compileArgs = InitCompileArgs(cx, options, "wasm_eval");
    437  if (!compileArgs) {
    438    return false;
    439  }
    440 
    441  BytecodeSource source((uint8_t*)code->dataPointerEither().unwrap(),
    442                        code->byteLength().valueOr(0));
    443  UniqueChars error;
    444  UniqueCharsVector warnings;
    445  SharedModule module = CompileBuffer(
    446      *compileArgs, BytecodeBufferOrSource(source), &error, &warnings, nullptr);
    447  if (!module) {
    448    if (error) {
    449      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    450                               JSMSG_WASM_COMPILE_ERROR, error.get());
    451      return false;
    452    }
    453    return ThrowCompileOutOfMemory(cx);
    454  }
    455 
    456  Rooted<ImportValues> imports(cx);
    457  if (!GetImports(cx, *module, importObj, imports.address())) {
    458    return false;
    459  }
    460 
    461  return module->instantiate(cx, imports.get(), nullptr, instanceObj);
    462 }
    463 
    464 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
    465  // MOZ_STACK_CLASS means these can be nops.
    466  MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
    467  MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
    468 
    469  mozilla::DebugOnly<bool> called = false;
    470  Bytes* serialized;
    471  explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
    472 
    473  void storeOptimizedEncoding(const uint8_t* bytes, size_t length) override {
    474    MOZ_ASSERT(!called);
    475    called = true;
    476    if (serialized->resizeUninitialized(length)) {
    477      memcpy(serialized->begin(), bytes, length);
    478    }
    479  }
    480 };
    481 
    482 bool wasm::CompileAndSerialize(JSContext* cx,
    483                               const BytecodeSource& bytecodeSource,
    484                               Bytes* serialized) {
    485  // The caller must check that code caching is available
    486  MOZ_ASSERT(CodeCachingAvailable(cx));
    487 
    488  // Create and manually fill in compile args for code caching
    489  MutableCompileArgs compileArgs = js_new<CompileArgs>();
    490  if (!compileArgs) {
    491    return false;
    492  }
    493 
    494  // The caller has ensured CodeCachingAvailable(). Moreover, we want to ensure
    495  // we go straight to tier-2 so that we synchronously call
    496  // JS::OptimizedEncodingListener::storeOptimizedEncoding().
    497  compileArgs->baselineEnabled = false;
    498  compileArgs->forceTiering = false;
    499 
    500  // We always pick Ion here, and we depend on CodeCachingAvailable() having
    501  // determined that Ion is available, see comments at CodeCachingAvailable().
    502  // To do better, we need to pass information about which compiler that should
    503  // be used into CompileAndSerialize().
    504  compileArgs->ionEnabled = true;
    505 
    506  // Select features that are enabled. This is guaranteed to be consistent with
    507  // our compiler selection, as code caching is only available if ion is
    508  // available, and ion is only available if it's not disabled by enabled
    509  // features.
    510  compileArgs->features = FeatureArgs::build(cx, FeatureOptions());
    511 
    512  SerializeListener listener(serialized);
    513 
    514  UniqueChars error;
    515  UniqueCharsVector warnings;
    516  SharedModule module =
    517      CompileBuffer(*compileArgs, BytecodeBufferOrSource(bytecodeSource),
    518                    &error, &warnings, &listener);
    519  if (!module) {
    520    fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
    521    return false;
    522  }
    523 
    524  MOZ_ASSERT(module->code().hasCompleteTier(Tier::Serialized));
    525  MOZ_ASSERT(listener.called);
    526  return !listener.serialized->empty();
    527 }
    528 
    529 bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized,
    530                             MutableHandleObject moduleObj) {
    531  MutableModule module =
    532      Module::deserialize(serialized.begin(), serialized.length());
    533  if (!module) {
    534    ReportOutOfMemory(cx);
    535    return false;
    536  }
    537 
    538  moduleObj.set(module->createObject(cx));
    539  return !!moduleObj;
    540 }
    541 
    542 // ============================================================================
    543 // Common functions
    544 
    545 // '[EnforceRange] unsigned long' types are coerced with
    546 //    ConvertToInt(v, 32, 'unsigned')
    547 // defined in Web IDL Section 3.2.4.9.
    548 //
    549 // This just generalizes that to an arbitrary limit that is representable as an
    550 // integer in double form.
    551 
    552 static bool EnforceRange(JSContext* cx, HandleValue v, const char* kind,
    553                         const char* noun, uint64_t max, uint64_t* val) {
    554  // Step 4.
    555  double x;
    556  if (!ToNumber(cx, v, &x)) {
    557    return false;
    558  }
    559 
    560  // Step 5.
    561  if (mozilla::IsNegativeZero(x)) {
    562    x = 0.0;
    563  }
    564 
    565  // Step 6.1.
    566  if (!std::isfinite(x)) {
    567    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    568                             JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun);
    569    return false;
    570  }
    571 
    572  // Step 6.2.
    573  x = JS::ToInteger(x);
    574 
    575  // Step 6.3.
    576  if (x < 0 || x > double(max)) {
    577    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    578                             JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun);
    579    return false;
    580  }
    581 
    582  *val = uint64_t(x);
    583  MOZ_ASSERT(double(*val) == x);
    584  return true;
    585 }
    586 
    587 static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
    588                            const char* noun, uint32_t* u32) {
    589  uint64_t u64 = 0;
    590  if (!EnforceRange(cx, v, kind, noun, uint64_t(UINT32_MAX), &u64)) {
    591    return false;
    592  }
    593  *u32 = uint32_t(u64);
    594  return true;
    595 }
    596 
    597 static bool EnforceRangeU64(JSContext* cx, HandleValue v, const char* kind,
    598                            const char* noun, uint64_t* u64) {
    599  // The max is Number.MAX_SAFE_INTEGER
    600  return EnforceRange(cx, v, kind, noun, (1LL << 53) - 1, u64);
    601 }
    602 
    603 static bool EnforceRangeBigInt64(JSContext* cx, HandleValue v, const char* kind,
    604                                 const char* noun, uint64_t* u64) {
    605  RootedBigInt bi(cx, ToBigInt(cx, v));
    606  if (!bi) {
    607    return false;
    608  }
    609  if (!BigInt::isUint64(bi, u64)) {
    610    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    611                             JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun);
    612    return false;
    613  }
    614  return true;
    615 }
    616 
    617 static bool EnforceAddressValue(JSContext* cx, HandleValue v,
    618                                AddressType addressType, const char* kind,
    619                                const char* noun, uint64_t* result) {
    620  switch (addressType) {
    621    case AddressType::I32: {
    622      uint32_t result32;
    623      if (!EnforceRangeU32(cx, v, kind, noun, &result32)) {
    624        return false;
    625      }
    626      *result = uint64_t(result32);
    627      return true;
    628    }
    629    case AddressType::I64:
    630      return EnforceRangeBigInt64(cx, v, kind, noun, result);
    631    default:
    632      MOZ_CRASH("unknown address type");
    633  }
    634 }
    635 
    636 // The AddressValue typedef, a union of number and bigint, is used in the JS API
    637 // spec for memory and table arguments, where number is used for memory32 and
    638 // bigint is used for memory64.
    639 [[nodiscard]] static bool CreateAddressValue(JSContext* cx, uint64_t value,
    640                                             AddressType addressType,
    641                                             MutableHandleValue addressValue) {
    642  switch (addressType) {
    643    case AddressType::I32:
    644      MOZ_ASSERT(value <= UINT32_MAX);
    645      addressValue.set(NumberValue(value));
    646      return true;
    647    case AddressType::I64: {
    648      BigInt* bi = BigInt::createFromUint64(cx, value);
    649      if (!bi) {
    650        return false;
    651      }
    652      addressValue.set(BigIntValue(bi));
    653      return true;
    654    }
    655    default:
    656      MOZ_CRASH("unknown address type");
    657  }
    658 }
    659 
    660 // Gets an AddressValue property ("initial" or "maximum") from a
    661 // MemoryDescriptor or TableDescriptor. The values returned by this should be
    662 // run through CheckLimits to enforce the validation limits prescribed by the
    663 // spec.
    664 static bool GetDescriptorAddressValue(JSContext* cx, HandleObject obj,
    665                                      const char* name, const char* noun,
    666                                      const char* msg, AddressType addressType,
    667                                      bool* found, uint64_t* value) {
    668  JSAtom* atom = Atomize(cx, name, strlen(name));
    669  if (!atom) {
    670    return false;
    671  }
    672  RootedId id(cx, AtomToId(atom));
    673 
    674  RootedValue val(cx);
    675  if (!GetProperty(cx, obj, obj, id, &val)) {
    676    return false;
    677  }
    678 
    679  if (val.isUndefined()) {
    680    *found = false;
    681    return true;
    682  }
    683  *found = true;
    684 
    685  return EnforceAddressValue(cx, val, addressType, noun, msg, value);
    686 }
    687 
    688 static bool GetLimits(JSContext* cx, HandleObject obj, LimitsKind kind,
    689                      Limits* limits) {
    690  limits->addressType = AddressType::I32;
    691 
    692  // Limits may specify an alternate address type, and we need this to check the
    693  // ranges for initial and maximum, so look for the address type first.
    694  // Get the address type field
    695  JSAtom* addressTypeAtom = Atomize(cx, "address", strlen("address"));
    696  if (!addressTypeAtom) {
    697    return false;
    698  }
    699  RootedId addressTypeId(cx, AtomToId(addressTypeAtom));
    700  RootedValue addressTypeVal(cx);
    701  if (!GetProperty(cx, obj, obj, addressTypeId, &addressTypeVal)) {
    702    return false;
    703  }
    704 
    705  // The address type has a default value
    706  if (!addressTypeVal.isUndefined()) {
    707    if (!ToAddressType(cx, addressTypeVal, &limits->addressType)) {
    708      return false;
    709    }
    710  }
    711 
    712  const char* noun = ToString(kind);
    713  uint64_t limit = 0;
    714 
    715  bool haveInitial = false;
    716  if (!GetDescriptorAddressValue(cx, obj, "initial", noun, "initial size",
    717                                 limits->addressType, &haveInitial, &limit)) {
    718    return false;
    719  }
    720  if (haveInitial) {
    721    limits->initial = limit;
    722  }
    723 
    724  bool haveMinimum = false;
    725 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
    726  if (!GetDescriptorAddressValue(cx, obj, "minimum", noun, "initial size",
    727                                 limits->addressType, &haveMinimum, &limit)) {
    728    return false;
    729  }
    730  if (haveMinimum) {
    731    limits->initial = limit;
    732  }
    733 #endif
    734 
    735  if (!(haveInitial || haveMinimum)) {
    736    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    737                             JSMSG_WASM_MISSING_REQUIRED, "initial");
    738    return false;
    739  }
    740  if (haveInitial && haveMinimum) {
    741    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    742                             JSMSG_WASM_SUPPLY_ONLY_ONE, "minimum", "initial");
    743    return false;
    744  }
    745 
    746  bool haveMaximum = false;
    747  if (!GetDescriptorAddressValue(cx, obj, "maximum", noun, "maximum size",
    748                                 limits->addressType, &haveMaximum, &limit)) {
    749    return false;
    750  }
    751  if (haveMaximum) {
    752    limits->maximum = Some(limit);
    753  }
    754 
    755  limits->shared = Shareable::False;
    756 
    757  // Memory limits may be shared.
    758  if (kind == LimitsKind::Memory) {
    759    // Get the shared field
    760    JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
    761    if (!sharedAtom) {
    762      return false;
    763    }
    764    RootedId sharedId(cx, AtomToId(sharedAtom));
    765 
    766    RootedValue sharedVal(cx);
    767    if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) {
    768      return false;
    769    }
    770 
    771    // shared's default value is false, which is already the value set above.
    772    if (!sharedVal.isUndefined()) {
    773      limits->shared =
    774          ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
    775 
    776      if (limits->shared == Shareable::True) {
    777        if (!haveMaximum) {
    778          JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    779                                    JSMSG_WASM_MISSING_MAXIMUM, noun);
    780          return false;
    781        }
    782 
    783        if (!cx->realm()
    784                 ->creationOptions()
    785                 .getSharedMemoryAndAtomicsEnabled()) {
    786          JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    787                                    JSMSG_WASM_NO_SHMEM_LINK);
    788          return false;
    789        }
    790      }
    791    }
    792 
    793    // TODO: Should be updated when the JS API for custom page sizes
    794    // is finalized, see https://bugzilla.mozilla.org/show_bug.cgi?id=1985679
    795    limits->pageSize = PageSize::Standard;
    796  }
    797 
    798  return true;
    799 }
    800 
    801 static bool CheckLimits(JSContext* cx, uint64_t validationMax, LimitsKind kind,
    802                        Limits* limits) {
    803  const char* noun = ToString(kind);
    804 
    805  // There are several layers of validation and error-throwing here, including
    806  // one which is currently not defined by the JS API spec:
    807  //
    808  // - [EnforceRange] on parameters (must be TypeError)
    809  // - A check that initial <= maximum (must be RangeError)
    810  // - Either a mem_alloc or table_alloc operation, which has two components:
    811  //   - A pre-condition that the given memory or table type is valid
    812  //     (not specified, RangeError in practice)
    813  //   - The actual allocation (should report OOM if it fails)
    814  //
    815  // There are two questions currently left open by the spec: when is the memory
    816  // or table type validated, and if it is invalid, what type of exception does
    817  // it throw? In practice, all browsers throw RangeError, and by the time you
    818  // read this the spec will hopefully have been updated to reflect this. See
    819  // the following issue: https://github.com/WebAssembly/spec/issues/1792
    820 
    821  // Check that initial <= maximum
    822  if (limits->maximum.isSome() && *limits->maximum < limits->initial) {
    823    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    824                             JSMSG_WASM_MAX_LT_INITIAL, noun);
    825    return false;
    826  }
    827 
    828  // Check wasm validation limits
    829  if (limits->initial > validationMax) {
    830    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
    831                             noun, "initial size");
    832    return false;
    833  }
    834  if (limits->maximum.isSome() && *limits->maximum > validationMax) {
    835    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
    836                             noun, "maximum size");
    837    return false;
    838  }
    839 
    840  return true;
    841 }
    842 
    843 template <class Class, const char* name>
    844 static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
    845  Rooted<JSAtom*> className(cx, Atomize(cx, name, strlen(name)));
    846  if (!className) {
    847    return nullptr;
    848  }
    849 
    850  return NewNativeConstructor(cx, Class::construct, 1, className);
    851 }
    852 
    853 static JSObject* GetWasmConstructorPrototype(JSContext* cx,
    854                                             const CallArgs& callArgs,
    855                                             JSProtoKey key) {
    856  RootedObject proto(cx);
    857  if (!GetPrototypeFromBuiltinConstructor(cx, callArgs, key, &proto)) {
    858    return nullptr;
    859  }
    860  if (!proto) {
    861    proto = GlobalObject::getOrCreatePrototype(cx, key);
    862  }
    863  return proto;
    864 }
    865 
    866 [[nodiscard]] static bool ParseValTypes(JSContext* cx, HandleValue src,
    867                                        ValTypeVector& dest) {
    868  JS::ForOfIterator iterator(cx);
    869 
    870  if (!iterator.init(src, JS::ForOfIterator::ThrowOnNonIterable)) {
    871    return false;
    872  }
    873 
    874  RootedValue nextParam(cx);
    875  while (true) {
    876    bool done;
    877    if (!iterator.next(&nextParam, &done)) {
    878      return false;
    879    }
    880    if (done) {
    881      break;
    882    }
    883 
    884    ValType valType;
    885    if (!ToValType(cx, nextParam, &valType) || !dest.append(valType)) {
    886      return false;
    887    }
    888  }
    889  return true;
    890 }
    891 
    892 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
    893 template <typename T>
    894 static JSString* TypeToString(JSContext* cx, T type) {
    895  UniqueChars chars = ToString(type, nullptr);
    896  if (!chars) {
    897    return nullptr;
    898  }
    899  return NewStringCopyUTF8Z(
    900      cx, JS::ConstUTF8CharsZ(chars.get(), strlen(chars.get())));
    901 }
    902 
    903 static JSString* AddressTypeToString(JSContext* cx, AddressType type) {
    904  return JS_NewStringCopyZ(cx, ToString(type));
    905 }
    906 
    907 [[nodiscard]] static JSObject* ValTypesToArray(JSContext* cx,
    908                                               const ValTypeVector& valTypes) {
    909  Rooted<ArrayObject*> arrayObj(cx, NewDenseEmptyArray(cx));
    910  if (!arrayObj) {
    911    return nullptr;
    912  }
    913  for (ValType valType : valTypes) {
    914    RootedString type(cx, TypeToString(cx, valType));
    915    if (!type) {
    916      return nullptr;
    917    }
    918    if (!NewbornArrayPush(cx, arrayObj, StringValue(type))) {
    919      return nullptr;
    920    }
    921  }
    922  return arrayObj;
    923 }
    924 
    925 static JSObject* FuncTypeToObject(JSContext* cx, const FuncType& type) {
    926  Rooted<IdValueVector> props(cx, IdValueVector(cx));
    927 
    928  RootedObject parametersObj(cx, ValTypesToArray(cx, type.args()));
    929  if (!parametersObj ||
    930      !props.append(IdValuePair(NameToId(cx->names().parameters),
    931                                ObjectValue(*parametersObj)))) {
    932    ReportOutOfMemory(cx);
    933    return nullptr;
    934  }
    935 
    936  RootedObject resultsObj(cx, ValTypesToArray(cx, type.results()));
    937  if (!resultsObj || !props.append(IdValuePair(NameToId(cx->names().results),
    938                                               ObjectValue(*resultsObj)))) {
    939    ReportOutOfMemory(cx);
    940    return nullptr;
    941  }
    942 
    943  return NewPlainObjectWithUniqueNames(cx, props);
    944 }
    945 
    946 static JSObject* TableTypeToObject(JSContext* cx, AddressType addressType,
    947                                   RefType type, uint64_t initial,
    948                                   Maybe<uint64_t> maximum) {
    949  Rooted<IdValueVector> props(cx, IdValueVector(cx));
    950 
    951  RootedString elementType(cx, TypeToString(cx, type));
    952  if (!elementType || !props.append(IdValuePair(NameToId(cx->names().element),
    953                                                StringValue(elementType)))) {
    954    ReportOutOfMemory(cx);
    955    return nullptr;
    956  }
    957 
    958  if (maximum.isSome()) {
    959    RootedId maximumId(cx, NameToId(cx->names().maximum));
    960    RootedValue maximumValue(cx);
    961    if (!CreateAddressValue(cx, maximum.value(), addressType, &maximumValue)) {
    962      ReportOutOfMemory(cx);
    963      return nullptr;
    964    }
    965    if (!props.append(IdValuePair(maximumId, maximumValue))) {
    966      ReportOutOfMemory(cx);
    967      return nullptr;
    968    }
    969  }
    970 
    971  RootedId minimumId(cx, NameToId(cx->names().minimum));
    972  RootedValue minimumValue(cx);
    973  if (!CreateAddressValue(cx, initial, addressType, &minimumValue)) {
    974    ReportOutOfMemory(cx);
    975    return nullptr;
    976  }
    977  if (!props.append(IdValuePair(minimumId, minimumValue))) {
    978    ReportOutOfMemory(cx);
    979    return nullptr;
    980  }
    981 
    982  RootedString at(cx, AddressTypeToString(cx, addressType));
    983  if (!at) {
    984    return nullptr;
    985  }
    986  if (!props.append(
    987          IdValuePair(NameToId(cx->names().address), StringValue(at)))) {
    988    ReportOutOfMemory(cx);
    989    return nullptr;
    990  }
    991 
    992  return NewPlainObjectWithUniqueNames(cx, props);
    993 }
    994 
    995 static JSObject* MemoryTypeToObject(JSContext* cx, bool shared,
    996                                    wasm::AddressType addressType,
    997                                    wasm::Pages minPages,
    998                                    Maybe<wasm::Pages> maxPages) {
    999  Rooted<IdValueVector> props(cx, IdValueVector(cx));
   1000  if (maxPages) {
   1001    RootedId maximumId(cx, NameToId(cx->names().maximum));
   1002    RootedValue maximumValue(cx);
   1003    if (!CreateAddressValue(cx, maxPages.value().pageCount(), addressType,
   1004                            &maximumValue)) {
   1005      ReportOutOfMemory(cx);
   1006      return nullptr;
   1007    }
   1008    if (!props.append(IdValuePair(maximumId, maximumValue))) {
   1009      ReportOutOfMemory(cx);
   1010      return nullptr;
   1011    }
   1012  }
   1013 
   1014  RootedId minimumId(cx, NameToId(cx->names().minimum));
   1015  RootedValue minimumValue(cx);
   1016  if (!CreateAddressValue(cx, minPages.pageCount(), addressType,
   1017                          &minimumValue)) {
   1018    ReportOutOfMemory(cx);
   1019    return nullptr;
   1020  }
   1021  if (!props.append(IdValuePair(minimumId, minimumValue))) {
   1022    ReportOutOfMemory(cx);
   1023    return nullptr;
   1024  }
   1025 
   1026  RootedString at(cx, AddressTypeToString(cx, addressType));
   1027  if (!at) {
   1028    return nullptr;
   1029  }
   1030  if (!props.append(
   1031          IdValuePair(NameToId(cx->names().address), StringValue(at)))) {
   1032    ReportOutOfMemory(cx);
   1033    return nullptr;
   1034  }
   1035 
   1036  if (!props.append(
   1037          IdValuePair(NameToId(cx->names().shared), BooleanValue(shared)))) {
   1038    ReportOutOfMemory(cx);
   1039    return nullptr;
   1040  }
   1041 
   1042  return NewPlainObjectWithUniqueNames(cx, props);
   1043 }
   1044 
   1045 static JSObject* GlobalTypeToObject(JSContext* cx, ValType type,
   1046                                    bool isMutable) {
   1047  Rooted<IdValueVector> props(cx, IdValueVector(cx));
   1048 
   1049  if (!props.append(IdValuePair(NameToId(cx->names().mutable_),
   1050                                BooleanValue(isMutable)))) {
   1051    ReportOutOfMemory(cx);
   1052    return nullptr;
   1053  }
   1054 
   1055  RootedString valueType(cx, TypeToString(cx, type));
   1056  if (!valueType || !props.append(IdValuePair(NameToId(cx->names().value),
   1057                                              StringValue(valueType)))) {
   1058    ReportOutOfMemory(cx);
   1059    return nullptr;
   1060  }
   1061 
   1062  return NewPlainObjectWithUniqueNames(cx, props);
   1063 }
   1064 
   1065 static JSObject* TagTypeToObject(JSContext* cx,
   1066                                 const wasm::ValTypeVector& params) {
   1067  Rooted<IdValueVector> props(cx, IdValueVector(cx));
   1068 
   1069  RootedObject parametersObj(cx, ValTypesToArray(cx, params));
   1070  if (!parametersObj ||
   1071      !props.append(IdValuePair(NameToId(cx->names().parameters),
   1072                                ObjectValue(*parametersObj)))) {
   1073    ReportOutOfMemory(cx);
   1074    return nullptr;
   1075  }
   1076 
   1077  return NewPlainObjectWithUniqueNames(cx, props);
   1078 }
   1079 #endif  // ENABLE_WASM_TYPE_REFLECTIONS
   1080 
   1081 // ============================================================================
   1082 // WebAssembly.Module class and methods
   1083 
   1084 const JSClassOps WasmModuleObject::classOps_ = {
   1085    nullptr,                     // addProperty
   1086    nullptr,                     // delProperty
   1087    nullptr,                     // enumerate
   1088    nullptr,                     // newEnumerate
   1089    nullptr,                     // resolve
   1090    nullptr,                     // mayResolve
   1091    WasmModuleObject::finalize,  // finalize
   1092    nullptr,                     // call
   1093    nullptr,                     // construct
   1094    nullptr,                     // trace
   1095 };
   1096 
   1097 const JSClass WasmModuleObject::class_ = {
   1098    "WebAssembly.Module",
   1099    JSCLASS_DELAY_METADATA_BUILDER |
   1100        JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
   1101        JSCLASS_FOREGROUND_FINALIZE,
   1102    &WasmModuleObject::classOps_,
   1103    &WasmModuleObject::classSpec_,
   1104 };
   1105 
   1106 const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
   1107 
   1108 static constexpr char WasmModuleName[] = "Module";
   1109 
   1110 const ClassSpec WasmModuleObject::classSpec_ = {
   1111    CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
   1112    GenericCreatePrototype<WasmModuleObject>,
   1113    WasmModuleObject::static_methods,
   1114    nullptr,
   1115    WasmModuleObject::methods,
   1116    WasmModuleObject::properties,
   1117    nullptr,
   1118    ClassSpec::DontDefineConstructor,
   1119 };
   1120 
   1121 const JSPropertySpec WasmModuleObject::properties[] = {
   1122    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
   1123    JS_PS_END,
   1124 };
   1125 
   1126 const JSFunctionSpec WasmModuleObject::methods[] = {
   1127    JS_FS_END,
   1128 };
   1129 
   1130 const JSFunctionSpec WasmModuleObject::static_methods[] = {
   1131    JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
   1132    JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
   1133    JS_FN("customSections", WasmModuleObject::customSections, 2,
   1134          JSPROP_ENUMERATE),
   1135    JS_FS_END,
   1136 };
   1137 
   1138 /* static */
   1139 void WasmModuleObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   1140  const Module& module = obj->as<WasmModuleObject>().module();
   1141  size_t codeMemory = module.tier1CodeMemoryUsed();
   1142  if (codeMemory) {
   1143    obj->zone()->decJitMemory(codeMemory);
   1144  }
   1145  gcx->release(obj, &module, module.gcMallocBytesExcludingCode(),
   1146               MemoryUse::WasmModule);
   1147 }
   1148 
   1149 static bool IsModuleObject(JSObject* obj, const Module** module) {
   1150  WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>();
   1151  if (!mobj) {
   1152    return false;
   1153  }
   1154 
   1155  *module = &mobj->module();
   1156  return true;
   1157 }
   1158 
   1159 static bool GetModuleArg(JSContext* cx, const CallArgs& args,
   1160                         uint32_t numRequired, const char* name,
   1161                         const Module** module) {
   1162  if (!args.requireAtLeast(cx, name, numRequired)) {
   1163    return false;
   1164  }
   1165 
   1166  if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
   1167    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1168                             JSMSG_WASM_BAD_MOD_ARG);
   1169    return false;
   1170  }
   1171 
   1172  return true;
   1173 }
   1174 
   1175 struct KindNames {
   1176  Rooted<PropertyName*> kind;
   1177  Rooted<PropertyName*> table;
   1178  Rooted<PropertyName*> memory;
   1179  Rooted<PropertyName*> tag;
   1180  Rooted<PropertyName*> type;
   1181 
   1182  explicit KindNames(JSContext* cx)
   1183      : kind(cx), table(cx), memory(cx), tag(cx), type(cx) {}
   1184 };
   1185 
   1186 static bool InitKindNames(JSContext* cx, KindNames* names) {
   1187  JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
   1188  if (!kind) {
   1189    return false;
   1190  }
   1191  names->kind = kind->asPropertyName();
   1192 
   1193  JSAtom* table = Atomize(cx, "table", strlen("table"));
   1194  if (!table) {
   1195    return false;
   1196  }
   1197  names->table = table->asPropertyName();
   1198 
   1199  JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
   1200  if (!memory) {
   1201    return false;
   1202  }
   1203  names->memory = memory->asPropertyName();
   1204 
   1205  JSAtom* tag = Atomize(cx, "tag", strlen("tag"));
   1206  if (!tag) {
   1207    return false;
   1208  }
   1209  names->tag = tag->asPropertyName();
   1210 
   1211  JSAtom* type = Atomize(cx, "type", strlen("type"));
   1212  if (!type) {
   1213    return false;
   1214  }
   1215  names->type = type->asPropertyName();
   1216 
   1217  return true;
   1218 }
   1219 
   1220 static JSString* KindToString(JSContext* cx, const KindNames& names,
   1221                              DefinitionKind kind) {
   1222  switch (kind) {
   1223    case DefinitionKind::Function:
   1224      return cx->names().function;
   1225    case DefinitionKind::Table:
   1226      return names.table;
   1227    case DefinitionKind::Memory:
   1228      return names.memory;
   1229    case DefinitionKind::Global:
   1230      return cx->names().global;
   1231    case DefinitionKind::Tag:
   1232      return names.tag;
   1233  }
   1234 
   1235  MOZ_CRASH("invalid kind");
   1236 }
   1237 
   1238 /* static */
   1239 bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
   1240  CallArgs args = CallArgsFromVp(argc, vp);
   1241 
   1242  const Module* module;
   1243  if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) {
   1244    return false;
   1245  }
   1246 
   1247  KindNames names(cx);
   1248  if (!InitKindNames(cx, &names)) {
   1249    return false;
   1250  }
   1251 
   1252  const ModuleMetadata& moduleMeta = module->moduleMeta();
   1253 
   1254  RootedValueVector elems(cx);
   1255  if (!elems.reserve(moduleMeta.imports.length())) {
   1256    return false;
   1257  }
   1258 
   1259  const CodeMetadata& codeMeta = module->codeMeta();
   1260 #if defined(ENABLE_WASM_TYPE_REFLECTIONS)
   1261  size_t numFuncImport = 0;
   1262  size_t numMemoryImport = 0;
   1263  size_t numGlobalImport = 0;
   1264  size_t numTableImport = 0;
   1265  size_t numTagImport = 0;
   1266 #endif  // ENABLE_WASM_TYPE_REFLECTIONS
   1267 
   1268  for (const Import& import : moduleMeta.imports) {
   1269    Maybe<BuiltinModuleId> builtinModule = ImportMatchesBuiltinModule(
   1270        import.module.utf8Bytes(), codeMeta.features().builtinModules);
   1271    if (builtinModule) {
   1272      continue;
   1273    }
   1274 
   1275    Rooted<IdValueVector> props(cx, IdValueVector(cx));
   1276    if (!props.reserve(3)) {
   1277      return false;
   1278    }
   1279 
   1280    JSString* moduleStr = import.module.toAtom(cx);
   1281    if (!moduleStr) {
   1282      return false;
   1283    }
   1284    props.infallibleAppend(
   1285        IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
   1286 
   1287    JSString* nameStr = import.field.toAtom(cx);
   1288    if (!nameStr) {
   1289      return false;
   1290    }
   1291    props.infallibleAppend(
   1292        IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
   1293 
   1294    JSString* kindStr = KindToString(cx, names, import.kind);
   1295    if (!kindStr) {
   1296      return false;
   1297    }
   1298    props.infallibleAppend(
   1299        IdValuePair(NameToId(names.kind), StringValue(kindStr)));
   1300 
   1301 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   1302    RootedObject typeObj(cx);
   1303    switch (import.kind) {
   1304      case DefinitionKind::Function: {
   1305        size_t funcIndex = numFuncImport++;
   1306        const FuncType& funcType = codeMeta.getFuncType(funcIndex);
   1307        typeObj = FuncTypeToObject(cx, funcType);
   1308        break;
   1309      }
   1310      case DefinitionKind::Table: {
   1311        size_t tableIndex = numTableImport++;
   1312        const TableDesc& table = codeMeta.tables[tableIndex];
   1313        typeObj =
   1314            TableTypeToObject(cx, table.addressType(), table.elemType,
   1315                              table.initialLength(), table.maximumLength());
   1316        break;
   1317      }
   1318      case DefinitionKind::Memory: {
   1319        size_t memoryIndex = numMemoryImport++;
   1320        const MemoryDesc& memory = codeMeta.memories[memoryIndex];
   1321        typeObj =
   1322            MemoryTypeToObject(cx, memory.isShared(), memory.addressType(),
   1323                               memory.initialPages(), memory.maximumPages());
   1324        break;
   1325      }
   1326      case DefinitionKind::Global: {
   1327        size_t globalIndex = numGlobalImport++;
   1328        const GlobalDesc& global = codeMeta.globals[globalIndex];
   1329        typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable());
   1330        break;
   1331      }
   1332      case DefinitionKind::Tag: {
   1333        size_t tagIndex = numTagImport++;
   1334        const TagDesc& tag = codeMeta.tags[tagIndex];
   1335        typeObj = TagTypeToObject(cx, tag.type->argTypes());
   1336        break;
   1337      }
   1338    }
   1339 
   1340    if (!typeObj || !props.append(IdValuePair(NameToId(names.type),
   1341                                              ObjectValue(*typeObj)))) {
   1342      ReportOutOfMemory(cx);
   1343      return false;
   1344    }
   1345 #endif  // ENABLE_WASM_TYPE_REFLECTIONS
   1346 
   1347    JSObject* obj = NewPlainObjectWithUniqueNames(cx, props);
   1348    if (!obj) {
   1349      return false;
   1350    }
   1351 
   1352    elems.infallibleAppend(ObjectValue(*obj));
   1353  }
   1354 
   1355  JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
   1356  if (!arr) {
   1357    return false;
   1358  }
   1359 
   1360  args.rval().setObject(*arr);
   1361  return true;
   1362 }
   1363 
   1364 /* static */
   1365 bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
   1366  CallArgs args = CallArgsFromVp(argc, vp);
   1367 
   1368  const Module* module;
   1369  if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) {
   1370    return false;
   1371  }
   1372 
   1373  KindNames names(cx);
   1374  if (!InitKindNames(cx, &names)) {
   1375    return false;
   1376  }
   1377 
   1378  const ModuleMetadata& moduleMeta = module->moduleMeta();
   1379 
   1380  RootedValueVector elems(cx);
   1381  if (!elems.reserve(moduleMeta.exports.length())) {
   1382    return false;
   1383  }
   1384 
   1385 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   1386  const CodeMetadata& codeMeta = module->codeMeta();
   1387 #endif  // ENABLE_WASM_TYPE_REFLECTIONS
   1388 
   1389  for (const Export& exp : moduleMeta.exports) {
   1390    Rooted<IdValueVector> props(cx, IdValueVector(cx));
   1391    if (!props.reserve(2)) {
   1392      return false;
   1393    }
   1394 
   1395    JSString* nameStr = exp.fieldName().toAtom(cx);
   1396    if (!nameStr) {
   1397      return false;
   1398    }
   1399    props.infallibleAppend(
   1400        IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
   1401 
   1402    JSString* kindStr = KindToString(cx, names, exp.kind());
   1403    if (!kindStr) {
   1404      return false;
   1405    }
   1406    props.infallibleAppend(
   1407        IdValuePair(NameToId(names.kind), StringValue(kindStr)));
   1408 
   1409 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   1410    RootedObject typeObj(cx);
   1411    switch (exp.kind()) {
   1412      case DefinitionKind::Function: {
   1413        const FuncType& funcType = codeMeta.getFuncType(exp.funcIndex());
   1414        typeObj = FuncTypeToObject(cx, funcType);
   1415        break;
   1416      }
   1417      case DefinitionKind::Table: {
   1418        const TableDesc& table = codeMeta.tables[exp.tableIndex()];
   1419        typeObj =
   1420            TableTypeToObject(cx, table.addressType(), table.elemType,
   1421                              table.initialLength(), table.maximumLength());
   1422        break;
   1423      }
   1424      case DefinitionKind::Memory: {
   1425        const MemoryDesc& memory = codeMeta.memories[exp.memoryIndex()];
   1426        typeObj =
   1427            MemoryTypeToObject(cx, memory.isShared(), memory.addressType(),
   1428                               memory.initialPages(), memory.maximumPages());
   1429        break;
   1430      }
   1431      case DefinitionKind::Global: {
   1432        const GlobalDesc& global = codeMeta.globals[exp.globalIndex()];
   1433        typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable());
   1434        break;
   1435      }
   1436      case DefinitionKind::Tag: {
   1437        const TagDesc& tag = codeMeta.tags[exp.tagIndex()];
   1438        typeObj = TagTypeToObject(cx, tag.type->argTypes());
   1439        break;
   1440      }
   1441    }
   1442 
   1443    if (!typeObj || !props.append(IdValuePair(NameToId(names.type),
   1444                                              ObjectValue(*typeObj)))) {
   1445      ReportOutOfMemory(cx);
   1446      return false;
   1447    }
   1448 #endif  // ENABLE_WASM_TYPE_REFLECTIONS
   1449 
   1450    JSObject* obj = NewPlainObjectWithUniqueNames(cx, props);
   1451    if (!obj) {
   1452      return false;
   1453    }
   1454 
   1455    elems.infallibleAppend(ObjectValue(*obj));
   1456  }
   1457 
   1458  JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
   1459  if (!arr) {
   1460    return false;
   1461  }
   1462 
   1463  args.rval().setObject(*arr);
   1464  return true;
   1465 }
   1466 
   1467 /* static */
   1468 bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) {
   1469  CallArgs args = CallArgsFromVp(argc, vp);
   1470 
   1471  const Module* module;
   1472  if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections",
   1473                    &module)) {
   1474    return false;
   1475  }
   1476 
   1477  Vector<char, 8> name(cx);
   1478  {
   1479    RootedString str(cx, ToString(cx, args.get(1)));
   1480    if (!str) {
   1481      return false;
   1482    }
   1483 
   1484    Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
   1485    if (!linear) {
   1486      return false;
   1487    }
   1488 
   1489    if (!name.initLengthUninitialized(
   1490            JS::GetDeflatedUTF8StringLength(linear))) {
   1491      return false;
   1492    }
   1493 
   1494    (void)JS::DeflateStringToUTF8Buffer(linear,
   1495                                        Span(name.begin(), name.length()));
   1496  }
   1497 
   1498  RootedValueVector elems(cx);
   1499  Rooted<ArrayBufferObject*> buf(cx);
   1500  for (const CustomSection& cs : module->moduleMeta().customSections) {
   1501    if (name.length() != cs.name.length()) {
   1502      continue;
   1503    }
   1504    if (memcmp(name.begin(), cs.name.begin(), name.length()) != 0) {
   1505      continue;
   1506    }
   1507 
   1508    buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
   1509    if (!buf) {
   1510      return false;
   1511    }
   1512 
   1513    memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
   1514    if (!elems.append(ObjectValue(*buf))) {
   1515      return false;
   1516    }
   1517  }
   1518 
   1519  JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
   1520  if (!arr) {
   1521    return false;
   1522  }
   1523 
   1524  args.rval().setObject(*arr);
   1525  return true;
   1526 }
   1527 
   1528 /* static */
   1529 WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module,
   1530                                           HandleObject proto) {
   1531  AutoSetNewObjectMetadata metadata(cx);
   1532  auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
   1533  if (!obj) {
   1534    return nullptr;
   1535  }
   1536 
   1537  // The pipeline state on some architectures may retain stale instructions
   1538  // even after we invalidate the instruction cache. There is no generally
   1539  // available method to broadcast this pipeline flush to all threads after
   1540  // we've compiled new code, so conservatively perform one here when we're
   1541  // receiving a module that may have been compiled from another thread.
   1542  //
   1543  // The cost of this flush is expected to minimal enough to not be worth
   1544  // optimizing away in the case the module was compiled on this thread.
   1545  jit::FlushExecutionContext();
   1546 
   1547  // This accounts for module allocation size (excluding code which is handled
   1548  // separately - see below). This assumes that the size of associated data
   1549  // doesn't change for the life of the WasmModuleObject. The size is counted
   1550  // once per WasmModuleObject referencing a Module.
   1551  InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module),
   1552                   module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule);
   1553  module.AddRef();
   1554 
   1555  // Bug 1569888: We account for the first tier here; the second tier, if
   1556  // different, also needs to be accounted for.
   1557  size_t codeMemory = module.tier1CodeMemoryUsed();
   1558  if (codeMemory) {
   1559    cx->zone()->incJitMemory(codeMemory);
   1560  }
   1561  return obj;
   1562 }
   1563 
   1564 struct MOZ_STACK_CLASS AutoPinBufferSourceLength {
   1565  explicit AutoPinBufferSourceLength(JSContext* cx, JSObject* bufferSource)
   1566      : bufferSource_(cx, bufferSource),
   1567        wasPinned_(!JS::PinArrayBufferOrViewLength(bufferSource_, true)) {}
   1568  ~AutoPinBufferSourceLength() {
   1569    if (!wasPinned_) {
   1570      JS::PinArrayBufferOrViewLength(bufferSource_, false);
   1571    }
   1572  }
   1573 
   1574 private:
   1575  Rooted<JSObject*> bufferSource_;
   1576  bool wasPinned_;
   1577 };
   1578 
   1579 static bool GetBytecodeSource(JSContext* cx, Handle<JSObject*> obj,
   1580                              unsigned errorNumber, BytecodeSource* bytecode) {
   1581  JSObject* unwrapped = CheckedUnwrapStatic(obj);
   1582 
   1583  SharedMem<uint8_t*> dataPointer;
   1584  size_t byteLength;
   1585  if (!unwrapped ||
   1586      !IsBufferSource(cx, unwrapped, /*allowShared*/ false,
   1587                      /*allowResizable*/ false, &dataPointer, &byteLength)) {
   1588    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
   1589    return false;
   1590  }
   1591 
   1592  *bytecode = BytecodeSource(dataPointer.unwrap(), byteLength);
   1593  return true;
   1594 }
   1595 
   1596 static bool GetBytecodeBuffer(JSContext* cx, Handle<JSObject*> obj,
   1597                              unsigned errorNumber, BytecodeBuffer* bytecode) {
   1598  BytecodeSource source;
   1599  if (!GetBytecodeSource(cx, obj, errorNumber, &source)) {
   1600    return false;
   1601  }
   1602  AutoPinBufferSourceLength pin(cx, obj);
   1603  if (!BytecodeBuffer::fromSource(source, bytecode)) {
   1604    ReportOutOfMemory(cx);
   1605    return false;
   1606  }
   1607  return true;
   1608 }
   1609 
   1610 static bool ReportCompileWarnings(JSContext* cx,
   1611                                  const UniqueCharsVector& warnings) {
   1612  // Avoid spamming the console.
   1613  size_t numWarnings = std::min<size_t>(warnings.length(), 3);
   1614 
   1615  for (size_t i = 0; i < numWarnings; i++) {
   1616    if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, warnings[i].get())) {
   1617      return false;
   1618    }
   1619  }
   1620 
   1621  if (warnings.length() > numWarnings) {
   1622    if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING,
   1623                         "other warnings suppressed")) {
   1624      return false;
   1625    }
   1626  }
   1627 
   1628  return true;
   1629 }
   1630 
   1631 /* static */
   1632 bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   1633  CallArgs callArgs = CallArgsFromVp(argc, vp);
   1634 
   1635  Log(cx, "sync new Module() started");
   1636 
   1637  if (!ThrowIfNotConstructing(cx, callArgs, "Module")) {
   1638    return false;
   1639  }
   1640 
   1641  JS::RootedVector<JSString*> parameterStrings(cx);
   1642  JS::RootedVector<Value> parameterArgs(cx);
   1643  bool canCompileStrings = false;
   1644  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
   1645                                   JS::CompilationType::Undefined,
   1646                                   parameterStrings, nullptr, parameterArgs,
   1647                                   NullHandleValue, &canCompileStrings)) {
   1648    return false;
   1649  }
   1650  if (!canCompileStrings) {
   1651    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1652                              JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module");
   1653    return false;
   1654  }
   1655 
   1656  if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) {
   1657    return false;
   1658  }
   1659 
   1660  if (!callArgs[0].isObject()) {
   1661    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1662                             JSMSG_WASM_BAD_BUF_ARG);
   1663    return false;
   1664  }
   1665 
   1666  FeatureOptions options;
   1667  if (!options.init(cx, callArgs.get(1))) {
   1668    return false;
   1669  }
   1670 
   1671  SharedCompileArgs compileArgs =
   1672      InitCompileArgs(cx, options, "WebAssembly.Module");
   1673  if (!compileArgs) {
   1674    return false;
   1675  }
   1676 
   1677  BytecodeSource source;
   1678  Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject());
   1679  if (!GetBytecodeSource(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG, &source)) {
   1680    return false;
   1681  }
   1682 
   1683  UniqueChars error;
   1684  UniqueCharsVector warnings;
   1685  SharedModule module;
   1686  {
   1687    AutoPinBufferSourceLength pin(cx, sourceObj.get());
   1688    module = CompileBuffer(*compileArgs, BytecodeBufferOrSource(source), &error,
   1689                           &warnings, nullptr);
   1690  }
   1691 
   1692  if (!ReportCompileWarnings(cx, warnings)) {
   1693    return false;
   1694  }
   1695  if (!module) {
   1696    if (error) {
   1697      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1698                               JSMSG_WASM_COMPILE_ERROR, error.get());
   1699      return false;
   1700    }
   1701    return ThrowCompileOutOfMemory(cx);
   1702  }
   1703 
   1704  RootedObject proto(
   1705      cx, GetWasmConstructorPrototype(cx, callArgs, JSProto_WasmModule));
   1706  if (!proto) {
   1707    ReportOutOfMemory(cx);
   1708    return false;
   1709  }
   1710 
   1711  RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
   1712  if (!moduleObj) {
   1713    return false;
   1714  }
   1715 
   1716  Log(cx, "sync new Module() succeded");
   1717 
   1718  callArgs.rval().setObject(*moduleObj);
   1719  return true;
   1720 }
   1721 
   1722 const Module& WasmModuleObject::module() const {
   1723  MOZ_ASSERT(is<WasmModuleObject>());
   1724  return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
   1725 }
   1726 
   1727 // ============================================================================
   1728 // WebAssembly.Instance class and methods
   1729 
   1730 const JSClassOps WasmInstanceObject::classOps_ = {
   1731    nullptr,                       // addProperty
   1732    nullptr,                       // delProperty
   1733    nullptr,                       // enumerate
   1734    nullptr,                       // newEnumerate
   1735    nullptr,                       // resolve
   1736    nullptr,                       // mayResolve
   1737    WasmInstanceObject::finalize,  // finalize
   1738    nullptr,                       // call
   1739    nullptr,                       // construct
   1740    WasmInstanceObject::trace,     // trace
   1741 };
   1742 
   1743 const JSClass WasmInstanceObject::class_ = {
   1744    "WebAssembly.Instance",
   1745    JSCLASS_DELAY_METADATA_BUILDER |
   1746        JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
   1747        JSCLASS_FOREGROUND_FINALIZE,
   1748    &WasmInstanceObject::classOps_,
   1749    &WasmInstanceObject::classSpec_,
   1750 };
   1751 
   1752 const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
   1753 
   1754 static constexpr char WasmInstanceName[] = "Instance";
   1755 
   1756 const ClassSpec WasmInstanceObject::classSpec_ = {
   1757    CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
   1758    GenericCreatePrototype<WasmInstanceObject>,
   1759    WasmInstanceObject::static_methods,
   1760    nullptr,
   1761    WasmInstanceObject::methods,
   1762    WasmInstanceObject::properties,
   1763    nullptr,
   1764    ClassSpec::DontDefineConstructor,
   1765 };
   1766 
   1767 static bool IsInstance(HandleValue v) {
   1768  return v.isObject() && v.toObject().is<WasmInstanceObject>();
   1769 }
   1770 
   1771 /* static */
   1772 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
   1773                                           const CallArgs& args) {
   1774  args.rval().setObject(
   1775      args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
   1776  return true;
   1777 }
   1778 
   1779 /* static */
   1780 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
   1781                                       Value* vp) {
   1782  CallArgs args = CallArgsFromVp(argc, vp);
   1783  return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
   1784 }
   1785 
   1786 const JSPropertySpec WasmInstanceObject::properties[] = {
   1787    JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
   1788    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
   1789    JS_PS_END,
   1790 };
   1791 
   1792 const JSFunctionSpec WasmInstanceObject::methods[] = {
   1793    JS_FS_END,
   1794 };
   1795 
   1796 const JSFunctionSpec WasmInstanceObject::static_methods[] = {
   1797    JS_FS_END,
   1798 };
   1799 
   1800 bool WasmInstanceObject::isNewborn() const {
   1801  MOZ_ASSERT(is<WasmInstanceObject>());
   1802  return getReservedSlot(INSTANCE_SLOT).isUndefined();
   1803 }
   1804 
   1805 // WeakScopeMap maps from function index to js::Scope. This maps is weak
   1806 // to avoid holding scope objects alive. The scopes are normally created
   1807 // during debugging.
   1808 //
   1809 // This is defined here in order to avoid recursive dependency between
   1810 // WasmJS.h and Scope.h.
   1811 using WasmFunctionScopeMap =
   1812    JS::WeakCache<GCHashMap<uint32_t, WeakHeapPtr<WasmFunctionScope*>,
   1813                            DefaultHasher<uint32_t>, CellAllocPolicy>>;
   1814 class WasmInstanceObject::UnspecifiedScopeMap {
   1815 public:
   1816  WasmFunctionScopeMap& asWasmFunctionScopeMap() {
   1817    return *(WasmFunctionScopeMap*)this;
   1818  }
   1819 };
   1820 
   1821 /* static */
   1822 void WasmInstanceObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   1823  WasmInstanceObject& instance = obj->as<WasmInstanceObject>();
   1824  gcx->delete_(obj, &instance.scopes().asWasmFunctionScopeMap(),
   1825               MemoryUse::WasmInstanceScopes);
   1826  gcx->delete_(obj, &instance.indirectGlobals(),
   1827               MemoryUse::WasmInstanceGlobals);
   1828  if (!instance.isNewborn()) {
   1829    if (instance.instance().debugEnabled()) {
   1830      instance.instance().debug().finalize(gcx);
   1831    }
   1832    Instance::destroy(&instance.instance());
   1833    gcx->removeCellMemory(obj, sizeof(Instance),
   1834                          MemoryUse::WasmInstanceInstance);
   1835  }
   1836 }
   1837 
   1838 /* static */
   1839 void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) {
   1840  WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
   1841  instanceObj.indirectGlobals().trace(trc);
   1842  if (!instanceObj.isNewborn()) {
   1843    instanceObj.instance().tracePrivate(trc);
   1844  }
   1845 }
   1846 
   1847 /* static */
   1848 WasmInstanceObject* WasmInstanceObject::create(
   1849    JSContext* cx, const SharedCode& code,
   1850    const DataSegmentVector& dataSegments,
   1851    const ModuleElemSegmentVector& elemSegments, uint32_t instanceDataLength,
   1852    Handle<WasmMemoryObjectVector> memories, SharedTableVector&& tables,
   1853    const JSObjectVector& funcImports, const GlobalDescVector& globals,
   1854    const ValVector& globalImportValues,
   1855    const WasmGlobalObjectVector& globalObjs,
   1856    const WasmTagObjectVector& tagObjs, HandleObject proto,
   1857    UniqueDebugState maybeDebug) {
   1858  UniquePtr<WasmFunctionScopeMap> scopes =
   1859      js::MakeUnique<WasmFunctionScopeMap>(cx->zone(), cx->zone());
   1860  if (!scopes) {
   1861    ReportOutOfMemory(cx);
   1862    return nullptr;
   1863  }
   1864  // Note that `scopes` is a WeakCache, auto-linked into a sweep list on the
   1865  // Zone, and so does not require rooting.
   1866 
   1867  uint32_t indirectGlobals = 0;
   1868 
   1869  for (uint32_t i = 0; i < globalObjs.length(); i++) {
   1870    if (globalObjs[i] && globals[i].isIndirect()) {
   1871      indirectGlobals++;
   1872    }
   1873  }
   1874 
   1875  Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs(
   1876      cx, js::MakeUnique<GlobalObjectVector>(cx->zone()));
   1877  if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
   1878    ReportOutOfMemory(cx);
   1879    return nullptr;
   1880  }
   1881 
   1882  {
   1883    uint32_t next = 0;
   1884    for (uint32_t i = 0; i < globalObjs.length(); i++) {
   1885      if (globalObjs[i] && globals[i].isIndirect()) {
   1886        (*indirectGlobalObjs)[next++] = globalObjs[i];
   1887      }
   1888    }
   1889  }
   1890 
   1891  Instance* instance = nullptr;
   1892  Rooted<WasmInstanceObject*> obj(cx);
   1893 
   1894  {
   1895    // We must delay creating metadata for this object until after all its
   1896    // slots have been initialized. We must also create the metadata before
   1897    // calling Instance::init as that may allocate new objects.
   1898    AutoSetNewObjectMetadata metadata(cx);
   1899    obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
   1900    if (!obj) {
   1901      return nullptr;
   1902    }
   1903 
   1904    MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
   1905 
   1906    InitReservedSlot(obj, SCOPES_SLOT, scopes.release(),
   1907                     MemoryUse::WasmInstanceScopes);
   1908 
   1909    InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(),
   1910                     MemoryUse::WasmInstanceGlobals);
   1911 
   1912    obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
   1913 
   1914    // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
   1915    // leading to an observable "newborn" state in tracing/finalization.
   1916    MOZ_ASSERT(obj->isNewborn());
   1917 
   1918    // Create this just before constructing Instance to avoid rooting hazards.
   1919    instance = Instance::create(cx, obj, code, instanceDataLength,
   1920                                std::move(tables), std::move(maybeDebug));
   1921    if (!instance) {
   1922      return nullptr;
   1923    }
   1924 
   1925    InitReservedSlot(obj, INSTANCE_SLOT, instance,
   1926                     MemoryUse::WasmInstanceInstance);
   1927    MOZ_ASSERT(!obj->isNewborn());
   1928  }
   1929 
   1930  if (!instance->init(cx, funcImports, globalImportValues, memories, globalObjs,
   1931                      tagObjs, dataSegments, elemSegments)) {
   1932    return nullptr;
   1933  }
   1934 
   1935  return obj;
   1936 }
   1937 
   1938 void WasmInstanceObject::initExportsObj(JSObject& exportsObj) {
   1939  MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
   1940  setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
   1941 }
   1942 
   1943 static bool GetImportArg(JSContext* cx, HandleValue importArg,
   1944                         MutableHandleObject importObj) {
   1945  if (!importArg.isUndefined()) {
   1946    if (!importArg.isObject()) {
   1947      return ThrowBadImportArg(cx);
   1948    }
   1949    importObj.set(&importArg.toObject());
   1950  }
   1951  return true;
   1952 }
   1953 
   1954 /* static */
   1955 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   1956  CallArgs args = CallArgsFromVp(argc, vp);
   1957 
   1958  Log(cx, "sync new Instance() started");
   1959 
   1960  if (!ThrowIfNotConstructing(cx, args, "Instance")) {
   1961    return false;
   1962  }
   1963 
   1964  if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
   1965    return false;
   1966  }
   1967 
   1968  const Module* module;
   1969  if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
   1970    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   1971                             JSMSG_WASM_BAD_MOD_ARG);
   1972    return false;
   1973  }
   1974 
   1975  RootedObject importObj(cx);
   1976  if (!GetImportArg(cx, args.get(1), &importObj)) {
   1977    return false;
   1978  }
   1979 
   1980  RootedObject proto(
   1981      cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmInstance));
   1982  if (!proto) {
   1983    ReportOutOfMemory(cx);
   1984    return false;
   1985  }
   1986 
   1987  Rooted<ImportValues> imports(cx);
   1988  if (!GetImports(cx, *module, importObj, imports.address())) {
   1989    return false;
   1990  }
   1991 
   1992  Rooted<WasmInstanceObject*> instanceObj(cx);
   1993  if (!module->instantiate(cx, imports.get(), proto, &instanceObj)) {
   1994    return false;
   1995  }
   1996 
   1997  Log(cx, "sync new Instance() succeeded");
   1998 
   1999  args.rval().setObject(*instanceObj);
   2000  return true;
   2001 }
   2002 
   2003 Instance& WasmInstanceObject::instance() const {
   2004  MOZ_ASSERT(!isNewborn());
   2005  return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
   2006 }
   2007 
   2008 JSObject& WasmInstanceObject::exportsObj() const {
   2009  return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
   2010 }
   2011 
   2012 WasmInstanceObject::UnspecifiedScopeMap& WasmInstanceObject::scopes() const {
   2013  return *(UnspecifiedScopeMap*)(getReservedSlot(SCOPES_SLOT).toPrivate());
   2014 }
   2015 
   2016 WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals()
   2017    const {
   2018  return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate();
   2019 }
   2020 
   2021 /* static */
   2022 bool WasmInstanceObject::getExportedFunction(
   2023    JSContext* cx, Handle<WasmInstanceObject*> instanceObj, uint32_t funcIndex,
   2024    MutableHandleFunction fun) {
   2025  Instance& instance = instanceObj->instance();
   2026  return instance.getExportedFunction(cx, funcIndex, fun);
   2027 }
   2028 
   2029 /* static */
   2030 WasmInstanceScope* WasmInstanceObject::getScope(
   2031    JSContext* cx, Handle<WasmInstanceObject*> instanceObj) {
   2032  if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) {
   2033    return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT)
   2034        .toGCThing();
   2035  }
   2036 
   2037  Rooted<WasmInstanceScope*> instanceScope(
   2038      cx, WasmInstanceScope::create(cx, instanceObj));
   2039  if (!instanceScope) {
   2040    return nullptr;
   2041  }
   2042 
   2043  instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT,
   2044                               PrivateGCThingValue(instanceScope));
   2045 
   2046  return instanceScope;
   2047 }
   2048 
   2049 /* static */
   2050 WasmFunctionScope* WasmInstanceObject::getFunctionScope(
   2051    JSContext* cx, Handle<WasmInstanceObject*> instanceObj,
   2052    uint32_t funcIndex) {
   2053  if (auto p =
   2054          instanceObj->scopes().asWasmFunctionScopeMap().lookup(funcIndex)) {
   2055    return p->value();
   2056  }
   2057 
   2058  Rooted<WasmInstanceScope*> instanceScope(
   2059      cx, WasmInstanceObject::getScope(cx, instanceObj));
   2060  if (!instanceScope) {
   2061    return nullptr;
   2062  }
   2063 
   2064  Rooted<WasmFunctionScope*> funcScope(
   2065      cx, WasmFunctionScope::create(cx, instanceScope, funcIndex));
   2066  if (!funcScope) {
   2067    return nullptr;
   2068  }
   2069 
   2070  if (!instanceObj->scopes().asWasmFunctionScopeMap().putNew(funcIndex,
   2071                                                             funcScope)) {
   2072    ReportOutOfMemory(cx);
   2073    return nullptr;
   2074  }
   2075 
   2076  return funcScope;
   2077 }
   2078 
   2079 // ============================================================================
   2080 // WebAssembly.Memory class and methods
   2081 
   2082 const JSClassOps WasmMemoryObject::classOps_ = {
   2083    nullptr,                     // addProperty
   2084    nullptr,                     // delProperty
   2085    nullptr,                     // enumerate
   2086    nullptr,                     // newEnumerate
   2087    nullptr,                     // resolve
   2088    nullptr,                     // mayResolve
   2089    WasmMemoryObject::finalize,  // finalize
   2090    nullptr,                     // call
   2091    nullptr,                     // construct
   2092    nullptr,                     // trace
   2093 };
   2094 
   2095 const JSClass WasmMemoryObject::class_ = {
   2096    "WebAssembly.Memory",
   2097    JSCLASS_DELAY_METADATA_BUILDER |
   2098        JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
   2099        JSCLASS_FOREGROUND_FINALIZE,
   2100    &WasmMemoryObject::classOps_,
   2101    &WasmMemoryObject::classSpec_,
   2102 };
   2103 
   2104 const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_;
   2105 
   2106 static constexpr char WasmMemoryName[] = "Memory";
   2107 
   2108 static JSObject* CreateWasmMemoryPrototype(JSContext* cx, JSProtoKey key) {
   2109  RootedObject proto(cx, GlobalObject::createBlankPrototype(
   2110                             cx, cx->global(), &WasmMemoryObject::protoClass_));
   2111  if (!proto) {
   2112    return nullptr;
   2113  }
   2114  if (MemoryControlAvailable(cx)) {
   2115    if (!JS_DefineFunctions(cx, proto,
   2116                            WasmMemoryObject::memoryControlMethods)) {
   2117      return nullptr;
   2118    }
   2119  }
   2120  return proto;
   2121 }
   2122 
   2123 const ClassSpec WasmMemoryObject::classSpec_ = {
   2124    CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>,
   2125    CreateWasmMemoryPrototype,
   2126    WasmMemoryObject::static_methods,
   2127    nullptr,
   2128    WasmMemoryObject::methods,
   2129    WasmMemoryObject::properties,
   2130    nullptr,
   2131    ClassSpec::DontDefineConstructor,
   2132 };
   2133 
   2134 /* static */
   2135 void WasmMemoryObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   2136  WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
   2137  if (memory.hasObservers()) {
   2138    gcx->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers);
   2139  }
   2140 }
   2141 
   2142 /* static */
   2143 WasmMemoryObject* WasmMemoryObject::create(
   2144    JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, bool isHuge,
   2145    HandleObject proto) {
   2146  AutoSetNewObjectMetadata metadata(cx);
   2147  auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
   2148  if (!obj) {
   2149    return nullptr;
   2150  }
   2151 
   2152  obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
   2153  obj->initReservedSlot(ISHUGE_SLOT, BooleanValue(isHuge));
   2154  MOZ_ASSERT(!obj->hasObservers());
   2155 
   2156  return obj;
   2157 }
   2158 
   2159 /* static */
   2160 bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   2161  CallArgs args = CallArgsFromVp(argc, vp);
   2162 
   2163  if (!ThrowIfNotConstructing(cx, args, "Memory")) {
   2164    return false;
   2165  }
   2166 
   2167  if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1)) {
   2168    return false;
   2169  }
   2170 
   2171  if (!args.get(0).isObject()) {
   2172    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2173                             JSMSG_WASM_BAD_DESC_ARG, "memory");
   2174    return false;
   2175  }
   2176 
   2177  RootedObject obj(cx, &args[0].toObject());
   2178  Limits limits;
   2179  if (!GetLimits(cx, obj, LimitsKind::Memory, &limits) ||
   2180      !CheckLimits(
   2181          cx, MaxMemoryPagesValidation(limits.addressType, limits.pageSize),
   2182          LimitsKind::Memory, &limits)) {
   2183    return false;
   2184  }
   2185 
   2186  if (Pages::fromPageCount(limits.initial, limits.pageSize) >
   2187      MaxMemoryPages(limits.addressType, limits.pageSize)) {
   2188    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2189                             JSMSG_WASM_MEM_IMP_LIMIT);
   2190    return false;
   2191  }
   2192  MemoryDesc memory(limits);
   2193 
   2194  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx,
   2195                                               CreateWasmBuffer(cx, memory));
   2196  if (!buffer) {
   2197    return false;
   2198  }
   2199 
   2200  RootedObject proto(cx,
   2201                     GetWasmConstructorPrototype(cx, args, JSProto_WasmMemory));
   2202  if (!proto) {
   2203    ReportOutOfMemory(cx);
   2204    return false;
   2205  }
   2206 
   2207  Rooted<WasmMemoryObject*> memoryObj(
   2208      cx, WasmMemoryObject::create(
   2209              cx, buffer,
   2210              IsHugeMemoryEnabled(limits.addressType, limits.pageSize), proto));
   2211  if (!memoryObj) {
   2212    return false;
   2213  }
   2214 
   2215  args.rval().setObject(*memoryObj);
   2216  return true;
   2217 }
   2218 
   2219 static bool IsMemory(HandleValue v) {
   2220  return v.isObject() && v.toObject().is<WasmMemoryObject>();
   2221 }
   2222 
   2223 /* static */
   2224 ArrayBufferObjectMaybeShared* WasmMemoryObject::refreshBuffer(
   2225    JSContext* cx, Handle<WasmMemoryObject*> memoryObj,
   2226    Handle<ArrayBufferObjectMaybeShared*> buffer) {
   2227  if (memoryObj->isShared()) {
   2228    size_t memoryLength = memoryObj->volatileMemoryLength();
   2229    MOZ_ASSERT_IF(!buffer->is<GrowableSharedArrayBufferObject>(),
   2230                  memoryLength >= buffer->byteLength());
   2231 
   2232    // The `length` field on a fixed length SAB cannot change even if
   2233    // the underlying memory has grown. The spec therefore requires that
   2234    // accessing the buffer property will create a new fixed length SAB
   2235    // with the current length if the underlying raw buffer's length has
   2236    // changed. We don't need to do this for growable SAB.
   2237    if (!buffer->is<GrowableSharedArrayBufferObject>() &&
   2238        memoryLength > buffer->byteLength()) {
   2239      Rooted<SharedArrayBufferObject*> newBuffer(
   2240          cx, SharedArrayBufferObject::New(
   2241                  cx, memoryObj->sharedArrayRawBuffer(), memoryLength));
   2242      if (!newBuffer) {
   2243        return nullptr;
   2244      }
   2245      MOZ_ASSERT(newBuffer->is<FixedLengthSharedArrayBufferObject>());
   2246      // OK to addReference after we try to allocate because the memoryObj
   2247      // keeps the rawBuffer alive.
   2248      if (!memoryObj->sharedArrayRawBuffer()->addReference()) {
   2249        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2250                                  JSMSG_SC_SAB_REFCNT_OFLO);
   2251        return nullptr;
   2252      }
   2253      memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer));
   2254      return newBuffer;
   2255    }
   2256  }
   2257  return buffer;
   2258 }
   2259 
   2260 /* static */
   2261 bool WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args) {
   2262  Rooted<WasmMemoryObject*> memoryObj(
   2263      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2264 
   2265  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memoryObj->buffer());
   2266  MOZ_RELEASE_ASSERT(buffer->isWasm() && !buffer->isPreparedForAsmJS());
   2267 
   2268  ArrayBufferObjectMaybeShared* refreshedBuffer =
   2269      WasmMemoryObject::refreshBuffer(cx, memoryObj, buffer);
   2270  if (!refreshedBuffer) {
   2271    return false;
   2272  }
   2273 
   2274  args.rval().setObject(*refreshedBuffer);
   2275  return true;
   2276 }
   2277 
   2278 /* static */
   2279 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
   2280  CallArgs args = CallArgsFromVp(argc, vp);
   2281  return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
   2282 }
   2283 
   2284 const JSPropertySpec WasmMemoryObject::properties[] = {
   2285    JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE),
   2286    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY),
   2287    JS_PS_END,
   2288 };
   2289 
   2290 /* static */
   2291 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) {
   2292  Rooted<WasmMemoryObject*> memory(
   2293      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2294 
   2295  if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) {
   2296    return false;
   2297  }
   2298 
   2299  uint64_t delta;
   2300  if (!EnforceAddressValue(cx, args.get(0), memory->addressType(), "Memory",
   2301                           "grow delta", &delta)) {
   2302    return false;
   2303  }
   2304 
   2305  uint32_t ret = grow(memory, delta, cx);
   2306 
   2307  if (ret == uint32_t(-1)) {
   2308    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
   2309                             "memory");
   2310    return false;
   2311  }
   2312 
   2313  RootedValue result(cx);
   2314  if (!CreateAddressValue(cx, ret, memory->addressType(), &result)) {
   2315    ReportOutOfMemory(cx);
   2316    return false;
   2317  }
   2318  args.rval().set(result);
   2319  return true;
   2320 }
   2321 
   2322 /* static */
   2323 bool WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp) {
   2324  CallArgs args = CallArgsFromVp(argc, vp);
   2325  return CallNonGenericMethod<IsMemory, growImpl>(cx, args);
   2326 }
   2327 
   2328 /* static */
   2329 bool WasmMemoryObject::discardImpl(JSContext* cx, const CallArgs& args) {
   2330  Rooted<WasmMemoryObject*> memory(
   2331      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2332 
   2333  if (!args.requireAtLeast(cx, "WebAssembly.Memory.discard", 2)) {
   2334    return false;
   2335  }
   2336 
   2337  uint64_t byteOffset;
   2338  if (!EnforceRangeU64(cx, args.get(0), "Memory", "byte offset", &byteOffset)) {
   2339    return false;
   2340  }
   2341 
   2342  uint64_t byteLen;
   2343  if (!EnforceRangeU64(cx, args.get(1), "Memory", "length", &byteLen)) {
   2344    return false;
   2345  }
   2346 
   2347  if (byteOffset % wasm::StandardPageSizeBytes != 0 ||
   2348      byteLen % wasm::StandardPageSizeBytes != 0) {
   2349    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2350                             JSMSG_WASM_UNALIGNED_ACCESS);
   2351    return false;
   2352  }
   2353 
   2354  if (!wasm::MemoryBoundsCheck(byteOffset, byteLen,
   2355                               memory->volatileMemoryLength())) {
   2356    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2357                             JSMSG_WASM_OUT_OF_BOUNDS);
   2358    return false;
   2359  }
   2360 
   2361  discard(memory, byteOffset, byteLen, cx);
   2362 
   2363  args.rval().setUndefined();
   2364  return true;
   2365 }
   2366 
   2367 /* static */
   2368 bool WasmMemoryObject::discard(JSContext* cx, unsigned argc, Value* vp) {
   2369  CallArgs args = CallArgsFromVp(argc, vp);
   2370  return CallNonGenericMethod<IsMemory, discardImpl>(cx, args);
   2371 }
   2372 
   2373 #ifdef ENABLE_WASM_RESIZABLE_ARRAYBUFFER
   2374 /* static */
   2375 bool WasmMemoryObject::toFixedLengthBufferImpl(JSContext* cx,
   2376                                               const CallArgs& args) {
   2377  Rooted<WasmMemoryObject*> memory(
   2378      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2379 
   2380  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memory->buffer());
   2381  MOZ_RELEASE_ASSERT(buffer->isWasm() && !buffer->isPreparedForAsmJS());
   2382  // If IsFixedLengthArrayBuffer(buffer) is true, return buffer.
   2383  if (!buffer->isResizable()) {
   2384    ArrayBufferObjectMaybeShared* refreshedBuffer =
   2385        refreshBuffer(cx, memory, buffer);
   2386    if (!refreshedBuffer) {
   2387      return false;
   2388    }
   2389    args.rval().set(ObjectValue(*refreshedBuffer));
   2390    return true;
   2391  }
   2392 
   2393  Rooted<ArrayBufferObjectMaybeShared*> fixedBuffer(cx);
   2394  if (memory->isShared()) {
   2395    Rooted<SharedArrayBufferObject*> oldBuffer(
   2396        cx, &buffer->as<SharedArrayBufferObject>());
   2397    fixedBuffer.set(SharedArrayBufferObject::createFromWasmObject<
   2398                    FixedLengthSharedArrayBufferObject>(cx, oldBuffer));
   2399  } else {
   2400    Rooted<ArrayBufferObject*> oldBuffer(cx, &buffer->as<ArrayBufferObject>());
   2401    fixedBuffer.set(
   2402        ArrayBufferObject::createFromWasmObject<FixedLengthArrayBufferObject>(
   2403            cx, oldBuffer));
   2404  }
   2405 
   2406  if (!fixedBuffer) {
   2407    return false;
   2408  }
   2409  memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*fixedBuffer));
   2410  args.rval().set(ObjectValue(*fixedBuffer));
   2411  return true;
   2412 }
   2413 
   2414 /* static */
   2415 bool WasmMemoryObject::toFixedLengthBuffer(JSContext* cx, unsigned argc,
   2416                                           Value* vp) {
   2417  CallArgs args = CallArgsFromVp(argc, vp);
   2418  return CallNonGenericMethod<IsMemory, toFixedLengthBufferImpl>(cx, args);
   2419 }
   2420 
   2421 /* static */
   2422 bool WasmMemoryObject::toResizableBufferImpl(JSContext* cx,
   2423                                             const CallArgs& args) {
   2424  Rooted<WasmMemoryObject*> memory(
   2425      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2426 
   2427  Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memory->buffer());
   2428  // If IsFixedLengthArrayBuffer(buffer) is false, return buffer.
   2429  if (buffer->isResizable()) {
   2430    args.rval().set(ObjectValue(*buffer));
   2431    return true;
   2432  }
   2433 
   2434  if (buffer->wasmSourceMaxPages().isNothing()) {
   2435    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2436                              JSMSG_WASM_MEMORY_NOT_RESIZABLE);
   2437    return false;
   2438  }
   2439 
   2440  Rooted<ArrayBufferObjectMaybeShared*> resizableBuffer(cx);
   2441  if (memory->isShared()) {
   2442    Rooted<SharedArrayBufferObject*> oldBuffer(
   2443        cx, &buffer->as<SharedArrayBufferObject>());
   2444    resizableBuffer.set(SharedArrayBufferObject::createFromWasmObject<
   2445                        GrowableSharedArrayBufferObject>(cx, oldBuffer));
   2446  } else {
   2447    Rooted<ArrayBufferObject*> oldBuffer(cx, &buffer->as<ArrayBufferObject>());
   2448    resizableBuffer.set(
   2449        ArrayBufferObject::createFromWasmObject<ResizableArrayBufferObject>(
   2450            cx, oldBuffer));
   2451  }
   2452 
   2453  if (!resizableBuffer) {
   2454    return false;
   2455  }
   2456  memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*resizableBuffer));
   2457  args.rval().set(ObjectValue(*resizableBuffer));
   2458  return true;
   2459 }
   2460 
   2461 /* static */
   2462 bool WasmMemoryObject::toResizableBuffer(JSContext* cx, unsigned argc,
   2463                                         Value* vp) {
   2464  CallArgs args = CallArgsFromVp(argc, vp);
   2465  return CallNonGenericMethod<IsMemory, toResizableBufferImpl>(cx, args);
   2466 }
   2467 #endif  // ENABLE_WASM_RESIZABLE_ARRAYBUFFER
   2468 
   2469 const JSFunctionSpec WasmMemoryObject::methods[] = {
   2470 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   2471    JS_FN("type", WasmMemoryObject::type, 0, JSPROP_ENUMERATE),
   2472 #endif
   2473    JS_FN("grow", WasmMemoryObject::grow, 1, JSPROP_ENUMERATE),
   2474 #ifdef ENABLE_WASM_RESIZABLE_ARRAYBUFFER
   2475    JS_FN("toFixedLengthBuffer", WasmMemoryObject::toFixedLengthBuffer, 0,
   2476          JSPROP_ENUMERATE),
   2477    JS_FN("toResizableBuffer", WasmMemoryObject::toResizableBuffer, 0,
   2478          JSPROP_ENUMERATE),
   2479 #endif
   2480    JS_FS_END,
   2481 };
   2482 
   2483 const JSFunctionSpec WasmMemoryObject::memoryControlMethods[] = {
   2484    JS_FN("discard", WasmMemoryObject::discard, 2, JSPROP_ENUMERATE),
   2485    JS_FS_END,
   2486 };
   2487 
   2488 const JSFunctionSpec WasmMemoryObject::static_methods[] = {
   2489    JS_FS_END,
   2490 };
   2491 
   2492 ArrayBufferObjectMaybeShared& WasmMemoryObject::buffer() const {
   2493  return getReservedSlot(BUFFER_SLOT)
   2494      .toObject()
   2495      .as<ArrayBufferObjectMaybeShared>();
   2496 }
   2497 
   2498 WasmSharedArrayRawBuffer* WasmMemoryObject::sharedArrayRawBuffer() const {
   2499  MOZ_ASSERT(isShared());
   2500  return buffer().as<SharedArrayBufferObject>().rawWasmBufferObject();
   2501 }
   2502 
   2503 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   2504 bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) {
   2505  Rooted<WasmMemoryObject*> memoryObj(
   2506      cx, &args.thisv().toObject().as<WasmMemoryObject>());
   2507  RootedObject typeObj(cx, MemoryTypeToObject(cx, memoryObj->isShared(),
   2508                                              memoryObj->addressType(),
   2509                                              memoryObj->volatilePages(),
   2510                                              memoryObj->sourceMaxPages()));
   2511  if (!typeObj) {
   2512    return false;
   2513  }
   2514  args.rval().setObject(*typeObj);
   2515  return true;
   2516 }
   2517 
   2518 bool WasmMemoryObject::type(JSContext* cx, unsigned argc, Value* vp) {
   2519  CallArgs args = CallArgsFromVp(argc, vp);
   2520  return CallNonGenericMethod<IsMemory, typeImpl>(cx, args);
   2521 }
   2522 #endif
   2523 
   2524 size_t WasmMemoryObject::volatileMemoryLength() const {
   2525  if (isShared()) {
   2526    return sharedArrayRawBuffer()->volatileByteLength();
   2527  }
   2528  return buffer().byteLength();
   2529 }
   2530 
   2531 wasm::Pages WasmMemoryObject::volatilePages() const {
   2532  if (isShared()) {
   2533    return sharedArrayRawBuffer()->volatileWasmPages();
   2534  }
   2535  return buffer().wasmPages();
   2536 }
   2537 
   2538 wasm::Pages WasmMemoryObject::clampedMaxPages() const {
   2539  if (isShared()) {
   2540    return sharedArrayRawBuffer()->wasmClampedMaxPages();
   2541  }
   2542  return buffer().wasmClampedMaxPages();
   2543 }
   2544 
   2545 Maybe<wasm::Pages> WasmMemoryObject::sourceMaxPages() const {
   2546  if (isShared()) {
   2547    return Some(sharedArrayRawBuffer()->wasmSourceMaxPages());
   2548  }
   2549  return buffer().wasmSourceMaxPages();
   2550 }
   2551 
   2552 wasm::AddressType WasmMemoryObject::addressType() const {
   2553  if (isShared()) {
   2554    return sharedArrayRawBuffer()->wasmAddressType();
   2555  }
   2556  return buffer().wasmAddressType();
   2557 }
   2558 
   2559 bool WasmMemoryObject::isShared() const {
   2560  return buffer().is<SharedArrayBufferObject>();
   2561 }
   2562 
   2563 bool WasmMemoryObject::hasObservers() const {
   2564  return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
   2565 }
   2566 
   2567 WasmMemoryObject::InstanceSet& WasmMemoryObject::observers() const {
   2568  MOZ_ASSERT(hasObservers());
   2569  return *reinterpret_cast<InstanceSet*>(
   2570      getReservedSlot(OBSERVERS_SLOT).toPrivate());
   2571 }
   2572 
   2573 WasmMemoryObject::InstanceSet* WasmMemoryObject::getOrCreateObservers(
   2574    JSContext* cx) {
   2575  if (!hasObservers()) {
   2576    auto observers = MakeUnique<InstanceSet>(cx->zone(), cx->zone());
   2577    if (!observers) {
   2578      ReportOutOfMemory(cx);
   2579      return nullptr;
   2580    }
   2581 
   2582    InitReservedSlot(this, OBSERVERS_SLOT, observers.release(),
   2583                     MemoryUse::WasmMemoryObservers);
   2584  }
   2585 
   2586  return &observers();
   2587 }
   2588 
   2589 bool WasmMemoryObject::isHuge() const {
   2590  return getReservedSlot(ISHUGE_SLOT).toBoolean();
   2591 }
   2592 
   2593 bool WasmMemoryObject::movingGrowable() const {
   2594  return !isHuge() && !buffer().wasmSourceMaxPages();
   2595 }
   2596 
   2597 size_t WasmMemoryObject::boundsCheckLimit() const {
   2598  if (!buffer().isWasm() || isHuge()) {
   2599    return buffer().byteLength();
   2600  }
   2601 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
   2602  // For tiny page sizes, we need to use the actual byte length as the bounds
   2603  // check as we cannot rely on virtual memory for accesses between the byte
   2604  // length and the mapped size.
   2605  if (buffer().wasmPageSize() == wasm::PageSize::Tiny) {
   2606    size_t limit = buffer().byteLength();
   2607    MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(),
   2608                                                  buffer().wasmPageSize()));
   2609    return limit;
   2610  }
   2611 #endif
   2612  size_t mappedSize = buffer().wasmMappedSize();
   2613 #if !defined(JS_64BIT)
   2614  // See clamping performed in CreateSpecificWasmBuffer().  On 32-bit systems
   2615  // we do not want to overflow a uint32_t.  For the other 64-bit compilers,
   2616  // all constraints are implied by the largest accepted value for a memory's
   2617  // max field.
   2618  MOZ_ASSERT(mappedSize < UINT32_MAX);
   2619 #endif
   2620  MOZ_ASSERT(buffer().wasmPageSize() == wasm::PageSize::Standard);
   2621  MOZ_ASSERT(mappedSize % wasm::StandardPageSizeBytes == 0);
   2622  MOZ_ASSERT(mappedSize >= wasm::GuardSize);
   2623  size_t limit = mappedSize - wasm::GuardSize;
   2624  MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(),
   2625                                                wasm::PageSize::Standard));
   2626  return limit;
   2627 }
   2628 
   2629 wasm::PageSize WasmMemoryObject::pageSize() const {
   2630  if (isShared()) {
   2631    return sharedArrayRawBuffer()->wasmPageSize();
   2632  }
   2633  return buffer().wasmPageSize();
   2634 }
   2635 
   2636 bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx,
   2637                                             WasmInstanceObject* instance) {
   2638  MOZ_ASSERT(movingGrowable());
   2639 
   2640  InstanceSet* observers = getOrCreateObservers(cx);
   2641  if (!observers) {
   2642    return false;
   2643  }
   2644 
   2645  // A memory can be imported multiple times into an instance, but we only
   2646  // register the instance as an observer once.
   2647  if (!observers->put(instance)) {
   2648    ReportOutOfMemory(cx);
   2649    return false;
   2650  }
   2651 
   2652  return true;
   2653 }
   2654 
   2655 /* static */
   2656 uint64_t WasmMemoryObject::growShared(Handle<WasmMemoryObject*> memory,
   2657                                      uint64_t delta) {
   2658  WasmSharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer();
   2659  WasmSharedArrayRawBuffer::Lock lock(rawBuf);
   2660 
   2661  Pages oldNumPages = rawBuf->volatileWasmPages();
   2662  Pages newPages = oldNumPages;
   2663  if (!newPages.checkedIncrement(delta)) {
   2664    return uint64_t(int64_t(-1));
   2665  }
   2666 
   2667  if (!rawBuf->wasmGrowToPagesInPlace(lock, memory->addressType(), newPages)) {
   2668    return uint64_t(int64_t(-1));
   2669  }
   2670  // New buffer objects will be created lazily in all agents (including in
   2671  // this agent) by bufferGetterImpl, above, so no more work to do here.
   2672 
   2673  return oldNumPages.pageCount();
   2674 }
   2675 
   2676 /* static */
   2677 uint64_t WasmMemoryObject::grow(Handle<WasmMemoryObject*> memory,
   2678                                uint64_t delta, JSContext* cx) {
   2679  if (memory->isShared()) {
   2680    return growShared(memory, delta);
   2681  }
   2682 
   2683  Rooted<ArrayBufferObject*> oldBuf(cx,
   2684                                    &memory->buffer().as<ArrayBufferObject>());
   2685 
   2686 #if !defined(JS_64BIT)
   2687  // TODO (large ArrayBuffer): See more information at the definition of
   2688  // MaxMemoryBytes().
   2689  MOZ_ASSERT(
   2690      MaxMemoryBytes(memory->addressType(), memory->pageSize()) <= UINT32_MAX,
   2691      "Avoid 32-bit overflows");
   2692 #endif
   2693 
   2694  Pages oldNumPages = oldBuf->wasmPages();
   2695  Pages newPages = oldNumPages;
   2696  if (!newPages.checkedIncrement(delta)) {
   2697    return uint64_t(int64_t(-1));
   2698  }
   2699 
   2700  ArrayBufferObject* newBuf;
   2701  if (memory->movingGrowable()) {
   2702    MOZ_ASSERT(!memory->isHuge());
   2703    newBuf = ArrayBufferObject::wasmMovingGrowToPages(memory->addressType(),
   2704                                                      newPages, oldBuf, cx);
   2705  } else {
   2706    newBuf = ArrayBufferObject::wasmGrowToPagesInPlace(memory->addressType(),
   2707                                                       newPages, oldBuf, cx);
   2708  }
   2709  if (!newBuf) {
   2710    return uint64_t(int64_t(-1));
   2711  }
   2712 
   2713  memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
   2714 
   2715  // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
   2716  // since observers will call buffer().
   2717  if (memory->hasObservers()) {
   2718    for (InstanceSet::Range r = memory->observers().all(); !r.empty();
   2719         r.popFront()) {
   2720      r.front()->instance().onMovingGrowMemory(memory);
   2721    }
   2722  }
   2723 
   2724  return oldNumPages.pageCount();
   2725 }
   2726 
   2727 /* static */
   2728 void WasmMemoryObject::discard(Handle<WasmMemoryObject*> memory,
   2729                               uint64_t byteOffset, uint64_t byteLen,
   2730                               JSContext* cx) {
   2731  if (memory->isShared()) {
   2732    Rooted<SharedArrayBufferObject*> buf(
   2733        cx, &memory->buffer().as<SharedArrayBufferObject>());
   2734    SharedArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen);
   2735  } else {
   2736    Rooted<ArrayBufferObject*> buf(cx,
   2737                                   &memory->buffer().as<ArrayBufferObject>());
   2738    ArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen);
   2739  }
   2740 }
   2741 
   2742 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) {
   2743  WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>();
   2744  return mobj && mobj->isShared();
   2745 }
   2746 
   2747 // ============================================================================
   2748 // WebAssembly.Table class and methods
   2749 
   2750 const JSClassOps WasmTableObject::classOps_ = {
   2751    nullptr,                    // addProperty
   2752    nullptr,                    // delProperty
   2753    nullptr,                    // enumerate
   2754    nullptr,                    // newEnumerate
   2755    nullptr,                    // resolve
   2756    nullptr,                    // mayResolve
   2757    WasmTableObject::finalize,  // finalize
   2758    nullptr,                    // call
   2759    nullptr,                    // construct
   2760    WasmTableObject::trace,     // trace
   2761 };
   2762 
   2763 const JSClass WasmTableObject::class_ = {
   2764    "WebAssembly.Table",
   2765    JSCLASS_DELAY_METADATA_BUILDER |
   2766        JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
   2767        JSCLASS_FOREGROUND_FINALIZE,
   2768    &WasmTableObject::classOps_,
   2769    &WasmTableObject::classSpec_,
   2770 };
   2771 
   2772 const JSClass& WasmTableObject::protoClass_ = PlainObject::class_;
   2773 
   2774 static constexpr char WasmTableName[] = "Table";
   2775 
   2776 const ClassSpec WasmTableObject::classSpec_ = {
   2777    CreateWasmConstructor<WasmTableObject, WasmTableName>,
   2778    GenericCreatePrototype<WasmTableObject>,
   2779    WasmTableObject::static_methods,
   2780    nullptr,
   2781    WasmTableObject::methods,
   2782    WasmTableObject::properties,
   2783    nullptr,
   2784    ClassSpec::DontDefineConstructor,
   2785 };
   2786 
   2787 bool WasmTableObject::isNewborn() const {
   2788  MOZ_ASSERT(is<WasmTableObject>());
   2789  return getReservedSlot(TABLE_SLOT).isUndefined();
   2790 }
   2791 
   2792 /* static */
   2793 void WasmTableObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   2794  WasmTableObject& tableObj = obj->as<WasmTableObject>();
   2795  if (!tableObj.isNewborn()) {
   2796    auto& table = tableObj.table();
   2797    gcx->release(obj, &table, table.gcMallocBytes(), MemoryUse::WasmTableTable);
   2798  }
   2799 }
   2800 
   2801 /* static */
   2802 void WasmTableObject::trace(JSTracer* trc, JSObject* obj) {
   2803  WasmTableObject& tableObj = obj->as<WasmTableObject>();
   2804  if (!tableObj.isNewborn()) {
   2805    tableObj.table().tracePrivate(trc);
   2806  }
   2807 }
   2808 
   2809 // Return the JS value to use when a parameter to a function requiring a table
   2810 // value is omitted. An implementation of [1].
   2811 //
   2812 // [1]
   2813 // https://webassembly.github.io/spec/js-api/#defaultvalue
   2814 static Value RefTypeDefaultValue(wasm::RefType tableType) {
   2815  return tableType.isExtern() ? UndefinedValue() : NullValue();
   2816 }
   2817 
   2818 /* static */
   2819 WasmTableObject* WasmTableObject::create(JSContext* cx, Limits limits,
   2820                                         wasm::RefType tableType,
   2821                                         HandleObject proto) {
   2822  AutoSetNewObjectMetadata metadata(cx);
   2823  Rooted<WasmTableObject*> obj(
   2824      cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
   2825  if (!obj) {
   2826    return nullptr;
   2827  }
   2828 
   2829  MOZ_ASSERT(obj->isNewborn());
   2830 
   2831  TableDesc td(limits, tableType, Nothing(),
   2832               /*isAsmJS*/ false,
   2833               /*isImported=*/true, /*isExported=*/true);
   2834 
   2835  SharedTable table = Table::create(cx, td, obj);
   2836  if (!table) {
   2837    return nullptr;
   2838  }
   2839 
   2840  size_t size = table->gcMallocBytes();
   2841  InitReservedSlot(obj, TABLE_SLOT, table.forget().take(), size,
   2842                   MemoryUse::WasmTableTable);
   2843 
   2844  MOZ_ASSERT(!obj->isNewborn());
   2845  return obj;
   2846 }
   2847 
   2848 /* static */
   2849 bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   2850  CallArgs args = CallArgsFromVp(argc, vp);
   2851 
   2852  if (!ThrowIfNotConstructing(cx, args, "Table")) {
   2853    return false;
   2854  }
   2855 
   2856  if (!args.requireAtLeast(cx, "WebAssembly.Table", 1)) {
   2857    return false;
   2858  }
   2859 
   2860  if (!args.get(0).isObject()) {
   2861    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2862                             JSMSG_WASM_BAD_DESC_ARG, "table");
   2863    return false;
   2864  }
   2865 
   2866  RootedObject obj(cx, &args[0].toObject());
   2867 
   2868  JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
   2869  if (!elementAtom) {
   2870    return false;
   2871  }
   2872  RootedId elementId(cx, AtomToId(elementAtom));
   2873 
   2874  RootedValue elementVal(cx);
   2875  if (!GetProperty(cx, obj, obj, elementId, &elementVal)) {
   2876    return false;
   2877  }
   2878 
   2879  RefType tableType;
   2880  if (!ToRefType(cx, elementVal, &tableType)) {
   2881    return false;
   2882  }
   2883 
   2884  Limits limits;
   2885  if (!GetLimits(cx, obj, LimitsKind::Table, &limits) ||
   2886      !CheckLimits(cx, MaxTableElemsValidation(limits.addressType),
   2887                   LimitsKind::Table, &limits)) {
   2888    return false;
   2889  }
   2890 
   2891  if (limits.initial > MaxTableElemsRuntime) {
   2892    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2893                             JSMSG_WASM_TABLE_IMP_LIMIT);
   2894    return false;
   2895  }
   2896 
   2897  RootedObject proto(cx,
   2898                     GetWasmConstructorPrototype(cx, args, JSProto_WasmTable));
   2899  if (!proto) {
   2900    ReportOutOfMemory(cx);
   2901    return false;
   2902  }
   2903 
   2904  Rooted<WasmTableObject*> table(
   2905      cx, WasmTableObject::create(cx, limits, tableType, proto));
   2906  if (!table) {
   2907    return false;
   2908  }
   2909 
   2910  // Initialize the table to a default value
   2911  RootedValue initValue(
   2912      cx, args.length() < 2 ? RefTypeDefaultValue(tableType) : args[1]);
   2913  if (!CheckRefType(cx, tableType, initValue)) {
   2914    return false;
   2915  }
   2916 
   2917  // Skip initializing the table if the fill value is null, as that is the
   2918  // default value.
   2919  if (!initValue.isNull() &&
   2920      !table->fillRange(cx, 0, limits.initial, initValue)) {
   2921    return false;
   2922  }
   2923 #ifdef DEBUG
   2924  // Assert that null is the default value of a new table.
   2925  if (initValue.isNull()) {
   2926    table->table().assertRangeNull(0, limits.initial);
   2927  }
   2928  if (!tableType.isNullable()) {
   2929    table->table().assertRangeNotNull(0, limits.initial);
   2930  }
   2931 #endif
   2932 
   2933  args.rval().setObject(*table);
   2934  return true;
   2935 }
   2936 
   2937 static bool IsTable(HandleValue v) {
   2938  return v.isObject() && v.toObject().is<WasmTableObject>();
   2939 }
   2940 
   2941 /* static */
   2942 bool WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args) {
   2943  const WasmTableObject& tableObj =
   2944      args.thisv().toObject().as<WasmTableObject>();
   2945  RootedValue length(cx);
   2946  if (!CreateAddressValue(cx, tableObj.table().length(),
   2947                          tableObj.table().addressType(), &length)) {
   2948    ReportOutOfMemory(cx);
   2949    return false;
   2950  }
   2951  args.rval().set(length);
   2952  return true;
   2953 }
   2954 
   2955 /* static */
   2956 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
   2957  CallArgs args = CallArgsFromVp(argc, vp);
   2958  return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
   2959 }
   2960 
   2961 const JSPropertySpec WasmTableObject::properties[] = {
   2962    JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE),
   2963    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY),
   2964    JS_PS_END,
   2965 };
   2966 
   2967 // Gets an AddressValue parameter for a table. This differs from our general
   2968 // EnforceAddressValue because our table implementation still uses 32-bit sizes
   2969 // internally, and this function therefore returns a uint32_t. Values outside
   2970 // the 32-bit range will be clamped to UINT32_MAX, which will always trigger
   2971 // bounds checks for all Table uses of AddressValue. See
   2972 // MacroAssembler::wasmClampTable64Address and its uses.
   2973 //
   2974 // isAddress should be true if the value is an actual address, and false if it
   2975 // is a different quantity (e.g. a grow delta).
   2976 static bool EnforceTableAddressValue(JSContext* cx, HandleValue v,
   2977                                     const Table& table, const char* noun,
   2978                                     uint32_t* result, bool isAddress) {
   2979  uint64_t result64;
   2980  if (!EnforceAddressValue(cx, v, table.addressType(), "Table", noun,
   2981                           &result64)) {
   2982    return false;
   2983  }
   2984 
   2985  static_assert(MaxTableElemsRuntime < UINT32_MAX);
   2986  *result = result64 > UINT32_MAX ? UINT32_MAX : uint32_t(result64);
   2987 
   2988  if (isAddress && *result >= table.length()) {
   2989    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   2990                              JSMSG_WASM_BAD_RANGE, "Table", noun);
   2991    return false;
   2992  }
   2993 
   2994  return true;
   2995 }
   2996 
   2997 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   2998 /* static */
   2999 bool WasmTableObject::typeImpl(JSContext* cx, const CallArgs& args) {
   3000  Table& table = args.thisv().toObject().as<WasmTableObject>().table();
   3001  RootedObject typeObj(
   3002      cx, TableTypeToObject(cx, table.addressType(), table.elemType(),
   3003                            table.length(), table.maximum()));
   3004  if (!typeObj) {
   3005    return false;
   3006  }
   3007  args.rval().setObject(*typeObj);
   3008  return true;
   3009 }
   3010 
   3011 /* static */
   3012 bool WasmTableObject::type(JSContext* cx, unsigned argc, Value* vp) {
   3013  CallArgs args = CallArgsFromVp(argc, vp);
   3014  return CallNonGenericMethod<IsTable, typeImpl>(cx, args);
   3015 }
   3016 #endif
   3017 
   3018 /* static */
   3019 bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) {
   3020  Rooted<WasmTableObject*> tableObj(
   3021      cx, &args.thisv().toObject().as<WasmTableObject>());
   3022  const Table& table = tableObj->table();
   3023 
   3024  if (!args.requireAtLeast(cx, "WebAssembly.Table.get", 1)) {
   3025    return false;
   3026  }
   3027 
   3028  uint32_t address;
   3029  if (!EnforceTableAddressValue(cx, args.get(0), table, "get address", &address,
   3030                                /*isAddress=*/true)) {
   3031    return false;
   3032  }
   3033 
   3034  return table.getValue(cx, address, args.rval());
   3035 }
   3036 
   3037 /* static */
   3038 bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) {
   3039  CallArgs args = CallArgsFromVp(argc, vp);
   3040  return CallNonGenericMethod<IsTable, getImpl>(cx, args);
   3041 }
   3042 
   3043 /* static */
   3044 bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
   3045  Rooted<WasmTableObject*> tableObj(
   3046      cx, &args.thisv().toObject().as<WasmTableObject>());
   3047  Table& table = tableObj->table();
   3048 
   3049  if (!args.requireAtLeast(cx, "WebAssembly.Table.set", 1)) {
   3050    return false;
   3051  }
   3052 
   3053  uint32_t address;
   3054  if (!EnforceTableAddressValue(cx, args.get(0), table, "set address", &address,
   3055                                /*isAddress=*/true)) {
   3056    return false;
   3057  }
   3058 
   3059  RootedValue fillValue(
   3060      cx, args.length() < 2 ? RefTypeDefaultValue(table.elemType()) : args[1]);
   3061  if (!tableObj->fillRange(cx, address, 1, fillValue)) {
   3062    return false;
   3063  }
   3064 
   3065  args.rval().setUndefined();
   3066  return true;
   3067 }
   3068 
   3069 /* static */
   3070 bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) {
   3071  CallArgs args = CallArgsFromVp(argc, vp);
   3072  return CallNonGenericMethod<IsTable, setImpl>(cx, args);
   3073 }
   3074 
   3075 /* static */
   3076 bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
   3077  Rooted<WasmTableObject*> tableObj(
   3078      cx, &args.thisv().toObject().as<WasmTableObject>());
   3079  Table& table = tableObj->table();
   3080 
   3081  if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) {
   3082    return false;
   3083  }
   3084 
   3085  uint32_t delta;
   3086  if (!EnforceTableAddressValue(cx, args.get(0), table, "grow delta", &delta,
   3087                                /*isAddress=*/false)) {
   3088    return false;
   3089  }
   3090 
   3091  RootedValue fillValue(
   3092      cx, args.length() < 2 ? RefTypeDefaultValue(table.elemType()) : args[1]);
   3093  if (!CheckRefType(cx, table.elemType(), fillValue)) {
   3094    return false;
   3095  }
   3096 
   3097  uint32_t oldLength = table.grow(delta);
   3098 
   3099  if (oldLength == uint32_t(-1)) {
   3100    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
   3101                             "table");
   3102    return false;
   3103  }
   3104 
   3105  // Skip filling the grown range of the table if the fill value is null, as
   3106  // that is the default value.
   3107  if (!fillValue.isNull() &&
   3108      !tableObj->fillRange(cx, oldLength, delta, fillValue)) {
   3109    return false;
   3110  }
   3111 #ifdef DEBUG
   3112  // Assert that null is the default value of the grown range.
   3113  if (fillValue.isNull()) {
   3114    table.assertRangeNull(oldLength, delta);
   3115  }
   3116  if (!table.elemType().isNullable()) {
   3117    table.assertRangeNotNull(oldLength, delta);
   3118  }
   3119 #endif
   3120 
   3121  RootedValue result(cx);
   3122  if (!CreateAddressValue(cx, oldLength, table.addressType(), &result)) {
   3123    ReportOutOfMemory(cx);
   3124    return false;
   3125  }
   3126  args.rval().set(result);
   3127  return true;
   3128 }
   3129 
   3130 /* static */
   3131 bool WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp) {
   3132  CallArgs args = CallArgsFromVp(argc, vp);
   3133  return CallNonGenericMethod<IsTable, growImpl>(cx, args);
   3134 }
   3135 
   3136 const JSFunctionSpec WasmTableObject::methods[] = {
   3137 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   3138    JS_FN("type", WasmTableObject::type, 0, JSPROP_ENUMERATE),
   3139 #endif
   3140    JS_FN("get", WasmTableObject::get, 1, JSPROP_ENUMERATE),
   3141    JS_FN("set", WasmTableObject::set, 2, JSPROP_ENUMERATE),
   3142    JS_FN("grow", WasmTableObject::grow, 1, JSPROP_ENUMERATE),
   3143    JS_FS_END,
   3144 };
   3145 
   3146 const JSFunctionSpec WasmTableObject::static_methods[] = {
   3147    JS_FS_END,
   3148 };
   3149 
   3150 Table& WasmTableObject::table() const {
   3151  return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
   3152 }
   3153 
   3154 bool WasmTableObject::fillRange(JSContext* cx, uint32_t index, uint32_t length,
   3155                                HandleValue value) const {
   3156  Table& tab = table();
   3157 
   3158  // All consumers are required to either bounds check or statically be in
   3159  // bounds
   3160  MOZ_ASSERT(uint64_t(index) + uint64_t(length) <= tab.length());
   3161 
   3162  RootedAnyRef any(cx, AnyRef::null());
   3163  if (!wasm::CheckRefType(cx, tab.elemType(), value, &any)) {
   3164    return false;
   3165  }
   3166  switch (tab.repr()) {
   3167    case TableRepr::Func:
   3168      MOZ_RELEASE_ASSERT(!tab.isAsmJS());
   3169      tab.fillFuncRef(index, length, FuncRef::fromAnyRefUnchecked(any.get()),
   3170                      cx);
   3171      break;
   3172    case TableRepr::Ref:
   3173      tab.fillAnyRef(index, length, any);
   3174      break;
   3175  }
   3176  return true;
   3177 }
   3178 
   3179 // ============================================================================
   3180 // WebAssembly.global class and methods
   3181 
   3182 const JSClassOps WasmGlobalObject::classOps_ = {
   3183    nullptr,                     // addProperty
   3184    nullptr,                     // delProperty
   3185    nullptr,                     // enumerate
   3186    nullptr,                     // newEnumerate
   3187    nullptr,                     // resolve
   3188    nullptr,                     // mayResolve
   3189    WasmGlobalObject::finalize,  // finalize
   3190    nullptr,                     // call
   3191    nullptr,                     // construct
   3192    WasmGlobalObject::trace,     // trace
   3193 };
   3194 
   3195 const JSClass WasmGlobalObject::class_ = {
   3196    "WebAssembly.Global",
   3197    JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
   3198        JSCLASS_BACKGROUND_FINALIZE,
   3199    &WasmGlobalObject::classOps_,
   3200    &WasmGlobalObject::classSpec_,
   3201 };
   3202 
   3203 const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_;
   3204 
   3205 static constexpr char WasmGlobalName[] = "Global";
   3206 
   3207 const ClassSpec WasmGlobalObject::classSpec_ = {
   3208    CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>,
   3209    GenericCreatePrototype<WasmGlobalObject>,
   3210    WasmGlobalObject::static_methods,
   3211    nullptr,
   3212    WasmGlobalObject::methods,
   3213    WasmGlobalObject::properties,
   3214    nullptr,
   3215    ClassSpec::DontDefineConstructor,
   3216 };
   3217 
   3218 /* static */
   3219 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
   3220  WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
   3221  if (global->isNewborn()) {
   3222    // This can happen while we're allocating the object, in which case
   3223    // every single slot of the object is not defined yet. In particular,
   3224    // there's nothing to trace yet.
   3225    return;
   3226  }
   3227  global->val().get().trace(trc);
   3228 }
   3229 
   3230 /* static */
   3231 void WasmGlobalObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   3232  WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
   3233  if (!global->isNewborn()) {
   3234    // Release the strong reference to the type definitions this global could
   3235    // be referencing.
   3236    global->type().Release();
   3237    gcx->delete_(obj, &global->mutableVal(), MemoryUse::WasmGlobalCell);
   3238  }
   3239 }
   3240 
   3241 /* static */
   3242 WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value,
   3243                                           bool isMutable, HandleObject proto) {
   3244  Rooted<WasmGlobalObject*> obj(
   3245      cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
   3246  if (!obj) {
   3247    return nullptr;
   3248  }
   3249 
   3250  MOZ_ASSERT(obj->isNewborn());
   3251  MOZ_ASSERT(obj->isTenured(), "assumed by global.set post barriers");
   3252 
   3253  GCPtrVal* val = js_new<GCPtrVal>(Val());
   3254  if (!val) {
   3255    ReportOutOfMemory(cx);
   3256    return nullptr;
   3257  }
   3258  obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
   3259  InitReservedSlot(obj, VAL_SLOT, val, MemoryUse::WasmGlobalCell);
   3260 
   3261  // It's simpler to initialize the cell after the object has been created,
   3262  // to avoid needing to root the cell before the object creation.
   3263  // We don't use `setVal` here because the assumes the cell has already
   3264  // been initialized.
   3265  obj->mutableVal() = value.get();
   3266  // Acquire a strong reference to a type definition this global could
   3267  // be referencing.
   3268  obj->type().AddRef();
   3269 
   3270  MOZ_ASSERT(!obj->isNewborn());
   3271 
   3272  return obj;
   3273 }
   3274 
   3275 /* static */
   3276 bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   3277  CallArgs args = CallArgsFromVp(argc, vp);
   3278 
   3279  if (!ThrowIfNotConstructing(cx, args, "Global")) {
   3280    return false;
   3281  }
   3282 
   3283  if (!args.requireAtLeast(cx, "WebAssembly.Global", 1)) {
   3284    return false;
   3285  }
   3286 
   3287  if (!args.get(0).isObject()) {
   3288    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3289                             JSMSG_WASM_BAD_DESC_ARG, "global");
   3290    return false;
   3291  }
   3292 
   3293  RootedObject obj(cx, &args[0].toObject());
   3294 
   3295  // Extract properties in lexicographic order per spec.
   3296 
   3297  RootedValue mutableVal(cx);
   3298  if (!JS_GetProperty(cx, obj, "mutable", &mutableVal)) {
   3299    return false;
   3300  }
   3301 
   3302  RootedValue typeVal(cx);
   3303  if (!JS_GetProperty(cx, obj, "value", &typeVal)) {
   3304    return false;
   3305  }
   3306 
   3307  ValType globalType;
   3308  if (!ToValType(cx, typeVal, &globalType)) {
   3309    return false;
   3310  }
   3311 
   3312  if (!globalType.isExposable()) {
   3313    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3314                             JSMSG_WASM_BAD_VAL_TYPE);
   3315    return false;
   3316  }
   3317 
   3318  bool isMutable = ToBoolean(mutableVal);
   3319 
   3320  // Extract the initial value, or provide a suitable default.
   3321  RootedVal globalVal(cx, globalType);
   3322 
   3323  // Override with non-undefined value, if provided.
   3324  RootedValue valueVal(cx);
   3325  if (globalType.isRefType()) {
   3326    valueVal.set(args.length() < 2 ? RefTypeDefaultValue(globalType.refType())
   3327                                   : args[1]);
   3328    if (!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
   3329      return false;
   3330    }
   3331  } else {
   3332    valueVal.set(args.get(1));
   3333    if (!valueVal.isUndefined() &&
   3334        !Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
   3335      return false;
   3336    }
   3337  }
   3338 
   3339  RootedObject proto(cx,
   3340                     GetWasmConstructorPrototype(cx, args, JSProto_WasmGlobal));
   3341  if (!proto) {
   3342    ReportOutOfMemory(cx);
   3343    return false;
   3344  }
   3345 
   3346  WasmGlobalObject* global =
   3347      WasmGlobalObject::create(cx, globalVal, isMutable, proto);
   3348  if (!global) {
   3349    return false;
   3350  }
   3351 
   3352  args.rval().setObject(*global);
   3353  return true;
   3354 }
   3355 
   3356 static bool IsGlobal(HandleValue v) {
   3357  return v.isObject() && v.toObject().is<WasmGlobalObject>();
   3358 }
   3359 
   3360 /* static */
   3361 bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) {
   3362  const WasmGlobalObject& globalObj =
   3363      args.thisv().toObject().as<WasmGlobalObject>();
   3364  if (!globalObj.type().isExposable()) {
   3365    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3366                             JSMSG_WASM_BAD_VAL_TYPE);
   3367    return false;
   3368  }
   3369  return globalObj.val().get().toJSValue(cx, args.rval());
   3370 }
   3371 
   3372 /* static */
   3373 bool WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp) {
   3374  CallArgs args = CallArgsFromVp(argc, vp);
   3375  return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
   3376 }
   3377 
   3378 /* static */
   3379 bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) {
   3380  if (!args.requireAtLeast(cx, "WebAssembly.Global setter", 1)) {
   3381    return false;
   3382  }
   3383 
   3384  Rooted<WasmGlobalObject*> global(
   3385      cx, &args.thisv().toObject().as<WasmGlobalObject>());
   3386  if (!global->isMutable()) {
   3387    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3388                             JSMSG_WASM_GLOBAL_IMMUTABLE);
   3389    return false;
   3390  }
   3391 
   3392  RootedVal val(cx);
   3393  if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) {
   3394    return false;
   3395  }
   3396  global->setVal(val);
   3397 
   3398  args.rval().setUndefined();
   3399  return true;
   3400 }
   3401 
   3402 /* static */
   3403 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) {
   3404  CallArgs args = CallArgsFromVp(argc, vp);
   3405  return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
   3406 }
   3407 
   3408 const JSPropertySpec WasmGlobalObject::properties[] = {
   3409    JS_PSGS("value", WasmGlobalObject::valueGetter,
   3410            WasmGlobalObject::valueSetter, JSPROP_ENUMERATE),
   3411    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY),
   3412    JS_PS_END,
   3413 };
   3414 
   3415 const JSFunctionSpec WasmGlobalObject::methods[] = {
   3416 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   3417    JS_FN("type", WasmGlobalObject::type, 0, JSPROP_ENUMERATE),
   3418 #endif
   3419    JS_FN("valueOf", WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE),
   3420    JS_FS_END,
   3421 };
   3422 
   3423 const JSFunctionSpec WasmGlobalObject::static_methods[] = {
   3424    JS_FS_END,
   3425 };
   3426 
   3427 bool WasmGlobalObject::isMutable() const {
   3428  return getReservedSlot(MUTABLE_SLOT).toBoolean();
   3429 }
   3430 
   3431 ValType WasmGlobalObject::type() const { return val().get().type(); }
   3432 
   3433 GCPtrVal& WasmGlobalObject::mutableVal() {
   3434  return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate());
   3435 }
   3436 
   3437 const GCPtrVal& WasmGlobalObject::val() const {
   3438  return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate());
   3439 }
   3440 
   3441 void WasmGlobalObject::setVal(wasm::HandleVal value) {
   3442  MOZ_ASSERT(type() == value.get().type());
   3443  mutableVal() = value;
   3444 }
   3445 
   3446 void* WasmGlobalObject::addressOfCell() const {
   3447  return (void*)&val().get().cell();
   3448 }
   3449 
   3450 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   3451 /* static */
   3452 bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) {
   3453  Rooted<WasmGlobalObject*> global(
   3454      cx, &args.thisv().toObject().as<WasmGlobalObject>());
   3455  RootedObject typeObj(
   3456      cx, GlobalTypeToObject(cx, global->type(), global->isMutable()));
   3457  if (!typeObj) {
   3458    return false;
   3459  }
   3460  args.rval().setObject(*typeObj);
   3461  return true;
   3462 }
   3463 
   3464 /* static */
   3465 bool WasmGlobalObject::type(JSContext* cx, unsigned argc, Value* vp) {
   3466  CallArgs args = CallArgsFromVp(argc, vp);
   3467  return CallNonGenericMethod<IsGlobal, typeImpl>(cx, args);
   3468 }
   3469 #endif
   3470 
   3471 // ============================================================================
   3472 // WebAssembly.Tag class and methods
   3473 
   3474 const JSClassOps WasmTagObject::classOps_ = {
   3475    nullptr,                  // addProperty
   3476    nullptr,                  // delProperty
   3477    nullptr,                  // enumerate
   3478    nullptr,                  // newEnumerate
   3479    nullptr,                  // resolve
   3480    nullptr,                  // mayResolve
   3481    WasmTagObject::finalize,  // finalize
   3482    nullptr,                  // call
   3483    nullptr,                  // construct
   3484    nullptr,                  // trace
   3485 };
   3486 
   3487 const JSClass WasmTagObject::class_ = {
   3488    "WebAssembly.Tag",
   3489    JSCLASS_HAS_RESERVED_SLOTS(WasmTagObject::RESERVED_SLOTS) |
   3490        JSCLASS_FOREGROUND_FINALIZE,
   3491    &WasmTagObject::classOps_,
   3492    &WasmTagObject::classSpec_,
   3493 };
   3494 
   3495 const JSClass& WasmTagObject::protoClass_ = PlainObject::class_;
   3496 
   3497 static constexpr char WasmTagName[] = "Tag";
   3498 
   3499 const ClassSpec WasmTagObject::classSpec_ = {
   3500    CreateWasmConstructor<WasmTagObject, WasmTagName>,
   3501    GenericCreatePrototype<WasmTagObject>,
   3502    WasmTagObject::static_methods,
   3503    nullptr,
   3504    WasmTagObject::methods,
   3505    WasmTagObject::properties,
   3506    nullptr,
   3507    ClassSpec::DontDefineConstructor,
   3508 };
   3509 
   3510 /* static */
   3511 void WasmTagObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   3512  WasmTagObject& tagObj = obj->as<WasmTagObject>();
   3513  tagObj.tagType()->Release();
   3514 }
   3515 
   3516 static bool IsTag(HandleValue v) {
   3517  return v.isObject() && v.toObject().is<WasmTagObject>();
   3518 }
   3519 
   3520 bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   3521  CallArgs args = CallArgsFromVp(argc, vp);
   3522 
   3523  if (!ThrowIfNotConstructing(cx, args, "Tag")) {
   3524    return false;
   3525  }
   3526 
   3527  if (!args.requireAtLeast(cx, "WebAssembly.Tag", 1)) {
   3528    return false;
   3529  }
   3530 
   3531  if (!args.get(0).isObject()) {
   3532    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3533                             JSMSG_WASM_BAD_DESC_ARG, "tag");
   3534    return false;
   3535  }
   3536 
   3537  RootedObject obj(cx, &args[0].toObject());
   3538  RootedValue paramsVal(cx);
   3539  if (!JS_GetProperty(cx, obj, "parameters", &paramsVal)) {
   3540    return false;
   3541  }
   3542 
   3543  ValTypeVector params;
   3544  if (!ParseValTypes(cx, paramsVal, params)) {
   3545    return false;
   3546  }
   3547  if (params.length() > MaxParams) {
   3548    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3549                             JSMSG_WASM_BAD_EXN_TAG_PARAMS);
   3550    return false;
   3551  }
   3552 
   3553  RefPtr<TypeContext> types = js_new<TypeContext>();
   3554  if (!types) {
   3555    ReportOutOfMemory(cx);
   3556    return false;
   3557  }
   3558  const TypeDef* tagTypeDef =
   3559      types->addType(FuncType(std::move(params), ValTypeVector()));
   3560  if (!tagTypeDef) {
   3561    ReportOutOfMemory(cx);
   3562    return false;
   3563  }
   3564 
   3565  wasm::MutableTagType tagType = js_new<wasm::TagType>();
   3566  if (!tagType || !tagType->initialize(tagTypeDef)) {
   3567    ReportOutOfMemory(cx);
   3568    return false;
   3569  }
   3570 
   3571  RootedObject proto(cx,
   3572                     GetWasmConstructorPrototype(cx, args, JSProto_WasmTag));
   3573  if (!proto) {
   3574    ReportOutOfMemory(cx);
   3575    return false;
   3576  }
   3577 
   3578  Rooted<WasmTagObject*> tagObj(cx, WasmTagObject::create(cx, tagType, proto));
   3579  if (!tagObj) {
   3580    return false;
   3581  }
   3582 
   3583  args.rval().setObject(*tagObj);
   3584  return true;
   3585 }
   3586 
   3587 /* static */
   3588 WasmTagObject* WasmTagObject::create(JSContext* cx,
   3589                                     const wasm::SharedTagType& tagType,
   3590                                     HandleObject proto) {
   3591  Rooted<WasmTagObject*> obj(cx,
   3592                             NewObjectWithGivenProto<WasmTagObject>(cx, proto));
   3593  if (!obj) {
   3594    return nullptr;
   3595  }
   3596 
   3597  tagType.get()->AddRef();
   3598  obj->initReservedSlot(TYPE_SLOT, PrivateValue((void*)tagType.get()));
   3599 
   3600  return obj;
   3601 }
   3602 
   3603 const JSPropertySpec WasmTagObject::properties[] = {
   3604    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Tag", JSPROP_READONLY),
   3605    JS_PS_END,
   3606 };
   3607 
   3608 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   3609 /* static */
   3610 bool WasmTagObject::typeImpl(JSContext* cx, const CallArgs& args) {
   3611  Rooted<WasmTagObject*> tag(cx, &args.thisv().toObject().as<WasmTagObject>());
   3612  RootedObject typeObj(cx, TagTypeToObject(cx, tag->valueTypes()));
   3613  if (!typeObj) {
   3614    return false;
   3615  }
   3616  args.rval().setObject(*typeObj);
   3617  return true;
   3618 }
   3619 
   3620 /* static  */
   3621 bool WasmTagObject::type(JSContext* cx, unsigned argc, Value* vp) {
   3622  CallArgs args = CallArgsFromVp(argc, vp);
   3623  return CallNonGenericMethod<IsTag, typeImpl>(cx, args);
   3624 }
   3625 #endif
   3626 
   3627 const JSFunctionSpec WasmTagObject::methods[] = {
   3628 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   3629    JS_FN("type", WasmTagObject::type, 0, JSPROP_ENUMERATE),
   3630 #endif
   3631    JS_FS_END,
   3632 };
   3633 
   3634 const JSFunctionSpec WasmTagObject::static_methods[] = {
   3635    JS_FS_END,
   3636 };
   3637 
   3638 const TagType* WasmTagObject::tagType() const {
   3639  return (const TagType*)getFixedSlot(TYPE_SLOT).toPrivate();
   3640 };
   3641 
   3642 const wasm::ValTypeVector& WasmTagObject::valueTypes() const {
   3643  return tagType()->argTypes();
   3644 };
   3645 
   3646 // ============================================================================
   3647 // WebAssembly.Exception class and methods
   3648 
   3649 const JSClassOps WasmExceptionObject::classOps_ = {
   3650    nullptr,                        // addProperty
   3651    nullptr,                        // delProperty
   3652    nullptr,                        // enumerate
   3653    nullptr,                        // newEnumerate
   3654    nullptr,                        // resolve
   3655    nullptr,                        // mayResolve
   3656    WasmExceptionObject::finalize,  // finalize
   3657    nullptr,                        // call
   3658    nullptr,                        // construct
   3659    WasmExceptionObject::trace,     // trace
   3660 };
   3661 
   3662 const JSClass WasmExceptionObject::class_ = {
   3663    "WebAssembly.Exception",
   3664    JSCLASS_HAS_RESERVED_SLOTS(WasmExceptionObject::RESERVED_SLOTS) |
   3665        JSCLASS_FOREGROUND_FINALIZE,
   3666    &WasmExceptionObject::classOps_,
   3667    &WasmExceptionObject::classSpec_,
   3668 };
   3669 
   3670 const JSClass& WasmExceptionObject::protoClass_ = PlainObject::class_;
   3671 
   3672 static constexpr char WasmExceptionName[] = "Exception";
   3673 
   3674 const ClassSpec WasmExceptionObject::classSpec_ = {
   3675    CreateWasmConstructor<WasmExceptionObject, WasmExceptionName>,
   3676    GenericCreatePrototype<WasmExceptionObject>,
   3677    WasmExceptionObject::static_methods,
   3678    nullptr,
   3679    WasmExceptionObject::methods,
   3680    WasmExceptionObject::properties,
   3681    nullptr,
   3682    ClassSpec::DontDefineConstructor,
   3683 };
   3684 
   3685 /* static */
   3686 void WasmExceptionObject::finalize(JS::GCContext* gcx, JSObject* obj) {
   3687  WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>();
   3688  if (exnObj.isNewborn()) {
   3689    return;
   3690  }
   3691  gcx->free_(obj, exnObj.typedMem(), exnObj.tagType()->tagSize(),
   3692             MemoryUse::WasmExceptionData);
   3693  exnObj.tagType()->Release();
   3694 }
   3695 
   3696 /* static */
   3697 void WasmExceptionObject::trace(JSTracer* trc, JSObject* obj) {
   3698  WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>();
   3699  if (exnObj.isNewborn()) {
   3700    return;
   3701  }
   3702 
   3703  wasm::SharedTagType tag = exnObj.tagType();
   3704  const wasm::ValTypeVector& params = tag->argTypes();
   3705  const wasm::TagOffsetVector& offsets = tag->argOffsets();
   3706  uint8_t* typedMem = exnObj.typedMem();
   3707  for (size_t i = 0; i < params.length(); i++) {
   3708    ValType paramType = params[i];
   3709    if (paramType.isRefRepr()) {
   3710      GCPtr<wasm::AnyRef>* paramPtr =
   3711          reinterpret_cast<GCPtr<AnyRef>*>(typedMem + offsets[i]);
   3712      TraceNullableEdge(trc, paramPtr, "wasm exception param");
   3713    }
   3714  }
   3715 }
   3716 
   3717 static bool IsException(HandleValue v) {
   3718  return v.isObject() && v.toObject().is<WasmExceptionObject>();
   3719 }
   3720 
   3721 struct ExceptionOptions {
   3722  bool traceStack;
   3723 
   3724  ExceptionOptions() : traceStack(false) {}
   3725 
   3726  [[nodiscard]] bool init(JSContext* cx, HandleValue val);
   3727 };
   3728 
   3729 bool ExceptionOptions::init(JSContext* cx, HandleValue val) {
   3730  if (val.isNullOrUndefined()) {
   3731    return true;
   3732  }
   3733 
   3734  if (!val.isObject()) {
   3735    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3736                             JSMSG_WASM_BAD_EXN_OPTIONS);
   3737    return false;
   3738  }
   3739  RootedObject obj(cx, &val.toObject());
   3740 
   3741  // Get `traceStack` and coerce to boolean
   3742  RootedValue traceStackVal(cx);
   3743  if (!JS_GetProperty(cx, obj, "traceStack", &traceStackVal)) {
   3744    return false;
   3745  }
   3746  traceStack = ToBoolean(traceStackVal);
   3747 
   3748  return true;
   3749 }
   3750 
   3751 bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   3752  CallArgs args = CallArgsFromVp(argc, vp);
   3753 
   3754  if (!ThrowIfNotConstructing(cx, args, "Exception")) {
   3755    return false;
   3756  }
   3757 
   3758  if (!args.requireAtLeast(cx, "WebAssembly.Exception", 2)) {
   3759    return false;
   3760  }
   3761 
   3762  if (!IsTag(args[0])) {
   3763    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3764                             JSMSG_WASM_BAD_EXN_ARG);
   3765    return false;
   3766  }
   3767  Rooted<WasmTagObject*> exnTag(cx, &args[0].toObject().as<WasmTagObject>());
   3768 
   3769  if (!args.get(1).isObject()) {
   3770    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3771                             JSMSG_WASM_BAD_EXN_PAYLOAD);
   3772    return false;
   3773  }
   3774 
   3775  JS::ForOfIterator iterator(cx);
   3776  if (!iterator.init(args.get(1), JS::ForOfIterator::ThrowOnNonIterable)) {
   3777    return false;
   3778  }
   3779 
   3780  // Get the optional 'options' parameter
   3781  ExceptionOptions options;
   3782  if (!options.init(cx, args.get(2))) {
   3783    return false;
   3784  }
   3785 
   3786  // Trace the stack if requested
   3787  RootedObject stack(cx);
   3788  bool captureStack =
   3789      options.traceStack || JS::Prefs::wasm_exception_force_stack_trace();
   3790  if (captureStack && !CaptureStack(cx, &stack)) {
   3791    ReportOutOfMemory(cx);
   3792    return false;
   3793  }
   3794 
   3795  RootedObject proto(
   3796      cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmException));
   3797  if (!proto) {
   3798    ReportOutOfMemory(cx);
   3799    return false;
   3800  }
   3801 
   3802  Rooted<WasmExceptionObject*> exnObj(
   3803      cx, WasmExceptionObject::create(cx, exnTag, stack, proto));
   3804  if (!exnObj) {
   3805    return false;
   3806  }
   3807 
   3808  wasm::SharedTagType tagType = exnObj->tagType();
   3809  const wasm::ValTypeVector& params = tagType->argTypes();
   3810  const wasm::TagOffsetVector& offsets = tagType->argOffsets();
   3811 
   3812  RootedValue nextArg(cx);
   3813  for (size_t i = 0; i < params.length(); i++) {
   3814    bool done;
   3815    if (!iterator.next(&nextArg, &done)) {
   3816      return false;
   3817    }
   3818    if (done) {
   3819      UniqueChars expected(JS_smprintf("%zu", params.length()));
   3820      UniqueChars got(JS_smprintf("%zu", i));
   3821      if (!expected || !got) {
   3822        ReportOutOfMemory(cx);
   3823        return false;
   3824      }
   3825 
   3826      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3827                               JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, expected.get(),
   3828                               got.get());
   3829      return false;
   3830    }
   3831 
   3832    if (!exnObj->initArg(cx, offsets[i], params[i], nextArg)) {
   3833      return false;
   3834    }
   3835  }
   3836 
   3837  args.rval().setObject(*exnObj);
   3838  return true;
   3839 }
   3840 
   3841 /* static */
   3842 WasmExceptionObject* WasmExceptionObject::create(JSContext* cx,
   3843                                                 Handle<WasmTagObject*> tag,
   3844                                                 HandleObject stack,
   3845                                                 HandleObject proto) {
   3846  Rooted<WasmExceptionObject*> obj(
   3847      cx, NewObjectWithGivenProto<WasmExceptionObject>(cx, proto));
   3848  if (!obj) {
   3849    return nullptr;
   3850  }
   3851  const TagType* tagType = tag->tagType();
   3852 
   3853  // Allocate the data buffer before initializing the object so that an OOM
   3854  // does not result in a partially constructed object.
   3855  uint8_t* data = (uint8_t*)js_calloc(tagType->tagSize());
   3856  if (!data) {
   3857    ReportOutOfMemory(cx);
   3858    return nullptr;
   3859  }
   3860 
   3861  MOZ_ASSERT(obj->isNewborn());
   3862  obj->initFixedSlot(TAG_SLOT, ObjectValue(*tag));
   3863  tagType->AddRef();
   3864  obj->initFixedSlot(TYPE_SLOT, PrivateValue((void*)tagType));
   3865  InitReservedSlot(obj, DATA_SLOT, data, tagType->tagSize(),
   3866                   MemoryUse::WasmExceptionData);
   3867  obj->initFixedSlot(STACK_SLOT, ObjectOrNullValue(stack));
   3868 
   3869  MOZ_ASSERT(!obj->isNewborn());
   3870 
   3871  return obj;
   3872 }
   3873 
   3874 WasmExceptionObject* WasmExceptionObject::wrapJSValue(JSContext* cx,
   3875                                                      HandleValue value) {
   3876  Rooted<WasmNamespaceObject*> wasm(cx, WasmNamespaceObject::getOrCreate(cx));
   3877  if (!wasm) {
   3878    return nullptr;
   3879  }
   3880 
   3881  Rooted<AnyRef> valueAnyRef(cx);
   3882  if (!AnyRef::fromJSValue(cx, value, &valueAnyRef)) {
   3883    return nullptr;
   3884  }
   3885 
   3886  Rooted<WasmTagObject*> wrappedJSValueTag(cx, wasm->wrappedJSValueTag());
   3887  WasmExceptionObject* exn =
   3888      WasmExceptionObject::create(cx, wrappedJSValueTag, nullptr, nullptr);
   3889  if (!exn) {
   3890    return nullptr;
   3891  }
   3892  MOZ_ASSERT(exn->isWrappedJSValue());
   3893 
   3894  exn->initRefArg(WrappedJSValueTagType_ValueOffset, valueAnyRef);
   3895  return exn;
   3896 }
   3897 
   3898 bool WasmExceptionObject::isNewborn() const {
   3899  MOZ_ASSERT(is<WasmExceptionObject>());
   3900  return getReservedSlot(DATA_SLOT).isUndefined();
   3901 }
   3902 
   3903 bool WasmExceptionObject::isWrappedJSValue() const {
   3904  return tagType() == sWrappedJSValueTagType;
   3905 }
   3906 
   3907 Value WasmExceptionObject::wrappedJSValue() const {
   3908  MOZ_ASSERT(isWrappedJSValue());
   3909  return loadRefArg(WrappedJSValueTagType_ValueOffset).toJSValue();
   3910 }
   3911 
   3912 const JSPropertySpec WasmExceptionObject::properties[] = {
   3913    JS_PSG("stack", WasmExceptionObject::getStack, 0),
   3914    JS_STRING_SYM_PS(toStringTag, "WebAssembly.Exception", JSPROP_READONLY),
   3915    JS_PS_END,
   3916 };
   3917 
   3918 /* static */
   3919 bool WasmExceptionObject::isImpl(JSContext* cx, const CallArgs& args) {
   3920  Rooted<WasmExceptionObject*> exnObj(
   3921      cx, &args.thisv().toObject().as<WasmExceptionObject>());
   3922 
   3923  if (!args.requireAtLeast(cx, "WebAssembly.Exception.is", 1)) {
   3924    return false;
   3925  }
   3926 
   3927  if (!IsTag(args[0])) {
   3928    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3929                             JSMSG_WASM_BAD_EXN_ARG);
   3930    return false;
   3931  }
   3932 
   3933  Rooted<WasmTagObject*> exnTag(cx,
   3934                                &args.get(0).toObject().as<WasmTagObject>());
   3935  args.rval().setBoolean(exnTag.get() == &exnObj->tag());
   3936 
   3937  return true;
   3938 }
   3939 
   3940 /* static  */
   3941 bool WasmExceptionObject::isMethod(JSContext* cx, unsigned argc, Value* vp) {
   3942  CallArgs args = CallArgsFromVp(argc, vp);
   3943  return CallNonGenericMethod<IsException, isImpl>(cx, args);
   3944 }
   3945 
   3946 /* static */
   3947 bool WasmExceptionObject::getArgImpl(JSContext* cx, const CallArgs& args) {
   3948  Rooted<WasmExceptionObject*> exnObj(
   3949      cx, &args.thisv().toObject().as<WasmExceptionObject>());
   3950 
   3951  if (!args.requireAtLeast(cx, "WebAssembly.Exception.getArg", 2)) {
   3952    return false;
   3953  }
   3954 
   3955  if (!IsTag(args[0])) {
   3956    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3957                             JSMSG_WASM_BAD_EXN_ARG);
   3958    return false;
   3959  }
   3960 
   3961  Rooted<WasmTagObject*> exnTag(cx,
   3962                                &args.get(0).toObject().as<WasmTagObject>());
   3963  if (exnTag.get() != &exnObj->tag()) {
   3964    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   3965                             JSMSG_WASM_BAD_EXN_TAG);
   3966    return false;
   3967  }
   3968 
   3969  uint32_t index;
   3970  if (!EnforceRangeU32(cx, args.get(1), "Exception", "getArg index", &index)) {
   3971    return false;
   3972  }
   3973 
   3974  const wasm::ValTypeVector& params = exnTag->valueTypes();
   3975  if (index >= params.length()) {
   3976    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
   3977                             "Exception", "getArg index");
   3978    return false;
   3979  }
   3980 
   3981  uint32_t offset = exnTag->tagType()->argOffsets()[index];
   3982  RootedValue result(cx);
   3983  if (!exnObj->loadArg(cx, offset, params[index], &result)) {
   3984    return false;
   3985  }
   3986  args.rval().set(result);
   3987  return true;
   3988 }
   3989 
   3990 /* static  */
   3991 bool WasmExceptionObject::getArg(JSContext* cx, unsigned argc, Value* vp) {
   3992  CallArgs args = CallArgsFromVp(argc, vp);
   3993  return CallNonGenericMethod<IsException, getArgImpl>(cx, args);
   3994 }
   3995 
   3996 /* static */
   3997 bool WasmExceptionObject::getStack_impl(JSContext* cx, const CallArgs& args) {
   3998  Rooted<WasmExceptionObject*> exnObj(
   3999      cx, &args.thisv().toObject().as<WasmExceptionObject>());
   4000  RootedObject savedFrameObj(cx, exnObj->stack());
   4001  if (!savedFrameObj) {
   4002    args.rval().setUndefined();
   4003    return true;
   4004  }
   4005  JSPrincipals* principals = exnObj->realm()->principals();
   4006  RootedString stackString(cx);
   4007  if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) {
   4008    return false;
   4009  }
   4010  args.rval().setString(stackString);
   4011  return true;
   4012 }
   4013 
   4014 /* static */
   4015 bool WasmExceptionObject::getStack(JSContext* cx, unsigned argc, Value* vp) {
   4016  CallArgs args = CallArgsFromVp(argc, vp);
   4017  return CallNonGenericMethod<IsException, getStack_impl>(cx, args);
   4018 }
   4019 
   4020 JSObject* WasmExceptionObject::stack() const {
   4021  return getReservedSlot(STACK_SLOT).toObjectOrNull();
   4022 }
   4023 
   4024 uint8_t* WasmExceptionObject::typedMem() const {
   4025  return (uint8_t*)getReservedSlot(DATA_SLOT).toPrivate();
   4026 }
   4027 
   4028 bool WasmExceptionObject::loadArg(JSContext* cx, size_t offset,
   4029                                  wasm::ValType type,
   4030                                  MutableHandleValue vp) const {
   4031  if (!type.isExposable()) {
   4032    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4033                             JSMSG_WASM_BAD_VAL_TYPE);
   4034    return false;
   4035  }
   4036  return ToJSValue(cx, typedMem() + offset, type, vp);
   4037 }
   4038 
   4039 bool WasmExceptionObject::initArg(JSContext* cx, size_t offset,
   4040                                  wasm::ValType type, HandleValue value) {
   4041  if (!type.isExposable()) {
   4042    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4043                             JSMSG_WASM_BAD_VAL_TYPE);
   4044    return false;
   4045  }
   4046 
   4047  // Avoid rooting hazard of `this` being live across `fromJSValue`
   4048  // which may GC.
   4049  uint8_t* dest = typedMem() + offset;
   4050  RootedVal val(cx);
   4051  if (!Val::fromJSValue(cx, type, value, &val)) {
   4052    return false;
   4053  }
   4054  val.get().writeToHeapLocation(dest);
   4055  return true;
   4056 }
   4057 
   4058 void WasmExceptionObject::initRefArg(size_t offset, wasm::AnyRef ref) {
   4059  uint8_t* dest = typedMem() + offset;
   4060  *((GCPtr<AnyRef>*)dest) = ref;
   4061 }
   4062 
   4063 wasm::AnyRef WasmExceptionObject::loadRefArg(size_t offset) const {
   4064  uint8_t* src = typedMem() + offset;
   4065  return *((GCPtr<AnyRef>*)src);
   4066 }
   4067 
   4068 const JSFunctionSpec WasmExceptionObject::methods[] = {
   4069    JS_FN("is", WasmExceptionObject::isMethod, 1, JSPROP_ENUMERATE),
   4070    JS_FN("getArg", WasmExceptionObject::getArg, 2, JSPROP_ENUMERATE),
   4071    JS_FS_END,
   4072 };
   4073 
   4074 const JSFunctionSpec WasmExceptionObject::static_methods[] = {
   4075    JS_FS_END,
   4076 };
   4077 
   4078 const TagType* WasmExceptionObject::tagType() const {
   4079  return (const TagType*)getReservedSlot(TYPE_SLOT).toPrivate();
   4080 }
   4081 
   4082 WasmTagObject& WasmExceptionObject::tag() const {
   4083  return getReservedSlot(TAG_SLOT).toObject().as<WasmTagObject>();
   4084 }
   4085 
   4086 // ============================================================================
   4087 // WebAssembly.Function and methods
   4088 #if defined(ENABLE_WASM_TYPE_REFLECTIONS) || defined(ENABLE_WASM_JSPI)
   4089 [[nodiscard]] static bool IsWasmFunction(HandleValue v) {
   4090  if (!v.isObject()) {
   4091    return false;
   4092  }
   4093  if (!v.toObject().is<JSFunction>()) {
   4094    return false;
   4095  }
   4096  return v.toObject().as<JSFunction>().isWasm();
   4097 }
   4098 #endif  // ENABLE_WASM_TYPE_REFLECTIONS || ENABLE_WASM_JSPI
   4099 
   4100 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   4101 static JSObject* CreateWasmFunctionPrototype(JSContext* cx, JSProtoKey key) {
   4102  // WasmFunction's prototype should inherit from JSFunction's prototype.
   4103  RootedObject jsProto(cx, &cx->global()->getFunctionPrototype());
   4104  return GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
   4105                                                      jsProto);
   4106 }
   4107 
   4108 bool WasmFunctionTypeImpl(JSContext* cx, const CallArgs& args) {
   4109  RootedFunction function(cx, &args.thisv().toObject().as<JSFunction>());
   4110  const FuncType& funcType = function->wasmTypeDef()->funcType();
   4111  RootedObject typeObj(cx, FuncTypeToObject(cx, funcType));
   4112  if (!typeObj) {
   4113    return false;
   4114  }
   4115  args.rval().setObject(*typeObj);
   4116  return true;
   4117 }
   4118 
   4119 bool WasmFunctionType(JSContext* cx, unsigned argc, Value* vp) {
   4120  CallArgs args = CallArgsFromVp(argc, vp);
   4121  return CallNonGenericMethod<IsWasmFunction, WasmFunctionTypeImpl>(cx, args);
   4122 }
   4123 
   4124 static JSFunction* WasmFunctionCreate(JSContext* cx, HandleObject func,
   4125                                      wasm::ValTypeVector&& params,
   4126                                      wasm::ValTypeVector&& results,
   4127                                      HandleObject proto) {
   4128  MOZ_ASSERT(IsCallableNonCCW(ObjectValue(*func)));
   4129 
   4130  // We want to import the function to a wasm module and then export it again so
   4131  // that it behaves exactly like a normal wasm function and can be used like
   4132  // one in wasm tables. We synthesize such a module below, instantiate it, and
   4133  // then return the exported function as the result.
   4134  FeatureOptions options;
   4135  ScriptedCaller scriptedCaller;
   4136  SharedCompileArgs compileArgs =
   4137      CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
   4138  if (!compileArgs) {
   4139    return nullptr;
   4140  }
   4141 
   4142  MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>();
   4143  if (!moduleMeta || !moduleMeta->init(*compileArgs)) {
   4144    return nullptr;
   4145  }
   4146  MutableCodeMetadata codeMeta = moduleMeta->codeMeta;
   4147  CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
   4148                                  DebugEnabled::False);
   4149  compilerEnv.computeParameters();
   4150 
   4151  FuncType funcType = FuncType(std::move(params), std::move(results));
   4152  if (!codeMeta->types->addType(std::move(funcType))) {
   4153    return nullptr;
   4154  }
   4155 
   4156  // Add an (import (func ...))
   4157  FuncDesc funcDesc = FuncDesc(0);
   4158  if (!codeMeta->funcs.append(funcDesc)) {
   4159    return nullptr;
   4160  }
   4161  codeMeta->numFuncImports = 1;
   4162  codeMeta->funcImportsAreJS = true;
   4163 
   4164  // Add an (export (func 0))
   4165  codeMeta->funcs[0].declareFuncExported(/* eager */ true,
   4166                                         /* canRefFunc */ true);
   4167 
   4168  // We will be looking up and using the function in the future by index so the
   4169  // name doesn't matter.
   4170  CacheableName fieldName;
   4171  if (!moduleMeta->exports.emplaceBack(std::move(fieldName), 0,
   4172                                       DefinitionKind::Function)) {
   4173    return nullptr;
   4174  }
   4175 
   4176  if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
   4177    return nullptr;
   4178  }
   4179 
   4180  ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(),
   4181                     nullptr, nullptr, nullptr);
   4182  if (!mg.initializeCompleteTier()) {
   4183    return nullptr;
   4184  }
   4185  // We're not compiling any function definitions.
   4186  if (!mg.finishFuncDefs()) {
   4187    return nullptr;
   4188  }
   4189  SharedModule module = mg.finishModule(BytecodeBufferOrSource(), *moduleMeta,
   4190                                        /*maybeCompleteTier2Listener=*/nullptr);
   4191  if (!module) {
   4192    return nullptr;
   4193  }
   4194 
   4195  // Instantiate the module.
   4196  Rooted<ImportValues> imports(cx);
   4197  if (!imports.get().funcs.append(func)) {
   4198    return nullptr;
   4199  }
   4200  Rooted<WasmInstanceObject*> instance(cx);
   4201  if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
   4202    MOZ_ASSERT(cx->isThrowingOutOfMemory());
   4203    return nullptr;
   4204  }
   4205 
   4206  // Get the exported function which wraps the JS function to return.
   4207  RootedFunction wasmFunc(cx);
   4208  if (!instance->getExportedFunction(cx, instance, 0, &wasmFunc)) {
   4209    return nullptr;
   4210  }
   4211  return wasmFunc;
   4212 }
   4213 
   4214 bool WasmFunctionConstruct(JSContext* cx, unsigned argc, Value* vp) {
   4215  CallArgs args = CallArgsFromVp(argc, vp);
   4216 
   4217  if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Function")) {
   4218    return false;
   4219  }
   4220 
   4221  if (!args.requireAtLeast(cx, "WebAssembly.Function", 2)) {
   4222    return false;
   4223  }
   4224 
   4225  if (!args[0].isObject()) {
   4226    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4227                             JSMSG_WASM_BAD_DESC_ARG, "function");
   4228    return false;
   4229  }
   4230  RootedObject typeObj(cx, &args[0].toObject());
   4231 
   4232  // Extract properties in lexicographic order per spec.
   4233 
   4234  RootedValue parametersVal(cx);
   4235  if (!JS_GetProperty(cx, typeObj, "parameters", &parametersVal)) {
   4236    return false;
   4237  }
   4238 
   4239  ValTypeVector params;
   4240  if (!ParseValTypes(cx, parametersVal, params)) {
   4241    return false;
   4242  }
   4243  if (params.length() > MaxParams) {
   4244    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4245                             JSMSG_WASM_BAD_FUNCTION_TYPE, "parameters");
   4246    return false;
   4247  }
   4248 
   4249  RootedValue resultsVal(cx);
   4250  if (!JS_GetProperty(cx, typeObj, "results", &resultsVal)) {
   4251    return false;
   4252  }
   4253 
   4254  ValTypeVector results;
   4255  if (!ParseValTypes(cx, resultsVal, results)) {
   4256    return false;
   4257  }
   4258  if (results.length() > MaxResults) {
   4259    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4260                             JSMSG_WASM_BAD_FUNCTION_TYPE, "results");
   4261    return false;
   4262  }
   4263 
   4264  // Get the target function
   4265 
   4266  if (!IsCallableNonCCW(args[1])) {
   4267    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4268                             JSMSG_WASM_BAD_FUNCTION_VALUE);
   4269    return false;
   4270  }
   4271  RootedObject func(cx, &args[1].toObject());
   4272 
   4273  RootedObject proto(
   4274      cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmFunction));
   4275  if (!proto) {
   4276    ReportOutOfMemory(cx);
   4277    return false;
   4278  }
   4279 
   4280  RootedFunction wasmFunc(cx, WasmFunctionCreate(cx, func, std::move(params),
   4281                                                 std::move(results), proto));
   4282  if (!wasmFunc) {
   4283    ReportOutOfMemory(cx);
   4284    return false;
   4285  }
   4286  args.rval().setObject(*wasmFunc);
   4287 
   4288  return true;
   4289 }
   4290 
   4291 static constexpr char WasmFunctionName[] = "Function";
   4292 
   4293 static JSObject* CreateWasmFunctionConstructor(JSContext* cx, JSProtoKey key) {
   4294  RootedObject proto(cx, &cx->global()->getFunctionConstructor());
   4295 
   4296  Rooted<JSAtom*> className(
   4297      cx, Atomize(cx, WasmFunctionName, strlen(WasmFunctionName)));
   4298  if (!className) {
   4299    return nullptr;
   4300  }
   4301  return NewFunctionWithProto(cx, WasmFunctionConstruct, 1,
   4302                              FunctionFlags::NATIVE_CTOR, nullptr, className,
   4303                              proto, gc::AllocKind::FUNCTION, TenuredObject);
   4304 }
   4305 
   4306 const JSFunctionSpec WasmFunctionMethods[] = {
   4307    JS_FN("type", WasmFunctionType, 0, 0),
   4308    JS_FS_END,
   4309 };
   4310 
   4311 const ClassSpec WasmFunctionClassSpec = {
   4312    CreateWasmFunctionConstructor,
   4313    CreateWasmFunctionPrototype,
   4314    nullptr,
   4315    nullptr,
   4316    WasmFunctionMethods,
   4317    nullptr,
   4318    nullptr,
   4319    ClassSpec::DontDefineConstructor,
   4320 };
   4321 
   4322 const JSClass js::WasmFunctionClass = {
   4323    "WebAssembly.Function",
   4324    0,
   4325    JS_NULL_CLASS_OPS,
   4326    &WasmFunctionClassSpec,
   4327 };
   4328 
   4329 #endif
   4330 
   4331 // ============================================================================
   4332 // WebAssembly class and static methods
   4333 
   4334 static bool WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) {
   4335  CallArgs args = CallArgsFromVp(argc, vp);
   4336  args.rval().setString(cx->names().WebAssembly);
   4337  return true;
   4338 }
   4339 
   4340 static bool RejectWithPendingException(JSContext* cx,
   4341                                       Handle<PromiseObject*> promise) {
   4342  if (!cx->isExceptionPending()) {
   4343    return false;
   4344  }
   4345 
   4346  RootedValue rejectionValue(cx);
   4347  if (!GetAndClearException(cx, &rejectionValue)) {
   4348    return false;
   4349  }
   4350 
   4351  return PromiseObject::reject(cx, promise, rejectionValue);
   4352 }
   4353 
   4354 static bool Reject(JSContext* cx, const CompileArgs& args,
   4355                   Handle<PromiseObject*> promise, const UniqueChars& error) {
   4356  if (!error) {
   4357    ThrowCompileOutOfMemory(cx);
   4358    return RejectWithPendingException(cx, promise);
   4359  }
   4360 
   4361  RootedObject stack(cx, promise->allocationSite());
   4362  RootedString fileName(cx);
   4363  if (const char* filename = args.scriptedCaller.filename.get()) {
   4364    fileName =
   4365        JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(filename, strlen(filename)));
   4366  } else {
   4367    fileName = JS_GetEmptyString(cx);
   4368  }
   4369  if (!fileName) {
   4370    return false;
   4371  }
   4372 
   4373  uint32_t line = args.scriptedCaller.line;
   4374 
   4375  // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
   4376  // way to create an ErrorObject for an arbitrary error code with multiple
   4377  // replacements.
   4378  UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
   4379  if (!str) {
   4380    return false;
   4381  }
   4382 
   4383  size_t len = strlen(str.get());
   4384  RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len));
   4385  if (!message) {
   4386    return false;
   4387  }
   4388 
   4389  // There's no error |cause| available here.
   4390  auto cause = JS::NothingHandleValue;
   4391 
   4392  RootedObject errorObj(
   4393      cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, fileName, 0,
   4394                              line, JS::ColumnNumberOneOrigin(), nullptr,
   4395                              message, cause));
   4396  if (!errorObj) {
   4397    return false;
   4398  }
   4399 
   4400  RootedValue rejectionValue(cx, ObjectValue(*errorObj));
   4401  return PromiseObject::reject(cx, promise, rejectionValue);
   4402 }
   4403 
   4404 static bool RejectWithOutOfMemory(JSContext* cx,
   4405                                  Handle<PromiseObject*> promise) {
   4406  ReportOutOfMemory(cx);
   4407  return RejectWithPendingException(cx, promise);
   4408 }
   4409 
   4410 static void LogAsync(JSContext* cx, const char* funcName,
   4411                     const Module& module) {
   4412  Log(cx, "async %s succeeded%s", funcName,
   4413      module.loggingDeserialized() ? " (loaded from cache)" : "");
   4414 }
   4415 
   4416 enum class Ret { Pair, Instance };
   4417 
   4418 class AsyncInstantiateTask : public OffThreadPromiseTask {
   4419  SharedModule module_;
   4420  PersistentRooted<ImportValues> imports_;
   4421  Ret ret_;
   4422 
   4423 public:
   4424  AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret,
   4425                       Handle<PromiseObject*> promise)
   4426      : OffThreadPromiseTask(cx, promise),
   4427        module_(&module),
   4428        imports_(cx),
   4429        ret_(ret) {}
   4430 
   4431  ImportValues& imports() { return imports_.get(); }
   4432 
   4433  bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
   4434    RootedObject instanceProto(
   4435        cx, &cx->global()->getPrototype(JSProto_WasmInstance));
   4436 
   4437    Rooted<WasmInstanceObject*> instanceObj(cx);
   4438    if (!module_->instantiate(cx, imports_.get(), instanceProto,
   4439                              &instanceObj)) {
   4440      return RejectWithPendingException(cx, promise);
   4441    }
   4442 
   4443    RootedValue resolutionValue(cx);
   4444    if (ret_ == Ret::Instance) {
   4445      resolutionValue = ObjectValue(*instanceObj);
   4446    } else {
   4447      RootedObject resultObj(cx, JS_NewPlainObject(cx));
   4448      if (!resultObj) {
   4449        return RejectWithPendingException(cx, promise);
   4450      }
   4451 
   4452      RootedObject moduleProto(cx,
   4453                               &cx->global()->getPrototype(JSProto_WasmModule));
   4454      RootedObject moduleObj(
   4455          cx, WasmModuleObject::create(cx, *module_, moduleProto));
   4456      if (!moduleObj) {
   4457        return RejectWithPendingException(cx, promise);
   4458      }
   4459 
   4460      RootedValue val(cx, ObjectValue(*moduleObj));
   4461      if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
   4462        return RejectWithPendingException(cx, promise);
   4463      }
   4464 
   4465      val = ObjectValue(*instanceObj);
   4466      if (!JS_DefineProperty(cx, resultObj, "instance", val,
   4467                             JSPROP_ENUMERATE)) {
   4468        return RejectWithPendingException(cx, promise);
   4469      }
   4470 
   4471      resolutionValue = ObjectValue(*resultObj);
   4472    }
   4473 
   4474    if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
   4475      return RejectWithPendingException(cx, promise);
   4476    }
   4477 
   4478    LogAsync(cx, "instantiate", *module_);
   4479    return true;
   4480  }
   4481 };
   4482 
   4483 static bool AsyncInstantiate(JSContext* cx, const Module& module,
   4484                             HandleObject importObj, Ret ret,
   4485                             Handle<PromiseObject*> promise) {
   4486  auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise);
   4487  if (!task || !task->init(cx)) {
   4488    return RejectWithOutOfMemory(cx, promise);
   4489  }
   4490 
   4491  if (!GetImports(cx, module, importObj, &task->imports())) {
   4492    return RejectWithPendingException(cx, promise);
   4493  }
   4494 
   4495  OffThreadPromiseTask::DispatchResolveAndDestroy(std::move(task));
   4496  return true;
   4497 }
   4498 
   4499 static bool ResolveCompile(JSContext* cx, const Module& module,
   4500                           Handle<PromiseObject*> promise) {
   4501  RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule));
   4502  RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
   4503  if (!moduleObj) {
   4504    return RejectWithPendingException(cx, promise);
   4505  }
   4506 
   4507  RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
   4508  if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
   4509    return RejectWithPendingException(cx, promise);
   4510  }
   4511 
   4512  LogAsync(cx, "compile", module);
   4513  return true;
   4514 }
   4515 
   4516 struct CompileBufferTask : PromiseHelperTask {
   4517  BytecodeBuffer bytecode;
   4518  SharedCompileArgs compileArgs;
   4519  UniqueChars error;
   4520  UniqueCharsVector warnings;
   4521  SharedModule module;
   4522  bool instantiate;
   4523  PersistentRootedObject importObj;
   4524 
   4525  CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise,
   4526                    HandleObject importObj)
   4527      : PromiseHelperTask(cx, promise),
   4528        instantiate(true),
   4529        importObj(cx, importObj) {}
   4530 
   4531  CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise)
   4532      : PromiseHelperTask(cx, promise), instantiate(false) {}
   4533 
   4534  bool init(JSContext* cx, FeatureOptions options, const char* introducer) {
   4535    compileArgs = InitCompileArgs(cx, options, introducer);
   4536    if (!compileArgs) {
   4537      return false;
   4538    }
   4539    return PromiseHelperTask::init(cx);
   4540  }
   4541 
   4542  void execute() override {
   4543    module = CompileBuffer(*compileArgs, BytecodeBufferOrSource(bytecode),
   4544                           &error, &warnings, nullptr);
   4545  }
   4546 
   4547  bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
   4548    if (!ReportCompileWarnings(cx, warnings)) {
   4549      return false;
   4550    }
   4551    if (!module) {
   4552      return Reject(cx, *compileArgs, promise, error);
   4553    }
   4554    if (instantiate) {
   4555      return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise);
   4556    }
   4557    return ResolveCompile(cx, *module, promise);
   4558  }
   4559 };
   4560 
   4561 static bool RejectWithPendingException(JSContext* cx,
   4562                                       Handle<PromiseObject*> promise,
   4563                                       CallArgs& callArgs) {
   4564  if (!RejectWithPendingException(cx, promise)) {
   4565    return false;
   4566  }
   4567 
   4568  callArgs.rval().setObject(*promise);
   4569  return true;
   4570 }
   4571 
   4572 static bool EnsurePromiseSupport(JSContext* cx) {
   4573  if (!cx->runtime()->offThreadPromiseState.ref().initialized()) {
   4574    JS_ReportErrorASCII(
   4575        cx, "WebAssembly Promise APIs not supported in this runtime.");
   4576    return false;
   4577  }
   4578  return true;
   4579 }
   4580 
   4581 static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
   4582  if (!EnsurePromiseSupport(cx)) {
   4583    return false;
   4584  }
   4585 
   4586  Log(cx, "async compile() started");
   4587 
   4588  Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
   4589  if (!promise) {
   4590    return false;
   4591  }
   4592 
   4593  CallArgs callArgs = CallArgsFromVp(argc, vp);
   4594 
   4595  JS::RootedVector<JSString*> parameterStrings(cx);
   4596  JS::RootedVector<Value> parameterArgs(cx);
   4597  bool canCompileStrings = false;
   4598  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
   4599                                   JS::CompilationType::Undefined,
   4600                                   parameterStrings, nullptr, parameterArgs,
   4601                                   NullHandleValue, &canCompileStrings)) {
   4602    return RejectWithPendingException(cx, promise, callArgs);
   4603  }
   4604  if (!canCompileStrings) {
   4605    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4606                              JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile");
   4607    return RejectWithPendingException(cx, promise, callArgs);
   4608  }
   4609 
   4610  auto task = cx->make_unique<CompileBufferTask>(cx, promise);
   4611  if (!task) {
   4612    return false;
   4613  }
   4614 
   4615  if (!callArgs.requireAtLeast(cx, "WebAssembly.compile", 1)) {
   4616    return RejectWithPendingException(cx, promise, callArgs);
   4617  }
   4618 
   4619  FeatureOptions options;
   4620  if (!options.init(cx, callArgs.get(1))) {
   4621    return RejectWithPendingException(cx, promise, callArgs);
   4622  }
   4623 
   4624  if (!callArgs[0].isObject()) {
   4625    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4626                             JSMSG_WASM_BAD_BUF_ARG);
   4627    return RejectWithPendingException(cx, promise, callArgs);
   4628  }
   4629 
   4630  Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject());
   4631  if (!GetBytecodeBuffer(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG,
   4632                         &task->bytecode)) {
   4633    return RejectWithPendingException(cx, promise, callArgs);
   4634  }
   4635 
   4636  if (!task->init(cx, options, "WebAssembly.compile")) {
   4637    return false;
   4638  }
   4639 
   4640  if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
   4641    return false;
   4642  }
   4643 
   4644  callArgs.rval().setObject(*promise);
   4645  return true;
   4646 }
   4647 
   4648 static bool GetInstantiateArgs(JSContext* cx, const CallArgs& callArgs,
   4649                               MutableHandleObject firstArg,
   4650                               MutableHandleObject importObj,
   4651                               MutableHandleValue featureOptions) {
   4652  if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1)) {
   4653    return false;
   4654  }
   4655 
   4656  if (!callArgs[0].isObject()) {
   4657    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4658                             JSMSG_WASM_BAD_BUF_MOD_ARG);
   4659    return false;
   4660  }
   4661 
   4662  firstArg.set(&callArgs[0].toObject());
   4663 
   4664  if (!GetImportArg(cx, callArgs.get(1), importObj)) {
   4665    return false;
   4666  }
   4667 
   4668  featureOptions.set(callArgs.get(2));
   4669  return true;
   4670 }
   4671 
   4672 static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
   4673  if (!EnsurePromiseSupport(cx)) {
   4674    return false;
   4675  }
   4676 
   4677  Log(cx, "async instantiate() started");
   4678 
   4679  Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
   4680  if (!promise) {
   4681    return false;
   4682  }
   4683 
   4684  CallArgs callArgs = CallArgsFromVp(argc, vp);
   4685 
   4686  RootedObject firstArg(cx);
   4687  RootedObject importObj(cx);
   4688  RootedValue featureOptions(cx);
   4689  if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj,
   4690                          &featureOptions)) {
   4691    return RejectWithPendingException(cx, promise, callArgs);
   4692  }
   4693 
   4694  const Module* module;
   4695  if (IsModuleObject(firstArg, &module)) {
   4696    if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) {
   4697      return false;
   4698    }
   4699  } else {
   4700    JS::RootedVector<JSString*> parameterStrings(cx);
   4701    JS::RootedVector<Value> parameterArgs(cx);
   4702    bool canCompileStrings = false;
   4703    if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
   4704                                     JS::CompilationType::Undefined,
   4705                                     parameterStrings, nullptr, parameterArgs,
   4706                                     NullHandleValue, &canCompileStrings)) {
   4707      return RejectWithPendingException(cx, promise, callArgs);
   4708    }
   4709    if (!canCompileStrings) {
   4710      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   4711                                JSMSG_CSP_BLOCKED_WASM,
   4712                                "WebAssembly.instantiate");
   4713      return RejectWithPendingException(cx, promise, callArgs);
   4714    }
   4715 
   4716    FeatureOptions options;
   4717    if (!options.init(cx, featureOptions)) {
   4718      return false;
   4719    }
   4720 
   4721    auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
   4722    if (!task || !task->init(cx, options, "WebAssembly.instantiate")) {
   4723      return false;
   4724    }
   4725 
   4726    if (!GetBytecodeBuffer(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG,
   4727                           &task->bytecode)) {
   4728      return RejectWithPendingException(cx, promise, callArgs);
   4729    }
   4730 
   4731    if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
   4732      return false;
   4733    }
   4734  }
   4735 
   4736  callArgs.rval().setObject(*promise);
   4737  return true;
   4738 }
   4739 
   4740 static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) {
   4741  CallArgs callArgs = CallArgsFromVp(argc, vp);
   4742 
   4743  if (!callArgs.requireAtLeast(cx, "WebAssembly.validate", 1)) {
   4744    return false;
   4745  }
   4746 
   4747  FeatureOptions options;
   4748  if (!options.init(cx, callArgs.get(1))) {
   4749    return false;
   4750  }
   4751 
   4752  if (!callArgs[0].isObject()) {
   4753    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   4754                             JSMSG_WASM_BAD_BUF_ARG);
   4755    return false;
   4756  }
   4757 
   4758  BytecodeSource source;
   4759  Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject());
   4760  if (!GetBytecodeSource(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG, &source)) {
   4761    return false;
   4762  }
   4763 
   4764  UniqueChars error;
   4765  bool validated;
   4766  {
   4767    AutoPinBufferSourceLength pin(cx, sourceObj.get());
   4768    validated = Validate(cx, source, options, &error);
   4769  }
   4770 
   4771  // If the reason for validation failure was OOM (signalled by null error
   4772  // message), report out-of-memory so that validate's return is always
   4773  // correct.
   4774  if (!validated && !error) {
   4775    ReportOutOfMemory(cx);
   4776    return false;
   4777  }
   4778 
   4779  if (error) {
   4780    MOZ_ASSERT(!validated);
   4781    Log(cx, "validate() failed with: %s", error.get());
   4782  }
   4783 
   4784  callArgs.rval().setBoolean(validated);
   4785  return true;
   4786 }
   4787 
   4788 static bool EnsureStreamSupport(JSContext* cx) {
   4789  // This should match wasm::StreamingCompilationAvailable().
   4790 
   4791  if (!EnsurePromiseSupport(cx)) {
   4792    return false;
   4793  }
   4794 
   4795  if (!CanUseExtraThreads()) {
   4796    JS_ReportErrorASCII(
   4797        cx, "WebAssembly.compileStreaming not supported with --no-threads");
   4798    return false;
   4799  }
   4800 
   4801  if (!cx->runtime()->consumeStreamCallback) {
   4802    JS_ReportErrorASCII(cx,
   4803                        "WebAssembly streaming not supported in this runtime");
   4804    return false;
   4805  }
   4806 
   4807  return true;
   4808 }
   4809 
   4810 // This value is chosen and asserted to be disjoint from any host error code.
   4811 static const size_t StreamOOMCode = 0;
   4812 
   4813 static bool RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode,
   4814                                        Handle<PromiseObject*> promise) {
   4815  if (errorCode == StreamOOMCode) {
   4816    ReportOutOfMemory(cx);
   4817    return false;
   4818  }
   4819 
   4820  cx->runtime()->reportStreamErrorCallback(cx, errorCode);
   4821  return RejectWithPendingException(cx, promise);
   4822 }
   4823 
   4824 class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer {
   4825  // The stream progresses monotonically through these states; the helper
   4826  // thread wait()s for streamState_ to reach Closed.
   4827  enum StreamState { Env, Code, Tail, Closed };
   4828  ExclusiveWaitableData<StreamState> streamState_;
   4829 
   4830  // Immutable:
   4831  const bool instantiate_;
   4832  const PersistentRootedObject importObj_;
   4833 
   4834  // Immutable after noteResponseURLs() which is called at most once before
   4835  // first call on stream thread:
   4836  const MutableCompileArgs compileArgs_;
   4837 
   4838  // Immutable after Env state:
   4839  MutableBytes envBytes_;
   4840  BytecodeRange codeSection_;
   4841 
   4842  // The code section vector is resized once during the Env state and filled
   4843  // in chunk by chunk during the Code state, updating the end-pointer after
   4844  // each chunk:
   4845  MutableBytes codeBytes_;
   4846  uint8_t* codeBytesEnd_;
   4847  ExclusiveBytesPtr exclusiveCodeBytesEnd_;
   4848 
   4849  // Immutable after Tail state:
   4850  MutableBytes tailBytes_;
   4851  ExclusiveStreamEndData exclusiveStreamEnd_;
   4852 
   4853  // Written once before Closed state and read in Closed state on main thread:
   4854  SharedModule module_;
   4855  Maybe<size_t> streamError_;
   4856  UniqueChars compileError_;
   4857  UniqueCharsVector warnings_;
   4858 
   4859  // Set on stream thread and read racily on helper thread to abort compilation:
   4860  mozilla::Atomic<bool> streamFailed_;
   4861 
   4862  // Called on some thread before consumeChunk(), streamEnd(), streamError()):
   4863 
   4864  void noteResponseURLs(const char* url, const char* sourceMapUrl) override {
   4865    if (url) {
   4866      compileArgs_->scriptedCaller.filename = DuplicateString(url);
   4867      compileArgs_->scriptedCaller.filenameIsURL = true;
   4868    }
   4869    if (sourceMapUrl) {
   4870      compileArgs_->sourceMapURL = DuplicateString(sourceMapUrl);
   4871    }
   4872  }
   4873 
   4874  // Called on a stream thread:
   4875 
   4876  // Until StartOffThreadPromiseHelperTask succeeds, we are responsible for
   4877  // dispatching ourselves back to the JS thread.
   4878  //
   4879  // Warning: After this function returns, 'this' can be deleted at any time, so
   4880  // the caller must immediately return from the stream callback.
   4881  void setClosedAndDestroyBeforeHelperThreadStarted() {
   4882    streamState_.lock().get() = Closed;
   4883    dispatchResolveAndDestroy();
   4884  }
   4885 
   4886  // See setClosedAndDestroyBeforeHelperThreadStarted() comment.
   4887  bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) {
   4888    MOZ_ASSERT(streamState_.lock() == Env);
   4889    MOZ_ASSERT(!streamError_);
   4890    streamError_ = Some(errorNumber);
   4891    setClosedAndDestroyBeforeHelperThreadStarted();
   4892    return false;
   4893  }
   4894 
   4895  // Once StartOffThreadPromiseHelperTask succeeds, the helper thread will
   4896  // dispatchResolveAndDestroy() after execute() returns, but execute()
   4897  // wait()s for state to be Closed.
   4898  //
   4899  // Warning: After this function returns, 'this' can be deleted at any time, so
   4900  // the caller must immediately return from the stream callback.
   4901  void setClosedAndDestroyAfterHelperThreadStarted() {
   4902    auto streamState = streamState_.lock();
   4903    MOZ_ASSERT(streamState != Closed);
   4904    streamState.get() = Closed;
   4905    streamState.notify_one(/* stream closed */);
   4906  }
   4907 
   4908  // See setClosedAndDestroyAfterHelperThreadStarted() comment.
   4909  bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) {
   4910    MOZ_ASSERT(!streamError_);
   4911    streamError_ = Some(errorNumber);
   4912    streamFailed_ = true;
   4913    exclusiveCodeBytesEnd_.lock().notify_one();
   4914    exclusiveStreamEnd_.lock().notify_one();
   4915    setClosedAndDestroyAfterHelperThreadStarted();
   4916    return false;
   4917  }
   4918 
   4919  bool consumeChunk(const uint8_t* begin, size_t length) override {
   4920    switch (streamState_.lock().get()) {
   4921      case Env: {
   4922        if (!envBytes_->append(begin, length)) {
   4923          return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
   4924        }
   4925 
   4926        if (!StartsCodeSection(envBytes_->begin(), envBytes_->end(),
   4927                               &codeSection_)) {
   4928          return true;
   4929        }
   4930 
   4931        uint32_t extraBytes = envBytes_->length() - codeSection_.start;
   4932        if (extraBytes) {
   4933          envBytes_->shrinkTo(codeSection_.start);
   4934        }
   4935 
   4936        if (codeSection_.size() > MaxCodeSectionBytes) {
   4937          return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
   4938        }
   4939 
   4940        if (!codeBytes_->vector.resize(codeSection_.size())) {
   4941          return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
   4942        }
   4943 
   4944        codeBytesEnd_ = codeBytes_->begin();
   4945        exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_;
   4946 
   4947        if (!StartOffThreadPromiseHelperTask(this)) {
   4948          return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
   4949        }
   4950 
   4951        // Set the state to Code iff StartOffThreadPromiseHelperTask()
   4952        // succeeds so that the state tells us whether we are before or
   4953        // after the helper thread started.
   4954        streamState_.lock().get() = Code;
   4955 
   4956        if (extraBytes) {
   4957          return consumeChunk(begin + length - extraBytes, extraBytes);
   4958        }
   4959 
   4960        return true;
   4961      }
   4962      case Code: {
   4963        size_t copyLength =
   4964            std::min<size_t>(length, codeBytes_->end() - codeBytesEnd_);
   4965        memcpy(codeBytesEnd_, begin, copyLength);
   4966        codeBytesEnd_ += copyLength;
   4967 
   4968        {
   4969          auto codeStreamEnd = exclusiveCodeBytesEnd_.lock();
   4970          codeStreamEnd.get() = codeBytesEnd_;
   4971          codeStreamEnd.notify_one();
   4972        }
   4973 
   4974        if (codeBytesEnd_ != codeBytes_->end()) {
   4975          return true;
   4976        }
   4977 
   4978        streamState_.lock().get() = Tail;
   4979 
   4980        if (uint32_t extraBytes = length - copyLength) {
   4981          return consumeChunk(begin + copyLength, extraBytes);
   4982        }
   4983 
   4984        return true;
   4985      }
   4986      case Tail: {
   4987        if (!tailBytes_->append(begin, length)) {
   4988          return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode);
   4989        }
   4990 
   4991        return true;
   4992      }
   4993      case Closed:
   4994        MOZ_CRASH("consumeChunk() in Closed state");
   4995    }
   4996    MOZ_CRASH("unreachable");
   4997  }
   4998 
   4999  void streamEnd(
   5000      JS::OptimizedEncodingListener* completeTier2Listener) override {
   5001    switch (streamState_.lock().get()) {
   5002      case Env: {
   5003        BytecodeBuffer bytecode(envBytes_, nullptr, nullptr);
   5004        module_ = CompileBuffer(*compileArgs_, BytecodeBufferOrSource(bytecode),
   5005                                &compileError_, &warnings_, nullptr);
   5006        setClosedAndDestroyBeforeHelperThreadStarted();
   5007        return;
   5008      }
   5009      case Code:
   5010      case Tail:
   5011        // Unlock exclusiveStreamEnd_ before locking streamState_.
   5012        {
   5013          auto streamEnd = exclusiveStreamEnd_.lock();
   5014          MOZ_ASSERT(!streamEnd->reached);
   5015          streamEnd->reached = true;
   5016          streamEnd->tailBytes = tailBytes_;
   5017          streamEnd->completeTier2Listener = completeTier2Listener;
   5018          streamEnd.notify_one();
   5019        }
   5020        setClosedAndDestroyAfterHelperThreadStarted();
   5021        return;
   5022      case Closed:
   5023        MOZ_CRASH("streamEnd() in Closed state");
   5024    }
   5025  }
   5026 
   5027  void streamError(size_t errorCode) override {
   5028    MOZ_ASSERT(errorCode != StreamOOMCode);
   5029    switch (streamState_.lock().get()) {
   5030      case Env:
   5031        rejectAndDestroyBeforeHelperThreadStarted(errorCode);
   5032        return;
   5033      case Tail:
   5034      case Code:
   5035        rejectAndDestroyAfterHelperThreadStarted(errorCode);
   5036        return;
   5037      case Closed:
   5038        MOZ_CRASH("streamError() in Closed state");
   5039    }
   5040  }
   5041 
   5042  void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override {
   5043    module_ = Module::deserialize(begin, length);
   5044 
   5045    MOZ_ASSERT(streamState_.lock().get() == Env);
   5046    setClosedAndDestroyBeforeHelperThreadStarted();
   5047  }
   5048 
   5049  // Called on a helper thread:
   5050 
   5051  void execute() override {
   5052    module_ = CompileStreaming(*compileArgs_, *envBytes_, *codeBytes_,
   5053                               exclusiveCodeBytesEnd_, exclusiveStreamEnd_,
   5054                               streamFailed_, &compileError_, &warnings_);
   5055 
   5056    // When execute() returns, the CompileStreamTask will be dispatched
   5057    // back to its JS thread to call resolve() and then be destroyed. We
   5058    // can't let this happen until the stream has been closed lest
   5059    // consumeChunk() or streamEnd() be called on a dead object.
   5060    auto streamState = streamState_.lock();
   5061    while (streamState != Closed) {
   5062      streamState.wait(/* stream closed */);
   5063    }
   5064  }
   5065 
   5066  // Called on a JS thread after streaming compilation completes/errors:
   5067 
   5068  bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
   5069    MOZ_ASSERT(streamState_.lock() == Closed);
   5070 
   5071    if (!ReportCompileWarnings(cx, warnings_)) {
   5072      return false;
   5073    }
   5074    if (module_) {
   5075      MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_);
   5076      if (instantiate_) {
   5077        return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise);
   5078      }
   5079      return ResolveCompile(cx, *module_, promise);
   5080    }
   5081 
   5082    if (streamError_) {
   5083      return RejectWithStreamErrorNumber(cx, *streamError_, promise);
   5084    }
   5085 
   5086    return Reject(cx, *compileArgs_, promise, compileError_);
   5087  }
   5088 
   5089 public:
   5090  CompileStreamTask(JSContext* cx, Handle<PromiseObject*> promise,
   5091                    CompileArgs& compileArgs, bool instantiate,
   5092                    HandleObject importObj)
   5093      : PromiseHelperTask(cx, promise),
   5094        streamState_(mutexid::WasmStreamStatus, Env),
   5095        instantiate_(instantiate),
   5096        importObj_(cx, importObj),
   5097        compileArgs_(&compileArgs),
   5098        codeSection_{},
   5099        codeBytesEnd_(nullptr),
   5100        exclusiveCodeBytesEnd_(mutexid::WasmCodeBytesEnd, nullptr),
   5101        exclusiveStreamEnd_(mutexid::WasmStreamEnd),
   5102        streamFailed_(false) {
   5103    MOZ_ASSERT_IF(importObj_, instantiate_);
   5104  }
   5105 
   5106  [[nodiscard]] bool init(JSContext* cx) {
   5107    envBytes_ = cx->new_<ShareableBytes>();
   5108    if (!envBytes_) {
   5109      return false;
   5110    }
   5111 
   5112    codeBytes_ = js_new<ShareableBytes>();
   5113    if (!codeBytes_) {
   5114      return false;
   5115    }
   5116 
   5117    tailBytes_ = js_new<ShareableBytes>();
   5118    if (!tailBytes_) {
   5119      return false;
   5120    }
   5121 
   5122    return PromiseHelperTask::init(cx);
   5123  }
   5124 };
   5125 
   5126 // A short-lived object that captures the arguments of a
   5127 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for
   5128 // the Promise<Response> to resolve to a (hopefully) Promise.
   5129 class ResolveResponseClosure : public NativeObject {
   5130  static const unsigned COMPILE_ARGS_SLOT = 0;
   5131  static const unsigned PROMISE_OBJ_SLOT = 1;
   5132  static const unsigned INSTANTIATE_SLOT = 2;
   5133  static const unsigned IMPORT_OBJ_SLOT = 3;
   5134  static const JSClassOps classOps_;
   5135 
   5136  static void finalize(JS::GCContext* gcx, JSObject* obj) {
   5137    auto& closure = obj->as<ResolveResponseClosure>();
   5138    gcx->release(obj, &closure.compileArgs(),
   5139                 MemoryUse::WasmResolveResponseClosure);
   5140  }
   5141 
   5142 public:
   5143  static const unsigned RESERVED_SLOTS = 4;
   5144  static const JSClass class_;
   5145 
   5146  static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args,
   5147                                        HandleObject promise, bool instantiate,
   5148                                        HandleObject importObj) {
   5149    MOZ_ASSERT_IF(importObj, instantiate);
   5150 
   5151    AutoSetNewObjectMetadata metadata(cx);
   5152    auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr);
   5153    if (!obj) {
   5154      return nullptr;
   5155    }
   5156 
   5157    args.AddRef();
   5158    InitReservedSlot(obj, COMPILE_ARGS_SLOT, const_cast<CompileArgs*>(&args),
   5159                     MemoryUse::WasmResolveResponseClosure);
   5160    obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise));
   5161    obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate));
   5162    obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj));
   5163    return obj;
   5164  }
   5165 
   5166  CompileArgs& compileArgs() const {
   5167    return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate();
   5168  }
   5169  PromiseObject& promise() const {
   5170    return getReservedSlot(PROMISE_OBJ_SLOT).toObject().as<PromiseObject>();
   5171  }
   5172  bool instantiate() const {
   5173    return getReservedSlot(INSTANTIATE_SLOT).toBoolean();
   5174  }
   5175  JSObject* importObj() const {
   5176    return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull();
   5177  }
   5178 };
   5179 
   5180 const JSClassOps ResolveResponseClosure::classOps_ = {
   5181    nullptr,                           // addProperty
   5182    nullptr,                           // delProperty
   5183    nullptr,                           // enumerate
   5184    nullptr,                           // newEnumerate
   5185    nullptr,                           // resolve
   5186    nullptr,                           // mayResolve
   5187    ResolveResponseClosure::finalize,  // finalize
   5188    nullptr,                           // call
   5189    nullptr,                           // construct
   5190    nullptr,                           // trace
   5191 };
   5192 
   5193 const JSClass ResolveResponseClosure::class_ = {
   5194    "WebAssembly ResolveResponseClosure",
   5195    JSCLASS_DELAY_METADATA_BUILDER |
   5196        JSCLASS_HAS_RESERVED_SLOTS(ResolveResponseClosure::RESERVED_SLOTS) |
   5197        JSCLASS_FOREGROUND_FINALIZE,
   5198    &ResolveResponseClosure::classOps_,
   5199 };
   5200 
   5201 static ResolveResponseClosure* ToResolveResponseClosure(const CallArgs& args) {
   5202  return &args.callee()
   5203              .as<JSFunction>()
   5204              .getExtendedSlot(0)
   5205              .toObject()
   5206              .as<ResolveResponseClosure>();
   5207 }
   5208 
   5209 static bool RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber,
   5210                                  Handle<PromiseObject*> promise) {
   5211  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
   5212  return RejectWithPendingException(cx, promise);
   5213 }
   5214 
   5215 static bool ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc,
   5216                                        Value* vp) {
   5217  CallArgs callArgs = CallArgsFromVp(argc, vp);
   5218 
   5219  Rooted<ResolveResponseClosure*> closure(cx,
   5220                                          ToResolveResponseClosure(callArgs));
   5221  Rooted<PromiseObject*> promise(cx, &closure->promise());
   5222  CompileArgs& compileArgs = closure->compileArgs();
   5223  bool instantiate = closure->instantiate();
   5224  Rooted<JSObject*> importObj(cx, closure->importObj());
   5225 
   5226  auto task = cx->make_unique<CompileStreamTask>(cx, promise, compileArgs,
   5227                                                 instantiate, importObj);
   5228  if (!task || !task->init(cx)) {
   5229    return RejectWithOutOfMemory(cx, promise);
   5230  }
   5231 
   5232  if (!callArgs.get(0).isObject()) {
   5233    return RejectWithErrorNumber(cx, JSMSG_WASM_BAD_RESPONSE_VALUE, promise);
   5234  }
   5235 
   5236  RootedObject response(cx, &callArgs.get(0).toObject());
   5237  if (!cx->runtime()->consumeStreamCallback(cx, response, JS::MimeType::Wasm,
   5238                                            task.get())) {
   5239    return RejectWithPendingException(cx, promise);
   5240  }
   5241 
   5242  (void)task.release();
   5243 
   5244  callArgs.rval().setUndefined();
   5245  return true;
   5246 }
   5247 
   5248 static bool ResolveResponse_OnRejected(JSContext* cx, unsigned argc,
   5249                                       Value* vp) {
   5250  CallArgs args = CallArgsFromVp(argc, vp);
   5251 
   5252  Rooted<ResolveResponseClosure*> closure(cx, ToResolveResponseClosure(args));
   5253  Rooted<PromiseObject*> promise(cx, &closure->promise());
   5254 
   5255  if (!PromiseObject::reject(cx, promise, args.get(0))) {
   5256    return false;
   5257  }
   5258 
   5259  args.rval().setUndefined();
   5260  return true;
   5261 }
   5262 
   5263 static bool ResolveResponse(JSContext* cx, Handle<Value> responsePromise,
   5264                            Handle<Value> featureOptions,
   5265                            Handle<PromiseObject*> resultPromise,
   5266                            bool instantiate = false,
   5267                            HandleObject importObj = nullptr) {
   5268  MOZ_ASSERT_IF(importObj, instantiate);
   5269 
   5270  const char* introducer = instantiate ? "WebAssembly.instantiateStreaming"
   5271                                       : "WebAssembly.compileStreaming";
   5272 
   5273  FeatureOptions options;
   5274  if (!options.init(cx, featureOptions)) {
   5275    return false;
   5276  }
   5277 
   5278  SharedCompileArgs compileArgs = InitCompileArgs(cx, options, introducer);
   5279  if (!compileArgs) {
   5280    return false;
   5281  }
   5282 
   5283  RootedObject closure(
   5284      cx, ResolveResponseClosure::create(cx, *compileArgs, resultPromise,
   5285                                         instantiate, importObj));
   5286  if (!closure) {
   5287    return false;
   5288  }
   5289 
   5290  RootedFunction onResolved(
   5291      cx, NewNativeFunction(cx, ResolveResponse_OnFulfilled, 1, nullptr,
   5292                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
   5293  if (!onResolved) {
   5294    return false;
   5295  }
   5296 
   5297  RootedFunction onRejected(
   5298      cx, NewNativeFunction(cx, ResolveResponse_OnRejected, 1, nullptr,
   5299                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
   5300  if (!onRejected) {
   5301    return false;
   5302  }
   5303 
   5304  onResolved->setExtendedSlot(0, ObjectValue(*closure));
   5305  onRejected->setExtendedSlot(0, ObjectValue(*closure));
   5306 
   5307  RootedObject resolve(cx,
   5308                       PromiseObject::unforgeableResolve(cx, responsePromise));
   5309  if (!resolve) {
   5310    return false;
   5311  }
   5312 
   5313  return JS::AddPromiseReactions(cx, resolve, onResolved, onRejected);
   5314 }
   5315 
   5316 static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
   5317                                         Value* vp) {
   5318  if (!EnsureStreamSupport(cx)) {
   5319    return false;
   5320  }
   5321 
   5322  Log(cx, "async compileStreaming() started");
   5323 
   5324  Rooted<PromiseObject*> resultPromise(
   5325      cx, PromiseObject::createSkippingExecutor(cx));
   5326  if (!resultPromise) {
   5327    return false;
   5328  }
   5329 
   5330  CallArgs callArgs = CallArgsFromVp(argc, vp);
   5331 
   5332  JS::RootedVector<JSString*> parameterStrings(cx);
   5333  JS::RootedVector<Value> parameterArgs(cx);
   5334  bool canCompileStrings = false;
   5335  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
   5336                                   JS::CompilationType::Undefined,
   5337                                   parameterStrings, nullptr, parameterArgs,
   5338                                   NullHandleValue, &canCompileStrings)) {
   5339    return RejectWithPendingException(cx, resultPromise, callArgs);
   5340  }
   5341  if (!canCompileStrings) {
   5342    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5343                              JSMSG_CSP_BLOCKED_WASM,
   5344                              "WebAssembly.compileStreaming");
   5345    return RejectWithPendingException(cx, resultPromise, callArgs);
   5346  }
   5347 
   5348  Rooted<Value> responsePromise(cx, callArgs.get(0));
   5349  Rooted<Value> featureOptions(cx, callArgs.get(1));
   5350  if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise)) {
   5351    return RejectWithPendingException(cx, resultPromise, callArgs);
   5352  }
   5353 
   5354  callArgs.rval().setObject(*resultPromise);
   5355  return true;
   5356 }
   5357 
   5358 static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
   5359                                             Value* vp) {
   5360  if (!EnsureStreamSupport(cx)) {
   5361    return false;
   5362  }
   5363 
   5364  Log(cx, "async instantiateStreaming() started");
   5365 
   5366  Rooted<PromiseObject*> resultPromise(
   5367      cx, PromiseObject::createSkippingExecutor(cx));
   5368  if (!resultPromise) {
   5369    return false;
   5370  }
   5371 
   5372  CallArgs callArgs = CallArgsFromVp(argc, vp);
   5373 
   5374  JS::RootedVector<JSString*> parameterStrings(cx);
   5375  JS::RootedVector<Value> parameterArgs(cx);
   5376  bool canCompileStrings = false;
   5377  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
   5378                                   JS::CompilationType::Undefined,
   5379                                   parameterStrings, nullptr, parameterArgs,
   5380                                   NullHandleValue, &canCompileStrings)) {
   5381    return RejectWithPendingException(cx, resultPromise, callArgs);
   5382  }
   5383  if (!canCompileStrings) {
   5384    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   5385                              JSMSG_CSP_BLOCKED_WASM,
   5386                              "WebAssembly.instantiateStreaming");
   5387    return RejectWithPendingException(cx, resultPromise, callArgs);
   5388  }
   5389 
   5390  Rooted<JSObject*> firstArg(cx);
   5391  Rooted<JSObject*> importObj(cx);
   5392  Rooted<Value> featureOptions(cx);
   5393  if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj,
   5394                          &featureOptions)) {
   5395    return RejectWithPendingException(cx, resultPromise, callArgs);
   5396  }
   5397  Rooted<Value> responsePromise(cx, ObjectValue(*firstArg.get()));
   5398 
   5399  if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise, true,
   5400                       importObj)) {
   5401    return RejectWithPendingException(cx, resultPromise, callArgs);
   5402  }
   5403 
   5404  callArgs.rval().setObject(*resultPromise);
   5405  return true;
   5406 }
   5407 
   5408 #ifdef ENABLE_WASM_JSPI
   5409 const ClassSpec WasmSuspendingObject::classSpec_ = {
   5410    GenericCreateConstructor<construct, 1, gc::AllocKind::FUNCTION>,
   5411    GenericCreatePrototype<WasmSuspendingObject>,
   5412    nullptr,
   5413    nullptr,
   5414    nullptr,
   5415    nullptr,
   5416    nullptr,
   5417    ClassSpec::DontDefineConstructor,
   5418 };
   5419 
   5420 const JSClass WasmSuspendingObject::class_ = {
   5421    "Suspending",
   5422    JSCLASS_HAS_RESERVED_SLOTS(WasmSuspendingObject::RESERVED_SLOTS),
   5423    JS_NULL_CLASS_OPS,
   5424    &classSpec_,
   5425 };
   5426 
   5427 const JSClass& WasmSuspendingObject::protoClass_ = PlainObject::class_;
   5428 
   5429 /* static */
   5430 bool WasmSuspendingObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   5431  CallArgs args = CallArgsFromVp(argc, vp);
   5432 
   5433  if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Suspending")) {
   5434    return false;
   5435  }
   5436 
   5437  if (!args.requireAtLeast(cx, "WebAssembly.Suspending", 1)) {
   5438    return false;
   5439  }
   5440 
   5441  if (!IsCallableNonCCW(args[0])) {
   5442    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   5443                             JSMSG_WASM_BAD_FUNCTION_VALUE);
   5444    return false;
   5445  }
   5446 
   5447  RootedObject callable(cx, &args[0].toObject());
   5448  Rooted<WasmSuspendingObject*> suspending(
   5449      cx, NewBuiltinClassInstance<WasmSuspendingObject>(cx));
   5450  if (!suspending) {
   5451    return false;
   5452  }
   5453  suspending->setWrappedFunction(callable);
   5454  args.rval().setObject(*suspending);
   5455  return true;
   5456 }
   5457 
   5458 static bool WebAssembly_promising(JSContext* cx, unsigned argc, Value* vp) {
   5459  CallArgs args = CallArgsFromVp(argc, vp);
   5460 
   5461  if (!args.requireAtLeast(cx, "WebAssembly.promising", 1)) {
   5462    return false;
   5463  }
   5464 
   5465  if (!IsWasmFunction(args[0])) {
   5466    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   5467                             JSMSG_WASM_BAD_FUNCTION_VALUE);
   5468    return false;
   5469  }
   5470 
   5471  RootedObject func(cx, &args[0].toObject());
   5472  RootedFunction promise(
   5473      cx, WasmPromisingFunctionCreate(cx, func, wasm::ValTypeVector(),
   5474                                      wasm::ValTypeVector()));
   5475  if (!promise) {
   5476    return false;
   5477  }
   5478  args.rval().setObject(*promise);
   5479  return true;
   5480 }
   5481 
   5482 static const JSFunctionSpec WebAssembly_jspi_methods[] = {
   5483    JS_FN("promising", WebAssembly_promising, 1, JSPROP_ENUMERATE),
   5484    JS_FS_END,
   5485 };
   5486 
   5487 bool js::IsWasmSuspendingObject(JSObject* obj) {
   5488  return obj->is<WasmSuspendingObject>();
   5489 }
   5490 
   5491 JSObject* js::MaybeUnwrapSuspendingObject(JSObject* wrapper) {
   5492  if (!wrapper->is<WasmSuspendingObject>()) {
   5493    return nullptr;
   5494  }
   5495  return wrapper->as<WasmSuspendingObject>().wrappedFunction();
   5496 }
   5497 #else
   5498 bool js::IsWasmSuspendingObject(JSObject* obj) { return false; }
   5499 #endif  // ENABLE_WASM_JSPI
   5500 
   5501 #ifdef ENABLE_WASM_MOZ_INTGEMM
   5502 
   5503 static bool WebAssembly_mozIntGemm(JSContext* cx, unsigned argc, Value* vp) {
   5504  CallArgs args = CallArgsFromVp(argc, vp);
   5505 
   5506  Rooted<WasmModuleObject*> module(cx);
   5507  if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::IntGemm, nullptr,
   5508                                  &module)) {
   5509    ReportOutOfMemory(cx);
   5510    return false;
   5511  }
   5512  args.rval().set(ObjectValue(*module.get()));
   5513  return true;
   5514 }
   5515 
   5516 static const JSFunctionSpec WebAssembly_mozIntGemm_methods[] = {
   5517    JS_FN("mozIntGemm", WebAssembly_mozIntGemm, 0, JSPROP_ENUMERATE),
   5518    JS_FS_END,
   5519 };
   5520 
   5521 #endif  // ENABLE_WASM_MOZ_INTGEMM
   5522 
   5523 static const JSFunctionSpec WebAssembly_static_methods[] = {
   5524    JS_FN("toSource", WebAssembly_toSource, 0, 0),
   5525    JS_FN("compile", WebAssembly_compile, 1, JSPROP_ENUMERATE),
   5526    JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE),
   5527    JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE),
   5528    JS_FN("compileStreaming", WebAssembly_compileStreaming, 1,
   5529          JSPROP_ENUMERATE),
   5530    JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1,
   5531          JSPROP_ENUMERATE),
   5532    JS_FS_END,
   5533 };
   5534 
   5535 static const JSPropertySpec WebAssembly_static_properties[] = {
   5536    JS_STRING_SYM_PS(toStringTag, "WebAssembly", JSPROP_READONLY),
   5537    JS_PS_END,
   5538 };
   5539 
   5540 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
   5541  MOZ_RELEASE_ASSERT(HasSupport(cx));
   5542 
   5543  RootedObject proto(cx, &cx->global()->getObjectPrototype());
   5544  return NewTenuredObjectWithGivenProto(cx, &WasmNamespaceObject::class_,
   5545                                        proto);
   5546 }
   5547 
   5548 struct NameAndProtoKey {
   5549  const char* const name;
   5550  JSProtoKey key;
   5551 };
   5552 
   5553 static bool WebAssemblyDefineConstructor(JSContext* cx,
   5554                                         Handle<WasmNamespaceObject*> wasm,
   5555                                         NameAndProtoKey entry,
   5556                                         MutableHandleValue ctorValue,
   5557                                         MutableHandleId id) {
   5558  JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, entry.key);
   5559  if (!ctor) {
   5560    return false;
   5561  }
   5562  ctorValue.setObject(*ctor);
   5563 
   5564  JSAtom* className = Atomize(cx, entry.name, strlen(entry.name));
   5565  if (!className) {
   5566    return false;
   5567  }
   5568  id.set(AtomToId(className));
   5569 
   5570  return DefineDataProperty(cx, wasm, id, ctorValue, 0);
   5571 }
   5572 
   5573 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object,
   5574                                   HandleObject proto) {
   5575  Handle<WasmNamespaceObject*> wasm = object.as<WasmNamespaceObject>();
   5576 
   5577  constexpr NameAndProtoKey entries[] = {
   5578      {"Module", JSProto_WasmModule},
   5579      {"Instance", JSProto_WasmInstance},
   5580      {"Memory", JSProto_WasmMemory},
   5581      {"Table", JSProto_WasmTable},
   5582      {"Global", JSProto_WasmGlobal},
   5583      {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
   5584      {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
   5585      {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
   5586 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
   5587      {"Function", JSProto_WasmFunction},
   5588 #endif
   5589  };
   5590  RootedValue ctorValue(cx);
   5591  RootedId id(cx);
   5592  for (const auto& entry : entries) {
   5593    if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) {
   5594      return false;
   5595    }
   5596  }
   5597 
   5598  constexpr NameAndProtoKey exceptionEntries[] = {
   5599      {"Tag", JSProto_WasmTag},
   5600      {"Exception", JSProto_WasmException},
   5601  };
   5602  for (const auto& entry : exceptionEntries) {
   5603    if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) {
   5604      return false;
   5605    }
   5606  }
   5607 
   5608  RootedObject tagProto(
   5609      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTag));
   5610  if (!tagProto) {
   5611    ReportOutOfMemory(cx);
   5612    return false;
   5613  }
   5614 
   5615  SharedTagType wrappedJSValueTagType(sWrappedJSValueTagType);
   5616  WasmTagObject* wrappedJSValueTagObject =
   5617      WasmTagObject::create(cx, wrappedJSValueTagType, tagProto);
   5618  if (!wrappedJSValueTagObject) {
   5619    return false;
   5620  }
   5621 
   5622  wasm->setWrappedJSValueTag(wrappedJSValueTagObject);
   5623 
   5624  RootedId jsTagName(cx, NameToId(cx->names().jsTag));
   5625  RootedValue jsTagValue(cx, ObjectValue(*wrappedJSValueTagObject));
   5626  if (!DefineDataProperty(cx, wasm, jsTagName, jsTagValue,
   5627                          JSPROP_READONLY | JSPROP_ENUMERATE)) {
   5628    return false;
   5629  }
   5630 
   5631 #ifdef ENABLE_WASM_JSPI
   5632  constexpr NameAndProtoKey jspiEntries[] = {
   5633      {"Suspending", JSProto_WasmSuspending},
   5634      {"SuspendError", GetExceptionProtoKey(JSEXN_WASMSUSPENDERROR)},
   5635  };
   5636  if (JSPromiseIntegrationAvailable(cx)) {
   5637    if (!JS_DefineFunctions(cx, wasm, WebAssembly_jspi_methods)) {
   5638      return false;
   5639    }
   5640    for (const auto& entry : jspiEntries) {
   5641      if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) {
   5642        return false;
   5643      }
   5644    }
   5645  }
   5646 #endif
   5647 
   5648 #ifdef ENABLE_WASM_MOZ_INTGEMM
   5649  if (MozIntGemmAvailable(cx) &&
   5650      !JS_DefineFunctions(cx, wasm, WebAssembly_mozIntGemm_methods)) {
   5651    return false;
   5652  }
   5653 #endif
   5654 
   5655  return true;
   5656 }
   5657 
   5658 WasmNamespaceObject* WasmNamespaceObject::getOrCreate(JSContext* cx) {
   5659  JSObject* wasm =
   5660      GlobalObject::getOrCreateConstructor(cx, JSProto_WebAssembly);
   5661  if (!wasm) {
   5662    return nullptr;
   5663  }
   5664  return &wasm->as<WasmNamespaceObject>();
   5665 }
   5666 
   5667 static const ClassSpec WebAssemblyClassSpec = {
   5668    CreateWebAssemblyObject,       nullptr, WebAssembly_static_methods,
   5669    WebAssembly_static_properties, nullptr, nullptr,
   5670    WebAssemblyClassFinish,
   5671 };
   5672 
   5673 const JSClass js::WasmNamespaceObject::class_ = {
   5674    "WebAssembly",
   5675    JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly) |
   5676        JSCLASS_HAS_RESERVED_SLOTS(WasmNamespaceObject::RESERVED_SLOTS),
   5677    JS_NULL_CLASS_OPS,
   5678    &WebAssemblyClassSpec,
   5679 };
   5680 
   5681 // Sundry