Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/Maybe.h"
23 : #include "mozilla/RangedPtr.h"
24 :
25 : #include "jsprf.h"
26 :
27 : #include "builtin/Promise.h"
28 : #include "jit/JitOptions.h"
29 : #include "vm/Interpreter.h"
30 : #include "vm/String.h"
31 : #include "vm/StringBuffer.h"
32 : #include "wasm/WasmCompile.h"
33 : #include "wasm/WasmInstance.h"
34 : #include "wasm/WasmModule.h"
35 : #include "wasm/WasmSignalHandlers.h"
36 : #include "wasm/WasmValidate.h"
37 :
38 : #include "jsobjinlines.h"
39 :
40 : #include "vm/NativeObject-inl.h"
41 :
42 : using namespace js;
43 : using namespace js::jit;
44 : using namespace js::wasm;
45 :
46 : using mozilla::BitwiseCast;
47 : using mozilla::CheckedInt;
48 : using mozilla::IsNaN;
49 : using mozilla::IsSame;
50 : using mozilla::Nothing;
51 : using mozilla::RangedPtr;
52 :
53 : bool
54 12 : wasm::HasCompilerSupport(JSContext* cx)
55 : {
56 12 : if (gc::SystemPageSize() > wasm::PageSize)
57 0 : return false;
58 :
59 12 : if (!cx->jitSupportsFloatingPoint())
60 0 : return false;
61 :
62 12 : if (!cx->jitSupportsUnalignedAccesses())
63 0 : return false;
64 :
65 12 : if (!wasm::HaveSignalHandlers())
66 0 : return false;
67 :
68 : #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
69 : return false;
70 : #else
71 12 : return true;
72 : #endif
73 : }
74 :
75 : bool
76 12 : wasm::HasSupport(JSContext* cx)
77 : {
78 12 : return cx->options().wasm() && HasCompilerSupport(cx);
79 : }
80 :
81 : // ============================================================================
82 : // Imports
83 :
84 : template<typename T>
85 : JSObject*
86 0 : wasm::CreateCustomNaNObject(JSContext* cx, T* addr)
87 : {
88 0 : MOZ_ASSERT(IsNaN(*addr));
89 :
90 0 : RootedObject obj(cx, JS_NewPlainObject(cx));
91 0 : if (!obj)
92 0 : return nullptr;
93 :
94 0 : int32_t* i32 = (int32_t*)addr;
95 0 : RootedValue intVal(cx, Int32Value(i32[0]));
96 0 : if (!JS_DefineProperty(cx, obj, "nan_low", intVal, JSPROP_ENUMERATE))
97 0 : return nullptr;
98 :
99 : if (IsSame<double, T>::value) {
100 0 : intVal = Int32Value(i32[1]);
101 0 : if (!JS_DefineProperty(cx, obj, "nan_high", intVal, JSPROP_ENUMERATE))
102 0 : return nullptr;
103 : }
104 :
105 0 : return obj;
106 : }
107 :
108 : template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, float* addr);
109 : template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, double* addr);
110 :
111 : bool
112 0 : wasm::ReadCustomFloat32NaNObject(JSContext* cx, HandleValue v, uint32_t* ret)
113 : {
114 0 : RootedObject obj(cx, &v.toObject());
115 0 : RootedValue val(cx);
116 :
117 : int32_t i32;
118 0 : if (!JS_GetProperty(cx, obj, "nan_low", &val))
119 0 : return false;
120 0 : if (!ToInt32(cx, val, &i32))
121 0 : return false;
122 :
123 0 : *ret = i32;
124 0 : return true;
125 : }
126 :
127 : bool
128 0 : wasm::ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, uint64_t* ret)
129 : {
130 0 : RootedObject obj(cx, &v.toObject());
131 0 : RootedValue val(cx);
132 :
133 : int32_t i32;
134 0 : if (!JS_GetProperty(cx, obj, "nan_high", &val))
135 0 : return false;
136 0 : if (!ToInt32(cx, val, &i32))
137 0 : return false;
138 0 : *ret = uint32_t(i32);
139 0 : *ret <<= 32;
140 :
141 0 : if (!JS_GetProperty(cx, obj, "nan_low", &val))
142 0 : return false;
143 0 : if (!ToInt32(cx, val, &i32))
144 0 : return false;
145 0 : *ret |= uint32_t(i32);
146 :
147 0 : return true;
148 : }
149 :
150 : JSObject*
151 0 : wasm::CreateI64Object(JSContext* cx, int64_t i64)
152 : {
153 0 : RootedObject result(cx, JS_NewPlainObject(cx));
154 0 : if (!result)
155 0 : return nullptr;
156 :
157 0 : RootedValue val(cx, Int32Value(uint32_t(i64)));
158 0 : if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
159 0 : return nullptr;
160 :
161 0 : val = Int32Value(uint32_t(i64 >> 32));
162 0 : if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
163 0 : return nullptr;
164 :
165 0 : return result;
166 : }
167 :
168 : bool
169 0 : wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
170 : {
171 0 : if (!v.isObject()) {
172 0 : JS_ReportErrorASCII(cx, "i64 JS value must be an object");
173 0 : return false;
174 : }
175 :
176 0 : RootedObject obj(cx, &v.toObject());
177 :
178 0 : int32_t* i32 = (int32_t*)i64;
179 :
180 0 : RootedValue val(cx);
181 0 : if (!JS_GetProperty(cx, obj, "low", &val))
182 0 : return false;
183 0 : if (!ToInt32(cx, val, &i32[0]))
184 0 : return false;
185 :
186 0 : if (!JS_GetProperty(cx, obj, "high", &val))
187 0 : return false;
188 0 : if (!ToInt32(cx, val, &i32[1]))
189 0 : return false;
190 :
191 0 : return true;
192 : }
193 :
194 : static bool
195 0 : ThrowBadImportArg(JSContext* cx)
196 : {
197 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
198 0 : return false;
199 : }
200 :
201 : static bool
202 0 : ThrowBadImportType(JSContext* cx, const char* field, const char* str)
203 : {
204 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_TYPE, field, str);
205 0 : return false;
206 : }
207 :
208 : static bool
209 0 : GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v)
210 : {
211 0 : JSAtom* atom = AtomizeUTF8Chars(cx, chars, strlen(chars));
212 0 : if (!atom)
213 0 : return false;
214 :
215 0 : RootedId id(cx, AtomToId(atom));
216 0 : return GetProperty(cx, obj, obj, id, v);
217 : }
218 :
219 : static bool
220 0 : GetImports(JSContext* cx,
221 : const Module& module,
222 : HandleObject importObj,
223 : MutableHandle<FunctionVector> funcImports,
224 : MutableHandleWasmTableObject tableImport,
225 : MutableHandleWasmMemoryObject memoryImport,
226 : ValVector* globalImports)
227 : {
228 0 : const ImportVector& imports = module.imports();
229 0 : if (!imports.empty() && !importObj)
230 0 : return ThrowBadImportArg(cx);
231 :
232 0 : const Metadata& metadata = module.metadata();
233 :
234 0 : uint32_t globalIndex = 0;
235 0 : const GlobalDescVector& globals = metadata.globals;
236 0 : for (const Import& import : imports) {
237 0 : RootedValue v(cx);
238 0 : if (!GetProperty(cx, importObj, import.module.get(), &v))
239 0 : return false;
240 :
241 0 : if (!v.isObject()) {
242 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_FIELD,
243 0 : import.module.get());
244 0 : return false;
245 : }
246 :
247 0 : RootedObject obj(cx, &v.toObject());
248 0 : if (!GetProperty(cx, obj, import.field.get(), &v))
249 0 : return false;
250 :
251 0 : switch (import.kind) {
252 : case DefinitionKind::Function:
253 0 : if (!IsFunctionObject(v))
254 0 : return ThrowBadImportType(cx, import.field.get(), "Function");
255 :
256 0 : if (!funcImports.append(&v.toObject().as<JSFunction>()))
257 0 : return false;
258 :
259 0 : break;
260 : case DefinitionKind::Table:
261 0 : if (!v.isObject() || !v.toObject().is<WasmTableObject>())
262 0 : return ThrowBadImportType(cx, import.field.get(), "Table");
263 :
264 0 : MOZ_ASSERT(!tableImport);
265 0 : tableImport.set(&v.toObject().as<WasmTableObject>());
266 0 : break;
267 : case DefinitionKind::Memory:
268 0 : if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
269 0 : return ThrowBadImportType(cx, import.field.get(), "Memory");
270 :
271 0 : MOZ_ASSERT(!memoryImport);
272 0 : memoryImport.set(&v.toObject().as<WasmMemoryObject>());
273 0 : break;
274 :
275 : case DefinitionKind::Global:
276 0 : Val val;
277 0 : const GlobalDesc& global = globals[globalIndex++];
278 0 : MOZ_ASSERT(global.importIndex() == globalIndex - 1);
279 0 : MOZ_ASSERT(!global.isMutable());
280 0 : switch (global.type()) {
281 : case ValType::I32: {
282 0 : if (!v.isNumber())
283 0 : return ThrowBadImportType(cx, import.field.get(), "Number");
284 : int32_t i32;
285 0 : if (!ToInt32(cx, v, &i32))
286 0 : return false;
287 0 : val = Val(uint32_t(i32));
288 0 : break;
289 : }
290 : case ValType::I64: {
291 0 : MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in JS");
292 : int64_t i64;
293 0 : if (!ReadI64Object(cx, v, &i64))
294 0 : return false;
295 0 : val = Val(uint64_t(i64));
296 0 : break;
297 : }
298 : case ValType::F32: {
299 0 : if (JitOptions.wasmTestMode && v.isObject()) {
300 : uint32_t bits;
301 0 : if (!ReadCustomFloat32NaNObject(cx, v, &bits))
302 0 : return false;
303 : float f;
304 0 : BitwiseCast(bits, &f);
305 0 : val = Val(f);
306 0 : break;
307 : }
308 0 : if (!v.isNumber())
309 0 : return ThrowBadImportType(cx, import.field.get(), "Number");
310 : double d;
311 0 : if (!ToNumber(cx, v, &d))
312 0 : return false;
313 0 : val = Val(float(d));
314 0 : break;
315 : }
316 : case ValType::F64: {
317 0 : if (JitOptions.wasmTestMode && v.isObject()) {
318 : uint64_t bits;
319 0 : if (!ReadCustomDoubleNaNObject(cx, v, &bits))
320 0 : return false;
321 : double d;
322 0 : BitwiseCast(bits, &d);
323 0 : val = Val(d);
324 0 : break;
325 : }
326 0 : if (!v.isNumber())
327 0 : return ThrowBadImportType(cx, import.field.get(), "Number");
328 : double d;
329 0 : if (!ToNumber(cx, v, &d))
330 0 : return false;
331 0 : val = Val(d);
332 0 : break;
333 : }
334 : default: {
335 0 : MOZ_CRASH("unexpected import value type");
336 : }
337 : }
338 0 : if (!globalImports->append(val))
339 0 : return false;
340 : }
341 : }
342 :
343 0 : MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
344 :
345 0 : return true;
346 : }
347 :
348 : // ============================================================================
349 : // Fuzzing support
350 :
351 : static bool
352 0 : DescribeScriptedCaller(JSContext* cx, ScriptedCaller* scriptedCaller)
353 : {
354 : // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
355 : // found, not whether an error was thrown. This wrapper function converts
356 : // back to the more ordinary false-if-error form.
357 :
358 0 : JS::AutoFilename af;
359 0 : if (JS::DescribeScriptedCaller(cx, &af, &scriptedCaller->line, &scriptedCaller->column)) {
360 0 : scriptedCaller->filename = DuplicateString(cx, af.get());
361 0 : if (!scriptedCaller->filename)
362 0 : return false;
363 : }
364 :
365 0 : return true;
366 : }
367 :
368 : bool
369 0 : wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
370 : MutableHandleWasmInstanceObject instanceObj)
371 : {
372 0 : if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly))
373 0 : return false;
374 :
375 0 : MutableBytes bytecode = cx->new_<ShareableBytes>();
376 0 : if (!bytecode)
377 0 : return false;
378 :
379 0 : if (!bytecode->append((uint8_t*)code->viewDataEither().unwrap(), code->byteLength())) {
380 0 : ReportOutOfMemory(cx);
381 0 : return false;
382 : }
383 :
384 0 : ScriptedCaller scriptedCaller;
385 0 : if (!DescribeScriptedCaller(cx, &scriptedCaller))
386 0 : return false;
387 :
388 0 : CompileArgs compileArgs;
389 0 : if (!compileArgs.initFromContext(cx, Move(scriptedCaller)))
390 0 : return false;
391 :
392 0 : UniqueChars error;
393 0 : SharedModule module = Compile(*bytecode, compileArgs, &error);
394 0 : if (!module) {
395 0 : if (error) {
396 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
397 0 : error.get());
398 0 : return false;
399 : }
400 0 : ReportOutOfMemory(cx);
401 0 : return false;
402 : }
403 :
404 0 : Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
405 0 : RootedWasmTableObject table(cx);
406 0 : RootedWasmMemoryObject memory(cx);
407 0 : ValVector globals;
408 0 : if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, &globals))
409 0 : return false;
410 :
411 0 : return module->instantiate(cx, funcs, table, memory, globals, nullptr, instanceObj);
412 : }
413 :
414 : // ============================================================================
415 : // Common functions
416 :
417 : static bool
418 0 : ToNonWrappingUint32(JSContext* cx, HandleValue v, uint32_t max, const char* kind, const char* noun,
419 : uint32_t* u32)
420 : {
421 : double dbl;
422 0 : if (!ToInteger(cx, v, &dbl))
423 0 : return false;
424 :
425 0 : if (dbl < 0 || dbl > max) {
426 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32, kind, noun);
427 0 : return false;
428 : }
429 :
430 0 : *u32 = uint32_t(dbl);
431 0 : MOZ_ASSERT(double(*u32) == dbl);
432 0 : return true;
433 : }
434 :
435 : static bool
436 0 : GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial, uint32_t maxMaximum,
437 : const char* kind, Limits* limits)
438 : {
439 0 : JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
440 0 : if (!initialAtom)
441 0 : return false;
442 0 : RootedId initialId(cx, AtomToId(initialAtom));
443 :
444 0 : RootedValue initialVal(cx);
445 0 : if (!GetProperty(cx, obj, obj, initialId, &initialVal))
446 0 : return false;
447 :
448 0 : if (!ToNonWrappingUint32(cx, initialVal, maxInitial, kind, "initial size", &limits->initial))
449 0 : return false;
450 :
451 0 : JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
452 0 : if (!maximumAtom)
453 0 : return false;
454 0 : RootedId maximumId(cx, AtomToId(maximumAtom));
455 :
456 : bool found;
457 0 : if (HasProperty(cx, obj, maximumId, &found) && found) {
458 0 : RootedValue maxVal(cx);
459 0 : if (!GetProperty(cx, obj, obj, maximumId, &maxVal))
460 0 : return false;
461 :
462 0 : limits->maximum.emplace();
463 0 : if (!ToNonWrappingUint32(cx, maxVal, maxMaximum, kind, "maximum size", limits->maximum.ptr()))
464 0 : return false;
465 :
466 0 : if (limits->initial > *limits->maximum) {
467 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_UINT32,
468 0 : kind, "maximum size");
469 0 : return false;
470 : }
471 : }
472 :
473 0 : return true;
474 : }
475 :
476 : // ============================================================================
477 : // WebAssembly.Module class and methods
478 :
479 : const ClassOps WasmModuleObject::classOps_ =
480 : {
481 : nullptr, /* addProperty */
482 : nullptr, /* delProperty */
483 : nullptr, /* getProperty */
484 : nullptr, /* setProperty */
485 : nullptr, /* enumerate */
486 : nullptr, /* newEnumerate */
487 : nullptr, /* resolve */
488 : nullptr, /* mayResolve */
489 : WasmModuleObject::finalize
490 : };
491 :
492 : const Class WasmModuleObject::class_ =
493 : {
494 : "WebAssembly.Module",
495 : JSCLASS_DELAY_METADATA_BUILDER |
496 : JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
497 : JSCLASS_FOREGROUND_FINALIZE,
498 : &WasmModuleObject::classOps_,
499 : };
500 :
501 : const JSPropertySpec WasmModuleObject::properties[] =
502 : { JS_PS_END };
503 :
504 : const JSFunctionSpec WasmModuleObject::methods[] =
505 : { JS_FS_END };
506 :
507 : const JSFunctionSpec WasmModuleObject::static_methods[] =
508 : {
509 : JS_FN("imports", WasmModuleObject::imports, 1, 0),
510 : JS_FN("exports", WasmModuleObject::exports, 1, 0),
511 : JS_FN("customSections", WasmModuleObject::customSections, 2, 0),
512 : JS_FS_END
513 : };
514 :
515 : /* static */ void
516 0 : WasmModuleObject::finalize(FreeOp* fop, JSObject* obj)
517 : {
518 0 : obj->as<WasmModuleObject>().module().Release();
519 0 : }
520 :
521 : static bool
522 0 : IsModuleObject(JSObject* obj, Module** module)
523 : {
524 0 : JSObject* unwrapped = CheckedUnwrap(obj);
525 0 : if (!unwrapped || !unwrapped->is<WasmModuleObject>())
526 0 : return false;
527 :
528 0 : *module = &unwrapped->as<WasmModuleObject>().module();
529 0 : return true;
530 : }
531 :
532 : static bool
533 0 : GetModuleArg(JSContext* cx, CallArgs args, const char* name, Module** module)
534 : {
535 0 : if (!args.requireAtLeast(cx, name, 1))
536 0 : return false;
537 :
538 0 : if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
539 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
540 0 : return false;
541 : }
542 :
543 0 : return true;
544 : }
545 :
546 0 : struct KindNames
547 : {
548 : RootedPropertyName kind;
549 : RootedPropertyName table;
550 : RootedPropertyName memory;
551 : RootedPropertyName signature;
552 :
553 0 : explicit KindNames(JSContext* cx) : kind(cx), table(cx), memory(cx), signature(cx) {}
554 : };
555 :
556 : static bool
557 0 : InitKindNames(JSContext* cx, KindNames* names)
558 : {
559 0 : JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
560 0 : if (!kind)
561 0 : return false;
562 0 : names->kind = kind->asPropertyName();
563 :
564 0 : JSAtom* table = Atomize(cx, "table", strlen("table"));
565 0 : if (!table)
566 0 : return false;
567 0 : names->table = table->asPropertyName();
568 :
569 0 : JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
570 0 : if (!memory)
571 0 : return false;
572 0 : names->memory = memory->asPropertyName();
573 :
574 0 : JSAtom* signature = Atomize(cx, "signature", strlen("signature"));
575 0 : if (!signature)
576 0 : return false;
577 0 : names->signature = signature->asPropertyName();
578 :
579 0 : return true;
580 : }
581 :
582 : static JSString*
583 0 : KindToString(JSContext* cx, const KindNames& names, DefinitionKind kind)
584 : {
585 0 : switch (kind) {
586 : case DefinitionKind::Function:
587 0 : return cx->names().function;
588 : case DefinitionKind::Table:
589 0 : return names.table;
590 : case DefinitionKind::Memory:
591 0 : return names.memory;
592 : case DefinitionKind::Global:
593 0 : return cx->names().global;
594 : }
595 :
596 0 : MOZ_CRASH("invalid kind");
597 : }
598 :
599 : static JSString*
600 0 : SigToString(JSContext* cx, const Sig& sig)
601 : {
602 0 : StringBuffer buf(cx);
603 0 : if (!buf.append('('))
604 0 : return nullptr;
605 :
606 0 : bool first = true;
607 0 : for (ValType arg : sig.args()) {
608 0 : if (!first && !buf.append(", ", strlen(", ")))
609 0 : return nullptr;
610 :
611 0 : const char* argStr = ToCString(arg);
612 0 : if (!buf.append(argStr, strlen(argStr)))
613 0 : return nullptr;
614 :
615 0 : first = false;
616 : }
617 :
618 0 : if (!buf.append(") -> (", strlen(") -> (")))
619 0 : return nullptr;
620 :
621 0 : if (sig.ret() != ExprType::Void) {
622 0 : const char* retStr = ToCString(sig.ret());
623 0 : if (!buf.append(retStr, strlen(retStr)))
624 0 : return nullptr;
625 : }
626 :
627 0 : if (!buf.append(')'))
628 0 : return nullptr;
629 :
630 0 : return buf.finishString();
631 : }
632 :
633 : static JSString*
634 0 : UTF8CharsToString(JSContext* cx, const char* chars)
635 : {
636 0 : return NewStringCopyUTF8Z<CanGC>(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
637 : }
638 :
639 : /* static */ bool
640 0 : WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp)
641 : {
642 0 : CallArgs args = CallArgsFromVp(argc, vp);
643 :
644 : Module* module;
645 0 : if (!GetModuleArg(cx, args, "WebAssembly.Module.imports", &module))
646 0 : return false;
647 :
648 0 : KindNames names(cx);
649 0 : if (!InitKindNames(cx, &names))
650 0 : return false;
651 :
652 0 : AutoValueVector elems(cx);
653 0 : if (!elems.reserve(module->imports().length()))
654 0 : return false;
655 :
656 0 : const FuncImportVector& funcImports = module->metadata(module->code().anyTier()).funcImports;
657 :
658 0 : size_t numFuncImport = 0;
659 0 : for (const Import& import : module->imports()) {
660 0 : Rooted<IdValueVector> props(cx, IdValueVector(cx));
661 0 : if (!props.reserve(3))
662 0 : return false;
663 :
664 0 : JSString* moduleStr = UTF8CharsToString(cx, import.module.get());
665 0 : if (!moduleStr)
666 0 : return false;
667 0 : props.infallibleAppend(IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
668 :
669 0 : JSString* nameStr = UTF8CharsToString(cx, import.field.get());
670 0 : if (!nameStr)
671 0 : return false;
672 0 : props.infallibleAppend(IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
673 :
674 0 : JSString* kindStr = KindToString(cx, names, import.kind);
675 0 : if (!kindStr)
676 0 : return false;
677 0 : props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr)));
678 :
679 0 : if (JitOptions.wasmTestMode && import.kind == DefinitionKind::Function) {
680 0 : JSString* sigStr = SigToString(cx, funcImports[numFuncImport++].sig());
681 0 : if (!sigStr)
682 0 : return false;
683 0 : if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr))))
684 0 : return false;
685 : }
686 :
687 0 : JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject);
688 0 : if (!obj)
689 0 : return false;
690 :
691 0 : elems.infallibleAppend(ObjectValue(*obj));
692 : }
693 :
694 0 : JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
695 0 : if (!arr)
696 0 : return false;
697 :
698 0 : args.rval().setObject(*arr);
699 0 : return true;
700 : }
701 :
702 : /* static */ bool
703 0 : WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp)
704 : {
705 0 : CallArgs args = CallArgsFromVp(argc, vp);
706 :
707 : Module* module;
708 0 : if (!GetModuleArg(cx, args, "WebAssembly.Module.exports", &module))
709 0 : return false;
710 :
711 0 : KindNames names(cx);
712 0 : if (!InitKindNames(cx, &names))
713 0 : return false;
714 :
715 0 : AutoValueVector elems(cx);
716 0 : if (!elems.reserve(module->exports().length()))
717 0 : return false;
718 :
719 0 : const FuncExportVector& funcExports = module->metadata(module->code().anyTier()).funcExports;
720 :
721 0 : size_t numFuncExport = 0;
722 0 : for (const Export& exp : module->exports()) {
723 0 : Rooted<IdValueVector> props(cx, IdValueVector(cx));
724 0 : if (!props.reserve(2))
725 0 : return false;
726 :
727 0 : JSString* nameStr = UTF8CharsToString(cx, exp.fieldName());
728 0 : if (!nameStr)
729 0 : return false;
730 0 : props.infallibleAppend(IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
731 :
732 0 : JSString* kindStr = KindToString(cx, names, exp.kind());
733 0 : if (!kindStr)
734 0 : return false;
735 0 : props.infallibleAppend(IdValuePair(NameToId(names.kind), StringValue(kindStr)));
736 :
737 0 : if (JitOptions.wasmTestMode && exp.kind() == DefinitionKind::Function) {
738 0 : JSString* sigStr = SigToString(cx, funcExports[numFuncExport++].sig());
739 0 : if (!sigStr)
740 0 : return false;
741 0 : if (!props.append(IdValuePair(NameToId(names.signature), StringValue(sigStr))))
742 0 : return false;
743 : }
744 :
745 0 : JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(), props.length(), GenericObject);
746 0 : if (!obj)
747 0 : return false;
748 :
749 0 : elems.infallibleAppend(ObjectValue(*obj));
750 : }
751 :
752 0 : JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
753 0 : if (!arr)
754 0 : return false;
755 :
756 0 : args.rval().setObject(*arr);
757 0 : return true;
758 : }
759 :
760 : /* static */ bool
761 0 : WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp)
762 : {
763 0 : CallArgs args = CallArgsFromVp(argc, vp);
764 :
765 : Module* module;
766 0 : if (!GetModuleArg(cx, args, "WebAssembly.Module.customSections", &module))
767 0 : return false;
768 :
769 0 : Vector<char, 8> name(cx);
770 : {
771 0 : RootedString str(cx, ToString(cx, args.get(1)));
772 0 : if (!str)
773 0 : return false;
774 :
775 0 : Rooted<JSFlatString*> flat(cx, str->ensureFlat(cx));
776 0 : if (!flat)
777 0 : return false;
778 :
779 0 : if (!name.initLengthUninitialized(JS::GetDeflatedUTF8StringLength(flat)))
780 0 : return false;
781 :
782 0 : JS::DeflateStringToUTF8Buffer(flat, RangedPtr<char>(name.begin(), name.length()));
783 : }
784 :
785 0 : const uint8_t* bytecode = module->bytecode().begin();
786 :
787 0 : AutoValueVector elems(cx);
788 0 : RootedArrayBufferObject buf(cx);
789 0 : for (const CustomSection& sec : module->metadata().customSections) {
790 0 : if (name.length() != sec.name.length)
791 0 : continue;
792 0 : if (memcmp(name.begin(), bytecode + sec.name.offset, name.length()))
793 0 : continue;
794 :
795 0 : buf = ArrayBufferObject::create(cx, sec.length);
796 0 : if (!buf)
797 0 : return false;
798 :
799 0 : memcpy(buf->dataPointer(), bytecode + sec.offset, sec.length);
800 0 : if (!elems.append(ObjectValue(*buf)))
801 0 : return false;
802 : }
803 :
804 0 : JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
805 0 : if (!arr)
806 0 : return false;
807 :
808 0 : args.rval().setObject(*arr);
809 0 : return true;
810 : }
811 :
812 : /* static */ WasmModuleObject*
813 0 : WasmModuleObject::create(JSContext* cx, Module& module, HandleObject proto)
814 : {
815 0 : AutoSetNewObjectMetadata metadata(cx);
816 0 : auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
817 0 : if (!obj)
818 0 : return nullptr;
819 :
820 0 : obj->initReservedSlot(MODULE_SLOT, PrivateValue(&module));
821 0 : module.AddRef();
822 : // We account for the first tier here; the second tier, if different, will be
823 : // accounted for separately when it's been compiled.
824 0 : cx->zone()->updateJitCodeMallocBytes(module.codeLength(module.code().anyTier()));
825 0 : return obj;
826 : }
827 :
828 : static bool
829 0 : GetBufferSource(JSContext* cx, JSObject* obj, unsigned errorNumber, MutableBytes* bytecode)
830 : {
831 0 : *bytecode = cx->new_<ShareableBytes>();
832 0 : if (!*bytecode)
833 0 : return false;
834 :
835 0 : JSObject* unwrapped = CheckedUnwrap(obj);
836 :
837 0 : size_t byteLength = 0;
838 0 : uint8_t* ptr = nullptr;
839 0 : if (unwrapped && unwrapped->is<TypedArrayObject>()) {
840 0 : TypedArrayObject& view = unwrapped->as<TypedArrayObject>();
841 0 : byteLength = view.byteLength();
842 0 : ptr = (uint8_t*)view.viewDataEither().unwrap();
843 0 : } else if (unwrapped && unwrapped->is<ArrayBufferObject>()) {
844 0 : ArrayBufferObject& buffer = unwrapped->as<ArrayBufferObject>();
845 0 : byteLength = buffer.byteLength();
846 0 : ptr = buffer.dataPointer();
847 : } else {
848 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
849 0 : return false;
850 : }
851 :
852 0 : if (!(*bytecode)->append(ptr, byteLength)) {
853 0 : ReportOutOfMemory(cx);
854 0 : return false;
855 : }
856 :
857 0 : return true;
858 : }
859 :
860 : static bool
861 0 : InitCompileArgs(JSContext* cx, CompileArgs* compileArgs)
862 : {
863 0 : ScriptedCaller scriptedCaller;
864 0 : if (!DescribeScriptedCaller(cx, &scriptedCaller))
865 0 : return false;
866 :
867 0 : return compileArgs->initFromContext(cx, Move(scriptedCaller));
868 : }
869 :
870 : /* static */ bool
871 0 : WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp)
872 : {
873 0 : CallArgs callArgs = CallArgsFromVp(argc, vp);
874 :
875 0 : if (!ThrowIfNotConstructing(cx, callArgs, "Module"))
876 0 : return false;
877 :
878 0 : if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1))
879 0 : return false;
880 :
881 0 : if (!callArgs[0].isObject()) {
882 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
883 0 : return false;
884 : }
885 :
886 0 : MutableBytes bytecode;
887 0 : if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG, &bytecode))
888 0 : return false;
889 :
890 0 : CompileArgs compileArgs;
891 0 : if (!InitCompileArgs(cx, &compileArgs))
892 0 : return false;
893 :
894 0 : UniqueChars error;
895 0 : SharedModule module = Compile(*bytecode, compileArgs, &error);
896 0 : if (!module) {
897 0 : if (error) {
898 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
899 0 : error.get());
900 0 : return false;
901 : }
902 0 : ReportOutOfMemory(cx);
903 0 : return false;
904 : }
905 :
906 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
907 0 : RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
908 0 : if (!moduleObj)
909 0 : return false;
910 :
911 0 : callArgs.rval().setObject(*moduleObj);
912 0 : return true;
913 : }
914 :
915 : Module&
916 0 : WasmModuleObject::module() const
917 : {
918 0 : MOZ_ASSERT(is<WasmModuleObject>());
919 0 : return *(Module*)getReservedSlot(MODULE_SLOT).toPrivate();
920 : }
921 :
922 : // ============================================================================
923 : // WebAssembly.Instance class and methods
924 :
925 : const ClassOps WasmInstanceObject::classOps_ =
926 : {
927 : nullptr, /* addProperty */
928 : nullptr, /* delProperty */
929 : nullptr, /* getProperty */
930 : nullptr, /* setProperty */
931 : nullptr, /* enumerate */
932 : nullptr, /* newEnumerate */
933 : nullptr, /* resolve */
934 : nullptr, /* mayResolve */
935 : WasmInstanceObject::finalize,
936 : nullptr, /* call */
937 : nullptr, /* hasInstance */
938 : nullptr, /* construct */
939 : WasmInstanceObject::trace
940 : };
941 :
942 : const Class WasmInstanceObject::class_ =
943 : {
944 : "WebAssembly.Instance",
945 : JSCLASS_DELAY_METADATA_BUILDER |
946 : JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
947 : JSCLASS_FOREGROUND_FINALIZE,
948 : &WasmInstanceObject::classOps_,
949 : };
950 :
951 : static bool
952 0 : IsInstance(HandleValue v)
953 : {
954 0 : return v.isObject() && v.toObject().is<WasmInstanceObject>();
955 : }
956 :
957 : /* static */ bool
958 0 : WasmInstanceObject::exportsGetterImpl(JSContext* cx, const CallArgs& args)
959 : {
960 0 : args.rval().setObject(args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
961 0 : return true;
962 : }
963 :
964 : /* static */ bool
965 0 : WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc, Value* vp)
966 : {
967 0 : CallArgs args = CallArgsFromVp(argc, vp);
968 0 : return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
969 : }
970 :
971 : const JSPropertySpec WasmInstanceObject::properties[] =
972 : {
973 : JS_PSG("exports", WasmInstanceObject::exportsGetter, 0),
974 : JS_PS_END
975 : };
976 :
977 : const JSFunctionSpec WasmInstanceObject::methods[] =
978 : { JS_FS_END };
979 :
980 : const JSFunctionSpec WasmInstanceObject::static_methods[] =
981 : { JS_FS_END };
982 :
983 : bool
984 0 : WasmInstanceObject::isNewborn() const
985 : {
986 0 : MOZ_ASSERT(is<WasmInstanceObject>());
987 0 : return getReservedSlot(INSTANCE_SLOT).isUndefined();
988 : }
989 :
990 : /* static */ void
991 0 : WasmInstanceObject::finalize(FreeOp* fop, JSObject* obj)
992 : {
993 0 : fop->delete_(&obj->as<WasmInstanceObject>().exports());
994 0 : fop->delete_(&obj->as<WasmInstanceObject>().scopes());
995 0 : if (!obj->as<WasmInstanceObject>().isNewborn())
996 0 : fop->delete_(&obj->as<WasmInstanceObject>().instance());
997 0 : }
998 :
999 : /* static */ void
1000 0 : WasmInstanceObject::trace(JSTracer* trc, JSObject* obj)
1001 : {
1002 0 : WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
1003 0 : instanceObj.exports().trace(trc);
1004 0 : if (!instanceObj.isNewborn())
1005 0 : instanceObj.instance().tracePrivate(trc);
1006 0 : }
1007 :
1008 : /* static */ WasmInstanceObject*
1009 0 : WasmInstanceObject::create(JSContext* cx,
1010 : SharedCode code,
1011 : UniqueDebugState debug,
1012 : UniqueGlobalSegment globals,
1013 : HandleWasmMemoryObject memory,
1014 : SharedTableVector&& tables,
1015 : Handle<FunctionVector> funcImports,
1016 : const ValVector& globalImports,
1017 : HandleObject proto)
1018 : {
1019 0 : UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
1020 0 : if (!exports || !exports->init()) {
1021 0 : ReportOutOfMemory(cx);
1022 0 : return nullptr;
1023 : }
1024 :
1025 0 : UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone());
1026 0 : if (!scopes || !scopes->init()) {
1027 0 : ReportOutOfMemory(cx);
1028 0 : return nullptr;
1029 : }
1030 :
1031 0 : AutoSetNewObjectMetadata metadata(cx);
1032 0 : RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
1033 0 : if (!obj)
1034 0 : return nullptr;
1035 :
1036 0 : obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
1037 0 : obj->setReservedSlot(SCOPES_SLOT, PrivateValue(scopes.release()));
1038 0 : MOZ_ASSERT(obj->isNewborn());
1039 :
1040 0 : MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
1041 :
1042 : // Root the Instance via WasmInstanceObject before any possible GC.
1043 0 : auto* instance = cx->new_<Instance>(cx,
1044 : obj,
1045 : code,
1046 0 : Move(debug),
1047 0 : Move(globals),
1048 : memory,
1049 0 : Move(tables),
1050 : funcImports,
1051 0 : globalImports);
1052 0 : if (!instance)
1053 0 : return nullptr;
1054 :
1055 0 : obj->initReservedSlot(INSTANCE_SLOT, PrivateValue(instance));
1056 0 : MOZ_ASSERT(!obj->isNewborn());
1057 :
1058 0 : if (!instance->init(cx))
1059 0 : return nullptr;
1060 :
1061 0 : return obj;
1062 : }
1063 :
1064 : void
1065 0 : WasmInstanceObject::initExportsObj(JSObject& exportsObj)
1066 : {
1067 0 : MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
1068 0 : setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
1069 0 : }
1070 :
1071 : static bool
1072 0 : GetImportArg(JSContext* cx, CallArgs callArgs, MutableHandleObject importObj)
1073 : {
1074 0 : if (!callArgs.get(1).isUndefined()) {
1075 0 : if (!callArgs[1].isObject())
1076 0 : return ThrowBadImportArg(cx);
1077 0 : importObj.set(&callArgs[1].toObject());
1078 : }
1079 0 : return true;
1080 : }
1081 :
1082 : static bool
1083 0 : Instantiate(JSContext* cx, const Module& module, HandleObject importObj,
1084 : MutableHandleWasmInstanceObject instanceObj)
1085 : {
1086 0 : RootedObject instanceProto(cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
1087 :
1088 0 : Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
1089 0 : RootedWasmTableObject table(cx);
1090 0 : RootedWasmMemoryObject memory(cx);
1091 0 : ValVector globals;
1092 0 : if (!GetImports(cx, module, importObj, &funcs, &table, &memory, &globals))
1093 0 : return false;
1094 :
1095 0 : return module.instantiate(cx, funcs, table, memory, globals, instanceProto, instanceObj);
1096 : }
1097 :
1098 : /* static */ bool
1099 0 : WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp)
1100 : {
1101 0 : CallArgs args = CallArgsFromVp(argc, vp);
1102 :
1103 0 : if (!ThrowIfNotConstructing(cx, args, "Instance"))
1104 0 : return false;
1105 :
1106 0 : if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1))
1107 0 : return false;
1108 :
1109 : Module* module;
1110 0 : if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
1111 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MOD_ARG);
1112 0 : return false;
1113 : }
1114 :
1115 0 : RootedObject importObj(cx);
1116 0 : if (!GetImportArg(cx, args, &importObj))
1117 0 : return false;
1118 :
1119 0 : RootedWasmInstanceObject instanceObj(cx);
1120 0 : if (!Instantiate(cx, *module, importObj, &instanceObj))
1121 0 : return false;
1122 :
1123 0 : args.rval().setObject(*instanceObj);
1124 0 : return true;
1125 : }
1126 :
1127 : Instance&
1128 0 : WasmInstanceObject::instance() const
1129 : {
1130 0 : MOZ_ASSERT(!isNewborn());
1131 0 : return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
1132 : }
1133 :
1134 : JSObject&
1135 0 : WasmInstanceObject::exportsObj() const
1136 : {
1137 0 : return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
1138 : }
1139 :
1140 : WasmInstanceObject::ExportMap&
1141 0 : WasmInstanceObject::exports() const
1142 : {
1143 0 : return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
1144 : }
1145 :
1146 : WasmInstanceObject::ScopeMap&
1147 0 : WasmInstanceObject::scopes() const
1148 : {
1149 0 : return *(ScopeMap*)getReservedSlot(SCOPES_SLOT).toPrivate();
1150 : }
1151 :
1152 : static bool
1153 0 : WasmCall(JSContext* cx, unsigned argc, Value* vp)
1154 : {
1155 0 : CallArgs args = CallArgsFromVp(argc, vp);
1156 0 : RootedFunction callee(cx, &args.callee().as<JSFunction>());
1157 :
1158 0 : Instance& instance = ExportedFunctionToInstance(callee);
1159 0 : uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
1160 0 : return instance.callExport(cx, funcIndex, args);
1161 : }
1162 :
1163 : /* static */ bool
1164 0 : WasmInstanceObject::getExportedFunction(JSContext* cx, HandleWasmInstanceObject instanceObj,
1165 : uint32_t funcIndex, MutableHandleFunction fun)
1166 : {
1167 0 : if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
1168 0 : fun.set(p->value());
1169 0 : return true;
1170 : }
1171 :
1172 0 : const Instance& instance = instanceObj->instance();
1173 0 : unsigned numArgs = instance.metadata(instance.code().anyTier()).lookupFuncExport(funcIndex).sig().args().length();
1174 :
1175 : // asm.js needs to act like a normal JS function which means having the name
1176 : // from the original source and being callable as a constructor.
1177 0 : if (instance.isAsmJS()) {
1178 0 : RootedAtom name(cx, instance.getFuncAtom(cx, funcIndex));
1179 0 : if (!name)
1180 0 : return false;
1181 :
1182 0 : fun.set(NewNativeConstructor(cx, WasmCall, numArgs, name, gc::AllocKind::FUNCTION_EXTENDED,
1183 0 : SingletonObject, JSFunction::ASMJS_CTOR));
1184 0 : if (!fun)
1185 0 : return false;
1186 : } else {
1187 0 : RootedAtom name(cx, NumberToAtom(cx, funcIndex));
1188 0 : if (!name)
1189 0 : return false;
1190 :
1191 0 : fun.set(NewNativeFunction(cx, WasmCall, numArgs, name, gc::AllocKind::FUNCTION_EXTENDED));
1192 0 : if (!fun)
1193 0 : return false;
1194 : }
1195 :
1196 0 : fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT, ObjectValue(*instanceObj));
1197 0 : fun->setExtendedSlot(FunctionExtended::WASM_FUNC_INDEX_SLOT, Int32Value(funcIndex));
1198 :
1199 0 : if (!instanceObj->exports().putNew(funcIndex, fun)) {
1200 0 : ReportOutOfMemory(cx);
1201 0 : return false;
1202 : }
1203 :
1204 0 : return true;
1205 : }
1206 :
1207 : const CodeRange&
1208 0 : WasmInstanceObject::getExportedFunctionCodeRange(HandleFunction fun, Tier tier)
1209 : {
1210 0 : uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
1211 0 : MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
1212 0 : const FuncExport& funcExport = instance().metadata(tier).lookupFuncExport(funcIndex);
1213 0 : return instance().metadata(tier).codeRanges[funcExport.codeRangeIndex()];
1214 : }
1215 :
1216 : /* static */ WasmFunctionScope*
1217 0 : WasmInstanceObject::getFunctionScope(JSContext* cx, HandleWasmInstanceObject instanceObj,
1218 : uint32_t funcIndex)
1219 : {
1220 0 : if (ScopeMap::Ptr p = instanceObj->scopes().lookup(funcIndex))
1221 0 : return p->value();
1222 :
1223 0 : Rooted<WasmFunctionScope*> funcScope(cx, WasmFunctionScope::create(cx, instanceObj, funcIndex));
1224 0 : if (!funcScope)
1225 0 : return nullptr;
1226 :
1227 0 : if (!instanceObj->scopes().putNew(funcIndex, funcScope)) {
1228 0 : ReportOutOfMemory(cx);
1229 0 : return nullptr;
1230 : }
1231 :
1232 0 : return funcScope;
1233 : }
1234 :
1235 : bool
1236 4 : wasm::IsExportedFunction(JSFunction* fun)
1237 : {
1238 4 : return fun->maybeNative() == WasmCall;
1239 : }
1240 :
1241 : bool
1242 0 : wasm::IsExportedWasmFunction(JSFunction* fun)
1243 : {
1244 0 : return IsExportedFunction(fun) && !ExportedFunctionToInstance(fun).isAsmJS();
1245 : }
1246 :
1247 : bool
1248 0 : wasm::IsExportedFunction(const Value& v, MutableHandleFunction f)
1249 : {
1250 0 : if (!v.isObject())
1251 0 : return false;
1252 :
1253 0 : JSObject& obj = v.toObject();
1254 0 : if (!obj.is<JSFunction>() || !IsExportedFunction(&obj.as<JSFunction>()))
1255 0 : return false;
1256 :
1257 0 : f.set(&obj.as<JSFunction>());
1258 0 : return true;
1259 : }
1260 :
1261 : Instance&
1262 0 : wasm::ExportedFunctionToInstance(JSFunction* fun)
1263 : {
1264 0 : return ExportedFunctionToInstanceObject(fun)->instance();
1265 : }
1266 :
1267 : WasmInstanceObject*
1268 0 : wasm::ExportedFunctionToInstanceObject(JSFunction* fun)
1269 : {
1270 0 : MOZ_ASSERT(IsExportedFunction(fun));
1271 0 : const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT);
1272 0 : return &v.toObject().as<WasmInstanceObject>();
1273 : }
1274 :
1275 : uint32_t
1276 0 : wasm::ExportedFunctionToFuncIndex(JSFunction* fun)
1277 : {
1278 0 : MOZ_ASSERT(IsExportedFunction(fun));
1279 0 : const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_FUNC_INDEX_SLOT);
1280 0 : return v.toInt32();
1281 : }
1282 :
1283 : // ============================================================================
1284 : // WebAssembly.Memory class and methods
1285 :
1286 : const ClassOps WasmMemoryObject::classOps_ =
1287 : {
1288 : nullptr, /* addProperty */
1289 : nullptr, /* delProperty */
1290 : nullptr, /* getProperty */
1291 : nullptr, /* setProperty */
1292 : nullptr, /* enumerate */
1293 : nullptr, /* newEnumerate */
1294 : nullptr, /* resolve */
1295 : nullptr, /* mayResolve */
1296 : WasmMemoryObject::finalize
1297 : };
1298 :
1299 : const Class WasmMemoryObject::class_ =
1300 : {
1301 : "WebAssembly.Memory",
1302 : JSCLASS_DELAY_METADATA_BUILDER |
1303 : JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
1304 : JSCLASS_FOREGROUND_FINALIZE,
1305 : &WasmMemoryObject::classOps_
1306 : };
1307 :
1308 : /* static */ void
1309 0 : WasmMemoryObject::finalize(FreeOp* fop, JSObject* obj)
1310 : {
1311 0 : WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
1312 0 : if (memory.hasObservers())
1313 0 : fop->delete_(&memory.observers());
1314 0 : }
1315 :
1316 : /* static */ WasmMemoryObject*
1317 0 : WasmMemoryObject::create(JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
1318 : HandleObject proto)
1319 : {
1320 0 : AutoSetNewObjectMetadata metadata(cx);
1321 0 : auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
1322 0 : if (!obj)
1323 0 : return nullptr;
1324 :
1325 0 : obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
1326 0 : MOZ_ASSERT(!obj->hasObservers());
1327 0 : return obj;
1328 : }
1329 :
1330 : /* static */ bool
1331 0 : WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp)
1332 : {
1333 0 : CallArgs args = CallArgsFromVp(argc, vp);
1334 :
1335 0 : if (!ThrowIfNotConstructing(cx, args, "Memory"))
1336 0 : return false;
1337 :
1338 0 : if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1))
1339 0 : return false;
1340 :
1341 0 : if (!args.get(0).isObject()) {
1342 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "memory");
1343 0 : return false;
1344 : }
1345 :
1346 0 : RootedObject obj(cx, &args[0].toObject());
1347 0 : Limits limits;
1348 0 : if (!GetLimits(cx, obj, MaxMemoryInitialPages, MaxMemoryMaximumPages, "Memory", &limits))
1349 0 : return false;
1350 :
1351 0 : limits.initial *= PageSize;
1352 0 : if (limits.maximum)
1353 0 : limits.maximum = Some(*limits.maximum * PageSize);
1354 :
1355 : RootedArrayBufferObject buffer(cx,
1356 0 : ArrayBufferObject::createForWasm(cx, limits.initial, limits.maximum));
1357 0 : if (!buffer)
1358 0 : return false;
1359 :
1360 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmMemory).toObject());
1361 0 : RootedWasmMemoryObject memoryObj(cx, WasmMemoryObject::create(cx, buffer, proto));
1362 0 : if (!memoryObj)
1363 0 : return false;
1364 :
1365 0 : args.rval().setObject(*memoryObj);
1366 0 : return true;
1367 : }
1368 :
1369 : static bool
1370 0 : IsMemory(HandleValue v)
1371 : {
1372 0 : return v.isObject() && v.toObject().is<WasmMemoryObject>();
1373 : }
1374 :
1375 : /* static */ bool
1376 0 : WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args)
1377 : {
1378 0 : args.rval().setObject(args.thisv().toObject().as<WasmMemoryObject>().buffer());
1379 0 : return true;
1380 : }
1381 :
1382 : /* static */ bool
1383 0 : WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp)
1384 : {
1385 0 : CallArgs args = CallArgsFromVp(argc, vp);
1386 0 : return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
1387 : }
1388 :
1389 : const JSPropertySpec WasmMemoryObject::properties[] =
1390 : {
1391 : JS_PSG("buffer", WasmMemoryObject::bufferGetter, 0),
1392 : JS_PS_END
1393 : };
1394 :
1395 : /* static */ bool
1396 0 : WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args)
1397 : {
1398 0 : RootedWasmMemoryObject memory(cx, &args.thisv().toObject().as<WasmMemoryObject>());
1399 :
1400 : uint32_t delta;
1401 0 : if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Memory", "grow delta", &delta))
1402 0 : return false;
1403 :
1404 0 : uint32_t ret = grow(memory, delta, cx);
1405 :
1406 0 : if (ret == uint32_t(-1)) {
1407 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "memory");
1408 0 : return false;
1409 : }
1410 :
1411 0 : args.rval().setInt32(ret);
1412 0 : return true;
1413 : }
1414 :
1415 : /* static */ bool
1416 0 : WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp)
1417 : {
1418 0 : CallArgs args = CallArgsFromVp(argc, vp);
1419 0 : return CallNonGenericMethod<IsMemory, growImpl>(cx, args);
1420 : }
1421 :
1422 : const JSFunctionSpec WasmMemoryObject::methods[] =
1423 : {
1424 : JS_FN("grow", WasmMemoryObject::grow, 1, 0),
1425 : JS_FS_END
1426 : };
1427 :
1428 : const JSFunctionSpec WasmMemoryObject::static_methods[] =
1429 : { JS_FS_END };
1430 :
1431 : ArrayBufferObjectMaybeShared&
1432 0 : WasmMemoryObject::buffer() const
1433 : {
1434 0 : return getReservedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObjectMaybeShared>();
1435 : }
1436 :
1437 : bool
1438 0 : WasmMemoryObject::hasObservers() const
1439 : {
1440 0 : return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
1441 : }
1442 :
1443 : WasmMemoryObject::InstanceSet&
1444 0 : WasmMemoryObject::observers() const
1445 : {
1446 0 : MOZ_ASSERT(hasObservers());
1447 0 : return *reinterpret_cast<InstanceSet*>(getReservedSlot(OBSERVERS_SLOT).toPrivate());
1448 : }
1449 :
1450 : WasmMemoryObject::InstanceSet*
1451 0 : WasmMemoryObject::getOrCreateObservers(JSContext* cx)
1452 : {
1453 0 : if (!hasObservers()) {
1454 0 : auto observers = MakeUnique<InstanceSet>(cx->zone());
1455 0 : if (!observers || !observers->init()) {
1456 0 : ReportOutOfMemory(cx);
1457 0 : return nullptr;
1458 : }
1459 :
1460 0 : setReservedSlot(OBSERVERS_SLOT, PrivateValue(observers.release()));
1461 : }
1462 :
1463 0 : return &observers();
1464 : }
1465 :
1466 : bool
1467 0 : WasmMemoryObject::movingGrowable() const
1468 : {
1469 : #ifdef WASM_HUGE_MEMORY
1470 0 : return false;
1471 : #else
1472 : return !buffer().wasmMaxSize();
1473 : #endif
1474 : }
1475 :
1476 : bool
1477 0 : WasmMemoryObject::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance)
1478 : {
1479 0 : MOZ_ASSERT(movingGrowable());
1480 :
1481 0 : InstanceSet* observers = getOrCreateObservers(cx);
1482 0 : if (!observers)
1483 0 : return false;
1484 :
1485 0 : if (!observers->putNew(instance)) {
1486 0 : ReportOutOfMemory(cx);
1487 0 : return false;
1488 : }
1489 :
1490 0 : return true;
1491 : }
1492 :
1493 : /* static */ uint32_t
1494 0 : WasmMemoryObject::grow(HandleWasmMemoryObject memory, uint32_t delta, JSContext* cx)
1495 : {
1496 0 : RootedArrayBufferObject oldBuf(cx, &memory->buffer().as<ArrayBufferObject>());
1497 :
1498 0 : MOZ_ASSERT(oldBuf->byteLength() % PageSize == 0);
1499 0 : uint32_t oldNumPages = oldBuf->byteLength() / PageSize;
1500 :
1501 0 : CheckedInt<uint32_t> newSize = oldNumPages;
1502 0 : newSize += delta;
1503 0 : newSize *= PageSize;
1504 0 : if (!newSize.isValid())
1505 0 : return -1;
1506 :
1507 0 : RootedArrayBufferObject newBuf(cx);
1508 0 : uint8_t* prevMemoryBase = nullptr;
1509 :
1510 0 : if (Maybe<uint32_t> maxSize = oldBuf->wasmMaxSize()) {
1511 0 : if (newSize.value() > maxSize.value())
1512 0 : return -1;
1513 :
1514 0 : if (!ArrayBufferObject::wasmGrowToSizeInPlace(newSize.value(), oldBuf, &newBuf, cx))
1515 0 : return -1;
1516 : } else {
1517 : #ifdef WASM_HUGE_MEMORY
1518 0 : if (!ArrayBufferObject::wasmGrowToSizeInPlace(newSize.value(), oldBuf, &newBuf, cx))
1519 0 : return -1;
1520 : #else
1521 : MOZ_ASSERT(memory->movingGrowable());
1522 : prevMemoryBase = oldBuf->dataPointer();
1523 : if (!ArrayBufferObject::wasmMovingGrowToSize(newSize.value(), oldBuf, &newBuf, cx))
1524 : return -1;
1525 : #endif
1526 : }
1527 :
1528 0 : memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
1529 :
1530 : // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
1531 : // since observers will call buffer().
1532 0 : if (memory->hasObservers()) {
1533 0 : MOZ_ASSERT(prevMemoryBase);
1534 0 : for (InstanceSet::Range r = memory->observers().all(); !r.empty(); r.popFront())
1535 0 : r.front()->instance().onMovingGrowMemory(prevMemoryBase);
1536 : }
1537 :
1538 0 : return oldNumPages;
1539 : }
1540 :
1541 : // ============================================================================
1542 : // WebAssembly.Table class and methods
1543 :
1544 : const ClassOps WasmTableObject::classOps_ =
1545 : {
1546 : nullptr, /* addProperty */
1547 : nullptr, /* delProperty */
1548 : nullptr, /* getProperty */
1549 : nullptr, /* setProperty */
1550 : nullptr, /* enumerate */
1551 : nullptr, /* newEnumerate */
1552 : nullptr, /* resolve */
1553 : nullptr, /* mayResolve */
1554 : WasmTableObject::finalize,
1555 : nullptr, /* call */
1556 : nullptr, /* hasInstance */
1557 : nullptr, /* construct */
1558 : WasmTableObject::trace
1559 : };
1560 :
1561 : const Class WasmTableObject::class_ =
1562 : {
1563 : "WebAssembly.Table",
1564 : JSCLASS_DELAY_METADATA_BUILDER |
1565 : JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
1566 : JSCLASS_FOREGROUND_FINALIZE,
1567 : &WasmTableObject::classOps_
1568 : };
1569 :
1570 : bool
1571 0 : WasmTableObject::isNewborn() const
1572 : {
1573 0 : MOZ_ASSERT(is<WasmTableObject>());
1574 0 : return getReservedSlot(TABLE_SLOT).isUndefined();
1575 : }
1576 :
1577 : /* static */ void
1578 0 : WasmTableObject::finalize(FreeOp* fop, JSObject* obj)
1579 : {
1580 0 : WasmTableObject& tableObj = obj->as<WasmTableObject>();
1581 0 : if (!tableObj.isNewborn())
1582 0 : tableObj.table().Release();
1583 0 : }
1584 :
1585 : /* static */ void
1586 0 : WasmTableObject::trace(JSTracer* trc, JSObject* obj)
1587 : {
1588 0 : WasmTableObject& tableObj = obj->as<WasmTableObject>();
1589 0 : if (!tableObj.isNewborn())
1590 0 : tableObj.table().tracePrivate(trc);
1591 0 : }
1592 :
1593 : /* static */ WasmTableObject*
1594 0 : WasmTableObject::create(JSContext* cx, const Limits& limits)
1595 : {
1596 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject());
1597 :
1598 0 : AutoSetNewObjectMetadata metadata(cx);
1599 0 : RootedWasmTableObject obj(cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
1600 0 : if (!obj)
1601 0 : return nullptr;
1602 :
1603 0 : MOZ_ASSERT(obj->isNewborn());
1604 :
1605 0 : TableDesc td(TableKind::AnyFunction, limits);
1606 0 : td.external = true;
1607 :
1608 0 : SharedTable table = Table::create(cx, td, obj);
1609 0 : if (!table)
1610 0 : return nullptr;
1611 :
1612 0 : obj->initReservedSlot(TABLE_SLOT, PrivateValue(table.forget().take()));
1613 :
1614 0 : MOZ_ASSERT(!obj->isNewborn());
1615 0 : return obj;
1616 : }
1617 :
1618 : /* static */ bool
1619 0 : WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp)
1620 : {
1621 0 : CallArgs args = CallArgsFromVp(argc, vp);
1622 :
1623 0 : if (!ThrowIfNotConstructing(cx, args, "Table"))
1624 0 : return false;
1625 :
1626 0 : if (!args.requireAtLeast(cx, "WebAssembly.Table", 1))
1627 0 : return false;
1628 :
1629 0 : if (!args.get(0).isObject()) {
1630 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "table");
1631 0 : return false;
1632 : }
1633 :
1634 0 : RootedObject obj(cx, &args[0].toObject());
1635 :
1636 0 : JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
1637 0 : if (!elementAtom)
1638 0 : return false;
1639 0 : RootedId elementId(cx, AtomToId(elementAtom));
1640 :
1641 0 : RootedValue elementVal(cx);
1642 0 : if (!GetProperty(cx, obj, obj, elementId, &elementVal))
1643 0 : return false;
1644 :
1645 0 : if (!elementVal.isString()) {
1646 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
1647 0 : return false;
1648 : }
1649 :
1650 0 : JSLinearString* elementStr = elementVal.toString()->ensureLinear(cx);
1651 0 : if (!elementStr)
1652 0 : return false;
1653 :
1654 0 : if (!StringEqualsAscii(elementStr, "anyfunc")) {
1655 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
1656 0 : return false;
1657 : }
1658 :
1659 0 : Limits limits;
1660 0 : if (!GetLimits(cx, obj, MaxTableInitialLength, UINT32_MAX, "Table", &limits))
1661 0 : return false;
1662 :
1663 0 : RootedWasmTableObject table(cx, WasmTableObject::create(cx, limits));
1664 0 : if (!table)
1665 0 : return false;
1666 :
1667 0 : args.rval().setObject(*table);
1668 0 : return true;
1669 : }
1670 :
1671 : static bool
1672 0 : IsTable(HandleValue v)
1673 : {
1674 0 : return v.isObject() && v.toObject().is<WasmTableObject>();
1675 : }
1676 :
1677 : /* static */ bool
1678 0 : WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args)
1679 : {
1680 0 : args.rval().setNumber(args.thisv().toObject().as<WasmTableObject>().table().length());
1681 0 : return true;
1682 : }
1683 :
1684 : /* static */ bool
1685 0 : WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp)
1686 : {
1687 0 : CallArgs args = CallArgsFromVp(argc, vp);
1688 0 : return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
1689 : }
1690 :
1691 : const JSPropertySpec WasmTableObject::properties[] =
1692 : {
1693 : JS_PSG("length", WasmTableObject::lengthGetter, 0),
1694 : JS_PS_END
1695 : };
1696 :
1697 : /* static */ bool
1698 0 : WasmTableObject::getImpl(JSContext* cx, const CallArgs& args)
1699 : {
1700 0 : RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
1701 0 : const Table& table = tableObj->table();
1702 :
1703 : uint32_t index;
1704 0 : if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "get index", &index))
1705 0 : return false;
1706 :
1707 0 : ExternalTableElem& elem = table.externalArray()[index];
1708 0 : if (!elem.code) {
1709 0 : args.rval().setNull();
1710 0 : return true;
1711 : }
1712 :
1713 0 : Instance& instance = *elem.tls->instance;
1714 0 : const CodeRange& codeRange = *instance.code().lookupRange(elem.code);
1715 0 : MOZ_ASSERT(codeRange.isFunction());
1716 :
1717 0 : RootedWasmInstanceObject instanceObj(cx, instance.object());
1718 0 : RootedFunction fun(cx);
1719 0 : if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange.funcIndex(), &fun))
1720 0 : return false;
1721 :
1722 0 : args.rval().setObject(*fun);
1723 0 : return true;
1724 : }
1725 :
1726 : /* static */ bool
1727 0 : WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp)
1728 : {
1729 0 : CallArgs args = CallArgsFromVp(argc, vp);
1730 0 : return CallNonGenericMethod<IsTable, getImpl>(cx, args);
1731 : }
1732 :
1733 : /* static */ bool
1734 0 : WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
1735 : {
1736 0 : RootedWasmTableObject tableObj(cx, &args.thisv().toObject().as<WasmTableObject>());
1737 0 : Table& table = tableObj->table();
1738 :
1739 0 : if (!args.requireAtLeast(cx, "set", 2))
1740 0 : return false;
1741 :
1742 : uint32_t index;
1743 0 : if (!ToNonWrappingUint32(cx, args.get(0), table.length() - 1, "Table", "set index", &index))
1744 0 : return false;
1745 :
1746 0 : RootedFunction value(cx);
1747 0 : if (!IsExportedFunction(args[1], &value) && !args[1].isNull()) {
1748 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_TABLE_VALUE);
1749 0 : return false;
1750 : }
1751 :
1752 0 : if (value) {
1753 0 : RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
1754 0 : uint32_t funcIndex = ExportedFunctionToFuncIndex(value);
1755 0 : Tier tier = Tier::TBD; // Perhaps the tier that the function is at?
1756 :
1757 : #ifdef DEBUG
1758 0 : RootedFunction f(cx);
1759 0 : MOZ_ASSERT(instanceObj->getExportedFunction(cx, instanceObj, funcIndex, &f));
1760 0 : MOZ_ASSERT(value == f);
1761 : #endif
1762 :
1763 0 : Instance& instance = instanceObj->instance();
1764 0 : const FuncExport& funcExport = instance.metadata(tier).lookupFuncExport(funcIndex);
1765 0 : const CodeRange& codeRange = instance.metadata(tier).codeRanges[funcExport.codeRangeIndex()];
1766 0 : void* code = instance.codeBase(tier) + codeRange.funcTableEntry();
1767 0 : table.set(index, code, instance);
1768 : } else {
1769 0 : table.setNull(index);
1770 : }
1771 :
1772 0 : args.rval().setUndefined();
1773 0 : return true;
1774 : }
1775 :
1776 : /* static */ bool
1777 0 : WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp)
1778 : {
1779 0 : CallArgs args = CallArgsFromVp(argc, vp);
1780 0 : return CallNonGenericMethod<IsTable, setImpl>(cx, args);
1781 : }
1782 :
1783 : /* static */ bool
1784 0 : WasmTableObject::growImpl(JSContext* cx, const CallArgs& args)
1785 : {
1786 0 : RootedWasmTableObject table(cx, &args.thisv().toObject().as<WasmTableObject>());
1787 :
1788 : uint32_t delta;
1789 0 : if (!ToNonWrappingUint32(cx, args.get(0), UINT32_MAX, "Table", "grow delta", &delta))
1790 0 : return false;
1791 :
1792 0 : uint32_t ret = table->table().grow(delta, cx);
1793 :
1794 0 : if (ret == uint32_t(-1)) {
1795 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, "table");
1796 0 : return false;
1797 : }
1798 :
1799 0 : args.rval().setInt32(ret);
1800 0 : return true;
1801 : }
1802 :
1803 : /* static */ bool
1804 0 : WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp)
1805 : {
1806 0 : CallArgs args = CallArgsFromVp(argc, vp);
1807 0 : return CallNonGenericMethod<IsTable, growImpl>(cx, args);
1808 : }
1809 :
1810 : const JSFunctionSpec WasmTableObject::methods[] =
1811 : {
1812 : JS_FN("get", WasmTableObject::get, 1, 0),
1813 : JS_FN("set", WasmTableObject::set, 2, 0),
1814 : JS_FN("grow", WasmTableObject::grow, 1, 0),
1815 : JS_FS_END
1816 : };
1817 :
1818 : const JSFunctionSpec WasmTableObject::static_methods[] =
1819 : { JS_FS_END };
1820 :
1821 : Table&
1822 0 : WasmTableObject::table() const
1823 : {
1824 0 : return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
1825 : }
1826 :
1827 : // ============================================================================
1828 : // WebAssembly class and static methods
1829 :
1830 : #if JS_HAS_TOSOURCE
1831 : static bool
1832 0 : WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp)
1833 : {
1834 0 : CallArgs args = CallArgsFromVp(argc, vp);
1835 0 : args.rval().setString(cx->names().WebAssembly);
1836 0 : return true;
1837 : }
1838 : #endif
1839 :
1840 : static bool
1841 0 : Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<PromiseObject*> promise)
1842 : {
1843 0 : if (!error) {
1844 0 : ReportOutOfMemory(cx);
1845 :
1846 0 : RootedValue rejectionValue(cx);
1847 0 : if (!cx->getPendingException(&rejectionValue))
1848 0 : return false;
1849 :
1850 0 : return PromiseObject::reject(cx, promise, rejectionValue);
1851 : }
1852 :
1853 0 : RootedObject stack(cx, promise->allocationSite());
1854 0 : RootedString filename(cx, JS_NewStringCopyZ(cx, args.scriptedCaller.filename.get()));
1855 0 : if (!filename)
1856 0 : return false;
1857 :
1858 0 : unsigned line = args.scriptedCaller.line;
1859 0 : unsigned column = args.scriptedCaller.column;
1860 :
1861 : // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
1862 : // way to create an ErrorObject for an arbitrary error code with multiple
1863 : // replacements.
1864 0 : UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
1865 0 : if (!str)
1866 0 : return false;
1867 :
1868 0 : RootedString message(cx, NewLatin1StringZ(cx, Move(str)));
1869 0 : if (!message)
1870 0 : return false;
1871 :
1872 : RootedObject errorObj(cx,
1873 0 : ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, filename, line, column, nullptr, message));
1874 0 : if (!errorObj)
1875 0 : return false;
1876 :
1877 0 : RootedValue rejectionValue(cx, ObjectValue(*errorObj));
1878 0 : return PromiseObject::reject(cx, promise, rejectionValue);
1879 : }
1880 :
1881 : static bool
1882 0 : ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise)
1883 : {
1884 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
1885 0 : RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
1886 0 : if (!moduleObj)
1887 0 : return false;
1888 :
1889 0 : RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
1890 0 : return PromiseObject::resolve(cx, promise, resolutionValue);
1891 : }
1892 :
1893 0 : struct CompilePromiseTask : PromiseTask
1894 : {
1895 : MutableBytes bytecode;
1896 : CompileArgs compileArgs;
1897 : UniqueChars error;
1898 : SharedModule module;
1899 :
1900 0 : CompilePromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
1901 0 : : PromiseTask(cx, promise)
1902 0 : {}
1903 :
1904 0 : void execute() override {
1905 0 : module = Compile(*bytecode, compileArgs, &error);
1906 0 : }
1907 :
1908 0 : bool finishPromise(JSContext* cx, Handle<PromiseObject*> promise) override {
1909 : return module
1910 0 : ? ResolveCompilation(cx, *module, promise)
1911 0 : : Reject(cx, compileArgs, Move(error), promise);
1912 : }
1913 : };
1914 :
1915 : static bool
1916 0 : RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
1917 : {
1918 0 : if (!cx->isExceptionPending())
1919 0 : return false;
1920 :
1921 0 : RootedValue rejectionValue(cx);
1922 0 : if (!GetAndClearException(cx, &rejectionValue))
1923 0 : return false;
1924 :
1925 0 : return PromiseObject::reject(cx, promise, rejectionValue);
1926 : }
1927 :
1928 : static bool
1929 0 : RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise, CallArgs& callArgs)
1930 : {
1931 0 : if (!RejectWithPendingException(cx, promise))
1932 0 : return false;
1933 :
1934 0 : callArgs.rval().setObject(*promise);
1935 0 : return true;
1936 : }
1937 :
1938 : static bool
1939 0 : GetBufferSource(JSContext* cx, CallArgs callArgs, const char* name, MutableBytes* bytecode)
1940 : {
1941 0 : if (!callArgs.requireAtLeast(cx, name, 1))
1942 0 : return false;
1943 :
1944 0 : if (!callArgs[0].isObject()) {
1945 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
1946 0 : return false;
1947 : }
1948 :
1949 0 : return GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG, bytecode);
1950 : }
1951 :
1952 : static bool
1953 0 : WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp)
1954 : {
1955 0 : if (!cx->runtime()->startAsyncTaskCallback || !cx->runtime()->finishAsyncTaskCallback) {
1956 0 : JS_ReportErrorASCII(cx, "WebAssembly.compile not supported in this runtime.");
1957 0 : return false;
1958 : }
1959 :
1960 0 : Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
1961 0 : if (!promise)
1962 0 : return false;
1963 :
1964 0 : auto task = cx->make_unique<CompilePromiseTask>(cx, promise);
1965 0 : if (!task)
1966 0 : return false;
1967 :
1968 0 : CallArgs callArgs = CallArgsFromVp(argc, vp);
1969 :
1970 0 : if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode))
1971 0 : return RejectWithPendingException(cx, promise, callArgs);
1972 :
1973 0 : if (!InitCompileArgs(cx, &task->compileArgs))
1974 0 : return false;
1975 :
1976 0 : if (!StartPromiseTask(cx, Move(task)))
1977 0 : return false;
1978 :
1979 0 : callArgs.rval().setObject(*promise);
1980 0 : return true;
1981 : }
1982 :
1983 : static bool
1984 0 : ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
1985 : Handle<PromiseObject*> promise)
1986 : {
1987 0 : RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
1988 0 : RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
1989 0 : if (!moduleObj)
1990 0 : return false;
1991 :
1992 0 : RootedWasmInstanceObject instanceObj(cx);
1993 0 : if (!Instantiate(cx, module, importObj, &instanceObj))
1994 0 : return RejectWithPendingException(cx, promise);
1995 :
1996 0 : RootedObject resultObj(cx, JS_NewPlainObject(cx));
1997 0 : if (!resultObj)
1998 0 : return false;
1999 :
2000 0 : RootedValue val(cx, ObjectValue(*moduleObj));
2001 0 : if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE))
2002 0 : return false;
2003 :
2004 0 : val = ObjectValue(*instanceObj);
2005 0 : if (!JS_DefineProperty(cx, resultObj, "instance", val, JSPROP_ENUMERATE))
2006 0 : return false;
2007 :
2008 0 : val = ObjectValue(*resultObj);
2009 0 : return PromiseObject::resolve(cx, promise, val);
2010 : }
2011 :
2012 0 : struct InstantiatePromiseTask : CompilePromiseTask
2013 : {
2014 : PersistentRootedObject importObj;
2015 :
2016 0 : InstantiatePromiseTask(JSContext* cx, Handle<PromiseObject*> promise, HandleObject importObj)
2017 0 : : CompilePromiseTask(cx, promise),
2018 0 : importObj(cx, importObj)
2019 0 : {}
2020 :
2021 0 : bool finishPromise(JSContext* cx, Handle<PromiseObject*> promise) override {
2022 : return module
2023 0 : ? ResolveInstantiation(cx, *module, importObj, promise)
2024 0 : : Reject(cx, compileArgs, Move(error), promise);
2025 : }
2026 : };
2027 :
2028 : static bool
2029 0 : GetInstantiateArgs(JSContext* cx, CallArgs callArgs, MutableHandleObject firstArg,
2030 : MutableHandleObject importObj)
2031 : {
2032 0 : if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1))
2033 0 : return false;
2034 :
2035 0 : if (!callArgs[0].isObject()) {
2036 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_MOD_ARG);
2037 0 : return false;
2038 : }
2039 :
2040 0 : firstArg.set(&callArgs[0].toObject());
2041 :
2042 0 : return GetImportArg(cx, callArgs, importObj);
2043 : }
2044 :
2045 : static bool
2046 0 : WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp)
2047 : {
2048 0 : if (!cx->runtime()->startAsyncTaskCallback || !cx->runtime()->finishAsyncTaskCallback) {
2049 0 : JS_ReportErrorASCII(cx, "WebAssembly.instantiate not supported in this runtime.");
2050 0 : return false;
2051 : }
2052 :
2053 0 : Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
2054 0 : if (!promise)
2055 0 : return false;
2056 :
2057 0 : CallArgs callArgs = CallArgsFromVp(argc, vp);
2058 :
2059 0 : RootedObject firstArg(cx);
2060 0 : RootedObject importObj(cx);
2061 0 : if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj))
2062 0 : return RejectWithPendingException(cx, promise, callArgs);
2063 :
2064 : Module* module;
2065 0 : if (IsModuleObject(firstArg, &module)) {
2066 0 : RootedWasmInstanceObject instanceObj(cx);
2067 0 : if (!Instantiate(cx, *module, importObj, &instanceObj))
2068 0 : return RejectWithPendingException(cx, promise, callArgs);
2069 :
2070 0 : RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
2071 0 : if (!PromiseObject::resolve(cx, promise, resolutionValue))
2072 0 : return false;
2073 : } else {
2074 0 : auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, importObj);
2075 0 : if (!task)
2076 0 : return false;
2077 :
2078 0 : if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG, &task->bytecode))
2079 0 : return RejectWithPendingException(cx, promise, callArgs);
2080 :
2081 0 : if (!InitCompileArgs(cx, &task->compileArgs))
2082 0 : return false;
2083 :
2084 0 : if (!StartPromiseTask(cx, Move(task)))
2085 0 : return false;
2086 : }
2087 :
2088 0 : callArgs.rval().setObject(*promise);
2089 0 : return true;
2090 : }
2091 :
2092 : static bool
2093 0 : WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp)
2094 : {
2095 0 : CallArgs callArgs = CallArgsFromVp(argc, vp);
2096 :
2097 0 : MutableBytes bytecode;
2098 0 : if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode))
2099 0 : return false;
2100 :
2101 0 : UniqueChars error;
2102 0 : bool validated = Validate(*bytecode, &error);
2103 :
2104 : // If the reason for validation failure was OOM (signalled by null error
2105 : // message), report out-of-memory so that validate's return is always
2106 : // correct.
2107 0 : if (!validated && !error) {
2108 0 : ReportOutOfMemory(cx);
2109 0 : return false;
2110 : }
2111 :
2112 0 : callArgs.rval().setBoolean(validated);
2113 0 : return true;
2114 : }
2115 :
2116 : static const JSFunctionSpec WebAssembly_static_methods[] =
2117 : {
2118 : #if JS_HAS_TOSOURCE
2119 : JS_FN(js_toSource_str, WebAssembly_toSource, 0, 0),
2120 : #endif
2121 : JS_FN("compile", WebAssembly_compile, 1, 0),
2122 : JS_FN("instantiate", WebAssembly_instantiate, 1, 0),
2123 : JS_FN("validate", WebAssembly_validate, 1, 0),
2124 : JS_FS_END
2125 : };
2126 :
2127 : const Class js::WebAssemblyClass =
2128 : {
2129 : js_WebAssembly_str,
2130 : JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly)
2131 : };
2132 :
2133 : template <class Class>
2134 : static bool
2135 24 : InitConstructor(JSContext* cx, HandleObject wasm, const char* name, MutableHandleObject proto)
2136 : {
2137 24 : proto.set(NewBuiltinClassInstance<PlainObject>(cx, SingletonObject));
2138 24 : if (!proto)
2139 0 : return false;
2140 :
2141 24 : if (!DefinePropertiesAndFunctions(cx, proto, Class::properties, Class::methods))
2142 0 : return false;
2143 :
2144 48 : RootedAtom className(cx, Atomize(cx, name, strlen(name)));
2145 24 : if (!className)
2146 0 : return false;
2147 :
2148 48 : RootedFunction ctor(cx, NewNativeConstructor(cx, Class::construct, 1, className));
2149 24 : if (!ctor)
2150 0 : return false;
2151 :
2152 24 : if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, Class::static_methods))
2153 0 : return false;
2154 :
2155 24 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
2156 0 : return false;
2157 :
2158 48 : UniqueChars tagStr(JS_smprintf("WebAssembly.%s", name));
2159 24 : if (!tagStr) {
2160 0 : ReportOutOfMemory(cx);
2161 0 : return false;
2162 : }
2163 :
2164 48 : RootedAtom tag(cx, Atomize(cx, tagStr.get(), strlen(tagStr.get())));
2165 24 : if (!tag)
2166 0 : return false;
2167 24 : if (!DefineToStringTag(cx, proto, tag))
2168 0 : return false;
2169 :
2170 48 : RootedId id(cx, AtomToId(className));
2171 48 : RootedValue ctorValue(cx, ObjectValue(*ctor));
2172 24 : return DefineProperty(cx, wasm, id, ctorValue, nullptr, nullptr, 0);
2173 : }
2174 :
2175 : static bool
2176 18 : InitErrorClass(JSContext* cx, HandleObject wasm, const char* name, JSExnType exn)
2177 : {
2178 18 : Handle<GlobalObject*> global = cx->global();
2179 36 : RootedObject proto(cx, GlobalObject::getOrCreateCustomErrorPrototype(cx, global, exn));
2180 18 : if (!proto)
2181 0 : return false;
2182 :
2183 36 : RootedAtom className(cx, Atomize(cx, name, strlen(name)));
2184 18 : if (!className)
2185 0 : return false;
2186 :
2187 36 : RootedId id(cx, AtomToId(className));
2188 36 : RootedValue ctorValue(cx, global->getConstructor(GetExceptionProtoKey(exn)));
2189 18 : return DefineProperty(cx, wasm, id, ctorValue, nullptr, nullptr, 0);
2190 : }
2191 :
2192 : JSObject*
2193 6 : js::InitWebAssemblyClass(JSContext* cx, HandleObject obj)
2194 : {
2195 6 : MOZ_RELEASE_ASSERT(HasSupport(cx));
2196 :
2197 6 : Handle<GlobalObject*> global = obj.as<GlobalObject>();
2198 6 : MOZ_ASSERT(!global->isStandardClassResolved(JSProto_WebAssembly));
2199 :
2200 12 : RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
2201 6 : if (!proto)
2202 0 : return nullptr;
2203 :
2204 12 : RootedObject wasm(cx, NewObjectWithGivenProto(cx, &WebAssemblyClass, proto, SingletonObject));
2205 6 : if (!wasm)
2206 0 : return nullptr;
2207 :
2208 6 : if (!JS_DefineFunctions(cx, wasm, WebAssembly_static_methods))
2209 0 : return nullptr;
2210 :
2211 12 : RootedObject moduleProto(cx), instanceProto(cx), memoryProto(cx), tableProto(cx);
2212 6 : if (!InitConstructor<WasmModuleObject>(cx, wasm, "Module", &moduleProto))
2213 0 : return nullptr;
2214 6 : if (!InitConstructor<WasmInstanceObject>(cx, wasm, "Instance", &instanceProto))
2215 0 : return nullptr;
2216 6 : if (!InitConstructor<WasmMemoryObject>(cx, wasm, "Memory", &memoryProto))
2217 0 : return nullptr;
2218 6 : if (!InitConstructor<WasmTableObject>(cx, wasm, "Table", &tableProto))
2219 0 : return nullptr;
2220 6 : if (!InitErrorClass(cx, wasm, "CompileError", JSEXN_WASMCOMPILEERROR))
2221 0 : return nullptr;
2222 6 : if (!InitErrorClass(cx, wasm, "LinkError", JSEXN_WASMLINKERROR))
2223 0 : return nullptr;
2224 6 : if (!InitErrorClass(cx, wasm, "RuntimeError", JSEXN_WASMRUNTIMEERROR))
2225 0 : return nullptr;
2226 :
2227 : // Perform the final fallible write of the WebAssembly object to a global
2228 : // object property at the end. Only after that succeeds write all the
2229 : // constructor and prototypes to the JSProto slots. This ensures that
2230 : // initialization is atomic since a failed initialization can be retried.
2231 :
2232 6 : if (!JS_DefineProperty(cx, global, js_WebAssembly_str, wasm, JSPROP_RESOLVING))
2233 0 : return nullptr;
2234 :
2235 6 : global->setPrototype(JSProto_WasmModule, ObjectValue(*moduleProto));
2236 6 : global->setPrototype(JSProto_WasmInstance, ObjectValue(*instanceProto));
2237 6 : global->setPrototype(JSProto_WasmMemory, ObjectValue(*memoryProto));
2238 6 : global->setPrototype(JSProto_WasmTable, ObjectValue(*tableProto));
2239 6 : global->setConstructor(JSProto_WebAssembly, ObjectValue(*wasm));
2240 :
2241 6 : MOZ_ASSERT(global->isStandardClassResolved(JSProto_WebAssembly));
2242 6 : return wasm;
2243 : }
|