Skip to content

Commit

Permalink
[WASM-Function-References] Improve type printing for reference types
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=247746

Reviewed by Justin Michaud.

Print reftypes when typed funcrefs are enabled in (ref null? kind)
format where `kind` is either a heap type (e.g., `i31`, `struct`)
or a concrete type index (e.g., `<struct:0>`, `<func:3>`).

This printing is only used for validation errors for now, as it
relies on being able to access the ModuleInformation.

* JSTests/wasm/function-references/local_init.js:
(async testLocalInit):
* JSTests/wasm/function-references/ref_types.js:
(async testNonNullExternrefIncompatible):
(async testNonNullFuncrefIncompatible):
* JSTests/wasm/gc/any.js:
(testValidation):
(testNullfuncref):
(testNullexternref):
* JSTests/wasm/gc/arrays.js:
(testArrayDeclaration):
* JSTests/wasm/gc/casts.js:
(testFunctionCasts):
(testEqCasts):
* JSTests/wasm/gc/const-exprs.js:
(async testInvalidConstExprs):
* JSTests/wasm/gc/i31.js:
(testI31Get):
* JSTests/wasm/gc/rec.js:
(testRecDeclaration):
* JSTests/wasm/gc/structs.js:
(testStructDeclaration):
* JSTests/wasm/gc/sub.js:
(testSubDeclaration):
* Source/JavaScriptCore/wasm/WasmFormat.h:
(JSC::Wasm::heapTypeKindAsString):
* Source/JavaScriptCore/wasm/WasmFunctionParser.h:
(JSC::Wasm::FunctionParser::validationFail const):
(JSC::Wasm::FunctionParser::validationFailHelper const):
(JSC::Wasm::FunctionParser::typeToStringModuleRelative const):

Canonical link: https://commits.webkit.org/270988@main
  • Loading branch information
takikawa committed Nov 20, 2023
1 parent fc50020 commit fd12481
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 30 deletions.
2 changes: 1 addition & 1 deletion JSTests/wasm/function-references/local_init.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ async function testLocalInit() {
assert.throws(
() => module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x86\x80\x80\x80\x00\x01\x60\x01\x64\x6f\x00\x03\x82\x80\x80\x80\x00\x01\x00\x0a\x8f\x80\x80\x80\x00\x01\x89\x80\x80\x80\x00\x01\x01\x64\x6f\xd0\x6f\x21\x01\x0b"),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: set_local to type Externref expected Externref, in function at index 0"
"WebAssembly.Module doesn't validate: set_local to type (ref null extern) expected (ref extern), in function at index 0"
);

/*
Expand Down
4 changes: 2 additions & 2 deletions JSTests/wasm/function-references/ref_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ async function testNonNullExternrefIncompatible() {
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x07\x01\x60\x01\x6f\x01\x64\x6f\x03\x02\x01\x00\x0a\x06\x01\x04\x00\x20\x00\x0b\x00\x0b\x04\x6e\x61\x6d\x65\x01\x04\x01\x00\x01\x66"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Externref is not a Externref, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null extern) is not a (ref extern), in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}

Expand Down Expand Up @@ -295,7 +295,7 @@ async function testNonNullFuncrefIncompatible() {
"\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x07\x01\x60\x01\x70\x01\x64\x70\x03\x02\x01\x00\x0a\x06\x01\x04\x00\x20\x00\x0b\x00\x0b\x04\x6e\x61\x6d\x65\x01\x04\x01\x00\x01\x66"
),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Funcref is not a Funcref, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null func) is not a (ref func), in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')"
);
}

Expand Down
16 changes: 8 additions & 8 deletions JSTests/wasm/gc/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function testValidation() {
(func (call 0 (ref.null extern))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: argument type mismatch in call, got Externref, expected Anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (ref null extern), expected (ref null any), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

assert.throws(
Expand All @@ -32,7 +32,7 @@ function testValidation() {
(func (call 0 (ref.null any))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: argument type mismatch in call, got Anyref, expected (), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (ref null any), expected (ref null <struct:0>), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

assert.throws(
Expand All @@ -42,7 +42,7 @@ function testValidation() {
(func (call 0 (ref.null any))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: argument type mismatch in call, got Anyref, expected Nullref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (ref null any), expected (ref null none), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

assert.throws(
Expand All @@ -53,7 +53,7 @@ function testValidation() {
(func (call 0 (struct.new 0))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (), expected Nullref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (ref <struct:0>), expected (ref none), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down Expand Up @@ -106,7 +106,7 @@ function testNullfuncref() {
(func (export "f") (result nullfuncref) (ref.null func)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Funcref is not a Nullfuncref"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null func) is not a (ref null nofunc)"
)

assert.throws(
Expand All @@ -115,7 +115,7 @@ function testNullfuncref() {
(func (export "f") (result nullref) (ref.null nofunc)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Nullfuncref is not a Nullref"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null nofunc) is not a (ref null none)"
)

instantiate(`
Expand Down Expand Up @@ -145,7 +145,7 @@ function testNullexternref() {
(func (export "f") (result nullexternref) (ref.null extern)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Externref is not a Nullexternref"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null extern) is not a (ref null noextern)"
)

assert.throws(
Expand All @@ -154,7 +154,7 @@ function testNullexternref() {
(func (export "f") (result nullref) (ref.null noextern)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Nullexternref is not a Nullref"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null noextern) is not a (ref null none)"
)

instantiate(`
Expand Down
6 changes: 3 additions & 3 deletions JSTests/wasm/gc/arrays.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function testArrayDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Arrayref is not a (I32, mutable), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null array) is not a (ref null <array:0>), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);

assert.throws(
Expand All @@ -82,7 +82,7 @@ function testArrayDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (I32, mutable) is not a Funcref, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null <array:0>) is not a (ref null func), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down Expand Up @@ -400,7 +400,7 @@ function testArraySet() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: array.set arrayref to type ((I32, immutable)) expected arrayref, in function at index 0"
"WebAssembly.Module doesn't validate: array.set arrayref to type (ref null <array:0>) expected arrayref, in function at index 0"
);

{
Expand Down
10 changes: 5 additions & 5 deletions JSTests/wasm/gc/casts.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ function testFunctionCasts() {
drop))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: ref.cast to type I31ref expected a funcref"
"WebAssembly.Module doesn't validate: ref.cast to type (ref i31) expected a funcref"
);

assert.throws(
Expand All @@ -214,7 +214,7 @@ function testFunctionCasts() {
(ref.test (ref func) (ref.i31 (i32.const 42)))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: ref.test to type I31ref expected a funcref"
"WebAssembly.Module doesn't validate: ref.test to type (ref i31) expected a funcref"
);

assert.throws(
Expand Down Expand Up @@ -730,7 +730,7 @@ function testEqCasts() {
drop))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: ref.cast to type Funcref expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: ref.cast to type (ref null func) expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down Expand Up @@ -813,7 +813,7 @@ function testAnyCasts() {
drop))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: ref.cast to type Funcref expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: ref.cast to type (ref null func) expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down Expand Up @@ -958,7 +958,7 @@ function testNullCasts() {
drop))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: ref.cast to type Funcref expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: ref.cast to type (ref null func) expected a subtype of anyref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down
2 changes: 1 addition & 1 deletion JSTests/wasm/gc/const-exprs.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ async function testInvalidConstExprs() {
(global (export "g") i32 (struct.new 0 (i32.const 1))))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (I32, mutable) is not a I32"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref <struct:0>) is not a I32"
);

assert.throws(
Expand Down
2 changes: 1 addition & 1 deletion JSTests/wasm/gc/i31.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ function testI31Get() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: i31.get_s ref to type Externref expected I31ref, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: i31.get_s ref to type (ref null extern) expected I31ref, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
)

assert.throws(
Expand Down
2 changes: 1 addition & 1 deletion JSTests/wasm/gc/rec.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ function testRecDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. ((() -> [Ref], () -> [Ref]).1) is not a ((() -> [Ref], () -> [Ref]).0), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref <func:1>) is not a (ref <func:0>), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

instantiate(`
Expand Down
8 changes: 4 additions & 4 deletions JSTests/wasm/gc/structs.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ function testStructDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (I32, mutable) is not a Structref, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null <array:0>) is not a (ref null struct), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);

// Invalid subtyping for structref.
Expand All @@ -120,7 +120,7 @@ function testStructDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Structref is not a (), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null struct) is not a (ref null <struct:0>), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);
}

Expand Down Expand Up @@ -726,7 +726,7 @@ function testStructGet() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get invalid index: Structref, in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: struct.get invalid index: (ref null struct), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

// Test null checks.
Expand Down Expand Up @@ -1184,7 +1184,7 @@ function testStructTable() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: struct.get invalid index: Structref, in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: struct.get invalid index: (ref null struct), in function at index 0 (evaluating 'new WebAssembly.Module(binary)')"
);

// Invalid non-defaultable table type.
Expand Down
6 changes: 3 additions & 3 deletions JSTests/wasm/gc/sub.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ function testSubDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. ((((())(I32, mutable))(I32, mutable, I64, mutable))(I32, mutable, I64, mutable, F32, mutable)) is not a (((())(I32, mutable))(I32, mutable, F64, mutable)), in function at index 0"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref null <struct:4>) is not a (ref null <struct:3>), in function at index 0"
);

assert.throws(
Expand All @@ -245,7 +245,7 @@ function testSubDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (() -> []) is not a ((() -> [])() -> []), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. (ref <func:0>) is not a (ref <func:1>), in function at index 1 (evaluating 'new WebAssembly.Module(binary)')"
);

instantiate(`
Expand Down Expand Up @@ -470,7 +470,7 @@ function testSubDeclaration() {
)
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (((() -> []), ((<current-rec-group>.0)() -> [])).1), expected (((() -> []), (), ((<current-rec-group>.0)() -> [])).0), in function at index 1"
"WebAssembly.Module doesn't validate: argument type mismatch in call, got (ref null <func:4>), expected (ref null <func:0>), in function at index 1"
);

// Check cases with a single non-recursive sub in a recursion group.
Expand Down
31 changes: 31 additions & 0 deletions Source/JavaScriptCore/wasm/WasmFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,37 @@ inline bool isValidHeapTypeKind(TypeKind kind)
return false;
}

// FIXME: separating out heap types in wasm.json could be cleaner in the long term.
inline const char* heapTypeKindAsString(TypeKind kind)
{
ASSERT(isValidHeapTypeKind(kind));
switch (kind) {
case TypeKind::Funcref:
return "func";
case TypeKind::Externref:
return "extern";
case TypeKind::I31ref:
return "i31";
case TypeKind::Arrayref:
return "array";
case TypeKind::Structref:
return "struct";
case TypeKind::Eqref:
return "eq";
case TypeKind::Anyref:
return "any";
case TypeKind::Nullref:
return "none";
case TypeKind::Nullfuncref:
return "nofunc";
case TypeKind::Nullexternref:
return "noextern";
default:
RELEASE_ASSERT_NOT_REACHED();
return "";
}
}

inline bool isDefaultableType(Type type)
{
return !type.isRef();
Expand Down
45 changes: 44 additions & 1 deletion Source/JavaScriptCore/wasm/WasmFunctionParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,53 @@ class FunctionParser : public Parser<void>, public FunctionParserTypes<typename
WTFBreakpointTrap();

StringPrintStream out;
out.print("WebAssembly.Module doesn't validate: "_s, args...);
out.print("WebAssembly.Module doesn't validate: "_s, validationFailHelper(args)...);
return UnexpectedResult(out.toString());
}

template <typename Arg>
String WARN_UNUSED_RETURN validationFailHelper(const Arg& arg) const
{
if constexpr (std::is_same<Arg, Type>())
return typeToStringModuleRelative(arg);
else
return FailureHelper::makeString(arg);
}

String typeToStringModuleRelative(const Type& type) const
{
if (isRefType(type) && Options::useWebAssemblyTypedFunctionReferences()) {
StringPrintStream out;
out.print("(ref ");
if (type.isNullable())
out.print("null ");
if (typeIndexIsType(type.index))
out.print(heapTypeKindAsString(static_cast<TypeKind>(type.index)));
// FIXME: use name section if it exists to provide a nicer name.
else {
const auto& typeDefinition = TypeInformation::get(type.index);
const auto& expandedDefinition = typeDefinition.expand();
if (expandedDefinition.is<FunctionSignature>())
out.print("<func:");
else if (expandedDefinition.is<ArrayType>())
out.print("<array:");
else {
ASSERT(expandedDefinition.is<StructType>());
out.print("<struct:");
}
ASSERT(m_info.typeSignatures.contains(Ref { typeDefinition }));
out.print(m_info.typeSignatures.findIf([&](auto& sig) {
return sig.get() == typeDefinition;
}));
out.print(">");
}
out.print(")");
return out.toString();
}
return FailureHelper::makeString(type);
}


#define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \
if (UNLIKELY(condition)) \
return validationFail(__VA_ARGS__); \
Expand Down

0 comments on commit fd12481

Please sign in to comment.