tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WasmJS.h (20234B)


      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"     // StableCellHasher
     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/WasmCodegenTypes.h"
     42 #include "wasm/WasmConstants.h"
     43 #include "wasm/WasmException.h"
     44 #include "wasm/WasmExprType.h"
     45 #include "wasm/WasmMemory.h"
     46 #include "wasm/WasmModuleTypes.h"
     47 #include "wasm/WasmTypeDecls.h"
     48 #include "wasm/WasmValType.h"
     49 #include "wasm/WasmValue.h"
     50 
     51 class JSObject;
     52 class JSTracer;
     53 struct JSContext;
     54 
     55 namespace JS {
     56 class CallArgs;
     57 class Value;
     58 }  // namespace JS
     59 
     60 namespace js {
     61 
     62 class ArrayBufferObject;
     63 class ArrayBufferObjectMaybeShared;
     64 class JSStringBuilder;
     65 class TypedArrayObject;
     66 class WasmFunctionScope;
     67 class WasmInstanceScope;
     68 class WasmSharedArrayRawBuffer;
     69 
     70 namespace wasm {
     71 
     72 struct ImportValues;
     73 
     74 // Compiles the given binary wasm module given the ArrayBufferObject
     75 // and links the module's imports with the given import object.
     76 
     77 [[nodiscard]] bool Eval(JSContext* cx, Handle<TypedArrayObject*> code,
     78                        HandleObject importObj,
     79                        MutableHandle<WasmInstanceObject*> instanceObj);
     80 
     81 // Extracts the various imports from the given import object into the given
     82 // ImportValues structure while checking the imports against the given module.
     83 // The resulting structure can be passed to WasmModule::instantiate.
     84 
     85 struct ImportValues;
     86 [[nodiscard]] bool GetImports(JSContext* cx, const Module& module,
     87                              HandleObject importObj, ImportValues* imports);
     88 
     89 // For testing cross-process (de)serialization, this pair of functions are
     90 // responsible for, in the child process, compiling the given wasm bytecode
     91 // to a wasm::Module that is serialized into the given byte array, and, in
     92 // the parent process, deserializing the given byte array into a
     93 // WebAssembly.Module object.
     94 
     95 [[nodiscard]] bool CompileAndSerialize(JSContext* cx,
     96                                       const BytecodeSource& source,
     97                                       Bytes* serialized);
     98 
     99 [[nodiscard]] bool DeserializeModule(JSContext* cx, const Bytes& serialized,
    100                                     MutableHandleObject module);
    101 
    102 bool IsSharedWasmMemoryObject(JSObject* obj);
    103 
    104 }  // namespace wasm
    105 
    106 // The class of WebAssembly.Module. Each WasmModuleObject owns a
    107 // wasm::Module. These objects are used both as content-facing JS objects and as
    108 // internal implementation details of asm.js.
    109 
    110 class WasmModuleObject : public NativeObject {
    111  static const unsigned MODULE_SLOT = 0;
    112  static const JSClassOps classOps_;
    113  static const ClassSpec classSpec_;
    114  static void finalize(JS::GCContext* gcx, JSObject* obj);
    115  static bool imports(JSContext* cx, unsigned argc, Value* vp);
    116  static bool exports(JSContext* cx, unsigned argc, Value* vp);
    117  static bool customSections(JSContext* cx, unsigned argc, Value* vp);
    118 
    119 public:
    120  static const unsigned RESERVED_SLOTS = 1;
    121  static const JSClass class_;
    122  static const JSClass& protoClass_;
    123  static const JSPropertySpec properties[];
    124  static const JSFunctionSpec methods[];
    125  static const JSFunctionSpec static_methods[];
    126  static bool construct(JSContext*, unsigned, Value*);
    127 
    128  static WasmModuleObject* create(JSContext* cx, const wasm::Module& module,
    129                                  HandleObject proto);
    130  const wasm::Module& module() const;
    131 };
    132 
    133 // The class of WebAssembly.Global.  This wraps a storage location, and there is
    134 // a per-agent one-to-one relationship between the WasmGlobalObject and the
    135 // storage location (the Cell) it wraps: if a module re-exports an imported
    136 // global, the imported and exported WasmGlobalObjects are the same, and if a
    137 // module exports a global twice, the two exported WasmGlobalObjects are the
    138 // same.
    139 
    140 class WasmGlobalObject : public NativeObject {
    141  static const unsigned MUTABLE_SLOT = 0;
    142  static const unsigned VAL_SLOT = 1;
    143 
    144  static const JSClassOps classOps_;
    145  static const ClassSpec classSpec_;
    146  static void finalize(JS::GCContext* gcx, JSObject* obj);
    147  static void trace(JSTracer* trc, JSObject* obj);
    148 
    149  static bool typeImpl(JSContext* cx, const CallArgs& args);
    150  static bool type(JSContext* cx, unsigned argc, Value* vp);
    151 
    152  static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
    153  static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
    154  static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
    155  static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
    156 
    157  wasm::GCPtrVal& mutableVal();
    158 
    159 public:
    160  static const unsigned RESERVED_SLOTS = 2;
    161  static const JSClass class_;
    162  static const JSClass& protoClass_;
    163  static const JSPropertySpec properties[];
    164  static const JSFunctionSpec methods[];
    165  static const JSFunctionSpec static_methods[];
    166  static bool construct(JSContext*, unsigned, Value*);
    167 
    168  static WasmGlobalObject* create(JSContext* cx, wasm::HandleVal value,
    169                                  bool isMutable, HandleObject proto);
    170  bool isNewborn() { return getReservedSlot(VAL_SLOT).isUndefined(); }
    171 
    172  bool isMutable() const;
    173  wasm::ValType type() const;
    174  const wasm::GCPtrVal& val() const;
    175  void setVal(wasm::HandleVal value);
    176  void* addressOfCell() const;
    177 };
    178 
    179 // The class of WebAssembly.Instance. Each WasmInstanceObject owns a
    180 // wasm::Instance. These objects are used both as content-facing JS objects and
    181 // as internal implementation details of asm.js.
    182 
    183 class WasmInstanceObject : public NativeObject {
    184  static const unsigned INSTANCE_SLOT = 0;
    185  static const unsigned EXPORTS_OBJ_SLOT = 1;
    186  static const unsigned SCOPES_SLOT = 2;
    187  static const unsigned INSTANCE_SCOPE_SLOT = 3;
    188  static const unsigned GLOBALS_SLOT = 4;
    189 
    190  static const JSClassOps classOps_;
    191  static const ClassSpec classSpec_;
    192  static bool exportsGetterImpl(JSContext* cx, const CallArgs& args);
    193  static bool exportsGetter(JSContext* cx, unsigned argc, Value* vp);
    194  bool isNewborn() const;
    195  static void finalize(JS::GCContext* gcx, JSObject* obj);
    196  static void trace(JSTracer* trc, JSObject* obj);
    197 
    198  // See the definition inside WasmJS.cpp.
    199  class UnspecifiedScopeMap;
    200  UnspecifiedScopeMap& scopes() const;
    201 
    202 public:
    203  static const unsigned RESERVED_SLOTS = 5;
    204  static const JSClass class_;
    205  static const JSClass& protoClass_;
    206  static const JSPropertySpec properties[];
    207  static const JSFunctionSpec methods[];
    208  static const JSFunctionSpec static_methods[];
    209  static bool construct(JSContext*, unsigned, Value*);
    210 
    211  static WasmInstanceObject* create(
    212      JSContext* cx, const RefPtr<const wasm::Code>& code,
    213      const wasm::DataSegmentVector& dataSegments,
    214      const wasm::ModuleElemSegmentVector& elemSegments,
    215      uint32_t instanceDataLength, Handle<WasmMemoryObjectVector> memories,
    216      Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
    217      const JSObjectVector& funcImports, const wasm::GlobalDescVector& globals,
    218      const wasm::ValVector& globalImportValues,
    219      const WasmGlobalObjectVector& globalObjs,
    220      const WasmTagObjectVector& tagObjs, HandleObject proto,
    221      UniquePtr<wasm::DebugState> maybeDebug);
    222  void initExportsObj(JSObject& exportsObj);
    223 
    224  wasm::Instance& instance() const;
    225  JSObject& exportsObj() const;
    226 
    227  [[nodiscard]] static bool getExportedFunction(
    228      JSContext* cx, Handle<WasmInstanceObject*> instanceObj,
    229      uint32_t funcIndex, MutableHandleFunction fun);
    230 
    231  static WasmInstanceScope* getScope(JSContext* cx,
    232                                     Handle<WasmInstanceObject*> instanceObj);
    233  static WasmFunctionScope* getFunctionScope(
    234      JSContext* cx, Handle<WasmInstanceObject*> instanceObj,
    235      uint32_t funcIndex);
    236 
    237  using GlobalObjectVector =
    238      GCVector<HeapPtr<WasmGlobalObject*>, 0, CellAllocPolicy>;
    239  GlobalObjectVector& indirectGlobals() const;
    240 };
    241 
    242 // The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
    243 // or SharedArrayBuffer object which owns the actual memory.
    244 
    245 class WasmMemoryObject : public NativeObject {
    246  static const unsigned BUFFER_SLOT = 0;
    247  static const unsigned OBSERVERS_SLOT = 1;
    248  static const unsigned ISHUGE_SLOT = 2;
    249  static const JSClassOps classOps_;
    250  static const ClassSpec classSpec_;
    251  static void finalize(JS::GCContext* gcx, JSObject* obj);
    252  static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
    253  static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
    254  static bool typeImpl(JSContext* cx, const CallArgs& args);
    255  static bool type(JSContext* cx, unsigned argc, Value* vp);
    256  static bool growImpl(JSContext* cx, const CallArgs& args);
    257  static bool grow(JSContext* cx, unsigned argc, Value* vp);
    258  static bool discardImpl(JSContext* cx, const CallArgs& args);
    259  static bool discard(JSContext* cx, unsigned argc, Value* vp);
    260  static uint64_t growShared(Handle<WasmMemoryObject*> memory, uint64_t delta);
    261  static bool toFixedLengthBufferImpl(JSContext* cx, const CallArgs& args);
    262  static bool toFixedLengthBuffer(JSContext* cx, unsigned argc, Value* vp);
    263  static bool toResizableBufferImpl(JSContext* cx, const CallArgs& args);
    264  static bool toResizableBuffer(JSContext* cx, unsigned argc, Value* vp);
    265 
    266  using InstanceSet = JS::WeakCache<GCHashSet<
    267      WeakHeapPtr<WasmInstanceObject*>,
    268      StableCellHasher<WeakHeapPtr<WasmInstanceObject*>>, CellAllocPolicy>>;
    269  bool hasObservers() const;
    270  InstanceSet& observers() const;
    271  InstanceSet* getOrCreateObservers(JSContext* cx);
    272 
    273  // The spec requires that the buffer property will create a new fixed length
    274  // SAB if the underlying raw buffer's length has changed. The method creates
    275  // new objects and updates the BUFFER_SLOT slot.
    276  static ArrayBufferObjectMaybeShared* refreshBuffer(
    277      JSContext* cx, Handle<WasmMemoryObject*> memoryObj,
    278      Handle<ArrayBufferObjectMaybeShared*> buffer);
    279 
    280 public:
    281  static const unsigned RESERVED_SLOTS = 3;
    282  static const JSClass class_;
    283  static const JSClass& protoClass_;
    284  static const JSPropertySpec properties[];
    285  static const JSFunctionSpec methods[];
    286  static const JSFunctionSpec memoryControlMethods[];
    287  static const JSFunctionSpec static_methods[];
    288  static bool construct(JSContext*, unsigned, Value*);
    289 
    290  static WasmMemoryObject* create(JSContext* cx,
    291                                  Handle<ArrayBufferObjectMaybeShared*> buffer,
    292                                  bool isHuge, HandleObject proto);
    293 
    294  // `buffer()` returns the current buffer object always.  If the buffer
    295  // represents shared memory then `buffer().byteLength()` never changes, and
    296  // in particular it may be a smaller value than that returned from
    297  // `volatileMemoryLength()` below.
    298  //
    299  // Generally, you do not want to call `buffer().byteLength()`, but to call
    300  // `volatileMemoryLength()`, instead.
    301  ArrayBufferObjectMaybeShared& buffer() const;
    302 
    303  // The current length of the memory in bytes. In the case of shared memory,
    304  // the length can change at any time.  Also note that this will acquire a lock
    305  // for shared memory, so do not call this from a signal handler.
    306  size_t volatileMemoryLength() const;
    307 
    308  // The current length of the memory in pages. See the comment for
    309  // `volatileMemoryLength` for details on why this is 'volatile'.
    310  wasm::Pages volatilePages() const;
    311 
    312  // The maximum length of the memory in pages. This is not 'volatile' in
    313  // contrast to the current length, as it cannot change for shared memories.
    314  wasm::Pages clampedMaxPages() const;
    315  mozilla::Maybe<wasm::Pages> sourceMaxPages() const;
    316 
    317  wasm::AddressType addressType() const;
    318  bool isShared() const;
    319  bool isHuge() const;
    320  bool movingGrowable() const;
    321  size_t boundsCheckLimit() const;
    322  wasm::PageSize pageSize() const;
    323 
    324  // If isShared() is true then obtain the underlying buffer object.
    325  WasmSharedArrayRawBuffer* sharedArrayRawBuffer() const;
    326 
    327  bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance);
    328  static uint64_t grow(Handle<WasmMemoryObject*> memory, uint64_t delta,
    329                       JSContext* cx);
    330  static void discard(Handle<WasmMemoryObject*> memory, uint64_t byteOffset,
    331                      uint64_t len, JSContext* cx);
    332 };
    333 
    334 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
    335 // wasm::Table, allowing a Table to be shared between multiple Instances
    336 // (eventually between multiple threads).
    337 
    338 class WasmTableObject : public NativeObject {
    339  static const unsigned TABLE_SLOT = 0;
    340  static const JSClassOps classOps_;
    341  static const ClassSpec classSpec_;
    342  bool isNewborn() const;
    343  static void finalize(JS::GCContext* gcx, JSObject* obj);
    344  static void trace(JSTracer* trc, JSObject* obj);
    345  static bool lengthGetterImpl(JSContext* cx, const CallArgs& args);
    346  static bool lengthGetter(JSContext* cx, unsigned argc, Value* vp);
    347  static bool typeImpl(JSContext* cx, const CallArgs& args);
    348  static bool type(JSContext* cx, unsigned argc, Value* vp);
    349  static bool getImpl(JSContext* cx, const CallArgs& args);
    350  static bool get(JSContext* cx, unsigned argc, Value* vp);
    351  static bool setImpl(JSContext* cx, const CallArgs& args);
    352  static bool set(JSContext* cx, unsigned argc, Value* vp);
    353  static bool growImpl(JSContext* cx, const CallArgs& args);
    354  static bool grow(JSContext* cx, unsigned argc, Value* vp);
    355 
    356 public:
    357  static const unsigned RESERVED_SLOTS = 1;
    358  static const JSClass class_;
    359  static const JSClass& protoClass_;
    360  static const JSPropertySpec properties[];
    361  static const JSFunctionSpec methods[];
    362  static const JSFunctionSpec static_methods[];
    363  static bool construct(JSContext*, unsigned, Value*);
    364 
    365  // Note that, after creation, a WasmTableObject's table() is not initialized
    366  // and must be initialized before use.
    367 
    368  static WasmTableObject* create(JSContext* cx, wasm::Limits limits,
    369                                 wasm::RefType tableType, HandleObject proto);
    370  wasm::Table& table() const;
    371 
    372  // Perform the standard `ToWebAssemblyValue` coercion on `value` and fill the
    373  // range [index, index + length) in the table. Callers are required to ensure
    374  // the range is within bounds. Returns false if the coercion failed.
    375  bool fillRange(JSContext* cx, uint32_t index, uint32_t length,
    376                 HandleValue value) const;
    377 };
    378 
    379 // The class of WebAssembly.Tag. This class is used to track exception tag
    380 // types for exports and imports.
    381 
    382 class WasmTagObject : public NativeObject {
    383  static const unsigned TYPE_SLOT = 0;
    384 
    385  static const JSClassOps classOps_;
    386  static const ClassSpec classSpec_;
    387  static void finalize(JS::GCContext* gcx, JSObject* obj);
    388  static bool typeImpl(JSContext* cx, const CallArgs& args);
    389  static bool type(JSContext* cx, unsigned argc, Value* vp);
    390 
    391 public:
    392  static const unsigned RESERVED_SLOTS = 1;
    393  static const JSClass class_;
    394  static const JSClass& protoClass_;
    395  static const JSPropertySpec properties[];
    396  static const JSFunctionSpec methods[];
    397  static const JSFunctionSpec static_methods[];
    398  static bool construct(JSContext*, unsigned, Value*);
    399 
    400  static WasmTagObject* create(JSContext* cx,
    401                               const wasm::SharedTagType& tagType,
    402                               HandleObject proto);
    403 
    404  const wasm::TagType* tagType() const;
    405  const wasm::ValTypeVector& valueTypes() const;
    406 };
    407 
    408 // The class of WebAssembly.Exception. This class is used for
    409 // representing exceptions thrown from Wasm in JS. (it is also used as
    410 // the internal representation for exceptions in Wasm)
    411 
    412 class WasmExceptionObject : public NativeObject {
    413  static const unsigned TAG_SLOT = 0;
    414  static const unsigned TYPE_SLOT = 1;
    415  static const unsigned DATA_SLOT = 2;
    416  static const unsigned STACK_SLOT = 3;
    417 
    418  static const JSClassOps classOps_;
    419  static const ClassSpec classSpec_;
    420  static void trace(JSTracer* trc, JSObject* obj);
    421  static void finalize(JS::GCContext* gcx, JSObject* obj);
    422  // Named isMethod instead of is to avoid name conflict.
    423  static bool isMethod(JSContext* cx, unsigned argc, Value* vp);
    424  static bool isImpl(JSContext* cx, const CallArgs& args);
    425  static bool getArg(JSContext* cx, unsigned argc, Value* vp);
    426  static bool getArgImpl(JSContext* cx, const CallArgs& args);
    427  static bool getStack(JSContext* cx, unsigned argc, Value* vp);
    428  static bool getStack_impl(JSContext* cx, const CallArgs& args);
    429 
    430  uint8_t* typedMem() const;
    431  [[nodiscard]] bool loadArg(JSContext* cx, size_t offset, wasm::ValType type,
    432                             MutableHandleValue vp) const;
    433  [[nodiscard]] bool initArg(JSContext* cx, size_t offset, wasm::ValType type,
    434                             HandleValue value);
    435 
    436  void initRefArg(size_t offset, wasm::AnyRef ref);
    437  wasm::AnyRef loadRefArg(size_t offset) const;
    438 
    439 public:
    440  static const unsigned RESERVED_SLOTS = 4;
    441  static const JSClass class_;
    442  static const JSClass& protoClass_;
    443  static const JSPropertySpec properties[];
    444  static const JSFunctionSpec methods[];
    445  static const JSFunctionSpec static_methods[];
    446  static bool construct(JSContext*, unsigned, Value*);
    447 
    448  static WasmExceptionObject* create(JSContext* cx, Handle<WasmTagObject*> tag,
    449                                     HandleObject stack, HandleObject proto);
    450  static WasmExceptionObject* wrapJSValue(JSContext* cx, HandleValue value);
    451  bool isNewborn() const;
    452 
    453  JSObject* stack() const;
    454  const wasm::TagType* tagType() const;
    455  WasmTagObject& tag() const;
    456 
    457  bool isWrappedJSValue() const;
    458  Value wrappedJSValue() const;
    459 
    460  static size_t offsetOfData() {
    461    return NativeObject::getFixedSlotOffset(DATA_SLOT);
    462  }
    463 };
    464 
    465 // The class of the WebAssembly global namespace object.
    466 
    467 class WasmNamespaceObject : public NativeObject {
    468 public:
    469  static const JSClass class_;
    470  static const unsigned JS_VALUE_TAG_SLOT = 0;
    471  static const unsigned RESERVED_SLOTS = 1;
    472 
    473  WasmTagObject* wrappedJSValueTag() const {
    474    return &getReservedSlot(JS_VALUE_TAG_SLOT)
    475                .toObjectOrNull()
    476                ->as<WasmTagObject>();
    477  }
    478  void setWrappedJSValueTag(WasmTagObject* tag) {
    479    return setReservedSlot(JS_VALUE_TAG_SLOT, ObjectValue(*tag));
    480  }
    481 
    482  static WasmNamespaceObject* getOrCreate(JSContext* cx);
    483 
    484 private:
    485  static const ClassSpec classSpec_;
    486 };
    487 
    488 extern const JSClass WasmFunctionClass;
    489 
    490 bool IsWasmSuspendingObject(JSObject* obj);
    491 
    492 #ifdef ENABLE_WASM_JSPI
    493 
    494 class WasmSuspendingObject : public NativeObject {
    495 public:
    496  static const ClassSpec classSpec_;
    497  static const JSClass class_;
    498  static const JSClass& protoClass_;
    499  static const unsigned WRAPPED_FN_SLOT = 0;
    500  static const unsigned RESERVED_SLOTS = 1;
    501  static bool construct(JSContext*, unsigned, Value*);
    502 
    503  JSObject* wrappedFunction() const {
    504    return getReservedSlot(WRAPPED_FN_SLOT).toObjectOrNull();
    505  }
    506  void setWrappedFunction(HandleObject fn) {
    507    return setReservedSlot(WRAPPED_FN_SLOT, ObjectValue(*fn));
    508  }
    509 };
    510 
    511 JSObject* MaybeUnwrapSuspendingObject(JSObject* wrapper);
    512 #endif
    513 
    514 }  // namespace js
    515 
    516 #endif  // wasm_js_h