-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[test] Add tentative JS API tests for Exported GC Object #355
Conversation
test/js-api/wasm-module-builder.js
Outdated
} | ||
|
||
// GC opcodes | ||
let kExprStructNew = 0x01; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the opcode numbers for struct.new
and array.new
differ from V8/SM currently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's because the implementations all along the stack (engines, Binaryen, *-to-Wasm compilers) have been maintaining (incremental) backwards compatibility when adding and removing instructions as the proposal evolved, whereas the official spec is not concerned with such mundane details.
This will resolve itself when we do the large final breaking opcode reshuffling. In the meantime, see https://bit.ly/3cWcm6Q for the authoritative documentation on what toolchains and engines implement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note in the spec about this would be useful, IMO. In the meantime, it might be best to use the de facto opcode numbers in the tests, so that they can be run against implementations - not much point in them otherwise.
test(() => { | ||
const struct = functions.makeStruct() | ||
const array = functions.makeArray() | ||
assert_throws_js(TypeError, () => Object.getPrototypeOf(struct)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I tried this in V8, this test failed because the getPrototypeOf
didn't throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, when we made [[Get]] not throw we also had to make [[GetPrototypeOf]] not throw (but return null
instead). Looks like I forgot to update the draft doc accordingly, sorry about that. Fixed it there just now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok thanks, I updated this test case to check for null instead as well. I think this actually makes it easier for JSC to implement [[Get]]'s behavior too, though I'm wondering if it'll be sufficient to future-proof for shared structs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm a little worried about future-proofing for improved prototypes later (related to alignment with shared structs). Is this is the only way we can make it so that async functions can return Wasm GC objects without blowing up? If so, it seems like this is the best possible compromise.
Other than the points I mentioned in the tests above, I was able to run this in V8 by manually importing it into |
test(() => { | ||
const struct = functions.makeStruct() | ||
const array = functions.makeArray() | ||
assert_throws_js(TypeError, () => { struct.foo = 5; }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now [[Set]] is specified to always throw, which matches the test cases here.
It is arguably a bit inconsistent with the rest of JS though, because usually [[Set]] returns false on failure but doesn't throw. In the usual case, that means that in non-strict mode, struct.foo = 5
will not add/mutate the field and just silently fail without throwing. With Wasm structs/arrays, it will throw even in non-strict mode. Not sure anyone will care about this difference, but I thought it might be worth mentioning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, returning false probably makes more sense. In any case, we should probably make sure to test these cases both in strict and loose mode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that makes sense. I've changed the test now to have strict and non-strict versions, with no error raised in the non-strict version (and the property shouldn't be added/deleted). We'll have to change the spec to match this though.
Who would be the best person to review this and provide review approval? @Ms2ger would you be interested in taking a look? |
I can't seem to add him as a reviewer, but it would make sense for @syg to do at least an unofficial review of this given the intersection with JS Structs. |
// Use these for multi-byte instructions (opcode > 0x7F needing two LEB bytes): | ||
function GCInstr(opcode) { | ||
if (opcode <= 0x7F) return [kGCPrefix, opcode]; | ||
return [kGCPrefix, 0x80 | (opcode & 0x7F), opcode >> 7]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this use wasmSignedLeb
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be able to.
In general for these wasm-module-builder.js
changes I took the upstream V8 code as-is when I could, so merging new changes would be easier without a complicated diff. Do you prefer that I make changes here anyway and let it diverge with upstream? (also applies to other comments)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps someone from the V8 side could comment. @jakobkummerow seems to have some blame around here.
Would be a nice project for someone with time to consolidate all the versions going around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this probably could use wasmSignedLeb
, but it also doesn't matter as it seems exceedingly unlikely that we'll have that many opcodes any time soon (if ever). From a V8 perspective, we see our wasm-module-builder.js
as very frequently changing code anyway: we update it whenever we need to (typically to support some new feature), and that happens a lot. Basically the reason we have our own thing there rather than importing someone else's implementation is because we need to change it so much, and want that to have low friction and low latency.
If you want to submit a patch to improve it, I'd volunteer to review that, but I'm currently not inclined to spend my own time to make this code more polished-looking.
test/js-api/wasm-module-builder.js
Outdated
} | ||
|
||
// GC opcodes | ||
let kExprStructNew = 0x01; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A note in the spec about this would be useful, IMO. In the meantime, it might be best to use the de facto opcode numbers in the tests, so that they can be run against implementations - not much point in them otherwise.
} | ||
|
||
emit_type(type) { | ||
if ((typeof type) == 'number') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if ((typeof type) == 'number') { | |
if (typeof type === 'number') { |
@@ -674,6 +742,35 @@ class WasmTableBuilder { | |||
} | |||
} | |||
|
|||
function makeField(type, mutability) { | |||
if ((typeof mutability) != 'boolean') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if ((typeof mutability) != 'boolean') { | |
if (typeof mutability !== 'boolean') { |
// We use {is_final = true} so that the MVP syntax is generated for | ||
// signatures. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this about, and should it really be controlled on a type-by-type basis?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the GC proposal type declarations for structs & arrays can be declared final or not. Since the finality is declared for particular type defs I think it has to be type-by-type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the MVP comment, then.
test(() => { | ||
const struct = functions.makeStruct() | ||
const array = functions.makeArray() | ||
assert_throws_js(TypeError, () => { struct.foo = 5; }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, returning false probably makes more sense. In any case, we should probably make sure to test these cases both in strict and loose mode
02409ff
to
508aeb7
Compare
Thanks for the comments, I resolved them aside from the ones about the module builder (see comment). The main change is to allow not throwing an exception in non-strict mode (and silently failing) for adding properties and deleting. This modified version should succeed on V8 except for the non-strict tests. On JSC it also passes (except non-strict) with some minor changes in opcodes. In JSC I implemented a quick fix for the non-strict case and got all tests to pass too. |
I have a concern about the change to not throw in non-strict mode. That's why I'd prefer to keep throwing in both strict and non-strict mode for now. Also, in general, when a PR named "add tentative tests" makes such a change to behavior, I think this needs to be explicitly called out to a wider audience (one might even argue that it should happen as a separate PR). |
Note that this PR doesn't change the spec. |
What is the status of this PR? |
Adds a test for the draft JS API spec from this PR: WebAssembly#352 This primarily tests the internal object methods described in the spec. It doesn't yet test the ToWebAssemblyValue / ToJSValue conversions. Also syncs a portion of the module builder code with V8 to allow constructing a subset of GC-using modules.
* Update opcodes to final versions in MVP document * Undid strict/non-strict behavior difference
@takikawa, ping, is this PR still relevant? |
508aeb7
to
920e636
Compare
Had to force push due to the rebase. This should now be updated to throw in non-strict mode as well, like @jakobkummerow requested (i.e., reverted to the original test behavior for this). It should also be updated to match the final opcodes in MVP.md. I've tested it on JSC/WebKit but haven't had a chance to run it on V8 or SpiderMonkey. |
@takikawa For testing this on V8 with the final binary encoding, you can patch in crrev.com/c/4756846. We're planning to land that after the Phase 4 vote. |
* The exported-object test should run on V8 now * Non-GC JS API tests should run correctly too
I've now tested this on V8 (on commit 014e0bb5ac4691c85b1da2dac579af77d02d17b7, already updated to new opcodes) in the I also pushed some fixes that I found while testing this, all in the On Firefox, some exception handling proposal tests do fail. This is because the exception tests (that are in WPT as tentative tests) use some type code names that have changed in the upstream |
In any case, I think this is ready to merge if other people are happy with it. Separately, I'm planning to work on on the rest of the JS API tests (testing |
Adds tests for the "Exported GC Object" described in the proposed JS API spec in #352.
This requires updating the wasm module builder helper library, which is now fairly de-synced with the V8 library it's based on. I've only synced a bare minimum in this PR to be able to construct some GC modules (syncing the whole thing causes other tests to fail).
The V8 version also uses a slightly different opcode numbering than the current spec document in this repo. In this PR I went with the spec numbering (in the end they will all need to be synchronized).
This doesn't have tests for all the ToWasmValue/ToJSValue cases for conversions, which would also be useful to add.