Source code

Revision control

Other Tools

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
*
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/Promise.h"
29
#include "builtin/TypedObject.h"
30
#include "gc/FreeOp.h"
31
#include "jit/AtomicOperations.h"
32
#include "jit/JitOptions.h"
33
#include "jit/Simulator.h"
34
#include "js/Printf.h"
35
#include "js/PropertySpec.h" // JS_{PS,FN}{,_END}
36
#include "util/StringBuffer.h"
37
#include "util/Text.h"
38
#include "vm/ErrorObject.h"
39
#include "vm/Interpreter.h"
40
#include "vm/StringType.h"
41
#include "wasm/WasmBaselineCompile.h"
42
#include "wasm/WasmCompile.h"
43
#include "wasm/WasmCraneliftCompile.h"
44
#include "wasm/WasmInstance.h"
45
#include "wasm/WasmIonCompile.h"
46
#include "wasm/WasmModule.h"
47
#include "wasm/WasmProcess.h"
48
#include "wasm/WasmSignalHandlers.h"
49
#include "wasm/WasmStubs.h"
50
#include "wasm/WasmValidate.h"
51
52
#include "vm/ArrayBufferObject-inl.h"
53
#include "vm/JSObject-inl.h"
54
#include "vm/NativeObject-inl.h"
55
56
using namespace js;
57
using namespace js::jit;
58
using namespace js::wasm;
59
60
using mozilla::CheckedInt;
61
using mozilla::MakeSpan;
62
using mozilla::Nothing;
63
using mozilla::RangedPtr;
64
65
extern mozilla::Atomic<bool> fuzzingSafe;
66
67
bool wasm::HasReftypesSupport(JSContext* cx) {
68
#ifdef ENABLE_WASM_CRANELIFT
69
if (cx->options().wasmCranelift()) {
70
return false;
71
}
72
#endif
73
#ifdef ENABLE_WASM_REFTYPES
74
return true;
75
#else
76
return false;
77
#endif
78
}
79
80
bool wasm::HasGcSupport(JSContext* cx) {
81
#ifdef ENABLE_WASM_CRANELIFT
82
if (cx->options().wasmCranelift()) {
83
return false;
84
}
85
#endif
86
#ifdef ENABLE_WASM_GC
87
return cx->options().wasmGc() && cx->options().wasmBaseline();
88
#else
89
return false;
90
#endif
91
}
92
93
bool wasm::HasMultiValueSupport(JSContext* cx) {
94
#ifdef ENABLE_WASM_CRANELIFT
95
if (cx->options().wasmCranelift()) {
96
return false;
97
}
98
#endif
99
#ifdef ENABLE_WASM_MULTI_VALUE
100
return true;
101
#else
102
return false;
103
#endif
104
}
105
106
bool wasm::HasI64BigIntSupport(JSContext* cx) {
107
#ifdef ENABLE_WASM_CRANELIFT
108
if (cx->options().wasmCranelift()) {
109
return false;
110
}
111
#endif
112
#ifdef ENABLE_WASM_BIGINT
113
return cx->options().isWasmBigIntEnabled();
114
#else
115
return false;
116
#endif
117
}
118
119
bool wasm::HasCompilerSupport(JSContext* cx) {
120
#if !MOZ_LITTLE_ENDIAN() || defined(JS_CODEGEN_NONE)
121
return false;
122
#endif
123
124
if (gc::SystemPageSize() > wasm::PageSize) {
125
return false;
126
}
127
128
if (!JitOptions.supportsFloatingPoint) {
129
return false;
130
}
131
132
if (!JitOptions.supportsUnalignedAccesses) {
133
return false;
134
}
135
136
if (!wasm::EnsureFullSignalHandlers(cx)) {
137
return false;
138
}
139
140
// Wasm threads require 8-byte lock-free atomics.
141
if (!jit::AtomicOperations::isLockfree8()) {
142
return false;
143
}
144
145
#ifdef JS_SIMULATOR
146
if (!Simulator::supportsAtomics()) {
147
return false;
148
}
149
#endif
150
151
return BaselineCanCompile() || IonCanCompile() || CraneliftCanCompile();
152
}
153
154
bool wasm::HasOptimizedCompilerTier(JSContext* cx) {
155
return (cx->options().wasmIon() && IonCanCompile())
156
#ifdef ENABLE_WASM_CRANELIFT
157
|| (cx->options().wasmCranelift() && CraneliftCanCompile())
158
#endif
159
;
160
}
161
162
// Return whether wasm compilation is allowed by prefs. This check
163
// only makes sense if HasCompilerSupport() is true.
164
static bool HasAvailableCompilerTier(JSContext* cx) {
165
return (cx->options().wasmBaseline() && BaselineCanCompile()) ||
166
HasOptimizedCompilerTier(cx);
167
}
168
169
bool wasm::HasSupport(JSContext* cx) {
170
// If the general wasm pref is on, it's on for everything.
171
bool prefEnabled = cx->options().wasm();
172
// If the general pref is off, check trusted principals.
173
if (MOZ_UNLIKELY(!prefEnabled)) {
174
prefEnabled = cx->options().wasmForTrustedPrinciples() && cx->realm() &&
175
cx->realm()->principals() &&
176
cx->realm()->principals()->isSystemOrAddonPrincipal();
177
}
178
return prefEnabled && HasCompilerSupport(cx) && HasAvailableCompilerTier(cx);
179
}
180
181
bool wasm::HasStreamingSupport(JSContext* cx) {
182
// This should match EnsureStreamSupport().
183
return HasSupport(cx) &&
184
cx->runtime()->offThreadPromiseState.ref().initialized() &&
185
CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
186
cx->runtime()->reportStreamErrorCallback;
187
}
188
189
bool wasm::HasCachingSupport(JSContext* cx) {
190
return HasStreamingSupport(cx) && HasOptimizedCompilerTier(cx);
191
}
192
193
static bool ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v,
194
MutableHandleVal val) {
195
switch (targetType.kind()) {
196
case ValType::I32: {
197
int32_t i32;
198
if (!ToInt32(cx, v, &i32)) {
199
return false;
200
}
201
val.set(Val(uint32_t(i32)));
202
return true;
203
}
204
case ValType::F32: {
205
double d;
206
if (!ToNumber(cx, v, &d)) {
207
return false;
208
}
209
val.set(Val(float(d)));
210
return true;
211
}
212
case ValType::F64: {
213
double d;
214
if (!ToNumber(cx, v, &d)) {
215
return false;
216
}
217
val.set(Val(d));
218
return true;
219
}
220
case ValType::I64: {
221
#ifdef ENABLE_WASM_BIGINT
222
if (HasI64BigIntSupport(cx)) {
223
BigInt* bigint = ToBigInt(cx, v);
224
if (!bigint) {
225
return false;
226
}
227
val.set(Val(BigInt::toUint64(bigint)));
228
return true;
229
}
230
#endif
231
break;
232
}
233
case ValType::Ref: {
234
switch (targetType.refTypeKind()) {
235
case RefType::Func: {
236
RootedFunction fun(cx);
237
if (!CheckFuncRefValue(cx, v, &fun)) {
238
return false;
239
}
240
val.set(Val(RefType::func(), FuncRef::fromJSFunction(fun)));
241
return true;
242
}
243
case RefType::Null: {
244
if (!v.isNull()) {
245
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
246
JSMSG_WASM_NULL_REQUIRED);
247
return false;
248
}
249
val.set(Val(RefType::null(), AnyRef::null()));
250
return true;
251
}
252
case RefType::Any: {
253
RootedAnyRef tmp(cx, AnyRef::null());
254
if (!BoxAnyRef(cx, v, &tmp)) {
255
return false;
256
}
257
val.set(Val(RefType::any(), tmp));
258
return true;
259
}
260
case RefType::TypeIndex: {
261
break;
262
}
263
}
264
break;
265
}
266
}
267
MOZ_CRASH("unexpected import value type, caller must guard");
268
}
269
270
static bool ToJSValue(JSContext* cx, const Val& val, MutableHandleValue out) {
271
switch (val.type().kind()) {
272
case ValType::I32:
273
out.setInt32(val.i32());
274
return true;
275
case ValType::F32:
276
out.setDouble(JS::CanonicalizeNaN(double(val.f32())));
277
return true;
278
case ValType::F64:
279
out.setDouble(JS::CanonicalizeNaN(val.f64()));
280
return true;
281
case ValType::I64: {
282
#ifdef ENABLE_WASM_BIGINT
283
if (HasI64BigIntSupport(cx)) {
284
BigInt* bi = BigInt::createFromInt64(cx, val.i64());
285
if (!bi) {
286
return false;
287
}
288
out.setBigInt(bi);
289
return true;
290
}
291
#endif
292
break;
293
}
294
case ValType::Ref:
295
switch (val.type().refTypeKind()) {
296
case RefType::Func:
297
out.set(UnboxFuncRef(FuncRef::fromAnyRefUnchecked(val.ref())));
298
return true;
299
case RefType::Any:
300
case RefType::Null:
301
out.set(UnboxAnyRef(val.ref()));
302
return true;
303
case RefType::TypeIndex:
304
break;
305
}
306
break;
307
}
308
MOZ_CRASH("unexpected type when translating to a JS value");
309
}
310
311
// ============================================================================
312
// Imports
313
314
static bool ThrowBadImportArg(JSContext* cx) {
315
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
316
JSMSG_WASM_BAD_IMPORT_ARG);
317
return false;
318
}
319
320
static bool ThrowBadImportType(JSContext* cx, const char* field,
321
const char* str) {
322
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
323
JSMSG_WASM_BAD_IMPORT_TYPE, field, str);
324
return false;
325
}
326
327
static bool GetProperty(JSContext* cx, HandleObject obj, const char* chars,
328
MutableHandleValue v) {
329
JSAtom* atom = AtomizeUTF8Chars(cx, chars, strlen(chars));
330
if (!atom) {
331
return false;
332
}
333
334
RootedId id(cx, AtomToId(atom));
335
return GetProperty(cx, obj, obj, id, v);
336
}
337
338
bool js::wasm::GetImports(JSContext* cx, const Module& module,
339
HandleObject importObj, ImportValues* imports) {
340
if (!module.imports().empty() && !importObj) {
341
return ThrowBadImportArg(cx);
342
}
343
344
const Metadata& metadata = module.metadata();
345
346
uint32_t globalIndex = 0;
347
const GlobalDescVector& globals = metadata.globals;
348
uint32_t tableIndex = 0;
349
const TableDescVector& tables = metadata.tables;
350
for (const Import& import : module.imports()) {
351
RootedValue v(cx);
352
if (!GetProperty(cx, importObj, import.module.get(), &v)) {
353
return false;
354
}
355
356
if (!v.isObject()) {
357
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
358
JSMSG_WASM_BAD_IMPORT_FIELD,
359
import.module.get());
360
return false;
361
}
362
363
RootedObject obj(cx, &v.toObject());
364
if (!GetProperty(cx, obj, import.field.get(), &v)) {
365
return false;
366
}
367
368
switch (import.kind) {
369
case DefinitionKind::Function: {
370
if (!IsFunctionObject(v)) {
371
return ThrowBadImportType(cx, import.field.get(), "Function");
372
}
373
374
if (!imports->funcs.append(&v.toObject().as<JSFunction>())) {
375
return false;
376
}
377
378
break;
379
}
380
case DefinitionKind::Table: {
381
const uint32_t index = tableIndex++;
382
if (!v.isObject() || !v.toObject().is<WasmTableObject>()) {
383
return ThrowBadImportType(cx, import.field.get(), "Table");
384
}
385
386
RootedWasmTableObject obj(cx, &v.toObject().as<WasmTableObject>());
387
if (obj->table().kind() != tables[index].kind) {
388
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
389
JSMSG_WASM_BAD_TBL_TYPE_LINK);
390
return false;
391
}
392
393
if (!imports->tables.append(obj)) {
394
return false;
395
}
396
break;
397
}
398
case DefinitionKind::Memory: {
399
if (!v.isObject() || !v.toObject().is<WasmMemoryObject>()) {
400
return ThrowBadImportType(cx, import.field.get(), "Memory");
401
}
402
403
MOZ_ASSERT(!imports->memory);
404
imports->memory = &v.toObject().as<WasmMemoryObject>();
405
break;
406
}
407
case DefinitionKind::Global: {
408
const uint32_t index = globalIndex++;
409
const GlobalDesc& global = globals[index];
410
MOZ_ASSERT(global.importIndex() == index);
411
412
RootedVal val(cx);
413
if (v.isObject() && v.toObject().is<WasmGlobalObject>()) {
414
RootedWasmGlobalObject obj(cx, &v.toObject().as<WasmGlobalObject>());
415
416
if (obj->isMutable() != global.isMutable()) {
417
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
418
JSMSG_WASM_BAD_GLOB_MUT_LINK);
419
return false;
420
}
421
if (obj->type() != global.type()) {
422
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
423
JSMSG_WASM_BAD_GLOB_TYPE_LINK);
424
return false;
425
}
426
427
if (imports->globalObjs.length() <= index &&
428
!imports->globalObjs.resize(index + 1)) {
429
ReportOutOfMemory(cx);
430
return false;
431
}
432
imports->globalObjs[index] = obj;
433
obj->val(&val);
434
} else {
435
if (IsNumberType(global.type())) {
436
if (HasI64BigIntSupport(cx)) {
437
if (global.type() == ValType::I64 && v.isNumber()) {
438
return ThrowBadImportType(cx, import.field.get(), "BigInt");
439
}
440
if (global.type() != ValType::I64 && !v.isNumber()) {
441
return ThrowBadImportType(cx, import.field.get(), "Number");
442
}
443
} else {
444
if (!v.isNumber()) {
445
return ThrowBadImportType(cx, import.field.get(), "Number");
446
}
447
if (global.type() == ValType::I64) {
448
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
449
JSMSG_WASM_BAD_I64_LINK);
450
return false;
451
}
452
}
453
} else {
454
MOZ_ASSERT(global.type().isReference());
455
if (!v.isNull() && !v.isObject() && global.type().isRef()) {
456
return ThrowBadImportType(cx, import.field.get(),
457
"Object-or-null");
458
}
459
}
460
461
if (global.isMutable()) {
462
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
463
JSMSG_WASM_BAD_GLOB_MUT_LINK);
464
return false;
465
}
466
467
if (!ToWebAssemblyValue(cx, global.type(), v, &val)) {
468
return false;
469
}
470
}
471
472
if (!imports->globalValues.append(val)) {
473
return false;
474
}
475
476
break;
477
}
478
}
479
}
480
481
MOZ_ASSERT(globalIndex == globals.length() ||
482
!globals[globalIndex].isImport());
483
484
return true;
485
}
486
487
static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller,
488
const char* introducer) {
489
// Note: JS::DescribeScriptedCaller returns whether a scripted caller was
490
// found, not whether an error was thrown. This wrapper function converts
491
// back to the more ordinary false-if-error form.
492
493
JS::AutoFilename af;
494
if (JS::DescribeScriptedCaller(cx, &af, &caller->line)) {
495
caller->filename =
496
FormatIntroducedFilename(cx, af.get(), caller->line, introducer);
497
if (!caller->filename) {
498
return false;
499
}
500
}
501
502
return true;
503
}
504
505
// ============================================================================
506
// Testing / Fuzzing support
507
508
bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code,
509
HandleObject importObj,
510
MutableHandleWasmInstanceObject instanceObj) {
511
if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
512
return false;
513
}
514
515
MutableBytes bytecode = cx->new_<ShareableBytes>();
516
if (!bytecode) {
517
return false;
518
}
519
520
if (!bytecode->append((uint8_t*)code->dataPointerEither().unwrap(),
521
code->byteLength())) {
522
ReportOutOfMemory(cx);
523
return false;
524
}
525
526
ScriptedCaller scriptedCaller;
527
if (!DescribeScriptedCaller(cx, &scriptedCaller, "wasm_eval")) {
528
return false;
529
}
530
531
SharedCompileArgs compileArgs =
532
CompileArgs::build(cx, std::move(scriptedCaller));
533
if (!compileArgs) {
534
return false;
535
}
536
537
UniqueChars error;
538
UniqueCharsVector warnings;
539
SharedModule module =
540
CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
541
if (!module) {
542
if (error) {
543
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
544
JSMSG_WASM_COMPILE_ERROR, error.get());
545
return false;
546
}
547
ReportOutOfMemory(cx);
548
return false;
549
}
550
551
Rooted<ImportValues> imports(cx);
552
if (!GetImports(cx, *module, importObj, imports.address())) {
553
return false;
554
}
555
556
return module->instantiate(cx, imports.get(), nullptr, instanceObj);
557
}
558
559
struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
560
// MOZ_STACK_CLASS means these can be nops.
561
MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
562
MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
563
564
DebugOnly<bool> called = false;
565
Bytes* serialized;
566
explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
567
568
void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes bytes) override {
569
MOZ_ASSERT(!called);
570
called = true;
571
if (serialized->resize(bytes->length())) {
572
memcpy(serialized->begin(), bytes->begin(), bytes->length());
573
}
574
}
575
};
576
577
bool wasm::CompileAndSerialize(const ShareableBytes& bytecode,
578
Bytes* serialized) {
579
MutableCompileArgs compileArgs = js_new<CompileArgs>(ScriptedCaller());
580
if (!compileArgs) {
581
return false;
582
}
583
584
// The caller has ensured HasCachingSupport(). Moreover, we want to ensure
585
// we go straight to tier-2 so that we synchronously call
586
// JS::OptimizedEncodingListener::storeOptimizedEncoding().
587
compileArgs->baselineEnabled = false;
588
compileArgs->ionEnabled = true;
589
590
// The caller must ensure that huge memory support is configured the same in
591
// the receiving process of this serialized module.
592
compileArgs->hugeMemory = wasm::IsHugeMemoryEnabled();
593
594
SerializeListener listener(serialized);
595
596
UniqueChars error;
597
UniqueCharsVector warnings;
598
SharedModule module =
599
CompileBuffer(*compileArgs, bytecode, &error, &warnings, &listener);
600
if (!module) {
601
fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
602
return false;
603
}
604
605
MOZ_ASSERT(module->code().hasTier(Tier::Serialized));
606
MOZ_ASSERT(listener.called);
607
return !listener.serialized->empty();
608
}
609
610
bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized,
611
MutableHandleObject moduleObj) {
612
MutableModule module =
613
Module::deserialize(serialized.begin(), serialized.length());
614
if (!module) {
615
ReportOutOfMemory(cx);
616
return false;
617
}
618
619
moduleObj.set(module->createObject(cx));
620
return !!moduleObj;
621
}
622
623
// ============================================================================
624
// Common functions
625
626
// '[EnforceRange] unsigned long' types are coerced with
627
// ConvertToInt(v, 32, 'unsigned')
628
// defined in Web IDL Section 3.2.4.9.
629
static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
630
const char* noun, uint32_t* u32) {
631
// Step 4.
632
double x;
633
if (!ToNumber(cx, v, &x)) {
634
return false;
635
}
636
637
// Step 5.
638
if (mozilla::IsNegativeZero(x)) {
639
x = 0.0;
640
}
641
642
// Step 6.1.
643
if (!mozilla::IsFinite(x)) {
644
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
645
JSMSG_WASM_BAD_UINT32, kind, noun);
646
return false;
647
}
648
649
// Step 6.2.
650
x = JS::ToInteger(x);
651
652
// Step 6.3.
653
if (x < 0 || x > double(UINT32_MAX)) {
654
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
655
JSMSG_WASM_BAD_UINT32, kind, noun);
656
return false;
657
}
658
659
*u32 = uint32_t(x);
660
MOZ_ASSERT(double(*u32) == x);
661
return true;
662
}
663
664
static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maxInitial,
665
uint32_t maxMaximum, const char* kind, Limits* limits,
666
Shareable allowShared) {
667
JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
668
if (!initialAtom) {
669
return false;
670
}
671
RootedId initialId(cx, AtomToId(initialAtom));
672
673
RootedValue initialVal(cx);
674
if (!GetProperty(cx, obj, obj, initialId, &initialVal)) {
675
return false;
676
}
677
678
if (!EnforceRangeU32(cx, initialVal, kind, "initial size",
679
&limits->initial)) {
680
return false;
681
}
682
683
if (limits->initial > maxInitial) {
684
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
685
kind, "initial size");
686
return false;
687
}
688
689
JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
690
if (!maximumAtom) {
691
return false;
692
}
693
RootedId maximumId(cx, AtomToId(maximumAtom));
694
695
RootedValue maxVal(cx);
696
if (!GetProperty(cx, obj, obj, maximumId, &maxVal)) {
697
return false;
698
}
699
700
// maxVal does not have a default value.
701
if (!maxVal.isUndefined()) {
702
limits->maximum.emplace();
703
if (!EnforceRangeU32(cx, maxVal, kind, "maximum size",
704
limits->maximum.ptr())) {
705
return false;
706
}
707
708
if (*limits->maximum > maxMaximum || limits->initial > *limits->maximum) {
709
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
710
JSMSG_WASM_BAD_RANGE, kind, "maximum size");
711
return false;
712
}
713
}
714
715
limits->shared = Shareable::False;
716
717
if (allowShared == Shareable::True) {
718
JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
719
if (!sharedAtom) {
720
return false;
721
}
722
RootedId sharedId(cx, AtomToId(sharedAtom));
723
724
RootedValue sharedVal(cx);
725
if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) {
726
return false;
727
}
728
729
// shared's default value is false, which is already the value set above.
730
if (!sharedVal.isUndefined()) {
731
limits->shared =
732
ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
733
734
if (limits->shared == Shareable::True) {
735
if (maxVal.isUndefined()) {
736
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
737
JSMSG_WASM_MISSING_MAXIMUM, kind);
738
return false;
739
}
740
741
if (!cx->realm()
742
->creationOptions()
743
.getSharedMemoryAndAtomicsEnabled()) {
744
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
745
JSMSG_WASM_NO_SHMEM_LINK);
746
return false;
747
}
748
}
749
}
750
}
751
752
return true;
753
}
754
755
template <class Class, const char* name>
756
static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
757
RootedAtom className(cx, Atomize(cx, name, strlen(name)));
758
if (!className) {
759
return nullptr;
760
}
761
762
return NewNativeConstructor(cx, Class::construct, 1, className);
763
}
764
765
// ============================================================================
766
// WebAssembly.Module class and methods
767
768
const JSClassOps WasmModuleObject::classOps_ = {nullptr, /* addProperty */
769
nullptr, /* delProperty */
770
nullptr, /* enumerate */
771
nullptr, /* newEnumerate */
772
nullptr, /* resolve */
773
nullptr, /* mayResolve */
774
WasmModuleObject::finalize};
775
776
const JSClass WasmModuleObject::class_ = {
777
"WebAssembly.Module",
778
JSCLASS_DELAY_METADATA_BUILDER |
779
JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
780
JSCLASS_FOREGROUND_FINALIZE,
781
&WasmModuleObject::classOps_,
782
&WasmModuleObject::classSpec_,
783
};
784
785
const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
786
787
static constexpr char WasmModuleName[] = "Module";
788
789
const ClassSpec WasmModuleObject::classSpec_ = {
790
CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
791
GenericCreatePrototype<WasmModuleObject>,
792
WasmModuleObject::static_methods,
793
nullptr,
794
WasmModuleObject::methods,
795
WasmModuleObject::properties,
796
nullptr,
797
ClassSpec::DontDefineConstructor};
798
799
const JSPropertySpec WasmModuleObject::properties[] = {
800
JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
801
JS_PS_END};
802
803
const JSFunctionSpec WasmModuleObject::methods[] = {JS_FS_END};
804
805
const JSFunctionSpec WasmModuleObject::static_methods[] = {
806
JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
807
JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
808
JS_FN("customSections", WasmModuleObject::customSections, 2,
809
JSPROP_ENUMERATE),
810
JS_FS_END};
811
812
/* static */
813
void WasmModuleObject::finalize(JSFreeOp* fop, JSObject* obj) {
814
const Module& module = obj->as<WasmModuleObject>().module();
815
obj->zone()->decJitMemory(module.codeLength(module.code().stableTier()));
816
fop->release(obj, &module, module.gcMallocBytesExcludingCode(),
817
MemoryUse::WasmModule);
818
}
819
820
static bool IsModuleObject(JSObject* obj, const Module** module) {
821
WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>();
822
if (!mobj) {
823
return false;
824
}
825
826
*module = &mobj->module();
827
return true;
828
}
829
830
static bool GetModuleArg(JSContext* cx, CallArgs args, uint32_t numRequired,
831
const char* name, const Module** module) {
832
if (!args.requireAtLeast(cx, name, numRequired)) {
833
return false;
834
}
835
836
if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
837
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
838
JSMSG_WASM_BAD_MOD_ARG);
839
return false;
840
}
841
842
return true;
843
}
844
845
struct KindNames {
846
RootedPropertyName kind;
847
RootedPropertyName table;
848
RootedPropertyName memory;
849
RootedPropertyName signature;
850
851
explicit KindNames(JSContext* cx)
852
: kind(cx), table(cx), memory(cx), signature(cx) {}
853
};
854
855
static bool InitKindNames(JSContext* cx, KindNames* names) {
856
JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
857
if (!kind) {
858
return false;
859
}
860
names->kind = kind->asPropertyName();
861
862
JSAtom* table = Atomize(cx, "table", strlen("table"));
863
if (!table) {
864
return false;
865
}
866
names->table = table->asPropertyName();
867
868
JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
869
if (!memory) {
870
return false;
871
}
872
names->memory = memory->asPropertyName();
873
874
JSAtom* signature = Atomize(cx, "signature", strlen("signature"));
875
if (!signature) {
876
return false;
877
}
878
names->signature = signature->asPropertyName();
879
880
return true;
881
}
882
883
static JSString* KindToString(JSContext* cx, const KindNames& names,
884
DefinitionKind kind) {
885
switch (kind) {
886
case DefinitionKind::Function:
887
return cx->names().function;
888
case DefinitionKind::Table:
889
return names.table;
890
case DefinitionKind::Memory:
891
return names.memory;
892
case DefinitionKind::Global:
893
return cx->names().global;
894
}
895
896
MOZ_CRASH("invalid kind");
897
}
898
899
static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) {
900
JSStringBuilder buf(cx);
901
if (!buf.append('(')) {
902
return nullptr;
903
}
904
905
bool first = true;
906
for (ValType arg : funcType.args()) {
907
if (!first && !buf.append(", ", strlen(", "))) {
908
return nullptr;
909
}
910
911
const char* argStr = ToCString(arg);
912
if (!buf.append(argStr, strlen(argStr))) {
913
return nullptr;
914
}
915
916
first = false;
917
}
918
919
if (!buf.append(") -> (", strlen(") -> ("))) {
920
return nullptr;
921
}
922
923
first = true;
924
for (ValType result : funcType.results()) {
925
if (!first && !buf.append(", ", strlen(", "))) {
926
return nullptr;
927
}
928
929
const char* resultStr = ToCString(result);
930
if (!buf.append(resultStr, strlen(resultStr))) {
931
return nullptr;
932
}
933
934
first = false;
935
}
936
937
if (!buf.append(')')) {
938
return nullptr;
939
}
940
941
return buf.finishString();
942
}
943
944
static JSString* UTF8CharsToString(JSContext* cx, const char* chars) {
945
return NewStringCopyUTF8Z<CanGC>(cx,
946
JS::ConstUTF8CharsZ(chars, strlen(chars)));
947
}
948
949
/* static */
950
bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
951
CallArgs args = CallArgsFromVp(argc, vp);
952
953
const Module* module;
954
if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) {
955
return false;
956
}
957
958
KindNames names(cx);
959
if (!InitKindNames(cx, &names)) {
960
return false;
961
}
962
963
RootedValueVector elems(cx);
964
if (!elems.reserve(module->imports().length())) {
965
return false;
966
}
967
968
const FuncImportVector& funcImports =
969
module->metadata(module->code().stableTier()).funcImports;
970
971
size_t numFuncImport = 0;
972
for (const Import& import : module->imports()) {
973
Rooted<IdValueVector> props(cx, IdValueVector(cx));
974
if (!props.reserve(3)) {
975
return false;
976
}
977
978
JSString* moduleStr = UTF8CharsToString(cx, import.module.get());
979
if (!moduleStr) {
980
return false;
981
}
982
props.infallibleAppend(
983
IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
984
985
JSString* nameStr = UTF8CharsToString(cx, import.field.get());
986
if (!nameStr) {
987
return false;
988
}
989
props.infallibleAppend(
990
IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
991
992
JSString* kindStr = KindToString(cx, names, import.kind);
993
if (!kindStr) {
994
return false;
995
}
996
props.infallibleAppend(
997
IdValuePair(NameToId(names.kind), StringValue(kindStr)));
998
999
if (fuzzingSafe && import.kind == DefinitionKind::Function) {
1000
JSString* ftStr =
1001
FuncTypeToString(cx, funcImports[numFuncImport++].funcType());
1002
if (!ftStr) {
1003
return false;
1004
}
1005
if (!props.append(
1006
IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1007
return false;
1008
}
1009
}
1010
1011
JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(),
1012
props.length(), GenericObject);
1013
if (!obj) {
1014
return false;
1015
}
1016
1017
elems.infallibleAppend(ObjectValue(*obj));
1018
}
1019
1020
JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1021
if (!arr) {
1022
return false;
1023
}
1024
1025
args.rval().setObject(*arr);
1026
return true;
1027
}
1028
1029
/* static */
1030
bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
1031
CallArgs args = CallArgsFromVp(argc, vp);
1032
1033
const Module* module;
1034
if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) {
1035
return false;
1036
}
1037
1038
KindNames names(cx);
1039
if (!InitKindNames(cx, &names)) {
1040
return false;
1041
}
1042
1043
RootedValueVector elems(cx);
1044
if (!elems.reserve(module->exports().length())) {
1045
return false;
1046
}
1047
1048
const FuncExportVector& funcExports =
1049
module->metadata(module->code().stableTier()).funcExports;
1050
1051
size_t numFuncExport = 0;
1052
for (const Export& exp : module->exports()) {
1053
Rooted<IdValueVector> props(cx, IdValueVector(cx));
1054
if (!props.reserve(2)) {
1055
return false;
1056
}
1057
1058
JSString* nameStr = UTF8CharsToString(cx, exp.fieldName());
1059
if (!nameStr) {
1060
return false;
1061
}
1062
props.infallibleAppend(
1063
IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1064
1065
JSString* kindStr = KindToString(cx, names, exp.kind());
1066
if (!kindStr) {
1067
return false;
1068
}
1069
props.infallibleAppend(
1070
IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1071
1072
if (fuzzingSafe && exp.kind() == DefinitionKind::Function) {
1073
JSString* ftStr =
1074
FuncTypeToString(cx, funcExports[numFuncExport++].funcType());
1075
if (!ftStr) {
1076
return false;
1077
}
1078
if (!props.append(
1079
IdValuePair(NameToId(names.signature), StringValue(ftStr)))) {
1080
return false;
1081
}
1082
}
1083
1084
JSObject* obj = ObjectGroup::newPlainObject(cx, props.begin(),
1085
props.length(), GenericObject);
1086
if (!obj) {
1087
return false;
1088
}
1089
1090
elems.infallibleAppend(ObjectValue(*obj));
1091
}
1092
1093
JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1094
if (!arr) {
1095
return false;
1096
}
1097
1098
args.rval().setObject(*arr);
1099
return true;
1100
}
1101
1102
/* static */
1103
bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) {
1104
CallArgs args = CallArgsFromVp(argc, vp);
1105
1106
const Module* module;
1107
if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections",
1108
&module)) {
1109
return false;
1110
}
1111
1112
Vector<char, 8> name(cx);
1113
{
1114
RootedString str(cx, ToString(cx, args.get(1)));
1115
if (!str) {
1116
return false;
1117
}
1118
1119
Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
1120
if (!linear) {
1121
return false;
1122
}
1123
1124
if (!name.initLengthUninitialized(
1125
JS::GetDeflatedUTF8StringLength(linear))) {
1126
return false;
1127
}
1128
1129
mozilla::Unused << JS::DeflateStringToUTF8Buffer(
1130
linear, MakeSpan(name.begin(), name.length()));
1131
}
1132
1133
RootedValueVector elems(cx);
1134
RootedArrayBufferObject buf(cx);
1135
for (const CustomSection& cs : module->customSections()) {
1136
if (name.length() != cs.name.length()) {
1137
continue;
1138
}
1139
if (memcmp(name.begin(), cs.name.begin(), name.length())) {
1140
continue;
1141
}
1142
1143
buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
1144
if (!buf) {
1145
return false;
1146
}
1147
1148
memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
1149
if (!elems.append(ObjectValue(*buf))) {
1150
return false;
1151
}
1152
}
1153
1154
JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1155
if (!arr) {
1156
return false;
1157
}
1158
1159
args.rval().setObject(*arr);
1160
return true;
1161
}
1162
1163
/* static */
1164
WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module,
1165
HandleObject proto) {
1166
AutoSetNewObjectMetadata metadata(cx);
1167
auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
1168
if (!obj) {
1169
return nullptr;
1170
}
1171
1172
// This accounts for module allocation size (excluding code which is handled
1173
// separately - see below). This assumes that the size of associated data
1174
// doesn't change for the life of the WasmModuleObject. The size is counted
1175
// once per WasmModuleObject referencing a Module.
1176
InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module),
1177
module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule);
1178
module.AddRef();
1179
1180
// Bug 1569888: We account for the first tier here; the second tier, if
1181
// different, also needs to be accounted for.
1182
cx->zone()->incJitMemory(module.codeLength(module.code().stableTier()));
1183
return obj;
1184
}
1185
1186
static bool GetBufferSource(JSContext* cx, JSObject* obj, unsigned errorNumber,
1187
MutableBytes* bytecode) {
1188
*bytecode = cx->new_<ShareableBytes>();
1189
if (!*bytecode) {
1190
return false;
1191
}
1192
1193
JSObject* unwrapped = CheckedUnwrapStatic(obj);
1194
1195
SharedMem<uint8_t*> dataPointer;
1196
size_t byteLength;
1197
if (!unwrapped || !IsBufferSource(unwrapped, &dataPointer, &byteLength)) {
1198
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
1199
return false;
1200
}
1201
1202
if (!(*bytecode)->append(dataPointer.unwrap(), byteLength)) {
1203
ReportOutOfMemory(cx);
1204
return false;
1205
}
1206
1207
return true;
1208
}
1209
1210
static SharedCompileArgs InitCompileArgs(JSContext* cx,
1211
const char* introducer) {
1212
ScriptedCaller scriptedCaller;
1213
if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
1214
return nullptr;
1215
}
1216
1217
return CompileArgs::build(cx, std::move(scriptedCaller));
1218
}
1219
1220
static bool ReportCompileWarnings(JSContext* cx,
1221
const UniqueCharsVector& warnings) {
1222
// Avoid spamming the console.
1223
size_t numWarnings = std::min<size_t>(warnings.length(), 3);
1224
1225
for (size_t i = 0; i < numWarnings; i++) {
1226
if (!JS_ReportErrorFlagsAndNumberASCII(
1227
cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
1228
JSMSG_WASM_COMPILE_WARNING, warnings[i].get()))
1229
return false;
1230
}
1231
1232
if (warnings.length() > numWarnings) {
1233
if (!JS_ReportErrorFlagsAndNumberASCII(
1234
cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
1235
JSMSG_WASM_COMPILE_WARNING, "other warnings suppressed"))
1236
return false;
1237
}
1238
1239
return true;
1240
}
1241
1242
/* static */
1243
bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1244
CallArgs callArgs = CallArgsFromVp(argc, vp);
1245
1246
Log(cx, "sync new Module() started");
1247
1248
if (!ThrowIfNotConstructing(cx, callArgs, "Module")) {
1249
return false;
1250
}
1251
1252
if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) {
1253
return false;
1254
}
1255
1256
if (!callArgs[0].isObject()) {
1257
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1258
JSMSG_WASM_BAD_BUF_ARG);
1259
return false;
1260
}
1261
1262
MutableBytes bytecode;
1263
if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
1264
&bytecode)) {
1265
return false;
1266
}
1267
1268
SharedCompileArgs compileArgs = InitCompileArgs(cx, "WebAssembly.Module");
1269
if (!compileArgs) {
1270
return false;
1271
}
1272
1273
UniqueChars error;
1274
UniqueCharsVector warnings;
1275
SharedModule module =
1276
CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
1277
if (!module) {
1278
if (error) {
1279
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1280
JSMSG_WASM_COMPILE_ERROR, error.get());
1281
return false;
1282
}
1283
ReportOutOfMemory(cx);
1284
return false;
1285
}
1286
1287
if (!ReportCompileWarnings(cx, warnings)) {
1288
return false;
1289
}
1290
1291
RootedObject proto(
1292
cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
1293
RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
1294
if (!moduleObj) {
1295
return false;
1296
}
1297
1298
Log(cx, "sync new Module() succeded");
1299
1300
callArgs.rval().setObject(*moduleObj);
1301
return true;
1302
}
1303
1304
const Module& WasmModuleObject::module() const {
1305
MOZ_ASSERT(is<WasmModuleObject>());
1306
return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
1307
}
1308
1309
// ============================================================================
1310
// WebAssembly.Instance class and methods
1311
1312
const JSClassOps WasmInstanceObject::classOps_ = {nullptr, /* addProperty */
1313
nullptr, /* delProperty */
1314
nullptr, /* enumerate */
1315
nullptr, /* newEnumerate */
1316
nullptr, /* resolve */
1317
nullptr, /* mayResolve */
1318
WasmInstanceObject::finalize,
1319
nullptr, /* call */
1320
nullptr, /* hasInstance */
1321
nullptr, /* construct */
1322
WasmInstanceObject::trace};
1323
1324
const JSClass WasmInstanceObject::class_ = {
1325
"WebAssembly.Instance",
1326
JSCLASS_DELAY_METADATA_BUILDER |
1327
JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
1328
JSCLASS_FOREGROUND_FINALIZE,
1329
&WasmInstanceObject::classOps_,
1330
&WasmInstanceObject::classSpec_,
1331
};
1332
1333
const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
1334
1335
static constexpr char WasmInstanceName[] = "Instance";
1336
1337
const ClassSpec WasmInstanceObject::classSpec_ = {
1338
CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
1339
GenericCreatePrototype<WasmInstanceObject>,
1340
WasmInstanceObject::static_methods,
1341
nullptr,
1342
WasmInstanceObject::methods,
1343
WasmInstanceObject::properties,
1344
nullptr,
1345
ClassSpec::DontDefineConstructor};
1346
1347
static bool IsInstance(HandleValue v) {
1348
return v.isObject() && v.toObject().is<WasmInstanceObject>();
1349
}
1350
1351
/* static */
1352
bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
1353
const CallArgs& args) {
1354
args.rval().setObject(
1355
args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
1356
return true;
1357
}
1358
1359
/* static */
1360
bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
1361
Value* vp) {
1362
CallArgs args = CallArgsFromVp(argc, vp);
1363
return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
1364
}
1365
1366
const JSPropertySpec WasmInstanceObject::properties[] = {
1367
JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
1368
JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
1369
JS_PS_END};
1370
1371
const JSFunctionSpec WasmInstanceObject::methods[] = {JS_FS_END};
1372
1373
const JSFunctionSpec WasmInstanceObject::static_methods[] = {JS_FS_END};
1374
1375
bool WasmInstanceObject::isNewborn() const {
1376
MOZ_ASSERT(is<WasmInstanceObject>());
1377
return getReservedSlot(INSTANCE_SLOT).isUndefined();
1378
}
1379
1380
/* static */
1381
void WasmInstanceObject::finalize(JSFreeOp* fop, JSObject* obj) {
1382
WasmInstanceObject& instance = obj->as<WasmInstanceObject>();
1383
fop->delete_(obj, &instance.exports(), MemoryUse::WasmInstanceExports);
1384
fop->delete_(obj, &instance.scopes(), MemoryUse::WasmInstanceScopes);
1385
fop->delete_(obj, &instance.indirectGlobals(),
1386
MemoryUse::WasmInstanceGlobals);
1387
if (!instance.isNewborn()) {
1388
if (instance.instance().debugEnabled()) {
1389
instance.instance().debug().finalize(fop);
1390
}
1391
fop->delete_(obj, &instance.instance(), MemoryUse::WasmInstanceInstance);
1392
}
1393
}
1394
1395
/* static */
1396
void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) {
1397
WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
1398
instanceObj.exports().trace(trc);
1399
instanceObj.indirectGlobals().trace(trc);
1400
if (!instanceObj.isNewborn()) {
1401
instanceObj.instance().tracePrivate(trc);
1402
}
1403
}
1404
1405
/* static */
1406
WasmInstanceObject* WasmInstanceObject::create(
1407
JSContext* cx, SharedCode code, const DataSegmentVector& dataSegments,
1408
const ElemSegmentVector& elemSegments, UniqueTlsData tlsData,
1409
HandleWasmMemoryObject memory, SharedTableVector&& tables,
1410
StructTypeDescrVector&& structTypeDescrs,
1411
const JSFunctionVector& funcImports, const GlobalDescVector& globals,
1412
const ValVector& globalImportValues,
1413
const WasmGlobalObjectVector& globalObjs, HandleObject proto,
1414
UniqueDebugState maybeDebug) {
1415
UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>(cx->zone());
1416
if (!exports) {
1417
ReportOutOfMemory(cx);
1418
return nullptr;
1419
}
1420
1421
UniquePtr<ScopeMap> scopes = js::MakeUnique<ScopeMap>(cx->zone(), cx->zone());
1422
if (!scopes) {
1423
ReportOutOfMemory(cx);
1424
return nullptr;
1425
}
1426
1427
uint32_t indirectGlobals = 0;
1428
1429
for (uint32_t i = 0; i < globalObjs.length(); i++) {
1430
if (globalObjs[i] && globals[i].isIndirect()) {
1431
indirectGlobals++;
1432
}
1433
}
1434
1435
Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs(
1436
cx, js::MakeUnique<GlobalObjectVector>(cx->zone()));
1437
if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
1438
ReportOutOfMemory(cx);
1439
return nullptr;
1440
}
1441
1442
{
1443
uint32_t next = 0;
1444
for (uint32_t i = 0; i < globalObjs.length(); i++) {
1445
if (globalObjs[i] && globals[i].isIndirect()) {
1446
(*indirectGlobalObjs)[next++] = globalObjs[i];
1447
}
1448
}
1449
}
1450
1451
AutoSetNewObjectMetadata metadata(cx);
1452
RootedWasmInstanceObject obj(
1453
cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
1454
if (!obj) {
1455
return nullptr;
1456
}
1457
1458
MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
1459
1460
// Finalization assumes these slots are always initialized:
1461
InitReservedSlot(obj, EXPORTS_SLOT, exports.release(),
1462
MemoryUse::WasmInstanceExports);
1463
1464
InitReservedSlot(obj, SCOPES_SLOT, scopes.release(),
1465
MemoryUse::WasmInstanceScopes);
1466
1467
InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(),
1468
MemoryUse::WasmInstanceGlobals);
1469
1470
obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
1471
1472
// The INSTANCE_SLOT may not be initialized if Instance allocation fails,
1473
// leading to an observable "newborn" state in tracing/finalization.
1474
MOZ_ASSERT(obj->isNewborn());
1475
1476
// Root the Instance via WasmInstanceObject before any possible GC.
1477
auto* instance = cx->new_<Instance>(
1478
cx, obj, code, std::move(tlsData), memory, std::move(tables),
1479
std::move(structTypeDescrs), funcImports, globalImportValues, globalObjs,
1480
std::move(maybeDebug));
1481
if (!instance) {
1482
return nullptr;
1483
}
1484
1485
InitReservedSlot(obj, INSTANCE_SLOT, instance,
1486
MemoryUse::WasmInstanceInstance);
1487
MOZ_ASSERT(!obj->isNewborn());
1488
1489
if (!instance->init(cx, dataSegments, elemSegments)) {
1490
return nullptr;
1491
}
1492
1493
return obj;
1494
}
1495
1496
void WasmInstanceObject::initExportsObj(JSObject& exportsObj) {
1497
MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
1498
setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
1499
}
1500
1501
static bool GetImportArg(JSContext* cx, CallArgs callArgs,
1502
MutableHandleObject importObj) {
1503
if (!callArgs.get(1).isUndefined()) {
1504
if (!callArgs[1].isObject()) {
1505
return ThrowBadImportArg(cx);
1506
}
1507
importObj.set(&callArgs[1].toObject());
1508
}
1509
return true;
1510
}
1511
1512
/* static */
1513
bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1514
CallArgs args = CallArgsFromVp(argc, vp);
1515
1516
Log(cx, "sync new Instance() started");
1517
1518
if (!ThrowIfNotConstructing(cx, args, "Instance")) {
1519
return false;
1520
}
1521
1522
if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
1523
return false;
1524
}
1525
1526
const Module* module;
1527
if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
1528
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1529
JSMSG_WASM_BAD_MOD_ARG);
1530
return false;
1531
}
1532
1533
RootedObject importObj(cx);
1534
if (!GetImportArg(cx, args, &importObj)) {
1535
return false;
1536
}
1537
1538
RootedObject instanceProto(
1539
cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
1540
1541
Rooted<ImportValues> imports(cx);
1542
if (!GetImports(cx, *module, importObj, imports.address())) {
1543
return false;
1544
}
1545
1546
RootedWasmInstanceObject instanceObj(cx);
1547
if (!module->instantiate(cx, imports.get(), instanceProto, &instanceObj)) {
1548
return false;
1549
}
1550
1551
Log(cx, "sync new Instance() succeeded");
1552
1553
args.rval().setObject(*instanceObj);
1554
return true;
1555
}
1556
1557
Instance& WasmInstanceObject::instance() const {
1558
MOZ_ASSERT(!isNewborn());
1559
return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
1560
}
1561
1562
JSObject& WasmInstanceObject::exportsObj() const {
1563
return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
1564
}
1565
1566
WasmInstanceObject::ExportMap& WasmInstanceObject::exports() const {
1567
return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
1568
}
1569
1570
WasmInstanceObject::ScopeMap& WasmInstanceObject::scopes() const {
1571
return *(ScopeMap*)getReservedSlot(SCOPES_SLOT).toPrivate();
1572
}
1573
1574
WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals()
1575
const {
1576
return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate();
1577
}
1578
1579
static bool WasmCall(JSContext* cx, unsigned argc, Value* vp) {
1580
CallArgs args = CallArgsFromVp(argc, vp);
1581
RootedFunction callee(cx, &args.callee().as<JSFunction>());
1582
1583
Instance& instance = ExportedFunctionToInstance(callee);
1584
uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
1585
return instance.callExport(cx, funcIndex, args);
1586
}
1587
1588
/* static */
1589
bool WasmInstanceObject::getExportedFunction(
1590
JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex,
1591
MutableHandleFunction fun) {
1592
if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
1593
fun.set(p->value());
1594
return true;
1595
}
1596
1597
const Instance& instance = instanceObj->instance();
1598
const FuncExport& funcExport =
1599
instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
1600
unsigned numArgs = funcExport.funcType().args().length();
1601
1602
if (instance.isAsmJS()) {
1603
// asm.js needs to act like a normal JS function which means having the
1604
// name from the original source and being callable as a constructor.
1605
RootedAtom name(cx, instance.getFuncDisplayAtom(cx, funcIndex));
1606
if (!name) {
1607
return false;
1608
}
1609
fun.set(NewNativeConstructor(cx, WasmCall, numArgs, name,
1610
gc::AllocKind::FUNCTION_EXTENDED,
1611
SingletonObject, FunctionFlags::ASMJS_CTOR));
1612
if (!fun) {
1613
return false;
1614
}
1615
1616
// asm.js does not support jit entries.
1617
fun->setWasmFuncIndex(funcIndex);
1618
} else {
1619
RootedAtom name(cx, NumberToAtom(cx, funcIndex));
1620
if (!name) {
1621
return false;
1622
}
1623
1624
fun.set(NewNativeFunction(cx, WasmCall, numArgs, name,
1625
gc::AllocKind::FUNCTION_EXTENDED, SingletonObject,
1626
FunctionFlags::WASM));
1627
if (!fun) {
1628
return false;
1629
}
1630
1631
// Some applications eagerly access all table elements which currently
1632
// triggers worst-case behavior for lazy stubs, since each will allocate a
1633
// separate 4kb code page. Most eagerly-accessed functions are not called,
1634
// so instead wait until Instance::callExport() to create the entry stubs.
1635
if (funcExport.hasEagerStubs() && funcExport.canHaveJitEntry()) {
1636
fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex));
1637
} else {
1638
fun->setWasmFuncIndex(funcIndex);
1639
}
1640
}
1641
1642
fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT,
1643
ObjectValue(*instanceObj));
1644
1645
void* tlsData = instanceObj->instance().tlsData();
1646
fun->setExtendedSlot(FunctionExtended::WASM_TLSDATA_SLOT,
1647
PrivateValue(tlsData));
1648
1649
if (!instanceObj->exports().putNew(funcIndex, fun)) {
1650
ReportOutOfMemory(cx);
1651
return false;
1652
}
1653
1654
return true;
1655
}
1656
1657
const CodeRange& WasmInstanceObject::getExportedFunctionCodeRange(
1658
JSFunction* fun, Tier tier) {
1659
uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
1660
MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
1661
const MetadataTier& metadata = instance().metadata(tier);
1662
return metadata.codeRange(metadata.lookupFuncExport(funcIndex));
1663
}
1664
1665
/* static */
1666
WasmInstanceScope* WasmInstanceObject::getScope(
1667
JSContext* cx, HandleWasmInstanceObject instanceObj) {
1668
if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) {
1669
return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT)
1670
.toGCThing();
1671
}
1672
1673
Rooted<WasmInstanceScope*> instanceScope(
1674
cx, WasmInstanceScope::create(cx, instanceObj));
1675
if (!instanceScope) {
1676
return nullptr;
1677
}
1678
1679
instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT,
1680
PrivateGCThingValue(instanceScope));
1681
1682
return instanceScope;
1683
}
1684
1685
/* static */
1686
WasmFunctionScope* WasmInstanceObject::getFunctionScope(
1687
JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex) {
1688
if (ScopeMap::Ptr p = instanceObj->scopes().lookup(funcIndex)) {
1689
return p->value();
1690
}
1691
1692
Rooted<WasmInstanceScope*> instanceScope(
1693
cx, WasmInstanceObject::getScope(cx, instanceObj));
1694
if (!instanceScope) {
1695
return nullptr;
1696
}
1697
1698
Rooted<WasmFunctionScope*> funcScope(
1699
cx, WasmFunctionScope::create(cx, instanceScope, funcIndex));
1700
if (!funcScope) {
1701
return nullptr;
1702
}
1703
1704
if (!instanceObj->scopes().putNew(funcIndex, funcScope)) {
1705
ReportOutOfMemory(cx);
1706
return nullptr;
1707
}
1708
1709
return funcScope;
1710
}
1711
1712
bool wasm::IsWasmExportedFunction(JSFunction* fun) {
1713
return fun->kind() == FunctionFlags::Wasm;
1714
}
1715
1716
bool wasm::CheckFuncRefValue(JSContext* cx, HandleValue v,
1717
MutableHandleFunction fun) {
1718
if (v.isNull()) {
1719
MOZ_ASSERT(!fun);
1720
return true;
1721
}
1722
1723
if (v.isObject()) {
1724
JSObject& obj = v.toObject();
1725
if (obj.is<JSFunction>()) {
1726
JSFunction* f = &obj.as<JSFunction>();
1727
if (IsWasmExportedFunction(f)) {
1728
fun.set(f);
1729
return true;
1730
}
1731
}
1732
}
1733
1734
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1735
JSMSG_WASM_BAD_FUNCREF_VALUE);
1736
return false;
1737
}
17