[dart2wasm] Represent ffi compounds as actual Dart objects (they are views on the actual memory)

This fixes issues where structs are accessed dynamically
or simply accessed in a nullable fashion, e.g. with `MyStruct? foo`

dart2wasm does not yet support using structs-by-value as arguments
or return values when calling linear-memory wasm functions.

dart2wasm also doesn't support structs-by-value created in dart
that are backed by WasmGC memory (e.g. arrays).

Change-Id: I9b9c60002d2d32db3f235f1cec25fe3b35fcc17b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365140
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Martin Kustermann 2024-05-01 10:33:20 +00:00 committed by Commit Queue
parent 60a0cd6e1c
commit 24370b42b0
4 changed files with 13 additions and 47 deletions

View file

@ -737,7 +737,6 @@ class CodeGenerator extends ExpressionVisitor1<w.ValueType, w.ValueType>
if (translator.needsConversion(thisLocal!.type, thisType) &&
!(cls == translator.objectInfo.cls ||
cls == translator.ffiPointerClass ||
translator.isFfiCompound(cls) ||
translator.isWasmType(cls))) {
preciseThisLocal = addLocal(thisType);
b.local_get(thisLocal!);

View file

@ -182,26 +182,6 @@ class Intrinsifier {
return w.NumType.i64;
}
// _Compound._typedDataBase
if (cls == translator.ffiCompoundClass && name == '_typedDataBase') {
// A compound (subclass of Struct or Union) is represented by its i32
// address. The _typedDataBase field contains a Pointer pointing to the
// compound, whose representation is the same.
// TODO(https://dartbug.com/55083): Implement structs backed by TypedData.
codeGen.wrap(receiver, w.NumType.i32);
return w.NumType.i32;
}
// _Compound._offsetInBytes
if (cls == translator.ffiCompoundClass && name == '_offsetInBytes') {
// A compound (subclass of Struct or Union) is represented by its i32
// address. The _offsetInBytes field contains is always 0.
// This also breaks nested structs, which are currently not used.
// TODO(https://dartbug.com/55083): Implement structs backed by TypedData.
b.i64_const(0);
return w.NumType.i64;
}
// Pointer.address
if (cls == translator.ffiPointerClass && name == 'address') {
// A Pointer is represented by its i32 address.
@ -1120,15 +1100,6 @@ class Intrinsifier {
return w.RefType.def(arrayType, nullable: false);
}
// _Compound.#fromTypedDataBase
if (name == "#fromTypedDataBase") {
// A compound (subclass of Struct or Union) is represented by its i32
// address. The argument to the #fromTypedDataBase constructor is a
// Pointer, whose representation is the same.
codeGen.wrap(node.arguments.positional.single, w.NumType.i32);
return w.NumType.i32;
}
return null;
}

View file

@ -523,8 +523,6 @@ class Translator with KernelNodes {
bool isWasmType(Class cls) =>
cls == wasmTypesBaseClass || _hasSuperclass(cls, wasmTypesBaseClass);
bool isFfiCompound(Class cls) => _hasSuperclass(cls, ffiCompoundClass);
w.StorageType translateStorageType(DartType type) {
bool nullable = type.isPotentiallyNullable;
if (type is InterfaceType) {
@ -569,12 +567,6 @@ class Translator with KernelNodes {
return w.RefType.def(wasmType, nullable: nullable);
}
// FFI compound?
if (isFfiCompound(cls)) {
if (nullable) throw "FFI types can't be nullable";
return w.NumType.i32;
}
// Other built-in type?
w.StorageType? builtin = builtinTypes[cls];
if (builtin != null) {
@ -668,9 +660,6 @@ class Translator with KernelNodes {
if (builtin != null && builtin.isPrimitive) {
return builtin as w.ValueType;
}
if (isFfiCompound(cls)) {
return w.NumType.i32;
}
}
}
// TODO(joshualitt): We'd like to use the potential nullability here too,

View file

@ -143,12 +143,19 @@ void main() {
toggleBool();
Expect.equals(boolReturn(789), false);
final struct_ = getStruct();
Expect.equals(struct_.ref.x, 1.0);
Expect.equals(struct_.ref.y, 2);
clearStruct(struct_);
Expect.equals(struct_.ref.x, 0.0);
Expect.equals(struct_.ref.y, 0);
final Pointer<MyStruct> structPointer = getStruct();
final MyStruct struct = structPointer.ref;
Expect.equals(struct.x, 1.0);
Expect.equals(struct.y, 2);
// Structs are Dart objects that are views on top of actual memory (which may
// be backed by C memory or typed data). The view objects can be accessed
// dynamically.
final l = <dynamic>[struct, 1];
Expect.equals(l[int.parse('0')].x, 1.0);
clearStruct(structPointer);
Expect.equals(struct.x, 0.0);
Expect.equals(struct.y, 0);
Expect.equals(incrementChar(1), 2);
Expect.equals(incrementUnsignedChar(3), 4);