1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-05 09:20:04 +00:00

[vm/ffi] ABI-specific integers

This CL adds support for users defining integers which are mapped to
differing sizes and signedness based on the application binary interface
the Dart VM is running on.

Notable implementation design decisions:
- ABIs are open world, so that adding an ABI to the Dart VM does not
  break existing definitions. Thus, we only figure out in the VM that
  we're missing a mapping. We throw compile-time errors.
  - In AOT, these show up in the precompilation step.
  - In JIT, these show up as `_CompileTimeError` at runtime. Note that
    these can be caught. So in subsequent compilation steps we need to
    ensure that we also throw the same compile-time error.
- We match on the call-sites (streaming_flowgraph_builder) rather than
  method bodies (kernel_to_il) of AbiSpecific loads and stores so that
  we can compile for the int-size of the call site.

API design decisions:
https://github.com/dart-lang/sdk/issues/42563#issuecomment-981774001

Closes: https://github.com/dart-lang/sdk/issues/42563

TEST=tests/ffi_2/abi_*_test.dart
TEST=tests/ffi/function_*_generated_test.dart
TEST=tests/ffi/vmspecific_static_checks_test.dart
Change-Id: I8c8df36fab939b6fb614c5f1ee8e1bf46b6e9521
Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try,analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,benchmark-linux-try,dart-sdk-linux-try,front-end-linux-release-x64-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-fuchsia-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-gcc-linux-try,vm-kernel-linux-debug-x64c-try,vm-kernel-mac-debug-x64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-nnbd-win-release-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-asan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-android-release-arm64c-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221501
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Daco Harkes 2021-12-16 22:07:00 +00:00
parent b80e682cbf
commit acdf82de17
87 changed files with 3556 additions and 132 deletions

View File

@ -15,6 +15,34 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
/// Represents a native unsigned pointer-sized integer in C.
///
/// [UintPtr] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint64(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint64(),
})
class UintPtr extends AbiSpecificInteger {
const UintPtr();
}
//
// Pointer store.
//
@ -73,6 +101,12 @@ void doStoreUint64(Pointer<Uint64> pointer, int length) {
}
}
void doStoreUintPtr(Pointer<UintPtr> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreFloat(Pointer<Float> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1.0;
@ -174,6 +208,14 @@ int doLoadUint64(Pointer<Uint64> pointer, int length) {
return x;
}
int doLoadUintPtr(Pointer<UintPtr> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
double doLoadFloat(Pointer<Float> pointer, int length) {
double x = 0;
for (int i = 0; i < length; i++) {
@ -452,6 +494,25 @@ class PointerUint64 extends BenchmarkBase {
}
}
class PointerUintPtr extends BenchmarkBase {
Pointer<UintPtr> pointer = nullptr;
PointerUintPtr() : super('FfiMemory.PointerUintPtr');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUintPtr(pointer, N);
final int x = doLoadUintPtr(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
class PointerFloat extends BenchmarkBase {
Pointer<Float> pointer = nullptr;
PointerFloat() : super('FfiMemory.PointerFloat');
@ -555,6 +616,7 @@ void main() {
() => PointerInt64(),
() => PointerInt64Mint(),
() => PointerUint64(),
() => PointerUintPtr(),
() => PointerFloat(),
() => PointerDouble(),
() => PointerPointer(),

View File

@ -17,6 +17,34 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:benchmark_harness/benchmark_harness.dart';
/// Represents a native unsigned pointer-sized integer in C.
///
/// [UintPtr] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint64(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint64(),
})
class UintPtr extends AbiSpecificInteger {
const UintPtr();
}
//
// Pointer store.
//
@ -75,6 +103,12 @@ void doStoreUint64(Pointer<Uint64> pointer, int length) {
}
}
void doStoreUintPtr(Pointer<UintPtr> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1;
}
}
void doStoreFloat(Pointer<Float> pointer, int length) {
for (int i = 0; i < length; i++) {
pointer[i] = 1.0;
@ -176,6 +210,14 @@ int doLoadUint64(Pointer<Uint64> pointer, int length) {
return x;
}
int doLoadUintPtr(Pointer<UintPtr> pointer, int length) {
int x = 0;
for (int i = 0; i < length; i++) {
x += pointer[i];
}
return x;
}
double doLoadFloat(Pointer<Float> pointer, int length) {
double x = 0;
for (int i = 0; i < length; i++) {
@ -391,7 +433,7 @@ class PointerUint32 extends BenchmarkBase {
}
class PointerUint32Unaligned extends BenchmarkBase {
Pointer<Float> pointer;
Pointer<Uint32> pointer;
Pointer<Uint32> unalignedPointer;
PointerUint32Unaligned() : super('FfiMemory.PointerUint32Unaligned');
@ -452,6 +494,25 @@ class PointerUint64 extends BenchmarkBase {
}
}
class PointerUintPtr extends BenchmarkBase {
Pointer<UintPtr> pointer;
PointerUintPtr() : super('FfiMemory.PointerUintPtr');
@override
void setup() => pointer = calloc(N);
@override
void teardown() => calloc.free(pointer);
@override
void run() {
doStoreUintPtr(pointer, N);
final int x = doLoadUintPtr(pointer, N);
if (x != N) {
throw Exception('$name: Unexpected result: $x');
}
}
}
class PointerFloat extends BenchmarkBase {
Pointer<Float> pointer;
PointerFloat() : super('FfiMemory.PointerFloat');
@ -555,6 +616,7 @@ void main() {
() => PointerInt64(),
() => PointerInt64Mint(),
() => PointerUint64(),
() => PointerUintPtr(),
() => PointerFloat(),
() => PointerDouble(),
() => PointerPointer(),

View File

@ -3835,6 +3835,26 @@ const MessageCode messageFastaUsageShort = const MessageCode("FastaUsageShort",
-o <file> Generate the output into <file>.
-h Display this message (add -v for information about all options).""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiAbiSpecificIntegerInvalid =
messageFfiAbiSpecificIntegerInvalid;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiAbiSpecificIntegerInvalid = const MessageCode(
"FfiAbiSpecificIntegerInvalid",
problemMessage:
r"""Classes extending 'AbiSpecificInteger' must have exactly one const constructor, no other members, and no type arguments.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Code<Null> codeFfiAbiSpecificIntegerMappingInvalid =
messageFfiAbiSpecificIntegerMappingInvalid;
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const MessageCode messageFfiAbiSpecificIntegerMappingInvalid = const MessageCode(
"FfiAbiSpecificIntegerMappingInvalid",
problemMessage:
r"""Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size.""");
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<
Message Function(

View File

@ -477,6 +477,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.YIELD_IN_NON_GENERATOR,
CompileTimeErrorCode.YIELD_EACH_OF_INVALID_TYPE,
CompileTimeErrorCode.YIELD_OF_INVALID_TYPE,
FfiCode.ABI_SPECIFIC_INTEGER_INVALID,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_EXTRA,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_MISSING,
FfiCode.ABI_SPECIFIC_INTEGER_MAPPING_UNSUPPORTED,

View File

@ -15,6 +15,18 @@ import "package:analyzer/src/error/analyzer_error_code.dart";
// ignore_for_file: slash_for_doc_comments
class FfiCode extends AnalyzerErrorCode {
/**
* No parameters.
*/
static const FfiCode ABI_SPECIFIC_INTEGER_INVALID = FfiCode(
'ABI_SPECIFIC_INTEGER_INVALID',
"Classes extending 'AbiSpecificInteger' must have exactly one const "
"constructor, no other members, and no type arguments.",
correctionMessage:
"Try removing all type arguments, removing all members, and adding one "
"const constructor.",
);
/**
* No parameters.
*/
@ -451,9 +463,10 @@ class FfiCode extends AnalyzerErrorCode {
*/
static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS = FfiCode(
'SUBTYPE_OF_STRUCT_CLASS',
"The class '{0}' can't extend '{1}' because '{1}' is a subtype of 'Struct' "
"or 'Union'.",
correctionMessage: "Try extending 'Struct' or 'Union' directly.",
"The class '{0}' can't extend '{1}' because '{1}' is a subtype of "
"'Struct', 'Union', or 'AbiSpecificInteger'.",
correctionMessage:
"Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly.",
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS',
);
@ -465,8 +478,9 @@ class FfiCode extends AnalyzerErrorCode {
static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS = FfiCode(
'SUBTYPE_OF_STRUCT_CLASS',
"The class '{0}' can't implement '{1}' because '{1}' is a subtype of "
"'Struct' or 'Union'.",
correctionMessage: "Try extending 'Struct' or 'Union' directly.",
"'Struct', 'Union', or 'AbiSpecificInteger'.",
correctionMessage:
"Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly.",
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS',
);
@ -477,9 +491,10 @@ class FfiCode extends AnalyzerErrorCode {
*/
static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_WITH = FfiCode(
'SUBTYPE_OF_STRUCT_CLASS',
"The class '{0}' can't mix in '{1}' because '{1}' is a subtype of 'Struct' "
"or 'Union'.",
correctionMessage: "Try extending 'Struct' or 'Union' directly.",
"The class '{0}' can't mix in '{1}' because '{1}' is a subtype of "
"'Struct', 'Union', or 'AbiSpecificInteger'.",
correctionMessage:
"Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly.",
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_WITH',
);

View File

@ -92,6 +92,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
_validatePackedAnnotation(node.metadata);
}
} else if (className == _abiSpecificIntegerClassName) {
_validateAbiSpecificIntegerAnnotation(node);
_validateAbiSpecificIntegerMappingAnnotation(
node.name, node.metadata);
} else if (className != _allocatorClassName &&
@ -102,7 +103,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
superclass.name,
[node.name.name, superclass.name.name]);
}
} else if (superclass.isCompoundSubtype) {
} else if (superclass.isCompoundSubtype ||
superclass.isAbiSpecificIntegerSubtype) {
_errorReporter.reportErrorForNode(
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS,
superclass,
@ -120,7 +122,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
if (typename.ffiClass != null) {
_errorReporter.reportErrorForNode(subtypeOfFfiCode, typename,
[node.name.name, typename.name.toSource()]);
} else if (typename.isCompoundSubtype) {
} else if (typename.isCompoundSubtype ||
typename.isAbiSpecificIntegerSubtype) {
_errorReporter.reportErrorForNode(subtypeOfStructCode, typename,
[node.name.name, typename.name.toSource()]);
}
@ -614,6 +617,16 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
return _PrimitiveDartType.none;
}
void _validateAbiSpecificIntegerAnnotation(ClassDeclaration node) {
if ((node.typeParameters?.length ?? 0) != 0 ||
node.members.length != 1 ||
node.members.single is! ConstructorDeclaration ||
(node.members.single as ConstructorDeclaration).constKeyword == null) {
_errorReporter.reportErrorForNode(
FfiCode.ABI_SPECIFIC_INTEGER_INVALID, node.name);
}
}
/// Validate that the [annotations] include at most one mapping annotation.
void _validateAbiSpecificIntegerMappingAnnotation(
AstNode errorNode, NodeList<Annotation> annotations) {
@ -1557,6 +1570,17 @@ extension on DartType {
return false;
}
bool get isAbiSpecificInteger {
final self = this;
if (self is InterfaceType) {
final element = self.element;
final name = element.name;
return name == FfiVerifier._abiSpecificIntegerClassName &&
element.isFfiClass;
}
return false;
}
/// Returns `true` iff this is an Abi-specific integer type,
/// i.e. a subtype of `AbiSpecificInteger`.
bool get isAbiSpecificIntegerSubtype {
@ -1626,4 +1650,13 @@ extension on NamedType {
}
return false;
}
/// Return `true` if this represents a subtype of `Struct` or `Union`.
bool get isAbiSpecificIntegerSubtype {
var element = name.staticElement;
if (element is ClassElement) {
return element.allSupertypes.any((e) => e.isAbiSpecificInteger);
}
return false;
}
}

View File

@ -13775,6 +13775,10 @@ CompileTimeErrorCode:
}
```
FfiCode:
ABI_SPECIFIC_INTEGER_INVALID:
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one const constructor, no other members, and no type arguments."
correctionMessage: Try removing all type arguments, removing all members, and adding one const constructor.
comment: No parameters.
ABI_SPECIFIC_INTEGER_MAPPING_EXTRA:
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a 'NativeType' integer with a fixed size."
correctionMessage: Try removing the extra annotation.
@ -13978,24 +13982,24 @@ FfiCode:
1: the name of the class being extended, implemented, or mixed in
SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS:
sharedName: SUBTYPE_OF_STRUCT_CLASS
problemMessage: "The class '{0}' can't extend '{1}' because '{1}' is a subtype of 'Struct' or 'Union'."
correctionMessage: "Try extending 'Struct' or 'Union' directly."
problemMessage: "The class '{0}' can't extend '{1}' because '{1}' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
correctionMessage: "Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly."
comment: |-
Parameters:
0: the name of the subclass
1: the name of the class being extended, implemented, or mixed in
SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS:
sharedName: SUBTYPE_OF_STRUCT_CLASS
problemMessage: "The class '{0}' can't implement '{1}' because '{1}' is a subtype of 'Struct' or 'Union'."
correctionMessage: "Try extending 'Struct' or 'Union' directly."
problemMessage: "The class '{0}' can't implement '{1}' because '{1}' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
correctionMessage: "Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly."
comment: |-
Parameters:
0: the name of the subclass
1: the name of the class being extended, implemented, or mixed in
SUBTYPE_OF_STRUCT_CLASS_IN_WITH:
sharedName: SUBTYPE_OF_STRUCT_CLASS
problemMessage: "The class '{0}' can't mix in '{1}' because '{1}' is a subtype of 'Struct' or 'Union'."
correctionMessage: "Try extending 'Struct' or 'Union' directly."
problemMessage: "The class '{0}' can't mix in '{1}' because '{1}' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'."
correctionMessage: "Try extending 'Struct', 'Union', or 'AbiSpecificInteger' directly."
comment: |-
Parameters:
0: the name of the subclass

View File

@ -45,6 +45,23 @@ class C extends S {}
@reflectiveTest
class SubtypeOfStructClassInImplementsTest extends PubPackageResolutionTest {
test_implements_abi_specific_int() async {
await assertErrorsInCode(r'''
import 'dart:ffi';
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
})
class AbiSpecificInteger1 extends AbiSpecificInteger {
const AbiSpecificInteger1();
}
class AbiSpecificInteger4 implements AbiSpecificInteger1 {
const AbiSpecificInteger4();
}
''', [
error(FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS, 204, 19),
]);
}
test_implements_struct() async {
await assertErrorsInCode(r'''
import 'dart:ffi';

View File

@ -54,6 +54,8 @@ export '../fasta/compiler_context.dart' show CompilerContext;
export '../fasta/fasta_codes.dart'
show
LocatedMessage,
messageFfiAbiSpecificIntegerInvalid,
messageFfiAbiSpecificIntegerMappingInvalid,
messageFfiExceptionalReturnNull,
messageFfiExpectedConstant,
messageFfiLeafCallMustNotReturnHandle,

View File

@ -334,6 +334,8 @@ FastaUsageLong/analyzerCode: Fail
FastaUsageLong/example: Fail
FastaUsageShort/analyzerCode: Fail
FastaUsageShort/example: Fail
FfiAbiSpecificIntegerInvalid/analyzerCode: Fail
FfiAbiSpecificIntegerMappingInvalid/analyzerCode: Fail
FfiDartTypeMismatch/analyzerCode: Fail
FfiEmptyStruct/analyzerCode: Fail
FfiExceptionalReturnNull/analyzerCode: Fail

View File

@ -4554,6 +4554,16 @@ StaticAndInstanceConflictCause:
problemMessage: "This is the instance member."
severity: CONTEXT
FfiAbiSpecificIntegerInvalid:
# Used by dart:ffi
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one const constructor, no other members, and no type arguments."
external: test/ffi_test.dart
FfiAbiSpecificIntegerMappingInvalid:
# Used by dart:ffi
problemMessage: "Classes extending 'AbiSpecificInteger' must have exactly one 'AbiSpecificIntegerMapping' annotation specifying the mapping from ABI to a NativeType integer with a fixed size."
external: test/ffi_test.dart
FfiTypeMismatch:
# Used by dart:ffi
problemMessage: "Expected type '#type' to be '#type2', which is the Dart type corresponding to '#type3'."

View File

@ -11,6 +11,7 @@
a
abbreviations
abi
ability
able
abort

View File

@ -10,6 +10,8 @@
# automatic tools might move it to the top of the file.
JS
abispecificinteger
abispecificintegermapping
adjusting
api
argument(s)
@ -65,6 +67,7 @@ placing
pubspec.yaml
re
sdksummary
size
solutions
stacktrace
staticinterop

View File

@ -40,6 +40,10 @@ additionalExports = (ffi::nullptr,
ffi::sizeOf,
ffi::Dart_NativeMessageHandler,
ffi::Abi,
ffi::AbiSpecificInteger,
ffi::AbiSpecificIntegerArray,
ffi::AbiSpecificIntegerMapping,
ffi::AbiSpecificIntegerPointer,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
@ -114,6 +118,10 @@ additionalExports = (ffi::nullptr,
ffi::sizeOf,
ffi::Dart_NativeMessageHandler,
ffi::Abi,
ffi::AbiSpecificInteger,
ffi::AbiSpecificIntegerArray,
ffi::AbiSpecificIntegerMapping,
ffi::AbiSpecificIntegerPointer,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,

View File

@ -40,6 +40,10 @@ additionalExports = (ffi::nullptr,
ffi::sizeOf,
ffi::Dart_NativeMessageHandler,
ffi::Abi,
ffi::AbiSpecificInteger,
ffi::AbiSpecificIntegerArray,
ffi::AbiSpecificIntegerMapping,
ffi::AbiSpecificIntegerPointer,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,
@ -114,6 +118,10 @@ additionalExports = (ffi::nullptr,
ffi::sizeOf,
ffi::Dart_NativeMessageHandler,
ffi::Abi,
ffi::AbiSpecificInteger,
ffi::AbiSpecificIntegerArray,
ffi::AbiSpecificIntegerMapping,
ffi::AbiSpecificIntegerPointer,
ffi::Allocator,
ffi::AllocatorAlloc,
ffi::Array,

View File

@ -25,5 +25,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -52,5 +52,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -25,5 +25,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -25,5 +25,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -52,5 +52,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -33,5 +33,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -84,5 +84,5 @@ Extra constant evaluation: evaluated: 110, effectively constant: 2
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -33,5 +33,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -33,5 +33,5 @@ constants {
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -84,5 +84,5 @@ Extra constant evaluation: evaluated: 110, effectively constant: 2
Constructor coverage from constants:
org-dartlang-testcase:///ffi_struct_inline_array_multi_dimensional.dart:
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:137:9)
- _ArraySize. (from org-dartlang-sdk:///sdk/lib/ffi/ffi.dart:138:9)
- Object. (from org-dartlang-sdk:///sdk/lib/core/object.dart:25:9)

View File

@ -18,6 +18,7 @@ import 'package:kernel/type_environment.dart'
show TypeEnvironment, SubtypeCheckMode;
import 'abi.dart';
import 'native_type_cfe.dart';
/// Represents the (instantiated) ffi.NativeType.
enum NativeType {
@ -44,7 +45,7 @@ enum NativeType {
kBool,
}
const Set<NativeType> nativeIntTypes = <NativeType>{
const Set<NativeType> nativeIntTypesFixedSize = <NativeType>{
NativeType.kInt8,
NativeType.kInt16,
NativeType.kInt32,
@ -53,6 +54,10 @@ const Set<NativeType> nativeIntTypes = <NativeType>{
NativeType.kUint16,
NativeType.kUint32,
NativeType.kUint64,
};
const Set<NativeType> nativeIntTypes = <NativeType>{
...nativeIntTypesFixedSize,
NativeType.kIntptr,
};
@ -178,11 +183,15 @@ class FfiTransformer extends Transformer {
final Class compoundClass;
final Class structClass;
final Class unionClass;
final Class abiSpecificIntegerClass;
final Class abiSpecificIntegerMappingClass;
final Class ffiNativeClass;
final Class nativeFieldWrapperClass1Class;
final Class ffiStructLayoutClass;
final Field ffiStructLayoutTypesField;
final Field ffiStructLayoutPackingField;
final Class ffiAbiSpecificMappingClass;
final Field ffiAbiSpecificMappingNativeTypesField;
final Class ffiInlineArrayClass;
final Field ffiInlineArrayElementTypeField;
final Field ffiInlineArrayLengthField;
@ -202,6 +211,12 @@ class FfiTransformer extends Transformer {
final Procedure unionArrayElemAt;
final Procedure arrayArrayElemAt;
final Procedure arrayArrayAssignAt;
final Procedure abiSpecificIntegerPointerGetValue;
final Procedure abiSpecificIntegerPointerSetValue;
final Procedure abiSpecificIntegerPointerElemAt;
final Procedure abiSpecificIntegerPointerSetElemAt;
final Procedure abiSpecificIntegerArrayElemAt;
final Procedure abiSpecificIntegerArraySetElemAt;
final Procedure asFunctionMethod;
final Procedure asFunctionInternal;
final Procedure sizeOfMethod;
@ -228,6 +243,12 @@ class FfiTransformer extends Transformer {
final Map<NativeType, Procedure> storeMethods;
final Map<NativeType, Procedure> storeUnalignedMethods;
final Map<NativeType, Procedure> elementAtMethods;
final Procedure loadAbiSpecificIntMethod;
final Procedure loadAbiSpecificIntAtIndexMethod;
final Procedure storeAbiSpecificIntMethod;
final Procedure storeAbiSpecificIntAtIndexMethod;
final Procedure abiCurrentMethod;
final Map<Constant, Abi> constantAbis;
final Procedure memCopy;
final Procedure allocationTearoff;
final Procedure asFunctionTearoff;
@ -301,6 +322,10 @@ class FfiTransformer extends Transformer {
compoundClass = index.getClass('dart:ffi', '_Compound'),
structClass = index.getClass('dart:ffi', 'Struct'),
unionClass = index.getClass('dart:ffi', 'Union'),
abiSpecificIntegerClass =
index.getClass('dart:ffi', 'AbiSpecificInteger'),
abiSpecificIntegerMappingClass =
index.getClass('dart:ffi', 'AbiSpecificIntegerMapping'),
ffiNativeClass = index.getClass('dart:ffi', 'FfiNative'),
nativeFieldWrapperClass1Class =
index.getClass('dart:nativewrappers', 'NativeFieldWrapperClass1'),
@ -309,6 +334,10 @@ class FfiTransformer extends Transformer {
index.getField('dart:ffi', '_FfiStructLayout', 'fieldTypes'),
ffiStructLayoutPackingField =
index.getField('dart:ffi', '_FfiStructLayout', 'packing'),
ffiAbiSpecificMappingClass =
index.getClass('dart:ffi', '_FfiAbiSpecificMapping'),
ffiAbiSpecificMappingNativeTypesField =
index.getField('dart:ffi', '_FfiAbiSpecificMapping', 'nativeTypes'),
ffiInlineArrayClass = index.getClass('dart:ffi', '_FfiInlineArray'),
ffiInlineArrayElementTypeField =
index.getField('dart:ffi', '_FfiInlineArray', 'elementType'),
@ -362,6 +391,18 @@ class FfiTransformer extends Transformer {
arrayArrayElemAt = index.getProcedure('dart:ffi', 'ArrayArray', '[]'),
arrayArrayAssignAt =
index.getProcedure('dart:ffi', 'ArrayArray', '[]='),
abiSpecificIntegerPointerGetValue = index.getProcedure(
'dart:ffi', 'AbiSpecificIntegerPointer', 'get:value'),
abiSpecificIntegerPointerSetValue = index.getProcedure(
'dart:ffi', 'AbiSpecificIntegerPointer', 'set:value'),
abiSpecificIntegerPointerElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerPointer', '[]'),
abiSpecificIntegerPointerSetElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerPointer', '[]='),
abiSpecificIntegerArrayElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]'),
abiSpecificIntegerArraySetElemAt =
index.getProcedure('dart:ffi', 'AbiSpecificIntegerArray', '[]='),
asFunctionMethod = index.getProcedure(
'dart:ffi', 'NativeFunctionPointer', 'asFunction'),
asFunctionInternal =
@ -406,6 +447,20 @@ class FfiTransformer extends Transformer {
final name = nativeTypeClassNames[t];
return index.getTopLevelProcedure('dart:ffi', "_elementAt$name");
}),
loadAbiSpecificIntMethod =
index.getTopLevelProcedure('dart:ffi', "_loadAbiSpecificInt"),
loadAbiSpecificIntAtIndexMethod = index.getTopLevelProcedure(
'dart:ffi', "_loadAbiSpecificIntAtIndex"),
storeAbiSpecificIntMethod =
index.getTopLevelProcedure('dart:ffi', "_storeAbiSpecificInt"),
storeAbiSpecificIntAtIndexMethod = index.getTopLevelProcedure(
'dart:ffi', "_storeAbiSpecificIntAtIndex"),
abiCurrentMethod = index.getProcedure('dart:ffi', 'Abi', 'current'),
constantAbis = abiNames.map((abi, name) => MapEntry(
(index.getField('dart:ffi', 'Abi', name).initializer
as ConstantExpression)
.constant,
abi)),
memCopy = index.getTopLevelProcedure('dart:ffi', '_memCopy'),
allocationTearoff = index.getProcedure(
'dart:ffi', 'AllocatorAlloc', LibraryIndex.tearoffPrefix + 'call'),
@ -451,6 +506,7 @@ class FfiTransformer extends Transformer {
/// [Uint32] -> [int]
/// [Uint64] -> [int]
/// [IntPtr] -> [int]
/// T extends [AbiSpecificInteger] -> [int]
/// [Double] -> [double]
/// [Float] -> [double]
/// [Bool] -> [bool]
@ -477,6 +533,9 @@ class FfiTransformer extends Transformer {
}
return nativeType;
}
if (hierarchy.isSubclassOf(nativeClass, abiSpecificIntegerClass)) {
return InterfaceType(intClass, Nullability.legacy);
}
if (hierarchy.isSubclassOf(nativeClass, compoundClass)) {
if (nativeClass == structClass || nativeClass == unionClass) {
return null;
@ -773,6 +832,24 @@ class FfiTransformer extends Transformer {
return dimensions;
}
bool isAbiSpecificIntegerSubtype(DartType type) {
if (type is InvalidType) {
return false;
}
if (type is NullType) {
return false;
}
if (type is InterfaceType) {
if (type.classNode == abiSpecificIntegerClass) {
return false;
}
}
return env.isSubtypeOf(
type,
InterfaceType(abiSpecificIntegerClass, Nullability.legacy),
SubtypeCheckMode.ignoringNullabilities);
}
bool isCompoundSubtype(DartType type) {
if (type is InvalidType) {
return false;
@ -822,6 +899,76 @@ class FfiTransformer extends Transformer {
interfaceTarget: numMultiplication,
functionType: numMultiplication.getterType as FunctionType);
}
Iterable<MapConstant> getAbiSpecificIntegerMappingAnnotations(Class node) {
return node.annotations
.whereType<ConstantExpression>()
.map((e) => e.constant)
.whereType<InstanceConstant>()
.where((e) => e.classNode == abiSpecificIntegerMappingClass)
.map((instanceConstant) =>
instanceConstant.fieldValues.values.single as MapConstant);
}
/// Generates an expression performing an Abi specific integer load or store.
///
/// If [value] is provided, it is a store, otherwise a load.
///
/// Provide either [index], or [offsetInBytes], or none for an offset of 0.
///
/// Generates an expression:
///
/// ```dart
/// _storeAbiSpecificInt(
/// [8, 8, 4][_abi()],
/// typedDataBase,
/// index * [8, 8, 4][_abi()],
/// value,
/// )
/// ```
Expression abiSpecificLoadOrStoreExpression(
AbiSpecificNativeTypeCfe nativeTypeCfe, {
required Expression typedDataBase,
Expression? offsetInBytes,
Expression? index,
Expression? value,
required fileOffset,
}) {
assert(index == null || offsetInBytes == null);
final method = () {
if (value != null) {
if (index != null) {
return storeAbiSpecificIntAtIndexMethod;
}
return storeAbiSpecificIntMethod;
}
if (index != null) {
return loadAbiSpecificIntAtIndexMethod;
}
return loadAbiSpecificIntMethod;
}();
final Expression offsetOrIndex = () {
if (offsetInBytes != null) {
return offsetInBytes;
}
if (index != null) {
return index;
}
return ConstantExpression(IntConstant(0));
}();
return StaticInvocation(
method,
Arguments([
typedDataBase,
offsetOrIndex,
if (value != null) value,
], types: [
InterfaceType(nativeTypeCfe.clazz, Nullability.nonNullable)
]),
)..fileOffset = fileOffset;
}
}
/// Checks if any library depends on dart:ffi.

View File

@ -4,6 +4,8 @@
import 'package:front_end/src/api_unstable/vm.dart'
show
messageFfiAbiSpecificIntegerInvalid,
messageFfiAbiSpecificIntegerMappingInvalid,
messageFfiPackedAnnotationAlignment,
messageNonPositiveArrayDimensions,
templateFfiEmptyStruct,
@ -248,8 +250,36 @@ class _FfiDefinitionTransformer extends FfiTransformer {
return true;
}
bool _isUserAbiSpecificInteger(Class node) =>
hierarchy.isSubclassOf(node, abiSpecificIntegerClass) &&
node != abiSpecificIntegerClass;
@override
visitClass(Class node) {
if (_isUserAbiSpecificInteger(node)) {
final nativeTypeCfe = NativeTypeCfe(
this, node.getThisType(coreTypes, Nullability.nonNullable))
as AbiSpecificNativeTypeCfe;
if (nativeTypeCfe.abiSpecificTypes.length == 0) {
// Annotation missing, multiple annotations, or invalid mapping.
diagnosticReporter.report(messageFfiAbiSpecificIntegerMappingInvalid,
node.fileOffset, node.name.length, node.location!.file);
}
if (node.typeParameters.length != 0 ||
node.procedures.where((Procedure e) => !e.isSynthetic).length != 0 ||
node.fields.length != 0 ||
node.redirectingFactories.length != 0 ||
node.constructors.length != 1 ||
!node.constructors.single.isConst) {
// We want exactly one constructor, no other members and no type arguments.
diagnosticReporter.report(messageFfiAbiSpecificIntegerInvalid,
node.fileOffset, node.name.length, node.location!.file);
}
final IndexedClass? indexedClass =
currentLibraryIndex?.lookupIndexedClass(node.name);
_addSizeOfField(node, indexedClass, nativeTypeCfe.size);
_annotateAbiSpecificTypeWithMapping(node, nativeTypeCfe);
}
if (!_isUserCompound(node)) {
return node;
}
@ -594,8 +624,13 @@ class _FfiDefinitionTransformer extends FfiTransformer {
final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
if (nativeTypeAnnos.length == 1) {
final clazz = nativeTypeAnnos.first;
final nativeType = _getFieldType(clazz)!;
type = PrimitiveNativeTypeCfe(nativeType, clazz);
if (_isUserAbiSpecificInteger(clazz)) {
type = NativeTypeCfe(
this, clazz.getThisType(coreTypes, Nullability.nonNullable));
} else {
final nativeType = _getFieldType(clazz)!;
type = PrimitiveNativeTypeCfe(nativeType, clazz);
}
}
}
@ -791,6 +826,33 @@ class _FfiDefinitionTransformer extends FfiTransformer {
InterfaceType(pragmaClass, Nullability.nonNullable, [])));
}
static const vmFfiAbiSpecificIntMapping = 'vm:ffi:abi-specific-mapping';
void _annotateAbiSpecificTypeWithMapping(
Class node, AbiSpecificNativeTypeCfe nativeTypeCfe) {
final constants = [
for (final abi in Abi.values)
nativeTypeCfe.abiSpecificTypes[abi]?.generateConstant(this) ??
NullConstant()
];
node.addAnnotation(ConstantExpression(
InstanceConstant(pragmaClass.reference, [], {
pragmaName.fieldReference: StringConstant(vmFfiAbiSpecificIntMapping),
pragmaOptions.fieldReference: InstanceConstant(
ffiAbiSpecificMappingClass.reference,
[],
{
ffiAbiSpecificMappingNativeTypesField.fieldReference:
ListConstant(
InterfaceType(typeClass, Nullability.nullable),
constants,
),
},
)
}),
InterfaceType(pragmaClass, Nullability.nonNullable, [])));
}
void _generateMethodsForField(
Class node,
Field field,
@ -890,7 +952,8 @@ class _FfiDefinitionTransformer extends FfiTransformer {
.map((expr) => expr.constant)
.whereType<InstanceConstant>()
.map((constant) => constant.classNode)
.where((klass) => _getFieldType(klass) != null);
.where((klass) =>
_getFieldType(klass) != null || _isUserAbiSpecificInteger(klass));
}
Iterable<List<int>> _getArraySizeAnnotations(Member node) {

View File

@ -16,7 +16,8 @@ import 'common.dart';
abstract class NativeTypeCfe {
factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
{List<int>? arrayDimensions,
Map<Class, NativeTypeCfe> compoundCache = const {}}) {
Map<Class, NativeTypeCfe> compoundCache = const {},
alreadyInAbiSpecificType = false}) {
if (transformer.isPrimitiveType(dartType)) {
final clazz = (dartType as InterfaceType).classNode;
final nativeType = transformer.getType(clazz)!;
@ -48,6 +49,32 @@ abstract class NativeTypeCfe {
}
return ArrayNativeTypeCfe.multi(elementCfeType, arrayDimensions);
}
if (transformer.isAbiSpecificIntegerSubtype(dartType)) {
final clazz = (dartType as InterfaceType).classNode;
final mappingConstants =
transformer.getAbiSpecificIntegerMappingAnnotations(clazz);
if (alreadyInAbiSpecificType || mappingConstants.length != 1) {
// Unsupported mapping.
return AbiSpecificNativeTypeCfe({}, clazz);
}
final mapping =
Map.fromEntries(mappingConstants.first.entries.map((e) => MapEntry(
transformer.constantAbis[e.key]!,
NativeTypeCfe(
transformer,
(e.value as InstanceConstant).classNode.getThisType(
transformer.coreTypes, Nullability.nonNullable),
alreadyInAbiSpecificType: true,
))));
for (final value in mapping.values) {
if (value is! PrimitiveNativeTypeCfe ||
!nativeIntTypesFixedSize.contains(value.nativeType)) {
// Unsupported mapping.
return AbiSpecificNativeTypeCfe({}, clazz);
}
}
return AbiSpecificNativeTypeCfe(mapping, clazz);
}
throw "Invalid type $dartType";
}
@ -557,6 +584,66 @@ class ArrayNativeTypeCfe implements NativeTypeCfe {
..fileOffset = fileOffset);
}
class AbiSpecificNativeTypeCfe implements NativeTypeCfe {
final Map<Abi, NativeTypeCfe> abiSpecificTypes;
final Class clazz;
AbiSpecificNativeTypeCfe(this.abiSpecificTypes, this.clazz);
@override
Map<Abi, int?> get size => abiSpecificTypes
.map((abi, nativeTypeCfe) => MapEntry(abi, nativeTypeCfe.size[abi]));
@override
Map<Abi, int?> get alignment => abiSpecificTypes
.map((abi, nativeTypeCfe) => MapEntry(abi, nativeTypeCfe.alignment[abi]));
@override
Constant generateConstant(FfiTransformer transformer) =>
TypeLiteralConstant(InterfaceType(clazz, Nullability.nonNullable));
@override
ReturnStatement generateGetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
FfiTransformer transformer,
) {
return ReturnStatement(
transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
fileOffset: fileOffset,
),
);
}
@override
ReturnStatement generateSetterStatement(
DartType dartType,
int fileOffset,
Map<Abi, int?> offsets,
bool unalignedAccess,
VariableDeclaration argument,
FfiTransformer transformer,
) {
return ReturnStatement(
transformer.abiSpecificLoadOrStoreExpression(
this,
typedDataBase: transformer.getCompoundTypedDataBaseField(
ThisExpression(), fileOffset),
offsetInBytes: transformer.runtimeBranchOnLayout(offsets),
value: VariableGet(argument),
fileOffset: fileOffset,
),
);
}
}
extension on int? {
int? align(int? alignment) =>
((this + alignment - 1) ~/ alignment) * alignment;

View File

@ -27,6 +27,7 @@ import 'package:kernel/type_algebra.dart' show Substitution;
import 'package:kernel/type_environment.dart';
import 'abi.dart' show wordSize;
import 'native_type_cfe.dart';
import 'common.dart'
show NativeType, FfiTransformer, nativeTypeSizes, WORD_SIZE, UNKNOWN;
@ -132,6 +133,42 @@ class _FfiUseSiteTransformer extends FfiTransformer {
final Member target = node.target;
try {
if (target == abiSpecificIntegerPointerGetValue ||
target == abiSpecificIntegerPointerSetValue ||
target == abiSpecificIntegerPointerElemAt ||
target == abiSpecificIntegerPointerSetElemAt ||
target == abiSpecificIntegerArrayElemAt ||
target == abiSpecificIntegerArraySetElemAt) {
final pointer = node.arguments.positional[0];
final pointerType =
pointer.getStaticType(_staticTypeContext!) as InterfaceType;
_ensureNativeTypeValid(pointerType, pointer,
allowCompounds: true, allowInlineArray: true);
final typeArg = pointerType.typeArguments.single;
final nativeTypeCfe =
NativeTypeCfe(this, typeArg) as AbiSpecificNativeTypeCfe;
return abiSpecificLoadOrStoreExpression(
nativeTypeCfe,
typedDataBase: (target == abiSpecificIntegerArrayElemAt ||
target == abiSpecificIntegerArraySetElemAt)
? getArrayTypedDataBaseField(node.arguments.positional[0])
: node.arguments.positional[0],
index: (target == abiSpecificIntegerPointerElemAt ||
target == abiSpecificIntegerPointerSetElemAt ||
target == abiSpecificIntegerArrayElemAt ||
target == abiSpecificIntegerArraySetElemAt)
? node.arguments.positional[1]
: null,
value: (target == abiSpecificIntegerPointerSetValue ||
target == abiSpecificIntegerPointerSetElemAt ||
target == abiSpecificIntegerArraySetElemAt)
? node.arguments.positional.last
: null,
fileOffset: node.fileOffset,
);
}
if (target == structPointerRef ||
target == structPointerElemAt ||
target == unionPointerRef ||
@ -785,13 +822,19 @@ class _FfiUseSiteTransformer extends FfiTransformer {
klass == opaqueClass ||
klass == structClass ||
klass == unionClass ||
klass == abiSpecificIntegerClass ||
classNativeTypes[klass] != null) {
return null;
}
// The Opaque and Struct classes can be extended, but subclasses
// cannot be (nor implemented).
final onlyDirectExtendsClasses = [opaqueClass, structClass, unionClass];
final onlyDirectExtendsClasses = [
opaqueClass,
structClass,
unionClass,
abiSpecificIntegerClass,
];
final superClass = klass.superclass;
for (final onlyDirectExtendsClass in onlyDirectExtendsClasses) {
if (hierarchy.isSubtypeOf(klass, onlyDirectExtendsClass)) {

View File

@ -0,0 +1,106 @@
// Copyright (c) 2021, 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.
import 'dart:ffi';
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint32(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint32(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint32(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint32(),
Abi.iosX64: Uint32(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint32(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint32(),
Abi.macosArm64: Uint32(),
Abi.macosX64: Uint32(),
Abi.windowsArm64: Uint16(),
Abi.windowsIA32: Uint16(),
Abi.windowsX64: Uint16(),
})
class WChar extends AbiSpecificInteger {
const WChar();
}
void main() {
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
}
void testSizeOf() {
final size = sizeOf<WChar>();
print(size);
}
void testStoreLoad() {
final p = noAlloc<WChar>();
p.value = 10;
print(p.value);
noAlloc.free(p);
}
void testStoreLoadIndexed() {
final p = noAlloc<WChar>(2);
p[0] = 10;
p[1] = 3;
print(p[0]);
print(p[1]);
noAlloc.free(p);
}
class WCharStruct extends Struct {
@WChar()
external int a0;
@WChar()
external int a1;
}
void testStruct() {
final p = noAlloc<WCharStruct>();
p.ref.a0 = 1;
print(p.ref.a0);
p.ref.a0 = 2;
print(p.ref.a0);
noAlloc.free(p);
}
class WCharArrayStruct extends Struct {
@Array(100)
external Array<WChar> a0;
}
void testInlineArray() {
final p = noAlloc<WCharArrayStruct>();
final array = p.ref.a0;
for (int i = 0; i < 100; i++) {
array[i] = i;
}
for (int i = 0; i < 100; i++) {
print(array[i]);
}
noAlloc.free(p);
}
const noAlloc = _DummyAllocator();
class _DummyAllocator implements Allocator {
const _DummyAllocator();
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
return Pointer.fromAddress(0);
}
@override
void free(Pointer pointer) {}
}

View File

@ -0,0 +1,205 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:ffi" as ffi;
import "dart:core" as core;
import "dart:typed_data" as typ;
import "dart:_internal" as _in;
import "dart:ffi";
@#C49
@#C56
class WChar extends ffi::AbiSpecificInteger /*hasConstConstructor*/ {
const constructor •() → self::WChar
: super ffi::AbiSpecificInteger::•()
;
@#C59
static get #sizeOf() → core::int*
return #C61.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C66
class WCharStruct extends ffi::Struct {
synthetic constructor •() → self::WCharStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::WCharStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
@#C67
get a0() → core::int
return ffi::_loadAbiSpecificInt<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C68.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C67
set a0(core::int #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C68.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C67
get a1() → core::int
return ffi::_loadAbiSpecificInt<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C61.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C67
set a1(core::int #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<self::WChar>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C61.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C59
static get #sizeOf() → core::int*
return #C70.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
@#C75
class WCharArrayStruct extends ffi::Struct {
synthetic constructor •() → self::WCharArrayStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::WCharArrayStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
@#C76
get a0() → ffi::Array<self::WChar>
return new ffi::Array::_<self::WChar>( block {
core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};
core::int #offset = #C68.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<self::WChar>(#typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, #C80.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}){([core::int, core::int?]) → typ::Uint8List}, #C71, #C81);
@#C76
set a0(ffi::Array<self::WChar> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C68.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::Array::_typedDataBase}{core::Object}, #C1, #C80.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C59
static get #sizeOf() → core::int*
return #C80.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
}
class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
const constructor •() → self::_DummyAllocator
: super core::Object::•()
;
@#C82
method allocate<T extends ffi::NativeType>(core::int byteCount, {core::int? alignment = #C58}) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
return ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
}
@#C82
method free(ffi::Pointer<ffi::NativeType> pointer) → void {}
}
static const field self::_DummyAllocator noAlloc = #C83;
static method main() → void {
self::testSizeOf();
self::testStoreLoad();
self::testStoreLoadIndexed();
self::testStruct();
self::testInlineArray();
}
static method testSizeOf() → void {
final core::int size = self::WChar::#sizeOf;
core::print(size);
}
static method testStoreLoad() → void {
final ffi::Pointer<self::WChar> p = #C83.{ffi::Allocator::allocate}<self::WChar>(self::WChar::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
ffi::_storeAbiSpecificInt<self::WChar>(p, #C1, 10);
core::print(ffi::_loadAbiSpecificInt<self::WChar>(p, #C1));
#C83.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStoreLoadIndexed() → void {
final ffi::Pointer<self::WChar> p = #C83.{ffi::Allocator::allocate}<self::WChar>(2.{core::num::*}(self::WChar::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, 0, 10);
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, 1, 3);
core::print(ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, 0));
core::print(ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, 1));
#C83.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStruct() → void {
final ffi::Pointer<self::WCharStruct> p = #C83.{ffi::Allocator::allocate}<self::WCharStruct>(self::WCharStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharStruct>};
new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0} = 1;
core::print(new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0}{core::int});
new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0} = 2;
core::print(new self::WCharStruct::#fromTypedDataBase(p!).{self::WCharStruct::a0}{core::int});
#C83.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testInlineArray() → void {
final ffi::Pointer<self::WCharArrayStruct> p = #C83.{ffi::Allocator::allocate}<self::WCharArrayStruct>(self::WCharArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharArrayStruct>};
final ffi::Array<self::WChar> array = new self::WCharArrayStruct::#fromTypedDataBase(p!).{self::WCharArrayStruct::a0}{ffi::Array<self::WChar>};
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(array.{ffi::Array::_typedDataBase}{core::Object}, i, i);
}
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
core::print(ffi::_loadAbiSpecificIntAtIndex<self::WChar>(array.{ffi::Array::_typedDataBase}{core::Object}, i));
}
#C83.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 0
#C2 = "android"
#C3 = ffi::_OS {index:#C1, _name:#C2}
#C4 = "arm"
#C5 = ffi::_Architecture {index:#C1, _name:#C4}
#C6 = ffi::Abi {_os:#C3, _architecture:#C5}
#C7 = ffi::Uint32 {}
#C8 = 1
#C9 = "arm64"
#C10 = ffi::_Architecture {index:#C8, _name:#C9}
#C11 = ffi::Abi {_os:#C3, _architecture:#C10}
#C12 = 2
#C13 = "ia32"
#C14 = ffi::_Architecture {index:#C12, _name:#C13}
#C15 = ffi::Abi {_os:#C3, _architecture:#C14}
#C16 = 3
#C17 = "x64"
#C18 = ffi::_Architecture {index:#C16, _name:#C17}
#C19 = ffi::Abi {_os:#C3, _architecture:#C18}
#C20 = "fuchsia"
#C21 = ffi::_OS {index:#C8, _name:#C20}
#C22 = ffi::Abi {_os:#C21, _architecture:#C10}
#C23 = ffi::Uint64 {}
#C24 = ffi::Abi {_os:#C21, _architecture:#C18}
#C25 = "ios"
#C26 = ffi::_OS {index:#C12, _name:#C25}
#C27 = ffi::Abi {_os:#C26, _architecture:#C5}
#C28 = ffi::Abi {_os:#C26, _architecture:#C10}
#C29 = ffi::Abi {_os:#C26, _architecture:#C18}
#C30 = "linux"
#C31 = ffi::_OS {index:#C16, _name:#C30}
#C32 = ffi::Abi {_os:#C31, _architecture:#C5}
#C33 = ffi::Abi {_os:#C31, _architecture:#C10}
#C34 = ffi::Abi {_os:#C31, _architecture:#C14}
#C35 = ffi::Abi {_os:#C31, _architecture:#C18}
#C36 = 4
#C37 = "macos"
#C38 = ffi::_OS {index:#C36, _name:#C37}
#C39 = ffi::Abi {_os:#C38, _architecture:#C10}
#C40 = ffi::Abi {_os:#C38, _architecture:#C18}
#C41 = 5
#C42 = "windows"
#C43 = ffi::_OS {index:#C41, _name:#C42}
#C44 = ffi::Abi {_os:#C43, _architecture:#C10}
#C45 = ffi::Uint16 {}
#C46 = ffi::Abi {_os:#C43, _architecture:#C14}
#C47 = ffi::Abi {_os:#C43, _architecture:#C18}
#C48 = <ffi::Abi*, ffi::NativeType*>{#C6:#C7, #C11:#C7, #C15:#C7, #C19:#C7, #C22:#C23, #C24:#C7, #C27:#C7, #C28:#C7, #C29:#C7, #C32:#C7, #C33:#C7, #C34:#C7, #C35:#C7, #C39:#C7, #C40:#C7, #C44:#C45, #C46:#C45, #C47:#C45)
#C49 = ffi::AbiSpecificIntegerMapping {mapping:#C48}
#C50 = "vm:ffi:abi-specific-mapping"
#C51 = TypeLiteralConstant(ffi::Uint32)
#C52 = TypeLiteralConstant(ffi::Uint64)
#C53 = TypeLiteralConstant(ffi::Uint16)
#C54 = <core::Type?>[#C51, #C51, #C51, #C51, #C52, #C51, #C51, #C51, #C51, #C51, #C51, #C51, #C51, #C51, #C51, #C53, #C53, #C53]
#C55 = ffi::_FfiAbiSpecificMapping {nativeTypes:#C54}
#C56 = core::pragma {name:#C50, options:#C55}
#C57 = "vm:prefer-inline"
#C58 = null
#C59 = core::pragma {name:#C57, options:#C58}
#C60 = 8
#C61 = <core::int*>[#C36, #C36, #C36, #C36, #C60, #C36, #C36, #C36, #C36, #C36, #C36, #C36, #C36, #C36, #C36, #C12, #C12, #C12]
#C62 = "vm:ffi:struct-fields"
#C63 = TypeLiteralConstant(self::WChar)
#C64 = <core::Type>[#C63, #C63]
#C65 = ffi::_FfiStructLayout {fieldTypes:#C64, packing:#C58}
#C66 = core::pragma {name:#C62, options:#C65}
#C67 = self::WChar {}
#C68 = <core::int*>[#C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1, #C1]
#C69 = 16
#C70 = <core::int*>[#C60, #C60, #C60, #C60, #C69, #C60, #C60, #C60, #C60, #C60, #C60, #C60, #C60, #C60, #C60, #C36, #C36, #C36]
#C71 = 100
#C72 = ffi::_FfiInlineArray {elementType:#C63, length:#C71}
#C73 = <core::Type>[#C72]
#C74 = ffi::_FfiStructLayout {fieldTypes:#C73, packing:#C58}
#C75 = core::pragma {name:#C62, options:#C74}
#C76 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C71, dimension2:#C58, dimension3:#C58, dimension4:#C58, dimension5:#C58, dimensions:#C58}
#C77 = 400
#C78 = 800
#C79 = 200
#C80 = <core::int*>[#C77, #C77, #C77, #C77, #C78, #C77, #C77, #C77, #C77, #C77, #C77, #C77, #C77, #C77, #C77, #C79, #C79, #C79]
#C81 = <core::int*>[]
#C82 = core::_Override {}
#C83 = self::_DummyAllocator {}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2021, 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.
import 'dart:ffi';
@AbiSpecificIntegerMapping({
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint32(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint32(),
})
class Incomplete extends AbiSpecificInteger {
const Incomplete();
}
void main() {
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
}
void testSizeOf() {
final size = sizeOf<Incomplete>();
print(size);
}
void testStoreLoad() {
final p = noAlloc<Incomplete>();
p.value = 10;
print(p.value);
noAlloc.free(p);
}
void testStoreLoadIndexed() {
final p = noAlloc<Incomplete>(2);
p[0] = 10;
p[1] = 3;
print(p[0]);
print(p[1]);
noAlloc.free(p);
}
class IncompleteStruct extends Struct {
@Incomplete()
external int a0;
@Incomplete()
external int a1;
}
void testStruct() {
final p = noAlloc<IncompleteStruct>();
p.ref.a0 = 1;
print(p.ref.a0);
p.ref.a0 = 2;
print(p.ref.a0);
noAlloc.free(p);
}
class IncompleteArrayStruct extends Struct {
@Array(100)
external Array<Incomplete> a0;
}
void testInlineArray() {
final p = noAlloc<IncompleteArrayStruct>();
final array = p.ref.a0;
for (int i = 0; i < 100; i++) {
array[i] = i;
}
for (int i = 0; i < 100; i++) {
print(array[i]);
}
noAlloc.free(p);
}
const noAlloc = _DummyAllocator();
class _DummyAllocator implements Allocator {
const _DummyAllocator();
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
return Pointer.fromAddress(0);
}
@override
void free(Pointer pointer) {}
}

View File

@ -0,0 +1,173 @@
library #lib /*isNonNullableByDefault*/;
import self as self;
import "dart:ffi" as ffi;
import "dart:core" as core;
import "dart:typed_data" as typ;
import "dart:_internal" as _in;
import "dart:ffi";
@#C21
@#C27
class Incomplete extends ffi::AbiSpecificInteger /*hasConstConstructor*/ {
const constructor •() → self::Incomplete
: super ffi::AbiSpecificInteger::•()
;
@#C29
static get #sizeOf() → core::int*
return ffi::_checkAbiSpecificIntegerMapping<core::int>(#C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
}
@#C36
class IncompleteStruct extends ffi::Struct {
synthetic constructor •() → self::IncompleteStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::IncompleteStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
@#C37
get a0() → core::int
return ffi::_loadAbiSpecificInt<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
@#C37
set a0(core::int #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue);
@#C37
get a1() → core::int
return ffi::_loadAbiSpecificInt<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
@#C37
set a1(core::int #externalFieldValue) → void
return ffi::_storeAbiSpecificInt<self::Incomplete>(this.{ffi::_Compound::_typedDataBase}{core::Object}, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C31.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}), #externalFieldValue);
@#C29
static get #sizeOf() → core::int*
return ffi::_checkAbiSpecificIntegerMapping<core::int>(#C40.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
}
@#C45
class IncompleteArrayStruct extends ffi::Struct {
synthetic constructor •() → self::IncompleteArrayStruct
: super ffi::Struct::•()
;
constructor #fromTypedDataBase(core::Object #typedDataBase) → self::IncompleteArrayStruct
: super ffi::Struct::_fromTypedDataBase(#typedDataBase)
;
@#C46
get a0() → ffi::Array<self::Incomplete>
return new ffi::Array::_<self::Incomplete>( block {
core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase}{core::Object};
core::int #offset = #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int*};
} =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<self::Incomplete>(#typedDataBase.{ffi::Pointer::address}{core::int}.{core::num::+}(#offset){(core::num) → core::num}) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}{typ::ByteBuffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}{core::int}.{core::num::+}(#offset){(core::num) → core::num}, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C48.{core::List::[]}(ffi::_abi()){(core::int) → core::int*})){([core::int, core::int?]) → typ::Uint8List}, #C41, #C49);
@#C46
set a0(ffi::Array<self::Incomplete> #externalFieldValue) → void
return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}{core::Object}, #C38.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}, #externalFieldValue.{ffi::Array::_typedDataBase}{core::Object}, #C4, ffi::_checkAbiSpecificIntegerMapping<core::int>(#C48.{core::List::[]}(ffi::_abi()){(core::int) → core::int*}));
@#C29
static get #sizeOf() → core::int*
return ffi::_checkAbiSpecificIntegerMapping<core::int>(#C48.{core::List::[]}(ffi::_abi()){(core::int) → core::int*});
}
class _DummyAllocator extends core::Object implements ffi::Allocator /*hasConstConstructor*/ {
const constructor •() → self::_DummyAllocator
: super core::Object::•()
;
@#C50
method allocate<T extends ffi::NativeType>(core::int byteCount, {core::int? alignment = #C23}) → ffi::Pointer<self::_DummyAllocator::allocate::T> {
return ffi::Pointer::fromAddress<self::_DummyAllocator::allocate::T>(0);
}
@#C50
method free(ffi::Pointer<ffi::NativeType> pointer) → void {}
}
static const field self::_DummyAllocator noAlloc = #C51;
static method main() → void {
self::testSizeOf();
self::testStoreLoad();
self::testStoreLoadIndexed();
self::testStruct();
self::testInlineArray();
}
static method testSizeOf() → void {
final core::int size = self::Incomplete::#sizeOf;
core::print(size);
}
static method testStoreLoad() → void {
final ffi::Pointer<self::Incomplete> p = #C51.{ffi::Allocator::allocate}<self::Incomplete>(self::Incomplete::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
ffi::_storeAbiSpecificInt<self::Incomplete>(p, #C4, 10);
core::print(ffi::_loadAbiSpecificInt<self::Incomplete>(p, #C4));
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStoreLoadIndexed() → void {
final ffi::Pointer<self::Incomplete> p = #C51.{ffi::Allocator::allocate}<self::Incomplete>(2.{core::num::*}(self::Incomplete::#sizeOf){(core::num) → core::num}){(core::int, {alignment: core::int?}) → ffi::Pointer<self::Incomplete>};
ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, 0, 10);
ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(p, 1, 3);
core::print(ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, 0));
core::print(ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(p, 1));
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testStruct() → void {
final ffi::Pointer<self::IncompleteStruct> p = #C51.{ffi::Allocator::allocate}<self::IncompleteStruct>(self::IncompleteStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteStruct>};
new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0} = 1;
core::print(new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0}{core::int});
new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0} = 2;
core::print(new self::IncompleteStruct::#fromTypedDataBase(p!).{self::IncompleteStruct::a0}{core::int});
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
static method testInlineArray() → void {
final ffi::Pointer<self::IncompleteArrayStruct> p = #C51.{ffi::Allocator::allocate}<self::IncompleteArrayStruct>(self::IncompleteArrayStruct::#sizeOf){(core::int, {alignment: core::int?}) → ffi::Pointer<self::IncompleteArrayStruct>};
final ffi::Array<self::Incomplete> array = new self::IncompleteArrayStruct::#fromTypedDataBase(p!).{self::IncompleteArrayStruct::a0}{ffi::Array<self::Incomplete>};
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
ffi::_storeAbiSpecificIntAtIndex<self::Incomplete>(array.{ffi::Array::_typedDataBase}{core::Object}, i, i);
}
for (core::int i = 0; i.{core::num::<}(100){(core::num) → core::bool}; i = i.{core::num::+}(1){(core::num) → core::int}) {
core::print(ffi::_loadAbiSpecificIntAtIndex<self::Incomplete>(array.{ffi::Array::_typedDataBase}{core::Object}, i));
}
#C51.{self::_DummyAllocator::free}(p){(ffi::Pointer<ffi::NativeType>) → void};
}
constants {
#C1 = 3
#C2 = "linux"
#C3 = ffi::_OS {index:#C1, _name:#C2}
#C4 = 0
#C5 = "arm"
#C6 = ffi::_Architecture {index:#C4, _name:#C5}
#C7 = ffi::Abi {_os:#C3, _architecture:#C6}
#C8 = ffi::Uint32 {}
#C9 = 1
#C10 = "arm64"
#C11 = ffi::_Architecture {index:#C9, _name:#C10}
#C12 = ffi::Abi {_os:#C3, _architecture:#C11}
#C13 = 2
#C14 = "ia32"
#C15 = ffi::_Architecture {index:#C13, _name:#C14}
#C16 = ffi::Abi {_os:#C3, _architecture:#C15}
#C17 = "x64"
#C18 = ffi::_Architecture {index:#C1, _name:#C17}
#C19 = ffi::Abi {_os:#C3, _architecture:#C18}
#C20 = <ffi::Abi*, ffi::NativeType*>{#C7:#C8, #C12:#C8, #C16:#C8, #C19:#C8)
#C21 = ffi::AbiSpecificIntegerMapping {mapping:#C20}
#C22 = "vm:ffi:abi-specific-mapping"
#C23 = null
#C24 = TypeLiteralConstant(ffi::Uint32)
#C25 = <core::Type?>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C24, #C24, #C24, #C24, #C23, #C23, #C23, #C23, #C23]
#C26 = ffi::_FfiAbiSpecificMapping {nativeTypes:#C25}
#C27 = core::pragma {name:#C22, options:#C26}
#C28 = "vm:prefer-inline"
#C29 = core::pragma {name:#C28, options:#C23}
#C30 = 4
#C31 = <core::int*>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C30, #C30, #C30, #C30, #C23, #C23, #C23, #C23, #C23]
#C32 = "vm:ffi:struct-fields"
#C33 = TypeLiteralConstant(self::Incomplete)
#C34 = <core::Type>[#C33, #C33]
#C35 = ffi::_FfiStructLayout {fieldTypes:#C34, packing:#C23}
#C36 = core::pragma {name:#C32, options:#C35}
#C37 = self::Incomplete {}
#C38 = <core::int*>[#C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4, #C4]
#C39 = 8
#C40 = <core::int*>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C39, #C39, #C39, #C39, #C23, #C23, #C23, #C23, #C23]
#C41 = 100
#C42 = ffi::_FfiInlineArray {elementType:#C33, length:#C41}
#C43 = <core::Type>[#C42]
#C44 = ffi::_FfiStructLayout {fieldTypes:#C43, packing:#C23}
#C45 = core::pragma {name:#C32, options:#C44}
#C46 = ffi::_ArraySize<ffi::NativeType*> {dimension1:#C41, dimension2:#C23, dimension3:#C23, dimension4:#C23, dimension5:#C23, dimensions:#C23}
#C47 = 400
#C48 = <core::int*>[#C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C23, #C47, #C47, #C47, #C47, #C23, #C23, #C23, #C23, #C23]
#C49 = <core::int*>[]
#C50 = core::_Override {}
#C51 = self::_DummyAllocator {}
}

View File

@ -22,6 +22,6 @@ class Class extends core::Object {
synthetic constructor •() → self::Class
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3303,getterSelectorId:3304] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3304,getterSelectorId:3305] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
}

View File

@ -51,6 +51,6 @@ class ConstClass extends core::Object {
synthetic constructor •() → self::ConstClass
: super core::Object::•()
;
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3307,getterSelectorId:3308] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3308,getterSelectorId:3309] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
}

View File

@ -1133,4 +1133,38 @@ DART_EXPORT uint64_t SizeOfStruct3BytesPackedInt() {
return sizeof(Struct3BytesPackedIntCopy);
}
// Define ssize_t for Windows as intptr_t.
#if defined(_WIN32)
typedef intptr_t ssize_t;
#endif
#define DEFINE_SIZE_OF(type_modifier, type) \
DART_EXPORT uint64_t FfiSizeOf_##type_modifier##_##type() { \
return sizeof(type_modifier type); \
}
#define SIZES(F) \
F(, intptr_t) \
F(, uintptr_t) \
F(, int) \
F(unsigned, int) \
F(, long) /* NOLINT */ \
F(unsigned, long) /* NOLINT */ \
F(, wchar_t) \
F(, size_t) \
F(, ssize_t) \
F(, off_t)
SIZES(DEFINE_SIZE_OF)
#undef DEFINE_SIZE_OF
#undef SIZES
DART_EXPORT int64_t WCharMinValue() {
return WCHAR_MIN;
}
DART_EXPORT int64_t WCharMaxValue() {
return WCHAR_MAX;
}
} // namespace dart

View File

@ -566,6 +566,10 @@ union Union16BytesNestedFloat {
Struct16BytesHomogeneousFloat a2;
};
struct StructInlineArrayInt {
wchar_t a0[10];
};
// Used for testing structs and unions by value.
// Smallest struct with data.
// 10 struct arguments will exhaust available registers.
@ -4894,6 +4898,46 @@ DART_EXPORT bool PassUint8Struct1ByteBool(uint8_t a0, Struct1ByteBool a1) {
return result % 2 != 0;
}
// Used for testing structs and unions by value.
// Returning a wchar.
DART_EXPORT wchar_t PassWCharStructInlineArrayIntUintPtrx2LongUnsigned(
wchar_t a0,
StructInlineArrayInt a1,
uintptr_t a2,
uintptr_t a3,
/* NOLINT(runtime/int) */ long a4,
/* NOLINT(runtime/int) */ unsigned long a5) {
std::cout << "PassWCharStructInlineArrayIntUintPtrx2LongUnsigned"
<< "(" << a0 << ", ([" << a1.a0[0] << ", " << a1.a0[1] << ", "
<< a1.a0[2] << ", " << a1.a0[3] << ", " << a1.a0[4] << ", "
<< a1.a0[5] << ", " << a1.a0[6] << ", " << a1.a0[7] << ", "
<< a1.a0[8] << ", " << a1.a0[9] << "]), " << a2 << ", " << a3
<< ", " << a4 << ", " << a5 << ")"
<< "\n";
wchar_t result = 0;
result += a0;
result += a1.a0[0];
result += a1.a0[1];
result += a1.a0[2];
result += a1.a0[3];
result += a1.a0[4];
result += a1.a0[5];
result += a1.a0[6];
result += a1.a0[7];
result += a1.a0[8];
result += a1.a0[9];
result += a2;
result += a3;
result += a4;
result += a5;
std::cout << "result = " << result << "\n";
return result;
}
// Used for testing structs and unions by value.
// Smallest struct with data.
DART_EXPORT Struct1ByteInt ReturnStruct1ByteInt(int8_t a0) {
@ -12599,21 +12643,85 @@ DART_EXPORT intptr_t TestPassUint8Struct1ByteBool(
std::cout << "result = " << result << "\n";
CHECK_APPROX(1, result);
CHECK_EQ(1, result);
// Pass argument that will make the Dart callback throw.
a0 = 42;
result = f(a0, a1);
CHECK_APPROX(0.0, result);
CHECK_EQ(0, result);
// Pass argument that will make the Dart callback return null.
a0 = 84;
result = f(a0, a1);
CHECK_APPROX(0.0, result);
CHECK_EQ(0, result);
return 0;
}
// Used for testing structs and unions by value.
// Returning a wchar.
DART_EXPORT intptr_t TestPassWCharStructInlineArrayIntUintPtrx2LongUnsigned(
// NOLINTNEXTLINE(whitespace/parens)
wchar_t (*f)(wchar_t a0,
StructInlineArrayInt a1,
uintptr_t a2,
uintptr_t a3,
/* NOLINT(runtime/int) */ long a4,
/* NOLINT(runtime/int) */ unsigned long a5)) {
wchar_t a0;
StructInlineArrayInt a1 = {};
uintptr_t a2;
uintptr_t a3;
/* NOLINT(runtime/int) */ long a4;
/* NOLINT(runtime/int) */ unsigned long a5;
a0 = 1;
a1.a0[0] = 2;
a1.a0[1] = 3;
a1.a0[2] = 4;
a1.a0[3] = 5;
a1.a0[4] = 6;
a1.a0[5] = 7;
a1.a0[6] = 8;
a1.a0[7] = 9;
a1.a0[8] = 10;
a1.a0[9] = 11;
a2 = 12;
a3 = 13;
a4 = 14;
a5 = 15;
std::cout << "Calling TestPassWCharStructInlineArrayIntUintPtrx2LongUnsigned("
<< "(" << a0 << ", ([" << a1.a0[0] << ", " << a1.a0[1] << ", "
<< a1.a0[2] << ", " << a1.a0[3] << ", " << a1.a0[4] << ", "
<< a1.a0[5] << ", " << a1.a0[6] << ", " << a1.a0[7] << ", "
<< a1.a0[8] << ", " << a1.a0[9] << "]), " << a2 << ", " << a3
<< ", " << a4 << ", " << a5 << ")"
<< ")\n";
wchar_t result = f(a0, a1, a2, a3, a4, a5);
std::cout << "result = " << result << "\n";
CHECK_EQ(120, result);
// Pass argument that will make the Dart callback throw.
a0 = 42;
result = f(a0, a1, a2, a3, a4, a5);
CHECK_EQ(0, result);
// Pass argument that will make the Dart callback return null.
a0 = 84;
result = f(a0, a1, a2, a3, a4, a5);
CHECK_EQ(0, result);
return 0;
}

View File

@ -26,6 +26,7 @@
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/ffi/call.h"
#include "vm/compiler/ffi/callback.h"
#include "vm/compiler/ffi/marshaller.h"
#include "vm/compiler/jit/compiler.h"
#endif // !defined(DART_PRECOMPILED_RUNTIME)
@ -98,6 +99,15 @@ DEFINE_NATIVE_ENTRY(Ffi_nativeCallbackFunction, 1, 2) {
func = func.parent_function();
ASSERT(func.is_static());
// AbiSpecificTypes can have an incomplete mapping.
const char* error = nullptr;
compiler::ffi::NativeFunctionTypeFromFunctionType(zone, native_signature,
&error);
if (error != nullptr) {
Exceptions::ThrowCompileTimeError(LanguageError::Handle(
zone, LanguageError::New(String::Handle(zone, String::New(error)))));
}
// We are returning an object which is not an Instance here. This is only OK
// because we know that the result will be passed directly to
// _pointerFromFunction and will not leak out into user code.

View File

@ -6695,6 +6695,10 @@ SerializationCluster* Serializer::NewClusterForClass(intptr_t cid,
case kStringCid:
return new (Z) StringSerializationCluster(
is_canonical, cluster_represents_canonical_set && !vm_);
#define CASE_FFI_CID(name) case kFfi##name##Cid:
CLASS_LIST_FFI_TYPE_MARKER(CASE_FFI_CID)
#undef CASE_FFI_CID
return new (Z) InstanceSerializationCluster(is_canonical, cid);
case kWeakSerializationReferenceCid:
#if defined(DART_PRECOMPILER)
ASSERT(kind_ == Snapshot::kFullAOT);
@ -7850,6 +7854,10 @@ DeserializationCluster* Deserializer::ReadCluster() {
return new (Z) StringDeserializationCluster(
is_canonical,
!is_non_root_unit_ && isolate_group() != Dart::vm_isolate_group());
#define CASE_FFI_CID(name) case kFfi##name##Cid:
CLASS_LIST_FFI_TYPE_MARKER(CASE_FFI_CID)
#undef CASE_FFI_CID
return new (Z) InstanceDeserializationCluster(cid, is_canonical);
default:
break;
}

View File

@ -47,42 +47,58 @@ static_assert(offsetof(AbiAlignmentUint64, i) == 8,
#if defined(DART_TARGET_OS_ANDROID)
#define DART_TARGET_OS_NAME Android
#define DART_TARGET_OS_NAME_LC android
#elif defined(DART_TARGET_OS_FUCHSIA)
#define DART_TARGET_OS_NAME Fuchsia
#define DART_TARGET_OS_NAME_LC fuchsia
#elif defined(DART_TARGET_OS_LINUX)
#define DART_TARGET_OS_NAME Linux
#define DART_TARGET_OS_NAME_LC linux
#elif defined(DART_TARGET_OS_MACOS)
#if DART_TARGET_OS_MACOS_IOS
#define DART_TARGET_OS_NAME IOS
#define DART_TARGET_OS_NAME_LC ios
#else
#define DART_TARGET_OS_NAME MacOS
#define DART_TARGET_OS_NAME_LC macos
#endif
#elif defined(DART_TARGET_OS_WINDOWS)
#define DART_TARGET_OS_NAME Windows
#define DART_TARGET_OS_NAME_LC windows
#else
#error Unknown OS
#endif
#if defined(TARGET_ARCH_IA32)
#define TARGET_ARCH_NAME IA32
#define TARGET_ARCH_NAME_LC ia32
#elif defined(TARGET_ARCH_X64)
#define TARGET_ARCH_NAME X64
#define TARGET_ARCH_NAME_LC x64
#elif defined(TARGET_ARCH_ARM)
#define TARGET_ARCH_NAME Arm
#define TARGET_ARCH_NAME_LC arm
#elif defined(TARGET_ARCH_ARM64)
#define TARGET_ARCH_NAME Arm64
#define TARGET_ARCH_NAME_LC arm64
#else
#error Unknown arch
#endif
#define ABI_NAME1(os, arch) k##os##arch
#define ABI_NAME2(os, arch) ABI_NAME1(os, arch)
#define ABI_NAME3 ABI_NAME2(DART_TARGET_OS_NAME, TARGET_ARCH_NAME)
#define ABI_ENUM_VALUE1(os, arch) k##os##arch
#define ABI_ENUM_VALUE2(os, arch) ABI_ENUM_VALUE1(os, arch)
#define ABI_ENUM_VALUE3 ABI_ENUM_VALUE2(DART_TARGET_OS_NAME, TARGET_ARCH_NAME)
Abi TargetAbi() {
return Abi::ABI_NAME3;
return Abi::ABI_ENUM_VALUE3;
}
#define STRINGIFY2(s) STRINGIFY(s)
#define STRINGIFY(s) #s
const char* target_abi_name =
STRINGIFY2(DART_TARGET_OS_NAME_LC) "_" STRINGIFY2(TARGET_ARCH_NAME_LC);
} // namespace ffi
} // namespace compiler

View File

@ -39,16 +39,23 @@ enum class Abi {
kWindowsIA32,
kWindowsX64,
};
const int64_t num_abis = static_cast<int64_t>(Abi::kWindowsX64) + 1;
// We use the integer values of this enum in
// runtime/vm/compiler/frontend/kernel_to_il.cc
// - runtime/vm/compiler/ffi/native_type.cc
// - runtime/vm/compiler/frontend/kernel_to_il.cc
static_assert(static_cast<int64_t>(Abi::kAndroidArm) == 0,
"Enum value unexpected.");
static_assert(static_cast<int64_t>(Abi::kWindowsX64) == 17,
"Enum value unexpected.");
static_assert(num_abis == 18, "Enum value unexpected.");
// The target ABI. Defines sizes and alignment of native types.
Abi TargetAbi();
extern const char* target_abi_name;
} // namespace ffi
} // namespace compiler

View File

@ -91,17 +91,22 @@ bool BaseMarshaller::IsCompound(intptr_t arg_index) const {
if (IsFfiTypeClassId(type.type_class_id())) {
return false;
}
#ifdef DEBUG
const auto& cls = Class::Handle(this->zone_, type.type_class());
const auto& superClass = Class::Handle(this->zone_, cls.SuperClass());
// TODO(http://dartbug.com/42563): Implement AbiSpecificInt.
const bool is_abi_specific_int =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::AbiSpecificInteger());
if (is_abi_specific_int) {
return false;
}
#ifdef DEBUG
const bool is_struct =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union =
String::Handle(this->zone_, superClass.UserVisibleName())
.Equals(Symbols::Union());
RELEASE_ASSERT(is_struct || is_union);
ASSERT(is_struct || is_union);
#endif
return true;
}

View File

@ -474,7 +474,33 @@ static const NativeType* CompoundFromPragma(Zone* zone,
}
}
// TODO(http://dartbug.com/42563): Implement AbiSpecificInt.
static const NativeType* AbiSpecificFromPragma(Zone* zone,
const Instance& pragma,
const Class& abi_specific_int,
const char** error) {
const auto& clazz = Class::Handle(zone, pragma.clazz());
const auto& fields = Array::Handle(zone, clazz.fields());
ASSERT(fields.Length() == 1);
const auto& native_types_field =
Field::Handle(zone, Field::RawCast(fields.At(0)));
ASSERT(String::Handle(zone, native_types_field.name())
.Equals(Symbols::FfiNativeTypes()));
const auto& native_types =
Array::Handle(zone, Array::RawCast(pragma.GetField(native_types_field)));
ASSERT(native_types.Length() == num_abis);
const int64_t abi_index = static_cast<int64_t>(TargetAbi());
const auto& abi_abstract_type = AbstractType::Handle(
zone, AbstractType::RawCast(native_types.At(abi_index)));
if (abi_abstract_type.IsNull()) {
*error = zone->PrintToString(
"AbiSpecificInteger '%s' is missing mapping for '%s'.",
abi_specific_int.UserVisibleNameCString(), target_abi_name);
return nullptr;
}
return NativeType::FromAbstractType(zone, abi_abstract_type, error);
}
const NativeType* NativeType::FromAbstractType(Zone* zone,
const AbstractType& type,
const char** error) {
@ -483,18 +509,26 @@ const NativeType* NativeType::FromAbstractType(Zone* zone,
return &NativeType::FromTypedDataClassId(zone, class_id);
}
// User-defined structs or unions.
// User-defined structs, unions, or Abi-specific integers.
const auto& cls = Class::Handle(zone, type.type_class());
const auto& superClass = Class::Handle(zone, cls.SuperClass());
const bool is_struct = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Union());
RELEASE_ASSERT(is_struct || is_union);
const bool is_abi_specific_int =
String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::AbiSpecificInteger());
RELEASE_ASSERT(is_struct || is_union || is_abi_specific_int);
auto& pragmas = Object::Handle(zone);
String& pragma_name = String::Handle(zone);
pragma_name = Symbols::vm_ffi_struct_fields().ptr();
if (is_struct || is_union) {
pragma_name = Symbols::vm_ffi_struct_fields().ptr();
} else {
ASSERT(is_abi_specific_int);
pragma_name = Symbols::vm_ffi_abi_specific_mapping().ptr();
}
Library::FindPragma(dart::Thread::Current(), /*only_core=*/false, cls,
pragma_name, /*multiple=*/true, &pragmas);
ASSERT(!pragmas.IsNull());
@ -503,7 +537,13 @@ const NativeType* NativeType::FromAbstractType(Zone* zone,
auto& pragma = Instance::Handle(zone);
auto& clazz = Class::Handle(zone);
auto& library = Library::Handle(zone);
const String& class_symbol = Symbols::FfiStructLayout();
String& class_symbol = String::Handle(zone);
if (is_struct || is_union) {
class_symbol = Symbols::FfiStructLayout().ptr();
} else {
ASSERT(is_abi_specific_int);
class_symbol = Symbols::FfiAbiSpecificMapping().ptr();
}
for (intptr_t i = 0; i < pragmas_array.Length(); i++) {
pragma ^= pragmas_array.At(i);
clazz ^= pragma.clazz();
@ -514,7 +554,11 @@ const NativeType* NativeType::FromAbstractType(Zone* zone,
}
}
return CompoundFromPragma(zone, pragma, is_struct, error);
if (is_struct || is_union) {
return CompoundFromPragma(zone, pragma, is_struct, error);
}
ASSERT(is_abi_specific_int);
return AbiSpecificFromPragma(zone, pragma, cls, error);
}
#endif

View File

@ -118,6 +118,36 @@ AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind) {
}
}
MethodRecognizer::Kind FfiLoad(const NativeType& native_type) {
if (native_type.IsPrimitive()) {
switch (native_type.AsPrimitive().representation()) {
#define CASE(type) \
case k##type: \
return MethodRecognizer::kFfiLoad##type;
CLASS_LIST_FFI_NUMERIC_FIXED_SIZE(CASE)
#undef CASE
default:
break;
}
}
UNIMPLEMENTED();
}
MethodRecognizer::Kind FfiStore(const NativeType& native_type) {
if (native_type.IsPrimitive()) {
switch (native_type.AsPrimitive().representation()) {
#define CASE(type) \
case k##type: \
return MethodRecognizer::kFfiStore##type;
CLASS_LIST_FFI_NUMERIC_FIXED_SIZE(CASE)
#undef CASE
default:
break;
}
}
UNIMPLEMENTED();
}
} // namespace ffi
} // namespace compiler

View File

@ -32,6 +32,9 @@ classid_t RecognizedMethodTypeArgCid(MethodRecognizer::Kind kind);
AlignmentType RecognizedMethodAlignment(MethodRecognizer::Kind kind);
MethodRecognizer::Kind FfiLoad(const NativeType& native_type);
MethodRecognizer::Kind FfiStore(const NativeType& native_type);
} // namespace ffi
} // namespace compiler

View File

@ -881,6 +881,19 @@ JoinEntryInstr* BaseFlowGraphBuilder::BuildThrowNoSuchMethod() {
return nsm;
}
Fragment BaseFlowGraphBuilder::ThrowException(TokenPosition position) {
Fragment instructions;
Value* exception = Pop();
instructions += Fragment(new (Z) ThrowInstr(InstructionSource(position),
GetNextDeoptId(), exception))
.closed();
// Use its side effect of leaving a constant on the stack (does not change
// the graph).
NullConstant();
return instructions;
}
Fragment BaseFlowGraphBuilder::AssertBool(TokenPosition position) {
Value* value = Pop();
AssertBooleanInstr* instr = new (Z)
@ -1000,14 +1013,23 @@ Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall(
ASSERT(signatures.IsInstantiated());
ASSERT(signatures.Length() == 2);
const AbstractType& dart_type = AbstractType::Handle(signatures.TypeAt(0));
const AbstractType& native_type = AbstractType::Handle(signatures.TypeAt(1));
const auto& dart_type =
FunctionType::Cast(AbstractType::Handle(signatures.TypeAt(0)));
const auto& native_type =
FunctionType::Cast(AbstractType::Handle(signatures.TypeAt(1)));
ASSERT(dart_type.IsFunctionType() && native_type.IsFunctionType());
const Function& target =
Function::ZoneHandle(compiler::ffi::TrampolineFunction(
FunctionType::Cast(dart_type), FunctionType::Cast(native_type),
is_leaf));
// AbiSpecificTypes can have an incomplete mapping.
const char* error = nullptr;
compiler::ffi::NativeFunctionTypeFromFunctionType(zone_, native_type, &error);
if (error != nullptr) {
const auto& language_error = Error::Handle(
LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
Report::kError, Heap::kOld));
Report::LongJump(language_error);
}
const Function& target = Function::ZoneHandle(
compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf));
Fragment code;
// Store the pointer in the context, we cannot load the untagged address

View File

@ -348,6 +348,7 @@ class BaseFlowGraphBuilder {
Fragment TestAnyTypeArgs(Fragment present, Fragment absent);
JoinEntryInstr* BuildThrowNoSuchMethod();
Fragment ThrowException(TokenPosition position);
Fragment AssertBool(TokenPosition position);
Fragment BooleanNegate();

View File

@ -6,6 +6,7 @@
#include "vm/closure_functions_cache.h"
#include "vm/compiler/ffi/callback.h"
#include "vm/compiler/ffi/recognized_method.h"
#include "vm/compiler/frontend/flow_graph_builder.h" // For dart::FlowGraphBuilder::SimpleInstanceOfType.
#include "vm/compiler/frontend/prologue_builder.h"
#include "vm/compiler/jit/compiler.h"
@ -3416,13 +3417,26 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
}
const auto recognized_kind = target.recognized_kind();
if (recognized_kind == MethodRecognizer::kNativeEffect) {
return BuildNativeEffect();
} else if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) {
return BuildFfiAsFunctionInternal();
} else if (CompilerState::Current().is_aot() &&
recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) {
return BuildFfiNativeCallbackFunction();
switch (recognized_kind) {
case MethodRecognizer::kNativeEffect:
return BuildNativeEffect();
case MethodRecognizer::kFfiAsFunctionInternal:
return BuildFfiAsFunctionInternal();
case MethodRecognizer::kFfiNativeCallbackFunction:
if (CompilerState::Current().is_aot()) {
return BuildFfiNativeCallbackFunction();
}
break;
case MethodRecognizer::kFfiLoadAbiSpecificInt:
return BuildLoadAbiSpecificInt(/*at_index=*/false);
case MethodRecognizer::kFfiLoadAbiSpecificIntAtIndex:
return BuildLoadAbiSpecificInt(/*at_index=*/true);
case MethodRecognizer::kFfiStoreAbiSpecificInt:
return BuildStoreAbiSpecificInt(/*at_index=*/false);
case MethodRecognizer::kFfiStoreAbiSpecificIntAtIndex:
return BuildStoreAbiSpecificInt(/*at_index=*/true);
default:
break;
}
Fragment instructions;
@ -5517,6 +5531,109 @@ Fragment StreamingFlowGraphBuilder::BuildNativeEffect() {
return code;
}
static void ReportIfNotNull(const char* error) {
if (error != nullptr) {
const auto& language_error = Error::Handle(
LanguageError::New(String::Handle(String::New(error, Heap::kOld)),
Report::kError, Heap::kOld));
Report::LongJump(language_error);
}
}
Fragment StreamingFlowGraphBuilder::BuildLoadAbiSpecificInt(bool at_index) {
const intptr_t argument_count = ReadUInt(); // Read argument count.
ASSERT(argument_count == 2); // TypedDataBase, offset/index
const intptr_t list_length = ReadListLength(); // Read types list length.
ASSERT(list_length == 1); // AbiSpecificInt.
// Read types.
const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
const AbstractType& type_argument =
AbstractType::Handle(type_arguments.TypeAt(0));
// AbiSpecificTypes can have an incomplete mapping.
const char* error = nullptr;
const auto* native_type =
compiler::ffi::NativeType::FromAbstractType(zone_, type_argument, &error);
ReportIfNotNull(error);
Fragment code;
// Read positional argument count.
const intptr_t positional_count = ReadListLength();
ASSERT(positional_count == 2);
code += BuildExpression(); // Argument 1: typedDataBase.
code += BuildExpression(); // Argument 2: offsetInBytes or index.
if (at_index) {
code += IntConstant(native_type->SizeInBytes());
code += B->BinaryIntegerOp(Token::kMUL, kTagged, /* truncate= */ true);
}
// Skip (empty) named arguments list.
const intptr_t named_args_len = ReadListLength();
ASSERT(named_args_len == 0);
// This call site is not guaranteed to be optimized. So, do a call to the
// correct force optimized function instead of compiling the body.
MethodRecognizer::Kind kind = compiler::ffi::FfiLoad(*native_type);
const char* function_name = MethodRecognizer::KindToFunctionNameCString(kind);
const Library& ffi_library = Library::Handle(Z, Library::FfiLibrary());
const Function& target = Function::ZoneHandle(
Z, ffi_library.LookupFunctionAllowPrivate(
String::Handle(Z, String::New(function_name))));
Array& argument_names = Array::ZoneHandle(Z);
code += StaticCall(TokenPosition::kNoSource, target, argument_count,
argument_names, ICData::kStatic);
return code;
}
Fragment StreamingFlowGraphBuilder::BuildStoreAbiSpecificInt(bool at_index) {
const intptr_t argument_count = ReadUInt();
ASSERT(argument_count == 3);
const intptr_t list_length = ReadListLength();
ASSERT(list_length == 1);
// Read types.
const TypeArguments& type_arguments = T.BuildTypeArguments(list_length);
const AbstractType& type_argument =
AbstractType::Handle(type_arguments.TypeAt(0));
// AbiSpecificTypes can have an incomplete mapping.
const char* error = nullptr;
const auto* native_type =
compiler::ffi::NativeType::FromAbstractType(zone_, type_argument, &error);
ReportIfNotNull(error);
Fragment code;
// Read positional argument count.
const intptr_t positional_count = ReadListLength();
ASSERT(positional_count == 3);
code += BuildExpression(); // Argument 1: typedDataBase.
code += BuildExpression(); // Argument 2: offsetInBytes or index.
if (at_index) {
code += IntConstant(native_type->SizeInBytes());
code += B->BinaryIntegerOp(Token::kMUL, kTagged, /* truncate= */ true);
}
code += BuildExpression(); // Argument 3: value
// Skip (empty) named arguments list.
const intptr_t named_args_len = ReadListLength();
ASSERT(named_args_len == 0);
// This call site is not guaranteed to be optimized. So, do a call to the
// correct force optimized function instead of compiling the body.
MethodRecognizer::Kind kind = compiler::ffi::FfiStore(*native_type);
const char* function_name = MethodRecognizer::KindToFunctionNameCString(kind);
const Library& ffi_library = Library::Handle(Z, Library::FfiLibrary());
const Function& target = Function::ZoneHandle(
Z, ffi_library.LookupFunctionAllowPrivate(
String::Handle(Z, String::New(function_name))));
ASSERT(!target.IsNull());
Array& argument_names = Array::ZoneHandle(Z);
code += StaticCall(TokenPosition::kNoSource, target, argument_count,
argument_names, ICData::kStatic);
return code;
}
Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
const intptr_t argc = ReadUInt(); // Read argument count.
ASSERT(argc == 2); // Pointer, isLeaf.
@ -5595,6 +5712,11 @@ Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction() {
ReadListLength(); // Skip (empty) named arguments list.
ASSERT(named_args_len == 0);
// AbiSpecificTypes can have an incomplete mapping.
const char* error = nullptr;
compiler::ffi::NativeFunctionTypeFromFunctionType(zone_, native_sig, &error);
ReportIfNotNull(error);
const Function& result =
Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction(
native_sig, target, exceptional_return));

View File

@ -362,6 +362,15 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
// Build flow graph for '_nativeEffect'.
Fragment BuildNativeEffect();
// Build flow graph for '_loadAbiSpecificInt' and
// '_loadAbiSpecificIntAtIndex', '_storeAbiSpecificInt', and
// '_storeAbiSpecificIntAtIndex' call sites.
//
// The second argument is either offsetInBytes (at_index==false), or
// index (at_index==true).
Fragment BuildLoadAbiSpecificInt(bool at_index);
Fragment BuildStoreAbiSpecificInt(bool at_index);
// Build FG for '_asFunctionInternal'. Reads an Arguments from the
// Kernel buffer and pushes the resulting closure.
Fragment BuildFfiAsFunctionInternal();

View File

@ -402,19 +402,6 @@ Fragment FlowGraphBuilder::FfiCall(
return body;
}
Fragment FlowGraphBuilder::ThrowException(TokenPosition position) {
Fragment instructions;
Value* exception = Pop();
instructions += Fragment(new (Z) ThrowInstr(InstructionSource(position),
GetNextDeoptId(), exception))
.closed();
// Use its side effect of leaving a constant on the stack (does not change
// the graph).
NullConstant();
return instructions;
}
Fragment FlowGraphBuilder::RethrowException(TokenPosition position,
int catch_try_index) {
Fragment instructions;
@ -4491,6 +4478,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
const char* error = nullptr;
const auto marshaller_ptr =
compiler::ffi::CallMarshaller::FromFunction(Z, function, &error);
// AbiSpecific integers can be incomplete causing us to not know the calling
// convention. However, this is caught in asFunction in both JIT/AOT.
RELEASE_ASSERT(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr;
@ -4642,6 +4631,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) {
const char* error = nullptr;
const auto marshaller_ptr =
compiler::ffi::CallbackMarshaller::FromFunction(Z, function, &error);
// AbiSpecific integers can be incomplete causing us to not know the calling
// convention. However, this is caught fromFunction in both JIT/AOT.
RELEASE_ASSERT(error == nullptr);
RELEASE_ASSERT(marshaller_ptr != nullptr);
const auto& marshaller = *marshaller_ptr;

View File

@ -196,7 +196,6 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
Fragment ThrowException(TokenPosition position);
Fragment RethrowException(TokenPosition position, int catch_try_index);
Fragment LoadLocal(LocalVariable* variable);
Fragment IndirectGoto(intptr_t target_count);

View File

@ -197,6 +197,12 @@ const char* MethodRecognizer::KindToCString(Kind kind) {
return "?";
}
const char* MethodRecognizer::KindToFunctionNameCString(Kind kind) {
if (kind >= kUnknown && kind < kNumRecognizedMethods)
return recognized_methods[kind].function_name;
return "?";
}
// Is this method marked with the vm:recognized pragma?
bool MethodRecognizer::IsMarkedAsRecognized(const Function& function,
const char* kind) {

View File

@ -52,6 +52,7 @@ class MethodRecognizer : public AllStatic {
static intptr_t MethodKindToReceiverCid(Kind kind);
static const char* KindToCString(Kind kind);
static const char* KindToFunctionNameCString(Kind kind);
static bool IsMarkedAsRecognized(const Function& function,
const char* kind = nullptr);

View File

@ -191,28 +191,32 @@ namespace dart {
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x92ae104f) \
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3ff5ae9c) \
V(::, _nativeEffect, NativeEffect, 0x61e00b59) \
V(::, _loadInt8, FfiLoadInt8, 0x0f04dfd6) \
V(::, _loadInt16, FfiLoadInt16, 0xec44312d) \
V(::, _loadInt32, FfiLoadInt32, 0xee223fc3) \
V(::, _loadInt64, FfiLoadInt64, 0xdeefbfa3) \
V(::, _loadUint8, FfiLoadUint8, 0xe14e1cd1) \
V(::, _loadUint16, FfiLoadUint16, 0x0cd65cea) \
V(::, _loadUint32, FfiLoadUint32, 0xf66e9055) \
V(::, _loadUint64, FfiLoadUint64, 0x0505fdcc) \
V(::, _loadAbiSpecificInt, FfiLoadAbiSpecificInt, 0x7807e872) \
V(::, _loadAbiSpecificIntAtIndex, FfiLoadAbiSpecificIntAtIndex, 0x6aa4cab4) \
V(::, _loadInt8, FfiLoadInt8, 0x0f04e397) \
V(::, _loadInt16, FfiLoadInt16, 0xec4434ee) \
V(::, _loadInt32, FfiLoadInt32, 0xee224384) \
V(::, _loadInt64, FfiLoadInt64, 0xdeefc364) \
V(::, _loadUint8, FfiLoadUint8, 0xe14e2092) \
V(::, _loadUint16, FfiLoadUint16, 0x0cd660ab) \
V(::, _loadUint32, FfiLoadUint32, 0xf66e9416) \
V(::, _loadUint64, FfiLoadUint64, 0x0506018d) \
V(::, _loadIntPtr, FfiLoadIntPtr, 0xebd9b43e) \
V(::, _loadFloat, FfiLoadFloat, 0xf8d9845d) \
V(::, _loadFloatUnaligned, FfiLoadFloatUnaligned, 0xc8c8dfff) \
V(::, _loadDouble, FfiLoadDouble, 0xf70cc619) \
V(::, _loadDoubleUnaligned, FfiLoadDoubleUnaligned, 0xc99ebd39) \
V(::, _loadPointer, FfiLoadPointer, 0x4e79d0fc) \
V(::, _storeInt8, FfiStoreInt8, 0xdf50af0c) \
V(::, _storeInt16, FfiStoreInt16, 0xd84df332) \
V(::, _storeInt32, FfiStoreInt32, 0xfbe62c5d) \
V(::, _storeInt64, FfiStoreInt64, 0xf1d40d7a) \
V(::, _storeUint8, FfiStoreUint8, 0x056dd2f6) \
V(::, _storeUint16, FfiStoreUint16, 0xe2fdaade) \
V(::, _storeUint32, FfiStoreUint32, 0xe5d7e8c5) \
V(::, _storeUint64, FfiStoreUint64, 0xe2d93239) \
V(::, _storeAbiSpecificInt, FfiStoreAbiSpecificInt, 0xc70954c0) \
V(::, _storeAbiSpecificIntAtIndex, FfiStoreAbiSpecificIntAtIndex, 0xc64efe4b)\
V(::, _storeInt8, FfiStoreInt8, 0xdf50b2cd) \
V(::, _storeInt16, FfiStoreInt16, 0xd84df6f3) \
V(::, _storeInt32, FfiStoreInt32, 0xfbe6301e) \
V(::, _storeInt64, FfiStoreInt64, 0xf1d4113b) \
V(::, _storeUint8, FfiStoreUint8, 0x056dd6b7) \
V(::, _storeUint16, FfiStoreUint16, 0xe2fdae9f) \
V(::, _storeUint32, FfiStoreUint32, 0xe5d7ec86) \
V(::, _storeUint64, FfiStoreUint64, 0xe2d935fa) \
V(::, _storeIntPtr, FfiStoreIntPtr, 0x080266a8) \
V(::, _storeFloat, FfiStoreFloat, 0x6484f07e) \
V(::, _storeFloatUnaligned, FfiStoreFloatUnaligned, 0x600a9203) \

View File

@ -1588,6 +1588,25 @@ void KernelLoader::FinishClassLoading(const Class& klass,
fields_[0]->set_is_nullable(true);
}
// Check that subclasses of AbiSpecificInteger have a mapping for the
// current ABI.
//
// TODO(https://github.com/dart-lang/language/issues/1889): If we make
// kernel know about the target platform, we can move this check to the
// frontend.
const auto& super_class = Class::Handle(Z, klass.SuperClass());
if (!super_class.IsNull() &&
super_class.UserVisibleName() == Symbols::AbiSpecificInteger().ptr() &&
Library::Handle(Z, super_class.library()).url() ==
Symbols::DartFfi().ptr()) {
const char* error = nullptr;
compiler::ffi::NativeType::FromAbstractType(
Z, AbstractType::Handle(Z, klass.DeclarationType()), &error);
if (error != nullptr) {
H.ReportError("%s", error);
}
}
// Due to ReadVMAnnotations(), the klass may have been loaded at this point
// (loading the class while evaluating annotations).
if (klass.is_loaded()) {

View File

@ -7567,15 +7567,20 @@ bool Function::FfiCSignatureReturnsStruct() const {
if (IsFfiTypeClassId(type.type_class_id())) {
return false;
}
// TODO(http://dartbug.com/42563): Implement AbiSpecificInt.
#ifdef DEBUG
const auto& cls = Class::Handle(zone, type.type_class());
const auto& superClass = Class::Handle(zone, cls.SuperClass());
const bool is_abi_specific_int =
String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::AbiSpecificInteger());
if (is_abi_specific_int) {
return false;
}
#ifdef DEBUG
const bool is_struct = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Struct());
const bool is_union = String::Handle(zone, superClass.UserVisibleName())
.Equals(Symbols::Union());
RELEASE_ASSERT(is_struct || is_union);
ASSERT(is_struct || is_union);
#endif
return true;
}

View File

@ -16,6 +16,7 @@ class ObjectPointerVisitor;
// One-character symbols are added implicitly.
#define PREDEFINED_SYMBOLS_LIST(V) \
V(AbiSpecificInteger, "AbiSpecificInteger") \
V(AbstractClassInstantiationError, "AbstractClassInstantiationError") \
V(AllocateInvocationMirror, "_allocateInvocationMirror") \
V(AllocateInvocationMirrorForClosure, "_allocateInvocationMirrorForClosure") \
@ -109,6 +110,7 @@ class ObjectPointerVisitor;
V(ExternalOneByteString, "_ExternalOneByteString") \
V(ExternalTwoByteString, "_ExternalTwoByteString") \
V(FallThroughError, "FallThroughError") \
V(FfiAbiSpecificMapping, "_FfiAbiSpecificMapping") \
V(FfiBool, "Bool") \
V(FfiCallback, "_FfiCallback") \
V(FfiDouble, "Double") \
@ -124,6 +126,7 @@ class ObjectPointerVisitor;
V(FfiIntPtr, "IntPtr") \
V(FfiNativeFunction, "NativeFunction") \
V(FfiNativeType, "NativeType") \
V(FfiNativeTypes, "nativeTypes") \
V(FfiPointer, "Pointer") \
V(FfiStructLayout, "_FfiStructLayout") \
V(FfiStructLayoutArray, "_FfiInlineArray") \
@ -467,6 +470,7 @@ class ObjectPointerVisitor;
V(vm_notify_debugger_on_exception, "vm:notify-debugger-on-exception") \
V(vm_recognized, "vm:recognized") \
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
V(vm_ffi_abi_specific_mapping, "vm:ffi:abi-specific-mapping") \
V(vm_ffi_struct_fields, "vm:ffi:struct-fields") \
V(vm_unsafe_no_interrupts, "vm:unsafe:no-interrupts") \
V(vm_external_name, "vm:external-name") \

View File

@ -55,6 +55,7 @@ class Uint32 extends _NativeInteger {}
@pragma("vm:entry-point")
class Uint64 extends _NativeInteger {}
// TODO(http://dartbug.com/47938): Implement as AbiSpecificInteger.
@patch
@pragma("vm:entry-point")
class IntPtr extends _NativeInteger {}

View File

@ -236,6 +236,15 @@ class Abi {
factory Abi.current() => values[_abi()];
}
@pragma("vm:entry-point")
class _FfiAbiSpecificMapping {
/// Indexed by [_abi].
@pragma("vm:entry-point")
final List<Object> nativeTypes;
const _FfiAbiSpecificMapping(this.nativeTypes);
}
/// Copies data byte-wise from [source] to [target].
///
/// [source] and [target] should either be [Pointer] or [TypedData].
@ -284,34 +293,42 @@ void _memCopy(Object target, int targetOffsetInBytes, Object source,
// allocating a Pointer with in elementAt/offsetBy. Allocating these pointers
// and GCing new spaces takes a lot of the benchmark time. The next speedup is
// getting rid of these allocations by inlining these functions.
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadInt8")
external int _loadInt8(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadInt16")
external int _loadInt16(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadInt32")
external int _loadInt32(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadInt64")
external int _loadInt64(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadUint8")
external int _loadUint8(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadUint16")
external int _loadUint16(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadUint32")
external int _loadUint32(Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadUint64")
external int _loadUint64(Object typedDataBase, int offsetInBytes);
@ -320,6 +337,14 @@ external int _loadUint64(Object typedDataBase, int offsetInBytes);
@pragma("vm:external-name", "Ffi_loadIntPtr")
external int _loadIntPtr(Object typedDataBase, int offsetInBytes);
@pragma("vm:recognized", "other")
external int _loadAbiSpecificInt<T extends AbiSpecificInteger>(
Object typedDataBase, int offsetInBytes);
@pragma("vm:recognized", "other")
external int _loadAbiSpecificIntAtIndex<T extends AbiSpecificInteger>(
Object typedDataBase, int index);
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_loadFloat")
external double _loadFloat(Object typedDataBase, int offsetInBytes);
@ -341,34 +366,42 @@ external double _loadDoubleUnaligned(Object typedDataBase, int offsetInBytes);
external Pointer<S> _loadPointer<S extends NativeType>(
Object typedDataBase, int offsetInBytes);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeInt8")
external void _storeInt8(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeInt16")
external void _storeInt16(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeInt32")
external void _storeInt32(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeInt64")
external void _storeInt64(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeUint8")
external void _storeUint8(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeUint16")
external void _storeUint16(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeUint32")
external void _storeUint32(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:entry-point")
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeUint64")
external void _storeUint64(Object typedDataBase, int offsetInBytes, int value);
@ -377,6 +410,14 @@ external void _storeUint64(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:external-name", "Ffi_storeIntPtr")
external void _storeIntPtr(Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:recognized", "other")
external int _storeAbiSpecificInt<T extends AbiSpecificInteger>(
Object typedDataBase, int offsetInBytes, int value);
@pragma("vm:recognized", "other")
external int _storeAbiSpecificIntAtIndex<T extends AbiSpecificInteger>(
Object typedDataBase, int index, int value);
@pragma("vm:recognized", "other")
@pragma("vm:external-name", "Ffi_storeFloat")
external void _storeFloat(
@ -937,6 +978,25 @@ extension UnionPointer<T extends Union> on Pointer<T> {
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
on Pointer<T> {
@patch
int get value =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
void set value(int value) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
int operator [](int index) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
@patch
void operator []=(int index, int value) =>
throw "UNREACHABLE: This case should have been rewritten in the CFE.";
}
extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
@patch
Pointer<T> operator [](int index) =>
@ -972,6 +1032,20 @@ extension UnionArray<T extends Union> on Array<T> {
}
}
extension AbiSpecificIntegerArray on Array<AbiSpecificInteger> {
@patch
int operator [](int index) {
throw ArgumentError(
"Receiver should be a subtype of AbiSpecificInteger at compile-time.");
}
@patch
void operator []=(int index, int value) {
throw ArgumentError(
"Receiver should be a subtype of AbiSpecificInteger at compile-time.");
}
}
extension NativePort on SendPort {
@patch
@pragma("vm:external-name", "SendPortImpl_get_id")

View File

@ -0,0 +1,53 @@
// Copyright (c) 2021, 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.
part of dart.ffi;
/// The supertype of all [Abi]-specific integer types.
///
/// [Abi]-specific integers should extend this class and annotate it with
/// [AbiSpecificIntegerMapping] to declare the integer size and signedness
/// for [Abi.values].
///
/// For example:
///
/// ```
/// /// Represents a `uintptr_t` in C.
/// ///
/// /// [UintPtr] is not constructible in the Dart code and serves purely as
/// /// marker in type signatures.
/// @AbiSpecificIntegerMapping({
/// Abi.androidArm: Uint32(),
/// Abi.androidArm64: Uint64(),
/// Abi.androidIA32: Uint32(),
/// Abi.androidX64: Uint64(),
/// Abi.fuchsiaArm64: Uint64(),
/// Abi.fuchsiaX64: Uint64(),
/// Abi.iosArm: Uint32(),
/// Abi.iosArm64: Uint64(),
/// Abi.linuxArm: Uint32(),
/// Abi.linuxArm64: Uint64(),
/// Abi.linuxIA32: Uint32(),
/// Abi.linuxX64: Uint64(),
/// Abi.macosArm64: Uint64(),
/// Abi.macosX64: Uint64(),
/// Abi.windowsIA32: Uint32(),
/// Abi.windowsX64: Uint64(),
/// })
/// class UintPtr extends AbiSpecificInteger {
/// const UintPtr();
/// }
/// ```
class AbiSpecificInteger extends NativeType {
const AbiSpecificInteger();
}
/// Mapping for a subtype of [AbiSpecificInteger].
///
/// See documentation on [AbiSpecificInteger].
class AbiSpecificIntegerMapping {
final Map<Abi, NativeType> mapping;
const AbiSpecificIntegerMapping(this.mapping);
}

View File

@ -16,6 +16,7 @@ import 'dart:isolate';
import 'dart:typed_data';
part 'abi.dart';
part 'abi_specific.dart';
part 'native_type.dart';
part 'allocation.dart';
part 'annotations.dart';
@ -738,6 +739,22 @@ extension UnionPointer<T extends Union> on Pointer<T> {
external T operator [](int index);
}
/// Extension on [Pointer] specialized for the type argument
/// [AbiSpecificInteger].
extension AbiSpecificIntegerPointer<T extends AbiSpecificInteger>
on Pointer<T> {
/// The integer at [address].
external int get value;
external void set value(int value);
/// The integer at `address + sizeOf<T>() * index`.
external int operator [](int index);
/// The integer at `address + sizeOf<T>() * index`.
external void operator []=(int index, int value);
}
/// Bounds checking indexing methods on [Array]s of [Pointer].
extension PointerArray<T extends NativeType> on Array<Pointer<T>> {
external Pointer<T> operator [](int index);
@ -764,6 +781,13 @@ extension ArrayArray<T extends NativeType> on Array<Array<T>> {
external void operator []=(int index, Array<T> value);
}
/// Bounds checking indexing methods on [Array]s of [AbiSpecificInteger].
extension AbiSpecificIntegerArray on Array<AbiSpecificInteger> {
external int operator [](int index);
external void operator []=(int index, int value);
}
/// Extension to retrieve the native `Dart_Port` from a [SendPort].
extension NativePort on SendPort {
/// The native port of this [SendPort].

View File

@ -7,6 +7,7 @@ ffi_sdk_sources = [
# The above file needs to be first as it lists the parts below.
"abi.dart",
"abi_specific.dart",
"allocation.dart",
"annotations.dart",
"dynamic_library.dart",

View File

@ -0,0 +1,23 @@
// Copyright (c) 2021, 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.
// SharedObjects=ffi_test_functions
import 'dart:ffi';
// We want at least 1 mapping to satisfy the static checks.
const notTestingOn = Abi.fuchsiaArm64;
@AbiSpecificIntegerMapping({
notTestingOn: Int8(),
})
class Incomplete extends AbiSpecificInteger {
const Incomplete();
}
void main() {
// Any use that causes the class to be used, causes a compile-time error
// during loading of the class.
nullptr.cast<Incomplete>(); //# 1: compile-time error
}

View File

@ -0,0 +1,160 @@
// Copyright (c) 2021, 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.
// SharedObjects=ffi_test_functions
import 'dart:ffi';
import 'package:expect/expect.dart';
import 'package:ffi/ffi.dart';
// We want at least 1 mapping to satisfy the static checks.
const notTestingOn = Abi.fuchsiaArm64;
@AbiSpecificIntegerMapping({
notTestingOn: Int8(),
})
class Incomplete extends AbiSpecificInteger {
const Incomplete();
}
void main() {
if (Abi.current() == notTestingOn) {
return;
}
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
testInlineArray2();
testAsFunction();
testFromFunction();
}
void testSizeOf() {
Expect.throws(() {
sizeOf<Incomplete>();
});
}
void testStoreLoad() {
final p = calloc<Int64>().cast<Incomplete>();
Expect.throws(() {
p.value = 10;
});
Expect.throws(() {
p.value;
});
calloc.free(p);
}
void testStoreLoadIndexed() {
final p = calloc<Int64>().cast<Incomplete>();
Expect.throws(() {
p[0] = 10;
});
Expect.throws(() {
p[1];
});
calloc.free(p);
}
class IncompleteStruct extends Struct {
@Incomplete()
external int a0;
@Incomplete()
external int a1;
}
void testStruct() {
final p = calloc<Int64>(2).cast<IncompleteStruct>();
Expect.throws(() {
p.ref.a0 = 1;
});
Expect.throws(() {
p.ref.a0;
});
calloc.free(p);
}
class IncompleteArrayStruct extends Struct {
@Array(100)
external Array<Incomplete> a0;
}
void testInlineArray() {
final p = calloc<Int64>(100).cast<IncompleteArrayStruct>();
final array = p.ref.a0;
Expect.throws(() {
array[3] = 4;
});
Expect.throws(() {
array[3];
});
calloc.free(p);
}
const _dim1 = 8;
const _dim2 = 4;
class IncompleteArrayArrayStruct extends Struct {
@Array(_dim1, _dim2)
external Array<Array<Incomplete>> a0;
}
void testInlineArray2() {
final p = calloc<Int64>(100).cast<IncompleteArrayArrayStruct>();
Expect.throws(() {
p.elementAt(3);
});
calloc.free(p);
}
void testAsFunction() {
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(Incomplete)>>()
.asFunction<int Function(int)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Incomplete Function(Int32)>>()
.asFunction<int Function(int)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(IncompleteArrayStruct)>>()
.asFunction<int Function(IncompleteArrayStruct)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<IncompleteArrayStruct Function()>>()
.asFunction<IncompleteArrayStruct Function()>();
});
}
int myIncr(int a) => a + 1;
IncompleteArrayStruct myIncompleteReturn() =>
nullptr.cast<IncompleteArrayStruct>().ref;
int myIncompleteArg(IncompleteArrayStruct a) => 5;
void testFromFunction() {
Expect.throws(() {
Pointer.fromFunction<Incomplete Function(Int32)>(myIncr, 3);
});
Expect.throws(() {
Pointer.fromFunction<Int32 Function(Incomplete)>(myIncr, 3);
});
Expect.throws(() {
Pointer.fromFunction<IncompleteArrayStruct Function()>(myIncompleteReturn);
});
Expect.throws(() {
Pointer.fromFunction<Int32 Function(IncompleteArrayStruct)>(
myIncompleteArg, 3);
});
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2021, 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.
import 'dart:ffi';
import 'dart:io';
import 'package:expect/expect.dart';
import 'package:ffi/ffi.dart';
import 'abi_specific_ints.dart';
void main() {
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
testInlineArray2();
}
void testSizeOf() {
final size = sizeOf<WChar>();
if (Platform.isWindows) {
Expect.equals(2, size);
} else {
Expect.equals(4, size);
}
}
void testStoreLoad() {
final p = calloc<WChar>();
p.value = 10;
Expect.equals(10, p.value);
calloc.free(p);
}
void testStoreLoadIndexed() {
final p = calloc<WChar>(2);
p[0] = 10;
p[1] = 3;
Expect.equals(10, p[0]);
Expect.equals(3, p[1]);
calloc.free(p);
}
class WCharStruct extends Struct {
@WChar()
external int a0;
@WChar()
external int a1;
}
void testStruct() {
final p = calloc<WCharStruct>();
p.ref.a0 = 1;
Expect.equals(1, p.ref.a0);
p.ref.a0 = 2;
Expect.equals(2, p.ref.a0);
calloc.free(p);
}
class WCharArrayStruct extends Struct {
@Array(100)
external Array<WChar> a0;
}
void testInlineArray() {
final p = calloc<WCharArrayStruct>();
final array = p.ref.a0;
for (int i = 0; i < 100; i++) {
array[i] = i;
}
for (int i = 0; i < 100; i++) {
Expect.equals(i, array[i]);
}
calloc.free(p);
}
const _dim0 = 3;
const _dim1 = 8;
const _dim2 = 4;
class WCharArrayArrayStruct extends Struct {
@Array(_dim1, _dim2)
external Array<Array<WChar>> a0;
}
void testInlineArray2() {
int someValue(int a, int b, int c) => a * 1337 + b * 42 + c;
final p = calloc<WCharArrayArrayStruct>(_dim0);
for (int i0 = 0; i0 < _dim0; i0++) {
final array = p.elementAt(i0).ref.a0;
for (int i1 = 0; i1 < _dim1; i1++) {
final array2 = array[i1];
for (int i2 = 0; i2 < _dim2; i2++) {
array2[i2] = someValue(i0, i1, i2);
}
}
}
for (int i0 = 0; i0 < _dim0; i0++) {
final array = p.elementAt(i0).ref.a0;
for (int i1 = 0; i1 < _dim1; i1++) {
final array2 = array[i1];
for (int i2 = 0; i2 < _dim2; i2++) {
Expect.equals(someValue(i0, i1, i2), array2[i2]);
}
}
}
calloc.free(p);
}

View File

@ -0,0 +1,137 @@
// Copyright (c) 2021, 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.
import 'dart:ffi';
// TODO(dacoharkes): These should move to `package:ffi`.
// `int` in C.
typedef Int = Int32;
// `unsigned int` in C.
typedef UnsignedInt = Uint32;
// `size_t` in C.
typedef Size = UintPtr;
// `ssize_t` in C.
typedef SSize = IntPtr;
// `off_t` in C.
typedef Off = Long;
/// Represents a native unsigned pointer-sized integer in C.
///
/// [UintPtr] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint64(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint64(),
})
class UintPtr extends AbiSpecificInteger {
const UintPtr();
}
/// `long` in C.
///
/// [Long] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Int32(),
Abi.androidArm64: Int64(),
Abi.androidIA32: Int32(),
Abi.androidX64: Int64(),
Abi.fuchsiaArm64: Int64(),
Abi.fuchsiaX64: Int64(),
Abi.iosArm: Int32(),
Abi.iosArm64: Int64(),
Abi.iosX64: Int64(),
Abi.linuxArm: Int32(),
Abi.linuxArm64: Int64(),
Abi.linuxIA32: Int32(),
Abi.linuxX64: Int64(),
Abi.macosArm64: Int64(),
Abi.macosX64: Int64(),
Abi.windowsArm64: Int32(),
Abi.windowsIA32: Int32(),
Abi.windowsX64: Int32(),
})
class Long extends AbiSpecificInteger {
const Long();
}
/// `unsigned long` in C.
///
/// [UnsignedLong] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint32(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint32(),
})
class UnsignedLong extends AbiSpecificInteger {
const UnsignedLong();
}
/// `wchar_t` in C.
///
/// The signedness of `wchar_t` is undefined in C. Here, it is exposed as an
/// unsigned integer.
///
/// [WChar] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint32(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint32(),
Abi.fuchsiaArm64: Uint32(),
Abi.fuchsiaX64: Uint32(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint32(),
Abi.iosX64: Uint32(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint32(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint32(),
Abi.macosArm64: Uint32(),
Abi.macosX64: Uint32(),
Abi.windowsArm64: Uint16(),
Abi.windowsIA32: Uint16(),
Abi.windowsX64: Uint16(),
})
class WChar extends AbiSpecificInteger {
const WChar();
}

134
tests/ffi/c_types_test.dart Normal file
View File

@ -0,0 +1,134 @@
// Copyright (c) 2019, 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.
//
// Tests the sizes of c types from https://dartbug.com/36140.
//
// SharedObjects=ffi_test_functions
import 'dart:ffi';
import "package:expect/expect.dart";
import 'dart:io' show Platform;
import 'abi_specific_ints.dart';
import 'ffi_test_helpers.dart';
void main() {
printSizes();
testSizes();
testIntAssumptions();
testSizeTAssumptions();
testLongAssumptions();
testOffTAssumptions();
testWCharTAssumptions();
}
class CType {
final int ffiSize;
final String modifier;
final String type;
CType(this.ffiSize, this.type, [this.modifier = ""]);
String get cRepresentation => "$modifier $type".trim();
String get _getSizeName => "FfiSizeOf_$modifier\_$type";
int Function() get sizeFunction => ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>(_getSizeName);
int get size => sizeFunction();
String toString() => cRepresentation;
}
final intptr_t = CType(sizeOf<IntPtr>(), "intptr_t");
final uintptr_t = CType(sizeOf<UintPtr>(), "uintptr_t");
final int_ = CType(sizeOf<Int>(), "int");
final uint = CType(sizeOf<UnsignedInt>(), "int", "unsigned");
final long = CType(sizeOf<Long>(), "long");
final ulong = CType(sizeOf<UnsignedLong>(), "long", "unsigned");
final wchar_t = CType(sizeOf<WChar>(), "wchar_t");
final size_t = CType(sizeOf<Size>(), "size_t");
final ssize_t = CType(sizeOf<SSize>(), "ssize_t");
final off_t = CType(sizeOf<Off>(), "off_t");
final cTypes = [
intptr_t,
uintptr_t,
int_,
uint,
long,
ulong,
wchar_t,
size_t,
ssize_t,
off_t
];
void printSizes() {
cTypes.forEach((element) {
print("${element.cRepresentation.padRight(20)}: ${element.size}");
});
}
void testSizes() {
cTypes.forEach((element) {
Expect.equals(element.size, element.ffiSize);
});
}
void testIntAssumptions() {
Expect.equals(4, int_.size);
Expect.equals(4, uint.size);
}
void testSizeTAssumptions() {
Expect.equals(intptr_t.size, size_t.size);
Expect.equals(intptr_t.size, ssize_t.size);
}
void testLongAssumptions() {
if (Platform.isWindows) {
Expect.equals(4, long.size);
Expect.equals(4, ulong.size);
} else {
Expect.equals(intptr_t.size, long.size);
Expect.equals(intptr_t.size, ulong.size);
}
}
void testOffTAssumptions() {
Expect.equals(long.size, off_t.size);
}
void testWCharTAssumptions() {
final bool isSigned = wCharMinValue() != 0;
print("wchar_t isSigned $isSigned");
if (Platform.isWindows) {
Expect.equals(2, wchar_t.size);
if (isSigned) {
Expect.equals(-0x8000, wCharMinValue());
Expect.equals(0x7fff, wCharMaxValue());
} else {
Expect.equals(0, wCharMinValue());
Expect.equals(0xffff, wCharMaxValue());
}
} else {
Expect.equals(4, wchar_t.size);
if (isSigned) {
Expect.equals(-0x80000000, wCharMinValue());
Expect.equals(0x7fffffff, wCharMaxValue());
} else {
Expect.equals(0, wCharMinValue());
Expect.equals(0xffffffff, wCharMaxValue());
}
}
}
int Function() wCharMinValue = ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>('WCharMinValue');
int Function() wCharMaxValue = ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>('WCharMaxValue');

View File

@ -47,3 +47,9 @@ vmspecific_function_callbacks_exit_test: SkipByDesign
[ $compiler == dart2analyzer || $compiler == fasta ]
vmspecific_enable_ffi_test: SkipByDesign # This is a check for VM only.
[ $compiler == dartkp || $arch == arm64 && $system == fuchsia ]
abi_specific_int_incomplete_jit_test: SkipByDesign # Only intended to run in JIT mode.
[ $compiler != dartkp || $arch == arm64 && $system == fuchsia ]
abi_specific_int_incomplete_aot_test: SkipByDesign # Only intended to run in AOT mode.

View File

@ -16,6 +16,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'callback_tests_utils.dart';
import 'dylib_utils.dart';
@ -365,6 +368,12 @@ final testCases = [
Pointer.fromFunction<PassUint8Struct1ByteBoolType>(
passUint8Struct1ByteBool, false),
passUint8Struct1ByteBoolAfterCallback),
CallbackTest.withCheck(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned",
Pointer.fromFunction<
PassWCharStructInlineArrayIntUintPtrx2LongUnsignedType>(
passWCharStructInlineArrayIntUintPtrx2LongUnsigned, 0),
passWCharStructInlineArrayIntUintPtrx2LongUnsignedAfterCallback),
CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
@ -7891,6 +7900,84 @@ void passUint8Struct1ByteBoolAfterCallback() {
Expect.equals(1 % 2 != 0, result);
}
typedef PassWCharStructInlineArrayIntUintPtrx2LongUnsignedType = WChar Function(
WChar, StructInlineArrayInt, UintPtr, UintPtr, Long, UnsignedLong);
// Global variables to be able to test inputs after callback returned.
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0 = 0;
StructInlineArrayInt passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1 =
Pointer<StructInlineArrayInt>.fromAddress(0).ref;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5 = 0;
// Result variable also global, so we can delete it after the callback.
int passWCharStructInlineArrayIntUintPtrx2LongUnsignedResult = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult() {
int result = 0;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[0];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[1];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[2];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[3];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[4];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[5];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[6];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[7];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[8];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[9];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5;
passWCharStructInlineArrayIntUintPtrx2LongUnsignedResult = result;
return result;
}
/// Returning a wchar.
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned(
int a0, StructInlineArrayInt a1, int a2, int a3, int a4, int a5) {
print(
"passWCharStructInlineArrayIntUintPtrx2LongUnsigned(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5})");
// In legacy mode, possibly return null.
// In both nnbd and legacy mode, possibly throw.
if (a0 == 42 || a0 == 84) {
print("throwing!");
throw Exception(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned throwing on purpose!");
}
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0 = a0;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1 = a1;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2 = a2;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3 = a3;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4 = a4;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5 = a5;
final result =
passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult();
print("result = $result");
return result;
}
void passWCharStructInlineArrayIntUintPtrx2LongUnsignedAfterCallback() {
final result =
passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult();
print("after callback result = $result");
Expect.equals(120, result);
}
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.

View File

@ -7,6 +7,9 @@
import 'dart:ffi';
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
class Struct1ByteBool extends Struct {
@Bool()
external bool a0;
@ -1257,3 +1260,10 @@ class Union16BytesNestedFloat extends Union {
String toString() => "(${a0}, ${a1}, ${a2})";
}
class StructInlineArrayInt extends Struct {
@Array(10)
external Array<WChar> a0;
String toString() => "(${[for (var i0 = 0; i0 < 10; i0 += 1) a0[i0]]})";
}

View File

@ -16,6 +16,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'dylib_utils.dart';
// Reuse the compound classes.
@ -89,6 +92,7 @@ void main() {
testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf();
testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf();
testPassUint8Struct1ByteBoolLeaf();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf();
testReturnStruct1ByteIntLeaf();
testReturnStruct3BytesHomogeneousUint8Leaf();
testReturnStruct3BytesInt2ByteAlignedLeaf();
@ -5461,6 +5465,50 @@ void testPassUint8Struct1ByteBoolLeaf() {
calloc.free(a1Pointer);
}
final passWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf = ffiTestFunctions
.lookupFunction<
WChar Function(WChar, StructInlineArrayInt, UintPtr, UintPtr, Long,
UnsignedLong),
int Function(int, StructInlineArrayInt, int, int, int, int)>(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned",
isLeaf: true);
/// Returning a wchar.
void testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf() {
int a0;
final a1Pointer = calloc<StructInlineArrayInt>();
final StructInlineArrayInt a1 = a1Pointer.ref;
int a2;
int a3;
int a4;
int a5;
a0 = 1;
a1.a0[0] = 2;
a1.a0[1] = 3;
a1.a0[2] = 4;
a1.a0[3] = 5;
a1.a0[4] = 6;
a1.a0[5] = 7;
a1.a0[6] = 8;
a1.a0[7] = 9;
a1.a0[8] = 10;
a1.a0[9] = 11;
a2 = 12;
a3 = 13;
a4 = 14;
a5 = 15;
final result = passWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf(
a0, a1, a2, a3, a4, a5);
print("result = $result");
Expect.equals(120, result);
calloc.free(a1Pointer);
}
final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true);

View File

@ -16,6 +16,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'dylib_utils.dart';
// Reuse the compound classes.
@ -89,6 +92,7 @@ void main() {
testPassUint8Boolx9Struct10BytesHomogeneousBoolBool();
testPassUint8Boolx9Struct10BytesInlineArrayBoolBool();
testPassUint8Struct1ByteBool();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@ -5385,6 +5389,49 @@ void testPassUint8Struct1ByteBool() {
calloc.free(a1Pointer);
}
final passWCharStructInlineArrayIntUintPtrx2LongUnsigned =
ffiTestFunctions.lookupFunction<
WChar Function(
WChar, StructInlineArrayInt, UintPtr, UintPtr, Long, UnsignedLong),
int Function(int, StructInlineArrayInt, int, int, int,
int)>("PassWCharStructInlineArrayIntUintPtrx2LongUnsigned");
/// Returning a wchar.
void testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned() {
int a0;
final a1Pointer = calloc<StructInlineArrayInt>();
final StructInlineArrayInt a1 = a1Pointer.ref;
int a2;
int a3;
int a4;
int a5;
a0 = 1;
a1.a0[0] = 2;
a1.a0[1] = 3;
a1.a0[2] = 4;
a1.a0[3] = 5;
a1.a0[4] = 6;
a1.a0[5] = 7;
a1.a0[6] = 8;
a1.a0[7] = 9;
a1.a0[8] = 10;
a1.a0[9] = 11;
a2 = 12;
a3 = 13;
a4 = 14;
a5 = 15;
final result = passWCharStructInlineArrayIntUintPtrx2LongUnsigned(
a0, a1, a2, a3, a4, a5);
print("result = $result");
Expect.equals(120, result);
calloc.free(a1Pointer);
}
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");

View File

@ -18,6 +18,10 @@ const uint64 = FundamentalType(PrimitiveType.uint64);
const intptr = FundamentalType(PrimitiveType.intptr);
const float = FundamentalType(PrimitiveType.float);
const double_ = FundamentalType(PrimitiveType.double_);
const long = FundamentalType(PrimitiveType.long);
const ulong = FundamentalType(PrimitiveType.ulong);
const uintptr = FundamentalType(PrimitiveType.uintptr);
const wchar = FundamentalType(PrimitiveType.wchar);
enum PrimitiveType {
bool_,
@ -32,25 +36,96 @@ enum PrimitiveType {
intptr,
float,
double_,
long,
ulong,
uintptr,
wchar,
}
const primitiveNames = [
"bool",
"int8",
"int16",
"int32",
"int64",
"uint8",
"uint16",
"uint32",
"uint64",
"intptr",
"float",
"double",
];
const primitiveNames = {
PrimitiveType.bool_: "bool",
PrimitiveType.int8: "int8",
PrimitiveType.int16: "int16",
PrimitiveType.int32: "int32",
PrimitiveType.int64: "int64",
PrimitiveType.uint8: "uint8",
PrimitiveType.uint16: "uint16",
PrimitiveType.uint32: "uint32",
PrimitiveType.uint64: "uint64",
PrimitiveType.intptr: "intptr",
PrimitiveType.float: "float",
PrimitiveType.double_: "double",
PrimitiveType.long: "long",
PrimitiveType.ulong: "ulong",
PrimitiveType.uintptr: "uintptr",
PrimitiveType.wchar: "wchar",
};
const intptrSize = -1;
const primitiveSizesInBytes = [1, 1, 2, 4, 8, 1, 2, 4, 8, intptrSize, 4, 8];
final primitiveCType = {
PrimitiveType.bool_: "bool",
PrimitiveType.int8: "int8_t",
PrimitiveType.int16: "int16_t",
PrimitiveType.int32: "int32_t",
PrimitiveType.int64: "int64_t",
PrimitiveType.uint8: "uint8_t",
PrimitiveType.uint16: "uint16_t",
PrimitiveType.uint32: "uint32_t",
PrimitiveType.uint64: "uint64_t",
PrimitiveType.intptr: "intptr",
PrimitiveType.float: "float",
PrimitiveType.double_: "double",
// People should use explicit sizes. But we also want to test `long`.
// Surpressing lint.
PrimitiveType.long: "/* NOLINT(runtime/int) */long",
PrimitiveType.ulong: "/* NOLINT(runtime/int) */unsigned long",
PrimitiveType.uintptr: "uintptr_t",
PrimitiveType.wchar: "wchar_t",
};
final primitiveDartCType = {
PrimitiveType.bool_: "Bool",
PrimitiveType.int8: "Int8",
PrimitiveType.int16: "Int16",
PrimitiveType.int32: "Int32",
PrimitiveType.int64: "Int64",
PrimitiveType.uint8: "Uint8",
PrimitiveType.uint16: "Uint16",
PrimitiveType.uint32: "Uint32",
PrimitiveType.uint64: "Uint64",
PrimitiveType.intptr: "Intptr",
PrimitiveType.float: "Float",
PrimitiveType.double_: "Double",
PrimitiveType.long: "Long",
PrimitiveType.ulong: "UnsignedLong",
PrimitiveType.uintptr: "UintPtr",
PrimitiveType.wchar: "WChar",
};
/// Sizes equal on all platforms.
const primitiveSizesInBytes = {
PrimitiveType.bool_: 1,
PrimitiveType.int8: 1,
PrimitiveType.int16: 2,
PrimitiveType.int32: 4,
PrimitiveType.int64: 8,
PrimitiveType.uint8: 1,
PrimitiveType.uint16: 2,
PrimitiveType.uint32: 4,
PrimitiveType.uint64: 8,
PrimitiveType.float: 4,
PrimitiveType.double_: 8,
};
const primitivesUnsigned = {
PrimitiveType.bool_,
PrimitiveType.uint8,
PrimitiveType.uint16,
PrimitiveType.uint32,
PrimitiveType.uint64,
PrimitiveType.uintptr,
PrimitiveType.ulong,
PrimitiveType.wchar,
};
abstract class CType {
String get cType;
@ -90,18 +165,13 @@ class FundamentalType extends CType {
bool get isOnlyFloatingPoint => isFloatingPoint;
bool get isOnlyInteger => isInteger;
bool get isOnlyBool => isBool;
bool get isUnsigned =>
primitive == PrimitiveType.bool_ ||
primitive == PrimitiveType.uint8 ||
primitive == PrimitiveType.uint16 ||
primitive == PrimitiveType.uint32 ||
primitive == PrimitiveType.uint64;
bool get isUnsigned => primitivesUnsigned.contains(primitive);
bool get isSigned => !isUnsigned;
String get name => primitiveNames[primitive.index];
String get name => primitiveNames[primitive]!;
String get cType => "${name}${isInteger ? "_t" : ""}";
String get dartCType => name.upperCaseFirst();
String get cType => primitiveCType[primitive]!;
String get dartCType => primitiveDartCType[primitive]!;
String get dartType {
if (isInteger) return 'int';
if (isOnlyFloatingPoint) return 'double';
@ -110,12 +180,12 @@ class FundamentalType extends CType {
}
String get dartStructFieldAnnotation => "@${dartCType}()";
bool get hasSize => primitive != PrimitiveType.intptr;
bool get hasSize => primitiveSizesInBytes.containsKey(primitive);
int get size {
if (!hasSize) {
throw "Size unknown.";
}
return primitiveSizesInBytes[primitive.index];
return primitiveSizesInBytes[primitive]!;
}
}

View File

@ -422,6 +422,18 @@ stack."""),
bool_,
"""
Returning a bool."""),
FunctionType(
[
wchar,
structArrayWChar,
uintptr,
uintptr,
long,
ulong,
],
wchar,
"""
Returning a wchar."""),
];
final functionsStructReturn = [
@ -672,6 +684,7 @@ final compounds = [
union12bytesInt,
union16bytesFloat,
union16bytesFloat2,
structArrayWChar,
];
final struct1byteBool = StructType([bool_]);
@ -863,3 +876,6 @@ final union16bytesFloat =
/// This union has homogenous floats of different sizes.
final union16bytesFloat2 =
UnionType([struct8bytesFloat, struct12bytesFloat, struct16bytesFloat]);
/// This struct contains an AbiSpecificInt type.
final structArrayWChar = StructType([FixedLengthArrayType(wchar, 10)]);

View File

@ -399,7 +399,7 @@ extension on CType {
switch (this.runtimeType) {
case FundamentalType:
final this_ = this as FundamentalType;
if (this_.isInteger) {
if (this_.isInteger || this_.isBool) {
return "CHECK_EQ(${expected}, ${actual});";
}
assert(this_.isFloatingPoint);
@ -426,7 +426,7 @@ for (intptr_t i = 0; i < ${this_.length}; i++){
switch (this.runtimeType) {
case FundamentalType:
final this_ = this as FundamentalType;
if (this_.isInteger) {
if (this_.isInteger || this_.isBool) {
return "CHECK_EQ(0, ${actual});";
}
assert(this_.isFloatingPoint);
@ -921,6 +921,9 @@ ${headerCommon(copyrightYear: copyrightYear)}
$dartVersion
import 'dart:ffi';
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
""";
}
@ -929,7 +932,7 @@ String compoundsPath({required bool isNnbd}) {
return Platform.script
.resolve(
"../../$folder/function_structs_by_value_generated_compounds.dart")
.path;
.toFilePath();
}
Future<void> writeDartCompounds() async {
@ -964,6 +967,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'dylib_utils.dart';
// Reuse the compound classes.
@ -1001,7 +1007,7 @@ String callTestPath({required bool isNnbd, required bool isLeaf}) {
return Platform.script
.resolve(
"../../$folder/function_structs_by_value_generated${suffix}_test.dart")
.path;
.toFilePath();
}
headerDartCallbackTest({required bool isNnbd, required int copyrightYear}) {
@ -1023,6 +1029,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'callback_tests_utils.dart';
import 'dylib_utils.dart';
@ -1067,7 +1076,7 @@ String callbackTestPath({required bool isNnbd}) {
return Platform.script
.resolve(
"../../$folder/function_callbacks_structs_by_value_generated_test.dart")
.path;
.toFilePath();
}
headerC({required int copyrightYear}) {
@ -1128,7 +1137,7 @@ Future<void> writeC() async {
final ccPath = Platform.script
.resolve("../../../runtime/bin/ffi_test/ffi_test_functions_generated.cc")
.path;
.toFilePath();
void printUsage() {
print("""

View File

@ -848,3 +848,35 @@ class TestStruct1802 extends Struct {
@Array.multi([2, 2, 2, 2, 2, 2, -1]) //# 1802: compile-time error
external Array<Uint8> inlineArray; //# 1802: compile-time error
}
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: IntPtr(), //# 1900: compile-time error
Abi.androidIA32: AbiSpecificInteger1(), //# 1901: compile-time error
})
@AbiSpecificIntegerMapping({}) //# 1902: compile-time error
class AbiSpecificInteger1 extends AbiSpecificInteger {
const AbiSpecificInteger1();
int get a => 4; //# 1910: compile-time error
external int b; //# 1911: compile-time error
}
class AbiSpecificInteger2
implements AbiSpecificInteger //# 1903: compile-time error
{
const AbiSpecificInteger2();
}
class AbiSpecificInteger3
extends AbiSpecificInteger1 //# 1904: compile-time error
{
const AbiSpecificInteger3();
}
class AbiSpecificInteger4
implements AbiSpecificInteger1 //# 1905: compile-time error
{
const AbiSpecificInteger4();
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2021, 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.
// @dart = 2.9
// SharedObjects=ffi_test_functions
import 'dart:ffi';
// We want at least 1 mapping to satisfy the static checks.
const notTestingOn = Abi.fuchsiaArm64;
@AbiSpecificIntegerMapping({
notTestingOn: Int8(),
})
class Incomplete extends AbiSpecificInteger {
const Incomplete();
}
void main() {
// Any use that causes the class to be used, causes a compile-time error
// during loading of the class.
nullptr.cast<Incomplete>(); //# 1: compile-time error
}

View File

@ -0,0 +1,162 @@
// Copyright (c) 2021, 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.
// @dart = 2.9
// SharedObjects=ffi_test_functions
import 'dart:ffi';
import 'package:expect/expect.dart';
import 'package:ffi/ffi.dart';
// We want at least 1 mapping to satisfy the static checks.
const notTestingOn = Abi.fuchsiaArm64;
@AbiSpecificIntegerMapping({
notTestingOn: Int8(),
})
class Incomplete extends AbiSpecificInteger {
const Incomplete();
}
void main() {
if (Abi.current() == notTestingOn) {
return;
}
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
testInlineArray2();
testAsFunction();
testFromFunction();
}
void testSizeOf() {
Expect.throws(() {
sizeOf<Incomplete>();
});
}
void testStoreLoad() {
final p = calloc<Int64>().cast<Incomplete>();
Expect.throws(() {
p.value = 10;
});
Expect.throws(() {
p.value;
});
calloc.free(p);
}
void testStoreLoadIndexed() {
final p = calloc<Int64>().cast<Incomplete>();
Expect.throws(() {
p[0] = 10;
});
Expect.throws(() {
p[1];
});
calloc.free(p);
}
class IncompleteStruct extends Struct {
@Incomplete()
int a0;
@Incomplete()
int a1;
}
void testStruct() {
final p = calloc<Int64>(2).cast<IncompleteStruct>();
Expect.throws(() {
p.ref.a0 = 1;
});
Expect.throws(() {
p.ref.a0;
});
calloc.free(p);
}
class IncompleteArrayStruct extends Struct {
@Array(100)
Array<Incomplete> a0;
}
void testInlineArray() {
final p = calloc<Int64>(100).cast<IncompleteArrayStruct>();
final array = p.ref.a0;
Expect.throws(() {
array[3] = 4;
});
Expect.throws(() {
array[3];
});
calloc.free(p);
}
const _dim1 = 8;
const _dim2 = 4;
class IncompleteArrayArrayStruct extends Struct {
@Array(_dim1, _dim2)
Array<Array<Incomplete>> a0;
}
void testInlineArray2() {
final p = calloc<Int64>(100).cast<IncompleteArrayArrayStruct>();
Expect.throws(() {
p.elementAt(3);
});
calloc.free(p);
}
void testAsFunction() {
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(Incomplete)>>()
.asFunction<int Function(int)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Incomplete Function(Int32)>>()
.asFunction<int Function(int)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<Int32 Function(IncompleteArrayStruct)>>()
.asFunction<int Function(IncompleteArrayStruct)>();
});
Expect.throws(() {
nullptr
.cast<NativeFunction<IncompleteArrayStruct Function()>>()
.asFunction<IncompleteArrayStruct Function()>();
});
}
int myIncr(int a) => a + 1;
IncompleteArrayStruct myIncompleteReturn() =>
nullptr.cast<IncompleteArrayStruct>().ref;
int myIncompleteArg(IncompleteArrayStruct a) => 5;
void testFromFunction() {
Expect.throws(() {
Pointer.fromFunction<Incomplete Function(Int32)>(myIncr, 3);
});
Expect.throws(() {
Pointer.fromFunction<Int32 Function(Incomplete)>(myIncr, 3);
});
Expect.throws(() {
Pointer.fromFunction<IncompleteArrayStruct Function()>(myIncompleteReturn);
});
Expect.throws(() {
Pointer.fromFunction<Int32 Function(IncompleteArrayStruct)>(
myIncompleteArg, 3);
});
}

View File

@ -0,0 +1,114 @@
// Copyright (c) 2021, 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.
// @dart = 2.9
import 'dart:ffi';
import 'dart:io';
import 'package:expect/expect.dart';
import 'package:ffi/ffi.dart';
import 'abi_specific_ints.dart';
void main() {
testSizeOf();
testStoreLoad();
testStoreLoadIndexed();
testStruct();
testInlineArray();
testInlineArray2();
}
void testSizeOf() {
final size = sizeOf<WChar>();
if (Platform.isWindows) {
Expect.equals(2, size);
} else {
Expect.equals(4, size);
}
}
void testStoreLoad() {
final p = calloc<WChar>();
p.value = 10;
Expect.equals(10, p.value);
calloc.free(p);
}
void testStoreLoadIndexed() {
final p = calloc<WChar>(2);
p[0] = 10;
p[1] = 3;
Expect.equals(10, p[0]);
Expect.equals(3, p[1]);
calloc.free(p);
}
class WCharStruct extends Struct {
@WChar()
int a0;
@WChar()
int a1;
}
void testStruct() {
final p = calloc<WCharStruct>();
p.ref.a0 = 1;
Expect.equals(1, p.ref.a0);
p.ref.a0 = 2;
Expect.equals(2, p.ref.a0);
calloc.free(p);
}
class WCharArrayStruct extends Struct {
@Array(100)
Array<WChar> a0;
}
void testInlineArray() {
final p = calloc<WCharArrayStruct>();
final array = p.ref.a0;
for (int i = 0; i < 100; i++) {
array[i] = i;
}
for (int i = 0; i < 100; i++) {
Expect.equals(i, array[i]);
}
calloc.free(p);
}
const _dim0 = 3;
const _dim1 = 8;
const _dim2 = 4;
class WCharArrayArrayStruct extends Struct {
@Array(_dim1, _dim2)
Array<Array<WChar>> a0;
}
void testInlineArray2() {
int someValue(int a, int b, int c) => a * 1337 + b * 42 + c;
final p = calloc<WCharArrayArrayStruct>(_dim0);
for (int i0 = 0; i0 < _dim0; i0++) {
final array = p.elementAt(i0).ref.a0;
for (int i1 = 0; i1 < _dim1; i1++) {
final array2 = array[i1];
for (int i2 = 0; i2 < _dim2; i2++) {
array2[i2] = someValue(i0, i1, i2);
}
}
}
for (int i0 = 0; i0 < _dim0; i0++) {
final array = p.elementAt(i0).ref.a0;
for (int i1 = 0; i1 < _dim1; i1++) {
final array2 = array[i1];
for (int i2 = 0; i2 < _dim2; i2++) {
Expect.equals(someValue(i0, i1, i2), array2[i2]);
}
}
}
calloc.free(p);
}

View File

@ -0,0 +1,124 @@
// Copyright (c) 2021, 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.
// @dart = 2.9
import 'dart:ffi';
// TODO(dacoharkes): These should move to `package:ffi`.
/// Represents a native unsigned pointer-sized integer in C.
///
/// [UintPtr] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint64(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint64(),
})
class UintPtr extends AbiSpecificInteger {
const UintPtr();
}
/// `long` in C.
///
/// [Long] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Int32(),
Abi.androidArm64: Int64(),
Abi.androidIA32: Int32(),
Abi.androidX64: Int64(),
Abi.fuchsiaArm64: Int64(),
Abi.fuchsiaX64: Int64(),
Abi.iosArm: Int32(),
Abi.iosArm64: Int64(),
Abi.iosX64: Int64(),
Abi.linuxArm: Int32(),
Abi.linuxArm64: Int64(),
Abi.linuxIA32: Int32(),
Abi.linuxX64: Int64(),
Abi.macosArm64: Int64(),
Abi.macosX64: Int64(),
Abi.windowsArm64: Int32(),
Abi.windowsIA32: Int32(),
Abi.windowsX64: Int32(),
})
class Long extends AbiSpecificInteger {
const Long();
}
/// `unsigned long` in C.
///
/// [UnsignedLong] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint64(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint64(),
Abi.fuchsiaArm64: Uint64(),
Abi.fuchsiaX64: Uint64(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint64(),
Abi.iosX64: Uint64(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint64(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint64(),
Abi.macosArm64: Uint64(),
Abi.macosX64: Uint64(),
Abi.windowsArm64: Uint32(),
Abi.windowsIA32: Uint32(),
Abi.windowsX64: Uint32(),
})
class UnsignedLong extends AbiSpecificInteger {
const UnsignedLong();
}
/// `wchar_t` in C.
///
/// The signedness of `wchar_t` is undefined in C. Here, it is exposed as an
/// unsigned integer.
///
/// [WChar] is not constructible in the Dart code and serves purely as marker in
/// type signatures.
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: Uint32(),
Abi.androidIA32: Uint32(),
Abi.androidX64: Uint32(),
Abi.fuchsiaArm64: Uint32(),
Abi.fuchsiaX64: Uint32(),
Abi.iosArm: Uint32(),
Abi.iosArm64: Uint32(),
Abi.iosX64: Uint32(),
Abi.linuxArm: Uint32(),
Abi.linuxArm64: Uint32(),
Abi.linuxIA32: Uint32(),
Abi.linuxX64: Uint32(),
Abi.macosArm64: Uint32(),
Abi.macosX64: Uint32(),
Abi.windowsArm64: Uint16(),
Abi.windowsIA32: Uint16(),
Abi.windowsX64: Uint16(),
})
class WChar extends AbiSpecificInteger {
const WChar();
}

View File

@ -0,0 +1,109 @@
// Copyright (c) 2019, 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.
//
// Tests the sizes of c types from https://dartbug.com/36140.
//
// SharedObjects=ffi_test_functions
// @dart = 2.9
import 'dart:ffi';
import "package:expect/expect.dart";
import 'dart:io' show Platform;
import 'abi_specific_ints.dart';
import 'ffi_test_helpers.dart';
void main() {
printSizes();
testSizes();
testLongAssumptions();
testWCharTAssumptions();
}
class CType {
final int ffiSize;
final String modifier;
final String type;
CType(this.ffiSize, this.type, [this.modifier = ""]);
String get cRepresentation => "$modifier $type".trim();
String get _getSizeName => "FfiSizeOf_$modifier\_$type";
int Function() get sizeFunction => ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>(_getSizeName);
int get size => sizeFunction();
String toString() => cRepresentation;
}
final intptr_t = CType(sizeOf<IntPtr>(), "intptr_t");
final uintptr_t = CType(sizeOf<UintPtr>(), "uintptr_t");
final long = CType(sizeOf<Long>(), "long");
final ulong = CType(sizeOf<UnsignedLong>(), "long", "unsigned");
final wchar_t = CType(sizeOf<WChar>(), "wchar_t");
final cTypes = [
intptr_t,
uintptr_t,
long,
ulong,
wchar_t,
];
void printSizes() {
cTypes.forEach((element) {
print("${element.cRepresentation.padRight(20)}: ${element.size}");
});
}
void testSizes() {
cTypes.forEach((element) {
Expect.equals(element.size, element.ffiSize);
});
}
void testLongAssumptions() {
if (Platform.isWindows) {
Expect.equals(4, long.size);
Expect.equals(4, ulong.size);
} else {
Expect.equals(intptr_t.size, long.size);
Expect.equals(intptr_t.size, ulong.size);
}
}
void testWCharTAssumptions() {
final bool isSigned = wCharMinValue() != 0;
print("wchar_t isSigned $isSigned");
if (Platform.isWindows) {
Expect.equals(2, wchar_t.size);
if (isSigned) {
Expect.equals(-0x8000, wCharMinValue());
Expect.equals(0x7fff, wCharMaxValue());
} else {
Expect.equals(0, wCharMinValue());
Expect.equals(0xffff, wCharMaxValue());
}
} else {
Expect.equals(4, wchar_t.size);
if (isSigned) {
Expect.equals(-0x80000000, wCharMinValue());
Expect.equals(0x7fffffff, wCharMaxValue());
} else {
Expect.equals(0, wCharMinValue());
Expect.equals(0xffffffff, wCharMaxValue());
}
}
}
int Function() wCharMinValue = ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>('WCharMinValue');
int Function() wCharMaxValue = ffiTestFunctions
.lookupFunction<Uint64 Function(), int Function()>('WCharMaxValue');

View File

@ -47,3 +47,9 @@ vmspecific_function_callbacks_exit_test: SkipByDesign
[ $compiler == dart2analyzer || $compiler == fasta ]
vmspecific_enable_ffi_test: SkipByDesign # This is a check for VM only.
[ $compiler == dartkp || $arch == arm64 && $system == fuchsia ]
abi_specific_int_incomplete_jit_test: SkipByDesign # Only intended to run in JIT mode.
[ $compiler != dartkp || $arch == arm64 && $system == fuchsia ]
abi_specific_int_incomplete_aot_test: SkipByDesign # Only intended to run in AOT mode.

View File

@ -18,6 +18,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'callback_tests_utils.dart';
import 'dylib_utils.dart';
@ -367,6 +370,12 @@ final testCases = [
Pointer.fromFunction<PassUint8Struct1ByteBoolType>(
passUint8Struct1ByteBool, false),
passUint8Struct1ByteBoolAfterCallback),
CallbackTest.withCheck(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned",
Pointer.fromFunction<
PassWCharStructInlineArrayIntUintPtrx2LongUnsignedType>(
passWCharStructInlineArrayIntUintPtrx2LongUnsigned, 0),
passWCharStructInlineArrayIntUintPtrx2LongUnsignedAfterCallback),
CallbackTest.withCheck(
"ReturnStruct1ByteInt",
Pointer.fromFunction<ReturnStruct1ByteIntType>(returnStruct1ByteInt),
@ -8153,6 +8162,88 @@ void passUint8Struct1ByteBoolAfterCallback() {
Expect.equals(1 % 2 != 0, result);
}
typedef PassWCharStructInlineArrayIntUintPtrx2LongUnsignedType = WChar Function(
WChar, StructInlineArrayInt, UintPtr, UintPtr, Long, UnsignedLong);
// Global variables to be able to test inputs after callback returned.
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0 = 0;
StructInlineArrayInt passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1 =
Pointer<StructInlineArrayInt>.fromAddress(0).ref;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4 = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5 = 0;
// Result variable also global, so we can delete it after the callback.
int passWCharStructInlineArrayIntUintPtrx2LongUnsignedResult = 0;
int passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult() {
int result = 0;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[0];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[1];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[2];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[3];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[4];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[5];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[6];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[7];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[8];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1.a0[9];
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4;
result += passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5;
passWCharStructInlineArrayIntUintPtrx2LongUnsignedResult = result;
return result;
}
/// Returning a wchar.
int passWCharStructInlineArrayIntUintPtrx2LongUnsigned(
int a0, StructInlineArrayInt a1, int a2, int a3, int a4, int a5) {
print(
"passWCharStructInlineArrayIntUintPtrx2LongUnsigned(${a0}, ${a1}, ${a2}, ${a3}, ${a4}, ${a5})");
// In legacy mode, possibly return null.
if (a0 == 84) {
print("returning null!");
return null;
}
// In both nnbd and legacy mode, possibly throw.
if (a0 == 42 || a0 == 84) {
print("throwing!");
throw Exception(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned throwing on purpose!");
}
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a0 = a0;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a1 = a1;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a2 = a2;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a3 = a3;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a4 = a4;
passWCharStructInlineArrayIntUintPtrx2LongUnsigned_a5 = a5;
final result =
passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult();
print("result = $result");
return result;
}
void passWCharStructInlineArrayIntUintPtrx2LongUnsignedAfterCallback() {
final result =
passWCharStructInlineArrayIntUintPtrx2LongUnsignedCalculateResult();
print("after callback result = $result");
Expect.equals(120, result);
}
typedef ReturnStruct1ByteIntType = Struct1ByteInt Function(Int8);
// Global variables to be able to test inputs after callback returned.

View File

@ -9,6 +9,9 @@
import 'dart:ffi';
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
class Struct1ByteBool extends Struct {
@Bool()
bool a0;
@ -1259,3 +1262,10 @@ class Union16BytesNestedFloat extends Union {
String toString() => "(${a0}, ${a1}, ${a2})";
}
class StructInlineArrayInt extends Struct {
@Array(10)
Array<WChar> a0;
String toString() => "(${[for (var i0 = 0; i0 < 10; i0 += 1) a0[i0]]})";
}

View File

@ -18,6 +18,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'dylib_utils.dart';
// Reuse the compound classes.
@ -91,6 +94,7 @@ void main() {
testPassUint8Boolx9Struct10BytesHomogeneousBoolBoolLeaf();
testPassUint8Boolx9Struct10BytesInlineArrayBoolBoolLeaf();
testPassUint8Struct1ByteBoolLeaf();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf();
testReturnStruct1ByteIntLeaf();
testReturnStruct3BytesHomogeneousUint8Leaf();
testReturnStruct3BytesInt2ByteAlignedLeaf();
@ -5463,6 +5467,50 @@ void testPassUint8Struct1ByteBoolLeaf() {
calloc.free(a1Pointer);
}
final passWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf = ffiTestFunctions
.lookupFunction<
WChar Function(WChar, StructInlineArrayInt, UintPtr, UintPtr, Long,
UnsignedLong),
int Function(int, StructInlineArrayInt, int, int, int, int)>(
"PassWCharStructInlineArrayIntUintPtrx2LongUnsigned",
isLeaf: true);
/// Returning a wchar.
void testPassWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf() {
int a0;
final a1Pointer = calloc<StructInlineArrayInt>();
final StructInlineArrayInt a1 = a1Pointer.ref;
int a2;
int a3;
int a4;
int a5;
a0 = 1;
a1.a0[0] = 2;
a1.a0[1] = 3;
a1.a0[2] = 4;
a1.a0[3] = 5;
a1.a0[4] = 6;
a1.a0[5] = 7;
a1.a0[6] = 8;
a1.a0[7] = 9;
a1.a0[8] = 10;
a1.a0[9] = 11;
a2 = 12;
a3 = 13;
a4 = 14;
a5 = 15;
final result = passWCharStructInlineArrayIntUintPtrx2LongUnsignedLeaf(
a0, a1, a2, a3, a4, a5);
print("result = $result");
Expect.equals(120, result);
calloc.free(a1Pointer);
}
final returnStruct1ByteIntLeaf = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt", isLeaf: true);

View File

@ -18,6 +18,9 @@ import 'dart:ffi';
import "package:expect/expect.dart";
import "package:ffi/ffi.dart";
// Reuse the AbiSpecificInts.
import 'abi_specific_ints.dart';
import 'dylib_utils.dart';
// Reuse the compound classes.
@ -91,6 +94,7 @@ void main() {
testPassUint8Boolx9Struct10BytesHomogeneousBoolBool();
testPassUint8Boolx9Struct10BytesInlineArrayBoolBool();
testPassUint8Struct1ByteBool();
testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned();
testReturnStruct1ByteInt();
testReturnStruct3BytesHomogeneousUint8();
testReturnStruct3BytesInt2ByteAligned();
@ -5387,6 +5391,49 @@ void testPassUint8Struct1ByteBool() {
calloc.free(a1Pointer);
}
final passWCharStructInlineArrayIntUintPtrx2LongUnsigned =
ffiTestFunctions.lookupFunction<
WChar Function(
WChar, StructInlineArrayInt, UintPtr, UintPtr, Long, UnsignedLong),
int Function(int, StructInlineArrayInt, int, int, int,
int)>("PassWCharStructInlineArrayIntUintPtrx2LongUnsigned");
/// Returning a wchar.
void testPassWCharStructInlineArrayIntUintPtrx2LongUnsigned() {
int a0;
final a1Pointer = calloc<StructInlineArrayInt>();
final StructInlineArrayInt a1 = a1Pointer.ref;
int a2;
int a3;
int a4;
int a5;
a0 = 1;
a1.a0[0] = 2;
a1.a0[1] = 3;
a1.a0[2] = 4;
a1.a0[3] = 5;
a1.a0[4] = 6;
a1.a0[5] = 7;
a1.a0[6] = 8;
a1.a0[7] = 9;
a1.a0[8] = 10;
a1.a0[9] = 11;
a2 = 12;
a3 = 13;
a4 = 14;
a5 = 15;
final result = passWCharStructInlineArrayIntUintPtrx2LongUnsigned(
a0, a1, a2, a3, a4, a5);
print("result = $result");
Expect.equals(120, result);
calloc.free(a1Pointer);
}
final returnStruct1ByteInt = ffiTestFunctions.lookupFunction<
Struct1ByteInt Function(Int8),
Struct1ByteInt Function(int)>("ReturnStruct1ByteInt");

View File

@ -847,3 +847,35 @@ class TestStruct1802 extends Struct {
@Array.multi([2, 2, 2, 2, 2, 2, -1]) //# 1802: compile-time error
Array<Uint8> inlineArray; //# 1802: compile-time error
}
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint32(),
Abi.androidArm64: IntPtr(), //# 1900: compile-time error
Abi.androidIA32: AbiSpecificInteger1(), //# 1901: compile-time error
})
@AbiSpecificIntegerMapping({}) //# 1902: compile-time error
class AbiSpecificInteger1 extends AbiSpecificInteger {
const AbiSpecificInteger1();
int get a => 4; //# 1910: compile-time error
int b; //# 1911: compile-time error
}
class AbiSpecificInteger2
implements AbiSpecificInteger //# 1903: compile-time error
{
const AbiSpecificInteger2();
}
class AbiSpecificInteger3
extends AbiSpecificInteger1 //# 1904: compile-time error
{
const AbiSpecificInteger3();
}
class AbiSpecificInteger4
implements AbiSpecificInteger1 //# 1905: compile-time error
{
const AbiSpecificInteger4();
}