tor-browser

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

WasmModule.cpp (35666B)


      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 2015 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/WasmModule.h"
     20 
     21 #include <chrono>
     22 
     23 #include "js/BuildId.h"                 // JS::BuildIdCharVector
     24 #include "js/experimental/TypedData.h"  // JS_NewUint8Array
     25 #include "js/friend/ErrorMessages.h"    // js::GetErrorMessage, JSMSG_*
     26 #include "js/Printf.h"                  // JS_smprintf
     27 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_DefinePropertyById
     28 #include "js/StreamConsumer.h"
     29 #include "threading/LockGuard.h"
     30 #include "threading/Thread.h"
     31 #include "util/DifferentialTesting.h"
     32 #include "vm/HelperThreadState.h"  // Tier2GeneratorTask
     33 #include "vm/PlainObject.h"        // js::PlainObject
     34 #include "vm/Warnings.h"           // WarnNumberASCII
     35 #include "wasm/WasmBaselineCompile.h"
     36 #include "wasm/WasmCompile.h"
     37 #include "wasm/WasmDebug.h"
     38 #include "wasm/WasmInstance.h"
     39 #include "wasm/WasmIonCompile.h"
     40 #include "wasm/WasmJS.h"
     41 #include "wasm/WasmModuleTypes.h"
     42 #include "wasm/WasmPI.h"
     43 #include "wasm/WasmSerialize.h"
     44 #include "wasm/WasmUtility.h"
     45 
     46 #include "debugger/DebugAPI-inl.h"
     47 #include "vm/ArrayBufferObject-inl.h"
     48 #include "vm/JSAtomUtils-inl.h"  // AtomToId
     49 #include "wasm/WasmInstance-inl.h"
     50 
     51 using namespace js;
     52 using namespace js::jit;
     53 using namespace js::wasm;
     54 
     55 static UniqueChars Tier2ResultsContext(const ScriptedCaller& scriptedCaller) {
     56  return scriptedCaller.filename
     57             ? JS_smprintf("%s:%d", scriptedCaller.filename.get(),
     58                           scriptedCaller.line)
     59             : UniqueChars();
     60 }
     61 
     62 void js::wasm::ReportTier2ResultsOffThread(bool cancelled, bool success,
     63                                           Maybe<uint32_t> maybeFuncIndex,
     64                                           const ScriptedCaller& scriptedCaller,
     65                                           const UniqueChars& error,
     66                                           const UniqueCharsVector& warnings) {
     67  // Get context to describe this tier-2 task.
     68  UniqueChars context = Tier2ResultsContext(scriptedCaller);
     69  const char* contextString = context ? context.get() : "unknown";
     70 
     71  // Display the main error, if any.
     72  if (!success || cancelled) {
     73    const char* errorString = error       ? error.get()
     74                              : cancelled ? "compilation cancelled"
     75                                          : "out of memory";
     76    if (maybeFuncIndex.isSome()) {
     77      LogOffThread(
     78          "'%s': wasm partial tier-2 (func index %u) failed with '%s'.\n",
     79          contextString, maybeFuncIndex.value(), errorString);
     80    } else {
     81      LogOffThread("'%s': wasm complete tier-2 failed with '%s'.\n",
     82                   contextString, errorString);
     83    }
     84  }
     85 
     86  // Display warnings as a follow-up, avoiding spamming the console.
     87  size_t numWarnings = std::min<size_t>(warnings.length(), 3);
     88 
     89  for (size_t i = 0; i < numWarnings; i++) {
     90    // Per the assertion above, we won't get warnings for partial tier-2.
     91    LogOffThread("'%s': wasm complete tier-2 warning: '%s'.\n'.", contextString,
     92                 warnings[i].get());
     93  }
     94  if (warnings.length() > numWarnings) {
     95    LogOffThread("'%s': other warnings suppressed.\n", contextString);
     96  }
     97 }
     98 
     99 class Module::CompleteTier2GeneratorTaskImpl
    100    : public CompleteTier2GeneratorTask {
    101  SharedBytes codeSection_;
    102  SharedModule module_;
    103  mozilla::Atomic<bool> cancelled_;
    104 
    105 public:
    106  CompleteTier2GeneratorTaskImpl(const ShareableBytes* codeSection,
    107                                 Module& module)
    108      : codeSection_(codeSection), module_(&module), cancelled_(false) {}
    109 
    110  ~CompleteTier2GeneratorTaskImpl() override {
    111    module_->completeTier2Listener_ = nullptr;
    112    module_->testingTier2Active_ = false;
    113  }
    114 
    115  void cancel() override { cancelled_ = true; }
    116 
    117  void runHelperThreadTask(AutoLockHelperThreadState& locked) override {
    118    {
    119      AutoUnlockHelperThreadState unlock(locked);
    120 
    121      // Compile complete tier-2 and report any warning/errors as long as it's
    122      // not a cancellation. Encountering a warning/error during compilation
    123      // and being cancelled may race with each other, but the only observable
    124      // race should be being cancelled after a warning/error is set, and
    125      // that's okay.
    126      UniqueChars error;
    127      UniqueCharsVector warnings;
    128      bool success = CompileCompleteTier2(codeSection_, *module_, &error,
    129                                          &warnings, &cancelled_);
    130      if (!cancelled_) {
    131        // We could try to dispatch a runnable to the thread that started this
    132        // compilation, so as to report the warning/error using a JSContext*.
    133        // For now we just report to stderr.
    134        ReportTier2ResultsOffThread(cancelled_, success, mozilla::Nothing(),
    135                                    module_->codeMeta().scriptedCaller(), error,
    136                                    warnings);
    137      }
    138    }
    139 
    140    // During shutdown the main thread will wait for any ongoing (cancelled)
    141    // complete tier-2 generation to shut down normally.  To do so, it waits on
    142    // the HelperThreadState's condition variable for the count of finished
    143    // generators to rise.
    144    HelperThreadState().incWasmCompleteTier2GeneratorsFinished(locked);
    145 
    146    // The task is finished, release it.
    147    js_delete(this);
    148  }
    149 
    150  ThreadType threadType() override {
    151    return ThreadType::THREAD_TYPE_WASM_GENERATOR_COMPLETE_TIER2;
    152  }
    153 };
    154 
    155 Module::~Module() {
    156  // Note: Modules can be destroyed on any thread.
    157  MOZ_ASSERT(!completeTier2Listener_);
    158  MOZ_ASSERT(!testingTier2Active_);
    159 }
    160 
    161 void Module::startTier2(const ShareableBytes* codeSection,
    162                        JS::OptimizedEncodingListener* listener) {
    163  MOZ_ASSERT(!testingTier2Active_);
    164  MOZ_ASSERT_IF(codeMeta().codeSectionRange.isSome(), codeSection);
    165 
    166  auto task = MakeUnique<CompleteTier2GeneratorTaskImpl>(codeSection, *this);
    167  if (!task) {
    168    return;
    169  }
    170 
    171  // These will be cleared asynchronously by ~CompleteTier2GeneratorTaskImpl()
    172  // if not sooner by finishTier2().
    173  completeTier2Listener_ = listener;
    174  testingTier2Active_ = true;
    175 
    176  StartOffThreadWasmCompleteTier2Generator(std::move(task));
    177 }
    178 
    179 bool Module::finishTier2(UniqueCodeBlock tier2CodeBlock,
    180                         UniqueLinkData tier2LinkData,
    181                         const CompileAndLinkStats& tier2Stats) const {
    182  if (!code_->finishTier2(std::move(tier2CodeBlock), std::move(tier2LinkData),
    183                          tier2Stats)) {
    184    return false;
    185  }
    186 
    187  // Tier-2 is done; let everyone know. Mark tier-2 active for testing
    188  // purposes so that wasmHasTier2CompilationCompleted() only returns true
    189  // after tier-2 has been fully cached.
    190 
    191  if (completeTier2Listener_ && canSerialize()) {
    192    Bytes bytes;
    193    if (serialize(&bytes)) {
    194      completeTier2Listener_->storeOptimizedEncoding(bytes.begin(),
    195                                                     bytes.length());
    196    }
    197    completeTier2Listener_ = nullptr;
    198  }
    199  testingTier2Active_ = false;
    200 
    201  return true;
    202 }
    203 
    204 void Module::testingBlockOnTier2Complete() const {
    205  while (testingTier2Active_) {
    206    ThisThread::SleepMilliseconds(1);
    207  }
    208 }
    209 
    210 /* virtual */
    211 JSObject* Module::createObject(JSContext* cx) const {
    212  if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
    213    return nullptr;
    214  }
    215 
    216  JS::RootedVector<JSString*> parameterStrings(cx);
    217  JS::RootedVector<Value> parameterArgs(cx);
    218  bool canCompileStrings = false;
    219  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr,
    220                                   JS::CompilationType::Undefined,
    221                                   parameterStrings, nullptr, parameterArgs,
    222                                   NullHandleValue, &canCompileStrings)) {
    223    return nullptr;
    224  }
    225  if (!canCompileStrings) {
    226    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    227                              JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module");
    228    return nullptr;
    229  }
    230 
    231  RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule));
    232  return WasmModuleObject::create(cx, *this, proto);
    233 }
    234 
    235 /* virtual */
    236 JSObject* Module::createObjectForAsmJS(JSContext* cx) const {
    237  // Use nullptr to get the default object prototype. These objects are never
    238  // exposed to script for asm.js.
    239  return WasmModuleObject::create(cx, *this, nullptr);
    240 }
    241 
    242 bool wasm::GetOptimizedEncodingBuildId(JS::BuildIdCharVector* buildId) {
    243  // From a JS API perspective, the "build id" covers everything that can
    244  // cause machine code to become invalid, so include both the actual build-id
    245  // and cpu-id.
    246 
    247  if (!GetBuildId || !GetBuildId(buildId)) {
    248    return false;
    249  }
    250 
    251  uint32_t cpu = ObservedCPUFeatures();
    252 
    253  if (!buildId->reserve(buildId->length() +
    254                        13 /* "()" + 8 nibbles + "m[+-][+-]" */)) {
    255    return false;
    256  }
    257 
    258  buildId->infallibleAppend('(');
    259  while (cpu) {
    260    buildId->infallibleAppend('0' + (cpu & 0xf));
    261    cpu >>= 4;
    262  }
    263  buildId->infallibleAppend(')');
    264 
    265  buildId->infallibleAppend('m');
    266  buildId->infallibleAppend(
    267      wasm::IsHugeMemoryEnabled(AddressType::I32, PageSize::Standard) ? '+'
    268                                                                      : '-');
    269  buildId->infallibleAppend(
    270      wasm::IsHugeMemoryEnabled(AddressType::I64, PageSize::Standard) ? '+'
    271                                                                      : '-');
    272 
    273  // We don't expect huge memory to be supported if custom page sizes are used.
    274 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
    275  MOZ_RELEASE_ASSERT(
    276      !wasm::IsHugeMemoryEnabled(AddressType::I32, PageSize::Tiny));
    277  MOZ_RELEASE_ASSERT(
    278      !wasm::IsHugeMemoryEnabled(AddressType::I64, PageSize::Tiny));
    279 #endif
    280 
    281  return true;
    282 }
    283 
    284 /* virtual */
    285 void Module::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf,
    286                           CodeMetadata::SeenSet* seenCodeMeta,
    287                           CodeMetadataForAsmJS::SeenSet* seenCodeMetaForAsmJS,
    288                           Code::SeenSet* seenCode, size_t* code,
    289                           size_t* data) const {
    290  code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenCodeMeta,
    291                                seenCodeMetaForAsmJS, seenCode, code, data);
    292  *data += mallocSizeOf(this);
    293 }
    294 
    295 // Extracting machine code as JS object. The result has the "code" property, as
    296 // a Uint8Array, and the "segments" property as array objects. The objects
    297 // contain offsets in the "code" array and basic information about a code
    298 // segment/function body.
    299 bool Module::extractCode(JSContext* cx, Tier tier,
    300                         MutableHandleValue vp) const {
    301  Rooted<PlainObject*> result(cx, NewPlainObject(cx));
    302  if (!result) {
    303    return false;
    304  }
    305 
    306  // This function is only used for testing purposes so we can simply
    307  // block on tiered compilation to complete.
    308  testingBlockOnTier2Complete();
    309 
    310  if (!code_->hasCompleteTier(tier)) {
    311    vp.setNull();
    312    return true;
    313  }
    314 
    315  const CodeBlock& codeBlock = code_->completeTierCodeBlock(tier);
    316  const CodeSegment& codeSegment = *codeBlock.segment;
    317  RootedObject codeObj(cx, JS_NewUint8Array(cx, codeSegment.lengthBytes()));
    318  if (!codeObj) {
    319    return false;
    320  }
    321 
    322  memcpy(codeObj->as<TypedArrayObject>().dataPointerUnshared(),
    323         codeSegment.base(), codeSegment.lengthBytes());
    324 
    325  RootedValue value(cx, ObjectValue(*codeObj));
    326  if (!JS_DefineProperty(cx, result, "code", value, JSPROP_ENUMERATE)) {
    327    return false;
    328  }
    329 
    330  RootedObject segments(cx, NewDenseEmptyArray(cx));
    331  if (!segments) {
    332    return false;
    333  }
    334 
    335  for (const CodeRange& p : codeBlock.codeRanges) {
    336    RootedObject segment(cx, NewPlainObjectWithProto(cx, nullptr));
    337    if (!segment) {
    338      return false;
    339    }
    340 
    341    value.setNumber((uint32_t)p.begin());
    342    if (!JS_DefineProperty(cx, segment, "begin", value, JSPROP_ENUMERATE)) {
    343      return false;
    344    }
    345 
    346    value.setNumber((uint32_t)p.end());
    347    if (!JS_DefineProperty(cx, segment, "end", value, JSPROP_ENUMERATE)) {
    348      return false;
    349    }
    350 
    351    value.setNumber((uint32_t)p.kind());
    352    if (!JS_DefineProperty(cx, segment, "kind", value, JSPROP_ENUMERATE)) {
    353      return false;
    354    }
    355 
    356    if (p.isFunction()) {
    357      value.setNumber((uint32_t)p.funcIndex());
    358      if (!JS_DefineProperty(cx, segment, "funcIndex", value,
    359                             JSPROP_ENUMERATE)) {
    360        return false;
    361      }
    362 
    363      value.setNumber((uint32_t)p.funcUncheckedCallEntry());
    364      if (!JS_DefineProperty(cx, segment, "funcBodyBegin", value,
    365                             JSPROP_ENUMERATE)) {
    366        return false;
    367      }
    368 
    369      value.setNumber((uint32_t)p.end());
    370      if (!JS_DefineProperty(cx, segment, "funcBodyEnd", value,
    371                             JSPROP_ENUMERATE)) {
    372        return false;
    373      }
    374    }
    375 
    376    if (!NewbornArrayPush(cx, segments, ObjectValue(*segment))) {
    377      return false;
    378    }
    379  }
    380 
    381  value.setObject(*segments);
    382  if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE)) {
    383    return false;
    384  }
    385 
    386  vp.setObject(*result);
    387  return true;
    388 }
    389 
    390 static const Import& FindImportFunction(const ImportVector& imports,
    391                                        uint32_t funcImportIndex) {
    392  for (const Import& import : imports) {
    393    if (import.kind != DefinitionKind::Function) {
    394      continue;
    395    }
    396    if (funcImportIndex == 0) {
    397      return import;
    398    }
    399    funcImportIndex--;
    400  }
    401  MOZ_CRASH("ran out of imports");
    402 }
    403 
    404 bool Module::instantiateFunctions(JSContext* cx,
    405                                  const JSObjectVector& funcImports) const {
    406 #ifdef DEBUG
    407  MOZ_ASSERT(funcImports.length() == code().funcImports().length());
    408 #endif
    409 
    410  if (codeMeta().isAsmJS()) {
    411    return true;
    412  }
    413 
    414  for (size_t i = 0; i < code().funcImports().length(); i++) {
    415    if (!funcImports[i]->is<JSFunction>()) {
    416      continue;
    417    }
    418 
    419    JSFunction* f = &funcImports[i]->as<JSFunction>();
    420    if (!f->isWasm() || codeMeta().funcImportsAreJS) {
    421      continue;
    422    }
    423 
    424    const TypeDef& exportFuncType = *f->wasmTypeDef();
    425    const TypeDef& importFuncType = code().codeMeta().getFuncTypeDef(i);
    426 
    427    if (!TypeDef::isSubTypeOf(&exportFuncType, &importFuncType)) {
    428      const Import& import = FindImportFunction(moduleMeta().imports, i);
    429      UniqueChars importModuleName = import.module.toQuotedString(cx);
    430      UniqueChars importFieldName = import.field.toQuotedString(cx);
    431      if (!importFieldName || !importModuleName) {
    432        ReportOutOfMemory(cx);
    433        return false;
    434      }
    435      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    436                               JSMSG_WASM_BAD_IMPORT_SIG,
    437                               importModuleName.get(), importFieldName.get());
    438      return false;
    439    }
    440  }
    441 
    442  return true;
    443 }
    444 
    445 template <typename T>
    446 static bool CheckLimits(JSContext* cx, T declaredMin,
    447                        const mozilla::Maybe<T>& declaredMax, T defaultMax,
    448                        T actualLength, const mozilla::Maybe<T>& actualMax,
    449                        bool isAsmJS, const char* kind) {
    450  if (isAsmJS) {
    451    MOZ_ASSERT(actualLength >= declaredMin);
    452    MOZ_ASSERT(!declaredMax);
    453    MOZ_ASSERT(actualLength == actualMax.value());
    454    return true;
    455  }
    456 
    457  if (actualLength < declaredMin ||
    458      actualLength > declaredMax.valueOr(defaultMax)) {
    459    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    460                             JSMSG_WASM_BAD_IMP_SIZE, kind);
    461    return false;
    462  }
    463 
    464  if ((actualMax && declaredMax && *actualMax > *declaredMax) ||
    465      (!actualMax && declaredMax)) {
    466    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    467                             JSMSG_WASM_BAD_IMP_MAX, kind);
    468    return false;
    469  }
    470 
    471  return true;
    472 }
    473 
    474 static bool CheckSharing(JSContext* cx, bool declaredShared, bool isShared) {
    475  if (isShared &&
    476      !cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
    477    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    478                              JSMSG_WASM_NO_SHMEM_LINK);
    479    return false;
    480  }
    481 
    482  if (declaredShared && !isShared) {
    483    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    484                              JSMSG_WASM_IMP_SHARED_REQD);
    485    return false;
    486  }
    487 
    488  if (!declaredShared && isShared) {
    489    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    490                              JSMSG_WASM_IMP_SHARED_BANNED);
    491    return false;
    492  }
    493 
    494  return true;
    495 }
    496 
    497 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
    498 static bool CheckPageSize(JSContext* cx, PageSize declaredPageSize,
    499                          PageSize actualPageSize) {
    500  if (declaredPageSize != actualPageSize) {
    501    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    502                             JSMSG_WASM_BAD_IMP_PAGE_SIZE);
    503    return false;
    504  }
    505 
    506  return true;
    507 }
    508 #endif
    509 
    510 // asm.js module instantiation supplies its own buffer, but for wasm, create and
    511 // initialize the buffer if one is requested. Either way, the buffer is wrapped
    512 // in a WebAssembly.Memory object which is what the Instance stores.
    513 bool Module::instantiateMemories(
    514    JSContext* cx, const WasmMemoryObjectVector& memoryImports,
    515    MutableHandle<WasmMemoryObjectVector> memoryObjs) const {
    516  for (uint32_t memoryIndex = 0; memoryIndex < codeMeta().memories.length();
    517       memoryIndex++) {
    518    const MemoryDesc& desc = codeMeta().memories[memoryIndex];
    519 
    520    Rooted<WasmMemoryObject*> memory(cx);
    521    if (memoryIndex < memoryImports.length()) {
    522      memory = memoryImports[memoryIndex];
    523      MOZ_ASSERT_IF(codeMeta().isAsmJS(),
    524                    memory->buffer().isPreparedForAsmJS());
    525      MOZ_ASSERT_IF(!codeMeta().isAsmJS(), memory->buffer().isWasm());
    526 
    527      if (memory->addressType() != desc.addressType()) {
    528        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    529                                 JSMSG_WASM_BAD_IMP_ADDRESS, "memory",
    530                                 ToString(memory->addressType()));
    531        return false;
    532      }
    533 
    534 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES
    535      // Page size needs to be checked first because comparisons between
    536      // incompatible page sizes are invalid in CheckLimits.
    537      if (!CheckPageSize(cx, desc.pageSize(), memory->pageSize())) {
    538        return false;
    539      }
    540 #endif
    541 
    542      if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(),
    543                       /* defaultMax */
    544                       MaxMemoryPages(desc.addressType(), desc.pageSize()),
    545                       /* actualLength */
    546                       memory->volatilePages(), memory->sourceMaxPages(),
    547                       codeMeta().isAsmJS(), "Memory")) {
    548        return false;
    549      }
    550 
    551      if (!CheckSharing(cx, desc.isShared(), memory->isShared())) {
    552        return false;
    553      }
    554    } else {
    555      MOZ_ASSERT(!codeMeta().isAsmJS());
    556 
    557      if (desc.initialPages() >
    558          MaxMemoryPages(desc.addressType(), desc.pageSize())) {
    559        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    560                                 JSMSG_WASM_MEM_IMP_LIMIT);
    561        return false;
    562      }
    563 
    564      Rooted<ArrayBufferObjectMaybeShared*> buffer(cx,
    565                                                   CreateWasmBuffer(cx, desc));
    566      if (!buffer) {
    567        return false;
    568      }
    569 
    570      RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory));
    571      memory = WasmMemoryObject::create(
    572          cx, buffer, IsHugeMemoryEnabled(desc.addressType(), desc.pageSize()),
    573          proto);
    574      if (!memory) {
    575        return false;
    576      }
    577    }
    578 
    579    MOZ_RELEASE_ASSERT(
    580        codeMeta().isAsmJS() ||
    581        memory->isHuge() ==
    582            IsHugeMemoryEnabled(desc.addressType(), desc.pageSize()));
    583 
    584    if (!memoryObjs.get().append(memory)) {
    585      ReportOutOfMemory(cx);
    586      return false;
    587    }
    588  }
    589  return true;
    590 }
    591 
    592 bool Module::instantiateTags(JSContext* cx,
    593                             WasmTagObjectVector& tagObjs) const {
    594  size_t tagLength = codeMeta().tags.length();
    595  if (tagLength == 0) {
    596    return true;
    597  }
    598  size_t importedTagsLength = tagObjs.length();
    599  if (tagObjs.length() <= tagLength && !tagObjs.resize(tagLength)) {
    600    ReportOutOfMemory(cx);
    601    return false;
    602  }
    603 
    604  uint32_t tagIndex = 0;
    605  RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTag));
    606  for (const TagDesc& desc : codeMeta().tags) {
    607    if (tagIndex >= importedTagsLength) {
    608      Rooted<WasmTagObject*> tagObj(
    609          cx, WasmTagObject::create(cx, desc.type, proto));
    610      if (!tagObj) {
    611        return false;
    612      }
    613      tagObjs[tagIndex] = tagObj;
    614    }
    615    tagIndex++;
    616  }
    617  return true;
    618 }
    619 
    620 bool Module::instantiateImportedTable(JSContext* cx, const TableDesc& td,
    621                                      Handle<WasmTableObject*> tableObj,
    622                                      WasmTableObjectVector* tableObjs,
    623                                      SharedTableVector* tables) const {
    624  MOZ_ASSERT(tableObj);
    625  MOZ_ASSERT(!codeMeta().isAsmJS());
    626 
    627  Table& table = tableObj->table();
    628  if (table.addressType() != td.addressType()) {
    629    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    630                             JSMSG_WASM_BAD_IMP_ADDRESS, "table",
    631                             ToString(tableObj->table().addressType()));
    632    return false;
    633  }
    634  if (!CheckLimits(cx, /*declaredMin=*/td.initialLength(),
    635                   /*declaredMax=*/td.maximumLength(),
    636                   /*defaultMax=*/MaxTableElemsValidation(td.addressType()),
    637                   /*actualLength=*/uint64_t(table.length()),
    638                   /*actualMax=*/table.maximum(), codeMeta().isAsmJS(),
    639                   "Table")) {
    640    return false;
    641  }
    642 
    643  if (!tables->append(&table)) {
    644    ReportOutOfMemory(cx);
    645    return false;
    646  }
    647 
    648  if (!tableObjs->append(tableObj)) {
    649    ReportOutOfMemory(cx);
    650    return false;
    651  }
    652 
    653  return true;
    654 }
    655 
    656 bool Module::instantiateLocalTable(JSContext* cx, const TableDesc& td,
    657                                   WasmTableObjectVector* tableObjs,
    658                                   SharedTableVector* tables) const {
    659  if (td.initialLength() > MaxTableElemsRuntime) {
    660    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    661                             JSMSG_WASM_TABLE_IMP_LIMIT);
    662    return false;
    663  }
    664 
    665  SharedTable table;
    666  Rooted<WasmTableObject*> tableObj(cx);
    667  if (td.isExported) {
    668    RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable));
    669    tableObj.set(WasmTableObject::create(cx, td.limits, td.elemType, proto));
    670    if (!tableObj) {
    671      return false;
    672    }
    673    table = &tableObj->table();
    674  } else {
    675    table = Table::create(cx, td, /* Handle<WasmTableObject*> = */ nullptr);
    676    if (!table) {
    677      return false;
    678    }
    679  }
    680 
    681  // Note, appending a null pointer for non-exported local tables.
    682  if (!tableObjs->append(tableObj.get())) {
    683    ReportOutOfMemory(cx);
    684    return false;
    685  }
    686 
    687  if (!tables->emplaceBack(table)) {
    688    ReportOutOfMemory(cx);
    689    return false;
    690  }
    691 
    692  return true;
    693 }
    694 
    695 bool Module::instantiateTables(JSContext* cx,
    696                               const WasmTableObjectVector& tableImports,
    697                               MutableHandle<WasmTableObjectVector> tableObjs,
    698                               SharedTableVector* tables) const {
    699  uint32_t tableIndex = 0;
    700  for (const TableDesc& td : codeMeta().tables) {
    701    if (tableIndex < tableImports.length()) {
    702      Rooted<WasmTableObject*> tableObj(cx, tableImports[tableIndex]);
    703 
    704      if (!instantiateImportedTable(cx, td, tableObj, &tableObjs.get(),
    705                                    tables)) {
    706        return false;
    707      }
    708    } else {
    709      if (!instantiateLocalTable(cx, td, &tableObjs.get(), tables)) {
    710        return false;
    711      }
    712    }
    713    tableIndex++;
    714  }
    715  return true;
    716 }
    717 
    718 static bool EnsureExportedGlobalObject(JSContext* cx,
    719                                       const ValVector& globalImportValues,
    720                                       size_t globalIndex,
    721                                       const GlobalDesc& global,
    722                                       WasmGlobalObjectVector& globalObjs) {
    723  if (globalIndex < globalObjs.length() && globalObjs[globalIndex]) {
    724    return true;
    725  }
    726 
    727  RootedVal val(cx);
    728  if (global.kind() == GlobalKind::Import) {
    729    // If this is an import, then this must be a constant global that was
    730    // provided without a global object. We must initialize it with the
    731    // provided value while we still can differentiate this case.
    732    MOZ_ASSERT(!global.isMutable());
    733    val.set(Val(globalImportValues[globalIndex]));
    734  } else {
    735    // If this is not an import, then the initial value will be set by
    736    // Instance::init() for indirect globals or else by CreateExportObject().
    737    // In either case, we initialize with a default value here.
    738    val.set(Val(global.type()));
    739  }
    740 
    741  RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal));
    742  Rooted<WasmGlobalObject*> go(
    743      cx, WasmGlobalObject::create(cx, val, global.isMutable(), proto));
    744  if (!go) {
    745    return false;
    746  }
    747 
    748  if (globalObjs.length() <= globalIndex &&
    749      !globalObjs.resize(globalIndex + 1)) {
    750    ReportOutOfMemory(cx);
    751    return false;
    752  }
    753 
    754  globalObjs[globalIndex] = go;
    755  return true;
    756 }
    757 
    758 bool Module::instantiateGlobals(JSContext* cx,
    759                                const ValVector& globalImportValues,
    760                                WasmGlobalObjectVector& globalObjs) const {
    761  // If there are exported globals that aren't in globalObjs because they
    762  // originate in this module or because they were immutable imports that came
    763  // in as primitive values then we must create cells in the globalObjs for
    764  // them here, as WasmInstanceObject::create() and CreateExportObject() will
    765  // need the cells to exist.
    766 
    767  const GlobalDescVector& globals = codeMeta().globals;
    768 
    769  for (const Export& exp : moduleMeta().exports) {
    770    if (exp.kind() != DefinitionKind::Global) {
    771      continue;
    772    }
    773    unsigned globalIndex = exp.globalIndex();
    774    const GlobalDesc& global = globals[globalIndex];
    775    if (!EnsureExportedGlobalObject(cx, globalImportValues, globalIndex, global,
    776                                    globalObjs)) {
    777      return false;
    778    }
    779  }
    780 
    781  // Imported globals that are not re-exported may also have received only a
    782  // primitive value; these globals are always immutable.  Assert that we do
    783  // not need to create any additional Global objects for such imports.
    784 
    785 #ifdef DEBUG
    786  size_t numGlobalImports = 0;
    787  for (const Import& import : moduleMeta().imports) {
    788    if (import.kind != DefinitionKind::Global) {
    789      continue;
    790    }
    791    size_t globalIndex = numGlobalImports++;
    792    const GlobalDesc& global = globals[globalIndex];
    793    MOZ_ASSERT(global.importIndex() == globalIndex);
    794    MOZ_ASSERT_IF(global.isIndirect(),
    795                  globalIndex < globalObjs.length() || globalObjs[globalIndex]);
    796  }
    797  MOZ_ASSERT_IF(!codeMeta().isAsmJS(),
    798                numGlobalImports == globals.length() ||
    799                    !globals[numGlobalImports].isImport());
    800 #endif
    801  return true;
    802 }
    803 
    804 static bool GetGlobalExport(JSContext* cx,
    805                            Handle<WasmInstanceObject*> instanceObj,
    806                            const JSObjectVector& funcImports,
    807                            const GlobalDesc& global, uint32_t globalIndex,
    808                            const ValVector& globalImportValues,
    809                            const WasmGlobalObjectVector& globalObjs,
    810                            MutableHandleValue val) {
    811  // A global object for this index is guaranteed to exist by
    812  // instantiateGlobals.
    813  Rooted<WasmGlobalObject*> globalObj(cx, globalObjs[globalIndex]);
    814  val.setObject(*globalObj);
    815 
    816  // We are responsible to set the initial value of the global object here if
    817  // it's not imported or indirect. Imported global objects have their initial
    818  // value set by their defining module, or are set by
    819  // EnsureExportedGlobalObject when a constant value is provided as an import.
    820  // Indirect exported globals that are not imported, are initialized in
    821  // Instance::init.
    822  if (global.isIndirect() || global.isImport()) {
    823    return true;
    824  }
    825 
    826  // This must be an exported immutable global defined in this module. The
    827  // instance either has compiled the value into the code or has its own copy
    828  // in its global data area. Either way, we must initialize the global object
    829  // with the same initial value.
    830  MOZ_ASSERT(!global.isMutable());
    831  MOZ_RELEASE_ASSERT(!global.isImport());
    832  RootedVal globalVal(cx);
    833  instanceObj->instance().constantGlobalGet(globalIndex, &globalVal);
    834  globalObj->setVal(globalVal);
    835  return true;
    836 }
    837 
    838 static bool CreateExportObject(
    839    JSContext* cx, Handle<WasmInstanceObject*> instanceObj,
    840    const JSObjectVector& funcImports, const WasmTableObjectVector& tableObjs,
    841    const WasmMemoryObjectVector& memoryObjs,
    842    const WasmTagObjectVector& tagObjs, const ValVector& globalImportValues,
    843    const WasmGlobalObjectVector& globalObjs, const ExportVector& exports) {
    844  Instance& instance = instanceObj->instance();
    845  const CodeMetadata& codeMeta = instance.codeMeta();
    846  const GlobalDescVector& globals = codeMeta.globals;
    847 
    848  if (codeMeta.isAsmJS() && exports.length() == 1 &&
    849      exports[0].fieldName().isEmpty()) {
    850    RootedFunction func(cx);
    851    if (!instance.getExportedFunction(cx, exports[0].funcIndex(), &func)) {
    852      return false;
    853    }
    854    instanceObj->initExportsObj(*func.get());
    855    return true;
    856  }
    857 
    858  RootedObject exportObj(cx);
    859  uint8_t propertyAttr = JSPROP_ENUMERATE;
    860 
    861  if (codeMeta.isAsmJS()) {
    862    exportObj = NewPlainObject(cx);
    863  } else {
    864    exportObj = NewPlainObjectWithProto(cx, nullptr);
    865    propertyAttr |= JSPROP_READONLY | JSPROP_PERMANENT;
    866  }
    867  if (!exportObj) {
    868    return false;
    869  }
    870 
    871  for (const Export& exp : exports) {
    872    JSAtom* atom = exp.fieldName().toAtom(cx);
    873    if (!atom) {
    874      return false;
    875    }
    876 
    877    RootedId id(cx, AtomToId(atom));
    878    RootedValue val(cx);
    879    switch (exp.kind()) {
    880      case DefinitionKind::Function: {
    881        RootedFunction func(cx);
    882        if (!instance.getExportedFunction(cx, exp.funcIndex(), &func)) {
    883          return false;
    884        }
    885        val = ObjectValue(*func);
    886        break;
    887      }
    888      case DefinitionKind::Table: {
    889        val = ObjectValue(*tableObjs[exp.tableIndex()]);
    890        break;
    891      }
    892      case DefinitionKind::Memory: {
    893        val = ObjectValue(*memoryObjs[exp.memoryIndex()]);
    894        break;
    895      }
    896      case DefinitionKind::Global: {
    897        const GlobalDesc& global = globals[exp.globalIndex()];
    898        if (!GetGlobalExport(cx, instanceObj, funcImports, global,
    899                             exp.globalIndex(), globalImportValues, globalObjs,
    900                             &val)) {
    901          return false;
    902        }
    903        break;
    904      }
    905      case DefinitionKind::Tag: {
    906        val = ObjectValue(*tagObjs[exp.tagIndex()]);
    907        break;
    908      }
    909    }
    910 
    911    if (!JS_DefinePropertyById(cx, exportObj, id, val, propertyAttr)) {
    912      return false;
    913    }
    914  }
    915 
    916  if (!codeMeta.isAsmJS()) {
    917    if (!PreventExtensions(cx, exportObj)) {
    918      return false;
    919    }
    920  }
    921 
    922  instanceObj->initExportsObj(*exportObj);
    923  return true;
    924 }
    925 
    926 bool Module::instantiate(JSContext* cx, ImportValues& imports,
    927                         HandleObject instanceProto,
    928                         MutableHandle<WasmInstanceObject*> instance) const {
    929  MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers);
    930 
    931  if (!instantiateFunctions(cx, imports.funcs)) {
    932    return false;
    933  }
    934 
    935  Rooted<WasmMemoryObjectVector> memories(cx);
    936  if (!instantiateMemories(cx, imports.memories, &memories)) {
    937    return false;
    938  }
    939 
    940  // Note that the following will extend imports.exceptionObjs with wrappers for
    941  // the local (non-imported) exceptions of the module.
    942  // The resulting vector is sparse, i.e., it will be null in slots that contain
    943  // exceptions that are neither exported or imported.
    944  // On the contrary, all the slots of exceptionTags will be filled with
    945  // unique tags.
    946 
    947  if (!instantiateTags(cx, imports.tagObjs)) {
    948    return false;
    949  }
    950 
    951  // Note that tableObjs is sparse: it will be null in slots that contain
    952  // tables that are neither exported nor imported.
    953 
    954  Rooted<WasmTableObjectVector> tableObjs(cx);
    955  SharedTableVector tables;
    956  if (!instantiateTables(cx, imports.tables, &tableObjs, &tables)) {
    957    return false;
    958  }
    959 
    960  if (!instantiateGlobals(cx, imports.globalValues, imports.globalObjs)) {
    961    return false;
    962  }
    963 
    964  UniqueDebugState maybeDebug;
    965  if (code().debugEnabled()) {
    966    maybeDebug = cx->make_unique<DebugState>(*code_, *this);
    967    if (!maybeDebug) {
    968      ReportOutOfMemory(cx);
    969      return false;
    970    }
    971  }
    972 
    973  instance.set(WasmInstanceObject::create(
    974      cx, code_, moduleMeta().dataSegments, moduleMeta().elemSegments,
    975      codeMeta().instanceDataLength, memories, std::move(tables), imports.funcs,
    976      codeMeta().globals, imports.globalValues, imports.globalObjs,
    977      imports.tagObjs, instanceProto, std::move(maybeDebug)));
    978  if (!instance) {
    979    return false;
    980  }
    981 
    982  if (!CreateExportObject(cx, instance, imports.funcs, tableObjs.get(),
    983                          memories.get(), imports.tagObjs, imports.globalValues,
    984                          imports.globalObjs, moduleMeta().exports)) {
    985    return false;
    986  }
    987 
    988  // Register the instance with the Realm so that it can find out about global
    989  // events like profiling being enabled in the realm. Registration does not
    990  // require a fully-initialized instance and must precede initSegments as the
    991  // final pre-requisite for a live instance.
    992 
    993  if (!cx->realm()->wasm.registerInstance(cx, instance)) {
    994    ReportOutOfMemory(cx);
    995    return false;
    996  }
    997 
    998  // Perform initialization as the final step after the instance is fully
    999  // constructed since this can make the instance live to content (even if the
   1000  // start function fails).
   1001 
   1002  if (!instance->instance().initSegments(cx, moduleMeta().dataSegments,
   1003                                         moduleMeta().elemSegments)) {
   1004    return false;
   1005  }
   1006 
   1007  // Now that the instance is fully live and initialized, the start function.
   1008  // Note that failure may cause instantiation to throw, but the instance may
   1009  // still be live via edges created by initSegments or the start function.
   1010 
   1011  if (codeMeta().startFuncIndex) {
   1012    FixedInvokeArgs<0> args(cx);
   1013    if (!instance->instance().callExport(cx, *codeMeta().startFuncIndex,
   1014                                         args)) {
   1015      return false;
   1016    }
   1017  }
   1018 
   1019  JSUseCounter useCounter =
   1020      codeMeta().isAsmJS() ? JSUseCounter::ASMJS : JSUseCounter::WASM;
   1021  cx->runtime()->setUseCounter(instance, useCounter);
   1022  SetUseCountersForFeatureUsage(cx, instance, moduleMeta().featureUsage);
   1023 
   1024  // Warn for deprecated features. Don't do this with differential testing as
   1025  // that will think these warnings are significant.
   1026  if (!js::SupportDifferentialTesting()) {
   1027    // Warn if the user is using the legacy exceptions proposal.
   1028    if (moduleMeta().featureUsage & FeatureUsage::LegacyExceptions) {
   1029      if (!js::WarnNumberASCII(cx, JSMSG_WASM_LEGACY_EXCEPTIONS_DEPRECATED)) {
   1030        if (cx->isExceptionPending()) {
   1031          cx->clearPendingException();
   1032        }
   1033      }
   1034    }
   1035  }
   1036 
   1037  if (cx->options().testWasmAwaitTier2() &&
   1038      code().mode() != CompileMode::LazyTiering) {
   1039    testingBlockOnTier2Complete();
   1040  }
   1041 
   1042  return true;
   1043 }