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