Skip to content

Conversation

@tlively
Copy link
Member

@tlively tlively commented Jan 23, 2026

No description provided.

Comment on lines +231 to +232
// An empty data array will cause a trap.
assert_throws_js(WebAssembly.RuntimeError, () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 currently throws a CompileError here. I think trapping makes more sense because the initial read is out of the array bounds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +241 to +242
// Extra prototypes will cause a trap.
assert_throws_js(WebAssembly.RuntimeError, () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 does not currently raise any error on extra contents in the arrays, but I think it's worth doing. See #86.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, no problem.

Comment on lines +341 to +344
configureAll(makeProtosArray([proto]),
makeMethodsArray([null, null, null]),
makeDataArray(data),
null);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment on lines +376 to +381
for (let disallowed of [null, undefined, nonextensible, unwritable, 10, "foo"]) {
const protosArray = makeProtosArray([disallowed]);
assert_throws_js(TypeError, () => {
configureAll(protosArray, methodsArray, dataArray, null);
});
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines +528 to +534
for (disallowed of [null, undefined, 10, "foo", nonextensible, unwritable]) {
assert_throws_js(TypeError, () => {
configureAll(makeProtosArray([{}]),
makeMethodsArray([makeStructWithProto]),
makeDataArray(data),
disallowed);
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 generally has the same mismatches in behavior here as when failing to write properties to a prototype object.

Comment on lines +558 to +561
configureAll(makeProtosArray([proto]),
makeMethodsArray([makeStructWithProto, null, null, null]),
makeDataArray(data),
constructors);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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({}, {})]) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

V8 does not currently raise an error on invalid UTF-8. Instead it seems to take the raw byte stream as the name somehow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link

@rmahdav rmahdav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks Thomas!

1, // one prototype
1, // one constructor
...stringToBytes("Foo"),
3, // three static method configs
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks!

Copy link
Contributor

@jakobkummerow jakobkummerow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with comments

Comment on lines +241 to +242
// Extra prototypes will cause a trap.
assert_throws_js(WebAssembly.RuntimeError, () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, no problem.

Comment on lines +231 to +232
// An empty data array will cause a trap.
assert_throws_js(WebAssembly.RuntimeError, () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +341 to +344
configureAll(makeProtosArray([proto]),
makeMethodsArray([null, null, null]),
makeDataArray(data),
null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Comment on lines +376 to +381
for (let disallowed of [null, undefined, nonextensible, unwritable, 10, "foo"]) {
const protosArray = makeProtosArray([disallowed]);
assert_throws_js(TypeError, () => {
configureAll(protosArray, methodsArray, dataArray, null);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

1, // one method
0x00, // normal method
0x2, // two bytes in name
0b11011111, 0b00000000, // invalid UTF-8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants