@@ -378,39 +378,90 @@ C |- br_on_cast_desc_fail l rt_1 rt_2 : t* rt_1 (ref null exact_1 y) -> t* rt_2
378
378
In JS engines,
379
379
WebAssembly RTTs correspond to JS shape descriptors.
380
380
Custom descriptors act as first-class handles to the engine-managed RTTs,
381
- so they can serve as extension points for the JS reflection of the Wasm objects they describe.
382
-
383
- We introduce a new ` WebAssembly.setDescriptorPrototype() ` API
384
- that takes a custom descriptor instance and a prototype object.
385
- The provided prototype will be attached to the descriptor's RTT
386
- and will become the prototype for the JS reflection
387
- of all Wasm objects the custom descriptor describes.
388
-
389
- The following is a full example that uses ` WebAssembly.setDescriptorPrototype() `
381
+ so they can serve as extension points for the JS reflection of the Wasm objects they describe,
382
+ and in particular they can allow JS prototypes to be associated with the described objects.
383
+ To make this work, we allow information about the intended JS reflection of Wasm objects
384
+ to be imported into a Wasm module and held by custom descriptors.
385
+ The ` [[GetPrototypeOf]] ` algorithm for a WebAssembly object can then look up this information
386
+ on the object's custom descriptor. The details of how this works are described below.
387
+
388
+ We introduce a new ` WebAssembly.DescriptorOptions ` type
389
+ that holds relevant information about the JS reflection of Wasm objects.
390
+ A ` DescriptorOptions ` is constructed with an option bag containing
391
+ an object to be used as a prototype. Other options may be added in the future,
392
+ for example to expose Wasm struct fields as own properties.
393
+
394
+ ``` webidl
395
+ dictionary DescriptorOptionsOptions {
396
+ object? prototype;
397
+ };
398
+
399
+ [LegacyNamespace=WebAssembly, Exposed=*]
400
+ interface DescriptorOptions {
401
+ constructor(DescriptorOptionsOptions options);
402
+ }
403
+ ```
404
+
405
+ A ` DescriptorOptions ` object has a ` [[WebAssemblyDescriptorOptions]] `
406
+ internal slot with the value ` true ` .
407
+ This allows it to be identified by the ` [[GetPrototypeOf]] ` algorithm.
408
+ Its constructor copies all of the options into the constructed ` DescriptorOptions ` .
409
+
410
+ The specification of the ` [[GetPrototypeOf]] ` internal method
411
+ of an Exported GC Object ` O ` is updated to perform the following steps
412
+ (which will be made more precise in the final spec):
413
+
414
+ 1 . If ` O.[[ObjectKind]] ` is not "struct":
415
+ 1 . Return ` null ` .
416
+ 1 . Let ` store ` be the surrounding agent's associated store
417
+ 1 . Look up the object's heap type from the store.
418
+ 1 . If the heap type does not have a descriptor clause:
419
+ 1 . Return ` null ` .
420
+ 1 . Get the descriptor value and descriptor type.
421
+ 1 . If the descriptor type has has no fields or its first field is not immutable or does not match ` externref ` :
422
+ 1 . Return ` null ` .
423
+ 1 . Get the value ` v ` of the first field.
424
+ 1 . Let ` u ` be ` ToJSValue(v) ` .
425
+ 1 . If ` u ` does not have a ` [[WebAssemblyDescriptorOptions]] ` internal slot:
426
+ 1 . Return ` null ` .
427
+ 1 . Return the prototype stored in ` u ` .
428
+
429
+ > Note: it would also be good to ensure a ` DescriptorOptions ` is opaque and can
430
+ > only be used once to avoid having to keep the configuration data live for the
431
+ > lifetime of the custom descriptor. TODO.
432
+
433
+ The only new capability required in the WebAssembly embedding interface is the
434
+ ability to inspect a reference's heap type.
435
+ The algorithm also needs to access the value's descriptor and its fields,
436
+ but in principle it could do that by synthesizing a new Wasm instance
437
+ exporting the functions necessary to perform that access,
438
+ so those would not be fundamentally new capabilities.
439
+
440
+ The following is a full example that uses ` WebAssembly.DescriptorOptions `
390
441
to allow JS to call ` get() ` and ` inc() ` methods on counter objects implemented in
391
442
WebAssembly.
392
443
393
- > Note: If there is demand for it,
394
- > a similar API could configure property names that JS could use to access fields
395
- > of the described WebAssembly objects.
396
-
397
444
``` wasm
398
445
;; counter.wasm
399
446
(module
400
447
(rec
401
448
(type $counter (descriptor $counter.vtable (struct (field $val i32))))
402
449
(type $counter.vtable (describes $counter (struct
450
+ (field $proto (ref extern))
403
451
(field $get (ref $get_t))
404
452
(field $inc (ref $inc_t))
405
- ))
453
+ )))
406
454
(type $get_t (func (param (ref null $counter)) (result i32)))
407
455
(type $inc_t (func (param (ref null $counter))))
408
456
)
409
457
458
+ (import "env" "counter.proto" (global $counter.proto (ref extern)))
459
+
410
460
(elem declare func $counter.get $counter.inc)
411
461
412
- (global $counter.vtable (export "counter.vtable") ( ref exact $counter.vtable)
462
+ (global $counter.vtable (ref exact $counter.vtable)
413
463
(struct.new $counter.vtable
464
+ (global.get $counter.proto)
414
465
(ref.func $counter.get)
415
466
(ref.func $counter.inc)
416
467
)
@@ -440,20 +491,32 @@ WebAssembly.
440
491
441
492
``` js
442
493
// counter.js
443
- var {module , instance} = await WebAssembly .instantiateStreaming (fetch (' counter.wasm' ));
444
494
445
- WebAssembly .setDescriptorPrototype (instance .exports [' counter.vtable' ], {
446
- get : function () { instance .exports [' counter.get' ](this ); },
447
- inc : function () { return instance .exports [' counter.get' ](this ); }
495
+ var counterProto = {};
496
+
497
+ var counterOpts = new WebAssembly.DescriptorOptions ({
498
+ prototype: counterProto
448
499
});
449
500
501
+ var {module , instance} = await WebAssembly .instantiateStreaming (fetch (' counter.wasm' ), {
502
+ env: {
503
+ " counter.proto" : counterOpts
504
+ }
505
+ });
506
+
507
+ counterProto .get = function () { return instance .exports [' counter.get' ](this ); };
508
+ counterProto .inc = function () { instance .exports [' counter.inc' ](this ); };
509
+
450
510
var counter = instance .exports [' counter' ];
451
511
452
512
console .log (counter .get ()); // 0
453
513
counter .inc ();
454
514
console .log (counter .get ()); // 1
455
515
```
456
516
517
+ > Note: Other API designs are also possible.
518
+ > See the discussion at https://github.com/WebAssembly/custom-rtts/issues/2 .
519
+
457
520
## Declarative Prototype Initialization
458
521
459
522
TODO
0 commit comments