WebAssembly / js-string-builtins

JS String Builtins
Other
8 stars 6 forks source link

Specify how exactly the `options` object and its `builtins` property are consumed #18

Closed jakobkummerow closed 9 months ago

jakobkummerow commented 9 months ago

The overview currently only describes what structure the data provided by the options object should have, but doesn't list the specific JavaScript-level steps that an engine should perform to access this data.

We're facing a tension between simplicity and flexibility there. For the vast majority of real-world use cases, supporting only very simple cases would likely be enough. On the other hand, developers who are accustomed to JavaScript might expect that they can perform arbitrarily complicated "gymnastics" if they wish.

Personally I have no strong opinion on this, except on the meta-level: I do think that these details should be specified exactly, and of course they should be reasonably implementable (e.g. it would be unfortunate if all implementations for all eternity were forced to take extra detours, such as re-reading the array's length after every element access, just to support "weird" corner cases).

For illustrative purposes, one random example of vaguely-similar prior art how converting a list-like JS value to a conceptual list could be specified is CanonicalizeLocaleList. (This isn't meant to be a particular endorsement.)

Example situations:

(1) What should happen when some of the involved values are primitives? Should that throw an exception, or be disregarded silently (i.e. as if the value wasn't there)? Consider:

new WebAssembly.Module(..., 123);
new WebAssembly.Module(..., {builtins: 123});
new WebAssembly.Module(..., {builtins: [123]});

(2) Can the builtins list be an array-like object, i.e. does this work?

new WebAssembly.Module(..., {builtins: {length: 1, 0: "js-string"}});
new WebAssembly.Module(..., {builtins: {length: 1, 0: 0, 1: "js-string"}});  // imports *not* applied due to length == 1

(3) Is the builtins list allowed to have nonexistent entries?

new WebAssembly.Module(..., {builtins: [, , , "js-string", , , ]});

(4) Can the builtins list have accessor properties? What if they throw? What if they modify the builtins list?

new WebAssembly.Module(..., {builtins: {length: 1, get 0() { return "js-string"; }}});  // works?
new WebAssembly.Module(..., {builtins: {length: 1, get 0() { throw "js-string"; }}});  // throws a CompileError or LinkError or other error?

new WebAssembly.Module(..., {builtins:  {
  length: 2,
  get 0() {this.length = 1; return ""},
  1: "js-string",
}});  // Does this successfully provide js-string builtins?

new WebAssembly.Module(..., {builtins: {
  length: 1,
  get 0() {this.length = 2; this[1] = "js-strings"; return ""},
}});  // Does this successfully provide js-string builtins?

new WebAssembly.Module(..., {builtins: {
  length: 2,
  get 0() {delete this[1]; return ""},
  1: "js-string",
}});  // What about this?

(5) Do we check the builtins list's prototype, i.e. does this work?

new WebAssembly.Module(..., {builtins: {__proto__: ["js-string"]}});

(6) Should we perform toString() on the elements of the builtins list, i.e. does this work?

new WebAssembly.Module(..., {builtins: [{toString() { return "js-string"; }}]});

(7) Can the builtins list be a Proxy, i.e. does this work?

new WebAssembly.Module(..., {builtins: new Proxy({}, {
  get(target, name) {
    if (name === "length") return 1;
    if (name === "0") return "js-string";
  },
  has(target, name) {
    return name === "length" || name === "0";
  }
})}

What if the Proxy's has trap returns false, does that matter?

ajklein commented 9 months ago

In the overview, builtins is specified as a WebIDL sequence<USVString>, so I believe the answer to these questions is fully-specified in WebIDL: see https://webidl.spec.whatwg.org/#create-sequence-from-iterable.

For reference, there's also Chromium bindings code; implementing some (not-fully-correct?) fast-paths.

eqrion commented 9 months ago

@ajklein @jakobkummerow Yes, I intended for the answer to this to be 'whatever WebIDL says'.

It's a little odd for us internally, because our WebIDL binding code generator is a layer above SM, so we have to hand-write the bindings code for situations like this. But that's already the precedent set up by the JS-API.

eqrion commented 9 months ago

I believe this has been answered. I don't think anything needs changing in the overview, but let me know if you think it does.