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 "vm/HelperThreadState.h" // Tier2GeneratorTask
31 #include "vm/PlainObject.h" // js::PlainObject
32 #include "wasm/WasmBaselineCompile.h"
33 #include "wasm/WasmCompile.h"
34 #include "wasm/WasmInstance.h"
35 #include "wasm/WasmIonCompile.h"
36 #include "wasm/WasmJS.h"
37 #include "wasm/WasmSerialize.h"
38 #include "wasm/WasmUtility.h"
39
40 #include "debugger/DebugAPI-inl.h"
41 #include "vm/ArrayBufferObject-inl.h"
42 #include "vm/JSAtom-inl.h"
43
44 using namespace js;
45 using namespace js::jit;
46 using namespace js::wasm;
47
Tier2ResultsContext(const ScriptedCaller & scriptedCaller)48 static UniqueChars Tier2ResultsContext(const ScriptedCaller& scriptedCaller) {
49 return scriptedCaller.filename
50 ? JS_smprintf("%s:%d", scriptedCaller.filename.get(),
51 scriptedCaller.line)
52 : UniqueChars();
53 }
54
ReportTier2ResultsOffThread(bool success,const ScriptedCaller & scriptedCaller,const UniqueChars & error,const UniqueCharsVector & warnings)55 static void ReportTier2ResultsOffThread(bool success,
56 const ScriptedCaller& scriptedCaller,
57 const UniqueChars& error,
58 const UniqueCharsVector& warnings) {
59 // Get context to describe this tier-2 task.
60 UniqueChars context = Tier2ResultsContext(scriptedCaller);
61 const char* contextString = context ? context.get() : "unknown";
62
63 // Display the main error, if any.
64 if (!success) {
65 const char* errorString = error ? error.get() : "out of memory";
66 LogOffThread("'%s': wasm tier-2 failed with '%s'.\n", contextString,
67 errorString);
68 }
69
70 // Display warnings as a follow-up, avoiding spamming the console.
71 size_t numWarnings = std::min<size_t>(warnings.length(), 3);
72
73 for (size_t i = 0; i < numWarnings; i++) {
74 LogOffThread("'%s': wasm tier-2 warning: '%s'.\n'.", contextString,
75 warnings[i].get());
76 }
77 if (warnings.length() > numWarnings) {
78 LogOffThread("'%s': other warnings suppressed.\n", contextString);
79 }
80 }
81
82 class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask {
83 SharedCompileArgs compileArgs_;
84 SharedBytes bytecode_;
85 SharedModule module_;
86 Atomic<bool> cancelled_;
87
88 public:
Tier2GeneratorTaskImpl(const CompileArgs & compileArgs,const ShareableBytes & bytecode,Module & module)89 Tier2GeneratorTaskImpl(const CompileArgs& compileArgs,
90 const ShareableBytes& bytecode, Module& module)
91 : compileArgs_(&compileArgs),
92 bytecode_(&bytecode),
93 module_(&module),
94 cancelled_(false) {}
95
~Tier2GeneratorTaskImpl()96 ~Tier2GeneratorTaskImpl() override {
97 module_->tier2Listener_ = nullptr;
98 module_->testingTier2Active_ = false;
99 }
100
cancel()101 void cancel() override { cancelled_ = true; }
102
runHelperThreadTask(AutoLockHelperThreadState & locked)103 void runHelperThreadTask(AutoLockHelperThreadState& locked) override {
104 {
105 AutoUnlockHelperThreadState unlock(locked);
106
107 // Compile tier-2 and report any warning/errors as long as it's not a
108 // cancellation. Encountering a warning/error during compilation and
109 // being cancelled may race with each other, but the only observable race
110 // should be being cancelled after a warning/error is set, and that's
111 // okay.
112 UniqueChars error;
113 UniqueCharsVector warnings;
114 bool success = CompileTier2(*compileArgs_, bytecode_->bytes, *module_,
115 &error, &warnings, &cancelled_);
116 if (!cancelled_) {
117 // We could try to dispatch a runnable to the thread that started this
118 // compilation, so as to report the warning/error using a JSContext*.
119 // For now we just report to stderr.
120 ReportTier2ResultsOffThread(success, compileArgs_->scriptedCaller,
121 error, warnings);
122 }
123 }
124
125 // During shutdown the main thread will wait for any ongoing (cancelled)
126 // tier-2 generation to shut down normally. To do so, it waits on the
127 // HelperThreadState's condition variable for the count of finished
128 // generators to rise.
129 HelperThreadState().incWasmTier2GeneratorsFinished(locked);
130
131 // The task is finished, release it.
132 js_delete(this);
133 }
134
threadType()135 ThreadType threadType() override {
136 return ThreadType::THREAD_TYPE_WASM_GENERATOR_TIER2;
137 }
138 };
139
~Module()140 Module::~Module() {
141 // Note: Modules can be destroyed on any thread.
142 MOZ_ASSERT(!tier2Listener_);
143 MOZ_ASSERT(!testingTier2Active_);
144 }
145
startTier2(const CompileArgs & args,const ShareableBytes & bytecode,JS::OptimizedEncodingListener * listener)146 void Module::startTier2(const CompileArgs& args, const ShareableBytes& bytecode,
147 JS::OptimizedEncodingListener* listener) {
148 MOZ_ASSERT(!testingTier2Active_);
149
150 auto task = MakeUnique<Tier2GeneratorTaskImpl>(args, bytecode, *this);
151 if (!task) {
152 return;
153 }
154
155 // These will be cleared asynchronously by ~Tier2GeneratorTaskImpl() if not
156 // sooner by finishTier2().
157 tier2Listener_ = listener;
158 testingTier2Active_ = true;
159
160 StartOffThreadWasmTier2Generator(std::move(task));
161 }
162
finishTier2(const LinkData & linkData2,UniqueCodeTier code2) const163 bool Module::finishTier2(const LinkData& linkData2,
164 UniqueCodeTier code2) const {
165 MOZ_ASSERT(code().bestTier() == Tier::Baseline &&
166 code2->tier() == Tier::Optimized);
167
168 // Install the data in the data structures. They will not be visible
169 // until commitTier2().
170
171 const CodeTier* borrowedTier2;
172 if (!code().setAndBorrowTier2(std::move(code2), linkData2, &borrowedTier2)) {
173 return false;
174 }
175
176 // Before we can make tier-2 live, we need to compile tier2 versions of any
177 // extant tier1 lazy stubs (otherwise, tiering would break the assumption
178 // that any extant exported wasm function has had a lazy entry stub already
179 // compiled for it).
180 //
181 // Also see doc block for stubs in WasmJS.cpp.
182 {
183 // We need to prevent new tier1 stubs generation until we've committed
184 // the newer tier2 stubs, otherwise we might not generate one tier2
185 // stub that has been generated for tier1 before we committed.
186
187 const MetadataTier& metadataTier1 = metadata(Tier::Baseline);
188
189 auto stubs1 = code().codeTier(Tier::Baseline).lazyStubs().readLock();
190 auto stubs2 = borrowedTier2->lazyStubs().writeLock();
191
192 MOZ_ASSERT(stubs2->entryStubsEmpty());
193
194 Uint32Vector funcExportIndices;
195 for (size_t i = 0; i < metadataTier1.funcExports.length(); i++) {
196 const FuncExport& fe = metadataTier1.funcExports[i];
197 if (fe.hasEagerStubs()) {
198 continue;
199 }
200 if (!stubs1->hasEntryStub(fe.funcIndex())) {
201 continue;
202 }
203 if (!funcExportIndices.emplaceBack(i)) {
204 return false;
205 }
206 }
207
208 Maybe<size_t> stub2Index;
209 if (!stubs2->createTier2(funcExportIndices, *borrowedTier2, &stub2Index)) {
210 return false;
211 }
212
213 // Now that we can't fail or otherwise abort tier2, make it live.
214
215 MOZ_ASSERT(!code().hasTier2());
216 code().commitTier2();
217
218 stubs2->setJitEntries(stub2Index, code());
219 }
220
221 // And we update the jump vectors with pointers to tier-2 functions and eager
222 // stubs. Callers will continue to invoke tier-1 code until, suddenly, they
223 // will invoke tier-2 code. This is benign.
224
225 uint8_t* base = code().segment(Tier::Optimized).base();
226 for (const CodeRange& cr : metadata(Tier::Optimized).codeRanges) {
227 // These are racy writes that we just want to be visible, atomically,
228 // eventually. All hardware we care about will do this right. But
229 // we depend on the compiler not splitting the stores hidden inside the
230 // set*Entry functions.
231 if (cr.isFunction()) {
232 code().setTieringEntry(cr.funcIndex(), base + cr.funcTierEntry());
233 } else if (cr.isJitEntry()) {
234 code().setJitEntry(cr.funcIndex(), base + cr.begin());
235 }
236 }
237
238 // Tier-2 is done; let everyone know. Mark tier-2 active for testing
239 // purposes so that wasmHasTier2CompilationCompleted() only returns true
240 // after tier-2 has been fully cached.
241
242 if (tier2Listener_) {
243 serialize(linkData2, *tier2Listener_);
244 tier2Listener_ = nullptr;
245 }
246 testingTier2Active_ = false;
247
248 return true;
249 }
250
testingBlockOnTier2Complete() const251 void Module::testingBlockOnTier2Complete() const {
252 while (testingTier2Active_) {
253 ThisThread::SleepMilliseconds(1);
254 }
255 }
256
257 /* virtual */
serializedSize(const LinkData & linkData) const258 size_t Module::serializedSize(const LinkData& linkData) const {
259 JS::BuildIdCharVector buildId;
260 {
261 AutoEnterOOMUnsafeRegion oom;
262 if (!GetOptimizedEncodingBuildId(&buildId)) {
263 oom.crash("getting build id");
264 }
265 }
266
267 return SerializedPodVectorSize(buildId) + linkData.serializedSize() +
268 SerializedVectorSize(imports_) + SerializedVectorSize(exports_) +
269 SerializedVectorSize(dataSegments_) +
270 SerializedVectorSize(elemSegments_) +
271 SerializedVectorSize(customSections_) + code_->serializedSize();
272 }
273
274 /* virtual */
serialize(const LinkData & linkData,uint8_t * begin,size_t size) const275 void Module::serialize(const LinkData& linkData, uint8_t* begin,
276 size_t size) const {
277 MOZ_RELEASE_ASSERT(!metadata().debugEnabled);
278 MOZ_RELEASE_ASSERT(code_->hasTier(Tier::Serialized));
279
280 JS::BuildIdCharVector buildId;
281 {
282 AutoEnterOOMUnsafeRegion oom;
283 if (!GetOptimizedEncodingBuildId(&buildId)) {
284 oom.crash("getting build id");
285 }
286 }
287
288 uint8_t* cursor = begin;
289 cursor = SerializePodVector(cursor, buildId);
290 cursor = linkData.serialize(cursor);
291 cursor = SerializeVector(cursor, imports_);
292 cursor = SerializeVector(cursor, exports_);
293 cursor = SerializeVector(cursor, dataSegments_);
294 cursor = SerializeVector(cursor, elemSegments_);
295 cursor = SerializeVector(cursor, customSections_);
296 cursor = code_->serialize(cursor, linkData);
297 MOZ_RELEASE_ASSERT(cursor == begin + size);
298 }
299
300 /* static */
deserialize(const uint8_t * begin,size_t size,Metadata * maybeMetadata)301 MutableModule Module::deserialize(const uint8_t* begin, size_t size,
302 Metadata* maybeMetadata) {
303 MutableMetadata metadata(maybeMetadata);
304 if (!metadata) {
305 metadata = js_new<Metadata>();
306 if (!metadata) {
307 return nullptr;
308 }
309 }
310
311 const uint8_t* cursor = begin;
312
313 JS::BuildIdCharVector currentBuildId;
314 if (!GetOptimizedEncodingBuildId(¤tBuildId)) {
315 return nullptr;
316 }
317
318 JS::BuildIdCharVector deserializedBuildId;
319 cursor = DeserializePodVector(cursor, &deserializedBuildId);
320 if (!cursor) {
321 return nullptr;
322 }
323
324 MOZ_RELEASE_ASSERT(EqualContainers(currentBuildId, deserializedBuildId));
325
326 LinkData linkData(Tier::Serialized);
327 cursor = linkData.deserialize(cursor);
328 if (!cursor) {
329 return nullptr;
330 }
331
332 ImportVector imports;
333 cursor = DeserializeVector(cursor, &imports);
334 if (!cursor) {
335 return nullptr;
336 }
337
338 ExportVector exports;
339 cursor = DeserializeVector(cursor, &exports);
340 if (!cursor) {
341 return nullptr;
342 }
343
344 DataSegmentVector dataSegments;
345 cursor = DeserializeVector(cursor, &dataSegments);
346 if (!cursor) {
347 return nullptr;
348 }
349
350 ElemSegmentVector elemSegments;
351 cursor = DeserializeVector(cursor, &elemSegments);
352 if (!cursor) {
353 return nullptr;
354 }
355
356 CustomSectionVector customSections;
357 cursor = DeserializeVector(cursor, &customSections);
358 if (!cursor) {
359 return nullptr;
360 }
361
362 SharedCode code;
363 cursor = Code::deserialize(cursor, linkData, *metadata, &code);
364 if (!cursor) {
365 return nullptr;
366 }
367
368 MOZ_RELEASE_ASSERT(cursor == begin + size);
369 MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
370
371 if (metadata->nameCustomSectionIndex) {
372 metadata->namePayload =
373 customSections[*metadata->nameCustomSectionIndex].payload;
374 } else {
375 MOZ_RELEASE_ASSERT(!metadata->moduleName);
376 MOZ_RELEASE_ASSERT(metadata->funcNames.empty());
377 }
378
379 return js_new<Module>(*code, std::move(imports), std::move(exports),
380 std::move(dataSegments), std::move(elemSegments),
381 std::move(customSections), nullptr, nullptr, nullptr,
382 /* loggingDeserialized = */ true);
383 }
384
serialize(const LinkData & linkData,JS::OptimizedEncodingListener & listener) const385 void Module::serialize(const LinkData& linkData,
386 JS::OptimizedEncodingListener& listener) const {
387 Bytes bytes;
388 if (!bytes.resizeUninitialized(serializedSize(linkData))) {
389 return;
390 }
391
392 serialize(linkData, bytes.begin(), bytes.length());
393
394 listener.storeOptimizedEncoding(bytes.begin(), bytes.length());
395 }
396
397 /* virtual */
createObject(JSContext * cx) const398 JSObject* Module::createObject(JSContext* cx) const {
399 if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
400 return nullptr;
401 }
402
403 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule));
404 return WasmModuleObject::create(cx, *this, proto);
405 }
406
407 /* virtual */
createObjectForAsmJS(JSContext * cx) const408 JSObject* Module::createObjectForAsmJS(JSContext* cx) const {
409 // Use nullptr to get the default object prototype. These objects are never
410 // exposed to script for asm.js.
411 return WasmModuleObject::create(cx, *this, nullptr);
412 }
413
GetOptimizedEncodingBuildId(JS::BuildIdCharVector * buildId)414 bool wasm::GetOptimizedEncodingBuildId(JS::BuildIdCharVector* buildId) {
415 // From a JS API perspective, the "build id" covers everything that can
416 // cause machine code to become invalid, so include both the actual build-id
417 // and cpu-id.
418
419 if (!GetBuildId || !GetBuildId(buildId)) {
420 return false;
421 }
422
423 uint32_t cpu = ObservedCPUFeatures();
424
425 if (!buildId->reserve(buildId->length() +
426 13 /* "()" + 8 nibbles + "m[+-][+-]" */)) {
427 return false;
428 }
429
430 buildId->infallibleAppend('(');
431 while (cpu) {
432 buildId->infallibleAppend('0' + (cpu & 0xf));
433 cpu >>= 4;
434 }
435 buildId->infallibleAppend(')');
436
437 buildId->infallibleAppend('m');
438 buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(IndexType::I32) ? '+'
439 : '-');
440 buildId->infallibleAppend(wasm::IsHugeMemoryEnabled(IndexType::I64) ? '+'
441 : '-');
442
443 return true;
444 }
445
446 /* virtual */
addSizeOfMisc(MallocSizeOf mallocSizeOf,Metadata::SeenSet * seenMetadata,Code::SeenSet * seenCode,size_t * code,size_t * data) const447 void Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
448 Metadata::SeenSet* seenMetadata,
449 Code::SeenSet* seenCode, size_t* code,
450 size_t* data) const {
451 code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code,
452 data);
453 *data += mallocSizeOf(this) +
454 SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
455 SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
456 SizeOfVectorExcludingThis(dataSegments_, mallocSizeOf) +
457 SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
458 SizeOfVectorExcludingThis(customSections_, mallocSizeOf);
459
460 if (debugUnlinkedCode_) {
461 *data += debugUnlinkedCode_->sizeOfExcludingThis(mallocSizeOf);
462 }
463 }
464
initGCMallocBytesExcludingCode()465 void Module::initGCMallocBytesExcludingCode() {
466 // The size doesn't have to be exact so use the serialization framework to
467 // calculate a value.
468 gcMallocBytesExcludingCode_ = sizeof(*this) + SerializedVectorSize(imports_) +
469 SerializedVectorSize(exports_) +
470 SerializedVectorSize(dataSegments_) +
471 SerializedVectorSize(elemSegments_) +
472 SerializedVectorSize(customSections_);
473 }
474
475 // Extracting machine code as JS object. The result has the "code" property, as
476 // a Uint8Array, and the "segments" property as array objects. The objects
477 // contain offsets in the "code" array and basic information about a code
478 // segment/function body.
extractCode(JSContext * cx,Tier tier,MutableHandleValue vp) const479 bool Module::extractCode(JSContext* cx, Tier tier,
480 MutableHandleValue vp) const {
481 RootedPlainObject result(cx, NewPlainObject(cx));
482 if (!result) {
483 return false;
484 }
485
486 // This function is only used for testing purposes so we can simply
487 // block on tiered compilation to complete.
488 testingBlockOnTier2Complete();
489
490 if (!code_->hasTier(tier)) {
491 vp.setNull();
492 return true;
493 }
494
495 const ModuleSegment& moduleSegment = code_->segment(tier);
496 RootedObject code(cx, JS_NewUint8Array(cx, moduleSegment.length()));
497 if (!code) {
498 return false;
499 }
500
501 memcpy(code->as<TypedArrayObject>().dataPointerUnshared(),
502 moduleSegment.base(), moduleSegment.length());
503
504 RootedValue value(cx, ObjectValue(*code));
505 if (!JS_DefineProperty(cx, result, "code", value, JSPROP_ENUMERATE)) {
506 return false;
507 }
508
509 RootedObject segments(cx, NewDenseEmptyArray(cx));
510 if (!segments) {
511 return false;
512 }
513
514 for (const CodeRange& p : metadata(tier).codeRanges) {
515 RootedObject segment(cx, NewPlainObjectWithProto(cx, nullptr));
516 if (!segment) {
517 return false;
518 }
519
520 value.setNumber((uint32_t)p.begin());
521 if (!JS_DefineProperty(cx, segment, "begin", value, JSPROP_ENUMERATE)) {
522 return false;
523 }
524
525 value.setNumber((uint32_t)p.end());
526 if (!JS_DefineProperty(cx, segment, "end", value, JSPROP_ENUMERATE)) {
527 return false;
528 }
529
530 value.setNumber((uint32_t)p.kind());
531 if (!JS_DefineProperty(cx, segment, "kind", value, JSPROP_ENUMERATE)) {
532 return false;
533 }
534
535 if (p.isFunction()) {
536 value.setNumber((uint32_t)p.funcIndex());
537 if (!JS_DefineProperty(cx, segment, "funcIndex", value,
538 JSPROP_ENUMERATE)) {
539 return false;
540 }
541
542 value.setNumber((uint32_t)p.funcUncheckedCallEntry());
543 if (!JS_DefineProperty(cx, segment, "funcBodyBegin", value,
544 JSPROP_ENUMERATE)) {
545 return false;
546 }
547
548 value.setNumber((uint32_t)p.end());
549 if (!JS_DefineProperty(cx, segment, "funcBodyEnd", value,
550 JSPROP_ENUMERATE)) {
551 return false;
552 }
553 }
554
555 if (!NewbornArrayPush(cx, segments, ObjectValue(*segment))) {
556 return false;
557 }
558 }
559
560 value.setObject(*segments);
561 if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE)) {
562 return false;
563 }
564
565 vp.setObject(*result);
566 return true;
567 }
568
569 #ifdef DEBUG
AllSegmentsArePassive(const DataSegmentVector & vec)570 static bool AllSegmentsArePassive(const DataSegmentVector& vec) {
571 for (const DataSegment* seg : vec) {
572 if (seg->active()) {
573 return false;
574 }
575 }
576 return true;
577 }
578 #endif
579
initSegments(JSContext * cx,HandleWasmInstanceObject instanceObj,HandleWasmMemoryObject memoryObj,const ValVector & globalImportValues) const580 bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj,
581 HandleWasmMemoryObject memoryObj,
582 const ValVector& globalImportValues) const {
583 MOZ_ASSERT_IF(!memoryObj, AllSegmentsArePassive(dataSegments_));
584
585 Instance& instance = instanceObj->instance();
586 const SharedTableVector& tables = instance.tables();
587
588 // Write data/elem segments into memories/tables.
589
590 for (const ElemSegment* seg : elemSegments_) {
591 if (seg->active()) {
592 RootedVal offsetVal(cx);
593 if (!seg->offset().evaluate(cx, globalImportValues, instanceObj,
594 &offsetVal)) {
595 return false; // OOM
596 }
597 uint32_t offset = offsetVal.get().i32();
598 uint32_t count = seg->length();
599
600 uint32_t tableLength = tables[seg->tableIndex]->length();
601 if (offset > tableLength || tableLength - offset < count) {
602 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
603 JSMSG_WASM_OUT_OF_BOUNDS);
604 return false;
605 }
606
607 if (!instance.initElems(seg->tableIndex, *seg, offset, 0, count)) {
608 return false; // OOM
609 }
610 }
611 }
612
613 if (memoryObj) {
614 size_t memoryLength = memoryObj->volatileMemoryLength();
615 uint8_t* memoryBase =
616 memoryObj->buffer().dataPointerEither().unwrap(/* memcpy */);
617
618 for (const DataSegment* seg : dataSegments_) {
619 if (!seg->active()) {
620 continue;
621 }
622
623 RootedVal offsetVal(cx);
624 if (!seg->offset().evaluate(cx, globalImportValues, instanceObj,
625 &offsetVal)) {
626 return false; // OOM
627 }
628 uint64_t offset = memoryObj->indexType() == IndexType::I32
629 ? offsetVal.get().i32()
630 : offsetVal.get().i64();
631 uint32_t count = seg->bytes.length();
632
633 if (offset > memoryLength || memoryLength - offset < count) {
634 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
635 JSMSG_WASM_OUT_OF_BOUNDS);
636 return false;
637 }
638 memcpy(memoryBase + uintptr_t(offset), seg->bytes.begin(), count);
639 }
640 }
641
642 return true;
643 }
644
FindImportFunction(const ImportVector & imports,uint32_t funcImportIndex)645 static const Import& FindImportFunction(const ImportVector& imports,
646 uint32_t funcImportIndex) {
647 for (const Import& import : imports) {
648 if (import.kind != DefinitionKind::Function) {
649 continue;
650 }
651 if (funcImportIndex == 0) {
652 return import;
653 }
654 funcImportIndex--;
655 }
656 MOZ_CRASH("ran out of imports");
657 }
658
instantiateFunctions(JSContext * cx,const JSFunctionVector & funcImports) const659 bool Module::instantiateFunctions(JSContext* cx,
660 const JSFunctionVector& funcImports) const {
661 #ifdef DEBUG
662 for (auto t : code().tiers()) {
663 MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
664 }
665 #endif
666
667 if (metadata().isAsmJS()) {
668 return true;
669 }
670
671 Tier tier = code().stableTier();
672
673 for (size_t i = 0; i < metadata(tier).funcImports.length(); i++) {
674 JSFunction* f = funcImports[i];
675 if (!IsWasmExportedFunction(f)) {
676 continue;
677 }
678
679 uint32_t funcIndex = ExportedFunctionToFuncIndex(f);
680 Instance& instance = ExportedFunctionToInstance(f);
681 Tier otherTier = instance.code().stableTier();
682
683 const FuncExport& funcExport =
684 instance.metadata(otherTier).lookupFuncExport(funcIndex);
685
686 if (funcExport.funcType() != metadata(tier).funcImports[i].funcType()) {
687 const Import& import = FindImportFunction(imports_, i);
688 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
689 JSMSG_WASM_BAD_IMPORT_SIG, import.module.get(),
690 import.field.get());
691 return false;
692 }
693 }
694
695 return true;
696 }
697
698 template <typename T>
CheckLimits(JSContext * cx,T declaredMin,const Maybe<T> & declaredMax,T defaultMax,T actualLength,const Maybe<T> & actualMax,bool isAsmJS,const char * kind)699 static bool CheckLimits(JSContext* cx, T declaredMin,
700 const Maybe<T>& declaredMax, T defaultMax,
701 T actualLength, const Maybe<T>& actualMax, bool isAsmJS,
702 const char* kind) {
703 if (isAsmJS) {
704 MOZ_ASSERT(actualLength >= declaredMin);
705 MOZ_ASSERT(!declaredMax);
706 MOZ_ASSERT(actualLength == actualMax.value());
707 return true;
708 }
709
710 if (actualLength < declaredMin ||
711 actualLength > declaredMax.valueOr(defaultMax)) {
712 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
713 JSMSG_WASM_BAD_IMP_SIZE, kind);
714 return false;
715 }
716
717 if ((actualMax && declaredMax && *actualMax > *declaredMax) ||
718 (!actualMax && declaredMax)) {
719 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
720 JSMSG_WASM_BAD_IMP_MAX, kind);
721 return false;
722 }
723
724 return true;
725 }
726
CheckSharing(JSContext * cx,bool declaredShared,bool isShared)727 static bool CheckSharing(JSContext* cx, bool declaredShared, bool isShared) {
728 if (isShared &&
729 !cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
730 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
731 JSMSG_WASM_NO_SHMEM_LINK);
732 return false;
733 }
734
735 if (declaredShared && !isShared) {
736 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
737 JSMSG_WASM_IMP_SHARED_REQD);
738 return false;
739 }
740
741 if (!declaredShared && isShared) {
742 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
743 JSMSG_WASM_IMP_SHARED_BANNED);
744 return false;
745 }
746
747 return true;
748 }
749
750 // asm.js module instantiation supplies its own buffer, but for wasm, create and
751 // initialize the buffer if one is requested. Either way, the buffer is wrapped
752 // in a WebAssembly.Memory object which is what the Instance stores.
instantiateMemory(JSContext * cx,MutableHandleWasmMemoryObject memory) const753 bool Module::instantiateMemory(JSContext* cx,
754 MutableHandleWasmMemoryObject memory) const {
755 if (!metadata().usesMemory()) {
756 MOZ_ASSERT(!memory);
757 MOZ_ASSERT(AllSegmentsArePassive(dataSegments_));
758 return true;
759 }
760
761 MemoryDesc desc = *metadata().memory;
762 if (memory) {
763 MOZ_ASSERT_IF(metadata().isAsmJS(), memory->buffer().isPreparedForAsmJS());
764 MOZ_ASSERT_IF(!metadata().isAsmJS(), memory->buffer().isWasm());
765
766 if (memory->indexType() != desc.indexType()) {
767 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
768 JSMSG_WASM_BAD_IMP_INDEX,
769 ToString(memory->indexType()));
770 return false;
771 }
772
773 if (!CheckLimits(cx, desc.initialPages(), desc.maximumPages(),
774 /* defaultMax */ MaxMemoryPages(desc.indexType()),
775 /* actualLength */
776 memory->volatilePages(), memory->sourceMaxPages(),
777 metadata().isAsmJS(), "Memory")) {
778 return false;
779 }
780
781 if (!CheckSharing(cx, desc.isShared(), memory->isShared())) {
782 return false;
783 }
784 } else {
785 MOZ_ASSERT(!metadata().isAsmJS());
786
787 if (desc.initialPages() > MaxMemoryPages(desc.indexType())) {
788 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
789 JSMSG_WASM_MEM_IMP_LIMIT);
790 return false;
791 }
792
793 RootedArrayBufferObjectMaybeShared buffer(cx);
794 if (!CreateWasmBuffer(cx, desc, &buffer)) {
795 return false;
796 }
797
798 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory));
799 memory.set(WasmMemoryObject::create(
800 cx, buffer, IsHugeMemoryEnabled(desc.indexType()), proto));
801 if (!memory) {
802 return false;
803 }
804 }
805
806 MOZ_RELEASE_ASSERT(memory->isHuge() == metadata().omitsBoundsChecks);
807
808 return true;
809 }
810
811 #ifdef ENABLE_WASM_EXCEPTIONS
instantiateTags(JSContext * cx,WasmTagObjectVector & tagObjs) const812 bool Module::instantiateTags(JSContext* cx,
813 WasmTagObjectVector& tagObjs) const {
814 size_t tagLength = metadata().tags.length();
815 if (tagLength == 0) {
816 return true;
817 }
818 size_t importedTagsLength = tagObjs.length();
819 if (tagObjs.length() <= tagLength && !tagObjs.resize(tagLength)) {
820 ReportOutOfMemory(cx);
821 return false;
822 }
823
824 uint32_t tagIndex = 0;
825 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTag));
826 for (const TagDesc& desc : metadata().tags) {
827 if (tagIndex >= importedTagsLength) {
828 RootedWasmTagObject tagObj(cx,
829 WasmTagObject::create(cx, desc.type, proto));
830 if (!tagObj) {
831 return false;
832 }
833 tagObjs[tagIndex] = tagObj;
834 }
835 tagIndex++;
836 }
837 return true;
838 }
839 #endif
840
instantiateImportedTable(JSContext * cx,const TableDesc & td,Handle<WasmTableObject * > tableObj,WasmTableObjectVector * tableObjs,SharedTableVector * tables) const841 bool Module::instantiateImportedTable(JSContext* cx, const TableDesc& td,
842 Handle<WasmTableObject*> tableObj,
843 WasmTableObjectVector* tableObjs,
844 SharedTableVector* tables) const {
845 MOZ_ASSERT(tableObj);
846 MOZ_ASSERT(!metadata().isAsmJS());
847
848 Table& table = tableObj->table();
849 if (!CheckLimits(cx, td.initialLength, td.maximumLength,
850 /* declaredMin */ MaxTableLimitField,
851 /* actualLength */ table.length(), table.maximum(),
852 metadata().isAsmJS(), "Table")) {
853 return false;
854 }
855
856 if (!tables->append(&table)) {
857 ReportOutOfMemory(cx);
858 return false;
859 }
860
861 if (!tableObjs->append(tableObj)) {
862 ReportOutOfMemory(cx);
863 return false;
864 }
865
866 return true;
867 }
868
instantiateLocalTable(JSContext * cx,const TableDesc & td,WasmTableObjectVector * tableObjs,SharedTableVector * tables) const869 bool Module::instantiateLocalTable(JSContext* cx, const TableDesc& td,
870 WasmTableObjectVector* tableObjs,
871 SharedTableVector* tables) const {
872 if (td.initialLength > MaxTableLength) {
873 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
874 JSMSG_WASM_TABLE_IMP_LIMIT);
875 return false;
876 }
877
878 SharedTable table;
879 Rooted<WasmTableObject*> tableObj(cx);
880 if (td.isImportedOrExported) {
881 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable));
882 tableObj.set(WasmTableObject::create(cx, td.initialLength, td.maximumLength,
883 td.elemType, proto));
884 if (!tableObj) {
885 return false;
886 }
887 table = &tableObj->table();
888 } else {
889 table = Table::create(cx, td, /* HandleWasmTableObject = */ nullptr);
890 if (!table) {
891 return false;
892 }
893 }
894
895 // Note, appending a null pointer for non-exported local tables.
896 if (!tableObjs->append(tableObj.get())) {
897 ReportOutOfMemory(cx);
898 return false;
899 }
900
901 if (!tables->emplaceBack(table)) {
902 ReportOutOfMemory(cx);
903 return false;
904 }
905
906 return true;
907 }
908
instantiateTables(JSContext * cx,const WasmTableObjectVector & tableImports,MutableHandle<WasmTableObjectVector> tableObjs,SharedTableVector * tables) const909 bool Module::instantiateTables(JSContext* cx,
910 const WasmTableObjectVector& tableImports,
911 MutableHandle<WasmTableObjectVector> tableObjs,
912 SharedTableVector* tables) const {
913 uint32_t tableIndex = 0;
914 for (const TableDesc& td : metadata().tables) {
915 if (tableIndex < tableImports.length()) {
916 Rooted<WasmTableObject*> tableObj(cx, tableImports[tableIndex]);
917 if (!instantiateImportedTable(cx, td, tableObj, &tableObjs.get(),
918 tables)) {
919 return false;
920 }
921 } else {
922 if (!instantiateLocalTable(cx, td, &tableObjs.get(), tables)) {
923 return false;
924 }
925 }
926 tableIndex++;
927 }
928 return true;
929 }
930
EnsureExportedGlobalObject(JSContext * cx,const ValVector & globalImportValues,size_t globalIndex,const GlobalDesc & global,WasmGlobalObjectVector & globalObjs)931 static bool EnsureExportedGlobalObject(JSContext* cx,
932 const ValVector& globalImportValues,
933 size_t globalIndex,
934 const GlobalDesc& global,
935 WasmGlobalObjectVector& globalObjs) {
936 if (globalIndex < globalObjs.length() && globalObjs[globalIndex]) {
937 return true;
938 }
939
940 RootedVal val(cx);
941 if (global.kind() == GlobalKind::Import) {
942 // If this is an import, then this must be a constant global that was
943 // provided without a global object. We must initialize it with the
944 // provided value while we still can differentiate this case.
945 MOZ_ASSERT(!global.isMutable());
946 val.set(Val(globalImportValues[globalIndex]));
947 } else {
948 // If this is not an import, then the initial value will be set by
949 // Instance::init() for indirect globals or else by CreateExportObject().
950 // In either case, we initialize with a default value here.
951 val.set(Val(global.type()));
952 }
953
954 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmGlobal));
955 RootedWasmGlobalObject go(
956 cx, WasmGlobalObject::create(cx, val, global.isMutable(), proto));
957 if (!go) {
958 return false;
959 }
960
961 if (globalObjs.length() <= globalIndex &&
962 !globalObjs.resize(globalIndex + 1)) {
963 ReportOutOfMemory(cx);
964 return false;
965 }
966
967 globalObjs[globalIndex] = go;
968 return true;
969 }
970
instantiateGlobals(JSContext * cx,const ValVector & globalImportValues,WasmGlobalObjectVector & globalObjs) const971 bool Module::instantiateGlobals(JSContext* cx,
972 const ValVector& globalImportValues,
973 WasmGlobalObjectVector& globalObjs) const {
974 // If there are exported globals that aren't in globalObjs because they
975 // originate in this module or because they were immutable imports that came
976 // in as primitive values then we must create cells in the globalObjs for
977 // them here, as WasmInstanceObject::create() and CreateExportObject() will
978 // need the cells to exist.
979
980 const GlobalDescVector& globals = metadata().globals;
981
982 for (const Export& exp : exports_) {
983 if (exp.kind() != DefinitionKind::Global) {
984 continue;
985 }
986 unsigned globalIndex = exp.globalIndex();
987 const GlobalDesc& global = globals[globalIndex];
988 if (!EnsureExportedGlobalObject(cx, globalImportValues, globalIndex, global,
989 globalObjs)) {
990 return false;
991 }
992 }
993
994 // Imported globals that are not re-exported may also have received only a
995 // primitive value; these globals are always immutable. Assert that we do
996 // not need to create any additional Global objects for such imports.
997
998 #ifdef DEBUG
999 size_t numGlobalImports = 0;
1000 for (const Import& import : imports_) {
1001 if (import.kind != DefinitionKind::Global) {
1002 continue;
1003 }
1004 size_t globalIndex = numGlobalImports++;
1005 const GlobalDesc& global = globals[globalIndex];
1006 MOZ_ASSERT(global.importIndex() == globalIndex);
1007 MOZ_ASSERT_IF(global.isIndirect(),
1008 globalIndex < globalObjs.length() || globalObjs[globalIndex]);
1009 }
1010 MOZ_ASSERT_IF(!metadata().isAsmJS(),
1011 numGlobalImports == globals.length() ||
1012 !globals[numGlobalImports].isImport());
1013 #endif
1014 return true;
1015 }
1016
getDebugEnabledCode() const1017 SharedCode Module::getDebugEnabledCode() const {
1018 MOZ_ASSERT(metadata().debugEnabled);
1019 MOZ_ASSERT(debugUnlinkedCode_);
1020 MOZ_ASSERT(debugLinkData_);
1021
1022 // The first time through, use the pre-linked code in the module but
1023 // mark it as having been claimed. Subsequently, instantiate the copy of the
1024 // code bytes that we keep around for debugging instead, because the
1025 // debugger may patch the pre-linked code at any time.
1026 if (debugCodeClaimed_.compareExchange(false, true)) {
1027 return code_;
1028 }
1029
1030 Tier tier = Tier::Baseline;
1031 auto segment =
1032 ModuleSegment::create(tier, *debugUnlinkedCode_, *debugLinkData_);
1033 if (!segment) {
1034 return nullptr;
1035 }
1036
1037 UniqueMetadataTier metadataTier = js::MakeUnique<MetadataTier>(tier);
1038 if (!metadataTier || !metadataTier->clone(metadata(tier))) {
1039 return nullptr;
1040 }
1041
1042 auto codeTier =
1043 js::MakeUnique<CodeTier>(std::move(metadataTier), std::move(segment));
1044 if (!codeTier) {
1045 return nullptr;
1046 }
1047
1048 JumpTables jumpTables;
1049 if (!jumpTables.init(CompileMode::Once, codeTier->segment(),
1050 metadata(tier).codeRanges)) {
1051 return nullptr;
1052 }
1053
1054 MutableCode debugCode =
1055 js_new<Code>(std::move(codeTier), metadata(), std::move(jumpTables));
1056 if (!debugCode || !debugCode->initialize(*debugLinkData_)) {
1057 return nullptr;
1058 }
1059
1060 return debugCode;
1061 }
1062
GetFunctionExport(JSContext * cx,HandleWasmInstanceObject instanceObj,const JSFunctionVector & funcImports,uint32_t funcIndex,MutableHandleFunction func)1063 static bool GetFunctionExport(JSContext* cx,
1064 HandleWasmInstanceObject instanceObj,
1065 const JSFunctionVector& funcImports,
1066 uint32_t funcIndex, MutableHandleFunction func) {
1067 if (funcIndex < funcImports.length() &&
1068 IsWasmExportedFunction(funcImports[funcIndex])) {
1069 func.set(funcImports[funcIndex]);
1070 return true;
1071 }
1072
1073 return instanceObj->getExportedFunction(cx, instanceObj, funcIndex, func);
1074 }
1075
GetGlobalExport(JSContext * cx,HandleWasmInstanceObject instanceObj,const JSFunctionVector & funcImports,const GlobalDesc & global,uint32_t globalIndex,const ValVector & globalImportValues,const WasmGlobalObjectVector & globalObjs,MutableHandleValue val)1076 static bool GetGlobalExport(JSContext* cx, HandleWasmInstanceObject instanceObj,
1077 const JSFunctionVector& funcImports,
1078 const GlobalDesc& global, uint32_t globalIndex,
1079 const ValVector& globalImportValues,
1080 const WasmGlobalObjectVector& globalObjs,
1081 MutableHandleValue val) {
1082 // A global object for this index is guaranteed to exist by
1083 // instantiateGlobals.
1084 RootedWasmGlobalObject globalObj(cx, globalObjs[globalIndex]);
1085 val.setObject(*globalObj);
1086
1087 // We are responsible to set the initial value of the global object here if
1088 // it's not imported or indirect. Imported global objects have their initial
1089 // value set by their defining module, or are set by
1090 // EnsureExportedGlobalObject when a constant value is provided as an import.
1091 // Indirect exported globals that are not imported, are initialized in
1092 // Instance::init.
1093 if (global.isIndirect() || global.isImport()) {
1094 return true;
1095 }
1096
1097 // This must be an exported immutable global defined in this module. The
1098 // instance either has compiled the value into the code or has its own copy
1099 // in its global data area. Either way, we must initialize the global object
1100 // with the same initial value.
1101 MOZ_ASSERT(!global.isMutable());
1102 MOZ_ASSERT(!global.isImport());
1103 RootedVal globalVal(cx);
1104 MOZ_RELEASE_ASSERT(!global.isImport());
1105 const InitExpr& init = global.initExpr();
1106 if (!init.evaluate(cx, globalImportValues, instanceObj, &globalVal)) {
1107 return false;
1108 }
1109 globalObj->val() = globalVal;
1110 return true;
1111 }
1112
CreateExportObject(JSContext * cx,HandleWasmInstanceObject instanceObj,const JSFunctionVector & funcImports,const WasmTableObjectVector & tableObjs,HandleWasmMemoryObject memoryObj,const WasmTagObjectVector & tagObjs,const ValVector & globalImportValues,const WasmGlobalObjectVector & globalObjs,const ExportVector & exports)1113 static bool CreateExportObject(
1114 JSContext* cx, HandleWasmInstanceObject instanceObj,
1115 const JSFunctionVector& funcImports, const WasmTableObjectVector& tableObjs,
1116 HandleWasmMemoryObject memoryObj, const WasmTagObjectVector& tagObjs,
1117 const ValVector& globalImportValues,
1118 const WasmGlobalObjectVector& globalObjs, const ExportVector& exports) {
1119 const Instance& instance = instanceObj->instance();
1120 const Metadata& metadata = instance.metadata();
1121 const GlobalDescVector& globals = metadata.globals;
1122
1123 if (metadata.isAsmJS() && exports.length() == 1 &&
1124 strlen(exports[0].fieldName()) == 0) {
1125 RootedFunction func(cx);
1126 if (!GetFunctionExport(cx, instanceObj, funcImports, exports[0].funcIndex(),
1127 &func)) {
1128 return false;
1129 }
1130 instanceObj->initExportsObj(*func.get());
1131 return true;
1132 }
1133
1134 RootedObject exportObj(cx);
1135 uint8_t propertyAttr = JSPROP_ENUMERATE;
1136
1137 if (metadata.isAsmJS()) {
1138 exportObj = NewPlainObject(cx);
1139 } else {
1140 exportObj = NewPlainObjectWithProto(cx, nullptr);
1141 propertyAttr |= JSPROP_READONLY | JSPROP_PERMANENT;
1142 }
1143 if (!exportObj) {
1144 return false;
1145 }
1146
1147 for (const Export& exp : exports) {
1148 JSAtom* atom =
1149 AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
1150 if (!atom) {
1151 return false;
1152 }
1153
1154 RootedId id(cx, AtomToId(atom));
1155 RootedValue val(cx);
1156 switch (exp.kind()) {
1157 case DefinitionKind::Function: {
1158 RootedFunction func(cx);
1159 if (!GetFunctionExport(cx, instanceObj, funcImports, exp.funcIndex(),
1160 &func)) {
1161 return false;
1162 }
1163 val = ObjectValue(*func);
1164 break;
1165 }
1166 case DefinitionKind::Table: {
1167 val = ObjectValue(*tableObjs[exp.tableIndex()]);
1168 break;
1169 }
1170 case DefinitionKind::Memory: {
1171 val = ObjectValue(*memoryObj);
1172 break;
1173 }
1174 case DefinitionKind::Global: {
1175 const GlobalDesc& global = globals[exp.globalIndex()];
1176 if (!GetGlobalExport(cx, instanceObj, funcImports, global,
1177 exp.globalIndex(), globalImportValues, globalObjs,
1178 &val)) {
1179 return false;
1180 }
1181 break;
1182 }
1183 #ifdef ENABLE_WASM_EXCEPTIONS
1184 case DefinitionKind::Tag: {
1185 val = ObjectValue(*tagObjs[exp.tagIndex()]);
1186 break;
1187 }
1188 #endif
1189 }
1190
1191 if (!JS_DefinePropertyById(cx, exportObj, id, val, propertyAttr)) {
1192 return false;
1193 }
1194 }
1195
1196 if (!metadata.isAsmJS()) {
1197 if (!PreventExtensions(cx, exportObj)) {
1198 return false;
1199 }
1200 }
1201
1202 instanceObj->initExportsObj(*exportObj);
1203 return true;
1204 }
1205
instantiate(JSContext * cx,ImportValues & imports,HandleObject instanceProto,MutableHandleWasmInstanceObject instance) const1206 bool Module::instantiate(JSContext* cx, ImportValues& imports,
1207 HandleObject instanceProto,
1208 MutableHandleWasmInstanceObject instance) const {
1209 MOZ_RELEASE_ASSERT(cx->wasm().haveSignalHandlers);
1210
1211 if (!instantiateFunctions(cx, imports.funcs)) {
1212 return false;
1213 }
1214
1215 RootedWasmMemoryObject memory(cx, imports.memory);
1216 if (!instantiateMemory(cx, &memory)) {
1217 return false;
1218 }
1219
1220 // Note that the following will extend imports.exceptionObjs with wrappers for
1221 // the local (non-imported) exceptions of the module.
1222 // The resulting vector is sparse, i.e., it will be null in slots that contain
1223 // exceptions that are neither exported or imported.
1224 // On the contrary, all the slots of exceptionTags will be filled with
1225 // unique tags.
1226
1227 #ifdef ENABLE_WASM_EXCEPTIONS
1228 if (!instantiateTags(cx, imports.tagObjs)) {
1229 return false;
1230 }
1231 #endif
1232
1233 // Note that tableObjs is sparse: it will be null in slots that contain
1234 // tables that are neither exported nor imported.
1235
1236 Rooted<WasmTableObjectVector> tableObjs(cx);
1237 SharedTableVector tables;
1238 if (!instantiateTables(cx, imports.tables, &tableObjs, &tables)) {
1239 return false;
1240 }
1241
1242 if (!instantiateGlobals(cx, imports.globalValues, imports.globalObjs)) {
1243 return false;
1244 }
1245
1246 SharedCode code;
1247 UniqueDebugState maybeDebug;
1248 if (metadata().debugEnabled) {
1249 code = getDebugEnabledCode();
1250 if (!code) {
1251 ReportOutOfMemory(cx);
1252 return false;
1253 }
1254
1255 maybeDebug = cx->make_unique<DebugState>(*code, *this);
1256 if (!maybeDebug) {
1257 ReportOutOfMemory(cx);
1258 return false;
1259 }
1260 } else {
1261 code = code_;
1262 }
1263
1264 instance.set(WasmInstanceObject::create(
1265 cx, code, dataSegments_, elemSegments_, metadata().globalDataLength,
1266 memory, std::move(tables), imports.funcs, metadata().globals,
1267 imports.globalValues, imports.globalObjs, imports.tagObjs, instanceProto,
1268 std::move(maybeDebug)));
1269 if (!instance) {
1270 return false;
1271 }
1272
1273 if (!CreateExportObject(cx, instance, imports.funcs, tableObjs.get(), memory,
1274 imports.tagObjs, imports.globalValues,
1275 imports.globalObjs, exports_)) {
1276 return false;
1277 }
1278
1279 // Register the instance with the Realm so that it can find out about global
1280 // events like profiling being enabled in the realm. Registration does not
1281 // require a fully-initialized instance and must precede initSegments as the
1282 // final pre-requisite for a live instance.
1283
1284 if (!cx->realm()->wasm.registerInstance(cx, instance)) {
1285 ReportOutOfMemory(cx);
1286 return false;
1287 }
1288
1289 // Perform initialization as the final step after the instance is fully
1290 // constructed since this can make the instance live to content (even if the
1291 // start function fails).
1292
1293 if (!initSegments(cx, instance, memory, imports.globalValues)) {
1294 return false;
1295 }
1296
1297 // Now that the instance is fully live and initialized, the start function.
1298 // Note that failure may cause instantiation to throw, but the instance may
1299 // still be live via edges created by initSegments or the start function.
1300
1301 if (metadata().startFuncIndex) {
1302 FixedInvokeArgs<0> args(cx);
1303 if (!instance->instance().callExport(cx, *metadata().startFuncIndex,
1304 args)) {
1305 return false;
1306 }
1307 }
1308
1309 JSUseCounter useCounter =
1310 metadata().isAsmJS() ? JSUseCounter::ASMJS : JSUseCounter::WASM;
1311 cx->runtime()->setUseCounter(instance, useCounter);
1312
1313 if (cx->options().testWasmAwaitTier2()) {
1314 testingBlockOnTier2Complete();
1315 }
1316
1317 return true;
1318 }
1319