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 "builtin/TypedObject.h"
29 #include "gc/FreeOp.h"
30 #include "jit/AtomicOperations.h"
31 #include "jit/JitOptions.h"
32 #include "jit/Simulator.h"
33 #include "js/Printf.h"
34 #include "js/PropertySpec.h"  // JS_{PS,FN}{,_END}
35 #if defined(JS_CODEGEN_X64)   // Assembler::HasSSE41
36 #  include "jit/x64/Assembler-x64.h"
37 #  include "jit/x86-shared/Architecture-x86-shared.h"
38 #  include "jit/x86-shared/Assembler-x86-shared.h"
39 #endif
40 #include "util/StringBuffer.h"
41 #include "util/Text.h"
42 #include "vm/ErrorObject.h"
43 #include "vm/FunctionFlags.h"  // js::FunctionFlags
44 #include "vm/GlobalObject.h"   // js::GlobalObject
45 #include "vm/Interpreter.h"
46 #include "vm/PlainObject.h"    // js::PlainObject
47 #include "vm/PromiseObject.h"  // js::PromiseObject
48 #include "vm/StringType.h"
49 #include "vm/Warnings.h"  // js::WarnNumberASCII
50 #include "wasm/WasmBaselineCompile.h"
51 #include "wasm/WasmCompile.h"
52 #include "wasm/WasmCraneliftCompile.h"
53 #include "wasm/WasmInstance.h"
54 #include "wasm/WasmIonCompile.h"
55 #include "wasm/WasmModule.h"
56 #include "wasm/WasmProcess.h"
57 #include "wasm/WasmSignalHandlers.h"
58 #include "wasm/WasmStubs.h"
59 #include "wasm/WasmValidate.h"
60 
61 #include "vm/ArrayBufferObject-inl.h"
62 #include "vm/JSObject-inl.h"
63 #include "vm/NativeObject-inl.h"
64 
65 using namespace js;
66 using namespace js::jit;
67 using namespace js::wasm;
68 
69 using mozilla::CheckedInt;
70 using mozilla::MakeSpan;
71 using mozilla::Nothing;
72 using mozilla::RangedPtr;
73 
74 extern mozilla::Atomic<bool> fuzzingSafe;
75 
WasmMultiValueFlag(JSContext * cx)76 static inline bool WasmMultiValueFlag(JSContext* cx) {
77 #ifdef ENABLE_WASM_MULTI_VALUE
78   return cx->options().wasmMultiValue();
79 #else
80   return false;
81 #endif
82 }
83 
WasmSimdFlag(JSContext * cx)84 static inline bool WasmSimdFlag(JSContext* cx) {
85 #ifdef ENABLE_WASM_SIMD
86   return cx->options().wasmSimd() && js::jit::JitSupportsWasmSimd();
87 #else
88   return false;
89 #endif
90 }
91 
92 // Compiler availability predicates.  These must be kept in sync with the
93 // feature predicates in the next section below.
94 //
95 // These can't call the feature predicates since the feature predicates call
96 // back to these predicates.  So there will be a small amount of duplicated
97 // logic here, but as compilers reach feature parity that duplication will go
98 // away.
99 //
100 // There's a static precedence order between the optimizing compilers.  This
101 // order currently ranks Cranelift over Ion on all platforms because Cranelift
102 // is disabled by default on all platforms: anyone who has enabled Cranelift
103 // will wish to use it instead of Ion.
104 //
105 // The precedence order is implemented by guards in IonAvailable() and
106 // CraneliftAvailable().  We expect that it will become more complex as the
107 // default settings change.  But it should remain static.
108 
BaselineAvailable(JSContext * cx)109 bool wasm::BaselineAvailable(JSContext* cx) {
110   // Baseline supports every feature supported by any compiler.
111   return cx->options().wasmBaseline() && BaselinePlatformSupport();
112 }
113 
IonAvailable(JSContext * cx)114 bool wasm::IonAvailable(JSContext* cx) {
115   if (!cx->options().wasmIon() || !IonPlatformSupport()) {
116     return false;
117   }
118   bool isDisabled = false;
119   MOZ_ALWAYS_TRUE(IonDisabledByFeatures(cx, &isDisabled));
120   return !isDisabled && !CraneliftAvailable(cx);
121 }
122 
123 template <size_t ArrayLength>
Append(JSStringBuilder * reason,const char (& s)[ArrayLength],char * sep)124 static inline bool Append(JSStringBuilder* reason, const char (&s)[ArrayLength],
125                           char* sep) {
126   if ((*sep && !reason->append(*sep)) || !reason->append(s)) {
127     return false;
128   }
129   *sep = ',';
130   return true;
131 }
132 
IonDisabledByFeatures(JSContext * cx,bool * isDisabled,JSStringBuilder * reason)133 bool wasm::IonDisabledByFeatures(JSContext* cx, bool* isDisabled,
134                                  JSStringBuilder* reason) {
135   // Ion has no debugging support, no gc support.
136   bool debug = cx->realm() && cx->realm()->debuggerObservesAsmJS();
137   bool gc = cx->options().wasmGc();
138   if (reason) {
139     char sep = 0;
140     if (debug && !Append(reason, "debug", &sep)) {
141       return false;
142     }
143     if (gc && !Append(reason, "gc", &sep)) {
144       return false;
145     }
146   }
147   *isDisabled = debug || gc;
148   return true;
149 }
150 
CraneliftAvailable(JSContext * cx)151 bool wasm::CraneliftAvailable(JSContext* cx) {
152   if (!cx->options().wasmCranelift() || !CraneliftPlatformSupport()) {
153     return false;
154   }
155   bool isDisabled = false;
156   MOZ_ALWAYS_TRUE(CraneliftDisabledByFeatures(cx, &isDisabled));
157   return !isDisabled;
158 }
159 
CraneliftDisabledByFeatures(JSContext * cx,bool * isDisabled,JSStringBuilder * reason)160 bool wasm::CraneliftDisabledByFeatures(JSContext* cx, bool* isDisabled,
161                                        JSStringBuilder* reason) {
162   // Cranelift has no debugging support, no gc support, no multi-value support,
163   // no threads, no simd, and on ARM64, no reference types.
164   bool debug = cx->realm() && cx->realm()->debuggerObservesAsmJS();
165   bool gc = cx->options().wasmGc();
166   bool multiValue = WasmMultiValueFlag(cx);
167   bool threads =
168       cx->realm() &&
169       cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
170 #if defined(JS_CODEGEN_ARM64)
171   bool reftypesOnArm64 = cx->options().wasmReftypes();
172 #else
173   // On other platforms, assume reftypes has been implemented.
174   bool reftypesOnArm64 = false;
175 #endif
176   bool simd = WasmSimdFlag(cx);
177   if (reason) {
178     char sep = 0;
179     if (debug && !Append(reason, "debug", &sep)) {
180       return false;
181     }
182     if (gc && !Append(reason, "gc", &sep)) {
183       return false;
184     }
185     if (multiValue && !Append(reason, "multi-value", &sep)) {
186       return false;
187     }
188     if (threads && !Append(reason, "threads", &sep)) {
189       return false;
190     }
191     if (reftypesOnArm64 && !Append(reason, "reftypes", &sep)) {
192       return false;
193     }
194     if (simd && !Append(reason, "simd", &sep)) {
195       return false;
196     }
197   }
198   *isDisabled = debug || gc || multiValue || threads || reftypesOnArm64 || simd;
199   return true;
200 }
201 
202 // Feature predicates.  These must be kept in sync with the predicates in the
203 // section above.
204 //
205 // The meaning of these predicates is tricky: A predicate is true for a feature
206 // if the feature is enabled and/or compiled-in *and* we have *at least one*
207 // compiler that can support the feature.  Subsequent compiler selection must
208 // ensure that only compilers that actually support the feature are used.
209 
ReftypesAvailable(JSContext * cx)210 bool wasm::ReftypesAvailable(JSContext* cx) {
211   // All compilers support reference types, except Cranelift on ARM64.
212 #ifdef ENABLE_WASM_REFTYPES
213   return cx->options().wasmReftypes() &&
214          (BaselineAvailable(cx) || IonAvailable(cx) || CraneliftAvailable(cx));
215 #else
216   return false;
217 #endif
218 }
219 
GcTypesAvailable(JSContext * cx)220 bool wasm::GcTypesAvailable(JSContext* cx) {
221   // Cranelift and Ion do not support GC.
222   return cx->options().wasmGc() && BaselineAvailable(cx);
223 }
224 
MultiValuesAvailable(JSContext * cx)225 bool wasm::MultiValuesAvailable(JSContext* cx) {
226   // Cranelift does not support multi-value.
227   return WasmMultiValueFlag(cx) && (BaselineAvailable(cx) || IonAvailable(cx));
228 }
229 
SimdAvailable(JSContext * cx)230 bool wasm::SimdAvailable(JSContext* cx) {
231   // Cranelift does not support SIMD.
232   return WasmSimdFlag(cx) && (BaselineAvailable(cx) || IonAvailable(cx));
233 }
234 
ThreadsAvailable(JSContext * cx)235 bool wasm::ThreadsAvailable(JSContext* cx) {
236   // Cranelift does not support atomics.
237   return cx->realm() &&
238          cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled() &&
239          (BaselineAvailable(cx) || IonAvailable(cx));
240 }
241 
HasPlatformSupport(JSContext * cx)242 bool wasm::HasPlatformSupport(JSContext* cx) {
243 #if !MOZ_LITTLE_ENDIAN() || defined(JS_CODEGEN_NONE)
244   return false;
245 #endif
246 
247   if (gc::SystemPageSize() > wasm::PageSize) {
248     return false;
249   }
250 
251   if (!JitOptions.supportsFloatingPoint) {
252     return false;
253   }
254 
255   if (!JitOptions.supportsUnalignedAccesses) {
256     return false;
257   }
258 
259   if (!wasm::EnsureFullSignalHandlers(cx)) {
260     return false;
261   }
262 
263   // Wasm threads require 8-byte lock-free atomics.
264   if (!jit::AtomicOperations::isLockfree8()) {
265     return false;
266   }
267 
268 #ifdef JS_SIMULATOR
269   if (!Simulator::supportsAtomics()) {
270     return false;
271   }
272 #endif
273 
274   // Test only whether the compilers are supported on the hardware, not whether
275   // they are enabled.
276   return BaselinePlatformSupport() || IonPlatformSupport() ||
277          CraneliftPlatformSupport();
278 }
279 
HasSupport(JSContext * cx)280 bool wasm::HasSupport(JSContext* cx) {
281   // If the general wasm pref is on, it's on for everything.
282   bool prefEnabled = cx->options().wasm();
283   // If the general pref is off, check trusted principals.
284   if (MOZ_UNLIKELY(!prefEnabled)) {
285     prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() &&
286                   cx->realm()->principals() &&
287                   cx->realm()->principals()->isSystemOrAddonPrincipal();
288   }
289   return prefEnabled && HasPlatformSupport(cx) &&
290          (BaselineAvailable(cx) || IonAvailable(cx) || CraneliftAvailable(cx));
291 }
292 
StreamingCompilationAvailable(JSContext * cx)293 bool wasm::StreamingCompilationAvailable(JSContext* cx) {
294   // This should match EnsureStreamSupport().
295   return HasSupport(cx) &&
296          cx->runtime()->offThreadPromiseState.ref().initialized() &&
297          CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
298          cx->runtime()->reportStreamErrorCallback;
299 }
300 
CodeCachingAvailable(JSContext * cx)301 bool wasm::CodeCachingAvailable(JSContext* cx) {
302   // At the moment, we require Ion support for code caching.  The main reason
303   // for this is that wasm::CompileAndSerialize() does not have access to
304   // information about which optimizing compiler it should use.  See comments in
305   // CompileAndSerialize(), below.
306   return StreamingCompilationAvailable(cx) && IonAvailable(cx);
307 }
308 
CheckRefType(JSContext * cx,RefType::Kind targetTypeKind,HandleValue v,MutableHandleFunction fnval,MutableHandleAnyRef refval)309 bool wasm::CheckRefType(JSContext* cx, RefType::Kind targetTypeKind,
310                         HandleValue v, MutableHandleFunction fnval,
311                         MutableHandleAnyRef refval) {
312   switch (targetTypeKind) {
313     case RefType::Func:
314       if (!CheckFuncRefValue(cx, v, fnval)) {
315         return false;
316       }
317       break;
318     case RefType::Any:
319       if (!BoxAnyRef(cx, v, refval)) {
320         return false;
321       }
322       break;
323     case RefType::TypeIndex:
324       MOZ_CRASH("temporarily unsupported Ref type");
325   }
326   return true;
327 }
328 
ToWebAssemblyValue(JSContext * cx,ValType targetType,HandleValue v,MutableHandleVal val)329 static bool ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v,
330                                MutableHandleVal val) {
331   switch (targetType.kind()) {
332     case ValType::I32: {
333       int32_t i32;
334       if (!ToInt32(cx, v, &i32)) {
335         return false;
336       }
337       val.set(Val(uint32_t(i32)));
338       return true;
339     }
340     case ValType::F32: {
341       double d;
342       if (!ToNumber(cx, v, &d)) {
343         return false;
344       }
345       val.set(Val(float(d)));
346       return true;
347     }
348     case ValType::F64: {
349       double d;
350       if (!ToNumber(cx, v, &d)) {
351         return false;
352       }
353       val.set(Val(d));
354       return true;
355     }
356     case ValType::I64: {
357       BigInt* bigint = ToBigInt(cx, v);
358       if (!bigint) {
359         return false;
360       }
361       val.set(Val(BigInt::toUint64(bigint)));
362       return true;
363     }
364     case ValType::Ref: {
365       RootedFunction fun(cx);
366       RootedAnyRef any(cx, AnyRef::null());
367       if (!CheckRefType(cx, targetType.refTypeKind(), v, &fun, &any)) {
368         return false;
369       }
370       switch (targetType.refTypeKind()) {
371         case RefType::Func:
372           val.set(Val(RefType::func(), FuncRef::fromJSFunction(fun)));
373           return true;
374         case RefType::Any:
375           val.set(Val(targetType.refType(), any));
376           return true;
377         case RefType::TypeIndex:
378           break;
379       }
380       break;
381     }
382     case ValType::V128: {
383       break;
384     }
385   }
386   MOZ_CRASH("unexpected import value type, caller must guard");
387 }
388 
ToJSValue(JSContext * cx,const Val & val,MutableHandleValue out)389 static bool ToJSValue(JSContext* cx, const Val& val, MutableHandleValue out) {
390   switch (val.type().kind()) {
391     case ValType::I32:
392       out.setInt32(val.i32());
393       return true;
394     case ValType::F32:
395       out.setDouble(JS::CanonicalizeNaN(double(val.f32())));
396       return true;
397     case ValType::F64:
398       out.setDouble(JS::CanonicalizeNaN(val.f64()));
399       return true;
400     case ValType::I64: {
401       BigInt* bi = BigInt::createFromInt64(cx, val.i64());
402       if (!bi) {
403         return false;
404       }
405       out.setBigInt(bi);
406       return true;
407     }
408     case ValType::Ref:
409       switch (val.type().refTypeKind()) {
410         case RefType::Func:
411           out.set(UnboxFuncRef(FuncRef::fromAnyRefUnchecked(val.ref())));
412           return true;
413         case RefType::Any:
414           out.set(UnboxAnyRef(val.ref()));
415           return true;
416         case RefType::TypeIndex:
417           break;
418       }
419       break;
420     case ValType::V128:
421       break;
422   }
423   MOZ_CRASH("unexpected type when translating to a JS value");
424 }
425 
426 // ============================================================================
427 // Imports
428 
ThrowBadImportArg(JSContext * cx)429 static bool ThrowBadImportArg(JSContext* cx) {
430   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
431                            JSMSG_WASM_BAD_IMPORT_ARG);
432   return false;
433 }
434 
ThrowBadImportType(JSContext * cx,const char * field,const char * str)435 static bool ThrowBadImportType(JSContext* cx, const char* field,
436                                const char* str) {
437   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
438                            JSMSG_WASM_BAD_IMPORT_TYPE, field, str);
439   return false;
440 }
441 
GetProperty(JSContext * cx,HandleObject obj,const char * chars,MutableHandleValue v)442 static bool GetProperty(JSContext* cx, HandleObject obj, const char* chars,
443                         MutableHandleValue v) {
444   JSAtom* atom = AtomizeUTF8Chars(cx, chars, strlen(chars));
445   if (!atom) {
446     return false;
447   }
448 
449   RootedId id(cx, AtomToId(atom));
450   return GetProperty(cx, obj, obj, id, v);
451 }
452 
GetImports(JSContext * cx,const Module & module,HandleObject importObj,ImportValues * imports)453 bool js::wasm::GetImports(JSContext* cx, const Module& module,
454                           HandleObject importObj, ImportValues* imports) {
455   if (!module.imports().empty() && !importObj) {
456     return ThrowBadImportArg(cx);
457   }
458 
459   const Metadata& metadata = module.metadata();
460 
461   uint32_t globalIndex = 0;
462   const GlobalDescVector& globals = metadata.globals;
463   uint32_t tableIndex = 0;
464   const TableDescVector& tables = metadata.tables;
465   for (const Import& import : module.imports()) {
466     RootedValue v(cx);
467     if (!GetProperty(cx, importObj, import.module.get(), &v)) {
468       return false;
469     }
470 
471     if (!v.isObject()) {
472       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
473                                JSMSG_WASM_BAD_IMPORT_FIELD,
474                                import.module.get());
475       return false;
476     }
477 
478     RootedObject obj(cx, &v.toObject());
479     if (!GetProperty(cx, obj, import.field.get(), &v)) {
480       return false;
481     }
482 
483     switch (import.kind) {
484       case DefinitionKind::Function: {
485         if (!IsFunctionObject(v)) {
486           return ThrowBadImportType(cx, import.field.get(), "Function");
487         }
488 
489         if (!imports->funcs.append(&v.toObject().as<JSFunction>())) {
490           return false;
491         }
492 
493         break;
494       }
495       case DefinitionKind::Table: {
496         const uint32_t index = tableIndex++;
497         if (!v.isObject() || !v.toObject().is<WasmTableObject>()) {
498           return ThrowBadImportType(cx, import.field.get(), "Table");
499         }
500 
501         RootedWasmTableObject obj(cx, &v.toObject().as<WasmTableObject>());
502         if (obj->table().kind() != tables[index].kind) {
503           JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
504                                    JSMSG_WASM_BAD_TBL_TYPE_LINK);
505           return false;
506         }
507 
508         if (!imports->tables.append(obj)) {
509           return false;
510         }
511         break;
512       }
513       case DefinitionKind::Memory: {
514         if (!v.isObject() || !v.toObject().is<WasmMemoryObject>()) {
515           return ThrowBadImportType(cx, import.field.get(), "Memory");
516         }
517 
518         MOZ_ASSERT(!imports->memory);
519         imports->memory = &v.toObject().as<WasmMemoryObject>();
520         break;
521       }
522       case DefinitionKind::Global: {
523         const uint32_t index = globalIndex++;
524         const GlobalDesc& global = globals[index];
525         MOZ_ASSERT(global.importIndex() == index);
526 
527         RootedVal val(cx);
528         if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
529           RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
530 
531           if (obj->isMutable() != global.isMutable()) {
532             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
533                                      JSMSG_WASM_BAD_GLOB_MUT_LINK);
534             return false;
535           }
536           if (obj->type() != global.type()) {
537             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
538                                      JSMSG_WASM_BAD_GLOB_TYPE_LINK);
539             return false;
540           }
541 
542           if (imports->globalObjs.length() <= index &&
543               !imports->globalObjs.resize(index + 1)) {
544             ReportOutOfMemory(cx);
545             return false;
546           }
547           imports->globalObjs[index] = obj;
548           obj->val(&val);
549         } else {
550           if (IsNumberType(global.type())) {
551             if (global.type() == ValType::I64 && !v.isBigInt()) {
552               return ThrowBadImportType(cx, import.field.get(), "BigInt");
553             }
554             if (global.type() != ValType::I64 && !v.isNumber()) {
555               return ThrowBadImportType(cx, import.field.get(), "Number");
556             }
557           } else {
558             MOZ_ASSERT(global.type().isReference());
559             if (!global.type().isAnyRef() && !v.isObjectOrNull()) {
560               return ThrowBadImportType(cx, import.field.get(),
561                                         "Object-or-null value required for "
562                                         "non-externref reference type");
563             }
564           }
565 
566           if (global.isMutable()) {
567             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
568                                      JSMSG_WASM_BAD_GLOB_MUT_LINK);
569             return false;
570           }
571 
572           if (global.type() == ValType::V128) {
573             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
574                                      JSMSG_WASM_BAD_VAL_TYPE);
575             return false;
576           }
577 
578           if (!ToWebAssemblyValue(cx, global.type(), v, &val)) {
579             return false;
580           }
581         }
582 
583         if (!imports->globalValues.append(val)) {
584           return false;
585         }
586 
587         break;
588       }
589     }
590   }
591 
592   MOZ_ASSERT(globalIndex == globals.length() ||
593              !globals[globalIndex].isImport());
594 
595   return true;
596 }
597 
DescribeScriptedCaller(JSContext * cx,ScriptedCaller * caller,const char * introducer)598 static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller,
599                                    const char* introducer) {
600   // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
601   // found, not whether an error was thrown. This wrapper function converts
602   // back to the more ordinary false-if-error form.
603 
604   JS::AutoFilename af;
605   if (JS::DescribeScriptedCaller(cx, &af, &caller->line)) {
606     caller->filename =
607         FormatIntroducedFilename(cx, af.get(), caller->line, introducer);
608     if (!caller->filename) {
609       return false;
610     }
611   }
612 
613   return true;
614 }
615 
616 // ============================================================================
617 // Testing / Fuzzing support
618 
Eval(JSContext * cx,Handle<TypedArrayObject * > code,HandleObject importObj,MutableHandleWasmInstanceObject instanceObj)619 bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code,
620                 HandleObject importObj,
621                 MutableHandleWasmInstanceObject instanceObj) {
622   if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
623     return false;
624   }
625 
626   MutableBytes bytecode = cx->new_<ShareableBytes>();
627   if (!bytecode) {
628     return false;
629   }
630 
631   if (!bytecode->append((uint8_t*)code->dataPointerEither().unwrap(),
632                         code->byteLength())) {
633     ReportOutOfMemory(cx);
634     return false;
635   }
636 
637   ScriptedCaller scriptedCaller;
638   if (!DescribeScriptedCaller(cx, &scriptedCaller, "wasm_eval")) {
639     return false;
640   }
641 
642   SharedCompileArgs compileArgs =
643       CompileArgs::build(cx, std::move(scriptedCaller));
644   if (!compileArgs) {
645     return false;
646   }
647 
648   UniqueChars error;
649   UniqueCharsVector warnings;
650   SharedModule module =
651       CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
652   if (!module) {
653     if (error) {
654       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
655                                JSMSG_WASM_COMPILE_ERROR, error.get());
656       return false;
657     }
658     ReportOutOfMemory(cx);
659     return false;
660   }
661 
662   Rooted<ImportValues> imports(cx);
663   if (!GetImports(cx, *module, importObj, imports.address())) {
664     return false;
665   }
666 
667   return module->instantiate(cx, imports.get(), nullptr, instanceObj);
668 }
669 
670 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
671   // MOZ_STACK_CLASS means these can be nops.
AddRefSerializeListener672   MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
ReleaseSerializeListener673   MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
674 
675   DebugOnly<bool> called = false;
676   Bytes* serialized;
SerializeListenerSerializeListener677   explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
678 
storeOptimizedEncodingSerializeListener679   void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes bytes) override {
680     MOZ_ASSERT(!called);
681     called = true;
682     if (serialized->resize(bytes->length())) {
683       memcpy(serialized->begin(), bytes->begin(), bytes->length());
684     }
685   }
686 };
687 
CompileAndSerialize(const ShareableBytes & bytecode,Bytes * serialized)688 bool wasm::CompileAndSerialize(const ShareableBytes& bytecode,
689                                Bytes* serialized) {
690   MutableCompileArgs compileArgs = js_new<CompileArgs>(ScriptedCaller());
691   if (!compileArgs) {
692     return false;
693   }
694 
695   // The caller has ensured CodeCachingAvailable(). Moreover, we want to ensure
696   // we go straight to tier-2 so that we synchronously call
697   // JS::OptimizedEncodingListener::storeOptimizedEncoding().
698   compileArgs->baselineEnabled = false;
699 
700   // We always pick Ion here, and we depend on CodeCachingAvailable() having
701   // determined that Ion is available, see comments at CodeCachingAvailable().
702   // To do better, we need to pass information about which compiler that should
703   // be used into CompileAndSerialize().
704   compileArgs->ionEnabled = true;
705 
706   // The caller must ensure that huge memory support is configured the same in
707   // the receiving process of this serialized module.
708   compileArgs->hugeMemory = wasm::IsHugeMemoryEnabled();
709 
710   SerializeListener listener(serialized);
711 
712   UniqueChars error;
713   UniqueCharsVector warnings;
714   SharedModule module =
715       CompileBuffer(*compileArgs, bytecode, &error, &warnings, &listener);
716   if (!module) {
717     fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
718     return false;
719   }
720 
721   MOZ_ASSERT(module->code().hasTier(Tier::Serialized));
722   MOZ_ASSERT(listener.called);
723   return !listener.serialized->empty();
724 }
725 
DeserializeModule(JSContext * cx,const Bytes & serialized,MutableHandleObject moduleObj)726 bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized,
727                              MutableHandleObject moduleObj) {
728   MutableModule module =
729       Module::deserialize(serialized.begin(), serialized.length());
730   if (!module) {
731     ReportOutOfMemory(cx);
732     return false;
733   }
734 
735   moduleObj.set(module->createObject(cx));
736   return !!moduleObj;
737 }
738 
739 // ============================================================================
740 // Common functions
741 
742 // '[EnforceRange] unsigned long' types are coerced with
743 //    ConvertToInt(v, 32, 'unsigned')
744 // defined in Web IDL Section 3.2.4.9.
EnforceRangeU32(JSContext * cx,HandleValue v,const char * kind,const char * noun,uint32_t * u32)745 static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
746                             const char* noun, uint32_t* u32) {
747   // Step 4.
748   double x;
749   if (!ToNumber(cx, v, &x)) {
750     return false;
751   }
752 
753   // Step 5.
754   if (mozilla::IsNegativeZero(x)) {
755     x = 0.0;
756   }
757 
758   // Step 6.1.
759   if (!mozilla::IsFinite(x)) {
760     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
761                              JSMSG_WASM_BAD_UINT32, kind, noun);
762     return false;
763   }
764 
765   // Step 6.2.
766   x = JS::ToInteger(x);
767 
768   // Step 6.3.
769   if (x < 0 || x > double(UINT32_MAX)) {
770     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
771                              JSMSG_WASM_BAD_UINT32, kind, noun);
772     return false;
773   }
774 
775   *u32 = uint32_t(x);
776   MOZ_ASSERT(double(*u32) == x);
777   return true;
778 }
779 
GetLimits(JSContext * cx,HandleObject obj,uint32_t maxInitial,uint32_t maxMaximum,const char * kind,Limits * limits,Shareable allowShared)780 static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial,
781                       uint32_t maxMaximum, const char* kind, Limits* limits,
782                       Shareable allowShared) {
783   JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
784   if (!initialAtom) {
785     return false;
786   }
787   RootedId initialId(cx, AtomToId(initialAtom));
788 
789   RootedValue initialVal(cx);
790   if (!GetProperty(cx, obj, obj, initialId, &initialVal)) {
791     return false;
792   }
793 
794   if (!EnforceRangeU32(cx, initialVal, kind, "initial size",
795                        &limits->initial)) {
796     return false;
797   }
798 
799   if (limits->initial > maxInitial) {
800     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
801                              kind, "initial size");
802     return false;
803   }
804 
805   JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
806   if (!maximumAtom) {
807     return false;
808   }
809   RootedId maximumId(cx, AtomToId(maximumAtom));
810 
811   RootedValue maxVal(cx);
812   if (!GetProperty(cx, obj, obj, maximumId, &maxVal)) {
813     return false;
814   }
815 
816   // maxVal does not have a default value.
817   if (!maxVal.isUndefined()) {
818     limits->maximum.emplace();
819     if (!EnforceRangeU32(cx, maxVal, kind, "maximum size",
820                          limits->maximum.ptr())) {
821       return false;
822     }
823 
824     if (*limits->maximum > maxMaximum || limits->initial > *limits->maximum) {
825       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
826                                JSMSG_WASM_BAD_RANGE, kind, "maximum size");
827       return false;
828     }
829   }
830 
831   limits->shared = Shareable::False;
832 
833   if (allowShared == Shareable::True) {
834     JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
835     if (!sharedAtom) {
836       return false;
837     }
838     RootedId sharedId(cx, AtomToId(sharedAtom));
839 
840     RootedValue sharedVal(cx);
841     if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) {
842       return false;
843     }
844 
845     // shared's default value is false, which is already the value set above.
846     if (!sharedVal.isUndefined()) {
847       limits->shared =
848           ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
849 
850       if (limits->shared == Shareable::True) {
851         if (maxVal.isUndefined()) {
852           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
853                                     JSMSG_WASM_MISSING_MAXIMUM, kind);
854           return false;
855         }
856 
857         if (!cx->realm()
858                  ->creationOptions()
859                  .getSharedMemoryAndAtomicsEnabled()) {
860           JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
861                                     JSMSG_WASM_NO_SHMEM_LINK);
862           return false;
863         }
864       }
865     }
866   }
867 
868   return true;
869 }
870 
871 template <class Class, const char* name>
CreateWasmConstructor(JSContext * cx,JSProtoKey key)872 static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
873   RootedAtom className(cx, Atomize(cx, name, strlen(name)));
874   if (!className) {
875     return nullptr;
876   }
877 
878   return NewNativeConstructor(cx, Class::construct, 1, className);
879 }
880 
881 // ============================================================================
882 // WebAssembly.Module class and methods
883 
884 const JSClassOps WasmModuleObject::classOps_ = {
885     nullptr,                     // addProperty
886     nullptr,                     // delProperty
887     nullptr,                     // enumerate
888     nullptr,                     // newEnumerate
889     nullptr,                     // resolve
890     nullptr,                     // mayResolve
891     WasmModuleObject::finalize,  // finalize
892     nullptr,                     // call
893     nullptr,                     // hasInstance
894     nullptr,                     // construct
895     nullptr,                     // trace
896 };
897 
898 const JSClass WasmModuleObject::class_ = {
899     "WebAssembly.Module",
900     JSCLASS_DELAY_METADATA_BUILDER |
901         JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
902         JSCLASS_FOREGROUND_FINALIZE,
903     &WasmModuleObject::classOps_,
904     &WasmModuleObject::classSpec_,
905 };
906 
907 const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
908 
909 static constexpr char WasmModuleName[] = "Module";
910 
911 const ClassSpec WasmModuleObject::classSpec_ = {
912     CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
913     GenericCreatePrototype<WasmModuleObject>,
914     WasmModuleObject::static_methods,
915     nullptr,
916     WasmModuleObject::methods,
917     WasmModuleObject::properties,
918     nullptr,
919     ClassSpec::DontDefineConstructor};
920 
921 const JSPropertySpec WasmModuleObject::properties[] = {
922     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
923     JS_PS_END};
924 
925 const JSFunctionSpec WasmModuleObject::methods[] = {JS_FS_END};
926 
927 const JSFunctionSpec WasmModuleObject::static_methods[] = {
928     JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
929     JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
930     JS_FN("customSections", WasmModuleObject::customSections, 2,
931           JSPROP_ENUMERATE),
932     JS_FS_END};
933 
934 /* static */
finalize(JSFreeOp * fop,JSObject * obj)935 void WasmModuleObject::finalize(JSFreeOp* fop, JSObject* obj) {
936   const Module& module = obj->as<WasmModuleObject>().module();
937   obj->zone()->decJitMemory(module.codeLength(module.code().stableTier()));
938   fop->release(obj, &module, module.gcMallocBytesExcludingCode(),
939                MemoryUse::WasmModule);
940 }
941 
IsModuleObject(JSObject * obj,const Module ** module)942 static bool IsModuleObject(JSObject* obj, const Module** module) {
943   WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>();
944   if (!mobj) {
945     return false;
946   }
947 
948   *module = &mobj->module();
949   return true;
950 }
951 
GetModuleArg(JSContext * cx,CallArgs args,uint32_t numRequired,const char * name,const Module ** module)952 static bool GetModuleArg(JSContext* cx, CallArgs args, uint32_t numRequired,
953                          const char* name, const Module** module) {
954   if (!args.requireAtLeast(cx, name, numRequired)) {
955     return false;
956   }
957 
958   if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
959     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
960                              JSMSG_WASM_BAD_MOD_ARG);
961     return false;
962   }
963 
964   return true;
965 }
966 
967 struct KindNames {
968   RootedPropertyName kind;
969   RootedPropertyName table;
970   RootedPropertyName memory;
971   RootedPropertyName signature;
972 
KindNamesKindNames973   explicit KindNames(JSContext* cx)
974       : kind(cx), table(cx), memory(cx), signature(cx) {}
975 };
976 
InitKindNames(JSContext * cx,KindNames * names)977 static bool InitKindNames(JSContext* cx, KindNames* names) {
978   JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
979   if (!kind) {
980     return false;
981   }
982   names->kind = kind->asPropertyName();
983 
984   JSAtom* table = Atomize(cx, "table", strlen("table"));
985   if (!table) {
986     return false;
987   }
988   names->table = table->asPropertyName();
989 
990   JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
991   if (!memory) {
992     return false;
993   }
994   names->memory = memory->asPropertyName();
995 
996   JSAtom* signature = Atomize(cx, "signature", strlen("signature"));
997   if (!signature) {
998     return false;
999   }
1000   names->signature = signature->asPropertyName();
1001 
1002   return true;
1003 }
1004 
KindToString(JSContext * cx,const KindNames & names,DefinitionKind kind)1005 static JSString* KindToString(JSContext* cx, const KindNames& names,
1006                               DefinitionKind kind) {
1007   switch (kind) {
1008     case DefinitionKind::Function:
1009       return cx->names().function;
1010     case DefinitionKind::Table:
1011       return names.table;
1012     case DefinitionKind::Memory:
1013       return names.memory;
1014     case DefinitionKind::Global:
1015       return cx->names().global;
1016   }
1017 
1018   MOZ_CRASH("invalid kind");
1019 }
1020 
FuncTypeToString(JSContext * cx,const FuncType & funcType)1021 static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) {
1022   JSStringBuilder buf(cx);
1023   if (!buf.append('(')) {
1024     return nullptr;
1025   }
1026 
1027   bool first = true;
1028   for (ValType arg : funcType.args()) {
1029     if (!first && !buf.append(", ", strlen(", "))) {
1030       return nullptr;
1031     }
1032 
1033     const char* argStr = ToCString(arg);
1034     if (!buf.append(argStr, strlen(argStr))) {
1035       return nullptr;
1036     }
1037 
1038     first = false;
1039   }
1040 
1041   if (!buf.append(") -> (", strlen(") -> ("))) {
1042     return nullptr;
1043   }
1044 
1045   first = true;
1046   for (ValType result : funcType.results()) {
1047     if (!first && !buf.append(", ", strlen(", "))) {
1048       return nullptr;
1049     }
1050 
1051     const char* resultStr = ToCString(result);
1052     if (!buf.append(resultStr, strlen(resultStr))) {
1053       return nullptr;
1054     }
1055 
1056     first = false;
1057   }
1058 
1059   if (!buf.append(')')) {
1060     return nullptr;
1061   }
1062 
1063   return buf.finishString();
1064 }
1065 
UTF8CharsToString(JSContext * cx,const char * chars)1066 static JSString* UTF8CharsToString(JSContext* cx, const char* chars) {
1067   return NewStringCopyUTF8Z<CanGC>(cx,
1068                                    JS::ConstUTF8CharsZ(chars, strlen(chars)));
1069 }
1070 
1071 /* static */
imports(JSContext * cx,unsigned argc,Value * vp)1072 bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
1073   CallArgs args = CallArgsFromVp(argc, vp);
1074 
1075   const Module* module;
1076   if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) {
1077     return false;
1078   }
1079 
1080   KindNames names(cx);
1081   if (!InitKindNames(cx, &names)) {
1082     return false;
1083   }
1084 
1085   RootedValueVector elems(cx);
1086   if (!elems.reserve(module->imports().length())) {
1087     return false;
1088   }
1089 
1090   const FuncImportVector& funcImports =
1091       module->metadata(module->code().stableTier()).funcImports;
1092 
1093   size_t numFuncImport = 0;
1094   for (const Import& import : module->imports()) {
1095     Rooted<IdValueVector> props(cx, IdValueVector(cx));
1096     if (!props.reserve(3)) {
1097       return false;
1098     }
1099 
1100     JSString* moduleStr = UTF8CharsToString(cx, import.module.get());
1101     if (!moduleStr) {
1102       return false;
1103     }
1104     props.infallibleAppend(
1105         IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
1106 
1107     JSString* nameStr = UTF8CharsToString(cx, import.field.get());
1108     if (!nameStr) {
1109       return false;
1110     }
1111     props.infallibleAppend(
1112         IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1113 
1114     JSString* kindStr = KindToString(cx, names, import.kind);
1115     if (!kindStr) {
1116       return false;
1117     }
1118     props.infallibleAppend(
1119         IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1120 
1121     if (fuzzingSafe && import.kind == DefinitionKind::Function) {
1122       JSString* ftStr =
1123           FuncTypeToString(cx, funcImports[numFuncImport++].funcType());
1124       if (!ftStr) {
1125         return false;
1126       }
1127       if (!props.append(
1128               IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1129         return false;
1130       }
1131     }
1132 
1133     JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(),
1134                                                 props.length(), GenericObject);
1135     if (!obj) {
1136       return false;
1137     }
1138 
1139     elems.infallibleAppend(ObjectValue(*obj));
1140   }
1141 
1142   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1143   if (!arr) {
1144     return false;
1145   }
1146 
1147   args.rval().setObject(*arr);
1148   return true;
1149 }
1150 
1151 /* static */
exports(JSContext * cx,unsigned argc,Value * vp)1152 bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
1153   CallArgs args = CallArgsFromVp(argc, vp);
1154 
1155   const Module* module;
1156   if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) {
1157     return false;
1158   }
1159 
1160   KindNames names(cx);
1161   if (!InitKindNames(cx, &names)) {
1162     return false;
1163   }
1164 
1165   RootedValueVector elems(cx);
1166   if (!elems.reserve(module->exports().length())) {
1167     return false;
1168   }
1169 
1170   const FuncExportVector& funcExports =
1171       module->metadata(module->code().stableTier()).funcExports;
1172 
1173   size_t numFuncExport = 0;
1174   for (const Export& exp : module->exports()) {
1175     Rooted<IdValueVector> props(cx, IdValueVector(cx));
1176     if (!props.reserve(2)) {
1177       return false;
1178     }
1179 
1180     JSString* nameStr = UTF8CharsToString(cx, exp.fieldName());
1181     if (!nameStr) {
1182       return false;
1183     }
1184     props.infallibleAppend(
1185         IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1186 
1187     JSString* kindStr = KindToString(cx, names, exp.kind());
1188     if (!kindStr) {
1189       return false;
1190     }
1191     props.infallibleAppend(
1192         IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1193 
1194     if (fuzzingSafe && exp.kind() == DefinitionKind::Function) {
1195       JSString* ftStr =
1196           FuncTypeToString(cx, funcExports[numFuncExport++].funcType());
1197       if (!ftStr) {
1198         return false;
1199       }
1200       if (!props.append(
1201               IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1202         return false;
1203       }
1204     }
1205 
1206     JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(),
1207                                                 props.length(), GenericObject);
1208     if (!obj) {
1209       return false;
1210     }
1211 
1212     elems.infallibleAppend(ObjectValue(*obj));
1213   }
1214 
1215   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1216   if (!arr) {
1217     return false;
1218   }
1219 
1220   args.rval().setObject(*arr);
1221   return true;
1222 }
1223 
1224 /* static */
customSections(JSContext * cx,unsigned argc,Value * vp)1225 bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) {
1226   CallArgs args = CallArgsFromVp(argc, vp);
1227 
1228   const Module* module;
1229   if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections",
1230                     &module)) {
1231     return false;
1232   }
1233 
1234   Vector<char, 8> name(cx);
1235   {
1236     RootedString str(cx, ToString(cx, args.get(1)));
1237     if (!str) {
1238       return false;
1239     }
1240 
1241     Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
1242     if (!linear) {
1243       return false;
1244     }
1245 
1246     if (!name.initLengthUninitialized(
1247             JS::GetDeflatedUTF8StringLength(linear))) {
1248       return false;
1249     }
1250 
1251     mozilla::Unused << JS::DeflateStringToUTF8Buffer(
1252         linear, MakeSpan(name.begin(), name.length()));
1253   }
1254 
1255   RootedValueVector elems(cx);
1256   RootedArrayBufferObject buf(cx);
1257   for (const CustomSection& cs : module->customSections()) {
1258     if (name.length() != cs.name.length()) {
1259       continue;
1260     }
1261     if (memcmp(name.begin(), cs.name.begin(), name.length())) {
1262       continue;
1263     }
1264 
1265     buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
1266     if (!buf) {
1267       return false;
1268     }
1269 
1270     memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
1271     if (!elems.append(ObjectValue(*buf))) {
1272       return false;
1273     }
1274   }
1275 
1276   JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1277   if (!arr) {
1278     return false;
1279   }
1280 
1281   args.rval().setObject(*arr);
1282   return true;
1283 }
1284 
1285 /* static */
create(JSContext * cx,const Module & module,HandleObject proto)1286 WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module,
1287                                            HandleObject proto) {
1288   AutoSetNewObjectMetadata metadata(cx);
1289   auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
1290   if (!obj) {
1291     return nullptr;
1292   }
1293 
1294   // This accounts for module allocation size (excluding code which is handled
1295   // separately - see below). This assumes that the size of associated data
1296   // doesn't change for the life of the WasmModuleObject. The size is counted
1297   // once per WasmModuleObject referencing a Module.
1298   InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module),
1299                    module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule);
1300   module.AddRef();
1301 
1302   // Bug 1569888: We account for the first tier here; the second tier, if
1303   // different, also needs to be accounted for.
1304   cx->zone()->incJitMemory(module.codeLength(module.code().stableTier()));
1305   return obj;
1306 }
1307 
GetBufferSource(JSContext * cx,JSObject * obj,unsigned errorNumber,MutableBytes * bytecode)1308 static bool GetBufferSource(JSContext* cx, JSObject* obj, unsigned errorNumber,
1309                             MutableBytes* bytecode) {
1310   *bytecode = cx->new_<ShareableBytes>();
1311   if (!*bytecode) {
1312     return false;
1313   }
1314 
1315   JSObject* unwrapped = CheckedUnwrapStatic(obj);
1316 
1317   SharedMem<uint8_t*> dataPointer;
1318   size_t byteLength;
1319   if (!unwrapped || !IsBufferSource(unwrapped, &dataPointer, &byteLength)) {
1320     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
1321     return false;
1322   }
1323 
1324   if (!(*bytecode)->append(dataPointer.unwrap(), byteLength)) {
1325     ReportOutOfMemory(cx);
1326     return false;
1327   }
1328 
1329   return true;
1330 }
1331 
InitCompileArgs(JSContext * cx,const char * introducer)1332 static SharedCompileArgs InitCompileArgs(JSContext* cx,
1333                                          const char* introducer) {
1334   ScriptedCaller scriptedCaller;
1335   if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
1336     return nullptr;
1337   }
1338 
1339   return CompileArgs::build(cx, std::move(scriptedCaller));
1340 }
1341 
ReportCompileWarnings(JSContext * cx,const UniqueCharsVector & warnings)1342 static bool ReportCompileWarnings(JSContext* cx,
1343                                   const UniqueCharsVector& warnings) {
1344   // Avoid spamming the console.
1345   size_t numWarnings = std::min<size_t>(warnings.length(), 3);
1346 
1347   for (size_t i = 0; i < numWarnings; i++) {
1348     if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, warnings[i].get())) {
1349       return false;
1350     }
1351   }
1352 
1353   if (warnings.length() > numWarnings) {
1354     if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING,
1355                          "other warnings suppressed")) {
1356       return false;
1357     }
1358   }
1359 
1360   return true;
1361 }
1362 
1363 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)1364 bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1365   CallArgs callArgs = CallArgsFromVp(argc, vp);
1366 
1367   Log(cx, "sync new Module() started");
1368 
1369   if (!ThrowIfNotConstructing(cx, callArgs, "Module")) {
1370     return false;
1371   }
1372 
1373   if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) {
1374     return false;
1375   }
1376 
1377   if (!callArgs[0].isObject()) {
1378     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1379                              JSMSG_WASM_BAD_BUF_ARG);
1380     return false;
1381   }
1382 
1383   MutableBytes bytecode;
1384   if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
1385                        &bytecode)) {
1386     return false;
1387   }
1388 
1389   SharedCompileArgs compileArgs = InitCompileArgs(cx, "WebAssembly.Module");
1390   if (!compileArgs) {
1391     return false;
1392   }
1393 
1394   UniqueChars error;
1395   UniqueCharsVector warnings;
1396   SharedModule module =
1397       CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
1398   if (!module) {
1399     if (error) {
1400       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1401                                JSMSG_WASM_COMPILE_ERROR, error.get());
1402       return false;
1403     }
1404     ReportOutOfMemory(cx);
1405     return false;
1406   }
1407 
1408   if (!ReportCompileWarnings(cx, warnings)) {
1409     return false;
1410   }
1411 
1412   RootedObject proto(cx);
1413   if (!GetPrototypeFromBuiltinConstructor(cx, callArgs, JSProto_WasmModule,
1414                                           &proto)) {
1415     return false;
1416   }
1417   if (!proto) {
1418     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmModule);
1419   }
1420 
1421   RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
1422   if (!moduleObj) {
1423     return false;
1424   }
1425 
1426   Log(cx, "sync new Module() succeded");
1427 
1428   callArgs.rval().setObject(*moduleObj);
1429   return true;
1430 }
1431 
module() const1432 const Module& WasmModuleObject::module() const {
1433   MOZ_ASSERT(is<WasmModuleObject>());
1434   return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
1435 }
1436 
1437 // ============================================================================
1438 // WebAssembly.Instance class and methods
1439 
1440 const JSClassOps WasmInstanceObject::classOps_ = {
1441     nullptr,                       // addProperty
1442     nullptr,                       // delProperty
1443     nullptr,                       // enumerate
1444     nullptr,                       // newEnumerate
1445     nullptr,                       // resolve
1446     nullptr,                       // mayResolve
1447     WasmInstanceObject::finalize,  // finalize
1448     nullptr,                       // call
1449     nullptr,                       // hasInstance
1450     nullptr,                       // construct
1451     WasmInstanceObject::trace,     // trace
1452 };
1453 
1454 const JSClass WasmInstanceObject::class_ = {
1455     "WebAssembly.Instance",
1456     JSCLASS_DELAY_METADATA_BUILDER |
1457         JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
1458         JSCLASS_FOREGROUND_FINALIZE,
1459     &WasmInstanceObject::classOps_,
1460     &WasmInstanceObject::classSpec_,
1461 };
1462 
1463 const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
1464 
1465 static constexpr char WasmInstanceName[] = "Instance";
1466 
1467 const ClassSpec WasmInstanceObject::classSpec_ = {
1468     CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
1469     GenericCreatePrototype<WasmInstanceObject>,
1470     WasmInstanceObject::static_methods,
1471     nullptr,
1472     WasmInstanceObject::methods,
1473     WasmInstanceObject::properties,
1474     nullptr,
1475     ClassSpec::DontDefineConstructor};
1476 
IsInstance(HandleValue v)1477 static bool IsInstance(HandleValue v) {
1478   return v.isObject() && v.toObject().is<WasmInstanceObject>();
1479 }
1480 
1481 /* static */
exportsGetterImpl(JSContext * cx,const CallArgs & args)1482 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
1483                                            const CallArgs& args) {
1484   args.rval().setObject(
1485       args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
1486   return true;
1487 }
1488 
1489 /* static */
exportsGetter(JSContext * cx,unsigned argc,Value * vp)1490 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
1491                                        Value* vp) {
1492   CallArgs args = CallArgsFromVp(argc, vp);
1493   return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
1494 }
1495 
1496 const JSPropertySpec WasmInstanceObject::properties[] = {
1497     JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
1498     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
1499     JS_PS_END};
1500 
1501 const JSFunctionSpec WasmInstanceObject::methods[] = {JS_FS_END};
1502 
1503 const JSFunctionSpec WasmInstanceObject::static_methods[] = {JS_FS_END};
1504 
isNewborn() const1505 bool WasmInstanceObject::isNewborn() const {
1506   MOZ_ASSERT(is<WasmInstanceObject>());
1507   return getReservedSlot(INSTANCE_SLOT).isUndefined();
1508 }
1509 
1510 /* static */
finalize(JSFreeOp * fop,JSObject * obj)1511 void WasmInstanceObject::finalize(JSFreeOp* fop, JSObject* obj) {
1512   WasmInstanceObject& instance = obj->as<WasmInstanceObject>();
1513   fop->delete_(obj, &instance.exports(), MemoryUse::WasmInstanceExports);
1514   fop->delete_(obj, &instance.scopes(), MemoryUse::WasmInstanceScopes);
1515   fop->delete_(obj, &instance.indirectGlobals(),
1516                MemoryUse::WasmInstanceGlobals);
1517   if (!instance.isNewborn()) {
1518     if (instance.instance().debugEnabled()) {
1519       instance.instance().debug().finalize(fop);
1520     }
1521     fop->delete_(obj, &instance.instance(), MemoryUse::WasmInstanceInstance);
1522   }
1523 }
1524 
1525 /* static */
trace(JSTracer * trc,JSObject * obj)1526 void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) {
1527   WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
1528   instanceObj.exports().trace(trc);
1529   instanceObj.indirectGlobals().trace(trc);
1530   if (!instanceObj.isNewborn()) {
1531     instanceObj.instance().tracePrivate(trc);
1532   }
1533 }
1534 
1535 /* static */
create(JSContext * cx,SharedCode code,const DataSegmentVector & dataSegments,const ElemSegmentVector & elemSegments,UniqueTlsData tlsData,HandleWasmMemoryObject memory,SharedTableVector && tables,StructTypeDescrVector && structTypeDescrs,const JSFunctionVector & funcImports,const GlobalDescVector & globals,const ValVector & globalImportValues,const WasmGlobalObjectVector & globalObjs,HandleObject proto,UniqueDebugState maybeDebug)1536 WasmInstanceObject* WasmInstanceObject::create(
1537     JSContext* cx, SharedCode code, const DataSegmentVector& dataSegments,
1538     const ElemSegmentVector& elemSegments, UniqueTlsData tlsData,
1539     HandleWasmMemoryObject memory, SharedTableVector&& tables,
1540     StructTypeDescrVector&& structTypeDescrs,
1541     const JSFunctionVector& funcImports, const GlobalDescVector& globals,
1542     const ValVector& globalImportValues,
1543     const WasmGlobalObjectVector& globalObjs, HandleObject proto,
1544     UniqueDebugState maybeDebug) {
1545   UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>(cx->zone());
1546   if (!exports) {
1547     ReportOutOfMemory(cx);
1548     return nullptr;
1549   }
1550 
1551   UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone(), cx->zone());
1552   if (!scopes) {
1553     ReportOutOfMemory(cx);
1554     return nullptr;
1555   }
1556 
1557   uint32_t indirectGlobals = 0;
1558 
1559   for (uint32_t i = 0; i < globalObjs.length(); i++) {
1560     if (globalObjs[i] && globals[i].isIndirect()) {
1561       indirectGlobals++;
1562     }
1563   }
1564 
1565   Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs(
1566       cx, js::MakeUnique<GlobalObjectVector>(cx->zone()));
1567   if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
1568     ReportOutOfMemory(cx);
1569     return nullptr;
1570   }
1571 
1572   {
1573     uint32_t next = 0;
1574     for (uint32_t i = 0; i < globalObjs.length(); i++) {
1575       if (globalObjs[i] && globals[i].isIndirect()) {
1576         (*indirectGlobalObjs)[next++] = globalObjs[i];
1577       }
1578     }
1579   }
1580 
1581   Instance* instance = nullptr;
1582   RootedWasmInstanceObject obj(cx);
1583 
1584   {
1585     // We must delay creating metadata for this object until after all its
1586     // slots have been initialized. We must also create the metadata before
1587     // calling Instance::init as that may allocate new objects.
1588     AutoSetNewObjectMetadata metadata(cx);
1589     obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
1590     if (!obj) {
1591       return nullptr;
1592     }
1593 
1594     MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
1595 
1596     // Finalization assumes these slots are always initialized:
1597     InitReservedSlot(obj, EXPORTS_SLOT, exports.release(),
1598                      MemoryUse::WasmInstanceExports);
1599 
1600     InitReservedSlot(obj, SCOPES_SLOT, scopes.release(),
1601                      MemoryUse::WasmInstanceScopes);
1602 
1603     InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(),
1604                      MemoryUse::WasmInstanceGlobals);
1605 
1606     obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
1607 
1608     // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
1609     // leading to an observable "newborn" state in tracing/finalization.
1610     MOZ_ASSERT(obj->isNewborn());
1611 
1612     // Root the Instance via WasmInstanceObject before any possible GC.
1613     instance = cx->new_<Instance>(
1614         cx, obj, code, std::move(tlsData), memory, std::move(tables),
1615         std::move(structTypeDescrs), std::move(maybeDebug));
1616     if (!instance) {
1617       return nullptr;
1618     }
1619 
1620     InitReservedSlot(obj, INSTANCE_SLOT, instance,
1621                      MemoryUse::WasmInstanceInstance);
1622     MOZ_ASSERT(!obj->isNewborn());
1623   }
1624 
1625   if (!instance->init(cx, funcImports, globalImportValues, globalObjs,
1626                       dataSegments, elemSegments)) {
1627     return nullptr;
1628   }
1629 
1630   return obj;
1631 }
1632 
initExportsObj(JSObject & exportsObj)1633 void WasmInstanceObject::initExportsObj(JSObject& exportsObj) {
1634   MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
1635   setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
1636 }
1637 
GetImportArg(JSContext * cx,CallArgs callArgs,MutableHandleObject importObj)1638 static bool GetImportArg(JSContext* cx, CallArgs callArgs,
1639                          MutableHandleObject importObj) {
1640   if (!callArgs.get(1).isUndefined()) {
1641     if (!callArgs[1].isObject()) {
1642       return ThrowBadImportArg(cx);
1643     }
1644     importObj.set(&callArgs[1].toObject());
1645   }
1646   return true;
1647 }
1648 
1649 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)1650 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1651   CallArgs args = CallArgsFromVp(argc, vp);
1652 
1653   Log(cx, "sync new Instance() started");
1654 
1655   if (!ThrowIfNotConstructing(cx, args, "Instance")) {
1656     return false;
1657   }
1658 
1659   if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
1660     return false;
1661   }
1662 
1663   const Module* module;
1664   if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
1665     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1666                              JSMSG_WASM_BAD_MOD_ARG);
1667     return false;
1668   }
1669 
1670   RootedObject importObj(cx);
1671   if (!GetImportArg(cx, args, &importObj)) {
1672     return false;
1673   }
1674 
1675   RootedObject instanceProto(cx);
1676   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmInstance,
1677                                           &instanceProto)) {
1678     return false;
1679   }
1680   if (!instanceProto) {
1681     instanceProto =
1682         GlobalObject::getOrCreatePrototype(cx, JSProto_WasmInstance);
1683   }
1684 
1685   Rooted<ImportValues> imports(cx);
1686   if (!GetImports(cx, *module, importObj, imports.address())) {
1687     return false;
1688   }
1689 
1690   RootedWasmInstanceObject instanceObj(cx);
1691   if (!module->instantiate(cx, imports.get(), instanceProto, &instanceObj)) {
1692     return false;
1693   }
1694 
1695   Log(cx, "sync new Instance() succeeded");
1696 
1697   args.rval().setObject(*instanceObj);
1698   return true;
1699 }
1700 
instance() const1701 Instance& WasmInstanceObject::instance() const {
1702   MOZ_ASSERT(!isNewborn());
1703   return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
1704 }
1705 
exportsObj() const1706 JSObject& WasmInstanceObject::exportsObj() const {
1707   return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
1708 }
1709 
exports() const1710 WasmInstanceObject::ExportMap& WasmInstanceObject::exports() const {
1711   return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
1712 }
1713 
scopes() const1714 WasmInstanceObject::ScopeMap& WasmInstanceObject::scopes() const {
1715   return *(ScopeMap*)getReservedSlot(SCOPES_SLOT).toPrivate();
1716 }
1717 
indirectGlobals() const1718 WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals()
1719     const {
1720   return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate();
1721 }
1722 
WasmCall(JSContext * cx,unsigned argc,Value * vp)1723 static bool WasmCall(JSContext* cx, unsigned argc, Value* vp) {
1724   CallArgs args = CallArgsFromVp(argc, vp);
1725   RootedFunction callee(cx, &args.callee().as<JSFunction>());
1726 
1727   Instance& instance = ExportedFunctionToInstance(callee);
1728   uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
1729   return instance.callExport(cx, funcIndex, args);
1730 }
1731 
1732 /* static */
getExportedFunction(JSContext * cx,HandleWasmInstanceObject instanceObj,uint32_t funcIndex,MutableHandleFunction fun)1733 bool WasmInstanceObject::getExportedFunction(
1734     JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex,
1735     MutableHandleFunction fun) {
1736   if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
1737     fun.set(p->value());
1738     return true;
1739   }
1740 
1741   const Instance& instance = instanceObj->instance();
1742   const FuncExport& funcExport =
1743       instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
1744   unsigned numArgs = funcExport.funcType().args().length();
1745 
1746   if (instance.isAsmJS()) {
1747     // asm.js needs to act like a normal JS function which means having the
1748     // name from the original source and being callable as a constructor.
1749     RootedAtom name(cx, instance.getFuncDisplayAtom(cx, funcIndex));
1750     if (!name) {
1751       return false;
1752     }
1753     fun.set(NewNativeConstructor(cx, WasmCall, numArgs, name,
1754                                  gc::AllocKind::FUNCTION_EXTENDED,
1755                                  SingletonObject, FunctionFlags::ASMJS_CTOR));
1756     if (!fun) {
1757       return false;
1758     }
1759 
1760     // asm.js does not support jit entries.
1761     fun->setWasmFuncIndex(funcIndex);
1762   } else {
1763     RootedAtom name(cx, NumberToAtom(cx, funcIndex));
1764     if (!name) {
1765       return false;
1766     }
1767 
1768     fun.set(NewNativeFunction(cx, WasmCall, numArgs, name,
1769                               gc::AllocKind::FUNCTION_EXTENDED, SingletonObject,
1770                               FunctionFlags::WASM));
1771     if (!fun) {
1772       return false;
1773     }
1774 
1775     // Some applications eagerly access all table elements which currently
1776     // triggers worst-case behavior for lazy stubs, since each will allocate a
1777     // separate 4kb code page. Most eagerly-accessed functions are not called,
1778     // so instead wait until Instance::callExport() to create the entry stubs.
1779     if (funcExport.hasEagerStubs() && funcExport.canHaveJitEntry()) {
1780       fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex));
1781     } else {
1782       fun->setWasmFuncIndex(funcIndex);
1783     }
1784   }
1785 
1786   fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT,
1787                        ObjectValue(*instanceObj));
1788 
1789   void* tlsData = instanceObj->instance().tlsData();
1790   fun->setExtendedSlot(FunctionExtended::WASM_TLSDATA_SLOT,
1791                        PrivateValue(tlsData));
1792 
1793   if (!instanceObj->exports().putNew(funcIndex, fun)) {
1794     ReportOutOfMemory(cx);
1795     return false;
1796   }
1797 
1798   return true;
1799 }
1800 
getExportedFunctionCodeRange(JSFunction * fun,Tier tier)1801 const CodeRange& WasmInstanceObject::getExportedFunctionCodeRange(
1802     JSFunction* fun, Tier tier) {
1803   uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
1804   MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
1805   const MetadataTier& metadata = instance().metadata(tier);
1806   return metadata.codeRange(metadata.lookupFuncExport(funcIndex));
1807 }
1808 
1809 /* static */
getScope(JSContext * cx,HandleWasmInstanceObject instanceObj)1810 WasmInstanceScope* WasmInstanceObject::getScope(
1811     JSContext* cx, HandleWasmInstanceObject instanceObj) {
1812   if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) {
1813     return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT)
1814         .toGCThing();
1815   }
1816 
1817   Rooted<WasmInstanceScope*> instanceScope(
1818       cx, WasmInstanceScope::create(cx, instanceObj));
1819   if (!instanceScope) {
1820     return nullptr;
1821   }
1822 
1823   instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT,
1824                                PrivateGCThingValue(instanceScope));
1825 
1826   return instanceScope;
1827 }
1828 
1829 /* static */
getFunctionScope(JSContext * cx,HandleWasmInstanceObject instanceObj,uint32_t funcIndex)1830 WasmFunctionScope* WasmInstanceObject::getFunctionScope(
1831     JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex) {
1832   if (ScopeMap::Ptr p = instanceObj->scopes().lookup(funcIndex)) {
1833     return p->value();
1834   }
1835 
1836   Rooted<WasmInstanceScope*> instanceScope(
1837       cx, WasmInstanceObject::getScope(cx, instanceObj));
1838   if (!instanceScope) {
1839     return nullptr;
1840   }
1841 
1842   Rooted<WasmFunctionScope*> funcScope(
1843       cx, WasmFunctionScope::create(cx, instanceScope, funcIndex));
1844   if (!funcScope) {
1845     return nullptr;
1846   }
1847 
1848   if (!instanceObj->scopes().putNew(funcIndex, funcScope)) {
1849     ReportOutOfMemory(cx);
1850     return nullptr;
1851   }
1852 
1853   return funcScope;
1854 }
1855 
IsWasmExportedFunction(JSFunction * fun)1856 bool wasm::IsWasmExportedFunction(JSFunction* fun) {
1857   return fun->kind() == FunctionFlags::Wasm;
1858 }
1859 
CheckFuncRefValue(JSContext * cx,HandleValue v,MutableHandleFunction fun)1860 bool wasm::CheckFuncRefValue(JSContext* cx, HandleValue v,
1861                              MutableHandleFunction fun) {
1862   if (v.isNull()) {
1863     MOZ_ASSERT(!fun);
1864     return true;
1865   }
1866 
1867   if (v.isObject()) {
1868     JSObject& obj = v.toObject();
1869     if (obj.is<JSFunction>()) {
1870       JSFunction* f = &obj.as<JSFunction>();
1871       if (IsWasmExportedFunction(f)) {
1872         fun.set(f);
1873         return true;
1874       }
1875     }
1876   }
1877 
1878   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1879                            JSMSG_WASM_BAD_FUNCREF_VALUE);
1880   return false;
1881 }
1882 
ExportedFunctionToInstance(JSFunction * fun)1883 Instance& wasm::ExportedFunctionToInstance(JSFunction* fun) {
1884   return ExportedFunctionToInstanceObject(fun)->instance();
1885 }
1886 
ExportedFunctionToInstanceObject(JSFunction * fun)1887 WasmInstanceObject* wasm::ExportedFunctionToInstanceObject(JSFunction* fun) {
1888   MOZ_ASSERT(fun->kind() == FunctionFlags::Wasm ||
1889              fun->kind() == FunctionFlags::AsmJS);
1890   const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT);
1891   return &v.toObject().as<WasmInstanceObject>();
1892 }
1893 
ExportedFunctionToFuncIndex(JSFunction * fun)1894 uint32_t wasm::ExportedFunctionToFuncIndex(JSFunction* fun) {
1895   Instance& instance = ExportedFunctionToInstanceObject(fun)->instance();
1896   return instance.code().getFuncIndex(fun);
1897 }
1898 
1899 // ============================================================================
1900 // WebAssembly.Memory class and methods
1901 
1902 const JSClassOps WasmMemoryObject::classOps_ = {
1903     nullptr,                     // addProperty
1904     nullptr,                     // delProperty
1905     nullptr,                     // enumerate
1906     nullptr,                     // newEnumerate
1907     nullptr,                     // resolve
1908     nullptr,                     // mayResolve
1909     WasmMemoryObject::finalize,  // finalize
1910     nullptr,                     // call
1911     nullptr,                     // hasInstance
1912     nullptr,                     // construct
1913     nullptr,                     // trace
1914 };
1915 
1916 const JSClass WasmMemoryObject::class_ = {
1917     "WebAssembly.Memory",
1918     JSCLASS_DELAY_METADATA_BUILDER |
1919         JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
1920         JSCLASS_FOREGROUND_FINALIZE,
1921     &WasmMemoryObject::classOps_, &WasmMemoryObject::classSpec_};
1922 
1923 const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_;
1924 
1925 static constexpr char WasmMemoryName[] = "Memory";
1926 
1927 const ClassSpec WasmMemoryObject::classSpec_ = {
1928     CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>,
1929     GenericCreatePrototype<WasmMemoryObject>,
1930     WasmMemoryObject::static_methods,
1931     nullptr,
1932     WasmMemoryObject::methods,
1933     WasmMemoryObject::properties,
1934     nullptr,
1935     ClassSpec::DontDefineConstructor};
1936 
1937 /* static */
finalize(JSFreeOp * fop,JSObject * obj)1938 void WasmMemoryObject::finalize(JSFreeOp* fop, JSObject* obj) {
1939   WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
1940   if (memory.hasObservers()) {
1941     fop->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers);
1942   }
1943 }
1944 
1945 /* static */
create(JSContext * cx,HandleArrayBufferObjectMaybeShared buffer,HandleObject proto)1946 WasmMemoryObject* WasmMemoryObject::create(
1947     JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
1948     HandleObject proto) {
1949   AutoSetNewObjectMetadata metadata(cx);
1950   auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
1951   if (!obj) {
1952     return nullptr;
1953   }
1954 
1955   obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
1956   MOZ_ASSERT(!obj->hasObservers());
1957   return obj;
1958 }
1959 
1960 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)1961 bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1962   CallArgs args = CallArgsFromVp(argc, vp);
1963 
1964   if (!ThrowIfNotConstructing(cx, args, "Memory")) {
1965     return false;
1966   }
1967 
1968   if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1)) {
1969     return false;
1970   }
1971 
1972   if (!args.get(0).isObject()) {
1973     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1974                              JSMSG_WASM_BAD_DESC_ARG, "memory");
1975     return false;
1976   }
1977 
1978   RootedObject obj(cx, &args[0].toObject());
1979   Limits limits;
1980   if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages,
1981                  "Memory", &limits, Shareable::True)) {
1982     return false;
1983   }
1984 
1985   ConvertMemoryPagesToBytes(&limits);
1986 
1987   RootedArrayBufferObjectMaybeShared buffer(cx);
1988   if (!CreateWasmBuffer(cx, limits, &buffer)) {
1989     return false;
1990   }
1991 
1992   RootedObject proto(cx);
1993   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmMemory,
1994                                           &proto)) {
1995     return false;
1996   }
1997   if (!proto) {
1998     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmMemory);
1999   }
2000 
2001   RootedWasmMemoryObject memoryObj(cx,
2002                                    WasmMemoryObject::create(cx, buffer, proto));
2003   if (!memoryObj) {
2004     return false;
2005   }
2006 
2007   args.rval().setObject(*memoryObj);
2008   return true;
2009 }
2010 
IsMemory(HandleValue v)2011 static bool IsMemory(HandleValue v) {
2012   return v.isObject() && v.toObject().is<WasmMemoryObject>();
2013 }
2014 
2015 /* static */
bufferGetterImpl(JSContext * cx,const CallArgs & args)2016 bool WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args) {
2017   RootedWasmMemoryObject memoryObj(
2018       cx, &args.thisv().toObject().as<WasmMemoryObject>());
2019   RootedArrayBufferObjectMaybeShared buffer(cx, &memoryObj->buffer());
2020 
2021   if (memoryObj->isShared()) {
2022     uint32_t memoryLength = memoryObj->volatileMemoryLength();
2023     MOZ_ASSERT(memoryLength >= buffer->byteLength());
2024 
2025     if (memoryLength > buffer->byteLength()) {
2026       RootedSharedArrayBufferObject newBuffer(
2027           cx, SharedArrayBufferObject::New(
2028                   cx, memoryObj->sharedArrayRawBuffer(), memoryLength));
2029       if (!newBuffer) {
2030         return false;
2031       }
2032       // OK to addReference after we try to allocate because the memoryObj
2033       // keeps the rawBuffer alive.
2034       if (!memoryObj->sharedArrayRawBuffer()->addReference()) {
2035         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2036                                   JSMSG_SC_SAB_REFCNT_OFLO);
2037         return false;
2038       }
2039       buffer = newBuffer;
2040       memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer));
2041     }
2042   }
2043 
2044   args.rval().setObject(*buffer);
2045   return true;
2046 }
2047 
2048 /* static */
bufferGetter(JSContext * cx,unsigned argc,Value * vp)2049 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
2050   CallArgs args = CallArgsFromVp(argc, vp);
2051   return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
2052 }
2053 
2054 const JSPropertySpec WasmMemoryObject::properties[] = {
2055     JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE),
2056     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY),
2057     JS_PS_END};
2058 
2059 /* static */
growImpl(JSContext * cx,const CallArgs & args)2060 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) {
2061   RootedWasmMemoryObject memory(
2062       cx, &args.thisv().toObject().as<WasmMemoryObject>());
2063 
2064   if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) {
2065     return false;
2066   }
2067 
2068   uint32_t delta;
2069   if (!EnforceRangeU32(cx, args.get(0), "Memory", "grow delta", &delta)) {
2070     return false;
2071   }
2072 
2073   uint32_t ret = grow(memory, delta, cx);
2074 
2075   if (ret == uint32_t(-1)) {
2076     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
2077                              "memory");
2078     return false;
2079   }
2080 
2081   args.rval().setInt32(ret);
2082   return true;
2083 }
2084 
2085 /* static */
grow(JSContext * cx,unsigned argc,Value * vp)2086 bool WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp) {
2087   CallArgs args = CallArgsFromVp(argc, vp);
2088   return CallNonGenericMethod<IsMemory, growImpl>(cx, args);
2089 }
2090 
2091 const JSFunctionSpec WasmMemoryObject::methods[] = {
2092     JS_FN("grow", WasmMemoryObject::grow, 1, JSPROP_ENUMERATE), JS_FS_END};
2093 
2094 const JSFunctionSpec WasmMemoryObject::static_methods[] = {JS_FS_END};
2095 
buffer() const2096 ArrayBufferObjectMaybeShared& WasmMemoryObject::buffer() const {
2097   return getReservedSlot(BUFFER_SLOT)
2098       .toObject()
2099       .as<ArrayBufferObjectMaybeShared>();
2100 }
2101 
sharedArrayRawBuffer() const2102 SharedArrayRawBuffer* WasmMemoryObject::sharedArrayRawBuffer() const {
2103   MOZ_ASSERT(isShared());
2104   return buffer().as<SharedArrayBufferObject>().rawBufferObject();
2105 }
2106 
volatileMemoryLength() const2107 uint32_t WasmMemoryObject::volatileMemoryLength() const {
2108   if (isShared()) {
2109     return sharedArrayRawBuffer()->volatileByteLength();
2110   }
2111   return buffer().byteLength();
2112 }
2113 
isShared() const2114 bool WasmMemoryObject::isShared() const {
2115   return buffer().is<SharedArrayBufferObject>();
2116 }
2117 
hasObservers() const2118 bool WasmMemoryObject::hasObservers() const {
2119   return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
2120 }
2121 
observers() const2122 WasmMemoryObject::InstanceSet& WasmMemoryObject::observers() const {
2123   MOZ_ASSERT(hasObservers());
2124   return *reinterpret_cast<InstanceSet*>(
2125       getReservedSlot(OBSERVERS_SLOT).toPrivate());
2126 }
2127 
getOrCreateObservers(JSContext * cx)2128 WasmMemoryObject::InstanceSet* WasmMemoryObject::getOrCreateObservers(
2129     JSContext* cx) {
2130   if (!hasObservers()) {
2131     auto observers = MakeUnique<InstanceSet>(cx->zone(), cx->zone());
2132     if (!observers) {
2133       ReportOutOfMemory(cx);
2134       return nullptr;
2135     }
2136 
2137     InitReservedSlot(this, OBSERVERS_SLOT, observers.release(),
2138                      MemoryUse::WasmMemoryObservers);
2139   }
2140 
2141   return &observers();
2142 }
2143 
isHuge() const2144 bool WasmMemoryObject::isHuge() const {
2145 #ifdef WASM_SUPPORTS_HUGE_MEMORY
2146   static_assert(ArrayBufferObject::MaxBufferByteLength < HugeMappedSize,
2147                 "Non-huge buffer may be confused as huge");
2148   return buffer().wasmMappedSize() >= HugeMappedSize;
2149 #else
2150   return false;
2151 #endif
2152 }
2153 
movingGrowable() const2154 bool WasmMemoryObject::movingGrowable() const {
2155   return !isHuge() && !buffer().wasmMaxSize();
2156 }
2157 
boundsCheckLimit() const2158 uint32_t WasmMemoryObject::boundsCheckLimit() const {
2159   if (!buffer().isWasm() || isHuge()) {
2160     return buffer().byteLength();
2161   }
2162   size_t mappedSize = buffer().wasmMappedSize();
2163   MOZ_ASSERT(mappedSize <= UINT32_MAX);
2164   MOZ_ASSERT(mappedSize >= wasm::GuardSize);
2165   MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize - wasm::GuardSize));
2166   return mappedSize - wasm::GuardSize;
2167 }
2168 
addMovingGrowObserver(JSContext * cx,WasmInstanceObject * instance)2169 bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx,
2170                                              WasmInstanceObject* instance) {
2171   MOZ_ASSERT(movingGrowable());
2172 
2173   InstanceSet* observers = getOrCreateObservers(cx);
2174   if (!observers) {
2175     return false;
2176   }
2177 
2178   if (!observers->putNew(instance)) {
2179     ReportOutOfMemory(cx);
2180     return false;
2181   }
2182 
2183   return true;
2184 }
2185 
2186 /* static */
growShared(HandleWasmMemoryObject memory,uint32_t delta)2187 uint32_t WasmMemoryObject::growShared(HandleWasmMemoryObject memory,
2188                                       uint32_t delta) {
2189   SharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer();
2190   SharedArrayRawBuffer::Lock lock(rawBuf);
2191 
2192   MOZ_ASSERT(rawBuf->volatileByteLength() % PageSize == 0);
2193   uint32_t oldNumPages = rawBuf->volatileByteLength() / PageSize;
2194 
2195   CheckedInt<uint32_t> newSize = oldNumPages;
2196   newSize += delta;
2197   newSize *= PageSize;
2198   if (!newSize.isValid()) {
2199     return -1;
2200   }
2201 
2202   if (newSize.value() > rawBuf->maxSize()) {
2203     return -1;
2204   }
2205 
2206   if (!rawBuf->wasmGrowToSizeInPlace(lock, newSize.value())) {
2207     return -1;
2208   }
2209 
2210   // New buffer objects will be created lazily in all agents (including in
2211   // this agent) by bufferGetterImpl, above, so no more work to do here.
2212 
2213   return oldNumPages;
2214 }
2215 
2216 /* static */
grow(HandleWasmMemoryObject memory,uint32_t delta,JSContext * cx)2217 uint32_t WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta,
2218                                 JSContext* cx) {
2219   if (memory->isShared()) {
2220     return growShared(memory, delta);
2221   }
2222 
2223   RootedArrayBufferObject oldBuf(cx, &memory->buffer().as<ArrayBufferObject>());
2224 
2225   MOZ_ASSERT(oldBuf->byteLength() % PageSize == 0);
2226   uint32_t oldNumPages = oldBuf->byteLength() / PageSize;
2227 
2228   CheckedInt<uint32_t> newSize = oldNumPages;
2229   newSize += delta;
2230   newSize *= PageSize;
2231   if (!newSize.isValid()) {
2232     return -1;
2233   }
2234 
2235   RootedArrayBufferObject newBuf(cx);
2236 
2237   if (memory->movingGrowable()) {
2238     MOZ_ASSERT(!memory->isHuge());
2239     if (!ArrayBufferObject::wasmMovingGrowToSize(newSize.value(), oldBuf,
2240                                                  &newBuf, cx)) {
2241       return -1;
2242     }
2243   } else {
2244     if (Maybe<uint32_t> maxSize = oldBuf->wasmMaxSize()) {
2245       if (newSize.value() > maxSize.value()) {
2246         return -1;
2247       }
2248     }
2249 
2250     if (!ArrayBufferObject::wasmGrowToSizeInPlace(newSize.value(), oldBuf,
2251                                                   &newBuf, cx)) {
2252       return -1;
2253     }
2254   }
2255 
2256   memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
2257 
2258   // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
2259   // since observers will call buffer().
2260   if (memory->hasObservers()) {
2261     for (InstanceSet::Range r = memory->observers().all(); !r.empty();
2262          r.popFront()) {
2263       r.front()->instance().onMovingGrowMemory();
2264     }
2265   }
2266 
2267   return oldNumPages;
2268 }
2269 
IsSharedWasmMemoryObject(JSObject * obj)2270 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) {
2271   WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>();
2272   return mobj && mobj->isShared();
2273 }
2274 
2275 // ============================================================================
2276 // WebAssembly.Table class and methods
2277 
2278 const JSClassOps WasmTableObject::classOps_ = {
2279     nullptr,                    // addProperty
2280     nullptr,                    // delProperty
2281     nullptr,                    // enumerate
2282     nullptr,                    // newEnumerate
2283     nullptr,                    // resolve
2284     nullptr,                    // mayResolve
2285     WasmTableObject::finalize,  // finalize
2286     nullptr,                    // call
2287     nullptr,                    // hasInstance
2288     nullptr,                    // construct
2289     WasmTableObject::trace,     // trace
2290 };
2291 
2292 const JSClass WasmTableObject::class_ = {
2293     "WebAssembly.Table",
2294     JSCLASS_DELAY_METADATA_BUILDER |
2295         JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
2296         JSCLASS_FOREGROUND_FINALIZE,
2297     &WasmTableObject::classOps_, &WasmTableObject::classSpec_};
2298 
2299 const JSClass& WasmTableObject::protoClass_ = PlainObject::class_;
2300 
2301 static constexpr char WasmTableName[] = "Table";
2302 
2303 const ClassSpec WasmTableObject::classSpec_ = {
2304     CreateWasmConstructor<WasmTableObject, WasmTableName>,
2305     GenericCreatePrototype<WasmTableObject>,
2306     WasmTableObject::static_methods,
2307     nullptr,
2308     WasmTableObject::methods,
2309     WasmTableObject::properties,
2310     nullptr,
2311     ClassSpec::DontDefineConstructor};
2312 
isNewborn() const2313 bool WasmTableObject::isNewborn() const {
2314   MOZ_ASSERT(is<WasmTableObject>());
2315   return getReservedSlot(TABLE_SLOT).isUndefined();
2316 }
2317 
2318 /* static */
finalize(JSFreeOp * fop,JSObject * obj)2319 void WasmTableObject::finalize(JSFreeOp* fop, JSObject* obj) {
2320   WasmTableObject& tableObj = obj->as<WasmTableObject>();
2321   if (!tableObj.isNewborn()) {
2322     auto& table = tableObj.table();
2323     fop->release(obj, &table, table.gcMallocBytes(), MemoryUse::WasmTableTable);
2324   }
2325 }
2326 
2327 /* static */
trace(JSTracer * trc,JSObject * obj)2328 void WasmTableObject::trace(JSTracer* trc, JSObject* obj) {
2329   WasmTableObject& tableObj = obj->as<WasmTableObject>();
2330   if (!tableObj.isNewborn()) {
2331     tableObj.table().tracePrivate(trc);
2332   }
2333 }
2334 
2335 /* static */
create(JSContext * cx,const Limits & limits,TableKind tableKind,HandleObject proto)2336 WasmTableObject* WasmTableObject::create(JSContext* cx, const Limits& limits,
2337                                          TableKind tableKind,
2338                                          HandleObject proto) {
2339   AutoSetNewObjectMetadata metadata(cx);
2340   RootedWasmTableObject obj(
2341       cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
2342   if (!obj) {
2343     return nullptr;
2344   }
2345 
2346   MOZ_ASSERT(obj->isNewborn());
2347 
2348   TableDesc td(tableKind, limits, /*importedOrExported=*/true);
2349 
2350   SharedTable table = Table::create(cx, td, obj);
2351   if (!table) {
2352     return nullptr;
2353   }
2354 
2355   size_t size = table->gcMallocBytes();
2356   InitReservedSlot(obj, TABLE_SLOT, table.forget().take(), size,
2357                    MemoryUse::WasmTableTable);
2358 
2359   MOZ_ASSERT(!obj->isNewborn());
2360   return obj;
2361 }
2362 
2363 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)2364 bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2365   CallArgs args = CallArgsFromVp(argc, vp);
2366 
2367   if (!ThrowIfNotConstructing(cx, args, "Table")) {
2368     return false;
2369   }
2370 
2371   if (!args.requireAtLeast(cx, "WebAssembly.Table", 1)) {
2372     return false;
2373   }
2374 
2375   if (!args.get(0).isObject()) {
2376     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2377                              JSMSG_WASM_BAD_DESC_ARG, "table");
2378     return false;
2379   }
2380 
2381   RootedObject obj(cx, &args[0].toObject());
2382 
2383   JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
2384   if (!elementAtom) {
2385     return false;
2386   }
2387   RootedId elementId(cx, AtomToId(elementAtom));
2388 
2389   RootedValue elementVal(cx);
2390   if (!GetProperty(cx, obj, obj, elementId, &elementVal)) {
2391     return false;
2392   }
2393 
2394   RootedString elementStr(cx, ToString(cx, elementVal));
2395   if (!elementStr) {
2396     return false;
2397   }
2398 
2399   RootedLinearString elementLinearStr(cx, elementStr->ensureLinear(cx));
2400   if (!elementLinearStr) {
2401     return false;
2402   }
2403 
2404   TableKind tableKind;
2405   if (StringEqualsLiteral(elementLinearStr, "anyfunc") ||
2406       StringEqualsLiteral(elementLinearStr, "funcref")) {
2407     tableKind = TableKind::FuncRef;
2408 #ifdef ENABLE_WASM_REFTYPES
2409   } else if (StringEqualsLiteral(elementLinearStr, "externref")) {
2410     if (!ReftypesAvailable(cx)) {
2411       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2412                                JSMSG_WASM_BAD_ELEMENT);
2413       return false;
2414     }
2415     tableKind = TableKind::AnyRef;
2416 #endif
2417   } else {
2418 #ifdef ENABLE_WASM_REFTYPES
2419     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2420                              JSMSG_WASM_BAD_ELEMENT_GENERALIZED);
2421 #else
2422     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2423                              JSMSG_WASM_BAD_ELEMENT);
2424 #endif
2425     return false;
2426   }
2427 
2428   Limits limits;
2429   if (!GetLimits(cx, obj, MaxTableInitialLength, MaxTableLength, "Table",
2430                  &limits, Shareable::False)) {
2431     return false;
2432   }
2433 
2434   RootedObject proto(cx);
2435   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmTable,
2436                                           &proto)) {
2437     return false;
2438   }
2439   if (!proto) {
2440     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTable);
2441   }
2442 
2443   RootedWasmTableObject table(
2444       cx, WasmTableObject::create(cx, limits, tableKind, proto));
2445   if (!table) {
2446     return false;
2447   }
2448 
2449   args.rval().setObject(*table);
2450   return true;
2451 }
2452 
IsTable(HandleValue v)2453 static bool IsTable(HandleValue v) {
2454   return v.isObject() && v.toObject().is<WasmTableObject>();
2455 }
2456 
2457 /* static */
lengthGetterImpl(JSContext * cx,const CallArgs & args)2458 bool WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args) {
2459   args.rval().setNumber(
2460       args.thisv().toObject().as<WasmTableObject>().table().length());
2461   return true;
2462 }
2463 
2464 /* static */
lengthGetter(JSContext * cx,unsigned argc,Value * vp)2465 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
2466   CallArgs args = CallArgsFromVp(argc, vp);
2467   return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
2468 }
2469 
2470 const JSPropertySpec WasmTableObject::properties[] = {
2471     JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE),
2472     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY),
2473     JS_PS_END};
2474 
ToTableIndex(JSContext * cx,HandleValue v,const Table & table,const char * noun,uint32_t * index)2475 static bool ToTableIndex(JSContext* cx, HandleValue v, const Table& table,
2476                          const char* noun, uint32_t* index) {
2477   if (!EnforceRangeU32(cx, v, "Table", noun, index)) {
2478     return false;
2479   }
2480 
2481   if (*index >= table.length()) {
2482     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2483                               JSMSG_WASM_BAD_RANGE, "Table", noun);
2484     return false;
2485   }
2486 
2487   return true;
2488 }
2489 
2490 /* static */
getImpl(JSContext * cx,const CallArgs & args)2491 bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) {
2492   RootedWasmTableObject tableObj(
2493       cx, &args.thisv().toObject().as<WasmTableObject>());
2494   const Table& table = tableObj->table();
2495 
2496   if (!args.requireAtLeast(cx, "WebAssembly.Table.get", 1)) {
2497     return false;
2498   }
2499 
2500   uint32_t index;
2501   if (!ToTableIndex(cx, args.get(0), table, "get index", &index)) {
2502     return false;
2503   }
2504 
2505   switch (table.repr()) {
2506     case TableRepr::Func: {
2507       MOZ_RELEASE_ASSERT(table.kind() == TableKind::FuncRef);
2508       RootedFunction fun(cx);
2509       if (!table.getFuncRef(cx, index, &fun)) {
2510         return false;
2511       }
2512       args.rval().setObjectOrNull(fun);
2513       break;
2514     }
2515     case TableRepr::Ref: {
2516       args.rval().set(UnboxAnyRef(table.getAnyRef(index)));
2517       break;
2518     }
2519   }
2520   return true;
2521 }
2522 
2523 /* static */
get(JSContext * cx,unsigned argc,Value * vp)2524 bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) {
2525   CallArgs args = CallArgsFromVp(argc, vp);
2526   return CallNonGenericMethod<IsTable, getImpl>(cx, args);
2527 }
2528 
2529 /* static */
setImpl(JSContext * cx,const CallArgs & args)2530 bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
2531   RootedWasmTableObject tableObj(
2532       cx, &args.thisv().toObject().as<WasmTableObject>());
2533   Table& table = tableObj->table();
2534 
2535   if (!args.requireAtLeast(cx, "WebAssembly.Table.set", 2)) {
2536     return false;
2537   }
2538 
2539   uint32_t index;
2540   if (!ToTableIndex(cx, args.get(0), table, "set index", &index)) {
2541     return false;
2542   }
2543 
2544   MOZ_ASSERT(index < MaxTableLength);
2545   static_assert(MaxTableLength < UINT32_MAX, "Invariant");
2546 
2547   RootedValue fillValue(cx, args[1]);
2548   RootedFunction fun(cx);
2549   RootedAnyRef any(cx, AnyRef::null());
2550   if (!CheckRefType(cx, ToElemValType(table.kind()).refTypeKind(), fillValue,
2551                     &fun, &any)) {
2552     return false;
2553   }
2554   switch (table.kind()) {
2555     case TableKind::AsmJS:
2556       MOZ_CRASH("Should not happen");
2557     case TableKind::FuncRef:
2558       table.fillFuncRef(index, 1, FuncRef::fromJSFunction(fun), cx);
2559       break;
2560     case TableKind::AnyRef:
2561       table.fillAnyRef(index, 1, any);
2562       break;
2563   }
2564 
2565   args.rval().setUndefined();
2566   return true;
2567 }
2568 
2569 /* static */
set(JSContext * cx,unsigned argc,Value * vp)2570 bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) {
2571   CallArgs args = CallArgsFromVp(argc, vp);
2572   return CallNonGenericMethod<IsTable, setImpl>(cx, args);
2573 }
2574 
2575 /* static */
growImpl(JSContext * cx,const CallArgs & args)2576 bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
2577   RootedWasmTableObject tableObj(
2578       cx, &args.thisv().toObject().as<WasmTableObject>());
2579   Table& table = tableObj->table();
2580 
2581   if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) {
2582     return false;
2583   }
2584 
2585   uint32_t delta;
2586   if (!EnforceRangeU32(cx, args.get(0), "Table", "grow delta", &delta)) {
2587     return false;
2588   }
2589 
2590   uint32_t oldLength = table.grow(delta);
2591 
2592   if (oldLength == uint32_t(-1)) {
2593     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
2594                              "table");
2595     return false;
2596   }
2597 
2598   RootedValue fillValue(cx);
2599   fillValue.setNull();
2600   if (args.length() > 1) {
2601     fillValue = args[1];
2602   }
2603 
2604   MOZ_ASSERT(delta <= MaxTableLength);              // grow() should ensure this
2605   MOZ_ASSERT(oldLength <= MaxTableLength - delta);  // ditto
2606 
2607   static_assert(MaxTableLength < UINT32_MAX, "Invariant");
2608 
2609   if (!fillValue.isNull()) {
2610     RootedFunction fun(cx);
2611     RootedAnyRef any(cx, AnyRef::null());
2612     if (!CheckRefType(cx, ToElemValType(table.kind()).refTypeKind(), fillValue,
2613                       &fun, &any)) {
2614       return false;
2615     }
2616     switch (table.repr()) {
2617       case TableRepr::Func:
2618         MOZ_ASSERT(table.kind() == TableKind::FuncRef);
2619         table.fillFuncRef(oldLength, delta, FuncRef::fromJSFunction(fun), cx);
2620         break;
2621       case TableRepr::Ref:
2622         table.fillAnyRef(oldLength, delta, any);
2623         break;
2624     }
2625   }
2626 
2627 #ifdef DEBUG
2628   if (fillValue.isNull()) {
2629     switch (table.repr()) {
2630       case TableRepr::Func:
2631         for (uint32_t index = oldLength; index < oldLength + delta; index++) {
2632           MOZ_ASSERT(table.getFuncRef(index).code == nullptr);
2633         }
2634         break;
2635       case TableRepr::Ref:
2636         for (uint32_t index = oldLength; index < oldLength + delta; index++) {
2637           MOZ_ASSERT(table.getAnyRef(index).isNull());
2638         }
2639         break;
2640     }
2641   }
2642 #endif
2643 
2644   args.rval().setInt32(oldLength);
2645   return true;
2646 }
2647 
2648 /* static */
grow(JSContext * cx,unsigned argc,Value * vp)2649 bool WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp) {
2650   CallArgs args = CallArgsFromVp(argc, vp);
2651   return CallNonGenericMethod<IsTable, growImpl>(cx, args);
2652 }
2653 
2654 const JSFunctionSpec WasmTableObject::methods[] = {
2655     JS_FN("get", WasmTableObject::get, 1, JSPROP_ENUMERATE),
2656     JS_FN("set", WasmTableObject::set, 2, JSPROP_ENUMERATE),
2657     JS_FN("grow", WasmTableObject::grow, 1, JSPROP_ENUMERATE), JS_FS_END};
2658 
2659 const JSFunctionSpec WasmTableObject::static_methods[] = {JS_FS_END};
2660 
table() const2661 Table& WasmTableObject::table() const {
2662   return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
2663 }
2664 
2665 // ============================================================================
2666 // WebAssembly.global class and methods
2667 
2668 const JSClassOps WasmGlobalObject::classOps_ = {
2669     nullptr,                     // addProperty
2670     nullptr,                     // delProperty
2671     nullptr,                     // enumerate
2672     nullptr,                     // newEnumerate
2673     nullptr,                     // resolve
2674     nullptr,                     // mayResolve
2675     WasmGlobalObject::finalize,  // finalize
2676     nullptr,                     // call
2677     nullptr,                     // hasInstance
2678     nullptr,                     // construct
2679     WasmGlobalObject::trace,     // trace
2680 };
2681 
2682 const JSClass WasmGlobalObject::class_ = {
2683     "WebAssembly.Global",
2684     JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
2685         JSCLASS_BACKGROUND_FINALIZE,
2686     &WasmGlobalObject::classOps_, &WasmGlobalObject::classSpec_};
2687 
2688 const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_;
2689 
2690 static constexpr char WasmGlobalName[] = "Global";
2691 
2692 const ClassSpec WasmGlobalObject::classSpec_ = {
2693     CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>,
2694     GenericCreatePrototype<WasmGlobalObject>,
2695     WasmGlobalObject::static_methods,
2696     nullptr,
2697     WasmGlobalObject::methods,
2698     WasmGlobalObject::properties,
2699     nullptr,
2700     ClassSpec::DontDefineConstructor};
2701 
2702 /* static */
trace(JSTracer * trc,JSObject * obj)2703 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
2704   WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
2705   if (global->isNewborn()) {
2706     // This can happen while we're allocating the object, in which case
2707     // every single slot of the object is not defined yet. In particular,
2708     // there's nothing to trace yet.
2709     return;
2710   }
2711   switch (global->type().kind()) {
2712     case ValType::I32:
2713     case ValType::F32:
2714     case ValType::I64:
2715     case ValType::F64:
2716     case ValType::V128:
2717       break;
2718     case ValType::Ref:
2719       switch (global->type().refTypeKind()) {
2720         case RefType::Func:
2721         case RefType::Any:
2722           if (!global->cell()->ref.isNull()) {
2723             // TODO/AnyRef-boxing: With boxed immediates and strings, the write
2724             // barrier is going to have to be more complicated.
2725             ASSERT_ANYREF_IS_JSOBJECT;
2726             TraceManuallyBarrieredEdge(trc,
2727                                        global->cell()->ref.asJSObjectAddress(),
2728                                        "wasm reference-typed global");
2729           }
2730           break;
2731         case RefType::TypeIndex:
2732           MOZ_CRASH("Ref NYI");
2733       }
2734       break;
2735   }
2736 }
2737 
2738 /* static */
finalize(JSFreeOp * fop,JSObject * obj)2739 void WasmGlobalObject::finalize(JSFreeOp* fop, JSObject* obj) {
2740   WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
2741   if (!global->isNewborn()) {
2742     fop->delete_(obj, global->cell(), MemoryUse::WasmGlobalCell);
2743   }
2744 }
2745 
2746 /* static */
create(JSContext * cx,HandleVal hval,bool isMutable,HandleObject proto)2747 WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal hval,
2748                                            bool isMutable, HandleObject proto) {
2749   AutoSetNewObjectMetadata metadata(cx);
2750   RootedWasmGlobalObject obj(
2751       cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
2752   if (!obj) {
2753     return nullptr;
2754   }
2755 
2756   MOZ_ASSERT(obj->isNewborn());
2757   MOZ_ASSERT(obj->isTenured(), "assumed by global.set post barriers");
2758 
2759   // It's simpler to initialize the cell after the object has been created,
2760   // to avoid needing to root the cell before the object creation.
2761 
2762   Cell* cell = js_new<Cell>();
2763   if (!cell) {
2764     ReportOutOfMemory(cx);
2765     return nullptr;
2766   }
2767 
2768   const Val& val = hval.get();
2769   switch (val.type().kind()) {
2770     case ValType::I32:
2771       cell->i32 = val.i32();
2772       break;
2773     case ValType::I64:
2774       cell->i64 = val.i64();
2775       break;
2776     case ValType::F32:
2777       cell->f32 = val.f32();
2778       break;
2779     case ValType::F64:
2780       cell->f64 = val.f64();
2781       break;
2782     case ValType::Ref:
2783       switch (val.type().refTypeKind()) {
2784         case RefType::Func:
2785         case RefType::Any:
2786           MOZ_ASSERT(cell->ref.isNull(), "no prebarriers needed");
2787           cell->ref = val.ref();
2788           if (!cell->ref.isNull()) {
2789             // TODO/AnyRef-boxing: With boxed immediates and strings, the write
2790             // barrier is going to have to be more complicated.
2791             ASSERT_ANYREF_IS_JSOBJECT;
2792             JSObject::writeBarrierPost(cell->ref.asJSObjectAddress(), nullptr,
2793                                        cell->ref.asJSObject());
2794           }
2795           break;
2796         case RefType::TypeIndex:
2797           MOZ_CRASH("Ref NYI");
2798       }
2799       break;
2800     case ValType::V128:
2801       cell->v128 = val.v128();
2802       break;
2803   }
2804   obj->initReservedSlot(TYPE_SLOT,
2805                         Int32Value(int32_t(val.type().bitsUnsafe())));
2806   obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
2807   InitReservedSlot(obj, CELL_SLOT, cell, MemoryUse::WasmGlobalCell);
2808 
2809   MOZ_ASSERT(!obj->isNewborn());
2810 
2811   return obj;
2812 }
2813 
2814 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)2815 bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2816   CallArgs args = CallArgsFromVp(argc, vp);
2817 
2818   if (!ThrowIfNotConstructing(cx, args, "Global")) {
2819     return false;
2820   }
2821 
2822   if (!args.requireAtLeast(cx, "WebAssembly.Global", 1)) {
2823     return false;
2824   }
2825 
2826   if (!args.get(0).isObject()) {
2827     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2828                              JSMSG_WASM_BAD_DESC_ARG, "global");
2829     return false;
2830   }
2831 
2832   RootedObject obj(cx, &args[0].toObject());
2833 
2834   // Extract properties in lexicographic order per spec.
2835 
2836   RootedValue mutableVal(cx);
2837   if (!JS_GetProperty(cx, obj, "mutable", &mutableVal)) {
2838     return false;
2839   }
2840 
2841   RootedValue typeVal(cx);
2842   if (!JS_GetProperty(cx, obj, "value", &typeVal)) {
2843     return false;
2844   }
2845 
2846   RootedString typeStr(cx, ToString(cx, typeVal));
2847   if (!typeStr) {
2848     return false;
2849   }
2850 
2851   RootedLinearString typeLinearStr(cx, typeStr->ensureLinear(cx));
2852   if (!typeLinearStr) {
2853     return false;
2854   }
2855 
2856   ValType globalType;
2857   if (StringEqualsLiteral(typeLinearStr, "i32")) {
2858     globalType = ValType::I32;
2859   } else if (args.length() == 1 && StringEqualsLiteral(typeLinearStr, "i64")) {
2860     // For the time being, i64 is allowed only if there is not an
2861     // initializing value.
2862     globalType = ValType::I64;
2863   } else if (StringEqualsLiteral(typeLinearStr, "f32")) {
2864     globalType = ValType::F32;
2865   } else if (StringEqualsLiteral(typeLinearStr, "f64")) {
2866     globalType = ValType::F64;
2867   } else if (StringEqualsLiteral(typeLinearStr, "i64")) {
2868     globalType = ValType::I64;
2869 #ifdef ENABLE_WASM_SIMD
2870   } else if (SimdAvailable(cx) && StringEqualsLiteral(typeLinearStr, "v128")) {
2871     globalType = ValType::V128;
2872 #endif
2873 #ifdef ENABLE_WASM_REFTYPES
2874   } else if (ReftypesAvailable(cx) &&
2875              StringEqualsLiteral(typeLinearStr, "funcref")) {
2876     globalType = RefType::func();
2877   } else if (ReftypesAvailable(cx) &&
2878              StringEqualsLiteral(typeLinearStr, "externref")) {
2879     globalType = RefType::any();
2880 #endif
2881   } else {
2882     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2883                              JSMSG_WASM_BAD_GLOBAL_TYPE);
2884     return false;
2885   }
2886 
2887   bool isMutable = ToBoolean(mutableVal);
2888 
2889   // Extract the initial value, or provide a suitable default.
2890   RootedVal globalVal(cx);
2891 
2892   // Initialize with default value.
2893   switch (globalType.kind()) {
2894     case ValType::I32:
2895       globalVal = Val(uint32_t(0));
2896       break;
2897     case ValType::I64:
2898       globalVal = Val(uint64_t(0));
2899       break;
2900     case ValType::F32:
2901       globalVal = Val(float(0.0));
2902       break;
2903     case ValType::F64:
2904       globalVal = Val(double(0.0));
2905       break;
2906     case ValType::V128:
2907       globalVal = Val(V128());
2908       break;
2909     case ValType::Ref:
2910       switch (globalType.refTypeKind()) {
2911         case RefType::Func:
2912           globalVal = Val(RefType::func(), AnyRef::null());
2913           break;
2914         case RefType::Any:
2915           globalVal = Val(RefType::any(), AnyRef::null());
2916           break;
2917         case RefType::TypeIndex:
2918           MOZ_CRASH("Ref NYI");
2919       }
2920       break;
2921   }
2922 
2923   // Override with non-undefined value, if provided.
2924   RootedValue valueVal(cx, args.get(1));
2925   if (!valueVal.isUndefined() ||
2926       (args.length() >= 2 && globalType.isReference())) {
2927     if (globalType == ValType::V128) {
2928       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2929                                JSMSG_WASM_BAD_VAL_TYPE);
2930       return false;
2931     }
2932     if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal)) {
2933       return false;
2934     }
2935   }
2936 
2937   RootedObject proto(cx);
2938   if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmGlobal,
2939                                           &proto)) {
2940     return false;
2941   }
2942   if (!proto) {
2943     proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal);
2944   }
2945 
2946   WasmGlobalObject* global =
2947       WasmGlobalObject::create(cx, globalVal, isMutable, proto);
2948   if (!global) {
2949     return false;
2950   }
2951 
2952   args.rval().setObject(*global);
2953   return true;
2954 }
2955 
IsGlobal(HandleValue v)2956 static bool IsGlobal(HandleValue v) {
2957   return v.isObject() && v.toObject().is<WasmGlobalObject>();
2958 }
2959 
2960 /* static */
valueGetterImpl(JSContext * cx,const CallArgs & args)2961 bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) {
2962   switch (args.thisv().toObject().as<WasmGlobalObject>().type().kind()) {
2963     case ValType::I32:
2964     case ValType::I64:
2965     case ValType::F32:
2966     case ValType::F64:
2967       args.thisv().toObject().as<WasmGlobalObject>().value(cx, args.rval());
2968       return true;
2969     case ValType::V128:
2970       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2971                                JSMSG_WASM_BAD_VAL_TYPE);
2972       return false;
2973     case ValType::Ref:
2974       switch (
2975           args.thisv().toObject().as<WasmGlobalObject>().type().refTypeKind()) {
2976         case RefType::Func:
2977         case RefType::Any:
2978           args.thisv().toObject().as<WasmGlobalObject>().value(cx, args.rval());
2979           return true;
2980         case RefType::TypeIndex:
2981           MOZ_CRASH("Ref NYI");
2982       }
2983       break;
2984   }
2985   MOZ_CRASH();
2986 }
2987 
2988 /* static */
valueGetter(JSContext * cx,unsigned argc,Value * vp)2989 bool WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp) {
2990   CallArgs args = CallArgsFromVp(argc, vp);
2991   return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
2992 }
2993 
2994 /* static */
valueSetterImpl(JSContext * cx,const CallArgs & args)2995 bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) {
2996   if (!args.requireAtLeast(cx, "WebAssembly.Global setter", 1)) {
2997     return false;
2998   }
2999 
3000   RootedWasmGlobalObject global(
3001       cx, &args.thisv().toObject().as<WasmGlobalObject>());
3002   if (!global->isMutable()) {
3003     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3004                              JSMSG_WASM_GLOBAL_IMMUTABLE);
3005     return false;
3006   }
3007 
3008   if (global->type() == ValType::V128) {
3009     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3010                              JSMSG_WASM_BAD_VAL_TYPE);
3011     return false;
3012   }
3013 
3014   RootedVal val(cx);
3015   if (!ToWebAssemblyValue(cx, global->type(), args.get(0), &val)) {
3016     return false;
3017   }
3018   global->setVal(cx, val);
3019 
3020   args.rval().setUndefined();
3021   return true;
3022 }
3023 
3024 /* static */
valueSetter(JSContext * cx,unsigned argc,Value * vp)3025 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) {
3026   CallArgs args = CallArgsFromVp(argc, vp);
3027   return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
3028 }
3029 
3030 const JSPropertySpec WasmGlobalObject::properties[] = {
3031     JS_PSGS("value", WasmGlobalObject::valueGetter,
3032             WasmGlobalObject::valueSetter, JSPROP_ENUMERATE),
3033     JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY),
3034     JS_PS_END};
3035 
3036 const JSFunctionSpec WasmGlobalObject::methods[] = {
3037     JS_FN(js_valueOf_str, WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE),
3038     JS_FS_END};
3039 
3040 const JSFunctionSpec WasmGlobalObject::static_methods[] = {JS_FS_END};
3041 
type() const3042 ValType WasmGlobalObject::type() const {
3043   return ValType::fromBitsUnsafe(getReservedSlot(TYPE_SLOT).toInt32());
3044 }
3045 
isMutable() const3046 bool WasmGlobalObject::isMutable() const {
3047   return getReservedSlot(MUTABLE_SLOT).toBoolean();
3048 }
3049 
setVal(JSContext * cx,wasm::HandleVal hval)3050 void WasmGlobalObject::setVal(JSContext* cx, wasm::HandleVal hval) {
3051   const Val& val = hval.get();
3052   Cell* cell = this->cell();
3053   MOZ_ASSERT(type() == val.type());
3054   switch (type().kind()) {
3055     case ValType::I32:
3056       cell->i32 = val.i32();
3057       break;
3058     case ValType::F32:
3059       cell->f32 = val.f32();
3060       break;
3061     case ValType::F64:
3062       cell->f64 = val.f64();
3063       break;
3064     case ValType::I64:
3065       cell->i64 = val.i64();
3066       break;
3067     case ValType::V128:
3068       MOZ_CRASH("unexpected v128 when setting global's value");
3069     case ValType::Ref:
3070       switch (this->type().refTypeKind()) {
3071         case RefType::Func:
3072         case RefType::Any: {
3073           AnyRef prevPtr = cell->ref;
3074           // TODO/AnyRef-boxing: With boxed immediates and strings, the write
3075           // barrier is going to have to be more complicated.
3076           ASSERT_ANYREF_IS_JSOBJECT;
3077           JSObject::writeBarrierPre(prevPtr.asJSObject());
3078           cell->ref = val.ref();
3079           if (!cell->ref.isNull()) {
3080             JSObject::writeBarrierPost(cell->ref.asJSObjectAddress(),
3081                                        prevPtr.asJSObject(),
3082                                        cell->ref.asJSObject());
3083           }
3084           break;
3085         }
3086         case RefType::TypeIndex: {
3087           MOZ_CRASH("Ref NYI");
3088         }
3089       }
3090       break;
3091   }
3092 }
3093 
val(MutableHandleVal outval) const3094 void WasmGlobalObject::val(MutableHandleVal outval) const {
3095   Cell* cell = this->cell();
3096   switch (type().kind()) {
3097     case ValType::I32:
3098       outval.set(Val(uint32_t(cell->i32)));
3099       return;
3100     case ValType::I64:
3101       outval.set(Val(uint64_t(cell->i64)));
3102       return;
3103     case ValType::V128:
3104       outval.set(Val(cell->v128));
3105       return;
3106     case ValType::F32:
3107       outval.set(Val(cell->f32));
3108       return;
3109     case ValType::F64:
3110       outval.set(Val(cell->f64));
3111       return;
3112     case ValType::Ref:
3113       switch (type().refTypeKind()) {
3114         case RefType::Func:
3115           outval.set(Val(RefType::func(), cell->ref));
3116           return;
3117         case RefType::Any:
3118           outval.set(Val(RefType::any(), cell->ref));
3119           return;
3120         case RefType::TypeIndex:
3121           MOZ_CRASH("Ref NYI");
3122       }
3123       break;
3124   }
3125   MOZ_CRASH("unexpected Global type");
3126 }
3127 
value(JSContext * cx,MutableHandleValue out)3128 bool WasmGlobalObject::value(JSContext* cx, MutableHandleValue out) {
3129   RootedVal result(cx);
3130   val(&result);
3131   return ToJSValue(cx, result.get(), out);
3132 }
3133 
cell() const3134 WasmGlobalObject::Cell* WasmGlobalObject::cell() const {
3135   return reinterpret_cast<Cell*>(getReservedSlot(CELL_SLOT).toPrivate());
3136 }
3137 
3138 // ============================================================================
3139 // WebAssembly class and static methods
3140 
WebAssembly_toSource(JSContext * cx,unsigned argc,Value * vp)3141 static bool WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) {
3142   CallArgs args = CallArgsFromVp(argc, vp);
3143   args.rval().setString(cx->names().WebAssembly);
3144   return true;
3145 }
3146 
RejectWithPendingException(JSContext * cx,Handle<PromiseObject * > promise)3147 static bool RejectWithPendingException(JSContext* cx,
3148                                        Handle<PromiseObject*> promise) {
3149   if (!cx->isExceptionPending()) {
3150     return false;
3151   }
3152 
3153   RootedValue rejectionValue(cx);
3154   if (!GetAndClearException(cx, &rejectionValue)) {
3155     return false;
3156   }
3157 
3158   return PromiseObject::reject(cx, promise, rejectionValue);
3159 }
3160 
Reject(JSContext * cx,const CompileArgs & args,Handle<PromiseObject * > promise,const UniqueChars & error)3161 static bool Reject(JSContext* cx, const CompileArgs& args,
3162                    Handle<PromiseObject*> promise, const UniqueChars& error) {
3163   if (!error) {
3164     ReportOutOfMemory(cx);
3165     return RejectWithPendingException(cx, promise);
3166   }
3167 
3168   RootedObject stack(cx, promise->allocationSite());
3169   RootedString filename(
3170       cx, JS_NewStringCopyZ(cx, args.scriptedCaller.filename.get()));
3171   if (!filename) {
3172     return false;
3173   }
3174 
3175   unsigned line = args.scriptedCaller.line;
3176 
3177   // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
3178   // way to create an ErrorObject for an arbitrary error code with multiple
3179   // replacements.
3180   UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
3181   if (!str) {
3182     return false;
3183   }
3184 
3185   size_t len = strlen(str.get());
3186   RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len));
3187   if (!message) {
3188     return false;
3189   }
3190 
3191   RootedObject errorObj(
3192       cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, filename, 0,
3193                               line, 0, nullptr, message));
3194   if (!errorObj) {
3195     return false;
3196   }
3197 
3198   RootedValue rejectionValue(cx, ObjectValue(*errorObj));
3199   return PromiseObject::reject(cx, promise, rejectionValue);
3200 }
3201 
LogAsync(JSContext * cx,const char * funcName,const Module & module)3202 static void LogAsync(JSContext* cx, const char* funcName,
3203                      const Module& module) {
3204   Log(cx, "async %s succeeded%s", funcName,
3205       module.loggingDeserialized() ? " (loaded from cache)" : "");
3206 }
3207 
3208 enum class Ret { Pair, Instance };
3209 
3210 class AsyncInstantiateTask : public OffThreadPromiseTask {
3211   SharedModule module_;
3212   PersistentRooted<ImportValues> imports_;
3213   Ret ret_;
3214 
3215  public:
AsyncInstantiateTask(JSContext * cx,const Module & module,Ret ret,Handle<PromiseObject * > promise)3216   AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret,
3217                        Handle<PromiseObject*> promise)
3218       : OffThreadPromiseTask(cx, promise),
3219         module_(&module),
3220         imports_(cx),
3221         ret_(ret) {}
3222 
imports()3223   ImportValues& imports() { return imports_.get(); }
3224 
resolve(JSContext * cx,Handle<PromiseObject * > promise)3225   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
3226     RootedObject instanceProto(
3227         cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
3228 
3229     RootedWasmInstanceObject instanceObj(cx);
3230     if (!module_->instantiate(cx, imports_.get(), instanceProto,
3231                               &instanceObj)) {
3232       return RejectWithPendingException(cx, promise);
3233     }
3234 
3235     RootedValue resolutionValue(cx);
3236     if (ret_ == Ret::Instance) {
3237       resolutionValue = ObjectValue(*instanceObj);
3238     } else {
3239       RootedObject resultObj(cx, JS_NewPlainObject(cx));
3240       if (!resultObj) {
3241         return RejectWithPendingException(cx, promise);
3242       }
3243 
3244       RootedObject moduleProto(
3245           cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
3246       RootedObject moduleObj(
3247           cx, WasmModuleObject::create(cx, *module_, moduleProto));
3248       if (!moduleObj) {
3249         return RejectWithPendingException(cx, promise);
3250       }
3251 
3252       RootedValue val(cx, ObjectValue(*moduleObj));
3253       if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
3254         return RejectWithPendingException(cx, promise);
3255       }
3256 
3257       val = ObjectValue(*instanceObj);
3258       if (!JS_DefineProperty(cx, resultObj, "instance", val,
3259                              JSPROP_ENUMERATE)) {
3260         return RejectWithPendingException(cx, promise);
3261       }
3262 
3263       resolutionValue = ObjectValue(*resultObj);
3264     }
3265 
3266     if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
3267       return RejectWithPendingException(cx, promise);
3268     }
3269 
3270     LogAsync(cx, "instantiate", *module_);
3271     return true;
3272   }
3273 };
3274 
AsyncInstantiate(JSContext * cx,const Module & module,HandleObject importObj,Ret ret,Handle<PromiseObject * > promise)3275 static bool AsyncInstantiate(JSContext* cx, const Module& module,
3276                              HandleObject importObj, Ret ret,
3277                              Handle<PromiseObject*> promise) {
3278   auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise);
3279   if (!task || !task->init(cx)) {
3280     return false;
3281   }
3282 
3283   if (!GetImports(cx, module, importObj, &task->imports())) {
3284     return RejectWithPendingException(cx, promise);
3285   }
3286 
3287   task.release()->dispatchResolveAndDestroy();
3288   return true;
3289 }
3290 
ResolveCompile(JSContext * cx,const Module & module,Handle<PromiseObject * > promise)3291 static bool ResolveCompile(JSContext* cx, const Module& module,
3292                            Handle<PromiseObject*> promise) {
3293   RootedObject proto(
3294       cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
3295   RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
3296   if (!moduleObj) {
3297     return RejectWithPendingException(cx, promise);
3298   }
3299 
3300   RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
3301   if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
3302     return RejectWithPendingException(cx, promise);
3303   }
3304 
3305   LogAsync(cx, "compile", module);
3306   return true;
3307 }
3308 
3309 struct CompileBufferTask : PromiseHelperTask {
3310   MutableBytes bytecode;
3311   SharedCompileArgs compileArgs;
3312   UniqueChars error;
3313   UniqueCharsVector warnings;
3314   SharedModule module;
3315   bool instantiate;
3316   PersistentRootedObject importObj;
3317 
CompileBufferTaskCompileBufferTask3318   CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise,
3319                     HandleObject importObj)
3320       : PromiseHelperTask(cx, promise),
3321         instantiate(true),
3322         importObj(cx, importObj) {}
3323 
CompileBufferTaskCompileBufferTask3324   CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise)
3325       : PromiseHelperTask(cx, promise), instantiate(false) {}
3326 
initCompileBufferTask3327   bool init(JSContext* cx, const char* introducer) {
3328     compileArgs = InitCompileArgs(cx, introducer);
3329     if (!compileArgs) {
3330       return false;
3331     }
3332     return PromiseHelperTask::init(cx);
3333   }
3334 
executeCompileBufferTask3335   void execute() override {
3336     module = CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
3337   }
3338 
resolveCompileBufferTask3339   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
3340     if (!module) {
3341       return Reject(cx, *compileArgs, promise, error);
3342     }
3343     if (!ReportCompileWarnings(cx, warnings)) {
3344       return false;
3345     }
3346     if (instantiate) {
3347       return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise);
3348     }
3349     return ResolveCompile(cx, *module, promise);
3350   }
3351 };
3352 
RejectWithPendingException(JSContext * cx,Handle<PromiseObject * > promise,CallArgs & callArgs)3353 static bool RejectWithPendingException(JSContext* cx,
3354                                        Handle<PromiseObject*> promise,
3355                                        CallArgs& callArgs) {
3356   if (!RejectWithPendingException(cx, promise)) {
3357     return false;
3358   }
3359 
3360   callArgs.rval().setObject(*promise);
3361   return true;
3362 }
3363 
EnsurePromiseSupport(JSContext * cx)3364 static bool EnsurePromiseSupport(JSContext* cx) {
3365   if (!cx->runtime()->offThreadPromiseState.ref().initialized()) {
3366     JS_ReportErrorASCII(
3367         cx, "WebAssembly Promise APIs not supported in this runtime.");
3368     return false;
3369   }
3370   return true;
3371 }
3372 
GetBufferSource(JSContext * cx,CallArgs callArgs,const char * name,MutableBytes * bytecode)3373 static bool GetBufferSource(JSContext* cx, CallArgs callArgs, const char* name,
3374                             MutableBytes* bytecode) {
3375   if (!callArgs.requireAtLeast(cx, name, 1)) {
3376     return false;
3377   }
3378 
3379   if (!callArgs[0].isObject()) {
3380     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3381                              JSMSG_WASM_BAD_BUF_ARG);
3382     return false;
3383   }
3384 
3385   return GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
3386                          bytecode);
3387 }
3388 
WebAssembly_compile(JSContext * cx,unsigned argc,Value * vp)3389 static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
3390   if (!EnsurePromiseSupport(cx)) {
3391     return false;
3392   }
3393 
3394   Log(cx, "async compile() started");
3395 
3396   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
3397   if (!promise) {
3398     return false;
3399   }
3400 
3401   auto task = cx->make_unique<CompileBufferTask>(cx, promise);
3402   if (!task || !task->init(cx, "WebAssembly.compile")) {
3403     return false;
3404   }
3405 
3406   CallArgs callArgs = CallArgsFromVp(argc, vp);
3407 
3408   if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode)) {
3409     return RejectWithPendingException(cx, promise, callArgs);
3410   }
3411 
3412   if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
3413     return false;
3414   }
3415 
3416   callArgs.rval().setObject(*promise);
3417   return true;
3418 }
3419 
GetInstantiateArgs(JSContext * cx,CallArgs callArgs,MutableHandleObject firstArg,MutableHandleObject importObj)3420 static bool GetInstantiateArgs(JSContext* cx, CallArgs callArgs,
3421                                MutableHandleObject firstArg,
3422                                MutableHandleObject importObj) {
3423   if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1)) {
3424     return false;
3425   }
3426 
3427   if (!callArgs[0].isObject()) {
3428     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3429                              JSMSG_WASM_BAD_BUF_MOD_ARG);
3430     return false;
3431   }
3432 
3433   firstArg.set(&callArgs[0].toObject());
3434 
3435   return GetImportArg(cx, callArgs, importObj);
3436 }
3437 
WebAssembly_instantiate(JSContext * cx,unsigned argc,Value * vp)3438 static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
3439   if (!EnsurePromiseSupport(cx)) {
3440     return false;
3441   }
3442 
3443   Log(cx, "async instantiate() started");
3444 
3445   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
3446   if (!promise) {
3447     return false;
3448   }
3449 
3450   CallArgs callArgs = CallArgsFromVp(argc, vp);
3451 
3452   RootedObject firstArg(cx);
3453   RootedObject importObj(cx);
3454   if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
3455     return RejectWithPendingException(cx, promise, callArgs);
3456   }
3457 
3458   const Module* module;
3459   if (IsModuleObject(firstArg, &module)) {
3460     if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) {
3461       return false;
3462     }
3463   } else {
3464     auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
3465     if (!task || !task->init(cx, "WebAssembly.instantiate")) {
3466       return false;
3467     }
3468 
3469     if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG,
3470                          &task->bytecode)) {
3471       return RejectWithPendingException(cx, promise, callArgs);
3472     }
3473 
3474     if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
3475       return false;
3476     }
3477   }
3478 
3479   callArgs.rval().setObject(*promise);
3480   return true;
3481 }
3482 
WebAssembly_validate(JSContext * cx,unsigned argc,Value * vp)3483 static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) {
3484   CallArgs callArgs = CallArgsFromVp(argc, vp);
3485 
3486   MutableBytes bytecode;
3487   if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode)) {
3488     return false;
3489   }
3490 
3491   UniqueChars error;
3492   bool validated = Validate(cx, *bytecode, &error);
3493 
3494   // If the reason for validation failure was OOM (signalled by null error
3495   // message), report out-of-memory so that validate's return is always
3496   // correct.
3497   if (!validated && !error) {
3498     ReportOutOfMemory(cx);
3499     return false;
3500   }
3501 
3502   if (error) {
3503     MOZ_ASSERT(!validated);
3504     Log(cx, "validate() failed with: %s", error.get());
3505   }
3506 
3507   callArgs.rval().setBoolean(validated);
3508   return true;
3509 }
3510 
EnsureStreamSupport(JSContext * cx)3511 static bool EnsureStreamSupport(JSContext* cx) {
3512   // This should match wasm::StreamingCompilationAvailable().
3513 
3514   if (!EnsurePromiseSupport(cx)) {
3515     return false;
3516   }
3517 
3518   if (!CanUseExtraThreads()) {
3519     JS_ReportErrorASCII(
3520         cx, "WebAssembly.compileStreaming not supported with --no-threads");
3521     return false;
3522   }
3523 
3524   if (!cx->runtime()->consumeStreamCallback) {
3525     JS_ReportErrorASCII(cx,
3526                         "WebAssembly streaming not supported in this runtime");
3527     return false;
3528   }
3529 
3530   return true;
3531 }
3532 
3533 // This value is chosen and asserted to be disjoint from any host error code.
3534 static const size_t StreamOOMCode = 0;
3535 
RejectWithStreamErrorNumber(JSContext * cx,size_t errorCode,Handle<PromiseObject * > promise)3536 static bool RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode,
3537                                         Handle<PromiseObject*> promise) {
3538   if (errorCode == StreamOOMCode) {
3539     ReportOutOfMemory(cx);
3540     return false;
3541   }
3542 
3543   cx->runtime()->reportStreamErrorCallback(cx, errorCode);
3544   return RejectWithPendingException(cx, promise);
3545 }
3546 
3547 class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer {
3548   // The stream progresses monotonically through these states; the helper
3549   // thread wait()s for streamState_ to reach Closed.
3550   enum StreamState { Env, Code, Tail, Closed };
3551   ExclusiveWaitableData<StreamState> streamState_;
3552 
3553   // Immutable:
3554   const bool instantiate_;
3555   const PersistentRootedObject importObj_;
3556 
3557   // Immutable after noteResponseURLs() which is called at most once before
3558   // first call on stream thread:
3559   const MutableCompileArgs compileArgs_;
3560 
3561   // Immutable after Env state:
3562   Bytes envBytes_;
3563   SectionRange codeSection_;
3564 
3565   // The code section vector is resized once during the Env state and filled
3566   // in chunk by chunk during the Code state, updating the end-pointer after
3567   // each chunk:
3568   Bytes codeBytes_;
3569   uint8_t* codeBytesEnd_;
3570   ExclusiveBytesPtr exclusiveCodeBytesEnd_;
3571 
3572   // Immutable after Tail state:
3573   Bytes tailBytes_;
3574   ExclusiveStreamEndData exclusiveStreamEnd_;
3575 
3576   // Written once before Closed state and read in Closed state on main thread:
3577   SharedModule module_;
3578   Maybe<size_t> streamError_;
3579   UniqueChars compileError_;
3580   UniqueCharsVector warnings_;
3581 
3582   // Set on stream thread and read racily on helper thread to abort compilation:
3583   Atomic<bool> streamFailed_;
3584 
3585   // Called on some thread before consumeChunk(), streamEnd(), streamError()):
3586 
noteResponseURLs(const char * url,const char * sourceMapUrl)3587   void noteResponseURLs(const char* url, const char* sourceMapUrl) override {
3588     if (url) {
3589       compileArgs_->scriptedCaller.filename = DuplicateString(url);
3590       compileArgs_->scriptedCaller.filenameIsURL = true;
3591     }
3592     if (sourceMapUrl) {
3593       compileArgs_->sourceMapURL = DuplicateString(sourceMapUrl);
3594     }
3595   }
3596 
3597   // Called on a stream thread:
3598 
3599   // Until StartOffThreadPromiseHelperTask succeeds, we are responsible for
3600   // dispatching ourselves back to the JS thread.
3601   //
3602   // Warning: After this function returns, 'this' can be deleted at any time, so
3603   // the caller must immediately return from the stream callback.
setClosedAndDestroyBeforeHelperThreadStarted()3604   void setClosedAndDestroyBeforeHelperThreadStarted() {
3605     streamState_.lock().get() = Closed;
3606     dispatchResolveAndDestroy();
3607   }
3608 
3609   // See setClosedAndDestroyBeforeHelperThreadStarted() comment.
rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber)3610   bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) {
3611     MOZ_ASSERT(streamState_.lock() == Env);
3612     MOZ_ASSERT(!streamError_);
3613     streamError_ = Some(errorNumber);
3614     setClosedAndDestroyBeforeHelperThreadStarted();
3615     return false;
3616   }
3617 
3618   // Once StartOffThreadPromiseHelperTask succeeds, the helper thread will
3619   // dispatchResolveAndDestroy() after execute() returns, but execute()
3620   // wait()s for state to be Closed.
3621   //
3622   // Warning: After this function returns, 'this' can be deleted at any time, so
3623   // the caller must immediately return from the stream callback.
setClosedAndDestroyAfterHelperThreadStarted()3624   void setClosedAndDestroyAfterHelperThreadStarted() {
3625     auto streamState = streamState_.lock();
3626     MOZ_ASSERT(streamState != Closed);
3627     streamState.get() = Closed;
3628     streamState.notify_one(/* stream closed */);
3629   }
3630 
3631   // See setClosedAndDestroyAfterHelperThreadStarted() comment.
rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber)3632   bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) {
3633     MOZ_ASSERT(!streamError_);
3634     streamError_ = Some(errorNumber);
3635     streamFailed_ = true;
3636     exclusiveCodeBytesEnd_.lock().notify_one();
3637     exclusiveStreamEnd_.lock().notify_one();
3638     setClosedAndDestroyAfterHelperThreadStarted();
3639     return false;
3640   }
3641 
consumeChunk(const uint8_t * begin,size_t length)3642   bool consumeChunk(const uint8_t* begin, size_t length) override {
3643     switch (streamState_.lock().get()) {
3644       case Env: {
3645         if (!envBytes_.append(begin, length)) {
3646           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
3647         }
3648 
3649         if (!StartsCodeSection(envBytes_.begin(), envBytes_.end(),
3650                                &codeSection_)) {
3651           return true;
3652         }
3653 
3654         uint32_t extraBytes = envBytes_.length() - codeSection_.start;
3655         if (extraBytes) {
3656           envBytes_.shrinkTo(codeSection_.start);
3657         }
3658 
3659         if (codeSection_.size > MaxCodeSectionBytes) {
3660           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
3661         }
3662 
3663         if (!codeBytes_.resize(codeSection_.size)) {
3664           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
3665         }
3666 
3667         codeBytesEnd_ = codeBytes_.begin();
3668         exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_;
3669 
3670         if (!StartOffThreadPromiseHelperTask(this)) {
3671           return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
3672         }
3673 
3674         // Set the state to Code iff StartOffThreadPromiseHelperTask()
3675         // succeeds so that the state tells us whether we are before or
3676         // after the helper thread started.
3677         streamState_.lock().get() = Code;
3678 
3679         if (extraBytes) {
3680           return consumeChunk(begin + length - extraBytes, extraBytes);
3681         }
3682 
3683         return true;
3684       }
3685       case Code: {
3686         size_t copyLength =
3687             std::min<size_t>(length, codeBytes_.end() - codeBytesEnd_);
3688         memcpy(codeBytesEnd_, begin, copyLength);
3689         codeBytesEnd_ += copyLength;
3690 
3691         {
3692           auto codeStreamEnd = exclusiveCodeBytesEnd_.lock();
3693           codeStreamEnd.get() = codeBytesEnd_;
3694           codeStreamEnd.notify_one();
3695         }
3696 
3697         if (codeBytesEnd_ != codeBytes_.end()) {
3698           return true;
3699         }
3700 
3701         streamState_.lock().get() = Tail;
3702 
3703         if (uint32_t extraBytes = length - copyLength) {
3704           return consumeChunk(begin + copyLength, extraBytes);
3705         }
3706 
3707         return true;
3708       }
3709       case Tail: {
3710         if (!tailBytes_.append(begin, length)) {
3711           return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode);
3712         }
3713 
3714         return true;
3715       }
3716       case Closed:
3717         MOZ_CRASH("consumeChunk() in Closed state");
3718     }
3719     MOZ_CRASH("unreachable");
3720   }
3721 
streamEnd(JS::OptimizedEncodingListener * tier2Listener)3722   void streamEnd(JS::OptimizedEncodingListener* tier2Listener) override {
3723     switch (streamState_.lock().get()) {
3724       case Env: {
3725         SharedBytes bytecode = js_new<ShareableBytes>(std::move(envBytes_));
3726         if (!bytecode) {
3727           rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
3728           return;
3729         }
3730         module_ =
3731             CompileBuffer(*compileArgs_, *bytecode, &compileError_, &warnings_);
3732         setClosedAndDestroyBeforeHelperThreadStarted();
3733         return;
3734       }
3735       case Code:
3736       case Tail:
3737         // Unlock exclusiveStreamEnd_ before locking streamState_.
3738         {
3739           auto streamEnd = exclusiveStreamEnd_.lock();
3740           MOZ_ASSERT(!streamEnd->reached);
3741           streamEnd->reached = true;
3742           streamEnd->tailBytes = &tailBytes_;
3743           streamEnd->tier2Listener = tier2Listener;
3744           streamEnd.notify_one();
3745         }
3746         setClosedAndDestroyAfterHelperThreadStarted();
3747         return;
3748       case Closed:
3749         MOZ_CRASH("streamEnd() in Closed state");
3750     }
3751   }
3752 
streamError(size_t errorCode)3753   void streamError(size_t errorCode) override {
3754     MOZ_ASSERT(errorCode != StreamOOMCode);
3755     switch (streamState_.lock().get()) {
3756       case Env:
3757         rejectAndDestroyBeforeHelperThreadStarted(errorCode);
3758         return;
3759       case Tail:
3760       case Code:
3761         rejectAndDestroyAfterHelperThreadStarted(errorCode);
3762         return;
3763       case Closed:
3764         MOZ_CRASH("streamError() in Closed state");
3765     }
3766   }
3767 
consumeOptimizedEncoding(const uint8_t * begin,size_t length)3768   void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override {
3769     module_ = Module::deserialize(begin, length);
3770 
3771     MOZ_ASSERT(streamState_.lock().get() == Env);
3772     setClosedAndDestroyBeforeHelperThreadStarted();
3773   }
3774 
3775   // Called on a helper thread:
3776 
execute()3777   void execute() override {
3778     module_ = CompileStreaming(*compileArgs_, envBytes_, codeBytes_,
3779                                exclusiveCodeBytesEnd_, exclusiveStreamEnd_,
3780                                streamFailed_, &compileError_, &warnings_);
3781 
3782     // When execute() returns, the CompileStreamTask will be dispatched
3783     // back to its JS thread to call resolve() and then be destroyed. We
3784     // can't let this happen until the stream has been closed lest
3785     // consumeChunk() or streamEnd() be called on a dead object.
3786     auto streamState = streamState_.lock();
3787     while (streamState != Closed) {
3788       streamState.wait(/* stream closed */);
3789     }
3790   }
3791 
3792   // Called on a JS thread after streaming compilation completes/errors:
3793 
resolve(JSContext * cx,Handle<PromiseObject * > promise)3794   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
3795     MOZ_ASSERT(streamState_.lock() == Closed);
3796 
3797     if (module_) {
3798       MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_);
3799       if (!ReportCompileWarnings(cx, warnings_)) {
3800         return false;
3801       }
3802       if (instantiate_) {
3803         return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise);
3804       }
3805       return ResolveCompile(cx, *module_, promise);
3806     }
3807 
3808     if (streamError_) {
3809       return RejectWithStreamErrorNumber(cx, *streamError_, promise);
3810     }
3811 
3812     return Reject(cx, *compileArgs_, promise, compileError_);
3813   }
3814 
3815  public:
CompileStreamTask(JSContext * cx,Handle<PromiseObject * > promise,CompileArgs & compileArgs,bool instantiate,HandleObject importObj)3816   CompileStreamTask(JSContext* cx, Handle<PromiseObject*> promise,
3817                     CompileArgs& compileArgs, bool instantiate,
3818                     HandleObject importObj)
3819       : PromiseHelperTask(cx, promise),
3820         streamState_(mutexid::WasmStreamStatus, Env),
3821         instantiate_(instantiate),
3822         importObj_(cx, importObj),
3823         compileArgs_(&compileArgs),
3824         codeSection_{},
3825         codeBytesEnd_(nullptr),
3826         exclusiveCodeBytesEnd_(mutexid::WasmCodeBytesEnd, nullptr),
3827         exclusiveStreamEnd_(mutexid::WasmStreamEnd),
3828         streamFailed_(false) {
3829     MOZ_ASSERT_IF(importObj_, instantiate_);
3830   }
3831 };
3832 
3833 // A short-lived object that captures the arguments of a
3834 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for
3835 // the Promise<Response> to resolve to a (hopefully) Promise.
3836 class ResolveResponseClosure : public NativeObject {
3837   static const unsigned COMPILE_ARGS_SLOT = 0;
3838   static const unsigned PROMISE_OBJ_SLOT = 1;
3839   static const unsigned INSTANTIATE_SLOT = 2;
3840   static const unsigned IMPORT_OBJ_SLOT = 3;
3841   static const JSClassOps classOps_;
3842 
finalize(JSFreeOp * fop,JSObject * obj)3843   static void finalize(JSFreeOp* fop, JSObject* obj) {
3844     auto& closure = obj->as<ResolveResponseClosure>();
3845     fop->release(obj, &closure.compileArgs(),
3846                  MemoryUse::WasmResolveResponseClosure);
3847   }
3848 
3849  public:
3850   static const unsigned RESERVED_SLOTS = 4;
3851   static const JSClass class_;
3852 
create(JSContext * cx,const CompileArgs & args,HandleObject promise,bool instantiate,HandleObject importObj)3853   static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args,
3854                                         HandleObject promise, bool instantiate,
3855                                         HandleObject importObj) {
3856     MOZ_ASSERT_IF(importObj, instantiate);
3857 
3858     AutoSetNewObjectMetadata metadata(cx);
3859     auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr);
3860     if (!obj) {
3861       return nullptr;
3862     }
3863 
3864     args.AddRef();
3865     InitReservedSlot(obj, COMPILE_ARGS_SLOT, const_cast<CompileArgs*>(&args),
3866                      MemoryUse::WasmResolveResponseClosure);
3867     obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise));
3868     obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate));
3869     obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj));
3870     return obj;
3871   }
3872 
compileArgs() const3873   CompileArgs& compileArgs() const {
3874     return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate();
3875   }
promise() const3876   PromiseObject& promise() const {
3877     return getReservedSlot(PROMISE_OBJ_SLOT).toObject().as<PromiseObject>();
3878   }
instantiate() const3879   bool instantiate() const {
3880     return getReservedSlot(INSTANTIATE_SLOT).toBoolean();
3881   }
importObj() const3882   JSObject* importObj() const {
3883     return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull();
3884   }
3885 };
3886 
3887 const JSClassOps ResolveResponseClosure::classOps_ = {
3888     nullptr,                           // addProperty
3889     nullptr,                           // delProperty
3890     nullptr,                           // enumerate
3891     nullptr,                           // newEnumerate
3892     nullptr,                           // resolve
3893     nullptr,                           // mayResolve
3894     ResolveResponseClosure::finalize,  // finalize
3895     nullptr,                           // call
3896     nullptr,                           // hasInstance
3897     nullptr,                           // construct
3898     nullptr,                           // trace
3899 };
3900 
3901 const JSClass ResolveResponseClosure::class_ = {
3902     "WebAssembly ResolveResponseClosure",
3903     JSCLASS_DELAY_METADATA_BUILDER |
3904         JSCLASS_HAS_RESERVED_SLOTS(ResolveResponseClosure::RESERVED_SLOTS) |
3905         JSCLASS_FOREGROUND_FINALIZE,
3906     &ResolveResponseClosure::classOps_,
3907 };
3908 
ToResolveResponseClosure(CallArgs args)3909 static ResolveResponseClosure* ToResolveResponseClosure(CallArgs args) {
3910   return &args.callee()
3911               .as<JSFunction>()
3912               .getExtendedSlot(0)
3913               .toObject()
3914               .as<ResolveResponseClosure>();
3915 }
3916 
RejectWithErrorNumber(JSContext * cx,uint32_t errorNumber,Handle<PromiseObject * > promise)3917 static bool RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber,
3918                                   Handle<PromiseObject*> promise) {
3919   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
3920   return RejectWithPendingException(cx, promise);
3921 }
3922 
ResolveResponse_OnFulfilled(JSContext * cx,unsigned argc,Value * vp)3923 static bool ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc,
3924                                         Value* vp) {
3925   CallArgs callArgs = CallArgsFromVp(argc, vp);
3926 
3927   Rooted<ResolveResponseClosure*> closure(cx,
3928                                           ToResolveResponseClosure(callArgs));
3929   Rooted<PromiseObject*> promise(cx, &closure->promise());
3930   CompileArgs& compileArgs = closure->compileArgs();
3931   bool instantiate = closure->instantiate();
3932   Rooted<JSObject*> importObj(cx, closure->importObj());
3933 
3934   auto task = cx->make_unique<CompileStreamTask>(cx, promise, compileArgs,
3935                                                  instantiate, importObj);
3936   if (!task || !task->init(cx)) {
3937     return false;
3938   }
3939 
3940   if (!callArgs.get(0).isObject()) {
3941     return RejectWithErrorNumber(cx, JSMSG_BAD_RESPONSE_VALUE, promise);
3942   }
3943 
3944   RootedObject response(cx, &callArgs.get(0).toObject());
3945   if (!cx->runtime()->consumeStreamCallback(cx, response, JS::MimeType::Wasm,
3946                                             task.get())) {
3947     return RejectWithPendingException(cx, promise);
3948   }
3949 
3950   Unused << task.release();
3951 
3952   callArgs.rval().setUndefined();
3953   return true;
3954 }
3955 
ResolveResponse_OnRejected(JSContext * cx,unsigned argc,Value * vp)3956 static bool ResolveResponse_OnRejected(JSContext* cx, unsigned argc,
3957                                        Value* vp) {
3958   CallArgs args = CallArgsFromVp(argc, vp);
3959 
3960   Rooted<ResolveResponseClosure*> closure(cx, ToResolveResponseClosure(args));
3961   Rooted<PromiseObject*> promise(cx, &closure->promise());
3962 
3963   if (!PromiseObject::reject(cx, promise, args.get(0))) {
3964     return false;
3965   }
3966 
3967   args.rval().setUndefined();
3968   return true;
3969 }
3970 
ResolveResponse(JSContext * cx,CallArgs callArgs,Handle<PromiseObject * > promise,bool instantiate=false,HandleObject importObj=nullptr)3971 static bool ResolveResponse(JSContext* cx, CallArgs callArgs,
3972                             Handle<PromiseObject*> promise,
3973                             bool instantiate = false,
3974                             HandleObject importObj = nullptr) {
3975   MOZ_ASSERT_IF(importObj, instantiate);
3976 
3977   const char* introducer = instantiate ? "WebAssembly.instantiateStreaming"
3978                                        : "WebAssembly.compileStreaming";
3979 
3980   SharedCompileArgs compileArgs = InitCompileArgs(cx, introducer);
3981   if (!compileArgs) {
3982     return false;
3983   }
3984 
3985   RootedObject closure(
3986       cx, ResolveResponseClosure::create(cx, *compileArgs, promise, instantiate,
3987                                          importObj));
3988   if (!closure) {
3989     return false;
3990   }
3991 
3992   RootedFunction onResolved(
3993       cx, NewNativeFunction(cx, ResolveResponse_OnFulfilled, 1, nullptr,
3994                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
3995   if (!onResolved) {
3996     return false;
3997   }
3998 
3999   RootedFunction onRejected(
4000       cx, NewNativeFunction(cx, ResolveResponse_OnRejected, 1, nullptr,
4001                             gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
4002   if (!onRejected) {
4003     return false;
4004   }
4005 
4006   onResolved->setExtendedSlot(0, ObjectValue(*closure));
4007   onRejected->setExtendedSlot(0, ObjectValue(*closure));
4008 
4009   RootedObject resolve(cx,
4010                        PromiseObject::unforgeableResolve(cx, callArgs.get(0)));
4011   if (!resolve) {
4012     return false;
4013   }
4014 
4015   return JS::AddPromiseReactions(cx, resolve, onResolved, onRejected);
4016 }
4017 
WebAssembly_compileStreaming(JSContext * cx,unsigned argc,Value * vp)4018 static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
4019                                          Value* vp) {
4020   if (!EnsureStreamSupport(cx)) {
4021     return false;
4022   }
4023 
4024   Log(cx, "async compileStreaming() started");
4025 
4026   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4027   if (!promise) {
4028     return false;
4029   }
4030 
4031   CallArgs callArgs = CallArgsFromVp(argc, vp);
4032 
4033   if (!ResolveResponse(cx, callArgs, promise)) {
4034     return RejectWithPendingException(cx, promise, callArgs);
4035   }
4036 
4037   callArgs.rval().setObject(*promise);
4038   return true;
4039 }
4040 
WebAssembly_instantiateStreaming(JSContext * cx,unsigned argc,Value * vp)4041 static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
4042                                              Value* vp) {
4043   if (!EnsureStreamSupport(cx)) {
4044     return false;
4045   }
4046 
4047   Log(cx, "async instantiateStreaming() started");
4048 
4049   Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4050   if (!promise) {
4051     return false;
4052   }
4053 
4054   CallArgs callArgs = CallArgsFromVp(argc, vp);
4055 
4056   RootedObject firstArg(cx);
4057   RootedObject importObj(cx);
4058   if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
4059     return RejectWithPendingException(cx, promise, callArgs);
4060   }
4061 
4062   if (!ResolveResponse(cx, callArgs, promise, true, importObj)) {
4063     return RejectWithPendingException(cx, promise, callArgs);
4064   }
4065 
4066   callArgs.rval().setObject(*promise);
4067   return true;
4068 }
4069 
4070 static const JSFunctionSpec WebAssembly_static_methods[] = {
4071     JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
4072     JS_FN("compile", WebAssembly_compile, 1, JSPROP_ENUMERATE),
4073     JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE),
4074     JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE),
4075     JS_FN("compileStreaming", WebAssembly_compileStreaming, 1,
4076           JSPROP_ENUMERATE),
4077     JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1,
4078           JSPROP_ENUMERATE),
4079     JS_FS_END};
4080 
CreateWebAssemblyObject(JSContext * cx,JSProtoKey key)4081 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
4082   MOZ_RELEASE_ASSERT(HasSupport(cx));
4083 
4084   Handle<GlobalObject*> global = cx->global();
4085   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
4086   if (!proto) {
4087     return nullptr;
4088   }
4089   return NewSingletonObjectWithGivenProto(cx, &WebAssemblyClass, proto);
4090 }
4091 
WebAssemblyClassFinish(JSContext * cx,HandleObject wasm,HandleObject proto)4092 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject wasm,
4093                                    HandleObject proto) {
4094   struct NameAndProtoKey {
4095     const char* const name;
4096     JSProtoKey key;
4097   };
4098 
4099   constexpr NameAndProtoKey entries[] = {
4100       {"Module", JSProto_WasmModule},
4101       {"Instance", JSProto_WasmInstance},
4102       {"Memory", JSProto_WasmMemory},
4103       {"Table", JSProto_WasmTable},
4104       {"Global", JSProto_WasmGlobal},
4105       {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
4106       {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
4107       {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
4108   };
4109 
4110   RootedValue ctorValue(cx);
4111   RootedId id(cx);
4112   for (const auto& entry : entries) {
4113     const char* name = entry.name;
4114     JSProtoKey key = entry.key;
4115 
4116     JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, key);
4117     if (!ctor) {
4118       return false;
4119     }
4120     ctorValue.setObject(*ctor);
4121 
4122     JSAtom* className = Atomize(cx, name, strlen(name));
4123     if (!className) {
4124       return false;
4125     }
4126     id.set(AtomToId(className));
4127 
4128     if (!DefineDataProperty(cx, wasm, id, ctorValue, 0)) {
4129       return false;
4130     }
4131   }
4132 
4133   return true;
4134 }
4135 
4136 static const ClassSpec WebAssemblyClassSpec = {CreateWebAssemblyObject,
4137                                                nullptr,
4138                                                WebAssembly_static_methods,
4139                                                nullptr,
4140                                                nullptr,
4141                                                nullptr,
4142                                                WebAssemblyClassFinish};
4143 
4144 const JSClass js::WebAssemblyClass = {
4145     js_WebAssembly_str, JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly),
4146     JS_NULL_CLASS_OPS, &WebAssemblyClassSpec};
4147