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