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/CheckedInt.h"
22 #include "mozilla/EndianUtils.h"
23 #include "mozilla/Maybe.h"
24 #include "mozilla/RangedPtr.h"
25 
26 #include <algorithm>
27 
28 #include "ds/IdValuePair.h"  // js::IdValuePair
29 #include "gc/FreeOp.h"
30 #include "jit/AtomicOperations.h"
31 #include "jit/JitOptions.h"
32 #include "jit/JitRuntime.h"
33 #include "jit/Simulator.h"
34 #if defined(JS_CODEGEN_X64)  // Assembler::HasSSE41
35 #  include "jit/x64/Assembler-x64.h"
36 #  include "jit/x86-shared/Architecture-x86-shared.h"
37 #  include "jit/x86-shared/Assembler-x86-shared.h"
38 #endif
39 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
40 #include "js/Printf.h"
41 #include "js/PropertySpec.h"  // JS_{PS,FN}{,_END}
42 #include "util/StringBuffer.h"
43 #include "util/Text.h"
44 #include "vm/ErrorObject.h"
45 #include "vm/FunctionFlags.h"      // js::FunctionFlags
46 #include "vm/GlobalObject.h"       // js::GlobalObject
47 #include "vm/HelperThreadState.h"  // js::PromiseHelperTask
48 #include "vm/Interpreter.h"
49 #include "vm/PlainObject.h"    // js::PlainObject
50 #include "vm/PromiseObject.h"  // js::PromiseObject
51 #include "vm/StringType.h"
52 #include "vm/Warnings.h"       // js::WarnNumberASCII
53 #include "vm/WellKnownAtom.h"  // js_*_str
54 #include "wasm/WasmBaselineCompile.h"
55 #include "wasm/WasmBuiltins.h"
56 #include "wasm/WasmCompile.h"
57 #include "wasm/WasmCraneliftCompile.h"
58 #include "wasm/WasmInstance.h"
59 #include "wasm/WasmIonCompile.h"
60 #include "wasm/WasmModule.h"
61 #include "wasm/WasmProcess.h"
62 #include "wasm/WasmSignalHandlers.h"
63 #include "wasm/WasmStubs.h"
64 #include "wasm/WasmValidate.h"
65 
66 #include "vm/ArrayBufferObject-inl.h"
67 #include "vm/JSObject-inl.h"
68 #include "vm/NativeObject-inl.h"
69 
70 using namespace js;
71 using namespace js::jit;
72 using namespace js::wasm;
73 
74 using mozilla::CheckedInt;
75 using mozilla::Nothing;
76 using mozilla::RangedPtr;
77 using mozilla::Span;
78 
79 // About the fuzzer intercession points: If fuzzing has been selected and only a
80 // single compiler has been selected then we will disable features that are not
81 // supported by that single compiler.  This is strictly a concession to the
82 // fuzzer infrastructure.
83 
IsFuzzingIon(JSContext * cx)84 static inline bool IsFuzzingIon(JSContext* cx) {
85   return IsFuzzing() && !cx->options().wasmBaseline() &&
86          cx->options().wasmIon() && !cx->options().wasmCranelift();
87 }
88 
IsFuzzingCranelift(JSContext * cx)89 static inline bool IsFuzzingCranelift(JSContext* cx) {
90   return IsFuzzing() && !cx->options().wasmBaseline() &&
91          !cx->options().wasmIon() && cx->options().wasmCranelift();
92 }
93 
94 // These functions read flags and apply fuzzing intercession policies.  Never go
95 // directly to the flags in code below, always go via these accessors.
96 
WasmSimdWormholeFlag(JSContext * cx)97 static inline bool WasmSimdWormholeFlag(JSContext* cx) {
98 #ifdef ENABLE_WASM_SIMD_WORMHOLE
99   return cx->options().wasmSimdWormhole();
100 #else
101   return false;
102 #endif
103 }
104 
WasmThreadsFlag(JSContext * cx)105 static inline bool WasmThreadsFlag(JSContext* cx) {
106   return cx->realm() &&
107          cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
108 }
109 
110 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \
111                      ...)                                                      \
112   static inline bool Wasm##NAME##Flag(JSContext* cx) {                         \
113     return (COMPILE_PRED) && (FLAG_PRED) && cx->options().wasm##NAME();        \
114   }
115 JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE);
116 #undef WASM_FEATURE
117 
WasmDebuggerActive(JSContext * cx)118 static inline bool WasmDebuggerActive(JSContext* cx) {
119   if (IsFuzzingIon(cx) || IsFuzzingCranelift(cx)) {
120     return false;
121   }
122   return cx->realm() && cx->realm()->debuggerObservesAsmJS();
123 }
124 
125 /*
126  * [SMDOC] Compiler and feature selection; compiler and feature availability.
127  *
128  * In order to make the computation of whether a wasm feature or wasm compiler
129  * is available predictable, we have established some rules, and implemented
130  * those rules.
131  *
132  * Code elsewhere should use the predicates below to test for features and
133  * compilers, it should never try to compute feature and compiler availability
134  * in other ways.
135  *
136  * At the outset, there is a set of selected compilers C containing at most one
137  * baseline compiler [*] and at most one optimizing compiler [**], and a set of
138  * selected features F.  These selections come from defaults and from overrides
139  * by command line switches in the shell and javascript.option.wasm_X in the
140  * browser.  Defaults for both features and compilers may be platform specific,
141  * for example, some compilers may not be available on some platforms because
142  * they do not support the architecture at all or they do not support features
143  * that must be enabled by default on the platform.
144  *
145  * [*] Currently we have only one, "baseline" aka "Rabaldr", but other
146  *     implementations have additional baseline translators, eg from wasm
147  *     bytecode to an internal code processed by an interpreter.
148  *
149  * [**] Currently we have two, "ion" aka "Baldr", and "Cranelift".
150  *
151  *
152  * Compiler availability:
153  *
154  * The set of features F induces a set of available compilers A: these are the
155  * compilers that all support all the features in F.  (Some of these compilers
156  * may not be in the set C.)
157  *
158  * The sets C and A are intersected, yielding a set of enabled compilers E.
159  * Notably, the set E may be empty, in which case wasm is effectively disabled
160  * (though the WebAssembly object is still present in the global environment).
161  *
162  * An important consequence is that selecting a feature that is not supported by
163  * a particular compiler disables that compiler completely -- there is no notion
164  * of a compiler being available but suddenly failing when an unsupported
165  * feature is used by a program.  If a compiler is available, it supports all
166  * the features that have been selected.
167  *
168  * Equally important, a feature cannot be enabled by default on a platform if
169  * the feature is not supported by all the compilers we wish to have enabled by
170  * default on the platform.  We MUST by-default disable features on a platform
171  * that are not supported by all the compilers on the platform.
172  *
173  * As an example:
174  *
175  *   On ARM64 the default compilers are Baseline and Cranelift.  Say Cranelift
176  *   does not support feature X.  Thus X cannot be enabled by default on ARM64.
177  *   However, X support can be compiled-in to SpiderMonkey, and the user can opt
178  *   to enable X.  Doing so will disable Cranelift.
179  *
180  *   In contrast, X can be enabled by default on x64, where the default
181  *   compilers are Baseline and Ion, both of which support X.
182  *
183  *   A subtlety is worth noting: on x64, enabling Cranelift (thus disabling Ion)
184  *   will not disable X.  Instead, the presence of X in the selected feature set
185  *   will disable Cranelift, leaving only Baseline.  This follows from the logic
186  *   described above.
187  *
188  * In a shell build, the testing functions wasmCompilersPresent,
189  * wasmCompileMode, wasmCraneliftDisabledByFeatures, and
190  * wasmIonDisabledByFeatures can be used to probe compiler availability and the
191  * reasons for a compiler being unavailable.
192  *
193  *
194  * Feature availability:
195  *
196  * A feature is available if it is selected and there is at least one available
197  * compiler that implements it.
198  *
199  * For example, --wasm-gc selects the GC feature, and if Baseline is available
200  * then the feature is available.
201  *
202  * In a shell build, there are per-feature testing functions (of the form
203  * wasmFeatureEnabled) to probe whether specific features are available.
204  */
205 
206 // Compiler availability predicates.  These must be kept in sync with the
207 // feature predicates in the next section below.
208 //
209 // These can't call the feature predicates since the feature predicates call
210 // back to these predicates.  So there will be a small amount of duplicated
211 // logic here, but as compilers reach feature parity that duplication will go
212 // away.
213 //
214 // There's a static precedence order between the optimizing compilers.  This
215 // order currently ranks Cranelift over Ion on all platforms because Cranelift
216 // is disabled by default on all platforms: anyone who has enabled Cranelift
217 // will wish to use it instead of Ion.
218 //
219 // The precedence order is implemented by guards in IonAvailable() and
220 // CraneliftAvailable().  We expect that it will become more complex as the
221 // default settings change.  But it should remain static.
222 
BaselineAvailable(JSContext * cx)223 bool wasm::BaselineAvailable(JSContext* cx) {
224   // Baseline supports every feature supported by any compiler.
225   return cx->options().wasmBaseline() && BaselinePlatformSupport();
226 }
227 
IonAvailable(JSContext * cx)228 bool wasm::IonAvailable(JSContext* cx) {
229   if (!cx->options().wasmIon() || !IonPlatformSupport()) {
230     return false;
231   }
232   bool isDisabled = false;
233   MOZ_ALWAYS_TRUE(IonDisabledByFeatures(cx, &isDisabled));
234   return !isDisabled && !CraneliftAvailable(cx);
235 }
236 
WasmCompilerForAsmJSAvailable(JSContext * cx)237 bool wasm::WasmCompilerForAsmJSAvailable(JSContext* cx) {
238   // For now, restrict this to Ion - we have not tested Cranelift properly.
239   return IonAvailable(cx);
240 }
241 
242 template <size_t ArrayLength>
Append(JSStringBuilder * reason,const char (& s)[ArrayLength],char * sep)243 static inline bool Append(JSStringBuilder* reason, const char (&s)[ArrayLength],
244                           char* sep) {
245   if ((*sep && !reason->append(*sep)) || !reason->append(s)) {
246     return false;
247   }
248   *sep = ',';
249   return true;
250 }
251 
IonDisabledByFeatures(JSContext * cx,bool * isDisabled,JSStringBuilder * reason)252 bool wasm::IonDisabledByFeatures(JSContext* cx, bool* isDisabled,
253                                  JSStringBuilder* reason) {
254   // Ion has no debugging support, no gc support.
255   bool debug = WasmDebuggerActive(cx);
256   bool functionReferences = WasmFunctionReferencesFlag(cx);
257   bool gc = WasmGcFlag(cx);
258   bool exn = WasmExceptionsFlag(cx);
259   if (reason) {
260     char sep = 0;
261     if (debug && !Append(reason, "debug", &sep)) {
262       return false;
263     }
264     if (functionReferences && !Append(reason, "function-references", &sep)) {
265       return false;
266     }
267     if (gc && !Append(reason, "gc", &sep)) {
268       return false;
269     }
270     if (exn && !Append(reason, "exceptions", &sep)) {
271       return false;
272     }
273   }
274   *isDisabled = debug || functionReferences || gc || exn;
275   return true;
276 }
277 
CraneliftAvailable(JSContext * cx)278 bool wasm::CraneliftAvailable(JSContext* cx) {
279   if (!cx->options().wasmCranelift() || !CraneliftPlatformSupport()) {
280     return false;
281   }
282   bool isDisabled = false;
283   MOZ_ALWAYS_TRUE(CraneliftDisabledByFeatures(cx, &isDisabled));
284   return !isDisabled;
285 }
286 
CraneliftDisabledByFeatures(JSContext * cx,bool * isDisabled,JSStringBuilder * reason)287 bool wasm::CraneliftDisabledByFeatures(JSContext* cx, bool* isDisabled,
288                                        JSStringBuilder* reason) {
289   // Cranelift has no debugging support, no gc support, no simd, and
290   // no exceptions support.
291   bool debug = WasmDebuggerActive(cx);
292   bool functionReferences = WasmFunctionReferencesFlag(cx);
293   bool gc = WasmGcFlag(cx);
294 #ifdef JS_CODEGEN_ARM64
295   // Cranelift aarch64 has full SIMD support.
296   bool simdOnNonAarch64 = false;
297 #else
298   bool simdOnNonAarch64 = WasmSimdFlag(cx);
299 #endif
300   bool exn = WasmExceptionsFlag(cx);
301   if (reason) {
302     char sep = 0;
303     if (debug && !Append(reason, "debug", &sep)) {
304       return false;
305     }
306     if (functionReferences && !Append(reason, "function-references", &sep)) {
307       return false;
308     }
309     if (gc && !Append(reason, "gc", &sep)) {
310       return false;
311     }
312     if (simdOnNonAarch64 && !Append(reason, "simd", &sep)) {
313       return false;
314     }
315     if (exn && !Append(reason, "exceptions", &sep)) {
316       return false;
317     }
318   }
319   *isDisabled = debug || functionReferences || gc || simdOnNonAarch64 || exn;
320   return true;
321 }
322 
AnyCompilerAvailable(JSContext * cx)323 bool wasm::AnyCompilerAvailable(JSContext* cx) {
324   return wasm::BaselineAvailable(cx) || wasm::IonAvailable(cx) ||
325          wasm::CraneliftAvailable(cx);
326 }
327 
328 // Feature predicates.  These must be kept in sync with the predicates in the
329 // section above.
330 //
331 // The meaning of these predicates is tricky: A predicate is true for a feature
332 // if the feature is enabled and/or compiled-in *and* we have *at least one*
333 // compiler that can support the feature.  Subsequent compiler selection must
334 // ensure that only compilers that actually support the feature are used.
335 
336 #define WASM_FEATURE(NAME, LOWER_NAME, COMPILE_PRED, COMPILER_PRED, FLAG_PRED, \
337                      ...)                                                      \
338   bool wasm::NAME##Available(JSContext* cx) {                                  \
339     return Wasm##NAME##Flag(cx) && (COMPILER_PRED);                            \
340   }
JS_FOR_WASM_FEATURES(WASM_FEATURE,WASM_FEATURE)341 JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
342 #undef WASM_FEATURE
343 
344 #ifdef ENABLE_WASM_SIMD_WORMHOLE
345 static bool IsSimdPrivilegedContext(JSContext* cx) {
346   // This may be slightly more lenient than we want in an ideal world, but it
347   // remains safe.
348   return cx->realm() && cx->realm()->principals() &&
349          cx->realm()->principals()->isSystemOrAddonPrincipal();
350 }
351 #endif
352 
SimdWormholeAvailable(JSContext * cx)353 bool wasm::SimdWormholeAvailable(JSContext* cx) {
354 #ifdef ENABLE_WASM_SIMD_WORMHOLE
355   // The #ifdef ensures that we only enable the wormhole on hardware that
356   // supports it and if SIMD support is compiled in.
357   //
358   // Next we must check that the CPU supports SIMD; it might not, even if SIMD
359   // is available.  Do this directly, not via WasmSimdFlag().
360   //
361   // Do not go via WasmSimdFlag() because we do not want to gate on
362   // j.o.wasm_simd.  If the wormhole is available, requesting it will
363   // force-enable SIMD.
364   return js::jit::JitSupportsWasmSimd() &&
365          (WasmSimdWormholeFlag(cx) || IsSimdPrivilegedContext(cx)) &&
366          (IonAvailable(cx) || BaselineAvailable(cx)) && !CraneliftAvailable(cx);
367 #else
368   return false;
369 #endif
370 }
371 
ThreadsAvailable(JSContext * cx)372 bool wasm::ThreadsAvailable(JSContext* cx) {
373   return WasmThreadsFlag(cx) && AnyCompilerAvailable(cx);
374 }
375 
HasPlatformSupport(JSContext * cx)376 bool wasm::HasPlatformSupport(JSContext* cx) {
377 #if !MOZ_LITTLE_ENDIAN() || defined(JS_CODEGEN_NONE) || defined(__wasi__)
378   return false;
379 #endif
380 
381   if (gc::SystemPageSize() > wasm::PageSize) {
382     return false;
383   }
384 
385   if (!JitOptions.supportsFloatingPoint) {
386     return false;
387   }
388 
389   if (!JitOptions.supportsUnalignedAccesses) {
390     return false;
391   }
392 
393   if (!wasm::EnsureFullSignalHandlers(cx)) {
394     return false;
395   }
396 
397   if (!jit::JitSupportsAtomics()) {
398     return false;
399   }
400 
401   // Wasm threads require 8-byte lock-free atomics.
402   if (!jit::AtomicOperations::isLockfree8()) {
403     return false;
404   }
405 
406   // Lazily initialize the global type context
407   if (!cx->wasm().ensureTypeContext(cx)) {
408     return false;
409   }
410 
411   // Test only whether the compilers are supported on the hardware, not whether
412   // they are enabled.
413   return BaselinePlatformSupport() || IonPlatformSupport() ||
414          CraneliftPlatformSupport();
415 }
416 
HasSupport(JSContext * cx)417 bool wasm::HasSupport(JSContext* cx) {
418   // If the general wasm pref is on, it's on for everything.
419   bool prefEnabled = cx->options().wasm();
420   // If the general pref is off, check trusted principals.
421   if (MOZ_UNLIKELY(!prefEnabled)) {
422     prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() &&
423                   cx->realm()->principals() &&
424                   cx->realm()->principals()->isSystemOrAddonPrincipal();
425   }
426   // Do not check for compiler availability, as that may be run-time variant.
427   // For HasSupport() we want a stable answer depending only on prefs.
428   return prefEnabled && HasPlatformSupport(cx);
429 }
430 
StreamingCompilationAvailable(JSContext * cx)431 bool wasm::StreamingCompilationAvailable(JSContext* cx) {
432   // This should match EnsureStreamSupport().
433   return HasSupport(cx) && AnyCompilerAvailable(cx) &&
434          cx->runtime()->offThreadPromiseState.ref().initialized() &&
435          CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
436          cx->runtime()->reportStreamErrorCallback;
437 }
438 
CodeCachingAvailable(JSContext * cx)439 bool wasm::CodeCachingAvailable(JSContext* cx) {
440   // Fuzzilli breaks the out-of-process compilation mechanism,
441   // so we disable it permanently in those builds.
442 #ifdef FUZZING_JS_FUZZILLI
443   return false;
444 #else
445 
446   // At the moment, we require Ion support for code caching.  The main reason
447   // for this is that wasm::CompileAndSerialize() does not have access to
448   // information about which optimizing compiler it should use.  See comments in
449   // CompileAndSerialize(), below.
450   return StreamingCompilationAvailable(cx) && IonAvailable(cx);
451 #endif
452 }
453 
454 // ============================================================================
455 // Imports
456 
ThrowBadImportArg(JSContext * cx)457 static bool ThrowBadImportArg(JSContext* cx) {
458   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
459                            JSMSG_WASM_BAD_IMPORT_ARG);
460   return false;
461 }
462 
ThrowBadImportType(JSContext * cx,const char * field,const char * str)463 static bool ThrowBadImportType(JSContext* cx, const char* field,
464                                const char* str) {
465   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
466                            JSMSG_WASM_BAD_IMPORT_TYPE, field, str);
467   return false;
468 }
469 
GetProperty(JSContext * cx,HandleObject obj,const char * chars,MutableHandleValue v)470 static bool GetProperty(JSContext* cx, HandleObject obj, const char* chars,
471                         MutableHandleValue v) {
472   JSAtom* atom = AtomizeUTF8Chars(cx, chars, strlen(chars));
473   if (!atom) {
474     return false;
475   }
476 
477   RootedId id(cx, AtomToId(atom));
478   return GetProperty(cx, obj, obj, id, v);
479 }
480 
GetImports(JSContext * cx,const Module & module,HandleObject importObj,ImportValues * imports)481 bool js::wasm::GetImports(JSContext* cx, const Module& module,
482                           HandleObject importObj, ImportValues* imports) {
483   if (!module.imports().empty() && !importObj) {
484     return ThrowBadImportArg(cx);
485   }
486 
487   const Metadata& metadata = module.metadata();
488 
489 #ifdef ENABLE_WASM_EXCEPTIONS
490   uint32_t eventIndex = 0;
491   const EventDescVector& events = metadata.events;
492 #endif
493   uint32_t globalIndex = 0;
494   const GlobalDescVector& globals = metadata.globals;
495   uint32_t tableIndex = 0;
496   const TableDescVector& tables = metadata.tables;
497   for (const Import& import : module.imports()) {
498     RootedValue v(cx);
499     if (!GetProperty(cx, importObj, import.module.get(), &v)) {
500       return false;
501     }
502 
503     if (!v.isObject()) {
504       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
505                                JSMSG_WASM_BAD_IMPORT_FIELD,
506                                import.module.get());
507       return false;
508     }
509 
510     RootedObject obj(cx, &v.toObject());
511     if (!GetProperty(cx, obj, import.field.get(), &v)) {
512       return false;
513     }
514 
515     switch (import.kind) {
516       case DefinitionKind::Function: {
517         if (!IsFunctionObject(v)) {
518           return ThrowBadImportType(cx, import.field.get(), "Function");
519         }
520 
521         if (!imports->funcs.append(&v.toObject().as<JSFunction>())) {
522           return false;
523         }
524 
525         break;
526       }
527       case DefinitionKind::Table: {
528         const uint32_t index = tableIndex++;
529         if (!v.isObject() || !v.toObject().is<WasmTableObject>()) {
530           return ThrowBadImportType(cx, import.field.get(), "Table");
531         }
532 
533         RootedWasmTableObject obj(cx, &v.toObject().as<WasmTableObject>());
534         if (obj->table().elemType() != tables[index].elemType) {
535           JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
536                                    JSMSG_WASM_BAD_TBL_TYPE_LINK);
537           return false;
538         }
539 
540         if (!imports->tables.append(obj)) {
541           return false;
542         }
543         break;
544       }
545       case DefinitionKind::Memory: {
546         if (!v.isObject() || !v.toObject().is<WasmMemoryObject>()) {
547           return ThrowBadImportType(cx, import.field.get(), "Memory");
548         }
549 
550         MOZ_ASSERT(!imports->memory);
551         imports->memory = &v.toObject().as<WasmMemoryObject>();
552         break;
553       }
554 #ifdef ENABLE_WASM_EXCEPTIONS
555       case DefinitionKind::Event: {
556         const uint32_t index = eventIndex++;
557         if (!v.isObject() || !v.toObject().is<WasmExceptionObject>()) {
558           return ThrowBadImportType(cx, import.field.get(), "Exception");
559         }
560 
561         RootedWasmExceptionObject obj(cx,
562                                       &v.toObject().as<WasmExceptionObject>());
563 
564         // Checks whether the signature of the imported exception object matches
565         // the signature declared in the exception import's EventDesc.
566         if (obj->resultType() != events[index].resultType()) {
567           JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
568                                    JSMSG_WASM_BAD_EXN_SIG, import.module.get(),
569                                    import.field.get());
570           return false;
571         }
572 
573         if (!imports->exceptionObjs.append(obj)) {
574           ReportOutOfMemory(cx);
575           return false;
576         }
577         break;
578       }
579 #endif
580       case DefinitionKind::Global: {
581         const uint32_t index = globalIndex++;
582         const GlobalDesc& global = globals[index];
583         MOZ_ASSERT(global.importIndex() == index);
584 
585         RootedVal val(cx);
586         if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
587           RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
588 
589           if (obj->isMutable() != global.isMutable()) {
590             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
591                                      JSMSG_WASM_BAD_GLOB_MUT_LINK);
592             return false;
593           }
594           if (obj->type() != global.type()) {
595             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
596                                      JSMSG_WASM_BAD_GLOB_TYPE_LINK);
597             return false;
598           }
599 
600           if (imports->globalObjs.length() <= index &&
601               !imports->globalObjs.resize(index + 1)) {
602             ReportOutOfMemory(cx);
603             return false;
604           }
605           imports->globalObjs[index] = obj;
606           val = obj->val();
607         } else {
608           if (IsNumberType(global.type())) {
609             if (global.type() == ValType::I64 && !v.isBigInt()) {
610               return ThrowBadImportType(cx, import.field.get(), "BigInt");
611             }
612             if (global.type() != ValType::I64 && !v.isNumber()) {
613               return ThrowBadImportType(cx, import.field.get(), "Number");
614             }
615           } else {
616             MOZ_ASSERT(global.type().isReference());
617             if (!global.type().isExternRef() && !v.isObjectOrNull()) {
618               return ThrowBadImportType(cx, import.field.get(),
619                                         "Object-or-null value required for "
620                                         "non-externref reference type");
621             }
622           }
623 
624           if (global.isMutable()) {
625             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
626                                      JSMSG_WASM_BAD_GLOB_MUT_LINK);
627             return false;
628           }
629 
630           if (!Val::fromJSValue(cx, global.type(), v, &val)) {
631             return false;
632           }
633         }
634 
635         if (!imports->globalValues.append(val)) {
636           return false;
637         }
638 
639         break;
640       }
641     }
642   }
643 
644   MOZ_ASSERT(globalIndex == globals.length() ||
645              !globals[globalIndex].isImport());
646 
647   return true;
648 }
649 
DescribeScriptedCaller(JSContext * cx,ScriptedCaller * caller,const char * introducer)650 static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller,
651                                    const char* introducer) {
652   // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
653   // found, not whether an error was thrown. This wrapper function converts
654   // back to the more ordinary false-if-error form.
655 
656   JS::AutoFilename af;
657   if (JS::DescribeScriptedCaller(cx, &af, &caller->line)) {
658     caller->filename =
659         FormatIntroducedFilename(cx, af.get(), caller->line, introducer);
660     if (!caller->filename) {
661       return false;
662     }
663   }
664 
665   return true;
666 }
667 
668 // Parse the options bag that is optionally passed to functions that compile
669 // wasm.  This is for internal experimentation purposes.  See comments about the
670 // SIMD wormhole in WasmConstants.h.
671 
ParseCompileOptions(JSContext * cx,HandleValue maybeOptions,FeatureOptions * options)672 static bool ParseCompileOptions(JSContext* cx, HandleValue maybeOptions,
673                                 FeatureOptions* options) {
674   if (SimdWormholeAvailable(cx)) {
675     if (maybeOptions.isObject()) {
676       RootedValue wormholeVal(cx);
677       RootedObject obj(cx, &maybeOptions.toObject());
678       if (!JS_GetProperty(cx, obj, "simdWormhole", &wormholeVal)) {
679         return false;
680       }
681       if (wormholeVal.isBoolean()) {
682         options->simdWormhole = wormholeVal.toBoolean();
683       }
684     }
685   }
686   return true;
687 }
688 
InitCompileArgs(JSContext * cx,HandleValue maybeOptions,const char * introducer)689 static SharedCompileArgs InitCompileArgs(JSContext* cx,
690                                          HandleValue maybeOptions,
691                                          const char* introducer) {
692   ScriptedCaller scriptedCaller;
693   if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
694     return nullptr;
695   }
696 
697   FeatureOptions options;
698   if (!ParseCompileOptions(cx, maybeOptions, &options)) {
699     return nullptr;
700   }
701   return CompileArgs::build(cx, std::move(scriptedCaller), options);
702 }
703 
704 // ============================================================================
705 // Testing / Fuzzing support
706 
Eval(JSContext * cx,Handle<TypedArrayObject * > code,HandleObject importObj,HandleValue maybeOptions,MutableHandleWasmInstanceObject instanceObj)707 bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code,
708                 HandleObject importObj, HandleValue maybeOptions,
709                 MutableHandleWasmInstanceObject instanceObj) {
710   if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
711     return false;
712   }
713 
714   MutableBytes bytecode = cx->new_<ShareableBytes>();
715   if (!bytecode) {
716     return false;
717   }
718 
719   if (!bytecode->append((uint8_t*)code->dataPointerEither().unwrap(),
720                         code->byteLength())) {
721     ReportOutOfMemory(cx);
722     return false;
723   }
724 
725   SharedCompileArgs compileArgs =
726       InitCompileArgs(cx, maybeOptions, "wasm_eval");
727   if (!compileArgs) {
728     return false;
729   }
730 
731   UniqueChars error;
732   UniqueCharsVector warnings;
733   SharedModule module =
734       CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
735   if (!module) {
736     if (error) {
737       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
738                                JSMSG_WASM_COMPILE_ERROR, error.get());
739       return false;
740     }
741     ReportOutOfMemory(cx);
742     return false;
743   }
744 
745   Rooted<ImportValues> imports(cx);
746   if (!GetImports(cx, *module, importObj, imports.address())) {
747     return false;
748   }
749 
750   return module->instantiate(cx, imports.get(), nullptr, instanceObj);
751 }
752 
753 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
754   // MOZ_STACK_CLASS means these can be nops.
AddRefSerializeListener755   MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
ReleaseSerializeListener756   MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
757 
758   DebugOnly<bool> called = false;
759   Bytes* serialized;
SerializeListenerSerializeListener760   explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
761 
storeOptimizedEncodingSerializeListener762   void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes bytes) override {
763     MOZ_ASSERT(!called);
764     called = true;
765     if (serialized->resize(bytes->length())) {
766       memcpy(serialized->begin(), bytes->begin(), bytes->length());
767     }
768   }
769 };
770 
CompileAndSerialize(const ShareableBytes & bytecode,Bytes * serialized)771 bool wasm::CompileAndSerialize(const ShareableBytes& bytecode,
772                                Bytes* serialized) {
773   MutableCompileArgs compileArgs = js_new<CompileArgs>(ScriptedCaller());
774   if (!compileArgs) {
775     return false;
776   }
777 
778   // The caller has ensured CodeCachingAvailable(). Moreover, we want to ensure
779   // we go straight to tier-2 so that we synchronously call
780   // JS::OptimizedEncodingListener::storeOptimizedEncoding().
781   compileArgs->baselineEnabled = false;
782 
783   // We always pick Ion here, and we depend on CodeCachingAvailable() having
784   // determined that Ion is available, see comments at CodeCachingAvailable().
785   // To do better, we need to pass information about which compiler that should
786   // be used into CompileAndSerialize().
787   compileArgs->ionEnabled = true;
788 
789   // The caller must ensure that huge memory support is configured the same in
790   // the receiving process of this serialized module.
791   compileArgs->features.hugeMemory = wasm::IsHugeMemoryEnabled();
792 
793   SerializeListener listener(serialized);
794 
795   UniqueChars error;
796   UniqueCharsVector warnings;
797   SharedModule module =
798       CompileBuffer(*compileArgs, bytecode, &error, &warnings, &listener);
799   if (!module) {
800     fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
801     return false;
802   }
803 
804   MOZ_ASSERT(module->code().hasTier(Tier::Serialized));
805   MOZ_ASSERT(listener.called);
806   return !listener.serialized->empty();
807 }
808 
DeserializeModule(JSContext * cx,const Bytes & serialized,MutableHandleObject moduleObj)809 bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized,
810                              MutableHandleObject moduleObj) {
811   MutableModule module =
812       Module::deserialize(serialized.begin(), serialized.length());
813   if (!module) {
814     ReportOutOfMemory(cx);
815     return false;
816   }
817 
818   moduleObj.set(module->createObject(cx));
819   return !!moduleObj;
820 }
821 
822 // ============================================================================
823 // Common functions
824 
825 // '[EnforceRange] unsigned long' types are coerced with
826 //    ConvertToInt(v, 32, 'unsigned')
827 // defined in Web IDL Section 3.2.4.9.
EnforceRangeU32(JSContext * cx,HandleValue v,const char * kind,const char * noun,uint32_t * u32)828 static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
829                             const char* noun, uint32_t* u32) {
830   // Step 4.
831   double x;
832   if (!ToNumber(cx, v, &x)) {
833     return false;
834   }
835 
836   // Step 5.
837   if (mozilla::IsNegativeZero(x)) {
838     x = 0.0;
839   }
840 
841   // Step 6.1.
842   if (!mozilla::IsFinite(x)) {
843     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
844                              JSMSG_WASM_BAD_UINT32, kind, noun);
845     return false;
846   }
847 
848   // Step 6.2.
849   x = JS::ToInteger(x);
850 
851   // Step 6.3.
852   if (x < 0 || x > double(UINT32_MAX)) {
853     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
854                              JSMSG_WASM_BAD_UINT32, kind, noun);
855     return false;
856   }
857 
858   *u32 = uint32_t(x);
859   MOZ_ASSERT(double(*u32) == x);
860   return true;
861 }
862 
GetLimits(JSContext * cx,HandleObject obj,uint32_t maximumField,const char * kind,Limits * limits,Shareable allowShared)863 static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
864                       const char* kind, Limits* limits, Shareable allowShared) {
865   JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
866   if (!initialAtom) {
867     return false;
868   }
869   RootedId initialId(cx, AtomToId(initialAtom));
870 
871   RootedValue initialVal(cx);
872   if (!GetProperty(cx, obj, obj, initialId, &initialVal)) {
873     return false;
874   }
875 
876   uint32_t initial = 0;
877   if (!initialVal.isUndefined() &&
878       !EnforceRangeU32(cx, initialVal, kind, "initial size", &initial)) {
879     return false;
880   }
881   limits->initial = initial;
882 
883   if (limits->initial > maximumField) {
884     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
885                              kind, "initial size");
886     return false;
887   }
888 
889 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
890   // Get minimum parameter.
891   JSAtom* minimumAtom = Atomize(cx, "minimum", strlen("minimum"));
892   if (!minimumAtom) {
893     return false;
894   }
895   RootedId minimumId(cx, AtomToId(minimumAtom));
896 
897   RootedValue minimumVal(cx);
898   if (!GetProperty(cx, obj, obj, minimumId, &minimumVal)) {
899     return false;
900   }
901 
902   uint32_t minimum = 0;
903   if (!minimumVal.isUndefined() &&
904       !EnforceRangeU32(cx, minimumVal, kind, "initial size", &minimum)) {
905     return false;
906   }
907   if (!minimumVal.isUndefined()) {
908     limits->initial = minimum;
909   }
910 #endif
911 
912   // Get maximum parameter.
913   JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
914   if (!maximumAtom) {
915     return false;
916   }
917   RootedId maximumId(cx, AtomToId(maximumAtom));
918 
919   RootedValue maxVal(cx);
920   if (!GetProperty(cx, obj, obj, maximumId, &maxVal)) {
921     return false;
922   }
923 
924   // maxVal does not have a default value.
925   if (!maxVal.isUndefined()) {
926     uint32_t maximum = 0;
927     if (!EnforceRangeU32(cx, maxVal, kind, "maximum size", &maximum)) {
928       return false;
929     }
930     limits->maximum = Some(maximum);
931 
932     if (*limits->maximum > maximumField || limits->initial > *limits->maximum) {
933       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
934                                JSMSG_WASM_BAD_RANGE, kind, "maximum size");
935       return false;
936     }
937   }
938 
939   limits->shared = Shareable::False;
940 
941   if (allowShared == Shareable::True) {
942     JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
943     if (!sharedAtom) {
944       return false;
945     }
946     RootedId sharedId(cx, AtomToId(sharedAtom));
947 
948     RootedValue sharedVal(cx);
949     if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) {
950       return false;
951     }
952 
953     // shared's default value is false, which is already the value set above.
954     if (!sharedVal.isUndefined()) {
955       limits->shared =
956           ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
957 
958       if (limits->shared == Shareable::True) {
959         if (maxVal.isUndefined()) {
960           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
961                                     JSMSG_WASM_MISSING_MAXIMUM, kind);
962           return false;
963         }
964 
965         if (!cx->realm()
966                  ->creationOptions()
967                  .getSharedMemoryAndAtomicsEnabled()) {
968           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
969                                     JSMSG_WASM_NO_SHMEM_LINK);
970           return false;
971         }
972       }
973     }
974   }
975 
976 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
977   // Check both minimum and initial are not supplied.
978   if (minimumVal.isUndefined() == initialVal.isUndefined()) {
979     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
980                              JSMSG_WASM_SUPPLY_ONLY_ONE, "minimum", "initial");
981     return false;
982   }
983 #else
984   if (initialVal.isUndefined()) {
985     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
986                              JSMSG_WASM_MISSING_REQUIRED, "initial");
987     return false;
988   }
989 #endif
990 
991   return true;
992 }
993 
994 template <class Class, const char* name>
CreateWasmConstructor(JSContext * cx,JSProtoKey key)995 static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
996   RootedAtom className(cx, Atomize(cx, name, strlen(name)));
997   if (!className) {
998     return nullptr;
999   }
1000 
1001   return NewNativeConstructor(cx, Class::construct, 1, className);
1002 }
1003 
1004 // ============================================================================
1005 // WebAssembly.Module class and methods
1006 
1007 const JSClassOps WasmModuleObject::classOps_ = {
1008     nullptr,                     // addProperty
1009     nullptr,                     // delProperty
1010     nullptr,                     // enumerate
1011     nullptr,                     // newEnumerate
1012     nullptr,                     // resolve
1013     nullptr,                     // mayResolve
1014     WasmModuleObject::finalize,  // finalize
1015     nullptr,                     // call
1016     nullptr,                     // hasInstance
1017     nullptr,                     // construct
1018     nullptr,                     // trace
1019 };
1020 
1021 const JSClass WasmModuleObject::class_ = {
1022     "WebAssembly.Module",
1023     JSCLASS_DELAY_METADATA_BUILDER |
1024         JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
1025         JSCLASS_FOREGROUND_FINALIZE,
1026     &WasmModuleObject::classOps_,
1027     &WasmModuleObject::classSpec_,
1028 };
1029 
1030 const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
1031 
1032 static constexpr char WasmModuleName[] = "Module";
1033 
1034 const ClassSpec WasmModuleObject::classSpec_ = {
1035     CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
1036     GenericCreatePrototype<WasmModuleObject>,
1037     WasmModuleObject::static_methods,
1038     nullptr,
1039     WasmModuleObject::methods,
1040     WasmModuleObject::properties,
1041     nullptr,
1042     ClassSpec::DontDefineConstructor};
1043 
1044 const JSPropertySpec WasmModuleObject::properties[] = {
1045     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
1046     JS_PS_END};
1047 
1048 const JSFunctionSpec WasmModuleObject::methods[] = {JS_FS_END};
1049 
1050 const JSFunctionSpec WasmModuleObject::static_methods[] = {
1051     JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
1052     JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
1053     JS_FN("customSections", WasmModuleObject::customSections, 2,
1054           JSPROP_ENUMERATE),
1055     JS_FS_END};
1056 
1057 /* static */
finalize(JSFreeOp * fop,JSObject * obj)1058 void WasmModuleObject::finalize(JSFreeOp* fop, JSObject* obj) {
1059   const Module& module = obj->as<WasmModuleObject>().module();
1060   obj->zone()->decJitMemory(module.codeLength(module.code().stableTier()));
1061   fop->release(obj, &module, module.gcMallocBytesExcludingCode(),
1062                MemoryUse::WasmModule);
1063 }
1064 
IsModuleObject(JSObject * obj,const Module ** module)1065 static bool IsModuleObject(JSObject* obj, const Module** module) {
1066   WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>();
1067   if (!mobj) {
1068     return false;
1069   }
1070 
1071   *module = &mobj->module();
1072   return true;
1073 }
1074 
GetModuleArg(JSContext * cx,CallArgs args,uint32_t numRequired,const char * name,const Module ** module)1075 static bool GetModuleArg(JSContext* cx, CallArgs args, uint32_t numRequired,
1076                          const char* name, const Module** module) {
1077   if (!args.requireAtLeast(cx, name, numRequired)) {
1078     return false;
1079   }
1080 
1081   if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
1082     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1083                              JSMSG_WASM_BAD_MOD_ARG);
1084     return false;
1085   }
1086 
1087   return true;
1088 }
1089 
1090 struct KindNames {
1091   RootedPropertyName kind;
1092   RootedPropertyName table;
1093   RootedPropertyName memory;
1094   RootedPropertyName event;
1095   RootedPropertyName signature;
1096 
KindNamesKindNames1097   explicit KindNames(JSContext* cx)
1098       : kind(cx), table(cx), memory(cx), event(cx), signature(cx) {}
1099 };
1100 
InitKindNames(JSContext * cx,KindNames * names)1101 static bool InitKindNames(JSContext* cx, KindNames* names) {
1102   JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
1103   if (!kind) {
1104     return false;
1105   }
1106   names->kind = kind->asPropertyName();
1107 
1108   JSAtom* table = Atomize(cx, "table", strlen("table"));
1109   if (!table) {
1110     return false;
1111   }
1112   names->table = table->asPropertyName();
1113 
1114   JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
1115   if (!memory) {
1116     return false;
1117   }
1118   names->memory = memory->asPropertyName();
1119 
1120 #ifdef ENABLE_WASM_EXCEPTIONS
1121   JSAtom* event = Atomize(cx, "event", strlen("event"));
1122   if (!event) {
1123     return false;
1124   }
1125   names->event = event->asPropertyName();
1126 #endif
1127 
1128   JSAtom* signature = Atomize(cx, "signature", strlen("signature"));
1129   if (!signature) {
1130     return false;
1131   }
1132   names->signature = signature->asPropertyName();
1133 
1134   return true;
1135 }
1136 
KindToString(JSContext * cx,const KindNames & names,DefinitionKind kind)1137 static JSString* KindToString(JSContext* cx, const KindNames& names,
1138                               DefinitionKind kind) {
1139   switch (kind) {
1140     case DefinitionKind::Function:
1141       return cx->names().function;
1142     case DefinitionKind::Table:
1143       return names.table;
1144     case DefinitionKind::Memory:
1145       return names.memory;
1146     case DefinitionKind::Global:
1147       return cx->names().global;
1148 #ifdef ENABLE_WASM_EXCEPTIONS
1149     case DefinitionKind::Event:
1150       return names.event;
1151 #endif
1152   }
1153 
1154   MOZ_CRASH("invalid kind");
1155 }
1156 
FuncTypeToString(JSContext * cx,const FuncType & funcType)1157 static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) {
1158   JSStringBuilder buf(cx);
1159   if (!buf.append('(')) {
1160     return nullptr;
1161   }
1162 
1163   bool first = true;
1164   for (ValType arg : funcType.args()) {
1165     if (!first && !buf.append(", ", strlen(", "))) {
1166       return nullptr;
1167     }
1168 
1169     UniqueChars argStr = ToString(arg);
1170     if (!argStr) {
1171       return nullptr;
1172     }
1173 
1174     if (!buf.append(argStr.get(), strlen(argStr.get()))) {
1175       return nullptr;
1176     }
1177 
1178     first = false;
1179   }
1180 
1181   if (!buf.append(") -> (", strlen(") -> ("))) {
1182     return nullptr;
1183   }
1184 
1185   first = true;
1186   for (ValType result : funcType.results()) {
1187     if (!first && !buf.append(", ", strlen(", "))) {
1188       return nullptr;
1189     }
1190 
1191     UniqueChars resultStr = ToString(result);
1192     if (!resultStr) {
1193       return nullptr;
1194     }
1195 
1196     if (!buf.append(resultStr.get(), strlen(resultStr.get()))) {
1197       return nullptr;
1198     }
1199 
1200     first = false;
1201   }
1202 
1203   if (!buf.append(')')) {
1204     return nullptr;
1205   }
1206 
1207   return buf.finishString();
1208 }
1209 
UTF8CharsToString(JSContext * cx,const char * chars)1210 static JSString* UTF8CharsToString(JSContext* cx, const char* chars) {
1211   return NewStringCopyUTF8Z<CanGC>(cx,
1212                                    JS::ConstUTF8CharsZ(chars, strlen(chars)));
1213 }
1214 
1215 /* static */
imports(JSContext * cx,unsigned argc,Value * vp)1216 bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
1217   CallArgs args = CallArgsFromVp(argc, vp);
1218 
1219   const Module* module;
1220   if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) {
1221     return false;
1222   }
1223 
1224   KindNames names(cx);
1225   if (!InitKindNames(cx, &names)) {
1226     return false;
1227   }
1228 
1229   RootedValueVector elems(cx);
1230   if (!elems.reserve(module->imports().length())) {
1231     return false;
1232   }
1233 
1234   const FuncImportVector& funcImports =
1235       module->metadata(module->code().stableTier()).funcImports;
1236 
1237   size_t numFuncImport = 0;
1238   for (const Import& import : module->imports()) {
1239     Rooted<IdValueVector> props(cx, IdValueVector(cx));
1240     if (!props.reserve(3)) {
1241       return false;
1242     }
1243 
1244     JSString* moduleStr = UTF8CharsToString(cx, import.module.get());
1245     if (!moduleStr) {
1246       return false;
1247     }
1248     props.infallibleAppend(
1249         IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
1250 
1251     JSString* nameStr = UTF8CharsToString(cx, import.field.get());
1252     if (!nameStr) {
1253       return false;
1254     }
1255     props.infallibleAppend(
1256         IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1257 
1258     JSString* kindStr = KindToString(cx, names, import.kind);
1259     if (!kindStr) {
1260       return false;
1261     }
1262     props.infallibleAppend(
1263         IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1264 
1265     if (fuzzingSafe && import.kind == DefinitionKind::Function) {
1266       JSString* ftStr =
1267           FuncTypeToString(cx, funcImports[numFuncImport++].funcType());
1268       if (!ftStr) {
1269         return false;
1270       }
1271       if (!props.append(
1272               IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1273         return false;
1274       }
1275     }
1276 
1277     JSObject* obj = NewPlainObjectWithProperties(cx, props.begin(),
1278                                                  props.length(), GenericObject);
1279     if (!obj) {
1280       return false;
1281     }
1282 
1283     elems.infallibleAppend(ObjectValue(*obj));
1284   }
1285 
1286   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1287   if (!arr) {
1288     return false;
1289   }
1290 
1291   args.rval().setObject(*arr);
1292   return true;
1293 }
1294 
1295 /* static */
exports(JSContext * cx,unsigned argc,Value * vp)1296 bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
1297   CallArgs args = CallArgsFromVp(argc, vp);
1298 
1299   const Module* module;
1300   if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) {
1301     return false;
1302   }
1303 
1304   KindNames names(cx);
1305   if (!InitKindNames(cx, &names)) {
1306     return false;
1307   }
1308 
1309   RootedValueVector elems(cx);
1310   if (!elems.reserve(module->exports().length())) {
1311     return false;
1312   }
1313 
1314   for (const Export& exp : module->exports()) {
1315     Rooted<IdValueVector> props(cx, IdValueVector(cx));
1316     if (!props.reserve(2)) {
1317       return false;
1318     }
1319 
1320     JSString* nameStr = UTF8CharsToString(cx, exp.fieldName());
1321     if (!nameStr) {
1322       return false;
1323     }
1324     props.infallibleAppend(
1325         IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1326 
1327     JSString* kindStr = KindToString(cx, names, exp.kind());
1328     if (!kindStr) {
1329       return false;
1330     }
1331     props.infallibleAppend(
1332         IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1333 
1334     if (fuzzingSafe && exp.kind() == DefinitionKind::Function) {
1335       const FuncExport& fe = module->metadata(module->code().stableTier())
1336                                  .lookupFuncExport(exp.funcIndex());
1337       JSString* ftStr = FuncTypeToString(cx, fe.funcType());
1338       if (!ftStr) {
1339         return false;
1340       }
1341       if (!props.append(
1342               IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1343         return false;
1344       }
1345     }
1346 
1347     JSObject* obj = NewPlainObjectWithProperties(cx, props.begin(),
1348                                                  props.length(), GenericObject);
1349     if (!obj) {
1350       return false;
1351     }
1352 
1353     elems.infallibleAppend(ObjectValue(*obj));
1354   }
1355 
1356   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1357   if (!arr) {
1358     return false;
1359   }
1360 
1361   args.rval().setObject(*arr);
1362   return true;
1363 }
1364 
1365 /* static */
customSections(JSContext * cx,unsigned argc,Value * vp)1366 bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) {
1367   CallArgs args = CallArgsFromVp(argc, vp);
1368 
1369   const Module* module;
1370   if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections",
1371                     &module)) {
1372     return false;
1373   }
1374 
1375   Vector<char, 8> name(cx);
1376   {
1377     RootedString str(cx, ToString(cx, args.get(1)));
1378     if (!str) {
1379       return false;
1380     }
1381 
1382     Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
1383     if (!linear) {
1384       return false;
1385     }
1386 
1387     if (!name.initLengthUninitialized(
1388             JS::GetDeflatedUTF8StringLength(linear))) {
1389       return false;
1390     }
1391 
1392     (void)JS::DeflateStringToUTF8Buffer(linear,
1393                                         Span(name.begin(), name.length()));
1394   }
1395 
1396   RootedValueVector elems(cx);
1397   RootedArrayBufferObject buf(cx);
1398   for (const CustomSection& cs : module->customSections()) {
1399     if (name.length() != cs.name.length()) {
1400       continue;
1401     }
1402     if (memcmp(name.begin(), cs.name.begin(), name.length()) != 0) {
1403       continue;
1404     }
1405 
1406     buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
1407     if (!buf) {
1408       return false;
1409     }
1410 
1411     memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
1412     if (!elems.append(ObjectValue(*buf))) {
1413       return false;
1414     }
1415   }
1416 
1417   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1418   if (!arr) {
1419     return false;
1420   }
1421 
1422   args.rval().setObject(*arr);
1423   return true;
1424 }
1425 
1426 /* static */
create(JSContext * cx,const Module & module,HandleObject proto)1427 WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module,
1428                                            HandleObject proto) {
1429   AutoSetNewObjectMetadata metadata(cx);
1430   auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
1431   if (!obj) {
1432     return nullptr;
1433   }
1434 
1435   // This accounts for module allocation size (excluding code which is handled
1436   // separately - see below). This assumes that the size of associated data
1437   // doesn't change for the life of the WasmModuleObject. The size is counted
1438   // once per WasmModuleObject referencing a Module.
1439   InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module),
1440                    module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule);
1441   module.AddRef();
1442 
1443   // Bug 1569888: We account for the first tier here; the second tier, if
1444   // different, also needs to be accounted for.
1445   cx->zone()->incJitMemory(module.codeLength(module.code().stableTier()));
1446   return obj;
1447 }
1448 
GetBufferSource(JSContext * cx,JSObject * obj,unsigned errorNumber,MutableBytes * bytecode)1449 static bool GetBufferSource(JSContext* cx, JSObject* obj, unsigned errorNumber,
1450                             MutableBytes* bytecode) {
1451   *bytecode = cx->new_<ShareableBytes>();
1452   if (!*bytecode) {
1453     return false;
1454   }
1455 
1456   JSObject* unwrapped = CheckedUnwrapStatic(obj);
1457 
1458   SharedMem<uint8_t*> dataPointer;
1459   size_t byteLength;
1460   if (!unwrapped || !IsBufferSource(unwrapped, &dataPointer, &byteLength)) {
1461     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
1462     return false;
1463   }
1464 
1465   if (!(*bytecode)->append(dataPointer.unwrap(), byteLength)) {
1466     ReportOutOfMemory(cx);
1467     return false;
1468   }
1469 
1470   return true;
1471 }
1472 
ReportCompileWarnings(JSContext * cx,const UniqueCharsVector & warnings)1473 static bool ReportCompileWarnings(JSContext* cx,
1474                                   const UniqueCharsVector& warnings) {
1475   // Avoid spamming the console.
1476   size_t numWarnings = std::min<size_t>(warnings.length(), 3);
1477 
1478   for (size_t i = 0; i < numWarnings; i++) {
1479     if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, warnings[i].get())) {
1480       return false;
1481     }
1482   }
1483 
1484   if (warnings.length() > numWarnings) {
1485     if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING,
1486                          "other warnings suppressed")) {
1487       return false;
1488     }
1489   }
1490 
1491   return true;
1492 }
1493 
1494 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)1495 bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1496   CallArgs callArgs = CallArgsFromVp(argc, vp);
1497 
1498   Log(cx, "sync new Module() started");
1499 
1500   if (!ThrowIfNotConstructing(cx, callArgs, "Module")) {
1501     return false;
1502   }
1503 
1504   if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) {
1505     return false;
1506   }
1507 
1508   if (!callArgs[0].isObject()) {
1509     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1510                              JSMSG_WASM_BAD_BUF_ARG);
1511     return false;
1512   }
1513 
1514   MutableBytes bytecode;
1515   if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
1516                        &bytecode)) {
1517     return false;
1518   }
1519 
1520   SharedCompileArgs compileArgs =
1521       InitCompileArgs(cx, callArgs.get(1), "WebAssembly.Module");
1522   if (!compileArgs) {
1523     return false;
1524   }
1525 
1526   UniqueChars error;
1527   UniqueCharsVector warnings;
1528   SharedModule module =
1529       CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
1530   if (!module) {
1531     if (error) {
1532       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1533                                JSMSG_WASM_COMPILE_ERROR, error.get());
1534       return false;
1535     }
1536     ReportOutOfMemory(cx);
1537     return false;
1538   }
1539 
1540   if (!ReportCompileWarnings(cx, warnings)) {
1541     return false;
1542   }
1543 
1544   RootedObject proto(cx);
1545   if (!GetPrototypeFromBuiltinConstructor(cx, callArgs, JSProto_WasmModule,
1546                                           &proto)) {
1547     return false;
1548   }
1549   if (!proto) {
1550     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmModule);
1551   }
1552 
1553   RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
1554   if (!moduleObj) {
1555     return false;
1556   }
1557 
1558   Log(cx, "sync new Module() succeded");
1559 
1560   callArgs.rval().setObject(*moduleObj);
1561   return true;
1562 }
1563 
module() const1564 const Module& WasmModuleObject::module() const {
1565   MOZ_ASSERT(is<WasmModuleObject>());
1566   return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
1567 }
1568 
1569 // ============================================================================
1570 // WebAssembly.Instance class and methods
1571 
1572 const JSClassOps WasmInstanceObject::classOps_ = {
1573     nullptr,                       // addProperty
1574     nullptr,                       // delProperty
1575     nullptr,                       // enumerate
1576     nullptr,                       // newEnumerate
1577     nullptr,                       // resolve
1578     nullptr,                       // mayResolve
1579     WasmInstanceObject::finalize,  // finalize
1580     nullptr,                       // call
1581     nullptr,                       // hasInstance
1582     nullptr,                       // construct
1583     WasmInstanceObject::trace,     // trace
1584 };
1585 
1586 const JSClass WasmInstanceObject::class_ = {
1587     "WebAssembly.Instance",
1588     JSCLASS_DELAY_METADATA_BUILDER |
1589         JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
1590         JSCLASS_FOREGROUND_FINALIZE,
1591     &WasmInstanceObject::classOps_,
1592     &WasmInstanceObject::classSpec_,
1593 };
1594 
1595 const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
1596 
1597 static constexpr char WasmInstanceName[] = "Instance";
1598 
1599 const ClassSpec WasmInstanceObject::classSpec_ = {
1600     CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
1601     GenericCreatePrototype<WasmInstanceObject>,
1602     WasmInstanceObject::static_methods,
1603     nullptr,
1604     WasmInstanceObject::methods,
1605     WasmInstanceObject::properties,
1606     nullptr,
1607     ClassSpec::DontDefineConstructor};
1608 
IsInstance(HandleValue v)1609 static bool IsInstance(HandleValue v) {
1610   return v.isObject() && v.toObject().is<WasmInstanceObject>();
1611 }
1612 
1613 /* static */
exportsGetterImpl(JSContext * cx,const CallArgs & args)1614 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
1615                                            const CallArgs& args) {
1616   args.rval().setObject(
1617       args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
1618   return true;
1619 }
1620 
1621 /* static */
exportsGetter(JSContext * cx,unsigned argc,Value * vp)1622 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
1623                                        Value* vp) {
1624   CallArgs args = CallArgsFromVp(argc, vp);
1625   return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
1626 }
1627 
1628 const JSPropertySpec WasmInstanceObject::properties[] = {
1629     JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
1630     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
1631     JS_PS_END};
1632 
1633 const JSFunctionSpec WasmInstanceObject::methods[] = {JS_FS_END};
1634 
1635 const JSFunctionSpec WasmInstanceObject::static_methods[] = {JS_FS_END};
1636 
isNewborn() const1637 bool WasmInstanceObject::isNewborn() const {
1638   MOZ_ASSERT(is<WasmInstanceObject>());
1639   return getReservedSlot(INSTANCE_SLOT).isUndefined();
1640 }
1641 
1642 // WeakScopeMap maps from function index to js::Scope. This maps is weak
1643 // to avoid holding scope objects alive. The scopes are normally created
1644 // during debugging.
1645 //
1646 // This is defined here in order to avoid recursive dependency between
1647 // WasmJS.h and Scope.h.
1648 using WasmFunctionScopeMap =
1649     JS::WeakCache<GCHashMap<uint32_t, WeakHeapPtr<WasmFunctionScope*>,
1650                             DefaultHasher<uint32_t>, ZoneAllocPolicy>>;
1651 class WasmInstanceObject::UnspecifiedScopeMap {
1652  public:
asWasmFunctionScopeMap()1653   WasmFunctionScopeMap& asWasmFunctionScopeMap() {
1654     return *(WasmFunctionScopeMap*)this;
1655   }
1656 };
1657 
1658 /* static */
finalize(JSFreeOp * fop,JSObject * obj)1659 void WasmInstanceObject::finalize(JSFreeOp* fop, JSObject* obj) {
1660   WasmInstanceObject& instance = obj->as<WasmInstanceObject>();
1661   fop->delete_(obj, &instance.exports(), MemoryUse::WasmInstanceExports);
1662   fop->delete_(obj, &instance.scopes().asWasmFunctionScopeMap(),
1663                MemoryUse::WasmInstanceScopes);
1664   fop->delete_(obj, &instance.indirectGlobals(),
1665                MemoryUse::WasmInstanceGlobals);
1666   if (!instance.isNewborn()) {
1667     if (instance.instance().debugEnabled()) {
1668       instance.instance().debug().finalize(fop);
1669     }
1670     fop->delete_(obj, &instance.instance(), MemoryUse::WasmInstanceInstance);
1671   }
1672 }
1673 
1674 /* static */
trace(JSTracer * trc,JSObject * obj)1675 void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) {
1676   WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
1677   instanceObj.exports().trace(trc);
1678   instanceObj.indirectGlobals().trace(trc);
1679   if (!instanceObj.isNewborn()) {
1680     instanceObj.instance().tracePrivate(trc);
1681   }
1682 }
1683 
1684 /* static */
create(JSContext * cx,SharedCode code,const DataSegmentVector & dataSegments,const ElemSegmentVector & elemSegments,UniqueTlsData tlsData,HandleWasmMemoryObject memory,SharedExceptionTagVector && exceptionTags,SharedTableVector && tables,const JSFunctionVector & funcImports,const GlobalDescVector & globals,const ValVector & globalImportValues,const WasmGlobalObjectVector & globalObjs,HandleObject proto,UniqueDebugState maybeDebug)1685 WasmInstanceObject* WasmInstanceObject::create(
1686     JSContext* cx, SharedCode code, const DataSegmentVector& dataSegments,
1687     const ElemSegmentVector& elemSegments, UniqueTlsData tlsData,
1688     HandleWasmMemoryObject memory, SharedExceptionTagVector&& exceptionTags,
1689     SharedTableVector&& tables, const JSFunctionVector& funcImports,
1690     const GlobalDescVector& globals, const ValVector& globalImportValues,
1691     const WasmGlobalObjectVector& globalObjs, HandleObject proto,
1692     UniqueDebugState maybeDebug) {
1693   Rooted<UniquePtr<ExportMap>> exports(cx,
1694                                        js::MakeUnique<ExportMap>(cx->zone()));
1695   if (!exports) {
1696     ReportOutOfMemory(cx);
1697     return nullptr;
1698   }
1699 
1700   UniquePtr<WasmFunctionScopeMap> scopes =
1701       js::MakeUnique<WasmFunctionScopeMap>(cx->zone(), cx->zone());
1702   if (!scopes) {
1703     ReportOutOfMemory(cx);
1704     return nullptr;
1705   }
1706   // Note that `scopes` is a WeakCache, auto-linked into a sweep list on the
1707   // Zone, and so does not require rooting.
1708 
1709   uint32_t indirectGlobals = 0;
1710 
1711   for (uint32_t i = 0; i < globalObjs.length(); i++) {
1712     if (globalObjs[i] && globals[i].isIndirect()) {
1713       indirectGlobals++;
1714     }
1715   }
1716 
1717   Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs(
1718       cx, js::MakeUnique<GlobalObjectVector>(cx->zone()));
1719   if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
1720     ReportOutOfMemory(cx);
1721     return nullptr;
1722   }
1723 
1724   {
1725     uint32_t next = 0;
1726     for (uint32_t i = 0; i < globalObjs.length(); i++) {
1727       if (globalObjs[i] && globals[i].isIndirect()) {
1728         (*indirectGlobalObjs)[next++] = globalObjs[i];
1729       }
1730     }
1731   }
1732 
1733   Instance* instance = nullptr;
1734   RootedWasmInstanceObject obj(cx);
1735 
1736   {
1737     // We must delay creating metadata for this object until after all its
1738     // slots have been initialized. We must also create the metadata before
1739     // calling Instance::init as that may allocate new objects.
1740     AutoSetNewObjectMetadata metadata(cx);
1741     obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
1742     if (!obj) {
1743       return nullptr;
1744     }
1745 
1746     MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
1747 
1748     // Finalization assumes these slots are always initialized:
1749     InitReservedSlot(obj, EXPORTS_SLOT, exports.release(),
1750                      MemoryUse::WasmInstanceExports);
1751 
1752     InitReservedSlot(obj, SCOPES_SLOT, scopes.release(),
1753                      MemoryUse::WasmInstanceScopes);
1754 
1755     InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(),
1756                      MemoryUse::WasmInstanceGlobals);
1757 
1758     obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
1759 
1760     // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
1761     // leading to an observable "newborn" state in tracing/finalization.
1762     MOZ_ASSERT(obj->isNewborn());
1763 
1764     // Root the Instance via WasmInstanceObject before any possible GC.
1765     instance = cx->new_<Instance>(cx, obj, code, std::move(tlsData), memory,
1766                                   std::move(exceptionTags), std::move(tables),
1767                                   std::move(maybeDebug));
1768     if (!instance) {
1769       return nullptr;
1770     }
1771 
1772     InitReservedSlot(obj, INSTANCE_SLOT, instance,
1773                      MemoryUse::WasmInstanceInstance);
1774     MOZ_ASSERT(!obj->isNewborn());
1775   }
1776 
1777   if (!instance->init(cx, funcImports, globalImportValues, globalObjs,
1778                       dataSegments, elemSegments)) {
1779     return nullptr;
1780   }
1781 
1782   return obj;
1783 }
1784 
initExportsObj(JSObject & exportsObj)1785 void WasmInstanceObject::initExportsObj(JSObject& exportsObj) {
1786   MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
1787   setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
1788 }
1789 
GetImportArg(JSContext * cx,CallArgs callArgs,MutableHandleObject importObj)1790 static bool GetImportArg(JSContext* cx, CallArgs callArgs,
1791                          MutableHandleObject importObj) {
1792   if (!callArgs.get(1).isUndefined()) {
1793     if (!callArgs[1].isObject()) {
1794       return ThrowBadImportArg(cx);
1795     }
1796     importObj.set(&callArgs[1].toObject());
1797   }
1798   return true;
1799 }
1800 
1801 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)1802 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1803   CallArgs args = CallArgsFromVp(argc, vp);
1804 
1805   Log(cx, "sync new Instance() started");
1806 
1807   if (!ThrowIfNotConstructing(cx, args, "Instance")) {
1808     return false;
1809   }
1810 
1811   if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
1812     return false;
1813   }
1814 
1815   const Module* module;
1816   if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
1817     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1818                              JSMSG_WASM_BAD_MOD_ARG);
1819     return false;
1820   }
1821 
1822   RootedObject importObj(cx);
1823   if (!GetImportArg(cx, args, &importObj)) {
1824     return false;
1825   }
1826 
1827   RootedObject instanceProto(cx);
1828   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmInstance,
1829                                           &instanceProto)) {
1830     return false;
1831   }
1832   if (!instanceProto) {
1833     instanceProto =
1834         GlobalObject::getOrCreatePrototype(cx, JSProto_WasmInstance);
1835   }
1836 
1837   Rooted<ImportValues> imports(cx);
1838   if (!GetImports(cx, *module, importObj, imports.address())) {
1839     return false;
1840   }
1841 
1842   RootedWasmInstanceObject instanceObj(cx);
1843   if (!module->instantiate(cx, imports.get(), instanceProto, &instanceObj)) {
1844     return false;
1845   }
1846 
1847   Log(cx, "sync new Instance() succeeded");
1848 
1849   args.rval().setObject(*instanceObj);
1850   return true;
1851 }
1852 
instance() const1853 Instance& WasmInstanceObject::instance() const {
1854   MOZ_ASSERT(!isNewborn());
1855   return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
1856 }
1857 
exportsObj() const1858 JSObject& WasmInstanceObject::exportsObj() const {
1859   return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
1860 }
1861 
exports() const1862 WasmInstanceObject::ExportMap& WasmInstanceObject::exports() const {
1863   return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
1864 }
1865 
scopes() const1866 WasmInstanceObject::UnspecifiedScopeMap& WasmInstanceObject::scopes() const {
1867   return *(UnspecifiedScopeMap*)(getReservedSlot(SCOPES_SLOT).toPrivate());
1868 }
1869 
indirectGlobals() const1870 WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals()
1871     const {
1872   return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate();
1873 }
1874 
WasmCall(JSContext * cx,unsigned argc,Value * vp)1875 static bool WasmCall(JSContext* cx, unsigned argc, Value* vp) {
1876   CallArgs args = CallArgsFromVp(argc, vp);
1877   RootedFunction callee(cx, &args.callee().as<JSFunction>());
1878 
1879   Instance& instance = ExportedFunctionToInstance(callee);
1880   uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
1881   return instance.callExport(cx, funcIndex, args);
1882 }
1883 
1884 /*
1885  * [SMDOC] Exported wasm functions and the jit-entry stubs
1886  *
1887  * ## The kinds of exported functions
1888  *
1889  * There are several kinds of exported wasm functions.  /Explicitly/ exported
1890  * functions are:
1891  *
1892  *  - any wasm function exported via the export section
1893  *  - any asm.js export
1894  *  - the module start function
1895  *
1896  * There are also /implicitly/ exported functions, these are the functions whose
1897  * indices in the module are referenced outside the code segment, eg, in element
1898  * segments and in global initializers.
1899  *
1900  * ## Wasm functions as JSFunctions
1901  *
1902  * Any exported function can be manipulated by JS and wasm code, and to both the
1903  * exported function is represented as a JSFunction.  To JS, that means that the
1904  * function can be called in the same way as any other JSFunction.  To Wasm, it
1905  * means that the function is a reference with the same representation as
1906  * externref.
1907  *
1908  * However, the JSFunction object is created only when the function value is
1909  * actually exposed to JS the first time.  The creation is performed by
1910  * getExportedFunction(), below, as follows:
1911  *
1912  *  - a function exported via the export section (or from asm.js) is created
1913  *    when the export object is created, which happens at instantiation time.
1914  *
1915  *  - a function implicitly exported via a table is created when the table
1916  *    element is read (by JS or wasm) and a function value is needed to
1917  *    represent that value.  Functions stored in tables by initializers have a
1918  *    special representation that does not require the function object to be
1919  *    created.
1920  *
1921  *  - a function implicitly exported via a global initializer is created when
1922  *    the global is initialized.
1923  *
1924  *  - a function referenced from a ref.func instruction in code is created when
1925  *    that instruction is executed the first time.
1926  *
1927  * The JSFunction representing a wasm function never changes: every reference to
1928  * the wasm function that exposes the JSFunction gets the same JSFunction.  In
1929  * particular, imported functions already have a JSFunction representation (from
1930  * JS or from their home module), and will be exposed using that representation.
1931  *
1932  * The mapping from a wasm function to its JSFunction is instance-specific, and
1933  * held in a hashmap in the instance.  If a module is shared across multiple
1934  * instances, possibly in multiple threads, each instance will have its own
1935  * JSFunction representing the wasm function.
1936  *
1937  * ## Stubs -- interpreter, eager, lazy, provisional, and absent
1938  *
1939  * While a Wasm exported function is just a JSFunction, the internal wasm ABI is
1940  * neither the C++ ABI nor the JS JIT ABI, so there needs to be an extra step
1941  * when C++ or JS JIT code calls wasm code.  For this, execution passes through
1942  * a stub that is adapted to both the JS caller and the wasm callee.
1943  *
1944  * ### Interpreter stubs and jit-entry stubs
1945  *
1946  * When JS interpreted code calls a wasm function, we end up in
1947  * Instance::callExport() to execute the call.  This function must enter wasm,
1948  * and to do this it uses a stub that is specific to the wasm function (see
1949  * GenerateInterpEntry) that is callable with the C++ interpreter ABI and which
1950  * will convert arguments as necessary and enter compiled wasm code.
1951  *
1952  * The interpreter stub is created eagerly, when the module is compiled.
1953  *
1954  * However, the interpreter call path is slow, and when JS jitted code calls
1955  * wasm we want to do better.  In this case, there is a different, optimized
1956  * stub that is to be invoked, and it uses the JIT ABI.  This is the jit-entry
1957  * stub for the function.  Jitted code will call a wasm function's jit-entry
1958  * stub to invoke the function with the JIT ABI.  The stub will adapt the call
1959  * to the wasm ABI.
1960  *
1961  * Some jit-entry stubs are created eagerly and some are created lazily.
1962  *
1963  * ### Eager jit-entry stubs
1964  *
1965  * The explicitly exported functions have stubs created for them eagerly.  Eager
1966  * stubs are created with their tier when the module is compiled, see
1967  * ModuleGenerator::finishCodeTier(), which calls wasm::GenerateStubs(), which
1968  * generates stubs for functions with eager stubs.
1969  *
1970  * An eager stub for tier-1 is upgraded to tier-2 if the module tiers up, see
1971  * below.
1972  *
1973  * ### Lazy jit-entry stubs
1974  *
1975  * Stubs are created lazily for all implicitly exported functions.  These
1976  * functions may flow out to JS, but will only need a stub if they are ever
1977  * called from jitted code.  (That's true for explicitly exported functions too,
1978  * but for them the presumption is that they will be called.)
1979  *
1980  * Lazy stubs are created only when they are needed, and they are /doubly/ lazy,
1981  * see getExportedFunction(), below: A function implicitly exported via a table
1982  * or global may be manipulated eagerly by host code without actually being
1983  * called (maybe ever), so we do not generate a lazy stub when the function
1984  * object escapes to JS, but instead delay stub generation until the function is
1985  * actually called.
1986  *
1987  * ### The provisional lazy jit-entry stub
1988  *
1989  * However, JS baseline compilation needs to have a stub to start with in order
1990  * to allow it to attach CacheIR data to the call (or it deoptimizes the call as
1991  * a C++ call).  Thus when the JSFunction for the wasm export is retrieved by JS
1992  * code, a /provisional/ lazy jit-entry stub is associated with the function.
1993  * The stub will invoke the wasm function on the slow interpreter path via
1994  * callExport - if the function is ever called - and will cause a fast jit-entry
1995  * stub to be created at the time of the call.  The provisional lazy stub is
1996  * shared globally, it contains no function-specific or context-specific data.
1997  *
1998  * Thus, the final lazy jit-entry stubs are eventually created by
1999  * Instance::callExport, when a call is routed through it on the slow path for
2000  * any of the reasons given above.
2001  *
2002  * ### Absent jit-entry stubs
2003  *
2004  * Some functions never get jit-entry stubs.  The predicate canHaveJitEntry()
2005  * determines if a wasm function gets a stub, and it will deny this if the
2006  * function's signature exposes non-JS-compatible types (such as v128) or if
2007  * stub optimization has been disabled by a jit option.  Calls to these
2008  * functions will continue to go via callExport and use the slow interpreter
2009  * stub.
2010  *
2011  * ## The jit-entry jump table
2012  *
2013  * The mapping from the exported function to its jit-entry stub is implemented
2014  * by the jit-entry jump table in the JumpTables object (see WasmCode.h).  The
2015  * jit-entry jump table entry for a function holds a stub that the jit can call
2016  * to perform fast calls.
2017  *
2018  * While there is a single contiguous jump table, it has two logical sections:
2019  * one for eager stubs, and one for lazy stubs.  These sections are initialized
2020  * and updated separately, using logic that is specific to each section.
2021  *
2022  * The value of the table element for an eager stub is a pointer to the stub
2023  * code in the current tier.  The pointer is installed just after the creation
2024  * of the stub, before any code in the module is executed.  If the module later
2025  * tiers up, the eager jit-entry stub for tier-1 code is replaced by one for
2026  * tier-2 code, see the next section.
2027  *
2028  * Initially the value of the jump table element for a lazy stub is null.
2029  *
2030  * If the function is retrieved by JS (by getExportedFunction()) and is not
2031  * barred from having a jit-entry, then the stub is upgraded to the shared
2032  * provisional lazy jit-entry stub.  This upgrade happens to be racy if the
2033  * module is shared, and so the update is atomic and only happens if the entry
2034  * is already null.  Since the provisional lazy stub is shared, this is fine; if
2035  * several threads try to upgrade at the same time, it is to the same shared
2036  * value.
2037  *
2038  * If the retrieved function is later invoked (via callExport()), the stub is
2039  * upgraded to an actual jit-entry stub for the current code tier, again if the
2040  * function is allowed to have a jit-entry.  This is not racy -- though multiple
2041  * threads can be trying to create a jit-entry stub at the same time, they do so
2042  * under a lock and only the first to take the lock will be allowed to create a
2043  * stub, the others will reuse the first-installed stub.
2044  *
2045  * If the module later tiers up, the lazy jit-entry stub for tier-1 code (if it
2046  * exists) is replaced by one for tier-2 code, see the next section.
2047  *
2048  * (Note, the InterpEntry stub is never stored in the jit-entry table, as it
2049  * uses the C++ ABI, not the JIT ABI.  It is accessible through the
2050  * FunctionEntry.)
2051  *
2052  * ### Interaction of the jit-entry jump table and tiering
2053  *
2054  * (For general info about tiering, see the comment in WasmCompile.cpp.)
2055  *
2056  * The jit-entry stub, whether eager or lazy, is specific to a code tier - a
2057  * stub will invoke the code for its function for the tier.  When we tier up,
2058  * new jit-entry stubs must be created that reference tier-2 code, and must then
2059  * be patched into the jit-entry table.  The complication here is that, since
2060  * the jump table is shared with its code between instances on multiple threads,
2061  * tier-1 code is running on other threads and new tier-1 specific jit-entry
2062  * stubs may be created concurrently with trying to create the tier-2 stubs on
2063  * the thread that performs the tiering-up.  Indeed, there may also be
2064  * concurrent attempts to upgrade null jit-entries to the provisional lazy stub.
2065  *
2066  * Eager stubs:
2067  *
2068  *  - Eager stubs for tier-2 code are patched in racily by Module::finishTier2()
2069  *    along with code pointers for tiering; nothing conflicts with these writes.
2070  *
2071  * Lazy stubs:
2072  *
2073  *  - An upgrade from a null entry to a lazy provisional stub is atomic and can
2074  *    only happen if the entry is null, and it only happens in
2075  *    getExportedFunction().  No lazy provisional stub will be installed if
2076  *    there's another stub present.
2077  *
2078  *  - The lazy tier-appropriate stub is installed by callExport() (really by
2079  *    EnsureEntryStubs()) during the first invocation of the exported function
2080  *    that reaches callExport().  That invocation must be from within JS, and so
2081  *    the jit-entry element can't be null, because a prior getExportedFunction()
2082  *    will have ensured that it is not: the lazy provisional stub will have been
2083  *    installed.  Hence the installing of the lazy tier-appropriate stub does
2084  *    not race with the installing of the lazy provisional stub.
2085  *
2086  *  - A lazy tier-1 stub is upgraded to a lazy tier-2 stub by
2087  *    Module::finishTier2().  The upgrade needs to ensure that all tier-1 stubs
2088  *    are upgraded, and that once the upgrade is finished, callExport() will
2089  *    only create tier-2 lazy stubs.  (This upgrading does not upgrade lazy
2090  *    provisional stubs or absent stubs.)
2091  *
2092  *    The locking protocol ensuring that all stubs are upgraded properly and
2093  *    that the system switches to creating tier-2 stubs is implemented in
2094  *    Module::finishTier2() and EnsureEntryStubs():
2095  *
2096  *    There are two locks, one per code tier.
2097  *
2098  *    EnsureEntryStubs() is attempting to create a tier-appropriate lazy stub,
2099  *    so it takes the lock for the current best tier, checks to see if there is
2100  *    a stub, and exits if there is.  If the tier changed racily it takes the
2101  *    other lock too, since that is now the lock for the best tier.  Then it
2102  *    creates the stub, installs it, and releases the locks.  Thus at most one
2103  *    stub per tier can be created at a time.
2104  *
2105  *    Module::finishTier2() takes both locks (tier-1 before tier-2), thus
2106  *    preventing EnsureEntryStubs() from creating stubs while stub upgrading is
2107  *    going on, and itself waiting until EnsureEntryStubs() is not active.  Once
2108  *    it has both locks, it upgrades all lazy stubs and makes tier-2 the new
2109  *    best tier.  Should EnsureEntryStubs subsequently enter, it will find that
2110  *    a stub already exists at tier-2 and will exit early.
2111  *
2112  * (It would seem that the locking protocol could be simplified a little by
2113  * having only one lock, hanging off the Code object, or by unconditionally
2114  * taking both locks in EnsureEntryStubs().  However, in some cases where we
2115  * acquire a lock the Code object is not readily available, so plumbing would
2116  * have to be added, and in EnsureEntryStubs(), there are sometimes not two code
2117  * tiers.)
2118  *
2119  * ## Stub lifetimes and serialization
2120  *
2121  * Eager jit-entry stub code, along with stub code for import functions, is
2122  * serialized along with the tier-2 code for the module.
2123  *
2124  * Lazy stub code and thunks for builtin functions (including the provisional
2125  * lazy jit-entry stub) are never serialized.
2126  */
2127 
2128 /* static */
getExportedFunction(JSContext * cx,HandleWasmInstanceObject instanceObj,uint32_t funcIndex,MutableHandleFunction fun)2129 bool WasmInstanceObject::getExportedFunction(
2130     JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex,
2131     MutableHandleFunction fun) {
2132   if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
2133     fun.set(p->value());
2134     return true;
2135   }
2136 
2137   const Instance& instance = instanceObj->instance();
2138   const FuncExport& funcExport =
2139       instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
2140   unsigned numArgs = funcExport.funcType().args().length();
2141 
2142   if (instance.isAsmJS()) {
2143     // asm.js needs to act like a normal JS function which means having the
2144     // name from the original source and being callable as a constructor.
2145     RootedAtom name(cx, instance.getFuncDisplayAtom(cx, funcIndex));
2146     if (!name) {
2147       return false;
2148     }
2149     fun.set(NewNativeConstructor(cx, WasmCall, numArgs, name,
2150                                  gc::AllocKind::FUNCTION_EXTENDED,
2151                                  TenuredObject, FunctionFlags::ASMJS_CTOR));
2152     if (!fun) {
2153       return false;
2154     }
2155 
2156     // asm.js does not support jit entries.
2157     fun->setWasmFuncIndex(funcIndex);
2158   } else {
2159     RootedAtom name(cx, NumberToAtom(cx, funcIndex));
2160     if (!name) {
2161       return false;
2162     }
2163 
2164     fun.set(NewNativeFunction(cx, WasmCall, numArgs, name,
2165                               gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
2166                               FunctionFlags::WASM));
2167     if (!fun) {
2168       return false;
2169     }
2170 
2171     // Some applications eagerly access all table elements which currently
2172     // triggers worst-case behavior for lazy stubs, since each will allocate a
2173     // separate 4kb code page. Most eagerly-accessed functions are not called,
2174     // so use a shared, provisional (and slow) lazy stub as JitEntry and wait
2175     // until Instance::callExport() to create the fast entry stubs.
2176     if (funcExport.canHaveJitEntry()) {
2177       if (!funcExport.hasEagerStubs()) {
2178         if (!EnsureBuiltinThunksInitialized()) {
2179           return false;
2180         }
2181         void* provisionalLazyJitEntryStub = ProvisionalLazyJitEntryStub();
2182         MOZ_ASSERT(provisionalLazyJitEntryStub);
2183         instance.code().setJitEntryIfNull(funcIndex,
2184                                           provisionalLazyJitEntryStub);
2185       }
2186       fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex));
2187     } else {
2188       fun->setWasmFuncIndex(funcIndex);
2189     }
2190   }
2191 
2192   fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT,
2193                        ObjectValue(*instanceObj));
2194 
2195   void* tlsData = instanceObj->instance().tlsData();
2196   fun->setExtendedSlot(FunctionExtended::WASM_TLSDATA_SLOT,
2197                        PrivateValue(tlsData));
2198 
2199   if (!instanceObj->exports().putNew(funcIndex, fun)) {
2200     ReportOutOfMemory(cx);
2201     return false;
2202   }
2203 
2204   return true;
2205 }
2206 
getExportedFunctionCodeRange(JSFunction * fun,Tier tier)2207 const CodeRange& WasmInstanceObject::getExportedFunctionCodeRange(
2208     JSFunction* fun, Tier tier) {
2209   uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
2210   MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
2211   const MetadataTier& metadata = instance().metadata(tier);
2212   return metadata.codeRange(metadata.lookupFuncExport(funcIndex));
2213 }
2214 
2215 /* static */
getScope(JSContext * cx,HandleWasmInstanceObject instanceObj)2216 WasmInstanceScope* WasmInstanceObject::getScope(
2217     JSContext* cx, HandleWasmInstanceObject instanceObj) {
2218   if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) {
2219     return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT)
2220         .toGCThing();
2221   }
2222 
2223   Rooted<WasmInstanceScope*> instanceScope(
2224       cx, WasmInstanceScope::create(cx, instanceObj));
2225   if (!instanceScope) {
2226     return nullptr;
2227   }
2228 
2229   instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT,
2230                                PrivateGCThingValue(instanceScope));
2231 
2232   return instanceScope;
2233 }
2234 
2235 /* static */
getFunctionScope(JSContext * cx,HandleWasmInstanceObject instanceObj,uint32_t funcIndex)2236 WasmFunctionScope* WasmInstanceObject::getFunctionScope(
2237     JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex) {
2238   if (auto p =
2239           instanceObj->scopes().asWasmFunctionScopeMap().lookup(funcIndex)) {
2240     return p->value();
2241   }
2242 
2243   Rooted<WasmInstanceScope*> instanceScope(
2244       cx, WasmInstanceObject::getScope(cx, instanceObj));
2245   if (!instanceScope) {
2246     return nullptr;
2247   }
2248 
2249   Rooted<WasmFunctionScope*> funcScope(
2250       cx, WasmFunctionScope::create(cx, instanceScope, funcIndex));
2251   if (!funcScope) {
2252     return nullptr;
2253   }
2254 
2255   if (!instanceObj->scopes().asWasmFunctionScopeMap().putNew(funcIndex,
2256                                                              funcScope)) {
2257     ReportOutOfMemory(cx);
2258     return nullptr;
2259   }
2260 
2261   return funcScope;
2262 }
2263 
IsWasmExportedFunction(JSFunction * fun)2264 bool wasm::IsWasmExportedFunction(JSFunction* fun) {
2265   return fun->kind() == FunctionFlags::Wasm;
2266 }
2267 
ExportedFunctionToInstance(JSFunction * fun)2268 Instance& wasm::ExportedFunctionToInstance(JSFunction* fun) {
2269   return ExportedFunctionToInstanceObject(fun)->instance();
2270 }
2271 
ExportedFunctionToInstanceObject(JSFunction * fun)2272 WasmInstanceObject* wasm::ExportedFunctionToInstanceObject(JSFunction* fun) {
2273   MOZ_ASSERT(fun->kind() == FunctionFlags::Wasm ||
2274              fun->kind() == FunctionFlags::AsmJS);
2275   const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT);
2276   return &v.toObject().as<WasmInstanceObject>();
2277 }
2278 
ExportedFunctionToFuncIndex(JSFunction * fun)2279 uint32_t wasm::ExportedFunctionToFuncIndex(JSFunction* fun) {
2280   Instance& instance = ExportedFunctionToInstanceObject(fun)->instance();
2281   return instance.code().getFuncIndex(fun);
2282 }
2283 
2284 // ============================================================================
2285 // WebAssembly.Memory class and methods
2286 
2287 const JSClassOps WasmMemoryObject::classOps_ = {
2288     nullptr,                     // addProperty
2289     nullptr,                     // delProperty
2290     nullptr,                     // enumerate
2291     nullptr,                     // newEnumerate
2292     nullptr,                     // resolve
2293     nullptr,                     // mayResolve
2294     WasmMemoryObject::finalize,  // finalize
2295     nullptr,                     // call
2296     nullptr,                     // hasInstance
2297     nullptr,                     // construct
2298     nullptr,                     // trace
2299 };
2300 
2301 const JSClass WasmMemoryObject::class_ = {
2302     "WebAssembly.Memory",
2303     JSCLASS_DELAY_METADATA_BUILDER |
2304         JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
2305         JSCLASS_FOREGROUND_FINALIZE,
2306     &WasmMemoryObject::classOps_, &WasmMemoryObject::classSpec_};
2307 
2308 const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_;
2309 
2310 static constexpr char WasmMemoryName[] = "Memory";
2311 
2312 const ClassSpec WasmMemoryObject::classSpec_ = {
2313     CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>,
2314     GenericCreatePrototype<WasmMemoryObject>,
2315     WasmMemoryObject::static_methods,
2316     nullptr,
2317     WasmMemoryObject::methods,
2318     WasmMemoryObject::properties,
2319     nullptr,
2320     ClassSpec::DontDefineConstructor};
2321 
2322 /* static */
finalize(JSFreeOp * fop,JSObject * obj)2323 void WasmMemoryObject::finalize(JSFreeOp* fop, JSObject* obj) {
2324   WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
2325   if (memory.hasObservers()) {
2326     fop->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers);
2327   }
2328 }
2329 
2330 /* static */
create(JSContext * cx,HandleArrayBufferObjectMaybeShared buffer,HandleObject proto)2331 WasmMemoryObject* WasmMemoryObject::create(
2332     JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
2333     HandleObject proto) {
2334   AutoSetNewObjectMetadata metadata(cx);
2335   auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
2336   if (!obj) {
2337     return nullptr;
2338   }
2339 
2340   obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
2341   MOZ_ASSERT(!obj->hasObservers());
2342   return obj;
2343 }
2344 
2345 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)2346 bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2347   CallArgs args = CallArgsFromVp(argc, vp);
2348 
2349   if (!ThrowIfNotConstructing(cx, args, "Memory")) {
2350     return false;
2351   }
2352 
2353   if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1)) {
2354     return false;
2355   }
2356 
2357   if (!args.get(0).isObject()) {
2358     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2359                              JSMSG_WASM_BAD_DESC_ARG, "memory");
2360     return false;
2361   }
2362 
2363   RootedObject obj(cx, &args[0].toObject());
2364   Limits limits;
2365   if (!GetLimits(cx, obj, MaxMemory32LimitField, "Memory", &limits,
2366                  Shareable::True)) {
2367     return false;
2368   }
2369 
2370   if (Pages(limits.initial) > MaxMemory32Pages()) {
2371     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2372                              JSMSG_WASM_MEM_IMP_LIMIT);
2373     return false;
2374   }
2375   MemoryDesc memory(MemoryKind::Memory32, limits);
2376 
2377   RootedArrayBufferObjectMaybeShared buffer(cx);
2378   if (!CreateWasmBuffer32(cx, memory, &buffer)) {
2379     return false;
2380   }
2381 
2382   RootedObject proto(cx);
2383   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmMemory,
2384                                           &proto)) {
2385     return false;
2386   }
2387   if (!proto) {
2388     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmMemory);
2389   }
2390 
2391   RootedWasmMemoryObject memoryObj(cx,
2392                                    WasmMemoryObject::create(cx, buffer, proto));
2393   if (!memoryObj) {
2394     return false;
2395   }
2396 
2397   args.rval().setObject(*memoryObj);
2398   return true;
2399 }
2400 
IsMemory(HandleValue v)2401 static bool IsMemory(HandleValue v) {
2402   return v.isObject() && v.toObject().is<WasmMemoryObject>();
2403 }
2404 
2405 /* static */
bufferGetterImpl(JSContext * cx,const CallArgs & args)2406 bool WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args) {
2407   RootedWasmMemoryObject memoryObj(
2408       cx, &args.thisv().toObject().as<WasmMemoryObject>());
2409   RootedArrayBufferObjectMaybeShared buffer(cx, &memoryObj->buffer());
2410 
2411   if (memoryObj->isShared()) {
2412     size_t memoryLength = memoryObj->volatileMemoryLength();
2413     MOZ_ASSERT(memoryLength >= buffer->byteLength());
2414 
2415     if (memoryLength > buffer->byteLength()) {
2416       RootedSharedArrayBufferObject newBuffer(
2417           cx, SharedArrayBufferObject::New(
2418                   cx, memoryObj->sharedArrayRawBuffer(), memoryLength));
2419       if (!newBuffer) {
2420         return false;
2421       }
2422       // OK to addReference after we try to allocate because the memoryObj
2423       // keeps the rawBuffer alive.
2424       if (!memoryObj->sharedArrayRawBuffer()->addReference()) {
2425         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2426                                   JSMSG_SC_SAB_REFCNT_OFLO);
2427         return false;
2428       }
2429       buffer = newBuffer;
2430       memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer));
2431     }
2432   }
2433 
2434   args.rval().setObject(*buffer);
2435   return true;
2436 }
2437 
2438 /* static */
bufferGetter(JSContext * cx,unsigned argc,Value * vp)2439 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
2440   CallArgs args = CallArgsFromVp(argc, vp);
2441   return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
2442 }
2443 
2444 const JSPropertySpec WasmMemoryObject::properties[] = {
2445     JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE),
2446     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY),
2447     JS_PS_END};
2448 
2449 /* static */
growImpl(JSContext * cx,const CallArgs & args)2450 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) {
2451   RootedWasmMemoryObject memory(
2452       cx, &args.thisv().toObject().as<WasmMemoryObject>());
2453 
2454   if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) {
2455     return false;
2456   }
2457 
2458   uint32_t delta;
2459   if (!EnforceRangeU32(cx, args.get(0), "Memory", "grow delta", &delta)) {
2460     return false;
2461   }
2462 
2463   uint32_t ret = grow(memory, delta, cx);
2464 
2465   if (ret == uint32_t(-1)) {
2466     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
2467                              "memory");
2468     return false;
2469   }
2470 
2471   args.rval().setInt32(ret);
2472   return true;
2473 }
2474 
2475 /* static */
grow(JSContext * cx,unsigned argc,Value * vp)2476 bool WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp) {
2477   CallArgs args = CallArgsFromVp(argc, vp);
2478   return CallNonGenericMethod<IsMemory, growImpl>(cx, args);
2479 }
2480 
2481 const JSFunctionSpec WasmMemoryObject::methods[] = {
2482 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
2483     JS_FN("type", WasmMemoryObject::type, 0, JSPROP_ENUMERATE),
2484 #endif
2485     JS_FN("grow", WasmMemoryObject::grow, 1, JSPROP_ENUMERATE), JS_FS_END};
2486 
2487 const JSFunctionSpec WasmMemoryObject::static_methods[] = {JS_FS_END};
2488 
buffer() const2489 ArrayBufferObjectMaybeShared& WasmMemoryObject::buffer() const {
2490   return getReservedSlot(BUFFER_SLOT)
2491       .toObject()
2492       .as<ArrayBufferObjectMaybeShared>();
2493 }
2494 
sharedArrayRawBuffer() const2495 SharedArrayRawBuffer* WasmMemoryObject::sharedArrayRawBuffer() const {
2496   MOZ_ASSERT(isShared());
2497   return buffer().as<SharedArrayBufferObject>().rawBufferObject();
2498 }
2499 
2500 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
typeImpl(JSContext * cx,const CallArgs & args)2501 bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) {
2502   RootedWasmMemoryObject memoryObj(
2503       cx, &args.thisv().toObject().as<WasmMemoryObject>());
2504   Rooted<IdValueVector> props(cx, IdValueVector(cx));
2505 
2506   Maybe<Pages> maxPages = memoryObj->maxPages();
2507   if (maxPages.isSome()) {
2508     uint32_t maxPages32 = mozilla::AssertedCast<uint32_t>(maxPages->value());
2509     if (!props.append(IdValuePair(NameToId(cx->names().maximum),
2510                                   Int32Value(maxPages32)))) {
2511       return false;
2512     }
2513   }
2514 
2515   uint32_t minimumPages =
2516       mozilla::AssertedCast<uint32_t>(memoryObj->volatilePages().value());
2517   if (!props.append(IdValuePair(NameToId(cx->names().minimum),
2518                                 Int32Value(minimumPages)))) {
2519     return false;
2520   }
2521 
2522   if (!props.append(IdValuePair(NameToId(cx->names().shared),
2523                                 BooleanValue(memoryObj->isShared())))) {
2524     return false;
2525   }
2526 
2527   JSObject* memoryType = NewPlainObjectWithProperties(
2528       cx, props.begin(), props.length(), GenericObject);
2529   if (!memoryType) {
2530     return false;
2531   }
2532   args.rval().setObject(*memoryType);
2533   return true;
2534 }
2535 
type(JSContext * cx,unsigned argc,Value * vp)2536 bool WasmMemoryObject::type(JSContext* cx, unsigned argc, Value* vp) {
2537   CallArgs args = CallArgsFromVp(argc, vp);
2538   return CallNonGenericMethod<IsMemory, typeImpl>(cx, args);
2539 }
2540 #endif
2541 
volatileMemoryLength() const2542 size_t WasmMemoryObject::volatileMemoryLength() const {
2543   if (isShared()) {
2544     return sharedArrayRawBuffer()->volatileByteLength();
2545   }
2546   return buffer().byteLength();
2547 }
2548 
volatilePages() const2549 wasm::Pages WasmMemoryObject::volatilePages() const {
2550   if (isShared()) {
2551     return sharedArrayRawBuffer()->volatileWasmPages();
2552   }
2553   return buffer().wasmPages();
2554 }
2555 
maxPages() const2556 Maybe<wasm::Pages> WasmMemoryObject::maxPages() const {
2557   if (isShared()) {
2558     return Some(sharedArrayRawBuffer()->wasmMaxPages());
2559   }
2560   return buffer().wasmMaxPages();
2561 }
2562 
isShared() const2563 bool WasmMemoryObject::isShared() const {
2564   return buffer().is<SharedArrayBufferObject>();
2565 }
2566 
hasObservers() const2567 bool WasmMemoryObject::hasObservers() const {
2568   return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
2569 }
2570 
observers() const2571 WasmMemoryObject::InstanceSet& WasmMemoryObject::observers() const {
2572   MOZ_ASSERT(hasObservers());
2573   return *reinterpret_cast<InstanceSet*>(
2574       getReservedSlot(OBSERVERS_SLOT).toPrivate());
2575 }
2576 
getOrCreateObservers(JSContext * cx)2577 WasmMemoryObject::InstanceSet* WasmMemoryObject::getOrCreateObservers(
2578     JSContext* cx) {
2579   if (!hasObservers()) {
2580     auto observers = MakeUnique<InstanceSet>(cx->zone(), cx->zone());
2581     if (!observers) {
2582       ReportOutOfMemory(cx);
2583       return nullptr;
2584     }
2585 
2586     InitReservedSlot(this, OBSERVERS_SLOT, observers.release(),
2587                      MemoryUse::WasmMemoryObservers);
2588   }
2589 
2590   return &observers();
2591 }
2592 
isHuge() const2593 bool WasmMemoryObject::isHuge() const {
2594 #ifdef WASM_SUPPORTS_HUGE_MEMORY
2595   // TODO: Turn this into a static_assert, if we are able to make
2596   // MaxMemory32Bytes() constexpr once the dust settles for the 4GB heaps.
2597   MOZ_ASSERT(MaxMemory32Bytes() < HugeMappedSize,
2598              "Non-huge buffer may be confused as huge");
2599   return buffer().wasmMappedSize() >= HugeMappedSize;
2600 #else
2601   return false;
2602 #endif
2603 }
2604 
movingGrowable() const2605 bool WasmMemoryObject::movingGrowable() const {
2606   return !isHuge() && !buffer().wasmMaxPages();
2607 }
2608 
boundsCheckLimit() const2609 size_t WasmMemoryObject::boundsCheckLimit() const {
2610   if (!buffer().isWasm() || isHuge()) {
2611     return buffer().byteLength();
2612   }
2613   size_t mappedSize = buffer().wasmMappedSize();
2614 #if !defined(JS_64BIT) || defined(ENABLE_WASM_CRANELIFT)
2615   // See clamping performed in CreateSpecificWasmBuffer().  On 32-bit systems
2616   // and on 64-bit with Cranelift, we do not want to overflow a uint32_t.  For
2617   // the other 64-bit compilers, all constraints are implied by the largest
2618   // accepted value for a memory's max field.
2619   MOZ_ASSERT(mappedSize < UINT32_MAX);
2620 #endif
2621   MOZ_ASSERT(mappedSize % wasm::PageSize == 0);
2622   MOZ_ASSERT(mappedSize >= wasm::GuardSize);
2623   MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize - wasm::GuardSize));
2624   size_t limit = mappedSize - wasm::GuardSize;
2625   MOZ_ASSERT(limit <= MaxMemory32BoundsCheckLimit());
2626   return limit;
2627 }
2628 
addMovingGrowObserver(JSContext * cx,WasmInstanceObject * instance)2629 bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx,
2630                                              WasmInstanceObject* instance) {
2631   MOZ_ASSERT(movingGrowable());
2632 
2633   InstanceSet* observers = getOrCreateObservers(cx);
2634   if (!observers) {
2635     return false;
2636   }
2637 
2638   if (!observers->putNew(instance)) {
2639     ReportOutOfMemory(cx);
2640     return false;
2641   }
2642 
2643   return true;
2644 }
2645 
2646 /* static */
growShared(HandleWasmMemoryObject memory,uint32_t delta)2647 uint32_t WasmMemoryObject::growShared(HandleWasmMemoryObject memory,
2648                                       uint32_t delta) {
2649   SharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer();
2650   SharedArrayRawBuffer::Lock lock(rawBuf);
2651 
2652   Pages oldNumPages = rawBuf->volatileWasmPages();
2653   Pages newPages = oldNumPages;
2654   if (!newPages.checkedIncrement(Pages(delta))) {
2655     return -1;
2656   }
2657 
2658   // Always check against the max here, do not rely on the buffer resizers to
2659   // use the correct limit, they don't have enough context.
2660   if (newPages > MaxMemory32Pages()) {
2661     return -1;
2662   }
2663 
2664   if (newPages > rawBuf->wasmMaxPages()) {
2665     return -1;
2666   }
2667 
2668   if (!rawBuf->wasmGrowToPagesInPlace(lock, newPages)) {
2669     return -1;
2670   }
2671   // New buffer objects will be created lazily in all agents (including in
2672   // this agent) by bufferGetterImpl, above, so no more work to do here.
2673 
2674   // It is safe to cast to uint32_t, as oldNumPages was within our
2675   // implementation limits of MaxMemory32Pages(), which is within uint32_t.
2676   return uint32_t(oldNumPages.value());
2677 }
2678 
2679 /* static */
grow(HandleWasmMemoryObject memory,uint32_t delta,JSContext * cx)2680 uint32_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta,
2681                                 JSContext* cx) {
2682   if (memory->isShared()) {
2683     return growShared(memory, delta);
2684   }
2685 
2686   RootedArrayBufferObject oldBuf(cx, &memory->buffer().as<ArrayBufferObject>());
2687 
2688 #if !defined(JS_64BIT) || defined(ENABLE_WASM_CRANELIFT)
2689   // TODO (large ArrayBuffer): For Cranelift, limit the memory size to something
2690   // that fits in a uint32_t.  See more information at the definition of
2691   // MaxMemory32Bytes().
2692   //
2693   // TODO: Turn this into a static_assert, if we are able to make
2694   // MaxMemory32Bytes() constexpr once the dust settles for the 4GB heaps.
2695   MOZ_ASSERT(MaxMemory32Bytes() <= UINT32_MAX, "Avoid 32-bit overflows");
2696 #endif
2697 
2698   Pages oldNumPages = oldBuf->wasmPages();
2699   Pages newPages = oldNumPages;
2700   if (!newPages.checkedIncrement(Pages(delta))) {
2701     return -1;
2702   }
2703 
2704   // Always check against the max here, do not rely on the buffer resizers to
2705   // use the correct limit, they don't have enough context.
2706   if (newPages > MaxMemory32Pages()) {
2707     return -1;
2708   }
2709 
2710   RootedArrayBufferObject newBuf(cx);
2711 
2712   if (memory->movingGrowable()) {
2713     MOZ_ASSERT(!memory->isHuge());
2714     if (!ArrayBufferObject::wasmMovingGrowToPages(newPages, oldBuf, &newBuf,
2715                                                   cx)) {
2716       return -1;
2717     }
2718   } else {
2719     if (Maybe<Pages> maxPages = oldBuf->wasmMaxPages()) {
2720       if (newPages > *maxPages) {
2721         return -1;
2722       }
2723     }
2724 
2725     if (!ArrayBufferObject::wasmGrowToPagesInPlace(newPages, oldBuf, &newBuf,
2726                                                    cx)) {
2727       return -1;
2728     }
2729   }
2730 
2731   memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
2732 
2733   // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
2734   // since observers will call buffer().
2735   if (memory->hasObservers()) {
2736     for (InstanceSet::Range r = memory->observers().all(); !r.empty();
2737          r.popFront()) {
2738       r.front()->instance().onMovingGrowMemory();
2739     }
2740   }
2741 
2742   // It is safe to cast to uint32_t, as oldNumPages was within our
2743   // implementation limits of MaxMemory32Pages(), which is within uint32_t.
2744   return uint32_t(oldNumPages.value());
2745 }
2746 
IsSharedWasmMemoryObject(JSObject * obj)2747 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) {
2748   WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>();
2749   return mobj && mobj->isShared();
2750 }
2751 
2752 // ============================================================================
2753 // WebAssembly.Table class and methods
2754 
2755 const JSClassOps WasmTableObject::classOps_ = {
2756     nullptr,                    // addProperty
2757     nullptr,                    // delProperty
2758     nullptr,                    // enumerate
2759     nullptr,                    // newEnumerate
2760     nullptr,                    // resolve
2761     nullptr,                    // mayResolve
2762     WasmTableObject::finalize,  // finalize
2763     nullptr,                    // call
2764     nullptr,                    // hasInstance
2765     nullptr,                    // construct
2766     WasmTableObject::trace,     // trace
2767 };
2768 
2769 const JSClass WasmTableObject::class_ = {
2770     "WebAssembly.Table",
2771     JSCLASS_DELAY_METADATA_BUILDER |
2772         JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
2773         JSCLASS_FOREGROUND_FINALIZE,
2774     &WasmTableObject::classOps_, &WasmTableObject::classSpec_};
2775 
2776 const JSClass& WasmTableObject::protoClass_ = PlainObject::class_;
2777 
2778 static constexpr char WasmTableName[] = "Table";
2779 
2780 const ClassSpec WasmTableObject::classSpec_ = {
2781     CreateWasmConstructor<WasmTableObject, WasmTableName>,
2782     GenericCreatePrototype<WasmTableObject>,
2783     WasmTableObject::static_methods,
2784     nullptr,
2785     WasmTableObject::methods,
2786     WasmTableObject::properties,
2787     nullptr,
2788     ClassSpec::DontDefineConstructor};
2789 
isNewborn() const2790 bool WasmTableObject::isNewborn() const {
2791   MOZ_ASSERT(is<WasmTableObject>());
2792   return getReservedSlot(TABLE_SLOT).isUndefined();
2793 }
2794 
2795 /* static */
finalize(JSFreeOp * fop,JSObject * obj)2796 void WasmTableObject::finalize(JSFreeOp* fop, JSObject* obj) {
2797   WasmTableObject& tableObj = obj->as<WasmTableObject>();
2798   if (!tableObj.isNewborn()) {
2799     auto& table = tableObj.table();
2800     fop->release(obj, &table, table.gcMallocBytes(), MemoryUse::WasmTableTable);
2801   }
2802 }
2803 
2804 /* static */
trace(JSTracer * trc,JSObject * obj)2805 void WasmTableObject::trace(JSTracer* trc, JSObject* obj) {
2806   WasmTableObject& tableObj = obj->as<WasmTableObject>();
2807   if (!tableObj.isNewborn()) {
2808     tableObj.table().tracePrivate(trc);
2809   }
2810 }
2811 
2812 // Return the JS value to use when a parameter to a function requiring a table
2813 // value is omitted. An implementation of [1].
2814 //
2815 // [1]
2816 // https://webassembly.github.io/reference-types/js-api/index.html#defaultvalue
TableDefaultValue(wasm::RefType tableType)2817 static Value TableDefaultValue(wasm::RefType tableType) {
2818   return tableType.isExtern() ? UndefinedValue() : NullValue();
2819 }
2820 
2821 /* static */
create(JSContext * cx,uint32_t initialLength,Maybe<uint32_t> maximumLength,wasm::RefType tableType,HandleObject proto)2822 WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength,
2823                                          Maybe<uint32_t> maximumLength,
2824                                          wasm::RefType tableType,
2825                                          HandleObject proto) {
2826   AutoSetNewObjectMetadata metadata(cx);
2827   RootedWasmTableObject obj(
2828       cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
2829   if (!obj) {
2830     return nullptr;
2831   }
2832 
2833   MOZ_ASSERT(obj->isNewborn());
2834 
2835   TableDesc td(tableType, initialLength, maximumLength, /*isAsmJS*/ false,
2836                /*importedOrExported=*/true);
2837 
2838   SharedTable table = Table::create(cx, td, obj);
2839   if (!table) {
2840     ReportOutOfMemory(cx);
2841     return nullptr;
2842   }
2843 
2844   size_t size = table->gcMallocBytes();
2845   InitReservedSlot(obj, TABLE_SLOT, table.forget().take(), size,
2846                    MemoryUse::WasmTableTable);
2847 
2848   MOZ_ASSERT(!obj->isNewborn());
2849   return obj;
2850 }
2851 
2852 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)2853 bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2854   CallArgs args = CallArgsFromVp(argc, vp);
2855 
2856   if (!ThrowIfNotConstructing(cx, args, "Table")) {
2857     return false;
2858   }
2859 
2860   if (!args.requireAtLeast(cx, "WebAssembly.Table", 1)) {
2861     return false;
2862   }
2863 
2864   if (!args.get(0).isObject()) {
2865     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2866                              JSMSG_WASM_BAD_DESC_ARG, "table");
2867     return false;
2868   }
2869 
2870   RootedObject obj(cx, &args[0].toObject());
2871 
2872   JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
2873   if (!elementAtom) {
2874     return false;
2875   }
2876   RootedId elementId(cx, AtomToId(elementAtom));
2877 
2878   RootedValue elementVal(cx);
2879   if (!GetProperty(cx, obj, obj, elementId, &elementVal)) {
2880     return false;
2881   }
2882 
2883   RootedString elementStr(cx, ToString(cx, elementVal));
2884   if (!elementStr) {
2885     return false;
2886   }
2887 
2888   RootedLinearString elementLinearStr(cx, elementStr->ensureLinear(cx));
2889   if (!elementLinearStr) {
2890     return false;
2891   }
2892 
2893   RefType tableType;
2894   if (StringEqualsLiteral(elementLinearStr, "anyfunc") ||
2895       StringEqualsLiteral(elementLinearStr, "funcref")) {
2896     tableType = RefType::func();
2897   } else if (StringEqualsLiteral(elementLinearStr, "externref")) {
2898     tableType = RefType::extern_();
2899 #ifdef ENABLE_WASM_GC
2900   } else if (StringEqualsLiteral(elementLinearStr, "eqref")) {
2901     if (!GcAvailable(cx)) {
2902       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2903                                JSMSG_WASM_BAD_ELEMENT);
2904       return false;
2905     }
2906     tableType = RefType::eq();
2907 #endif
2908   } else {
2909     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2910                              JSMSG_WASM_BAD_ELEMENT_GENERALIZED);
2911     return false;
2912   }
2913 
2914   Limits limits;
2915   if (!GetLimits(cx, obj, MaxTableLimitField, "Table", &limits,
2916                  Shareable::False)) {
2917     return false;
2918   }
2919 
2920   if (limits.initial > MaxTableLength) {
2921     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2922                              JSMSG_WASM_TABLE_IMP_LIMIT);
2923     return false;
2924   }
2925 
2926   RootedObject proto(cx);
2927   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmTable,
2928                                           &proto)) {
2929     return false;
2930   }
2931   if (!proto) {
2932     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTable);
2933   }
2934 
2935   // The rest of the runtime expects table limits to be within a 32-bit range.
2936   static_assert(MaxTableLimitField <= UINT32_MAX, "invariant");
2937   uint32_t initialLength = uint32_t(limits.initial);
2938   Maybe<uint32_t> maximumLength;
2939   if (limits.maximum) {
2940     maximumLength = Some(uint32_t(*limits.maximum));
2941   }
2942 
2943   RootedWasmTableObject table(
2944       cx, WasmTableObject::create(cx, initialLength, maximumLength, tableType,
2945                                   proto));
2946   if (!table) {
2947     return false;
2948   }
2949 
2950   // Initialize the table to a default value
2951   RootedValue initValue(
2952       cx, args.length() < 2 ? TableDefaultValue(tableType) : args[1]);
2953 
2954   // Skip initializing the table if the fill value is null, as that is the
2955   // default value.
2956   if (!initValue.isNull() &&
2957       !table->fillRange(cx, 0, initialLength, initValue)) {
2958     return false;
2959   }
2960 #ifdef DEBUG
2961   // Assert that null is the default value of a new table.
2962   if (initValue.isNull()) {
2963     table->assertRangeNull(0, initialLength);
2964   }
2965 #endif
2966 
2967   args.rval().setObject(*table);
2968   return true;
2969 }
2970 
IsTable(HandleValue v)2971 static bool IsTable(HandleValue v) {
2972   return v.isObject() && v.toObject().is<WasmTableObject>();
2973 }
2974 
2975 /* static */
lengthGetterImpl(JSContext * cx,const CallArgs & args)2976 bool WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args) {
2977   args.rval().setNumber(
2978       args.thisv().toObject().as<WasmTableObject>().table().length());
2979   return true;
2980 }
2981 
2982 /* static */
lengthGetter(JSContext * cx,unsigned argc,Value * vp)2983 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
2984   CallArgs args = CallArgsFromVp(argc, vp);
2985   return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
2986 }
2987 
2988 const JSPropertySpec WasmTableObject::properties[] = {
2989     JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE),
2990     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY),
2991     JS_PS_END};
2992 
ToTableIndex(JSContext * cx,HandleValue v,const Table & table,const char * noun,uint32_t * index)2993 static bool ToTableIndex(JSContext* cx, HandleValue v, const Table& table,
2994                          const char* noun, uint32_t* index) {
2995   if (!EnforceRangeU32(cx, v, "Table", noun, index)) {
2996     return false;
2997   }
2998 
2999   if (*index >= table.length()) {
3000     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3001                               JSMSG_WASM_BAD_RANGE, "Table", noun);
3002     return false;
3003   }
3004 
3005   return true;
3006 }
3007 
3008 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3009 /* static */
typeImpl(JSContext * cx,const CallArgs & args)3010 bool WasmTableObject::typeImpl(JSContext* cx, const CallArgs& args) {
3011   Rooted<IdValueVector> props(cx, IdValueVector(cx));
3012   Table& table = args.thisv().toObject().as<WasmTableObject>().table();
3013 
3014   const char* elementValue;
3015   switch (table.repr()) {
3016     case TableRepr::Func:
3017       elementValue = "funcref";
3018       break;
3019     case TableRepr::Ref:
3020       elementValue = "externref";
3021       break;
3022     default:
3023       MOZ_CRASH("Should not happen");
3024   }
3025   JSString* elementString = UTF8CharsToString(cx, elementValue);
3026   if (!elementString) {
3027     return false;
3028   }
3029   if (!props.append(IdValuePair(NameToId(cx->names().element),
3030                                 StringValue(elementString)))) {
3031     return false;
3032   }
3033 
3034   if (table.maximum().isSome()) {
3035     if (!props.append(IdValuePair(NameToId(cx->names().maximum),
3036                                   Int32Value(table.maximum().value())))) {
3037       return false;
3038     }
3039   }
3040 
3041   if (!props.append(IdValuePair(NameToId(cx->names().minimum),
3042                                 Int32Value(table.length())))) {
3043     return false;
3044   }
3045 
3046   JSObject* tableType = NewPlainObjectWithProperties(
3047       cx, props.begin(), props.length(), GenericObject);
3048   if (!tableType) {
3049     return false;
3050   }
3051   args.rval().setObject(*tableType);
3052   return true;
3053 }
3054 
3055 /* static */
type(JSContext * cx,unsigned argc,Value * vp)3056 bool WasmTableObject::type(JSContext* cx, unsigned argc, Value* vp) {
3057   CallArgs args = CallArgsFromVp(argc, vp);
3058   return CallNonGenericMethod<IsTable, typeImpl>(cx, args);
3059 }
3060 #endif
3061 
3062 /* static */
getImpl(JSContext * cx,const CallArgs & args)3063 bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) {
3064   RootedWasmTableObject tableObj(
3065       cx, &args.thisv().toObject().as<WasmTableObject>());
3066   const Table& table = tableObj->table();
3067 
3068   if (!args.requireAtLeast(cx, "WebAssembly.Table.get", 1)) {
3069     return false;
3070   }
3071 
3072   uint32_t index;
3073   if (!ToTableIndex(cx, args.get(0), table, "get index", &index)) {
3074     return false;
3075   }
3076 
3077   switch (table.repr()) {
3078     case TableRepr::Func: {
3079       MOZ_RELEASE_ASSERT(!table.isAsmJS());
3080       RootedFunction fun(cx);
3081       if (!table.getFuncRef(cx, index, &fun)) {
3082         return false;
3083       }
3084       args.rval().setObjectOrNull(fun);
3085       break;
3086     }
3087     case TableRepr::Ref: {
3088       args.rval().set(UnboxAnyRef(table.getAnyRef(index)));
3089       break;
3090     }
3091   }
3092   return true;
3093 }
3094 
3095 /* static */
get(JSContext * cx,unsigned argc,Value * vp)3096 bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) {
3097   CallArgs args = CallArgsFromVp(argc, vp);
3098   return CallNonGenericMethod<IsTable, getImpl>(cx, args);
3099 }
3100 
3101 /* static */
setImpl(JSContext * cx,const CallArgs & args)3102 bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
3103   RootedWasmTableObject tableObj(
3104       cx, &args.thisv().toObject().as<WasmTableObject>());
3105   Table& table = tableObj->table();
3106 
3107   if (!args.requireAtLeast(cx, "WebAssembly.Table.set", 1)) {
3108     return false;
3109   }
3110 
3111   uint32_t index;
3112   if (!ToTableIndex(cx, args.get(0), table, "set index", &index)) {
3113     return false;
3114   }
3115 
3116   RootedValue fillValue(
3117       cx, args.length() < 2 ? TableDefaultValue(table.elemType()) : args[1]);
3118   if (!tableObj->fillRange(cx, index, 1, fillValue)) {
3119     return false;
3120   }
3121 
3122   args.rval().setUndefined();
3123   return true;
3124 }
3125 
3126 /* static */
set(JSContext * cx,unsigned argc,Value * vp)3127 bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) {
3128   CallArgs args = CallArgsFromVp(argc, vp);
3129   return CallNonGenericMethod<IsTable, setImpl>(cx, args);
3130 }
3131 
3132 /* static */
growImpl(JSContext * cx,const CallArgs & args)3133 bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
3134   RootedWasmTableObject tableObj(
3135       cx, &args.thisv().toObject().as<WasmTableObject>());
3136   Table& table = tableObj->table();
3137 
3138   if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) {
3139     return false;
3140   }
3141 
3142   uint32_t delta;
3143   if (!EnforceRangeU32(cx, args.get(0), "Table", "grow delta", &delta)) {
3144     return false;
3145   }
3146 
3147   uint32_t oldLength = table.grow(delta);
3148 
3149   if (oldLength == uint32_t(-1)) {
3150     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
3151                              "table");
3152     return false;
3153   }
3154 
3155   // Fill the grown range of the table
3156   RootedValue fillValue(
3157       cx, args.length() < 2 ? TableDefaultValue(table.elemType()) : args[1]);
3158 
3159   // Skip filling the grown range of the table if the fill value is null, as
3160   // that is the default value.
3161   if (!fillValue.isNull() &&
3162       !tableObj->fillRange(cx, oldLength, delta, fillValue)) {
3163     return false;
3164   }
3165 #ifdef DEBUG
3166   // Assert that null is the default value of the grown range.
3167   if (fillValue.isNull()) {
3168     tableObj->assertRangeNull(oldLength, delta);
3169   }
3170 #endif
3171 
3172   args.rval().setInt32(oldLength);
3173   return true;
3174 }
3175 
3176 /* static */
grow(JSContext * cx,unsigned argc,Value * vp)3177 bool WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp) {
3178   CallArgs args = CallArgsFromVp(argc, vp);
3179   return CallNonGenericMethod<IsTable, growImpl>(cx, args);
3180 }
3181 
3182 const JSFunctionSpec WasmTableObject::methods[] = {
3183 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3184     JS_FN("type", WasmTableObject::type, 0, JSPROP_ENUMERATE),
3185 #endif
3186     JS_FN("get", WasmTableObject::get, 1, JSPROP_ENUMERATE),
3187     JS_FN("set", WasmTableObject::set, 2, JSPROP_ENUMERATE),
3188     JS_FN("grow", WasmTableObject::grow, 1, JSPROP_ENUMERATE), JS_FS_END};
3189 
3190 const JSFunctionSpec WasmTableObject::static_methods[] = {JS_FS_END};
3191 
table() const3192 Table& WasmTableObject::table() const {
3193   return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
3194 }
3195 
fillRange(JSContext * cx,uint32_t index,uint32_t length,HandleValue value) const3196 bool WasmTableObject::fillRange(JSContext* cx, uint32_t index, uint32_t length,
3197                                 HandleValue value) const {
3198   Table& tab = table();
3199 
3200   // All consumers are required to either bounds check or statically be in
3201   // bounds
3202   MOZ_ASSERT(uint64_t(index) + uint64_t(length) <= tab.length());
3203 
3204   RootedFunction fun(cx);
3205   RootedAnyRef any(cx, AnyRef::null());
3206   if (!CheckRefType(cx, tab.elemType(), value, &fun, &any)) {
3207     return false;
3208   }
3209   switch (tab.repr()) {
3210     case TableRepr::Func:
3211       MOZ_RELEASE_ASSERT(!tab.isAsmJS());
3212       tab.fillFuncRef(index, length, FuncRef::fromJSFunction(fun), cx);
3213       break;
3214     case TableRepr::Ref:
3215       tab.fillAnyRef(index, length, any);
3216       break;
3217   }
3218   return true;
3219 }
3220 
3221 #ifdef DEBUG
assertRangeNull(uint32_t index,uint32_t length) const3222 void WasmTableObject::assertRangeNull(uint32_t index, uint32_t length) const {
3223   Table& tab = table();
3224   switch (tab.repr()) {
3225     case TableRepr::Func:
3226       for (uint32_t i = index; i < index + length; i++) {
3227         MOZ_ASSERT(tab.getFuncRef(i).code == nullptr);
3228       }
3229       break;
3230     case TableRepr::Ref:
3231       for (uint32_t i = index; i < index + length; i++) {
3232         MOZ_ASSERT(tab.getAnyRef(i).isNull());
3233       }
3234       break;
3235   }
3236 }
3237 #endif
3238 
3239 // ============================================================================
3240 // WebAssembly.global class and methods
3241 
3242 const JSClassOps WasmGlobalObject::classOps_ = {
3243     nullptr,                     // addProperty
3244     nullptr,                     // delProperty
3245     nullptr,                     // enumerate
3246     nullptr,                     // newEnumerate
3247     nullptr,                     // resolve
3248     nullptr,                     // mayResolve
3249     WasmGlobalObject::finalize,  // finalize
3250     nullptr,                     // call
3251     nullptr,                     // hasInstance
3252     nullptr,                     // construct
3253     WasmGlobalObject::trace,     // trace
3254 };
3255 
3256 const JSClass WasmGlobalObject::class_ = {
3257     "WebAssembly.Global",
3258     JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
3259         JSCLASS_BACKGROUND_FINALIZE,
3260     &WasmGlobalObject::classOps_, &WasmGlobalObject::classSpec_};
3261 
3262 const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_;
3263 
3264 static constexpr char WasmGlobalName[] = "Global";
3265 
3266 const ClassSpec WasmGlobalObject::classSpec_ = {
3267     CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>,
3268     GenericCreatePrototype<WasmGlobalObject>,
3269     WasmGlobalObject::static_methods,
3270     nullptr,
3271     WasmGlobalObject::methods,
3272     WasmGlobalObject::properties,
3273     nullptr,
3274     ClassSpec::DontDefineConstructor};
3275 
3276 /* static */
trace(JSTracer * trc,JSObject * obj)3277 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
3278   WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
3279   if (global->isNewborn()) {
3280     // This can happen while we're allocating the object, in which case
3281     // every single slot of the object is not defined yet. In particular,
3282     // there's nothing to trace yet.
3283     return;
3284   }
3285   global->val().get().trace(trc);
3286 }
3287 
3288 /* static */
finalize(JSFreeOp * fop,JSObject * obj)3289 void WasmGlobalObject::finalize(JSFreeOp* fop, JSObject* obj) {
3290   WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
3291   if (!global->isNewborn()) {
3292     fop->delete_(obj, &global->val(), MemoryUse::WasmGlobalCell);
3293   }
3294 }
3295 
3296 /* static */
create(JSContext * cx,HandleVal value,bool isMutable,HandleObject proto)3297 WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value,
3298                                            bool isMutable, HandleObject proto) {
3299   AutoSetNewObjectMetadata metadata(cx);
3300   RootedWasmGlobalObject obj(
3301       cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
3302   if (!obj) {
3303     return nullptr;
3304   }
3305 
3306   MOZ_ASSERT(obj->isNewborn());
3307   MOZ_ASSERT(obj->isTenured(), "assumed by global.set post barriers");
3308 
3309   GCPtrVal* val = js_new<GCPtrVal>(Val(value.get().type()));
3310   if (!val) {
3311     ReportOutOfMemory(cx);
3312     return nullptr;
3313   }
3314   obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
3315   InitReservedSlot(obj, VAL_SLOT, val, MemoryUse::WasmGlobalCell);
3316 
3317   // It's simpler to initialize the cell after the object has been created,
3318   // to avoid needing to root the cell before the object creation.
3319   obj->val() = value.get();
3320 
3321   MOZ_ASSERT(!obj->isNewborn());
3322 
3323   return obj;
3324 }
3325 
3326 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)3327 bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
3328   CallArgs args = CallArgsFromVp(argc, vp);
3329 
3330   if (!ThrowIfNotConstructing(cx, args, "Global")) {
3331     return false;
3332   }
3333 
3334   if (!args.requireAtLeast(cx, "WebAssembly.Global", 1)) {
3335     return false;
3336   }
3337 
3338   if (!args.get(0).isObject()) {
3339     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3340                              JSMSG_WASM_BAD_DESC_ARG, "global");
3341     return false;
3342   }
3343 
3344   RootedObject obj(cx, &args[0].toObject());
3345 
3346   // Extract properties in lexicographic order per spec.
3347 
3348   RootedValue mutableVal(cx);
3349   if (!JS_GetProperty(cx, obj, "mutable", &mutableVal)) {
3350     return false;
3351   }
3352 
3353   RootedValue typeVal(cx);
3354   if (!JS_GetProperty(cx, obj, "value", &typeVal)) {
3355     return false;
3356   }
3357 
3358   ValType globalType;
3359   if (!ToValType(cx, typeVal, &globalType)) {
3360     return false;
3361   }
3362 
3363   bool isMutable = ToBoolean(mutableVal);
3364 
3365   // Extract the initial value, or provide a suitable default.
3366   RootedVal globalVal(cx, globalType);
3367 
3368   // Override with non-undefined value, if provided.
3369   RootedValue valueVal(cx, args.get(1));
3370   if (!valueVal.isUndefined() ||
3371       (args.length() >= 2 && globalType.isReference())) {
3372     if (!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
3373       return false;
3374     }
3375   }
3376 
3377   RootedObject proto(cx);
3378   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmGlobal,
3379                                           &proto)) {
3380     return false;
3381   }
3382   if (!proto) {
3383     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal);
3384   }
3385 
3386   WasmGlobalObject* global =
3387       WasmGlobalObject::create(cx, globalVal, isMutable, proto);
3388   if (!global) {
3389     return false;
3390   }
3391 
3392   args.rval().setObject(*global);
3393   return true;
3394 }
3395 
IsGlobal(HandleValue v)3396 static bool IsGlobal(HandleValue v) {
3397   return v.isObject() && v.toObject().is<WasmGlobalObject>();
3398 }
3399 
3400 /* static */
valueGetterImpl(JSContext * cx,const CallArgs & args)3401 bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) {
3402   const WasmGlobalObject& globalObj =
3403       args.thisv().toObject().as<WasmGlobalObject>();
3404   if (!globalObj.type().isExposable()) {
3405     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3406                              JSMSG_WASM_BAD_VAL_TYPE);
3407     return false;
3408   }
3409   return globalObj.val().get().toJSValue(cx, args.rval());
3410 }
3411 
3412 /* static */
valueGetter(JSContext * cx,unsigned argc,Value * vp)3413 bool WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp) {
3414   CallArgs args = CallArgsFromVp(argc, vp);
3415   return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
3416 }
3417 
3418 /* static */
valueSetterImpl(JSContext * cx,const CallArgs & args)3419 bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) {
3420   if (!args.requireAtLeast(cx, "WebAssembly.Global setter", 1)) {
3421     return false;
3422   }
3423 
3424   RootedWasmGlobalObject global(
3425       cx, &args.thisv().toObject().as<WasmGlobalObject>());
3426   if (!global->isMutable()) {
3427     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3428                              JSMSG_WASM_GLOBAL_IMMUTABLE);
3429     return false;
3430   }
3431 
3432   RootedVal val(cx);
3433   if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) {
3434     return false;
3435   }
3436   global->val() = val.get();
3437 
3438   args.rval().setUndefined();
3439   return true;
3440 }
3441 
3442 /* static */
valueSetter(JSContext * cx,unsigned argc,Value * vp)3443 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) {
3444   CallArgs args = CallArgsFromVp(argc, vp);
3445   return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
3446 }
3447 
3448 const JSPropertySpec WasmGlobalObject::properties[] = {
3449     JS_PSGS("value", WasmGlobalObject::valueGetter,
3450             WasmGlobalObject::valueSetter, JSPROP_ENUMERATE),
3451     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY),
3452     JS_PS_END};
3453 
3454 const JSFunctionSpec WasmGlobalObject::methods[] = {
3455 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3456     JS_FN("type", WasmGlobalObject::type, 0, JSPROP_ENUMERATE),
3457 #endif
3458     JS_FN(js_valueOf_str, WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE),
3459     JS_FS_END};
3460 
3461 const JSFunctionSpec WasmGlobalObject::static_methods[] = {JS_FS_END};
3462 
isMutable() const3463 bool WasmGlobalObject::isMutable() const {
3464   return getReservedSlot(MUTABLE_SLOT).toBoolean();
3465 }
3466 
type() const3467 ValType WasmGlobalObject::type() const { return val().get().type(); }
3468 
val() const3469 GCPtrVal& WasmGlobalObject::val() const {
3470   return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate());
3471 }
3472 
3473 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3474 /* static */
typeImpl(JSContext * cx,const CallArgs & args)3475 bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) {
3476   RootedWasmGlobalObject global(
3477       cx, &args.thisv().toObject().as<WasmGlobalObject>());
3478   Rooted<IdValueVector> props(cx, IdValueVector(cx));
3479 
3480   if (!props.append(IdValuePair(NameToId(cx->names().mutable_),
3481                                 BooleanValue(global->isMutable())))) {
3482     return false;
3483   }
3484 
3485   JSString* valueType = UTF8CharsToString(cx, ToString(global->type()).get());
3486   if (!valueType) {
3487     return false;
3488   }
3489   if (!props.append(
3490           IdValuePair(NameToId(cx->names().value), StringValue(valueType)))) {
3491     return false;
3492   }
3493 
3494   JSObject* globalType = NewPlainObjectWithProperties(
3495       cx, props.begin(), props.length(), GenericObject);
3496   if (!globalType) {
3497     return false;
3498   }
3499   args.rval().setObject(*globalType);
3500   return true;
3501 }
3502 
3503 /* static */
type(JSContext * cx,unsigned argc,Value * vp)3504 bool WasmGlobalObject::type(JSContext* cx, unsigned argc, Value* vp) {
3505   CallArgs args = CallArgsFromVp(argc, vp);
3506   return CallNonGenericMethod<IsGlobal, typeImpl>(cx, args);
3507 }
3508 #endif
3509 
3510 // ============================================================================
3511 // WebAssembly.Exception class and methods
3512 
3513 const JSClassOps WasmExceptionObject::classOps_ = {
3514     nullptr,                        // addProperty
3515     nullptr,                        // delProperty
3516     nullptr,                        // enumerate
3517     nullptr,                        // newEnumerate
3518     nullptr,                        // resolve
3519     nullptr,                        // mayResolve
3520     WasmExceptionObject::finalize,  // finalize
3521     nullptr,                        // call
3522     nullptr,                        // hasInstance
3523     nullptr,                        // construct
3524     nullptr,                        // trace
3525 };
3526 
3527 const JSClass WasmExceptionObject::class_ = {
3528     "WebAssembly.Exception",
3529     JSCLASS_HAS_RESERVED_SLOTS(WasmExceptionObject::RESERVED_SLOTS) |
3530         JSCLASS_FOREGROUND_FINALIZE,
3531     &WasmExceptionObject::classOps_, &WasmExceptionObject::classSpec_};
3532 
3533 const JSClass& WasmExceptionObject::protoClass_ = PlainObject::class_;
3534 
3535 static constexpr char WasmExceptionName[] = "Exception";
3536 
3537 const ClassSpec WasmExceptionObject::classSpec_ = {
3538     CreateWasmConstructor<WasmExceptionObject, WasmExceptionName>,
3539     GenericCreatePrototype<WasmExceptionObject>,
3540     WasmExceptionObject::static_methods,
3541     nullptr,
3542     WasmExceptionObject::methods,
3543     WasmExceptionObject::properties,
3544     nullptr,
3545     ClassSpec::DontDefineConstructor};
3546 
3547 /* static */
finalize(JSFreeOp * fop,JSObject * obj)3548 void WasmExceptionObject::finalize(JSFreeOp* fop, JSObject* obj) {
3549   WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>();
3550   if (!exnObj.isNewborn()) {
3551     fop->release(obj, &exnObj.tag(), MemoryUse::WasmExceptionTag);
3552     fop->delete_(obj, &exnObj.valueTypes(), MemoryUse::WasmExceptionType);
3553   }
3554 }
3555 
construct(JSContext * cx,unsigned argc,Value * vp)3556 bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
3557   CallArgs args = CallArgsFromVp(argc, vp);
3558 
3559   if (!ThrowIfNotConstructing(cx, args, "Exception")) {
3560     return false;
3561   }
3562 
3563   // FIXME: The JS API is not finalized and may specify a different behavior
3564   // here.
3565   //        For now, we implement the same behavior as V8 and error when called.
3566   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3567                            JSMSG_WASM_EXN_CONSTRUCTOR, "WebAssembly.Exception");
3568 
3569   return false;
3570 }
3571 
3572 /* static */
create(JSContext * cx,const ValTypeVector & type,HandleObject proto)3573 WasmExceptionObject* WasmExceptionObject::create(JSContext* cx,
3574                                                  const ValTypeVector& type,
3575                                                  HandleObject proto) {
3576   AutoSetNewObjectMetadata metadata(cx);
3577   RootedWasmExceptionObject obj(
3578       cx, NewObjectWithGivenProto<WasmExceptionObject>(cx, proto));
3579   if (!obj) {
3580     return nullptr;
3581   }
3582 
3583   MOZ_ASSERT(obj->isNewborn());
3584 
3585   SharedExceptionTag tag = SharedExceptionTag(cx->new_<ExceptionTag>());
3586   if (!tag) {
3587     ReportOutOfMemory(cx);
3588     return nullptr;
3589   }
3590 
3591   InitReservedSlot(obj, TAG_SLOT, tag.forget().take(),
3592                    MemoryUse::WasmExceptionTag);
3593 
3594   wasm::ValTypeVector* newValueTypes = js_new<ValTypeVector>();
3595   for (auto t : type) {
3596     if (!newValueTypes->append(t)) {
3597       return nullptr;
3598     }
3599   }
3600   InitReservedSlot(obj, TYPE_SLOT, newValueTypes, MemoryUse::WasmExceptionType);
3601 
3602   MOZ_ASSERT(!obj->isNewborn());
3603 
3604   return obj;
3605 }
3606 
isNewborn() const3607 bool WasmExceptionObject::isNewborn() const {
3608   MOZ_ASSERT(is<WasmExceptionObject>());
3609   return getReservedSlot(TYPE_SLOT).isUndefined();
3610 }
3611 
3612 const JSPropertySpec WasmExceptionObject::properties[] = {
3613     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Exception", JSPROP_READONLY),
3614     JS_PS_END};
3615 
3616 const JSFunctionSpec WasmExceptionObject::methods[] = {JS_FS_END};
3617 
3618 const JSFunctionSpec WasmExceptionObject::static_methods[] = {JS_FS_END};
3619 
valueTypes() const3620 wasm::ValTypeVector& WasmExceptionObject::valueTypes() const {
3621   return *(ValTypeVector*)getFixedSlot(TYPE_SLOT).toPrivate();
3622 };
3623 
resultType() const3624 wasm::ResultType WasmExceptionObject::resultType() const {
3625   return wasm::ResultType::Vector(valueTypes());
3626 }
3627 
tag() const3628 ExceptionTag& WasmExceptionObject::tag() const {
3629   return *(ExceptionTag*)getReservedSlot(TAG_SLOT).toPrivate();
3630 }
3631 
3632 // ============================================================================
3633 // WebAssembly.RuntimeException class and methods
3634 
3635 const JSClassOps WasmRuntimeExceptionObject::classOps_ = {
3636     nullptr,                               // addProperty
3637     nullptr,                               // delProperty
3638     nullptr,                               // enumerate
3639     nullptr,                               // newEnumerate
3640     nullptr,                               // resolve
3641     nullptr,                               // mayResolve
3642     WasmRuntimeExceptionObject::finalize,  // finalize
3643     nullptr,                               // call
3644     nullptr,                               // hasInstance
3645     nullptr,                               // construct
3646     nullptr,                               // trace
3647 };
3648 
3649 const JSClass WasmRuntimeExceptionObject::class_ = {
3650     "WebAssembly.RuntimeException",
3651     JSCLASS_HAS_RESERVED_SLOTS(WasmRuntimeExceptionObject::RESERVED_SLOTS) |
3652         JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
3653     &WasmRuntimeExceptionObject::classOps_,
3654     &WasmRuntimeExceptionObject::classSpec_};
3655 
3656 const JSClass& WasmRuntimeExceptionObject::protoClass_ = PlainObject::class_;
3657 
3658 static constexpr char WasmRuntimeExceptionName[] = "RuntimeException";
3659 
3660 const ClassSpec WasmRuntimeExceptionObject::classSpec_ = {
3661     CreateWasmConstructor<WasmRuntimeExceptionObject, WasmRuntimeExceptionName>,
3662     GenericCreatePrototype<WasmRuntimeExceptionObject>,
3663     WasmRuntimeExceptionObject::static_methods,
3664     nullptr,
3665     WasmRuntimeExceptionObject::methods,
3666     WasmRuntimeExceptionObject::properties,
3667     nullptr,
3668     ClassSpec::DontDefineConstructor};
3669 
3670 /* static */
finalize(JSFreeOp * fop,JSObject * obj)3671 void WasmRuntimeExceptionObject::finalize(JSFreeOp* fop, JSObject* obj) {
3672   WasmRuntimeExceptionObject& exnObj = obj->as<WasmRuntimeExceptionObject>();
3673   if (!exnObj.isNewborn()) {
3674     fop->release(obj, &exnObj.tag(), MemoryUse::WasmRuntimeExceptionTag);
3675   }
3676 }
3677 
construct(JSContext * cx,unsigned argc,Value * vp)3678 bool WasmRuntimeExceptionObject::construct(JSContext* cx, unsigned argc,
3679                                            Value* vp) {
3680   CallArgs args = CallArgsFromVp(argc, vp);
3681 
3682   if (!ThrowIfNotConstructing(cx, args, "RuntimeException")) {
3683     return false;
3684   }
3685 
3686   // FIXME: When the JS API is finalized, it may be possible to construct
3687   // WebAssembly.RuntimeException instances from JS, but not for now.
3688   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3689                            JSMSG_WASM_EXN_CONSTRUCTOR,
3690                            "WebAssembly.RuntimeException");
3691 
3692   return false;
3693 }
3694 
3695 /* static */
create(JSContext * cx,wasm::SharedExceptionTag tag,HandleArrayBufferObject values,HandleArrayObject refs)3696 WasmRuntimeExceptionObject* WasmRuntimeExceptionObject::create(
3697     JSContext* cx, wasm::SharedExceptionTag tag, HandleArrayBufferObject values,
3698     HandleArrayObject refs) {
3699   RootedObject proto(
3700       cx, &cx->global()->getPrototype(JSProto_WasmRuntimeException).toObject());
3701 
3702   AutoSetNewObjectMetadata metadata(cx);
3703   RootedWasmRuntimeExceptionObject obj(
3704       cx, NewObjectWithGivenProto<WasmRuntimeExceptionObject>(cx, proto));
3705   if (!obj) {
3706     return nullptr;
3707   }
3708 
3709   MOZ_ASSERT(obj->isNewborn());
3710   InitReservedSlot(obj, TAG_SLOT, tag.forget().take(),
3711                    MemoryUse::WasmRuntimeExceptionTag);
3712 
3713   obj->initFixedSlot(VALUES_SLOT, ObjectValue(*values));
3714   obj->initFixedSlot(REFS_SLOT, ObjectValue(*refs));
3715 
3716   MOZ_ASSERT(!obj->isNewborn());
3717 
3718   return obj;
3719 }
3720 
isNewborn() const3721 bool WasmRuntimeExceptionObject::isNewborn() const {
3722   MOZ_ASSERT(is<WasmRuntimeExceptionObject>());
3723   return getReservedSlot(REFS_SLOT).isUndefined();
3724 }
3725 
3726 const JSPropertySpec WasmRuntimeExceptionObject::properties[] = {
3727     JS_STRING_SYM_PS(toStringTag, "WebAssembly.RuntimeException",
3728                      JSPROP_READONLY),
3729     JS_PS_END};
3730 
3731 const JSFunctionSpec WasmRuntimeExceptionObject::methods[] = {JS_FS_END};
3732 
3733 const JSFunctionSpec WasmRuntimeExceptionObject::static_methods[] = {JS_FS_END};
3734 
tag() const3735 ExceptionTag& WasmRuntimeExceptionObject::tag() const {
3736   return *(ExceptionTag*)getReservedSlot(TAG_SLOT).toPrivate();
3737 }
3738 
refs() const3739 ArrayObject& WasmRuntimeExceptionObject::refs() const {
3740   return getReservedSlot(REFS_SLOT).toObject().as<ArrayObject>();
3741 }
3742 
3743 // ============================================================================
3744 // WebAssembly class and static methods
3745 
WebAssembly_toSource(JSContext * cx,unsigned argc,Value * vp)3746 static bool WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) {
3747   CallArgs args = CallArgsFromVp(argc, vp);
3748   args.rval().setString(cx->names().WebAssembly);
3749   return true;
3750 }
3751 
RejectWithPendingException(JSContext * cx,Handle<PromiseObject * > promise)3752 static bool RejectWithPendingException(JSContext* cx,
3753                                        Handle<PromiseObject*> promise) {
3754   if (!cx->isExceptionPending()) {
3755     return false;
3756   }
3757 
3758   RootedValue rejectionValue(cx);
3759   if (!GetAndClearException(cx, &rejectionValue)) {
3760     return false;
3761   }
3762 
3763   return PromiseObject::reject(cx, promise, rejectionValue);
3764 }
3765 
Reject(JSContext * cx,const CompileArgs & args,Handle<PromiseObject * > promise,const UniqueChars & error)3766 static bool Reject(JSContext* cx, const CompileArgs& args,
3767                    Handle<PromiseObject*> promise, const UniqueChars& error) {
3768   if (!error) {
3769     ReportOutOfMemory(cx);
3770     return RejectWithPendingException(cx, promise);
3771   }
3772 
3773   RootedObject stack(cx, promise->allocationSite());
3774   RootedString filename(
3775       cx, JS_NewStringCopyZ(cx, args.scriptedCaller.filename.get()));
3776   if (!filename) {
3777     return false;
3778   }
3779 
3780   unsigned line = args.scriptedCaller.line;
3781 
3782   // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
3783   // way to create an ErrorObject for an arbitrary error code with multiple
3784   // replacements.
3785   UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
3786   if (!str) {
3787     return false;
3788   }
3789 
3790   size_t len = strlen(str.get());
3791   RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len));
3792   if (!message) {
3793     return false;
3794   }
3795 
3796   // There's no error |cause| available here.
3797   auto cause = JS::NothingHandleValue;
3798 
3799   RootedObject errorObj(
3800       cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, filename, 0,
3801                               line, 0, nullptr, message, cause));
3802   if (!errorObj) {
3803     return false;
3804   }
3805 
3806   RootedValue rejectionValue(cx, ObjectValue(*errorObj));
3807   return PromiseObject::reject(cx, promise, rejectionValue);
3808 }
3809 
LogAsync(JSContext * cx,const char * funcName,const Module & module)3810 static void LogAsync(JSContext* cx, const char* funcName,
3811                      const Module& module) {
3812   Log(cx, "async %s succeeded%s", funcName,
3813       module.loggingDeserialized() ? " (loaded from cache)" : "");
3814 }
3815 
3816 enum class Ret { Pair, Instance };
3817 
3818 class AsyncInstantiateTask : public OffThreadPromiseTask {
3819   SharedModule module_;
3820   PersistentRooted<ImportValues> imports_;
3821   Ret ret_;
3822 
3823  public:
AsyncInstantiateTask(JSContext * cx,const Module & module,Ret ret,Handle<PromiseObject * > promise)3824   AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret,
3825                        Handle<PromiseObject*> promise)
3826       : OffThreadPromiseTask(cx, promise),
3827         module_(&module),
3828         imports_(cx),
3829         ret_(ret) {}
3830 
imports()3831   ImportValues& imports() { return imports_.get(); }
3832 
resolve(JSContext * cx,Handle<PromiseObject * > promise)3833   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
3834     RootedObject instanceProto(
3835         cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
3836 
3837     RootedWasmInstanceObject instanceObj(cx);
3838     if (!module_->instantiate(cx, imports_.get(), instanceProto,
3839                               &instanceObj)) {
3840       return RejectWithPendingException(cx, promise);
3841     }
3842 
3843     RootedValue resolutionValue(cx);
3844     if (ret_ == Ret::Instance) {
3845       resolutionValue = ObjectValue(*instanceObj);
3846     } else {
3847       RootedObject resultObj(cx, JS_NewPlainObject(cx));
3848       if (!resultObj) {
3849         return RejectWithPendingException(cx, promise);
3850       }
3851 
3852       RootedObject moduleProto(
3853           cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
3854       RootedObject moduleObj(
3855           cx, WasmModuleObject::create(cx, *module_, moduleProto));
3856       if (!moduleObj) {
3857         return RejectWithPendingException(cx, promise);
3858       }
3859 
3860       RootedValue val(cx, ObjectValue(*moduleObj));
3861       if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
3862         return RejectWithPendingException(cx, promise);
3863       }
3864 
3865       val = ObjectValue(*instanceObj);
3866       if (!JS_DefineProperty(cx, resultObj, "instance", val,
3867                              JSPROP_ENUMERATE)) {
3868         return RejectWithPendingException(cx, promise);
3869       }
3870 
3871       resolutionValue = ObjectValue(*resultObj);
3872     }
3873 
3874     if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
3875       return RejectWithPendingException(cx, promise);
3876     }
3877 
3878     LogAsync(cx, "instantiate", *module_);
3879     return true;
3880   }
3881 };
3882 
AsyncInstantiate(JSContext * cx,const Module & module,HandleObject importObj,Ret ret,Handle<PromiseObject * > promise)3883 static bool AsyncInstantiate(JSContext* cx, const Module& module,
3884                              HandleObject importObj, Ret ret,
3885                              Handle<PromiseObject*> promise) {
3886   auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise);
3887   if (!task || !task->init(cx)) {
3888     return false;
3889   }
3890 
3891   if (!GetImports(cx, module, importObj, &task->imports())) {
3892     return RejectWithPendingException(cx, promise);
3893   }
3894 
3895   task.release()->dispatchResolveAndDestroy();
3896   return true;
3897 }
3898 
ResolveCompile(JSContext * cx,const Module & module,Handle<PromiseObject * > promise)3899 static bool ResolveCompile(JSContext* cx, const Module& module,
3900                            Handle<PromiseObject*> promise) {
3901   RootedObject proto(
3902       cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
3903   RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
3904   if (!moduleObj) {
3905     return RejectWithPendingException(cx, promise);
3906   }
3907 
3908   RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
3909   if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
3910     return RejectWithPendingException(cx, promise);
3911   }
3912 
3913   LogAsync(cx, "compile", module);
3914   return true;
3915 }
3916 
3917 struct CompileBufferTask : PromiseHelperTask {
3918   MutableBytes bytecode;
3919   SharedCompileArgs compileArgs;
3920   UniqueChars error;
3921   UniqueCharsVector warnings;
3922   SharedModule module;
3923   bool instantiate;
3924   PersistentRootedObject importObj;
3925 
CompileBufferTaskCompileBufferTask3926   CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise,
3927                     HandleObject importObj)
3928       : PromiseHelperTask(cx, promise),
3929         instantiate(true),
3930         importObj(cx, importObj) {}
3931 
CompileBufferTaskCompileBufferTask3932   CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise)
3933       : PromiseHelperTask(cx, promise), instantiate(false) {}
3934 
initCompileBufferTask3935   bool init(JSContext* cx, HandleValue maybeOptions, const char* introducer) {
3936     compileArgs = InitCompileArgs(cx, maybeOptions, introducer);
3937     if (!compileArgs) {
3938       return false;
3939     }
3940     return PromiseHelperTask::init(cx);
3941   }
3942 
executeCompileBufferTask3943   void execute() override {
3944     module = CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
3945   }
3946 
resolveCompileBufferTask3947   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
3948     if (!module) {
3949       return Reject(cx, *compileArgs, promise, error);
3950     }
3951     if (!ReportCompileWarnings(cx, warnings)) {
3952       return false;
3953     }
3954     if (instantiate) {
3955       return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise);
3956     }
3957     return ResolveCompile(cx, *module, promise);
3958   }
3959 };
3960 
RejectWithPendingException(JSContext * cx,Handle<PromiseObject * > promise,CallArgs & callArgs)3961 static bool RejectWithPendingException(JSContext* cx,
3962                                        Handle<PromiseObject*> promise,
3963                                        CallArgs& callArgs) {
3964   if (!RejectWithPendingException(cx, promise)) {
3965     return false;
3966   }
3967 
3968   callArgs.rval().setObject(*promise);
3969   return true;
3970 }
3971 
EnsurePromiseSupport(JSContext * cx)3972 static bool EnsurePromiseSupport(JSContext* cx) {
3973   if (!cx->runtime()->offThreadPromiseState.ref().initialized()) {
3974     JS_ReportErrorASCII(
3975         cx, "WebAssembly Promise APIs not supported in this runtime.");
3976     return false;
3977   }
3978   return true;
3979 }
3980 
GetBufferSource(JSContext * cx,CallArgs callArgs,const char * name,MutableBytes * bytecode)3981 static bool GetBufferSource(JSContext* cx, CallArgs callArgs, const char* name,
3982                             MutableBytes* bytecode) {
3983   if (!callArgs.requireAtLeast(cx, name, 1)) {
3984     return false;
3985   }
3986 
3987   if (!callArgs[0].isObject()) {
3988     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3989                              JSMSG_WASM_BAD_BUF_ARG);
3990     return false;
3991   }
3992 
3993   return GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
3994                          bytecode);
3995 }
3996 
WebAssembly_compile(JSContext * cx,unsigned argc,Value * vp)3997 static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
3998   if (!EnsurePromiseSupport(cx)) {
3999     return false;
4000   }
4001 
4002   Log(cx, "async compile() started");
4003 
4004   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4005   if (!promise) {
4006     return false;
4007   }
4008 
4009   CallArgs callArgs = CallArgsFromVp(argc, vp);
4010 
4011   auto task = cx->make_unique<CompileBufferTask>(cx, promise);
4012   if (!task || !task->init(cx, callArgs.get(1), "WebAssembly.compile")) {
4013     return false;
4014   }
4015 
4016   if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode)) {
4017     return RejectWithPendingException(cx, promise, callArgs);
4018   }
4019 
4020   if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
4021     return false;
4022   }
4023 
4024   callArgs.rval().setObject(*promise);
4025   return true;
4026 }
4027 
GetInstantiateArgs(JSContext * cx,CallArgs callArgs,MutableHandleObject firstArg,MutableHandleObject importObj)4028 static bool GetInstantiateArgs(JSContext* cx, CallArgs callArgs,
4029                                MutableHandleObject firstArg,
4030                                MutableHandleObject importObj) {
4031   if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1)) {
4032     return false;
4033   }
4034 
4035   if (!callArgs[0].isObject()) {
4036     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
4037                              JSMSG_WASM_BAD_BUF_MOD_ARG);
4038     return false;
4039   }
4040 
4041   firstArg.set(&callArgs[0].toObject());
4042 
4043   return GetImportArg(cx, callArgs, importObj);
4044 }
4045 
WebAssembly_instantiate(JSContext * cx,unsigned argc,Value * vp)4046 static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
4047   if (!EnsurePromiseSupport(cx)) {
4048     return false;
4049   }
4050 
4051   Log(cx, "async instantiate() started");
4052 
4053   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4054   if (!promise) {
4055     return false;
4056   }
4057 
4058   CallArgs callArgs = CallArgsFromVp(argc, vp);
4059 
4060   RootedObject firstArg(cx);
4061   RootedObject importObj(cx);
4062   if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
4063     return RejectWithPendingException(cx, promise, callArgs);
4064   }
4065 
4066   const Module* module;
4067   if (IsModuleObject(firstArg, &module)) {
4068     if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) {
4069       return false;
4070     }
4071   } else {
4072     auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
4073     if (!task || !task->init(cx, callArgs.get(2), "WebAssembly.instantiate")) {
4074       return false;
4075     }
4076 
4077     if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG,
4078                          &task->bytecode)) {
4079       return RejectWithPendingException(cx, promise, callArgs);
4080     }
4081 
4082     if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
4083       return false;
4084     }
4085   }
4086 
4087   callArgs.rval().setObject(*promise);
4088   return true;
4089 }
4090 
WebAssembly_validate(JSContext * cx,unsigned argc,Value * vp)4091 static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) {
4092   CallArgs callArgs = CallArgsFromVp(argc, vp);
4093 
4094   MutableBytes bytecode;
4095   if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode)) {
4096     return false;
4097   }
4098 
4099   FeatureOptions options;
4100   ParseCompileOptions(cx, callArgs.get(1), &options);
4101   UniqueChars error;
4102   bool validated = Validate(cx, *bytecode, options, &error);
4103 
4104   // If the reason for validation failure was OOM (signalled by null error
4105   // message), report out-of-memory so that validate's return is always
4106   // correct.
4107   if (!validated && !error) {
4108     ReportOutOfMemory(cx);
4109     return false;
4110   }
4111 
4112   if (error) {
4113     MOZ_ASSERT(!validated);
4114     Log(cx, "validate() failed with: %s", error.get());
4115   }
4116 
4117   callArgs.rval().setBoolean(validated);
4118   return true;
4119 }
4120 
EnsureStreamSupport(JSContext * cx)4121 static bool EnsureStreamSupport(JSContext* cx) {
4122   // This should match wasm::StreamingCompilationAvailable().
4123 
4124   if (!EnsurePromiseSupport(cx)) {
4125     return false;
4126   }
4127 
4128   if (!CanUseExtraThreads()) {
4129     JS_ReportErrorASCII(
4130         cx, "WebAssembly.compileStreaming not supported with --no-threads");
4131     return false;
4132   }
4133 
4134   if (!cx->runtime()->consumeStreamCallback) {
4135     JS_ReportErrorASCII(cx,
4136                         "WebAssembly streaming not supported in this runtime");
4137     return false;
4138   }
4139 
4140   return true;
4141 }
4142 
4143 // This value is chosen and asserted to be disjoint from any host error code.
4144 static const size_t StreamOOMCode = 0;
4145 
RejectWithStreamErrorNumber(JSContext * cx,size_t errorCode,Handle<PromiseObject * > promise)4146 static bool RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode,
4147                                         Handle<PromiseObject*> promise) {
4148   if (errorCode == StreamOOMCode) {
4149     ReportOutOfMemory(cx);
4150     return false;
4151   }
4152 
4153   cx->runtime()->reportStreamErrorCallback(cx, errorCode);
4154   return RejectWithPendingException(cx, promise);
4155 }
4156 
4157 class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer {
4158   // The stream progresses monotonically through these states; the helper
4159   // thread wait()s for streamState_ to reach Closed.
4160   enum StreamState { Env, Code, Tail, Closed };
4161   ExclusiveWaitableData<StreamState> streamState_;
4162 
4163   // Immutable:
4164   const bool instantiate_;
4165   const PersistentRootedObject importObj_;
4166 
4167   // Immutable after noteResponseURLs() which is called at most once before
4168   // first call on stream thread:
4169   const MutableCompileArgs compileArgs_;
4170 
4171   // Immutable after Env state:
4172   Bytes envBytes_;
4173   SectionRange codeSection_;
4174 
4175   // The code section vector is resized once during the Env state and filled
4176   // in chunk by chunk during the Code state, updating the end-pointer after
4177   // each chunk:
4178   Bytes codeBytes_;
4179   uint8_t* codeBytesEnd_;
4180   ExclusiveBytesPtr exclusiveCodeBytesEnd_;
4181 
4182   // Immutable after Tail state:
4183   Bytes tailBytes_;
4184   ExclusiveStreamEndData exclusiveStreamEnd_;
4185 
4186   // Written once before Closed state and read in Closed state on main thread:
4187   SharedModule module_;
4188   Maybe<size_t> streamError_;
4189   UniqueChars compileError_;
4190   UniqueCharsVector warnings_;
4191 
4192   // Set on stream thread and read racily on helper thread to abort compilation:
4193   Atomic<bool> streamFailed_;
4194 
4195   // Called on some thread before consumeChunk(), streamEnd(), streamError()):
4196 
noteResponseURLs(const char * url,const char * sourceMapUrl)4197   void noteResponseURLs(const char* url, const char* sourceMapUrl) override {
4198     if (url) {
4199       compileArgs_->scriptedCaller.filename = DuplicateString(url);
4200       compileArgs_->scriptedCaller.filenameIsURL = true;
4201     }
4202     if (sourceMapUrl) {
4203       compileArgs_->sourceMapURL = DuplicateString(sourceMapUrl);
4204     }
4205   }
4206 
4207   // Called on a stream thread:
4208 
4209   // Until StartOffThreadPromiseHelperTask succeeds, we are responsible for
4210   // dispatching ourselves back to the JS thread.
4211   //
4212   // Warning: After this function returns, 'this' can be deleted at any time, so
4213   // the caller must immediately return from the stream callback.
setClosedAndDestroyBeforeHelperThreadStarted()4214   void setClosedAndDestroyBeforeHelperThreadStarted() {
4215     streamState_.lock().get() = Closed;
4216     dispatchResolveAndDestroy();
4217   }
4218 
4219   // See setClosedAndDestroyBeforeHelperThreadStarted() comment.
rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber)4220   bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) {
4221     MOZ_ASSERT(streamState_.lock() == Env);
4222     MOZ_ASSERT(!streamError_);
4223     streamError_ = Some(errorNumber);
4224     setClosedAndDestroyBeforeHelperThreadStarted();
4225     return false;
4226   }
4227 
4228   // Once StartOffThreadPromiseHelperTask succeeds, the helper thread will
4229   // dispatchResolveAndDestroy() after execute() returns, but execute()
4230   // wait()s for state to be Closed.
4231   //
4232   // Warning: After this function returns, 'this' can be deleted at any time, so
4233   // the caller must immediately return from the stream callback.
setClosedAndDestroyAfterHelperThreadStarted()4234   void setClosedAndDestroyAfterHelperThreadStarted() {
4235     auto streamState = streamState_.lock();
4236     MOZ_ASSERT(streamState != Closed);
4237     streamState.get() = Closed;
4238     streamState.notify_one(/* stream closed */);
4239   }
4240 
4241   // See setClosedAndDestroyAfterHelperThreadStarted() comment.
rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber)4242   bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) {
4243     MOZ_ASSERT(!streamError_);
4244     streamError_ = Some(errorNumber);
4245     streamFailed_ = true;
4246     exclusiveCodeBytesEnd_.lock().notify_one();
4247     exclusiveStreamEnd_.lock().notify_one();
4248     setClosedAndDestroyAfterHelperThreadStarted();
4249     return false;
4250   }
4251 
consumeChunk(const uint8_t * begin,size_t length)4252   bool consumeChunk(const uint8_t* begin, size_t length) override {
4253     switch (streamState_.lock().get()) {
4254       case Env: {
4255         if (!envBytes_.append(begin, length)) {
4256           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4257         }
4258 
4259         if (!StartsCodeSection(envBytes_.begin(), envBytes_.end(),
4260                                &codeSection_)) {
4261           return true;
4262         }
4263 
4264         uint32_t extraBytes = envBytes_.length() - codeSection_.start;
4265         if (extraBytes) {
4266           envBytes_.shrinkTo(codeSection_.start);
4267         }
4268 
4269         if (codeSection_.size > MaxCodeSectionBytes) {
4270           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4271         }
4272 
4273         if (!codeBytes_.resize(codeSection_.size)) {
4274           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4275         }
4276 
4277         codeBytesEnd_ = codeBytes_.begin();
4278         exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_;
4279 
4280         if (!StartOffThreadPromiseHelperTask(this)) {
4281           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4282         }
4283 
4284         // Set the state to Code iff StartOffThreadPromiseHelperTask()
4285         // succeeds so that the state tells us whether we are before or
4286         // after the helper thread started.
4287         streamState_.lock().get() = Code;
4288 
4289         if (extraBytes) {
4290           return consumeChunk(begin + length - extraBytes, extraBytes);
4291         }
4292 
4293         return true;
4294       }
4295       case Code: {
4296         size_t copyLength =
4297             std::min<size_t>(length, codeBytes_.end() - codeBytesEnd_);
4298         memcpy(codeBytesEnd_, begin, copyLength);
4299         codeBytesEnd_ += copyLength;
4300 
4301         {
4302           auto codeStreamEnd = exclusiveCodeBytesEnd_.lock();
4303           codeStreamEnd.get() = codeBytesEnd_;
4304           codeStreamEnd.notify_one();
4305         }
4306 
4307         if (codeBytesEnd_ != codeBytes_.end()) {
4308           return true;
4309         }
4310 
4311         streamState_.lock().get() = Tail;
4312 
4313         if (uint32_t extraBytes = length - copyLength) {
4314           return consumeChunk(begin + copyLength, extraBytes);
4315         }
4316 
4317         return true;
4318       }
4319       case Tail: {
4320         if (!tailBytes_.append(begin, length)) {
4321           return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode);
4322         }
4323 
4324         return true;
4325       }
4326       case Closed:
4327         MOZ_CRASH("consumeChunk() in Closed state");
4328     }
4329     MOZ_CRASH("unreachable");
4330   }
4331 
streamEnd(JS::OptimizedEncodingListener * tier2Listener)4332   void streamEnd(JS::OptimizedEncodingListener* tier2Listener) override {
4333     switch (streamState_.lock().get()) {
4334       case Env: {
4335         SharedBytes bytecode = js_new<ShareableBytes>(std::move(envBytes_));
4336         if (!bytecode) {
4337           rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4338           return;
4339         }
4340         module_ = CompileBuffer(*compileArgs_, *bytecode, &compileError_,
4341                                 &warnings_, nullptr);
4342         setClosedAndDestroyBeforeHelperThreadStarted();
4343         return;
4344       }
4345       case Code:
4346       case Tail:
4347         // Unlock exclusiveStreamEnd_ before locking streamState_.
4348         {
4349           auto streamEnd = exclusiveStreamEnd_.lock();
4350           MOZ_ASSERT(!streamEnd->reached);
4351           streamEnd->reached = true;
4352           streamEnd->tailBytes = &tailBytes_;
4353           streamEnd->tier2Listener = tier2Listener;
4354           streamEnd.notify_one();
4355         }
4356         setClosedAndDestroyAfterHelperThreadStarted();
4357         return;
4358       case Closed:
4359         MOZ_CRASH("streamEnd() in Closed state");
4360     }
4361   }
4362 
streamError(size_t errorCode)4363   void streamError(size_t errorCode) override {
4364     MOZ_ASSERT(errorCode != StreamOOMCode);
4365     switch (streamState_.lock().get()) {
4366       case Env:
4367         rejectAndDestroyBeforeHelperThreadStarted(errorCode);
4368         return;
4369       case Tail:
4370       case Code:
4371         rejectAndDestroyAfterHelperThreadStarted(errorCode);
4372         return;
4373       case Closed:
4374         MOZ_CRASH("streamError() in Closed state");
4375     }
4376   }
4377 
consumeOptimizedEncoding(const uint8_t * begin,size_t length)4378   void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override {
4379     module_ = Module::deserialize(begin, length);
4380 
4381     MOZ_ASSERT(streamState_.lock().get() == Env);
4382     setClosedAndDestroyBeforeHelperThreadStarted();
4383   }
4384 
4385   // Called on a helper thread:
4386 
execute()4387   void execute() override {
4388     module_ = CompileStreaming(*compileArgs_, envBytes_, codeBytes_,
4389                                exclusiveCodeBytesEnd_, exclusiveStreamEnd_,
4390                                streamFailed_, &compileError_, &warnings_);
4391 
4392     // When execute() returns, the CompileStreamTask will be dispatched
4393     // back to its JS thread to call resolve() and then be destroyed. We
4394     // can't let this happen until the stream has been closed lest
4395     // consumeChunk() or streamEnd() be called on a dead object.
4396     auto streamState = streamState_.lock();
4397     while (streamState != Closed) {
4398       streamState.wait(/* stream closed */);
4399     }
4400   }
4401 
4402   // Called on a JS thread after streaming compilation completes/errors:
4403 
resolve(JSContext * cx,Handle<PromiseObject * > promise)4404   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
4405     MOZ_ASSERT(streamState_.lock() == Closed);
4406 
4407     if (module_) {
4408       MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_);
4409       if (!ReportCompileWarnings(cx, warnings_)) {
4410         return false;
4411       }
4412       if (instantiate_) {
4413         return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise);
4414       }
4415       return ResolveCompile(cx, *module_, promise);
4416     }
4417 
4418     if (streamError_) {
4419       return RejectWithStreamErrorNumber(cx, *streamError_, promise);
4420     }
4421 
4422     return Reject(cx, *compileArgs_, promise, compileError_);
4423   }
4424 
4425  public:
CompileStreamTask(JSContext * cx,Handle<PromiseObject * > promise,CompileArgs & compileArgs,bool instantiate,HandleObject importObj)4426   CompileStreamTask(JSContext* cx, Handle<PromiseObject*> promise,
4427                     CompileArgs& compileArgs, bool instantiate,
4428                     HandleObject importObj)
4429       : PromiseHelperTask(cx, promise),
4430         streamState_(mutexid::WasmStreamStatus, Env),
4431         instantiate_(instantiate),
4432         importObj_(cx, importObj),
4433         compileArgs_(&compileArgs),
4434         codeSection_{},
4435         codeBytesEnd_(nullptr),
4436         exclusiveCodeBytesEnd_(mutexid::WasmCodeBytesEnd, nullptr),
4437         exclusiveStreamEnd_(mutexid::WasmStreamEnd),
4438         streamFailed_(false) {
4439     MOZ_ASSERT_IF(importObj_, instantiate_);
4440   }
4441 };
4442 
4443 // A short-lived object that captures the arguments of a
4444 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for
4445 // the Promise<Response> to resolve to a (hopefully) Promise.
4446 class ResolveResponseClosure : public NativeObject {
4447   static const unsigned COMPILE_ARGS_SLOT = 0;
4448   static const unsigned PROMISE_OBJ_SLOT = 1;
4449   static const unsigned INSTANTIATE_SLOT = 2;
4450   static const unsigned IMPORT_OBJ_SLOT = 3;
4451   static const JSClassOps classOps_;
4452 
finalize(JSFreeOp * fop,JSObject * obj)4453   static void finalize(JSFreeOp* fop, JSObject* obj) {
4454     auto& closure = obj->as<ResolveResponseClosure>();
4455     fop->release(obj, &closure.compileArgs(),
4456                  MemoryUse::WasmResolveResponseClosure);
4457   }
4458 
4459  public:
4460   static const unsigned RESERVED_SLOTS = 4;
4461   static const JSClass class_;
4462 
create(JSContext * cx,const CompileArgs & args,HandleObject promise,bool instantiate,HandleObject importObj)4463   static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args,
4464                                         HandleObject promise, bool instantiate,
4465                                         HandleObject importObj) {
4466     MOZ_ASSERT_IF(importObj, instantiate);
4467 
4468     AutoSetNewObjectMetadata metadata(cx);
4469     auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr);
4470     if (!obj) {
4471       return nullptr;
4472     }
4473 
4474     args.AddRef();
4475     InitReservedSlot(obj, COMPILE_ARGS_SLOT, const_cast<CompileArgs*>(&args),
4476                      MemoryUse::WasmResolveResponseClosure);
4477     obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise));
4478     obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate));
4479     obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj));
4480     return obj;
4481   }
4482 
compileArgs() const4483   CompileArgs& compileArgs() const {
4484     return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate();
4485   }
promise() const4486   PromiseObject& promise() const {
4487     return getReservedSlot(PROMISE_OBJ_SLOT).toObject().as<PromiseObject>();
4488   }
instantiate() const4489   bool instantiate() const {
4490     return getReservedSlot(INSTANTIATE_SLOT).toBoolean();
4491   }
importObj() const4492   JSObject* importObj() const {
4493     return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull();
4494   }
4495 };
4496 
4497 const JSClassOps ResolveResponseClosure::classOps_ = {
4498     nullptr,                           // addProperty
4499     nullptr,                           // delProperty
4500     nullptr,                           // enumerate
4501     nullptr,                           // newEnumerate
4502     nullptr,                           // resolve
4503     nullptr,                           // mayResolve
4504     ResolveResponseClosure::finalize,  // finalize
4505     nullptr,                           // call
4506     nullptr,                           // hasInstance
4507     nullptr,                           // construct
4508     nullptr,                           // trace
4509 };
4510 
4511 const JSClass ResolveResponseClosure::class_ = {
4512     "WebAssembly ResolveResponseClosure",
4513     JSCLASS_DELAY_METADATA_BUILDER |
4514         JSCLASS_HAS_RESERVED_SLOTS(ResolveResponseClosure::RESERVED_SLOTS) |
4515         JSCLASS_FOREGROUND_FINALIZE,
4516     &ResolveResponseClosure::classOps_,
4517 };
4518 
ToResolveResponseClosure(CallArgs args)4519 static ResolveResponseClosure* ToResolveResponseClosure(CallArgs args) {
4520   return &args.callee()
4521               .as<JSFunction>()
4522               .getExtendedSlot(0)
4523               .toObject()
4524               .as<ResolveResponseClosure>();
4525 }
4526 
RejectWithErrorNumber(JSContext * cx,uint32_t errorNumber,Handle<PromiseObject * > promise)4527 static bool RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber,
4528                                   Handle<PromiseObject*> promise) {
4529   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
4530   return RejectWithPendingException(cx, promise);
4531 }
4532 
ResolveResponse_OnFulfilled(JSContext * cx,unsigned argc,Value * vp)4533 static bool ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc,
4534                                         Value* vp) {
4535   CallArgs callArgs = CallArgsFromVp(argc, vp);
4536 
4537   Rooted<ResolveResponseClosure*> closure(cx,
4538                                           ToResolveResponseClosure(callArgs));
4539   Rooted<PromiseObject*> promise(cx, &closure->promise());
4540   CompileArgs& compileArgs = closure->compileArgs();
4541   bool instantiate = closure->instantiate();
4542   Rooted<JSObject*> importObj(cx, closure->importObj());
4543 
4544   auto task = cx->make_unique<CompileStreamTask>(cx, promise, compileArgs,
4545                                                  instantiate, importObj);
4546   if (!task || !task->init(cx)) {
4547     return false;
4548   }
4549 
4550   if (!callArgs.get(0).isObject()) {
4551     return RejectWithErrorNumber(cx, JSMSG_WASM_BAD_RESPONSE_VALUE, promise);
4552   }
4553 
4554   RootedObject response(cx, &callArgs.get(0).toObject());
4555   if (!cx->runtime()->consumeStreamCallback(cx, response, JS::MimeType::Wasm,
4556                                             task.get())) {
4557     return RejectWithPendingException(cx, promise);
4558   }
4559 
4560   (void)task.release();
4561 
4562   callArgs.rval().setUndefined();
4563   return true;
4564 }
4565 
ResolveResponse_OnRejected(JSContext * cx,unsigned argc,Value * vp)4566 static bool ResolveResponse_OnRejected(JSContext* cx, unsigned argc,
4567                                        Value* vp) {
4568   CallArgs args = CallArgsFromVp(argc, vp);
4569 
4570   Rooted<ResolveResponseClosure*> closure(cx, ToResolveResponseClosure(args));
4571   Rooted<PromiseObject*> promise(cx, &closure->promise());
4572 
4573   if (!PromiseObject::reject(cx, promise, args.get(0))) {
4574     return false;
4575   }
4576 
4577   args.rval().setUndefined();
4578   return true;
4579 }
4580 
ResolveResponse(JSContext * cx,CallArgs callArgs,Handle<PromiseObject * > promise,bool instantiate=false,HandleObject importObj=nullptr)4581 static bool ResolveResponse(JSContext* cx, CallArgs callArgs,
4582                             Handle<PromiseObject*> promise,
4583                             bool instantiate = false,
4584                             HandleObject importObj = nullptr) {
4585   MOZ_ASSERT_IF(importObj, instantiate);
4586 
4587   const char* introducer = instantiate ? "WebAssembly.instantiateStreaming"
4588                                        : "WebAssembly.compileStreaming";
4589 
4590   SharedCompileArgs compileArgs =
4591       InitCompileArgs(cx, callArgs.get(instantiate ? 2 : 1), introducer);
4592   if (!compileArgs) {
4593     return false;
4594   }
4595 
4596   RootedObject closure(
4597       cx, ResolveResponseClosure::create(cx, *compileArgs, promise, instantiate,
4598                                          importObj));
4599   if (!closure) {
4600     return false;
4601   }
4602 
4603   RootedFunction onResolved(
4604       cx, NewNativeFunction(cx, ResolveResponse_OnFulfilled, 1, nullptr,
4605                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
4606   if (!onResolved) {
4607     return false;
4608   }
4609 
4610   RootedFunction onRejected(
4611       cx, NewNativeFunction(cx, ResolveResponse_OnRejected, 1, nullptr,
4612                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
4613   if (!onRejected) {
4614     return false;
4615   }
4616 
4617   onResolved->setExtendedSlot(0, ObjectValue(*closure));
4618   onRejected->setExtendedSlot(0, ObjectValue(*closure));
4619 
4620   RootedObject resolve(cx,
4621                        PromiseObject::unforgeableResolve(cx, callArgs.get(0)));
4622   if (!resolve) {
4623     return false;
4624   }
4625 
4626   return JS::AddPromiseReactions(cx, resolve, onResolved, onRejected);
4627 }
4628 
WebAssembly_compileStreaming(JSContext * cx,unsigned argc,Value * vp)4629 static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
4630                                          Value* vp) {
4631   if (!EnsureStreamSupport(cx)) {
4632     return false;
4633   }
4634 
4635   Log(cx, "async compileStreaming() started");
4636 
4637   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4638   if (!promise) {
4639     return false;
4640   }
4641 
4642   CallArgs callArgs = CallArgsFromVp(argc, vp);
4643 
4644   if (!ResolveResponse(cx, callArgs, promise)) {
4645     return RejectWithPendingException(cx, promise, callArgs);
4646   }
4647 
4648   callArgs.rval().setObject(*promise);
4649   return true;
4650 }
4651 
WebAssembly_instantiateStreaming(JSContext * cx,unsigned argc,Value * vp)4652 static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
4653                                              Value* vp) {
4654   if (!EnsureStreamSupport(cx)) {
4655     return false;
4656   }
4657 
4658   Log(cx, "async instantiateStreaming() started");
4659 
4660   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4661   if (!promise) {
4662     return false;
4663   }
4664 
4665   CallArgs callArgs = CallArgsFromVp(argc, vp);
4666 
4667   RootedObject firstArg(cx);
4668   RootedObject importObj(cx);
4669   if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
4670     return RejectWithPendingException(cx, promise, callArgs);
4671   }
4672 
4673   if (!ResolveResponse(cx, callArgs, promise, true, importObj)) {
4674     return RejectWithPendingException(cx, promise, callArgs);
4675   }
4676 
4677   callArgs.rval().setObject(*promise);
4678   return true;
4679 }
4680 
4681 static const JSFunctionSpec WebAssembly_static_methods[] = {
4682     JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
4683     JS_FN("compile", WebAssembly_compile, 1, JSPROP_ENUMERATE),
4684     JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE),
4685     JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE),
4686     JS_FN("compileStreaming", WebAssembly_compileStreaming, 1,
4687           JSPROP_ENUMERATE),
4688     JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1,
4689           JSPROP_ENUMERATE),
4690     JS_FS_END};
4691 
CreateWebAssemblyObject(JSContext * cx,JSProtoKey key)4692 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
4693   MOZ_RELEASE_ASSERT(HasSupport(cx));
4694 
4695   Handle<GlobalObject*> global = cx->global();
4696   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
4697   if (!proto) {
4698     return nullptr;
4699   }
4700   return NewTenuredObjectWithGivenProto(cx, &WasmNamespaceObject::class_,
4701                                         proto);
4702 }
4703 
WebAssemblyClassFinish(JSContext * cx,HandleObject object,HandleObject proto)4704 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object,
4705                                    HandleObject proto) {
4706   Handle<WasmNamespaceObject*> wasm = object.as<WasmNamespaceObject>();
4707 
4708   struct NameAndProtoKey {
4709     const char* const name;
4710     JSProtoKey key;
4711   };
4712 
4713   constexpr NameAndProtoKey entries[] = {
4714       {"Module", JSProto_WasmModule},
4715       {"Instance", JSProto_WasmInstance},
4716       {"Memory", JSProto_WasmMemory},
4717       {"Table", JSProto_WasmTable},
4718       {"Global", JSProto_WasmGlobal},
4719 #ifdef ENABLE_WASM_EXCEPTIONS
4720       {"Exception", JSProto_WasmException},
4721       {"RuntimeException", JSProto_WasmRuntimeException},
4722 #endif
4723       {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
4724       {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
4725       {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
4726   };
4727 
4728   RootedValue ctorValue(cx);
4729   RootedId id(cx);
4730   for (const auto& entry : entries) {
4731     const char* name = entry.name;
4732     JSProtoKey key = entry.key;
4733 
4734     JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, key);
4735     if (!ctor) {
4736       return false;
4737     }
4738     ctorValue.setObject(*ctor);
4739 
4740     JSAtom* className = Atomize(cx, name, strlen(name));
4741     if (!className) {
4742       return false;
4743     }
4744     id.set(AtomToId(className));
4745 
4746     if (!DefineDataProperty(cx, wasm, id, ctorValue, 0)) {
4747       return false;
4748     }
4749   }
4750 
4751   return true;
4752 }
4753 
4754 static const ClassSpec WebAssemblyClassSpec = {CreateWebAssemblyObject,
4755                                                nullptr,
4756                                                WebAssembly_static_methods,
4757                                                nullptr,
4758                                                nullptr,
4759                                                nullptr,
4760                                                WebAssemblyClassFinish};
4761 
4762 const JSClass js::WasmNamespaceObject::class_ = {
4763     js_WebAssembly_str, JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly),
4764     JS_NULL_CLASS_OPS, &WebAssemblyClassSpec};
4765 
4766 // Sundry
4767 
4768 #ifdef JS_64BIT
4769 #  ifdef ENABLE_WASM_CRANELIFT
4770 // TODO (large ArrayBuffer): Cranelift needs to be updated to use more than the
4771 // low 32 bits of the boundsCheckLimit, so for now we limit its heap size to
4772 // something that satisfies the 32-bit invariants.
4773 //
4774 // The "-2" here accounts for the !huge-memory case in CreateSpecificWasmBuffer,
4775 // which is guarding against an overflow.  Also see
4776 // WasmMemoryObject::boundsCheckLimit() for related assertions.
MaxMemory32Pages()4777 wasm::Pages wasm::MaxMemory32Pages() {
4778   size_t desired = MaxMemory32LimitField - 2;
4779   size_t actual = ArrayBufferObject::maxBufferByteLength() / PageSize;
4780   return wasm::Pages(std::min(desired, actual));
4781 }
4782 
MaxMemory32BoundsCheckLimit()4783 size_t wasm::MaxMemory32BoundsCheckLimit() {
4784   return UINT32_MAX - 2 * PageSize + 1;
4785 }
4786 #  else
MaxMemory32Pages()4787 wasm::Pages wasm::MaxMemory32Pages() {
4788   size_t desired = MaxMemory32LimitField;
4789   size_t actual = ArrayBufferObject::maxBufferByteLength() / PageSize;
4790   return wasm::Pages(std::min(desired, actual));
4791 }
4792 
MaxMemory32BoundsCheckLimit()4793 size_t wasm::MaxMemory32BoundsCheckLimit() { return size_t(UINT32_MAX) + 1; }
4794 #  endif
4795 #else
MaxMemory32Pages()4796 wasm::Pages wasm::MaxMemory32Pages() {
4797   MOZ_ASSERT(ArrayBufferObject::maxBufferByteLength() >= INT32_MAX / PageSize);
4798   return wasm::Pages(INT32_MAX / PageSize);
4799 }
4800 
MaxMemory32BoundsCheckLimit()4801 size_t wasm::MaxMemory32BoundsCheckLimit() { return size_t(INT32_MAX) + 1; }
4802 #endif
4803