[vm/ffi] Revamp structs API to separate structs and pointers #37229
Labels
area-vm
Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends.
library-ffi
Problem
The representation of structs in the existing API leaves much to be desired:
C:
Dart:
Struct classes are defined as pointers, which conflates the concepts of the struct as a composite value and as a pointer to memory. When by-value structs are supported, there would be no (type-level) way to differentiate between value structs and pointers to structs. For example, the following two signatures in C would correspond to the same signature in Dart:
C:
Dart:
Since struct classes are pointers, they feature the
load()
andstore()
operations. Unfortunately,load()
andstore()
load/store a pointer recursively (since the target type of the load isStr
, which is itself a pointer), and that's incorrect. Rather,load()
should return a reference to the struct, as in C++:Dart:
C:
Pointer has a corresponding storage class in the VM defined (
RawPointer
), but can be subclassed by user code. We have to prohibit (non-FFI) fields from the subclasses to ensure the memory layout is consistent, but this design works against the VM architecture and there are complications with serialization and GC.New API
Defining structs
Struct definitions are changed as follows:
The type argument to
Struct<S>
will be removed when we can defineaddressOf
as an extension method onStruct
.Changes to Pointer
We will disallow subclassing or implementing Pointer (#35782). We will also replace
load()
andstore()
to reduce syntactic overhead for working with structs and distinguish between loading a value vs. a reference from pointers (#37284):val
andref
will become extension methods when extension methods are supported.Accessing fields
Instead of representing pointers, instances of the
Struct
subclasses will represent references. A reference is always backed by a pointer, but exposes additional methods on top of it.load()
(ref
) against a pointer to a struct returns a reference backed by the receiving pointer. When "by-value" structs are supported, it will also be possible tostore()
(val=
) an entire struct into a pointer by passing in a reference. For example:Dart:
Other changes
This change in struct representation allows us to fix other warts of our API:
allocate
andfromAddress
will be constructors, since they cannot return a more precise type thanPointer<T>
.Pointer
will not be allowed ([vm/ffi] Allow subclassing Pointer? #35782).NativeType
s (besidesStruct
itself) will not be allowed ([vm/ffi] struct inheritance #37298).ref
([vm/ffi] Constructing struct classes from load()/ref in new structs API #37363).Pointer
s will be a valuenullptr
separate from Dart'snull
, with an address of 0 ([vm/ffi] Pointer<Null> #37362, dart:ffi canonical representation of C null pointer in Dart #35756).final
fields in structs.Migration
There are other use-cases for extending
Pointer
besides structs, which will need to be handled differently in the new API. For example, theCString
class currently looks like:In the new API, this might instead look like:
When extension methods are available, this could be written more gracefully as follows:
The text was updated successfully, but these errors were encountered: