1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 *
4 * Copyright 2016 Mozilla Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #ifndef wasm_js_h
20 #define wasm_js_h
21
22 #include "mozilla/HashTable.h" // DefaultHasher
23 #include "mozilla/Maybe.h" // mozilla::Maybe
24
25 #include <stdint.h> // int32_t, int64_t, uint32_t
26
27 #include "gc/Barrier.h" // HeapPtr
28 #include "gc/ZoneAllocator.h" // ZoneAllocPolicy
29 #include "js/AllocPolicy.h" // SystemAllocPolicy
30 #include "js/Class.h" // JSClassOps, ClassSpec
31 #include "js/GCHashTable.h" // GCHashMap, GCHashSet
32 #include "js/GCVector.h" // GCVector
33 #include "js/PropertySpec.h" // JSPropertySpec, JSFunctionSpec
34 #include "js/RootingAPI.h" // MovableCellHasher
35 #include "js/SweepingAPI.h" // JS::WeakCache
36 #include "js/TypeDecls.h" // HandleValue, HandleObject, MutableHandleObject, MutableHandleFunction
37 #include "js/Vector.h" // JS::Vector
38 #include "js/WasmFeatures.h"
39 #include "vm/JSFunction.h" // JSFunction
40 #include "vm/NativeObject.h" // NativeObject
41 #include "wasm/WasmTlsData.h" // UniqueTlsData
42 #include "wasm/WasmTypes.h" // MutableHandleWasmInstanceObject, wasm::*
43
44 class JSFreeOp;
45 class JSObject;
46 class JSTracer;
47 struct JSContext;
48
49 namespace JS {
50 class CallArgs;
51 class Value;
52 } // namespace JS
53
54 namespace js {
55
56 class ArrayBufferObject;
57 class ArrayBufferObjectMaybeShared;
58 class JSStringBuilder;
59 class SharedArrayRawBuffer;
60 class TypedArrayObject;
61 class WasmFunctionScope;
62 class WasmInstanceScope;
63
64 namespace wasm {
65
66 struct ImportValues;
67
68 // Return whether WebAssembly can in principle be compiled on this platform (ie
69 // combination of hardware and OS), assuming at least one of the compilers that
70 // supports the platform is not disabled by other settings.
71 //
72 // This predicate must be checked and must be true to call any of the top-level
73 // wasm eval/compile methods.
74
75 bool HasPlatformSupport(JSContext* cx);
76
77 // Return whether WebAssembly is supported on this platform. This determines
78 // whether the WebAssembly object is exposed to JS in this context / realm and
79 //
80 // It does *not* guarantee that a compiler is actually available; that has to be
81 // checked separately, as it is sometimes run-time variant, depending on whether
82 // a debugger has been created or not.
83
84 bool HasSupport(JSContext* cx);
85
86 // Predicates for compiler availability.
87 //
88 // These three predicates together select zero or one baseline compiler and zero
89 // or one optimizing compiler, based on: what's compiled into the executable,
90 // what's supported on the current platform, what's selected by options, and the
91 // current run-time environment. As it is possible for the computed values to
92 // change (when a value changes in about:config or the debugger pane is shown or
93 // hidden), it is inadvisable to cache these values in such a way that they
94 // could become invalid. Generally it is cheap always to recompute them.
95
96 bool BaselineAvailable(JSContext* cx);
97 bool IonAvailable(JSContext* cx);
98 bool CraneliftAvailable(JSContext* cx);
99
100 // Test all three.
101
102 bool AnyCompilerAvailable(JSContext* cx);
103
104 // Asm.JS is translated to wasm and then compiled using the wasm optimizing
105 // compiler; test whether this compiler is available.
106
107 bool WasmCompilerForAsmJSAvailable(JSContext* cx);
108
109 // Predicates for white-box compiler disablement testing.
110 //
111 // These predicates determine whether the optimizing compilers were disabled by
112 // features that are enabled at compile-time or run-time. They do not consider
113 // the hardware platform on whether other compilers are enabled.
114 //
115 // If `reason` is not null then it is populated with a string that describes
116 // the specific features that disable the compiler.
117 //
118 // Returns false on OOM (which happens only when a reason is requested),
119 // otherwise true, with the result in `*isDisabled` and optionally the reason in
120 // `*reason`.
121
122 bool IonDisabledByFeatures(JSContext* cx, bool* isDisabled,
123 JSStringBuilder* reason = nullptr);
124 bool CraneliftDisabledByFeatures(JSContext* cx, bool* isDisabled,
125 JSStringBuilder* reason = nullptr);
126
127 // Predicates for feature availability.
128 //
129 // The following predicates check whether particular wasm features are enabled,
130 // and for each, whether at least one compiler is (currently) available that
131 // supports the feature.
132
133 // Streaming compilation.
134 bool StreamingCompilationAvailable(JSContext* cx);
135
136 // Caching of optimized code. Implies both streaming compilation and an
137 // optimizing compiler tier.
138 bool CodeCachingAvailable(JSContext* cx);
139
140 // Shared memory and atomics.
141 bool ThreadsAvailable(JSContext* cx);
142
143 #define WASM_FEATURE(NAME, ...) bool NAME##Available(JSContext* cx);
144 JS_FOR_WASM_FEATURES(WASM_FEATURE, WASM_FEATURE)
145 #undef WASM_FEATURE
146
147 // Very experimental SIMD operations.
148 bool SimdWormholeAvailable(JSContext* cx);
149
150 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
151 // Report the result of a Simd simplification to the testing infrastructure.
152 void ReportSimdAnalysis(const char* data);
153 #endif
154
155 // Returns true if WebAssembly as configured by compile-time flags and run-time
156 // options can support try/catch, throw, rethrow, and branch_on_exn (evolving).
157 bool ExceptionsAvailable(JSContext* cx);
158
159 Pages MaxMemory32Pages();
160 size_t MaxMemory32BoundsCheckLimit();
161
MaxMemory32Bytes()162 static inline size_t MaxMemory32Bytes() {
163 return MaxMemory32Pages().byteLength();
164 }
165
166 // Compiles the given binary wasm module given the ArrayBufferObject
167 // and links the module's imports with the given import object.
168
169 [[nodiscard]] bool Eval(JSContext* cx, Handle<TypedArrayObject*> code,
170 HandleObject importObj, HandleValue maybeOptions,
171 MutableHandleWasmInstanceObject instanceObj);
172
173 // Extracts the various imports from the given import object into the given
174 // ImportValues structure while checking the imports against the given module.
175 // The resulting structure can be passed to WasmModule::instantiate.
176
177 struct ImportValues;
178 [[nodiscard]] bool GetImports(JSContext* cx, const Module& module,
179 HandleObject importObj, ImportValues* imports);
180
181 // For testing cross-process (de)serialization, this pair of functions are
182 // responsible for, in the child process, compiling the given wasm bytecode
183 // to a wasm::Module that is serialized into the given byte array, and, in
184 // the parent process, deserializing the given byte array into a
185 // WebAssembly.Module object.
186
187 [[nodiscard]] bool CompileAndSerialize(const ShareableBytes& bytecode,
188 Bytes* serialized);
189
190 [[nodiscard]] bool DeserializeModule(JSContext* cx, const Bytes& serialized,
191 MutableHandleObject module);
192
193 // A WebAssembly "Exported Function" is the spec name for the JS function
194 // objects created to wrap wasm functions. This predicate returns false
195 // for asm.js functions which are semantically just normal JS functions
196 // (even if they are implemented via wasm under the hood). The accessor
197 // functions for extracting the instance and func-index of a wasm function
198 // can be used for both wasm and asm.js, however.
199
200 bool IsWasmExportedFunction(JSFunction* fun);
201
202 Instance& ExportedFunctionToInstance(JSFunction* fun);
203 WasmInstanceObject* ExportedFunctionToInstanceObject(JSFunction* fun);
204 uint32_t ExportedFunctionToFuncIndex(JSFunction* fun);
205
206 bool IsSharedWasmMemoryObject(JSObject* obj);
207
208 } // namespace wasm
209
210 // The class of WebAssembly.Module. Each WasmModuleObject owns a
211 // wasm::Module. These objects are used both as content-facing JS objects and as
212 // internal implementation details of asm.js.
213
214 class WasmModuleObject : public NativeObject {
215 static const unsigned MODULE_SLOT = 0;
216 static const JSClassOps classOps_;
217 static const ClassSpec classSpec_;
218 static void finalize(JSFreeOp* fop, JSObject* obj);
219 static bool imports(JSContext* cx, unsigned argc, Value* vp);
220 static bool exports(JSContext* cx, unsigned argc, Value* vp);
221 static bool customSections(JSContext* cx, unsigned argc, Value* vp);
222
223 public:
224 static const unsigned RESERVED_SLOTS = 1;
225 static const JSClass class_;
226 static const JSClass& protoClass_;
227 static const JSPropertySpec properties[];
228 static const JSFunctionSpec methods[];
229 static const JSFunctionSpec static_methods[];
230 static bool construct(JSContext*, unsigned, Value*);
231
232 static WasmModuleObject* create(JSContext* cx, const wasm::Module& module,
233 HandleObject proto);
234 const wasm::Module& module() const;
235 };
236
237 // The class of WebAssembly.Global. This wraps a storage location, and there is
238 // a per-agent one-to-one relationship between the WasmGlobalObject and the
239 // storage location (the Cell) it wraps: if a module re-exports an imported
240 // global, the imported and exported WasmGlobalObjects are the same, and if a
241 // module exports a global twice, the two exported WasmGlobalObjects are the
242 // same.
243
244 // TODO/AnyRef-boxing: With boxed immediates and strings, JSObject* is no longer
245 // the most appropriate representation for Cell::anyref.
246 STATIC_ASSERT_ANYREF_IS_JSOBJECT;
247
248 class WasmGlobalObject : public NativeObject {
249 static const unsigned MUTABLE_SLOT = 0;
250 static const unsigned VAL_SLOT = 1;
251
252 static const JSClassOps classOps_;
253 static const ClassSpec classSpec_;
254 static void finalize(JSFreeOp*, JSObject* obj);
255 static void trace(JSTracer* trc, JSObject* obj);
256
257 static bool typeImpl(JSContext* cx, const CallArgs& args);
258 static bool type(JSContext* cx, unsigned argc, Value* vp);
259
260 static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
261 static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
262 static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
263 static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
264
265 public:
266 static const unsigned RESERVED_SLOTS = 2;
267 static const JSClass class_;
268 static const JSClass& protoClass_;
269 static const JSPropertySpec properties[];
270 static const JSFunctionSpec methods[];
271 static const JSFunctionSpec static_methods[];
272 static bool construct(JSContext*, unsigned, Value*);
273
274 static WasmGlobalObject* create(JSContext* cx, wasm::HandleVal value,
275 bool isMutable, HandleObject proto);
isNewborn()276 bool isNewborn() { return getReservedSlot(VAL_SLOT).isUndefined(); }
277
278 bool isMutable() const;
279 wasm::ValType type() const;
280 wasm::GCPtrVal& val() const;
281 };
282
283 // The class of WebAssembly.Instance. Each WasmInstanceObject owns a
284 // wasm::Instance. These objects are used both as content-facing JS objects and
285 // as internal implementation details of asm.js.
286
287 class WasmInstanceObject : public NativeObject {
288 static const unsigned INSTANCE_SLOT = 0;
289 static const unsigned EXPORTS_OBJ_SLOT = 1;
290 static const unsigned EXPORTS_SLOT = 2;
291 static const unsigned SCOPES_SLOT = 3;
292 static const unsigned INSTANCE_SCOPE_SLOT = 4;
293 static const unsigned GLOBALS_SLOT = 5;
294
295 static const JSClassOps classOps_;
296 static const ClassSpec classSpec_;
297 static bool exportsGetterImpl(JSContext* cx, const CallArgs& args);
298 static bool exportsGetter(JSContext* cx, unsigned argc, Value* vp);
299 bool isNewborn() const;
300 static void finalize(JSFreeOp* fop, JSObject* obj);
301 static void trace(JSTracer* trc, JSObject* obj);
302
303 // ExportMap maps from function index to exported function object.
304 // This allows the instance to lazily create exported function
305 // objects on demand (instead up-front for all table elements) while
306 // correctly preserving observable function object identity.
307 using ExportMap = GCHashMap<uint32_t, HeapPtr<JSFunction*>,
308 DefaultHasher<uint32_t>, ZoneAllocPolicy>;
309 ExportMap& exports() const;
310
311 // See the definition inside WasmJS.cpp.
312 class UnspecifiedScopeMap;
313 UnspecifiedScopeMap& scopes() const;
314
315 public:
316 static const unsigned RESERVED_SLOTS = 6;
317 static const JSClass class_;
318 static const JSClass& protoClass_;
319 static const JSPropertySpec properties[];
320 static const JSFunctionSpec methods[];
321 static const JSFunctionSpec static_methods[];
322 static bool construct(JSContext*, unsigned, Value*);
323
324 static WasmInstanceObject* create(
325 JSContext* cx, RefPtr<const wasm::Code> code,
326 const wasm::DataSegmentVector& dataSegments,
327 const wasm::ElemSegmentVector& elemSegments, wasm::UniqueTlsData tlsData,
328 HandleWasmMemoryObject memory,
329 Vector<RefPtr<wasm::ExceptionTag>, 0, SystemAllocPolicy>&& exceptionTags,
330 Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
331 const JSFunctionVector& funcImports,
332 const wasm::GlobalDescVector& globals,
333 const wasm::ValVector& globalImportValues,
334 const WasmGlobalObjectVector& globalObjs, HandleObject proto,
335 UniquePtr<wasm::DebugState> maybeDebug);
336 void initExportsObj(JSObject& exportsObj);
337
338 wasm::Instance& instance() const;
339 JSObject& exportsObj() const;
340
341 static bool getExportedFunction(JSContext* cx,
342 HandleWasmInstanceObject instanceObj,
343 uint32_t funcIndex,
344 MutableHandleFunction fun);
345
346 const wasm::CodeRange& getExportedFunctionCodeRange(JSFunction* fun,
347 wasm::Tier tier);
348
349 static WasmInstanceScope* getScope(JSContext* cx,
350 HandleWasmInstanceObject instanceObj);
351 static WasmFunctionScope* getFunctionScope(
352 JSContext* cx, HandleWasmInstanceObject instanceObj, uint32_t funcIndex);
353
354 using GlobalObjectVector =
355 GCVector<HeapPtr<WasmGlobalObject*>, 0, ZoneAllocPolicy>;
356 GlobalObjectVector& indirectGlobals() const;
357 };
358
359 // The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
360 // or SharedArrayBuffer object which owns the actual memory.
361
362 class WasmMemoryObject : public NativeObject {
363 static const unsigned BUFFER_SLOT = 0;
364 static const unsigned OBSERVERS_SLOT = 1;
365 static const JSClassOps classOps_;
366 static const ClassSpec classSpec_;
367 static void finalize(JSFreeOp* fop, JSObject* obj);
368 static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
369 static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
370 static bool typeImpl(JSContext* cx, const CallArgs& args);
371 static bool type(JSContext* cx, unsigned argc, Value* vp);
372 static bool growImpl(JSContext* cx, const CallArgs& args);
373 static bool grow(JSContext* cx, unsigned argc, Value* vp);
374 static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta);
375
376 using InstanceSet =
377 JS::WeakCache<GCHashSet<WeakHeapPtrWasmInstanceObject,
378 MovableCellHasher<WeakHeapPtrWasmInstanceObject>,
379 ZoneAllocPolicy>>;
380 bool hasObservers() const;
381 InstanceSet& observers() const;
382 InstanceSet* getOrCreateObservers(JSContext* cx);
383
384 public:
385 static const unsigned RESERVED_SLOTS = 2;
386 static const JSClass class_;
387 static const JSClass& protoClass_;
388 static const JSPropertySpec properties[];
389 static const JSFunctionSpec methods[];
390 static const JSFunctionSpec static_methods[];
391 static bool construct(JSContext*, unsigned, Value*);
392
393 static WasmMemoryObject* create(JSContext* cx,
394 Handle<ArrayBufferObjectMaybeShared*> buffer,
395 HandleObject proto);
396
397 // `buffer()` returns the current buffer object always. If the buffer
398 // represents shared memory then `buffer().byteLength()` never changes, and
399 // in particular it may be a smaller value than that returned from
400 // `volatileMemoryLength()` below.
401 //
402 // Generally, you do not want to call `buffer().byteLength()`, but to call
403 // `volatileMemoryLength()`, instead.
404 ArrayBufferObjectMaybeShared& buffer() const;
405
406 // The current length of the memory in bytes. In the case of shared memory,
407 // the length can change at any time. Also note that this will acquire a lock
408 // for shared memory, so do not call this from a signal handler.
409 size_t volatileMemoryLength() const;
410
411 // The current length of the memory in pages. See the comment for
412 // `volatileMemoryLength` for details on why this is 'volatile'.
413 wasm::Pages volatilePages() const;
414
415 // The maximum length of the memory in pages. This is not 'volatile' in
416 // contrast to the current length, as it cannot change for shared memories.
417 mozilla::Maybe<wasm::Pages> maxPages() const;
418
419 bool isShared() const;
420 bool isHuge() const;
421 bool movingGrowable() const;
422 size_t boundsCheckLimit() const;
423
424 // If isShared() is true then obtain the underlying buffer object.
425 SharedArrayRawBuffer* sharedArrayRawBuffer() const;
426
427 bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance);
428 static uint32_t grow(HandleWasmMemoryObject memory, uint32_t delta,
429 JSContext* cx);
430 };
431
432 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
433 // wasm::Table, allowing a Table to be shared between multiple Instances
434 // (eventually between multiple threads).
435
436 class WasmTableObject : public NativeObject {
437 static const unsigned TABLE_SLOT = 0;
438 static const JSClassOps classOps_;
439 static const ClassSpec classSpec_;
440 bool isNewborn() const;
441 static void finalize(JSFreeOp* fop, JSObject* obj);
442 static void trace(JSTracer* trc, JSObject* obj);
443 static bool lengthGetterImpl(JSContext* cx, const CallArgs& args);
444 static bool lengthGetter(JSContext* cx, unsigned argc, Value* vp);
445 static bool typeImpl(JSContext* cx, const CallArgs& args);
446 static bool type(JSContext* cx, unsigned argc, Value* vp);
447 static bool getImpl(JSContext* cx, const CallArgs& args);
448 static bool get(JSContext* cx, unsigned argc, Value* vp);
449 static bool setImpl(JSContext* cx, const CallArgs& args);
450 static bool set(JSContext* cx, unsigned argc, Value* vp);
451 static bool growImpl(JSContext* cx, const CallArgs& args);
452 static bool grow(JSContext* cx, unsigned argc, Value* vp);
453
454 public:
455 static const unsigned RESERVED_SLOTS = 1;
456 static const JSClass class_;
457 static const JSClass& protoClass_;
458 static const JSPropertySpec properties[];
459 static const JSFunctionSpec methods[];
460 static const JSFunctionSpec static_methods[];
461 static bool construct(JSContext*, unsigned, Value*);
462
463 // Note that, after creation, a WasmTableObject's table() is not initialized
464 // and must be initialized before use.
465
466 static WasmTableObject* create(JSContext* cx, uint32_t initialLength,
467 mozilla::Maybe<uint32_t> maximumLength,
468 wasm::RefType tableType, HandleObject proto);
469 wasm::Table& table() const;
470
471 // Perform the standard `ToWebAssemblyValue` coercion on `value` and fill the
472 // range [index, index + length) in the table. Callers are required to ensure
473 // the range is within bounds. Returns false if the coercion failed.
474 bool fillRange(JSContext* cx, uint32_t index, uint32_t length,
475 HandleValue value) const;
476 #ifdef DEBUG
477 void assertRangeNull(uint32_t index, uint32_t length) const;
478 #endif
479 };
480
481 // The class of WebAssembly.Exception. This class is used to track exception
482 // types for exports and imports.
483
484 class WasmExceptionObject : public NativeObject {
485 static const unsigned TAG_SLOT = 0;
486 static const unsigned TYPE_SLOT = 1;
487
488 static const JSClassOps classOps_;
489 static const ClassSpec classSpec_;
490 static void finalize(JSFreeOp*, JSObject* obj);
491 static void trace(JSTracer* trc, JSObject* obj);
492
493 public:
494 static const unsigned RESERVED_SLOTS = 2;
495 static const JSClass class_;
496 static const JSClass& protoClass_;
497 static const JSPropertySpec properties[];
498 static const JSFunctionSpec methods[];
499 static const JSFunctionSpec static_methods[];
500 static bool construct(JSContext*, unsigned, Value*);
501
502 static WasmExceptionObject* create(JSContext* cx,
503 const wasm::ValTypeVector& type,
504 HandleObject proto);
505 bool isNewborn() const;
506
507 wasm::ValTypeVector& valueTypes() const;
508 wasm::ResultType resultType() const;
509 wasm::ExceptionTag& tag() const;
510 };
511
512 // The class of WebAssembly.RuntimeException. This class is used for
513 // representing exceptions thrown from Wasm in JS. (it is also used as
514 // the internal representation for exceptions in Wasm)
515
516 class WasmRuntimeExceptionObject : public NativeObject {
517 static const unsigned TAG_SLOT = 0;
518 static const unsigned VALUES_SLOT = 1;
519 static const unsigned REFS_SLOT = 2;
520
521 static const JSClassOps classOps_;
522 static const ClassSpec classSpec_;
523 static void finalize(JSFreeOp*, JSObject* obj);
524 static void trace(JSTracer* trc, JSObject* obj);
525
526 public:
527 static const unsigned RESERVED_SLOTS = 3;
528 static const JSClass class_;
529 static const JSClass& protoClass_;
530 static const JSPropertySpec properties[];
531 static const JSFunctionSpec methods[];
532 static const JSFunctionSpec static_methods[];
533 static bool construct(JSContext*, unsigned, Value*);
534
535 static WasmRuntimeExceptionObject* create(JSContext* cx,
536 wasm::SharedExceptionTag tag,
537 Handle<ArrayBufferObject*> values,
538 HandleArrayObject refs);
539 bool isNewborn() const;
540
541 wasm::ExceptionTag& tag() const;
542 ArrayObject& refs() const;
543
offsetOfValues()544 static size_t offsetOfValues() {
545 return NativeObject::getFixedSlotOffset(VALUES_SLOT);
546 }
547
offsetOfRefs()548 static size_t offsetOfRefs() {
549 return NativeObject::getFixedSlotOffset(REFS_SLOT);
550 }
551 };
552
553 // The class of the WebAssembly global namespace object.
554
555 class WasmNamespaceObject : public NativeObject {
556 public:
557 static const JSClass class_;
558
559 private:
560 static const ClassSpec classSpec_;
561 };
562
563 } // namespace js
564
565 #endif // wasm_js_h
566