Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dcharkes/8d41b7def9bf82c74bf0b7f8a1d66a6c to your computer and use it in GitHub Desktop.
Save dcharkes/8d41b7def9bf82c74bf0b7f8a1d66a6c to your computer and use it in GitHub Desktop.
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// ============================================================================
// Already existing definitions in dart:ffi.
// ============================================================================
class NativeType {
const NativeType();
}
class _NativeInteger extends NativeType {
const _NativeInteger();
}
class Int32 extends _NativeInteger {
const Int32();
}
class Int64 extends _NativeInteger {
const Int64();
}
class Pointer<T extends NativeType> {
external factory Pointer.fromAddress(int ptr);
}
abstract class Struct extends NativeType {
final Pointer<Struct> _addressOf;
Struct() : _addressOf = nullptr;
Struct._fromPointer(this._addressOf);
}
extension StructPointer<T extends Struct> on Pointer<T> {
T get ref => null;
T operator [](int index) => null;
}
extension StructAddressOf<T extends Struct> on T {
/// Returns the address backing the reference.
Pointer<T> get addressOf => _addressOf as Pointer<T>;
}
final Pointer<Never> nullptr = Pointer.fromAddress(0);
// ============================================================================
// Design 1:
// Use 20 type arguments in AbiSpecificInteger and AbiSpecificStruct.
//
// Requires subtyping to get around 'An annotation (metadata) can't use type
// arguments.dart (annotation_with_type_arguments)' for struct fields.
//
// Pros:
// - All 20 type arguments are defined, can not be missing when compiling
// for a specific ABI.
//
// Cons:
// - Cannot omit type arguments if size is unknown for ABI. This might
// incentivize something wrong in. Which will not be caught at compile time
// but crash at runtime.
// - Not extensible. (Though we might not run into this any time soon.)
// ============================================================================
// Add to dart:ffi.
class AbiSpecificInteger<
Arm32Linux extends _NativeInteger,
Arm32MacOS extends _NativeInteger,
Arm32Windows extends _NativeInteger,
Arm32Android extends _NativeInteger,
Arm32iOS extends _NativeInteger,
Arm64Linux extends _NativeInteger,
Arm64MacOS extends _NativeInteger,
Arm64Windows extends _NativeInteger,
Arm64Android extends _NativeInteger,
Arm64iOS extends _NativeInteger,
IA32Linux extends _NativeInteger,
IA32MacOS extends _NativeInteger,
IA32Windows extends _NativeInteger,
IA32Android extends _NativeInteger,
IA32iOS extends _NativeInteger,
X64Linux extends _NativeInteger,
X64MacOS extends _NativeInteger,
X64Windows extends _NativeInteger,
X64Android extends _NativeInteger,
X64iOS extends _NativeInteger> extends NativeType {
const AbiSpecificInteger();
}
class AbiSpecificStruct<
Arm32Linux extends Struct,
Arm32MacOS extends Struct,
Arm32Windows extends Struct,
Arm32Android extends Struct,
Arm32iOS extends Struct,
Arm64Linux extends Struct,
Arm64MacOS extends Struct,
Arm64Windows extends Struct,
Arm64Android extends Struct,
Arm64iOS extends Struct,
IA32Linux extends Struct,
IA32MacOS extends Struct,
IA32Windows extends Struct,
IA32Android extends Struct,
IA32iOS extends Struct,
X64Linux extends Struct,
X64MacOS extends Struct,
X64Windows extends Struct,
X64Android extends Struct,
X64iOS extends Struct> extends Struct {}
extension AbiSpecificIntegerPointer on Pointer<AbiSpecificInteger> {
int get value => 0;
void set value(int value) {}
int operator [](int index) => 0;
void operator []=(int index, int value) {}
}
// User definitions.
class IntPtr extends AbiSpecificInteger<
Int32,
Int32,
Int32,
Int32,
Int32,
Int64,
Int64,
Int64,
Int64,
Int64,
Int32,
Int32,
Int32,
Int32,
Int32,
Int64,
Int64,
Int64,
Int64,
Int64> {
const IntPtr();
}
class MyStructA extends Struct {
int a;
}
class MyStructB extends Struct {
int a;
int b;
}
class MyStruct extends AbiSpecificStruct<
MyStructA,
MyStructA,
MyStructA,
MyStructA,
MyStructB,
MyStructA,
MyStructA,
MyStructA,
MyStructA,
MyStructB,
MyStructA,
MyStructA,
MyStructA,
MyStructA,
MyStructB,
MyStructA,
MyStructA,
MyStructA,
MyStructA,
MyStructB> {
int a;
int b;
}
// User uses.
void useIntPtr(Pointer<IntPtr> p) {
int a = p.value;
print(a);
}
void useStruct(MyStruct myStruct, Pointer<MyStruct> pMyStruct) {
final p = myStruct.addressOf;
final s = p.ref;
print(s);
}
class UseInStruct extends Struct {
@IntPtr()
int x;
Pointer<MyStruct> nestedStructPointer;
MyStruct nestedStruct;
}
// ============================================================================
// Design 2:
// Use 20 type markers for 20 implements clauses.
//
// Pros:
// - Can omit type arguments if size is unknown for ABI.
// - Extensible. (Though we might not run into this any time soon.)
//
// Cons:
// - Not all 20 type arguments are defined, might be missing when compiling
// for a specific ABI.
// - We need manual type checks for not passing structs to AbiSpecificInteger2
// and for not passing integers to AbiSpecificStruct2 implement clauses.
// ============================================================================
// Add to dart:ffi.
class AbiSpecificInteger2 extends NativeType {
const AbiSpecificInteger2();
}
class AbiSpecificStruct2 extends Struct {}
class Arm32Linux<T extends NativeType> extends NativeType {}
class Arm32MacOS<T extends NativeType> extends NativeType {}
class Arm32Windows<T extends NativeType> extends NativeType {}
class Arm32Android<T extends NativeType> extends NativeType {}
class Arm32iOS<T extends NativeType> extends NativeType {}
class Arm64Linux<T extends NativeType> extends NativeType {}
class Arm64MacOS<T extends NativeType> extends NativeType {}
class Arm64Windows<T extends NativeType> extends NativeType {}
class Arm64Android<T extends NativeType> extends NativeType {}
class Arm64iOS<T extends NativeType> extends NativeType {}
class IA32Linux<T extends NativeType> extends NativeType {}
class IA32MacOS<T extends NativeType> extends NativeType {}
class IA32Windows<T extends NativeType> extends NativeType {}
class IA32Android<T extends NativeType> extends NativeType {}
class IA32iOS<T extends NativeType> extends NativeType {}
class X64Linux<T extends NativeType> extends NativeType {}
class X64MacOS<T extends NativeType> extends NativeType {}
class X64Windows<T extends NativeType> extends NativeType {}
class X64Android<T extends NativeType> extends NativeType {}
class X64iOS<T extends NativeType> extends NativeType {}
extension AbiSpecificIntegerPointer2 on Pointer<AbiSpecificInteger2> {
int get value => 0;
void set value(int value) {}
int operator [](int index) => 0;
void operator []=(int index, int value) {}
}
// User definitions.
class IntPtr2 extends AbiSpecificInteger2
implements
Arm32Linux<Int32>, // Does not catch Arm32Linux<MyStruct>
Arm32MacOS<Int32>,
Arm32Windows<Int32>,
Arm32Android<Int32>,
Arm32iOS<Int32>,
Arm64Linux<Int64>,
Arm64MacOS<Int64>,
Arm64Windows<Int64>,
Arm64Android<Int64>,
Arm64iOS<Int64>,
IA32Linux<Int32>,
IA32MacOS<Int32>,
IA32Windows<Int32>,
IA32Android<Int32>,
IA32iOS<Int32>,
X64Linux<Int64>,
X64MacOS<Int64>,
X64Windows<Int64>,
X64Android<Int64>,
X64iOS<Int64> {
const IntPtr2();
}
class MyStruct2A extends Struct {
int a;
}
class MyStruct2B extends Struct {
int a;
int b;
}
class MyStruct2 extends AbiSpecificStruct2
implements
Arm32Linux<MyStruct2A>, // Does not catch Arm32Linux<Int32>
Arm32MacOS<MyStruct2A>,
Arm32Windows<MyStruct2A>,
Arm32Android<MyStruct2A>,
Arm32iOS<MyStruct2B>,
Arm64Linux<MyStruct2A>,
Arm64MacOS<MyStruct2A>,
Arm64Windows<MyStruct2A>,
Arm64Android<MyStruct2A>,
Arm64iOS<MyStruct2B>,
IA32Linux<MyStruct2A>,
IA32MacOS<MyStruct2A>,
IA32Windows<MyStruct2A>,
IA32Android<MyStruct2A>,
IA32iOS<MyStruct2B>,
X64Linux<MyStruct2A>,
X64MacOS<MyStruct2A>,
X64Windows<MyStruct2A>,
X64Android<MyStruct2A>,
X64iOS<MyStruct2B> {
int a;
int b;
}
// User uses.
void useIntPtr2(Pointer<IntPtr2> p) {
int a = p.value;
print(a);
}
void useStruct2(MyStruct2 myStruct, Pointer<MyStruct2> pMyStruct) {
final p = myStruct.addressOf;
final s = p.ref;
print(s);
}
class UseInStruct2 extends Struct {
@IntPtr2()
int x;
Pointer<MyStruct2> nestedStructPointer;
MyStruct2 nestedStruct;
}
// ============================================================================
// Design 3:
// Use 20 type markers for 20 implements clauses. And make them integer and
// struct specific.
//
// Pros:
// - Can omit type arguments if size is unknown for ABI.
// - Extensible. (Though we might not run into this any time soon.)
//
// Cons:
// - Not all 20 type arguments are defined, might be missing when compiling
// for a specific ABI.
// - Duplicates the ABI type markers for structs and integers.
// ============================================================================
// Add to dart:ffi.
class AbiSpecificInteger3 extends NativeType {}
class AbiSpecificStruct3 extends NativeType {}
class IntegerArm32Linux<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerArm32MacOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerArm32Windows<T extends _NativeInteger>
extends AbiSpecificInteger3 {}
class IntegerArm32Android<T extends _NativeInteger>
extends AbiSpecificInteger3 {}
class IntegerArm32iOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerArm64Linux<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerArm64MacOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerArm64Windows<T extends _NativeInteger>
extends AbiSpecificInteger3 {}
class IntegerArm64Android<T extends _NativeInteger>
extends AbiSpecificInteger3 {}
class IntegerArm64iOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerIA32Linux<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerIA32MacOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerIA32Windows<T extends _NativeInteger> extends AbiSpecificInteger3 {
}
class IntegerIA32Android<T extends _NativeInteger> extends AbiSpecificInteger3 {
}
class IntegerIA32iOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerX64Linux<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerX64MacOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerX64Windows<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerX64Android<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class IntegerX64iOS<T extends _NativeInteger> extends AbiSpecificInteger3 {}
class StructArm32Linux<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm32MacOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm32Windows<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm32Android<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm32iOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm64Linux<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm64MacOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm64Windows<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm64Android<T extends Struct> extends AbiSpecificStruct3 {}
class StructArm64iOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructIA32Linux<T extends Struct> extends AbiSpecificStruct3 {}
class StructIA32MacOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructIA32Windows<T extends Struct> extends AbiSpecificStruct3 {}
class StructIA32Android<T extends Struct> extends AbiSpecificStruct3 {}
class StructIA32iOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructX64Linux<T extends Struct> extends AbiSpecificStruct3 {}
class StructX64MacOS<T extends Struct> extends AbiSpecificStruct3 {}
class StructX64Windows<T extends Struct> extends AbiSpecificStruct3 {}
class StructX64Android<T extends Struct> extends AbiSpecificStruct3 {}
class StructX64iOS<T extends Struct> extends AbiSpecificStruct3 {}
extension AbiSpecificIntegerPointer3 on Pointer<AbiSpecificInteger3> {
int get value => 0;
void set value(int value) {}
int operator [](int index) => 0;
void operator []=(int index, int value) {}
}
// User definitions.
class IntPtr3
implements
IntegerArm32Linux<Int32>,
IntegerArm32MacOS<Int32>,
IntegerArm32Windows<Int32>,
IntegerArm32Android<Int32>,
IntegerArm32iOS<Int32>,
IntegerArm64Linux<Int64>,
IntegerArm64MacOS<Int64>,
IntegerArm64Windows<Int64>,
IntegerArm64Android<Int64>,
IntegerArm64iOS<Int64>,
IntegerIA32Linux<Int32>,
IntegerIA32MacOS<Int32>,
IntegerIA32Windows<Int32>,
IntegerIA32Android<Int32>,
IntegerIA32iOS<Int32>,
IntegerX64Linux<Int64>,
IntegerX64MacOS<Int64>,
IntegerX64Windows<Int64>,
IntegerX64Android<Int64>,
IntegerX64iOS<Int64> {
const IntPtr3();
}
class MyStruct3A extends Struct {
int a;
}
class MyStruct3B extends Struct {
int a;
int b;
}
// Needs to extend struct to work with the addressof impl.
class MyStruct3 extends Struct
implements
StructArm32Linux<MyStruct3A>,
StructArm32MacOS<MyStruct3A>,
StructArm32Windows<MyStruct3A>,
StructArm32Android<MyStruct3A>,
StructArm32iOS<MyStruct3B>,
StructArm64Linux<MyStruct3A>,
StructArm64MacOS<MyStruct3A>,
StructArm64Windows<MyStruct3A>,
StructArm64Android<MyStruct3A>,
StructArm64iOS<MyStruct3B>,
StructIA32Linux<MyStruct3A>,
StructIA32MacOS<MyStruct3A>,
StructIA32Windows<MyStruct3A>,
StructIA32Android<MyStruct3A>,
StructIA32iOS<MyStruct3B>,
StructX64Linux<MyStruct3A>,
StructX64MacOS<MyStruct3A>,
StructX64Windows<MyStruct3A>,
StructX64Android<MyStruct3A>,
StructX64iOS<MyStruct3B> {
int a;
int b;
}
// User uses.
void useIntPtr3(Pointer<IntPtr3> p) {
int a = p.value;
print(a);
}
void useStruct3(MyStruct3 myStruct, Pointer<MyStruct3> pMyStruct) {
final p = myStruct.addressOf;
final s = p.ref;
print(s);
}
class UseInStruct3 extends Struct {
@IntPtr3()
int x;
Pointer<MyStruct3> nestedStructPointer;
MyStruct3 nestedStruct;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment