diff --git a/example/dart/lib/ICU4XLocale.g.dart b/example/dart/lib/ICU4XLocale.g.dart index 8e4670483..a0d1e02c5 100644 --- a/example/dart/lib/ICU4XLocale.g.dart +++ b/example/dart/lib/ICU4XLocale.g.dart @@ -20,7 +20,7 @@ final class ICU4XLocale implements ffi.Finalizable { /// Construct an [`ICU4XLocale`] from a locale identifier represented as a string. factory ICU4XLocale(String name) { final alloc = ffi2.Arena(); - final nameSlice = _SliceFfi2Utf8._fromDart(name, alloc); + final nameSlice = _SliceUtf8._fromDart(name, alloc); final result = _ICU4XLocale_new(nameSlice._bytes, nameSlice._length); alloc.releaseAll(); return ICU4XLocale._(result); diff --git a/example/dart/lib/lib.g.dart b/example/dart/lib/lib.g.dart index 228fcfa22..5b7c2a62b 100644 --- a/example/dart/lib/lib.g.dart +++ b/example/dart/lib/lib.g.dart @@ -54,7 +54,7 @@ final class _ResultVoidVoid extends ffi.Struct { external bool isOk; } -final class _SliceFfi2Utf8 extends ffi.Struct { +final class _SliceUtf8 extends ffi.Struct { external ffi.Pointer _bytes; @ffi.Size() @@ -63,13 +63,25 @@ final class _SliceFfi2Utf8 extends ffi.Struct { /// Produces a slice from a Dart object. The Dart object's data is copied into the given allocator /// as it cannot be borrowed directly, and gets freed with the slice object. // ignore: unused_element - static _SliceFfi2Utf8 _fromDart(String value, ffi.Allocator allocator) { - final pointer = allocator<_SliceFfi2Utf8>(); + static _SliceUtf8 _fromDart(String value, ffi.Allocator allocator) { + final pointer = allocator<_SliceUtf8>(); final slice = pointer.ref; - final units = Utf8Encoder().convert(value); - slice._length = units.length; + slice._length = 0; + for (var rune in value.runes) { + if (rune < 0x80) { + slice._length += 1; + } else if (rune < 0x800) { + slice._length += 2; + } else if (rune < 0x10000) { + slice._length += 3; + } else { + slice._length += 4; + } + } + // https://github.com/dart-lang/ffi/issues/223 slice._bytes = allocator(slice._length).cast(); - slice._bytes.cast().asTypedList(slice._length).setAll(0, units); + // https://github.com/dart-lang/sdk/issues/49470 + slice._bytes.cast().asTypedList(slice._length).setAll(0, Utf8Encoder().convert(value)); return slice; } @@ -79,7 +91,7 @@ final class _SliceFfi2Utf8 extends ffi.Struct { // This is expensive @override bool operator ==(Object other) { - if (other is! _SliceFfi2Utf8 || other._length != _length) { + if (other is! _SliceUtf8 || other._length != _length) { return false; } diff --git a/feature_tests/dart/lib/BorrowedFields.g.dart b/feature_tests/dart/lib/BorrowedFields.g.dart index 4c2d98c95..3da8e1bc0 100644 --- a/feature_tests/dart/lib/BorrowedFields.g.dart +++ b/feature_tests/dart/lib/BorrowedFields.g.dart @@ -6,8 +6,8 @@ part of 'lib.g.dart'; final class _BorrowedFieldsFfi extends ffi.Struct { - external _SliceFfiUtf16 a; - external _SliceFfi2Utf8 b; + external _SliceUtf16 a; + external _SliceUtf8 b; } final class BorrowedFields { @@ -27,7 +27,7 @@ final class BorrowedFields { set a(String a) { final alloc = ffi2.calloc; alloc.free(_underlying.a._bytes); - final aSlice = _SliceFfiUtf16._fromDart(a, alloc); + final aSlice = _SliceUtf16._fromDart(a, alloc); _underlying.a = aSlice; } @@ -35,7 +35,7 @@ final class BorrowedFields { set b(String b) { final alloc = ffi2.calloc; alloc.free(_underlying.b._bytes); - final bSlice = _SliceFfi2Utf8._fromDart(b, alloc); + final bSlice = _SliceUtf8._fromDart(b, alloc); _underlying.b = bSlice; } diff --git a/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart b/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart index 0d39c977b..5e7380fc2 100644 --- a/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart +++ b/feature_tests/dart/lib/BorrowedFieldsReturning.g.dart @@ -6,7 +6,7 @@ part of 'lib.g.dart'; final class _BorrowedFieldsReturningFfi extends ffi.Struct { - external _SliceFfi2Utf8 bytes; + external _SliceUtf8 bytes; } final class BorrowedFieldsReturning { @@ -26,7 +26,7 @@ final class BorrowedFieldsReturning { set bytes(String bytes) { final alloc = ffi2.calloc; alloc.free(_underlying.bytes._bytes); - final bytesSlice = _SliceFfi2Utf8._fromDart(bytes, alloc); + final bytesSlice = _SliceUtf8._fromDart(bytes, alloc); _underlying.bytes = bytesSlice; } diff --git a/feature_tests/dart/lib/Float64Vec.g.dart b/feature_tests/dart/lib/Float64Vec.g.dart index 0f52e0033..6fed261ac 100644 --- a/feature_tests/dart/lib/Float64Vec.g.dart +++ b/feature_tests/dart/lib/Float64Vec.g.dart @@ -16,7 +16,7 @@ final class Float64Vec implements ffi.Finalizable { factory Float64Vec(Float64List v) { final alloc = ffi2.Arena(); - final vSlice = _SliceFfiDouble._fromDart(v, alloc); + final vSlice = _SliceDouble._fromDart(v, alloc); final result = _Float64Vec_new(vSlice._bytes, vSlice._length); alloc.releaseAll(); return Float64Vec._(result); @@ -29,7 +29,7 @@ final class Float64Vec implements ffi.Finalizable { void fillSlice(Float64List v) { final alloc = ffi2.Arena(); - final vSlice = _SliceFfiDouble._fromDart(v, alloc); + final vSlice = _SliceDouble._fromDart(v, alloc); _Float64Vec_fill_slice(_underlying, vSlice._bytes, vSlice._length); alloc.releaseAll(); } @@ -41,7 +41,7 @@ final class Float64Vec implements ffi.Finalizable { void setValue(Float64List newSlice) { final alloc = ffi2.Arena(); - final newSliceSlice = _SliceFfiDouble._fromDart(newSlice, alloc); + final newSliceSlice = _SliceDouble._fromDart(newSlice, alloc); _Float64Vec_set_value(_underlying, newSliceSlice._bytes, newSliceSlice._length); alloc.releaseAll(); } diff --git a/feature_tests/dart/lib/Foo.g.dart b/feature_tests/dart/lib/Foo.g.dart index ab44942c9..c45f6434a 100644 --- a/feature_tests/dart/lib/Foo.g.dart +++ b/feature_tests/dart/lib/Foo.g.dart @@ -16,7 +16,7 @@ final class Foo implements ffi.Finalizable { factory Foo(String x) { final alloc = ffi2.Arena(); - final xSlice = _SliceFfi2Utf8._fromDart(x, alloc); + final xSlice = _SliceUtf8._fromDart(x, alloc); final result = _Foo_new(xSlice._bytes, xSlice._length); alloc.releaseAll(); return Foo._(result); @@ -39,7 +39,7 @@ final class Foo implements ffi.Finalizable { factory Foo.static_(String x) { final alloc = ffi2.Arena(); - final xSlice = _SliceFfi2Utf8._fromDart(x, alloc); + final xSlice = _SliceUtf8._fromDart(x, alloc); final result = _Foo_new_static(xSlice._bytes, xSlice._length); alloc.releaseAll(); return Foo._(result); diff --git a/feature_tests/dart/lib/MyString.g.dart b/feature_tests/dart/lib/MyString.g.dart index 5a02a5093..d81f3ed7a 100644 --- a/feature_tests/dart/lib/MyString.g.dart +++ b/feature_tests/dart/lib/MyString.g.dart @@ -16,7 +16,7 @@ final class MyString implements ffi.Finalizable { factory MyString(String v) { final alloc = ffi2.Arena(); - final vSlice = _SliceFfi2Utf8._fromDart(v, alloc); + final vSlice = _SliceUtf8._fromDart(v, alloc); final result = _MyString_new(vSlice._bytes, vSlice._length); alloc.releaseAll(); return MyString._(result); @@ -29,7 +29,7 @@ final class MyString implements ffi.Finalizable { void setStr(String newStr) { final alloc = ffi2.Arena(); - final newStrSlice = _SliceFfi2Utf8._fromDart(newStr, alloc); + final newStrSlice = _SliceUtf8._fromDart(newStr, alloc); _MyString_set_str(_underlying, newStrSlice._bytes, newStrSlice._length); alloc.releaseAll(); } diff --git a/feature_tests/dart/lib/Opaque.g.dart b/feature_tests/dart/lib/Opaque.g.dart index d7168906d..ad5074b5f 100644 --- a/feature_tests/dart/lib/Opaque.g.dart +++ b/feature_tests/dart/lib/Opaque.g.dart @@ -38,7 +38,23 @@ final class Opaque implements ffi.Finalizable { _capi, _MyStructFfi)>>('Opaque_assert_struct') .asFunction, _MyStructFfi)>(isLeaf: true); - static final int returnsUsize = _capi>('Opaque_returns_usize').asFunction(isLeaf: true)(); + static final int returnsUsize = () { + final result = _Opaque_returns_usize(); + return result; + }(); - static final ImportedStruct returnsImported = _capi>('Opaque_returns_imported').asFunction<_ImportedStructFfi Function()>(isLeaf: true)(); + // ignore: non_constant_identifier_names + static final _Opaque_returns_usize = + _capi>('Opaque_returns_usize') + .asFunction(isLeaf: true); + + static final ImportedStruct returnsImported = () { + final result = _Opaque_returns_imported(); + return ImportedStruct._(result); + }(); + + // ignore: non_constant_identifier_names + static final _Opaque_returns_imported = + _capi>('Opaque_returns_imported') + .asFunction<_ImportedStructFfi Function()>(isLeaf: true); } diff --git a/feature_tests/dart/lib/OptionOpaque.g.dart b/feature_tests/dart/lib/OptionOpaque.g.dart index f69d2b090..39f494cf6 100644 --- a/feature_tests/dart/lib/OptionOpaque.g.dart +++ b/feature_tests/dart/lib/OptionOpaque.g.dart @@ -24,11 +24,35 @@ final class OptionOpaque implements ffi.Finalizable { _capi Function(ffi.Int32)>>('OptionOpaque_new') .asFunction Function(int)>(isLeaf: true); - static final OptionOpaque? none = _capi Function()>>('OptionOpaque_new_none').asFunction Function()>(isLeaf: true)(); + static final OptionOpaque? none = () { + final result = _OptionOpaque_new_none(); + return result.address == 0 ? null : OptionOpaque._(result); + }(); + + // ignore: non_constant_identifier_names + static final _OptionOpaque_new_none = + _capi Function()>>('OptionOpaque_new_none') + .asFunction Function()>(isLeaf: true); - static final OptionStruct struct = _capi>('OptionOpaque_new_struct').asFunction<_OptionStructFfi Function()>(isLeaf: true)(); + static final OptionStruct struct = () { + final result = _OptionOpaque_new_struct(); + return OptionStruct._(result); + }(); - static final OptionStruct structNones = _capi>('OptionOpaque_new_struct_nones').asFunction<_OptionStructFfi Function()>(isLeaf: true)(); + // ignore: non_constant_identifier_names + static final _OptionOpaque_new_struct = + _capi>('OptionOpaque_new_struct') + .asFunction<_OptionStructFfi Function()>(isLeaf: true); + + static final OptionStruct structNones = () { + final result = _OptionOpaque_new_struct_nones(); + return OptionStruct._(result); + }(); + + // ignore: non_constant_identifier_names + static final _OptionOpaque_new_struct_nones = + _capi>('OptionOpaque_new_struct_nones') + .asFunction<_OptionStructFfi Function()>(isLeaf: true); void assertInteger(int i) { _OptionOpaque_assert_integer(_underlying, i); @@ -40,7 +64,7 @@ final class OptionOpaque implements ffi.Finalizable { .asFunction, int)>(isLeaf: true); static bool optionOpaqueArgument(OptionOpaque? arg) { - final result = _OptionOpaque_option_opaque_argument(arg == null ? ffi.Pointer.fromAddress(0) ? arg._underlying); + final result = _OptionOpaque_option_opaque_argument(arg == null ? ffi.Pointer.fromAddress(0) : arg._underlying); return result; } diff --git a/feature_tests/dart/lib/OptionStruct.g.dart b/feature_tests/dart/lib/OptionStruct.g.dart index 416f4e70b..ed0ed78bf 100644 --- a/feature_tests/dart/lib/OptionStruct.g.dart +++ b/feature_tests/dart/lib/OptionStruct.g.dart @@ -28,12 +28,12 @@ final class OptionStruct { OptionOpaque? get a => _underlying.a.address == 0 ? null : OptionOpaque._(_underlying.a); set a(OptionOpaque? a) { - _underlying.a = a == null ? ffi.Pointer.fromAddress(0) ? a._underlying; + _underlying.a = a == null ? ffi.Pointer.fromAddress(0) : a._underlying; } OptionOpaqueChar? get b => _underlying.b.address == 0 ? null : OptionOpaqueChar._(_underlying.b); set b(OptionOpaqueChar? b) { - _underlying.b = b == null ? ffi.Pointer.fromAddress(0) ? b._underlying; + _underlying.b = b == null ? ffi.Pointer.fromAddress(0) : b._underlying; } int get c => _underlying.c; @@ -43,7 +43,7 @@ final class OptionStruct { OptionOpaque? get d => _underlying.d.address == 0 ? null : OptionOpaque._(_underlying.d); set d(OptionOpaque? d) { - _underlying.d = d == null ? ffi.Pointer.fromAddress(0) ? d._underlying; + _underlying.d = d == null ? ffi.Pointer.fromAddress(0) : d._underlying; } @override diff --git a/feature_tests/dart/lib/lib.g.dart b/feature_tests/dart/lib/lib.g.dart index fc8b5167e..0f24bf3ce 100644 --- a/feature_tests/dart/lib/lib.g.dart +++ b/feature_tests/dart/lib/lib.g.dart @@ -130,8 +130,8 @@ final class _ResultVoidOpaque extends ffi.Struct { external bool isOk; } -final class _SliceFfi2Utf8 extends ffi.Struct { - external ffi.Pointer _bytes; +final class _SliceDouble extends ffi.Struct { + external ffi.Pointer _bytes; @ffi.Size() external int _length; @@ -139,28 +139,27 @@ final class _SliceFfi2Utf8 extends ffi.Struct { /// Produces a slice from a Dart object. The Dart object's data is copied into the given allocator /// as it cannot be borrowed directly, and gets freed with the slice object. // ignore: unused_element - static _SliceFfi2Utf8 _fromDart(String value, ffi.Allocator allocator) { - final pointer = allocator<_SliceFfi2Utf8>(); + static _SliceDouble _fromDart(Float64List value, ffi.Allocator allocator) { + final pointer = allocator<_SliceDouble>(); final slice = pointer.ref; - final units = Utf8Encoder().convert(value); - slice._length = units.length; - slice._bytes = allocator(slice._length).cast(); - slice._bytes.cast().asTypedList(slice._length).setAll(0, units); + slice._length = value.length; + slice._bytes = allocator(slice._length); + slice._bytes.asTypedList(slice._length).setAll(0, value); return slice; } // ignore: unused_element - String get _asDart => Utf8Decoder().convert(_bytes.cast().asTypedList(_length)); + Float64List get _asDart => _bytes.asTypedList(_length); // This is expensive @override bool operator ==(Object other) { - if (other is! _SliceFfi2Utf8 || other._length != _length) { + if (other is! _SliceDouble || other._length != _length) { return false; } for (var i = 0; i < _length; i++) { - if (other._bytes.cast()[i] != _bytes.cast()[i]) { + if (other._bytes[i] != _bytes[i]) { return false; } } @@ -172,8 +171,8 @@ final class _SliceFfi2Utf8 extends ffi.Struct { int get hashCode => _length.hashCode; } -final class _SliceFfiDouble extends ffi.Struct { - external ffi.Pointer _bytes; +final class _SliceUtf16 extends ffi.Struct { + external ffi.Pointer _bytes; @ffi.Size() external int _length; @@ -181,27 +180,28 @@ final class _SliceFfiDouble extends ffi.Struct { /// Produces a slice from a Dart object. The Dart object's data is copied into the given allocator /// as it cannot be borrowed directly, and gets freed with the slice object. // ignore: unused_element - static _SliceFfiDouble _fromDart(Float64List value, ffi.Allocator allocator) { - final pointer = allocator<_SliceFfiDouble>(); + static _SliceUtf16 _fromDart(String value, ffi.Allocator allocator) { + final pointer = allocator<_SliceUtf16>(); final slice = pointer.ref; slice._length = value.length; - slice._bytes = allocator(slice._length); - slice._bytes.asTypedList(slice._length).setAll(0, value); + // https://github.com/dart-lang/ffi/issues/223 + slice._bytes = allocator(slice._length).cast(); + slice._bytes.cast().asTypedList(slice._length).setAll(0, value.codeUnits); return slice; } // ignore: unused_element - Float64List get _asDart => _bytes.asTypedList(_length); + String get _asDart => String.fromCharCodes(_bytes.cast().asTypedList(_length)); // This is expensive @override bool operator ==(Object other) { - if (other is! _SliceFfiDouble || other._length != _length) { + if (other is! _SliceUtf16 || other._length != _length) { return false; } for (var i = 0; i < _length; i++) { - if (other._bytes[i] != _bytes[i]) { + if (other._bytes.cast()[i] != _bytes.cast()[i]) { return false; } } @@ -213,8 +213,8 @@ final class _SliceFfiDouble extends ffi.Struct { int get hashCode => _length.hashCode; } -final class _SliceFfiUtf16 extends ffi.Struct { - external ffi.Pointer _bytes; +final class _SliceUtf8 extends ffi.Struct { + external ffi.Pointer _bytes; @ffi.Size() external int _length; @@ -222,27 +222,40 @@ final class _SliceFfiUtf16 extends ffi.Struct { /// Produces a slice from a Dart object. The Dart object's data is copied into the given allocator /// as it cannot be borrowed directly, and gets freed with the slice object. // ignore: unused_element - static _SliceFfiUtf16 _fromDart(String value, ffi.Allocator allocator) { - final pointer = allocator<_SliceFfiUtf16>(); + static _SliceUtf8 _fromDart(String value, ffi.Allocator allocator) { + final pointer = allocator<_SliceUtf8>(); final slice = pointer.ref; - slice._length = value.length; - slice._bytes = allocator(slice._length).cast(); - slice._bytes.cast().asTypedList(slice._length).setAll(0, value.codeUnits); + slice._length = 0; + for (var rune in value.runes) { + if (rune < 0x80) { + slice._length += 1; + } else if (rune < 0x800) { + slice._length += 2; + } else if (rune < 0x10000) { + slice._length += 3; + } else { + slice._length += 4; + } + } + // https://github.com/dart-lang/ffi/issues/223 + slice._bytes = allocator(slice._length).cast(); + // https://github.com/dart-lang/sdk/issues/49470 + slice._bytes.cast().asTypedList(slice._length).setAll(0, Utf8Encoder().convert(value)); return slice; } // ignore: unused_element - String get _asDart => String.fromCharCodes(_bytes.cast().asTypedList(_length)); + String get _asDart => Utf8Decoder().convert(_bytes.cast().asTypedList(_length)); // This is expensive @override bool operator ==(Object other) { - if (other is! _SliceFfiUtf16 || other._length != _length) { + if (other is! _SliceUtf8 || other._length != _length) { return false; } for (var i = 0; i < _length; i++) { - if (other._bytes.cast()[i] != _bytes.cast()[i]) { + if (other._bytes.cast()[i] != _bytes.cast()[i]) { return false; } } diff --git a/tool/src/dart/formatter.rs b/tool/src/dart/formatter.rs index 4e569429c..a6c48cb84 100644 --- a/tool/src/dart/formatter.rs +++ b/tool/src/dart/formatter.rs @@ -208,7 +208,6 @@ impl<'tcx> DartFormatter<'tcx> { self.fmt_primitive_as_ffi(hir::PrimitiveType::Int(hir::IntType::I32), cast) } - /// Get the primitive type as a Dart FFI type pub fn fmt_primitive_as_ffi(&self, prim: hir::PrimitiveType, cast: bool) -> &'static str { use diplomat_core::hir::{FloatType, IntSizeType, IntType, PrimitiveType}; if cast { @@ -265,27 +264,27 @@ impl<'tcx> DartFormatter<'tcx> { use diplomat_core::hir::{FloatType, IntType, PrimitiveType}; match prim { PrimitiveType::Char => "_SliceRune", - PrimitiveType::Int(IntType::I8) => "_SliceFfiInt8", - PrimitiveType::Int(IntType::U8) => "_SliceFfiUint8", - PrimitiveType::Int(IntType::I16) => "_SliceFfiInt16", - PrimitiveType::Int(IntType::U16) => "_SliceFfiUint16", - PrimitiveType::Int(IntType::I32) => "_SliceFfiInt32", - PrimitiveType::Int(IntType::U32) => "_SliceFfiUint32", - PrimitiveType::Int(IntType::I64) => "_SliceFfiInt64", - PrimitiveType::Int(IntType::U64) => "_SliceFfiUint64", + PrimitiveType::Int(IntType::I8) => "_SliceInt8", + PrimitiveType::Int(IntType::U8) => "_SliceUint8", + PrimitiveType::Int(IntType::I16) => "_SliceInt16", + PrimitiveType::Int(IntType::U16) => "_SliceUint16", + PrimitiveType::Int(IntType::I32) => "_SliceInt32", + PrimitiveType::Int(IntType::U32) => "_SliceUint32", + PrimitiveType::Int(IntType::I64) => "_SliceInt64", + PrimitiveType::Int(IntType::U64) => "_SliceUint64", PrimitiveType::Int128(_) => panic!("i128 not supported in Dart"), PrimitiveType::IntSize(_) => self.fmt_primitive_list_type(prim), - PrimitiveType::Float(FloatType::F32) => "_SliceFfiFloat", - PrimitiveType::Float(FloatType::F64) => "_SliceFfiDouble", + PrimitiveType::Float(FloatType::F32) => "_SliceFloat", + PrimitiveType::Float(FloatType::F64) => "_SliceDouble", _ => panic!("Primitive {:?} not supported in lists", prim), } } pub fn fmt_utf8_slice_type(&self) -> &'static str { - "_SliceFfi2Utf8" + "_SliceUtf8" } pub fn fmt_utf16_slice_type(&self) -> &'static str { - "_SliceFfiUtf16" + "_SliceUtf16" } } diff --git a/tool/src/dart/mod.rs b/tool/src/dart/mod.rs index f7e53e157..0215ea9c9 100644 --- a/tool/src/dart/mod.rs +++ b/tool/src/dart/mod.rs @@ -122,6 +122,12 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } fn gen_enum(&mut self, ty: &'cx hir::EnumDef, id: TypeId, type_name: &str) -> String { + let methods = ty + .methods + .iter() + .flat_map(|method| self.gen_method_info(id, method, type_name)) + .collect::>(); + #[derive(Template)] #[template(path = "dart/enum.dart.jinja", escape = "none")] struct ImplTemplate<'a> { @@ -133,12 +139,6 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { is_contiguous: bool, } - let methods = ty - .methods - .iter() - .flat_map(|method| self.gen_method_info(id, method, type_name)) - .collect::>(); - ImplTemplate { ty, fmt: self.formatter, @@ -152,15 +152,6 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } fn gen_opaque_def(&mut self, ty: &'cx hir::OpaqueDef, id: TypeId, type_name: &str) -> String { - #[derive(Template)] - #[template(path = "dart/opaque.dart.jinja", escape = "none")] - struct ImplTemplate<'a> { - type_name: &'a str, - methods: &'a [MethodInfo<'a>], - docs: String, - destructor: String, - } - self.imports .insert(self.formatter.fmt_renamed_import("dart:ffi", "ffi")); @@ -172,6 +163,15 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { let destructor = self.formatter.fmt_destructor_name(id); + #[derive(Template)] + #[template(path = "dart/opaque.dart.jinja", escape = "none")] + struct ImplTemplate<'a> { + type_name: &'a str, + methods: &'a [MethodInfo<'a>], + docs: String, + destructor: String, + } + ImplTemplate { type_name, methods: methods.as_slice(), @@ -188,14 +188,8 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { id: TypeId, type_name: &str, ) -> String { - #[derive(Template)] - #[template(path = "dart/struct.dart.jinja", escape = "none")] - struct ImplTemplate<'a> { - type_name: &'a str, - fields: &'a [FieldInfo<'a>], - methods: &'a [MethodInfo<'a>], - docs: String, - } + self.imports + .insert(self.formatter.fmt_renamed_import("dart:ffi", "ffi")); struct FieldInfo<'a> { name: Cow<'a, str>, @@ -207,9 +201,6 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { set_slice_conversions: Vec>, } - self.imports - .insert(self.formatter.fmt_renamed_import("dart:ffi", "ffi")); - let fields = ty .fields .iter() @@ -259,6 +250,15 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { .flat_map(|method| self.gen_method_info(id, method, type_name)) .collect::>(); + #[derive(Template)] + #[template(path = "dart/struct.dart.jinja", escape = "none")] + struct ImplTemplate<'a> { + type_name: &'a str, + fields: &'a [FieldInfo<'a>], + methods: &'a [MethodInfo<'a>], + docs: String, + } + ImplTemplate { type_name, fields: &fields, @@ -292,12 +292,12 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { let mut param_decls_dart = Vec::new(); let mut param_types_ffi = Vec::new(); let mut param_types_ffi_cast = Vec::new(); - let mut dart_to_ffi_params = Vec::new(); + let mut param_conversions = Vec::new(); if let Some(param_self) = method.param_self.as_ref() { - param_types_ffi.push(self.gen_self_type_ffi(¶m_self.ty, false)); - param_types_ffi_cast.push(self.gen_self_type_ffi(¶m_self.ty, true)); - dart_to_ffi_params.push(self.gen_dart_to_c_self(¶m_self.ty)); + param_types_ffi.push(self.gen_self_type_name_ffi(¶m_self.ty, false)); + param_types_ffi_cast.push(self.gen_self_type_name_ffi(¶m_self.ty, true)); + param_conversions.push(self.gen_dart_to_c_self(¶m_self.ty)); } let mut slice_conversions = Vec::new(); @@ -324,17 +324,17 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { param_types_ffi_cast.push(self.formatter.fmt_pointer(¶m_type_ffi_cast).into()); param_types_ffi_cast.push(self.formatter.fmt_usize(true).into()); - dart_to_ffi_params.push(format!("{conversion}._bytes").into()); - dart_to_ffi_params.push(format!("{conversion}._length").into()); + param_conversions.push(format!("{conversion}._bytes").into()); + param_conversions.push(format!("{conversion}._length").into()); } else { param_types_ffi.push(param_type_ffi); param_types_ffi_cast.push(param_type_ffi_cast); - dart_to_ffi_params.push(conversion); + param_conversions.push(conversion); } } if method.is_writeable() { - dart_to_ffi_params.push("writeable._underlying".into()); + param_conversions.push("writeable._underlying".into()); param_types_ffi.push( self.formatter .fmt_pointer(self.formatter.fmt_opaque()) @@ -351,16 +351,14 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { ); } - let ffi_return_ty = self.gen_ffi_return_type_name(&method.output, false); - let ffi_cast_return_ty = self.gen_ffi_return_type_name(&method.output, true); + let return_ty = self.gen_return_type_name(&method.output); + let return_type_ffi = self.gen_return_type_name_ffi(&method.output, false); + let return_type_ffi_cast = self.gen_return_type_name_ffi(&method.output, true); - let dart_return_expression: Option> = - self.gen_c_to_dart_for_return_type(&method.output, "result".into()); + let return_expression = self.gen_c_to_dart_for_return_type(&method.output); let params = param_decls_dart.join(", "); - let return_ty = self.gen_dart_return_type_name(&method.output); - let declaration = if method.param_self.is_none() { // Constructor if return_ty == type_name { @@ -371,18 +369,17 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } // Static field } else if params.is_empty() - && !matches!(method.output, hir::ReturnType::Fallible(..)) + && !matches!( + method.output, + hir::ReturnType::Fallible(..) | hir::ReturnType::Infallible(None) + ) && return_ty != "bool" { let method_name = self .formatter .fmt_constructor_name(method) .unwrap_or("singleton".into()); - format!( - "static final {return_ty} {method_name} = \ - _capi>('{c_method_name}')\ - .asFunction<{ffi_cast_return_ty} Function()>(isLeaf: true)();" - ) + format!("static final {return_ty} {method_name} = ()") // Static method } else { let method_name = self.formatter.fmt_method_name(method); @@ -454,17 +451,15 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { c_method_name, param_types_ffi, param_types_ffi_cast, - ffi_return_ty, - ffi_cast_return_ty, - dart_to_ffi_params, - dart_return_expression, + return_type_ffi, + return_type_ffi_cast, slice_conversions, + param_conversions, + return_expression, }) } - /// Generates Dart code for referencing a particular Dart type. - /// - /// This function adds the necessary type imports to the decl and impl files. + /// Generates a type's Dart type. fn gen_type_name(&mut self, ty: &Type

) -> Cow<'cx, str> { match *ty { Type::Primitive(prim) => self.formatter.fmt_primitive_as_ffi(prim, true).into(), @@ -512,7 +507,25 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } } - /// Generates Dart code for referencing a particular FFI type with a given name. + /// Generates a return type's Dart type. + fn gen_return_type_name(&mut self, result_ty: &ReturnType) -> Cow<'cx, str> { + match *result_ty { + ReturnType::Infallible(None) => self.formatter.fmt_void().into(), + ReturnType::Infallible(Some(ref ty)) => match ty { + SuccessType::Writeable => self.formatter.fmt_string().into(), + SuccessType::OutType(o) => self.gen_type_name(o), + &_ => unreachable!("unknown AST/HIR variant"), + }, + ReturnType::Fallible(ref ok, _) => match ok { + Some(SuccessType::Writeable) => self.formatter.fmt_string().into(), + None => self.formatter.fmt_void().into(), + Some(SuccessType::OutType(o)) => self.gen_type_name(o), + &Some(_) => unreachable!("unknown AST/HIR variant"), + }, + } + } + + /// Generates a type's Dart FFI type. fn gen_type_name_ffi(&mut self, ty: &Type

, cast: bool) -> Cow<'cx, str> { match *ty { Type::Primitive(prim) => self.formatter.fmt_primitive_as_ffi(prim, cast).into(), @@ -558,7 +571,39 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } } - fn gen_self_type_ffi(&self, ty: &SelfType, cast: bool) -> Cow<'cx, str> { + /// Generates the Dart FFI type name of a return type. + fn gen_return_type_name_ffi(&mut self, result_ty: &ReturnType, cast: bool) -> Cow<'cx, str> { + match *result_ty { + ReturnType::Infallible(None) => if cast { + self.formatter.fmt_void() + } else { + self.formatter.fmt_ffi_void() + } + .into(), + ReturnType::Infallible(Some(ref ty)) => match ty { + SuccessType::Writeable => if cast { + self.formatter.fmt_void() + } else { + self.formatter.fmt_ffi_void() + } + .into(), + SuccessType::OutType(o) => { + if let hir::OutType::Slice(s) = o { + self.gen_slice(s).into() + } else { + self.gen_type_name_ffi(o, cast) + } + } + &_ => unreachable!("unknown AST/HIR variant"), + }, + ReturnType::Fallible(ref ok, ref err) => self + .gen_result(ok.as_ref().and_then(SuccessType::as_type), err.as_ref()) + .into(), + } + } + + /// Generates a self type's Dart FFI type. + fn gen_self_type_name_ffi(&self, ty: &SelfType, cast: bool) -> Cow<'cx, str> { match ty { SelfType::Opaque(_) => self .formatter @@ -570,11 +615,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } } - /// Generates Dart code for referencing a particular FFI type. - /// - /// This function adds the necessary type imports to the decl and impl files. - - /// Generates a C++ expression that converts from the C++ self type to the corresponding C self type. + /// Generates an FFI expression for a self type. fn gen_dart_to_c_self(&self, ty: &SelfType) -> Cow<'static, str> { match *ty { SelfType::Enum(ref e) if is_contiguous_enum(e.resolve(self.tcx)) => "index".into(), @@ -585,10 +626,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } } - /// Generates one or two C++ expressions that convert from a C++ type to the corresponding C type. - /// - /// Returns `PartiallyNamedExpression`s whose `suffix` is either empty, `Data`, or `Size` for - /// referencing fields of the C struct. + /// Generates an FFI expression for a type. fn gen_dart_to_c_for_type( &mut self, ty: &Type

, @@ -598,7 +636,7 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { match *ty { Type::Primitive(..) => dart_name.clone(), Type::Opaque(ref op) if op.is_optional() => format!( - "{dart_name} == null ? ffi.Pointer.fromAddress(0) ? {dart_name}._underlying" + "{dart_name} == null ? ffi.Pointer.fromAddress(0) : {dart_name}._underlying" ) .into(), Type::Enum(ref e) if is_contiguous_enum(e.resolve(self.tcx)) => { @@ -622,33 +660,104 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { } } - /// Generates the Dart type name of a return type. - fn gen_dart_return_type_name(&mut self, result_ty: &ReturnType) -> Cow<'cx, str> { + /// Generates a Dart expression for a type. + fn gen_c_to_dart_for_type( + &mut self, + ty: &Type

, + var_name: Cow<'cx, str>, + ) -> Cow<'cx, str> { + match *ty { + Type::Primitive(..) => var_name, + Type::Opaque(ref op) => { + let id = op.tcx_id.into(); + let type_name = self.formatter.fmt_type_name(id); + + match (op.owner.is_owned(), op.is_optional()) { + (false, _) => unimplemented!(), + (true, false) => format!("{type_name}._({var_name})").into(), + (true, true) => { + format!("{var_name}.address == 0 ? null : {type_name}._({var_name})").into() + } + } + } + Type::Struct(ref st) => { + let id = P::id_for_path(st); + let type_name = self.formatter.fmt_type_name(id); + format!("{type_name}._({var_name})").into() + } + Type::Enum(ref e) if is_contiguous_enum(e.resolve(self.tcx)) => { + let id = e.tcx_id.into(); + let type_name = self.formatter.fmt_type_name(id); + format!("{type_name}.values[{var_name}]").into() + } + Type::Enum(ref e) => { + let id = e.tcx_id.into(); + let type_name = self.formatter.fmt_type_name(id); + format!("{type_name}.values.firstWhere((v) => v._underlying == {var_name})").into() + } + Type::Slice(..) => format!("{var_name}._asDart").into(), + _ => unreachable!("unknown AST/HIR variant"), + } + } + + /// Generates a Dart expressions for a return type. + fn gen_c_to_dart_for_return_type(&mut self, result_ty: &ReturnType) -> Option> { match *result_ty { - ReturnType::Infallible(None) => self.formatter.fmt_void().into(), - ReturnType::Infallible(Some(ref ty)) => match ty { - SuccessType::Writeable => self.formatter.fmt_string().into(), - SuccessType::OutType(o) => self.gen_type_name(o), - &_ => unreachable!("unknown AST/HIR variant"), - }, - ReturnType::Fallible(ref ok, _) => match ok { - Some(SuccessType::Writeable) => self.formatter.fmt_string().into(), - None => self.formatter.fmt_void().into(), - Some(SuccessType::OutType(o)) => self.gen_type_name(o), - &Some(_) => unreachable!("unknown AST/HIR variant"), - }, + ReturnType::Infallible(None) => None, + ReturnType::Infallible(Some(SuccessType::Writeable)) => { + // Note: the `writeable` variable is initialized in the template + Some("return writeable.finalize();".into()) + } + ReturnType::Infallible(Some(SuccessType::OutType(ref out_ty))) => Some( + format!( + "return {};", + self.gen_c_to_dart_for_type(out_ty, "result".into()) + ) + .into(), + ), + ReturnType::Fallible(ref ok, ref err) => { + let err_conversion = match err { + Some(o) => self.gen_c_to_dart_for_type(o, "result.union.err".into()), + None => { + self.helper_classes.insert( + "voiderror".into(), + "/// An unspecified error value\nclass VoidError {}".into(), + ); + "VoidError()".into() + } + }; + let err_check = + format!("if (!result.isOk) {{\n throw {err_conversion};\n}}").into(); + let ok_conversion = match ok { + // Note: the `writeable` variable is initialized in the template + Some(SuccessType::Writeable) => "writeable.finalize()".into(), + Some(SuccessType::OutType(o)) => { + self.gen_c_to_dart_for_type(o, "result.union.ok".into()) + } + None => return Some(err_check), + &Some(_) => unreachable!("unknown AST/HIR variant"), + }; + Some(format!("{err_check}\nreturn {ok_conversion};").into()) + } + ReturnType::Infallible(Some(_)) => unreachable!("unknown AST/HIR variant"), } } + /// Generates a Dart helper class for a slice type. fn gen_slice(&mut self, slice: &hir::Slice) -> &'static str { - #[derive(askama::Template)] - #[template(path = "dart/slice.dart.jinja", escape = "none")] - struct SliceTemplate { - ffi_type: &'static str, - slice_ty: &'static str, - dart_ty: &'static str, - to_dart: &'static str, - from_dart: &'static str, + let slice_ty = match slice { + hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf8) => { + self.formatter.fmt_utf8_slice_type() + } + hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf16) => { + self.formatter.fmt_utf16_slice_type() + } + hir::Slice::Primitive(_, p) => self.formatter.fmt_slice_type(*p), + _ => todo!("{slice:?}"), + }; + + if self.helper_classes.contains_key(slice_ty) { + return slice_ty; } self.imports.insert( @@ -666,17 +775,6 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { _ => todo!("{slice:?}"), }; - let slice_ty = match slice { - hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf8) => { - self.formatter.fmt_utf8_slice_type() - } - hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf16) => { - self.formatter.fmt_utf16_slice_type() - } - hir::Slice::Primitive(_, p) => self.formatter.fmt_slice_type(*p), - _ => todo!("{slice:?}"), - }; - let ffi_type = match slice { hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf8) => { self.formatter.fmt_utf8_primitive() @@ -706,15 +804,26 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { let from_dart = match slice { hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf8) => concat!( - "final units = Utf8Encoder().convert(value);\n", - "slice._length = units.length;\n", - // TODO: Figure out why Pointer cannot be allocated + "slice._length = 0;\n", + "for (var rune in value.runes) {\n", + " if (rune < 0x80) {\n", + " slice._length += 1;\n", + " } else if (rune < 0x800) {\n", + " slice._length += 2;\n", + " } else if (rune < 0x10000) {\n", + " slice._length += 3;\n", + " } else {\n", + " slice._length += 4;\n", + " }\n", + "}\n", + "// https://github.com/dart-lang/ffi/issues/223\n", "slice._bytes = allocator(slice._length).cast();\n", - "slice._bytes.cast().asTypedList(slice._length).setAll(0, units);" + "// https://github.com/dart-lang/sdk/issues/49470\n", + "slice._bytes.cast().asTypedList(slice._length).setAll(0, Utf8Encoder().convert(value));" ), hir::Slice::Str(_, hir::StringEncoding::UnvalidatedUtf16) => concat!( "slice._length = value.length;\n", - // TODO: Figure out why Pointer cannot be allocated + "// https://github.com/dart-lang/ffi/issues/223\n", "slice._bytes = allocator(slice._length).cast();\n", "slice._bytes.cast().asTypedList(slice._length).setAll(0, value.codeUnits);" ), @@ -726,6 +835,16 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { ), }; + #[derive(askama::Template)] + #[template(path = "dart/slice.dart.jinja", escape = "none")] + struct SliceTemplate { + ffi_type: &'static str, + slice_ty: &'static str, + dart_ty: &'static str, + to_dart: &'static str, + from_dart: &'static str, + } + self.helper_classes.insert( slice_ty.into(), SliceTemplate { @@ -742,179 +861,59 @@ impl<'a, 'cx> TyGenContext<'a, 'cx> { slice_ty } - /// Generates the Dart FFI type name of a return type. - fn gen_ffi_return_type_name(&mut self, result_ty: &ReturnType, cast: bool) -> Cow<'cx, str> { - match *result_ty { - ReturnType::Infallible(None) => if cast { - self.formatter.fmt_void() - } else { - self.formatter.fmt_ffi_void() - } - .into(), - ReturnType::Infallible(Some(ref ty)) => match ty { - SuccessType::Writeable => if cast { - self.formatter.fmt_void() - } else { - self.formatter.fmt_ffi_void() - } - .into(), - SuccessType::OutType(o) => { - if let hir::OutType::Slice(s) = o { - self.gen_slice(s).into() - } else { - self.gen_type_name_ffi(o, cast) - } - } - &_ => unreachable!("unknown AST/HIR variant"), - }, - ReturnType::Fallible(ref ok, ref err) => { - #[derive(askama::Template)] - #[template(path = "dart/result.dart.jinja", escape = "none")] - struct ResultTemplate { - name: String, - decls: Vec, - } - - let ok = match ok { - None | Some(SuccessType::Writeable) => None, - Some(SuccessType::OutType(o)) => Some(o), - &Some(_) => unreachable!("unknown AST/HIR variant"), - }; - - let err = err.as_ref(); - - let name = format!( - "_Result{}{}", - &self - .formatter - .fmt_type_as_ident(ok.map(|o| self.gen_type_name_ffi(o, false)).as_deref()), - &self.formatter.fmt_type_as_ident( - err.map(|o| self.gen_type_name_ffi(o, false)).as_deref() - ) - ); - - let decls = [ok.map(|o| (o, "ok")), err.map(|o| (o, "err"))] - .into_iter() - .flatten() - .map(|(o, field_name)| { - format!( - "{}external {} {field_name};", - match o { - hir::OutType::Primitive(p) => { - format!( - "@{}()\n", - self.formatter.fmt_primitive_as_ffi(*p, false) - ) - } - hir::OutType::Enum(_) => - format!("@{}()\n", self.formatter.fmt_enum_as_ffi(false)), - _ => String::new(), - }, - { self.gen_type_name_ffi(o, true) } - ) - }) - .collect(); - - self.helper_classes.insert( - name.clone(), - ResultTemplate { - name: name.clone(), - decls, - } - .render() - .unwrap(), - ); + /// Generates a Dart helper class for a result type. + fn gen_result(&mut self, ok: Option<&hir::OutType>, err: Option<&hir::OutType>) -> String { + let name = format!( + "_Result{}{}", + &self + .formatter + .fmt_type_as_ident(ok.map(|o| self.gen_type_name_ffi(o, false)).as_deref()), + &self + .formatter + .fmt_type_as_ident(err.map(|o| self.gen_type_name_ffi(o, false)).as_deref()) + ); - name.into() - } + if self.helper_classes.contains_key(&name) { + return name; } - } - /// Generates a C++ expression that converts from a C type to the corresponding C++ type. - /// - /// If the type is a slice, this function assumes that `{var_name}_data` and `{var_name}_size` resolve - /// to valid expressions referencing the two different C variables for the pointer and the length. - fn gen_c_to_dart_for_type( - &mut self, - ty: &Type

, - var_name: Cow<'cx, str>, - ) -> Cow<'cx, str> { - match *ty { - Type::Primitive(..) => var_name, - Type::Opaque(ref op) => { - let id = op.tcx_id.into(); - let type_name = self.formatter.fmt_type_name(id); + let decls = [ok.map(|o| (o, "ok")), err.map(|o| (o, "err"))] + .into_iter() + .flatten() + .map(|(o, field_name)| { + format!( + "{}external {} {field_name};", + match o { + hir::OutType::Primitive(p) => { + format!("@{}()\n", self.formatter.fmt_primitive_as_ffi(*p, false)) + } + hir::OutType::Enum(_) => + format!("@{}()\n", self.formatter.fmt_enum_as_ffi(false)), + _ => String::new(), + }, + { self.gen_type_name_ffi(o, true) } + ) + }) + .collect(); - match (op.owner.is_owned(), op.is_optional()) { - (false, _) => unimplemented!(), - (true, false) => format!("{type_name}._({var_name})").into(), - (true, true) => { - format!("{var_name}.address == 0 ? null : {type_name}._({var_name})").into() - } - } - } - Type::Struct(ref st) => { - let id = P::id_for_path(st); - let type_name = self.formatter.fmt_type_name(id); - format!("{type_name}._({var_name})").into() - } - Type::Enum(ref e) if is_contiguous_enum(e.resolve(self.tcx)) => { - let id = e.tcx_id.into(); - let type_name = self.formatter.fmt_type_name(id); - format!("{type_name}.values[{var_name}]").into() - } - Type::Enum(ref e) => { - let id = e.tcx_id.into(); - let type_name = self.formatter.fmt_type_name(id); - format!("{type_name}.values.firstWhere((v) => v._underlying == {var_name})").into() - } - Type::Slice(..) => format!("{var_name}._asDart").into(), - _ => unreachable!("unknown AST/HIR variant"), + #[derive(askama::Template)] + #[template(path = "dart/result.dart.jinja", escape = "none")] + struct ResultTemplate { + name: String, + decls: Vec, } - } - /// Generates a C++ expression that converts from a C return type to the corresponding C++ return type. - /// - /// If the type is `Writeable`, this function assumes that there is a variable named `writeable` in scope. - fn gen_c_to_dart_for_return_type( - &mut self, - result_ty: &ReturnType, - var_name: Cow<'cx, str>, - ) -> Option> { - match *result_ty { - ReturnType::Infallible(None) => None, - ReturnType::Infallible(Some(SuccessType::Writeable)) => { - Some("return writeable.finalize();".into()) - } - ReturnType::Infallible(Some(SuccessType::OutType(ref out_ty))) => { - Some(format!("return {};", self.gen_c_to_dart_for_type(out_ty, var_name)).into()) - } - ReturnType::Fallible(ref ok, ref err) => { - let ok_path = format!("{var_name}.union.ok"); - let err_path = format!("{var_name}.union.err"); - let err_conversion = match err { - Some(o) => self.gen_c_to_dart_for_type(o, err_path.into()), - None => { - self.helper_classes.insert( - "voiderror".into(), - "/// An unspecified error value\nclass VoidError {}".into(), - ); - "VoidError()".into() - } - }; - let err_check = - format!("if (!{var_name}.isOk) {{\n throw {err_conversion};\n}}").into(); - let ok_conversion = match ok { - // Note: the `writeable` variable is a string initialized in the template - Some(SuccessType::Writeable) => "writeable.finalize()".into(), - Some(SuccessType::OutType(o)) => self.gen_c_to_dart_for_type(o, ok_path.into()), - None => return Some(err_check), - &Some(_) => unreachable!("unknown AST/HIR variant"), - }; - Some(format!("{err_check}\nreturn {ok_conversion};").into()) + self.helper_classes.insert( + name.clone(), + ResultTemplate { + name: name.clone(), + decls, } - ReturnType::Infallible(Some(_)) => unreachable!("unknown AST/HIR variant"), - } + .render() + .unwrap(), + ); + + name } } @@ -929,32 +928,28 @@ fn is_contiguous_enum(ty: &hir::EnumDef) -> bool { struct MethodInfo<'a> { /// HIR of the method being rendered method: &'a hir::Method, - /// + /// Docs docs: String, /// The declaration (everything before the parameter list) declaration: String, /// The C method name c_method_name: Cow<'a, str>, + // The types for the FFI declaration. The uncast types are the types + // from the `dart:ffi` package, the cast types are native Dart types. param_types_ffi: Vec>, param_types_ffi_cast: Vec>, - ffi_return_ty: Cow<'a, str>, - ffi_cast_return_ty: Cow<'a, str>, + return_type_ffi: Cow<'a, str>, + return_type_ffi_cast: Cow<'a, str>, + /// Conversion code for Dart arguments to slice helper structs slice_conversions: Vec>, - /// Dart conversion code for each parameter of the C function - dart_to_ffi_params: Vec>, + /// Conversion code for each parameter + param_conversions: Vec>, + /// If the function has a return value, the Dart code for the conversion. Assumes that /// the C function return value is saved to a variable named `result` or that the /// writeable, if present, is saved to a variable named `writeable`. - dart_return_expression: Option>, -} - -#[derive(PartialEq, Ord, PartialOrd, Clone, Eq, Debug)] -struct ResultClass { - ok_name: String, - err_name: String, - ok_decl: Option, - err_decl: Option, + return_expression: Option>, } diff --git a/tool/templates/dart/method.dart.jinja b/tool/templates/dart/method.dart.jinja index f72577fc4..85a582d99 100644 --- a/tool/templates/dart/method.dart.jinja +++ b/tool/templates/dart/method.dart.jinja @@ -1,8 +1,7 @@ {%- if !m.docs.is_empty() %} /// {{m.docs.replace('\n', "\n ")}} {%- endif %} - {{ m.declaration -}} - {%- if !m.declaration.starts_with("static final") %} { + {{ m.declaration }} { {%- for slice_conversion in m.slice_conversions %} {%- if loop.first %} final alloc = ffi2.Arena(); @@ -16,7 +15,7 @@ final result = {% else %} {% endif -%} _{{ m.c_method_name -}}( - {%- for param in m.dart_to_ffi_params %} + {%- for param in m.param_conversions %} {%- if loop.first %}{% else %}, {% endif -%} {{ param }} {%- endfor -%} @@ -24,25 +23,24 @@ {%- if !m.slice_conversions.is_empty() %} alloc.releaseAll(); {%- endif %} - {%- match m.dart_return_expression %} + {%- match m.return_expression %} {%- when Some with (statement) %} {{statement.replace('\n', "\n ")}} {%- when None %} {%- endmatch %} - } + }{% if m.declaration.starts_with("static final") %}();{% endif %} // ignore: non_constant_identifier_names static final _{{ m.c_method_name }} = - _capi>('{{ m.c_method_name }}') - .asFunction<{{ m.ffi_cast_return_ty }} Function( + .asFunction<{{ m.return_type_ffi_cast }} Function( {%- for param in m.param_types_ffi_cast %} {%- if !loop.first %}, {% endif -%} {{ param }} {%- endfor -%} - )>(isLeaf: true); -{%- endif %} \ No newline at end of file + )>(isLeaf: true); \ No newline at end of file