Skip to content

Commit

Permalink
[Wasm-GC] Fix JS API casts for some top types
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=270374

Reviewed by Justin Michaud.

Fixes too-optimistic assumption about static validation for top type casts.

* JSTests/wasm/gc/js-api.js:
(testCastFailure):
* Source/JavaScriptCore/wasm/WasmTypeDefinition.cpp:
(JSC::Wasm::TypeInformation::castReference):

Canonical link: https://commits.webkit.org/276580@main
  • Loading branch information
takikawa authored and justinmichaud committed Mar 22, 2024
1 parent 66f743c commit a2bcc49
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 2 deletions.
80 changes: 80 additions & 0 deletions JSTests/wasm/gc/js-api.js
Expand Up @@ -249,6 +249,37 @@ function testCastFailure() {
);
}

{
let m = instantiate(`
(module
(type (struct))
(global (export "g") (ref 0) (struct.new 0))
(func (export "f") (param (ref func)) (result))
)
`);
assert.throws(
() => m.exports.f(m.exports.g.value),
TypeError,
"Funcref must be an exported wasm function"
);
}

{
let m = instantiate(`
(module
(type (struct))
(type (func))
(global (export "g") (ref 0) (struct.new 0))
(func (export "f") (param (ref 1)) (result))
)
`);
assert.throws(
() => m.exports.f(m.exports.g.value),
TypeError,
"Argument value did not match reference type"
);
}

{
let m = instantiate(`
(module
Expand All @@ -275,6 +306,15 @@ function testCastFailure() {
);
}

{
let m = instantiate(`
(module
(func (export "f") (param (ref any)) (result))
)
`);
m.exports.f(m.exports.f);
}

{
let m = instantiate(`
(module
Expand Down Expand Up @@ -422,6 +462,19 @@ function testGlobal() {
assert.eq(m.exports.f(m.exports.g.value), 42);
}

{
let m1 = instantiate(`
(module
(func (export "f"))
)
`);
instantiate(`
(module
(global (import "m" "f") (ref any))
)
`, { m: { f: m1.exports.f } });
}

// Test portable globals, with mutation.
{
let m = instantiate(`
Expand Down Expand Up @@ -488,6 +541,33 @@ function testTable() {
assert.eq(m.exports.t.get(0)(m.exports.g.value), 42);
}

{
let m1 = instantiate(`
(module
(func (export "f"))
)
`);
let m2 = instantiate(`
(module
(table (export "t") 10 (ref null any))
)
`);
m2.exports.t.set(0, m1.exports.f);
}

{
let m1 = instantiate(`
(module
(table (export "t") 10 (ref null func))
)
`);
assert.throws(
() => m1.exports.t.set(0, 5),
TypeError,
"WebAssembly.Table.prototype.set expects the second argument to be null or an instance of WebAssembly.Function"
)
}

// Test type descriptor
{
let m = instantiate(`
Expand Down
5 changes: 3 additions & 2 deletions Source/JavaScriptCore/wasm/WasmTypeDefinition.cpp
Expand Up @@ -1078,11 +1078,12 @@ bool TypeInformation::castReference(JSValue refValue, bool allowNull, TypeIndex

if (typeIndexIsType(typeIndex)) {
switch (static_cast<TypeKind>(typeIndex)) {
case TypeKind::Funcref:
case TypeKind::Externref:
case TypeKind::Anyref:
// Casts to these types cannot fail as they are the top types of their respective hierarchies, and static type-checking does not allow cross-hierarchy casts.
// Casts to these types cannot fail as any value can be an externref/hostref.
return true;
case TypeKind::Funcref:
return !!jsDynamicCast<WebAssemblyFunctionBase*>(refValue);
case TypeKind::Eqref:
return (refValue.isInt32() && refValue.asInt32() <= maxI31ref && refValue.asInt32() >= minI31ref) || jsDynamicCast<JSWebAssemblyArray*>(refValue) || jsDynamicCast<JSWebAssemblyStruct*>(refValue);
case TypeKind::Nullref:
Expand Down

0 comments on commit a2bcc49

Please sign in to comment.