Skip to content

[vm/ffi] Struct.addressOf Pointer<Struct>.load() signatures #37284

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

Closed
dcharkes opened this issue Jun 17, 2019 · 9 comments
Closed

[vm/ffi] Struct.addressOf Pointer<Struct>.load() signatures #37284

dcharkes opened this issue Jun 17, 2019 · 9 comments
Assignees
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi type-design

Comments

@dcharkes
Copy link
Contributor

What should the API be to get from a Struct to a Pointer and vice versa?

Struct to Pointer: Struct.addressOf

Field of struct

abstract class Struct<S extends NativeType> extends NativeType {
  // If the struct is backed by native memory, returns the address of the
  // reference. If the struct is backed by Dart-allocated memory, returns null.
  final Pointer<S> addressOf;
}

class TestStruct4 extends ffi.Struct<TestStruct4> {
  @ffi.Double()
  double z;
}

Pros:

  • concise access through . notation
  • no specification of type argument on invocation

Con:

  • a type argument for Struct

Top level function

Pointer<S> addressOf<S extends Struct>(S s) {
  // ...
}

Pro:

  • no type argument on Struct
  • specify type argument at every invocation

Con:

  • no . notation

Method

abstract class Struct extends NativeType {
  // If the struct is backed by native memory, returns the address of the
  // reference. If the struct is backed by Dart-allocated memory, returns null.
 Pointer<S> addressOf<S>();
}

Pro:

  • no type argument on Struct
  • . notation

Con:

  • specify type argument at every invocation

Extension method

extension MyExtension on S extends Struct {
  Pointer<S> addressOf() {
    // ...
  }
}

@lrhn @leafpetersen is this supported by our design for extension methods?

Pro:

  • no type argument on Struct
  • . notation
  • no type argument on invocation

This has the least amount of cons, if this would be supported.

Pointer to Struct: Pointer.load

Similar argument for getting from a Pointer to a Struct by using load.

Extension method

extension MyExtension on Pointer<S extends Struct> {
  S load();
}

Pro:

  • No type argument on invacation

Method

This is the current API.

Pointer<N extends NativeType> {
  // T = int when N = Int32 etc
  // T = N when N is a subtype of Struct
  T load<T>();
}

Con:

  • specifying type argument on invocation

Pre-extension methods

If the extension methods will be supported in the future, what should we decide for now - with the easiest path of migrating to extension methods later?

@dcharkes dcharkes added area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi type-design labels Jun 17, 2019
@ds84182
Copy link
Contributor

ds84182 commented Jun 18, 2019

Honestly, I feel that extension methods on all pointer types would be the best, e.g.

// Say, NativeInt is a superclass to FFI's Int8, Int16, etc.
extension IntPointerExt on Pointer<T extends NativeInt> {
  int load();
  void store(int value);
}

extension DoublePointerExt on Pointer<T extends NativeDouble> {
  // ...
}

This removes the need for the type parameter on load, which seems redundant right now (I already have a Pointer, why do I need to specify that I'm loading an int?).

The only unfortunate bit is that extension methods are... NYI. But I think it would be a great change to do in the future (although breaking).

@leafpetersen
Copy link
Member

Yes, that should work fine with extension methods. It would look like this though:

extension MyExtension<S extends Struct> on S {
  Pointer<S> addressOf() {
    // ...
  }
}

This would apply to any object with a type which extends Struct.

@dcharkes
Copy link
Contributor Author

@ds84182 that is exactly what we are aiming for! Unfortunately we have to wait until extension methods get implemented in Dart. Until then we'll have to use T load<T>() and store(Object value).

@leafpetersen
Copy link
Member

Plan is for extension methods to be ready in Q3, so not too far out.

@sjindel-google
Copy link
Contributor

sjindel-google commented Jun 19, 2019

We could use this API today to have a clean API until extension methods, with a non-breaking migration path:

class Pointer<T extends NativeType> {
  // ...
  dynamic get val;  // FE ensure T !extends Struct
  void set val(dynamic val);  // FE ensure T !extends Struct
  T get ref;  // FE only allows when T extends Struct
  // ...
}

@leafpetersen : Will we get extension getters and setters?

@sjindel-google
Copy link
Contributor

We should change Pointer.address/fromAddress to Pointer.toInt/fromInt to avoid confusion with addressOf.

@dcharkes
Copy link
Contributor Author

Some documentation for the reasoning behind the above suggestion.

Separation ref and val

.ref and .val are semantically different. Using .val on Pointer<T extends NativeInt> and Pointer<T extends NativeDouble> directly stores and loads data. Using .ref on Pointer<T extends Struct> gives a reference which allows one to load and store fields of the struct.

Use of dynamic

Using dynamic for .val and T for .ref (previously load<T>() and store(Object o)) allows us to omit type arguments on code at the use site now and when we replace them with extension methods.

@leafpetersen
Copy link
Member

@leafpetersen : Will we get extension getters and setters?

Yes.

@sjindel-google
Copy link
Contributor

I think we've agreed on the design here -- the implementation remains in #37361.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends. library-ffi type-design
Projects
None yet
Development

No branches or pull requests

4 participants