Conversation
| // An empty data array will cause a trap. | ||
| assert_throws_js(WebAssembly.RuntimeError, () => { |
There was a problem hiding this comment.
V8 currently throws a CompileError here. I think trapping makes more sense because the initial read is out of the array bounds.
There was a problem hiding this comment.
I chose a CompileError because of symmetry with decoding wire bytes. Changing the type of error is a one-liner so I don't care much.
That said, it's convenient to throw the same kind of error for all possible failures, because then various failure cases can all fall through to the same bit of code that allocates and throws the actual error object. That seems to be what this PR suggests, so I'm fine with that.
| // Extra prototypes will cause a trap. | ||
| assert_throws_js(WebAssembly.RuntimeError, () => { |
There was a problem hiding this comment.
V8 does not currently raise any error on extra contents in the arrays, but I think it's worth doing. See #86.
| configureAll(makeProtosArray([proto]), | ||
| makeMethodsArray([null, null, null]), | ||
| makeDataArray(data), | ||
| null); |
There was a problem hiding this comment.
V8 currently traps on this, but I don't think it should be necessary to inspect the value of the methods or prototypes before trying to install them.
There was a problem hiding this comment.
@jakobkummerow pointed out that requiring non-null values here allows the optimized method wrappers to skip a null check, so I'll go ahead and make that change.
There was a problem hiding this comment.
Thanks. Also, for the record, we do need to inspect the value before installing it, because if it's a funcref then we have to fetch or allocate the JS wrapper for it. Conceptually this is a point where values transition from the Wasm world to the JS world (like extern.convert_any).
My personal preference would be to statically require an array of non-nullable (ref func).
| for (let disallowed of [null, undefined, nonextensible, unwritable, 10, "foo"]) { | ||
| const protosArray = makeProtosArray([disallowed]); | ||
| assert_throws_js(TypeError, () => { | ||
| configureAll(protosArray, methodsArray, dataArray, null); | ||
| }); | ||
| } |
There was a problem hiding this comment.
V8 currently throws on unwritable, but not most of the other disallowed values. It also traps instead of throwing a TypeError when writing to null.
| for (disallowed of [null, undefined, 10, "foo", nonextensible, unwritable]) { | ||
| assert_throws_js(TypeError, () => { | ||
| configureAll(makeProtosArray([{}]), | ||
| makeMethodsArray([makeStructWithProto]), | ||
| makeDataArray(data), | ||
| disallowed); | ||
| }); |
There was a problem hiding this comment.
V8 generally has the same mismatches in behavior here as when failing to write properties to a prototype object.
| configureAll(makeProtosArray([proto]), | ||
| makeMethodsArray([makeStructWithProto, null, null, null]), | ||
| makeDataArray(data), | ||
| constructors); |
There was a problem hiding this comment.
Just like with the non-static methods, V8 traps on these null methods.
| 0, // parent prototype index 0 | ||
| ]; | ||
|
|
||
| for (let allowed of [null, {}, Object.getPrototypeOf({}), new Proxy({}, {})]) { |
There was a problem hiding this comment.
V8 does not allow null or the Proxy here, but I don't think there's any reason to restrict what values are allowed as prototypes beyond the normal rules.
| 0, // parent prototype index 0 | ||
| ]; | ||
|
|
||
| assert_throws_js(WebAssembly.RuntimeError, () => { |
There was a problem hiding this comment.
V8 throws a CompileError here instead of trapping and similar for other cases of invalid data. I don't feel too strongly about what kind of error we throw for this kind of error.
| 1, // one method | ||
| 0x00, // normal method | ||
| 0x2, // two bytes in name | ||
| 0b11011111, 0b00000000, // invalid UTF-8 |
There was a problem hiding this comment.
V8 does not currently raise an error on invalid UTF-8. Instead it seems to take the raw byte stream as the name somehow.
There was a problem hiding this comment.
The current implementation happens to use an existing helper function that silently inserts replacement characters (0xFFFD) for invalid UTF-8. There's no particular reason for that, and it's easy to fix.
| 1, // one prototype | ||
| 1, // one constructor | ||
| ...stringToBytes("Foo"), | ||
| 3, // three static method configs |
There was a problem hiding this comment.
What will happen if we specify 3 as the count of method configs here but define less (2 methods) or more (4 methods) in the next lines? Could this also be one example of unconsumed elements?
There was a problem hiding this comment.
Yes, exactly. The case where there are extra method configs is similar to the "extra data" test and the case where there are not enough method configs is similar to the "early end of data" test.
jakobkummerow
left a comment
There was a problem hiding this comment.
LGTM with comments
| // Extra prototypes will cause a trap. | ||
| assert_throws_js(WebAssembly.RuntimeError, () => { |
| // An empty data array will cause a trap. | ||
| assert_throws_js(WebAssembly.RuntimeError, () => { |
There was a problem hiding this comment.
I chose a CompileError because of symmetry with decoding wire bytes. Changing the type of error is a one-liner so I don't care much.
That said, it's convenient to throw the same kind of error for all possible failures, because then various failure cases can all fall through to the same bit of code that allocates and throws the actual error object. That seems to be what this PR suggests, so I'm fine with that.
| configureAll(makeProtosArray([proto]), | ||
| makeMethodsArray([null, null, null]), | ||
| makeDataArray(data), | ||
| null); |
There was a problem hiding this comment.
Thanks. Also, for the record, we do need to inspect the value before installing it, because if it's a funcref then we have to fetch or allocate the JS wrapper for it. Conceptually this is a point where values transition from the Wasm world to the JS world (like extern.convert_any).
My personal preference would be to statically require an array of non-nullable (ref func).
| for (let disallowed of [null, undefined, nonextensible, unwritable, 10, "foo"]) { | ||
| const protosArray = makeProtosArray([disallowed]); | ||
| assert_throws_js(TypeError, () => { | ||
| configureAll(protosArray, methodsArray, dataArray, null); | ||
| }); | ||
| } |
| 1, // one method | ||
| 0x00, // normal method | ||
| 0x2, // two bytes in name | ||
| 0b11011111, 0b00000000, // invalid UTF-8 |
There was a problem hiding this comment.
The current implementation happens to use an existing helper function that silently inserts replacement characters (0xFFFD) for invalid UTF-8. There's no particular reason for that, and it's easy to fix.
This fixes minor spec divergences (mostly around the handling of error cases), so that the existing "prototypes.any" test passes, and the upcoming "configure-all.any" test [1] passes too. Specific changes to `configureAll()`: - accept all valid prototypes, not just JSObjects - throw for invalid UTF-8 names - throw if there are unconsumed functions or prototypes - change the type of a few errors [1] WebAssembly/custom-descriptors#87 Bug: 403372470 Change-Id: I699bd90858e1f28ab8c93f667d6b3bdaed7670bc Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7511038 Reviewed-by: Clemens Backes <clemensb@chromium.org> Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/main@{#104910}
No description provided.