mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:04:53 +00:00
[vm / library] Foreign function interface prototype
Prototype for `dart:ffi` on Linux/MacOS x64 in JIT mode. `dart:ffi` is experimental and its API is likely to change in the future. Progress and design decisions are tracked in https://github.com/dart-lang/sdk/projects/13 issue: https://github.com/dart-lang/sdk/issues/34452 Change-Id: Ifa4566388e42c8757f154741d11e303465ef305d Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-mac-release-simarm64-try, vm-kernel-precomp-win-release-x64-try, vm-kernel-mac-debug-x64-try, vm-kernel-asan-linux-release-x64 Reviewed-on: https://dart-review.googlesource.com/c/80124 Reviewed-by: Samir Jindel <sjindel@google.com> Auto-Submit: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
parent
671865cd1a
commit
7d46d4b5cb
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -37,6 +37,7 @@
|
|||
.idea
|
||||
CMakeLists.txt
|
||||
.clang_complete
|
||||
cmake-build-debug
|
||||
|
||||
# VSCode project files
|
||||
.vscode
|
||||
|
@ -49,6 +50,9 @@ compile_commands.json
|
|||
# GDB files
|
||||
.gdb_history
|
||||
|
||||
# https://github.com/Dart-Code/Dart-Code/issues/1295
|
||||
analysis_options.yaml
|
||||
|
||||
# Built by chromebot and downloaded from Google Storage
|
||||
client/tests/drt
|
||||
|
||||
|
|
2
BUILD.gn
2
BUILD.gn
|
@ -47,6 +47,8 @@ group("runtime") {
|
|||
"runtime/bin:sample_extension",
|
||||
"runtime/bin:test_extension",
|
||||
"runtime/bin:entrypoints_verification_test_extension",
|
||||
"runtime/bin:ffi_test_dynamic_library",
|
||||
"runtime/bin:ffi_test_functions",
|
||||
"runtime/vm:kernel_platform_files($host_toolchain)",
|
||||
"utils/kernel-service:kernel-service",
|
||||
]
|
||||
|
|
|
@ -61,6 +61,11 @@ const Map<String, LibraryInfo> libraries = const {
|
|||
categories: "Client,Server,Embedded",
|
||||
maturity: Maturity.UNSTABLE,
|
||||
dart2jsPatchPath: "_internal/js_runtime/lib/developer_patch.dart"),
|
||||
"ffi": const LibraryInfo("ffi/ffi.dart",
|
||||
categories: "Server",
|
||||
// TODO(dacoharkes): Update maturity when we release dart:ffi.
|
||||
// https://github.com/dart-lang/sdk/issues/34452
|
||||
maturity: Maturity.EXPERIMENTAL),
|
||||
"html": const LibraryInfo("html/dart2js/html_dart2js.dart",
|
||||
categories: "Client",
|
||||
maturity: Maturity.WEB_STABLE,
|
||||
|
|
|
@ -53,10 +53,13 @@ export '../fasta/fasta_codes.dart'
|
|||
templateConstEvalNonConstantLiteral,
|
||||
templateConstEvalNonConstantVariableGet,
|
||||
templateConstEvalZeroDivisor,
|
||||
templateFfiAnnotationMissing,
|
||||
templateFfiFieldAnnotation,
|
||||
templateFfiStructAnnotation,
|
||||
templateFfiNotStatic,
|
||||
templateFfiTypeInvalid,
|
||||
templateFfiTypeMismatch,
|
||||
templateFfiTypeOpaque;
|
||||
templateFfiTypeUnsized,
|
||||
templateFfiFieldInitializer;
|
||||
|
||||
export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
|
||||
|
||||
|
|
|
@ -3348,28 +3348,101 @@ const MessageCode messageFastaUsageShort =
|
|||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<
|
||||
Message Function(
|
||||
String
|
||||
name)> templateFfiAnnotationMissing = const Template<
|
||||
Message Function(String name)> templateFfiFieldAnnotation = const Template<
|
||||
Message Function(String name)>(
|
||||
messageTemplate:
|
||||
r"""Field '#name' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
|
||||
withArguments: _withArgumentsFfiAnnotationMissing);
|
||||
r"""Field '#name' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
|
||||
withArguments: _withArgumentsFfiFieldAnnotation);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeFfiAnnotationMissing =
|
||||
const Code<Message Function(String name)> codeFfiFieldAnnotation =
|
||||
const Code<Message Function(String name)>(
|
||||
"FfiAnnotationMissing",
|
||||
templateFfiAnnotationMissing,
|
||||
"FfiFieldAnnotation",
|
||||
templateFfiFieldAnnotation,
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiAnnotationMissing(String name) {
|
||||
Message _withArgumentsFfiFieldAnnotation(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeFfiAnnotationMissing,
|
||||
return new Message(codeFfiFieldAnnotation,
|
||||
message:
|
||||
"""Field '${name}' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
|
||||
"""Field '${name}' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<
|
||||
Message Function(String name)> templateFfiFieldInitializer = const Template<
|
||||
Message Function(String name)>(
|
||||
messageTemplate:
|
||||
r"""Field '#name' is a dart:ffi Pointer to a struct field and therefore cannot be initialized before constructor execution.""",
|
||||
withArguments: _withArgumentsFfiFieldInitializer);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeFfiFieldInitializer =
|
||||
const Code<Message Function(String name)>(
|
||||
"FfiFieldInitializer",
|
||||
templateFfiFieldInitializer,
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiFieldInitializer(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeFfiFieldInitializer,
|
||||
message:
|
||||
"""Field '${name}' is a dart:ffi Pointer to a struct field and therefore cannot be initialized before constructor execution.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<
|
||||
Message Function(String name)> templateFfiNotStatic = const Template<
|
||||
Message Function(String name)>(
|
||||
messageTemplate:
|
||||
r"""#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from c.""",
|
||||
withArguments: _withArgumentsFfiNotStatic);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeFfiNotStatic =
|
||||
const Code<Message Function(String name)>(
|
||||
"FfiNotStatic",
|
||||
templateFfiNotStatic,
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiNotStatic(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeFfiNotStatic,
|
||||
message:
|
||||
"""${name} expects a static function as parameter. dart:ffi only supports calling static Dart functions from c.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Template<
|
||||
Message Function(String name)> templateFfiStructAnnotation = const Template<
|
||||
Message Function(String name)>(
|
||||
messageTemplate:
|
||||
r"""Class '#name' is a dart:ffi Pointer but has no struct annotation. Only struct Pointers can have fields.""",
|
||||
withArguments: _withArgumentsFfiStructAnnotation);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name)> codeFfiStructAnnotation =
|
||||
const Code<Message Function(String name)>(
|
||||
"FfiStructAnnotation",
|
||||
templateFfiStructAnnotation,
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiStructAnnotation(String name) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
return new Message(codeFfiStructAnnotation,
|
||||
message:
|
||||
"""Class '${name}' is a dart:ffi Pointer but has no struct annotation. Only struct Pointers can have fields.""",
|
||||
arguments: {'name': name});
|
||||
}
|
||||
|
||||
|
@ -3378,7 +3451,7 @@ const Template<
|
|||
Message Function(DartType _type)> templateFfiTypeInvalid = const Template<
|
||||
Message Function(DartType _type)>(
|
||||
messageTemplate:
|
||||
r"""Expected type '#type' to be a valid and instantiated subtype of '_NativeType'.""",
|
||||
r"""Expected type '#type' to be a valid and instantiated subtype of 'NativeType'.""",
|
||||
withArguments: _withArgumentsFfiTypeInvalid);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
|
@ -3395,7 +3468,7 @@ Message _withArgumentsFfiTypeInvalid(DartType _type) {
|
|||
String type = typeParts.join();
|
||||
return new Message(codeFfiTypeInvalid,
|
||||
message:
|
||||
"""Expected type '${type}' to be a valid and instantiated subtype of '_NativeType'.""" +
|
||||
"""Expected type '${type}' to be a valid and instantiated subtype of 'NativeType'.""" +
|
||||
labeler.originMessages,
|
||||
arguments: {'type': _type});
|
||||
}
|
||||
|
@ -3442,29 +3515,29 @@ const Template<
|
|||
Message Function(
|
||||
String name,
|
||||
DartType
|
||||
_type)> templateFfiTypeOpaque = const Template<
|
||||
_type)> templateFfiTypeUnsized = const Template<
|
||||
Message Function(String name, DartType _type)>(
|
||||
messageTemplate:
|
||||
r"""Method '#name' cannot be called on something of type '#type' as this type is opaque.""",
|
||||
withArguments: _withArgumentsFfiTypeOpaque);
|
||||
r"""Method '#name' cannot be called on something of type '#type' as this type is unsized.""",
|
||||
withArguments: _withArgumentsFfiTypeUnsized);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
const Code<Message Function(String name, DartType _type)> codeFfiTypeOpaque =
|
||||
const Code<Message Function(String name, DartType _type)> codeFfiTypeUnsized =
|
||||
const Code<Message Function(String name, DartType _type)>(
|
||||
"FfiTypeOpaque",
|
||||
templateFfiTypeOpaque,
|
||||
"FfiTypeUnsized",
|
||||
templateFfiTypeUnsized,
|
||||
);
|
||||
|
||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||
Message _withArgumentsFfiTypeOpaque(String name, DartType _type) {
|
||||
Message _withArgumentsFfiTypeUnsized(String name, DartType _type) {
|
||||
if (name.isEmpty) throw 'No name provided';
|
||||
name = demangleMixinApplicationName(name);
|
||||
TypeLabeler labeler = new TypeLabeler();
|
||||
List<Object> typeParts = labeler.labelType(_type);
|
||||
String type = typeParts.join();
|
||||
return new Message(codeFfiTypeOpaque,
|
||||
return new Message(codeFfiTypeUnsized,
|
||||
message:
|
||||
"""Method '${name}' cannot be called on something of type '${type}' as this type is opaque.""" +
|
||||
"""Method '${name}' cannot be called on something of type '${type}' as this type is unsized.""" +
|
||||
labeler.originMessages,
|
||||
arguments: {'name': name, 'type': _type});
|
||||
}
|
||||
|
|
|
@ -554,6 +554,7 @@ class KernelTarget extends TargetImplementation {
|
|||
"dart:_internal",
|
||||
"dart:async",
|
||||
"dart:core",
|
||||
"dart:ffi",
|
||||
"dart:mirrors"
|
||||
]) {
|
||||
Uri uri = Uri.parse(platformLibrary);
|
||||
|
@ -569,8 +570,8 @@ class KernelTarget extends TargetImplementation {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!found && uri.path != "mirrors") {
|
||||
// dart:mirrors is optional.
|
||||
if (!found && uri.path != "mirrors" && uri.path != "ffi") {
|
||||
// dart:mirrors and dart:ffi are optional.
|
||||
throw "Can't find $uri";
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -173,10 +173,13 @@ FastaUsageLong/analyzerCode: Fail
|
|||
FastaUsageLong/example: Fail
|
||||
FastaUsageShort/analyzerCode: Fail
|
||||
FastaUsageShort/example: Fail
|
||||
FfiAnnotationMissing/analyzerCode : Fail
|
||||
FfiFieldAnnotation/analyzerCode: Fail
|
||||
FfiFieldInitializer/analyzerCode: Fail
|
||||
FfiNotStatic/analyzerCode: Fail
|
||||
FfiStructAnnotation/analyzerCode: Fail
|
||||
FfiTypeInvalid/analyzerCode: Fail
|
||||
FfiTypeMismatch/analyzerCode: Fail
|
||||
FfiTypeOpaque/analyzerCode: Fail
|
||||
FfiTypeUnsized/analyzerCode: Fail
|
||||
FieldInitializedOutsideDeclaringClass/script1: Fail
|
||||
FieldInitializerOutsideConstructor/script1: Fail
|
||||
FinalAndCovariant/script2: Fail
|
||||
|
|
|
@ -3434,15 +3434,30 @@ FfiTypeMismatch:
|
|||
|
||||
FfiTypeInvalid:
|
||||
# Used by dart:ffi
|
||||
template: "Expected type '#type' to be a valid and instantiated subtype of '_NativeType'."
|
||||
template: "Expected type '#type' to be a valid and instantiated subtype of 'NativeType'."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiTypeOpaque:
|
||||
FfiTypeUnsized:
|
||||
# Used by dart:ffi
|
||||
template: "Method '#name' cannot be called on something of type '#type' as this type is opaque."
|
||||
template: "Method '#name' cannot be called on something of type '#type' as this type is unsized."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiAnnotationMissing:
|
||||
FfiFieldAnnotation:
|
||||
# Used by dart:ffi
|
||||
template: "Field '#name' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields."
|
||||
template: "Field '#name' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi structs (Pointer<Void>) cannot have regular Dart fields."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiNotStatic:
|
||||
# Used by dart:ffi
|
||||
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from c."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiFieldInitializer:
|
||||
# Used by dart:ffi
|
||||
template: "Field '#name' is a dart:ffi Pointer to a struct field and therefore cannot be initialized before constructor execution."
|
||||
external: test/ffi_test.dart
|
||||
|
||||
FfiStructAnnotation:
|
||||
# Used by dart:ffi
|
||||
template: "Class '#name' is a dart:ffi Pointer but has no struct annotation. Only struct Pointers can have fields."
|
||||
external: test/ffi_test.dart
|
||||
|
|
|
@ -155,6 +155,8 @@ abstract class TreeNode extends Node {
|
|||
|
||||
Component get enclosingComponent => parent?.enclosingComponent;
|
||||
|
||||
Library get enclosingLibrary => parent?.enclosingLibrary;
|
||||
|
||||
/// Returns the best known source location of the given AST node, or `null` if
|
||||
/// the node is orphaned.
|
||||
///
|
||||
|
@ -466,6 +468,8 @@ class Library extends NamedNode implements Comparable<Library>, FileUriNode {
|
|||
Location _getLocationInEnclosingFile(int offset) {
|
||||
return _getLocationInComponent(enclosingComponent, fileUri, offset);
|
||||
}
|
||||
|
||||
Library get enclosingLibraray => this;
|
||||
}
|
||||
|
||||
/// An import or export declaration in a library.
|
||||
|
|
|
@ -33,6 +33,11 @@ class CoreTypes {
|
|||
'dart:async': [
|
||||
'Future',
|
||||
'Stream',
|
||||
],
|
||||
'dart:ffi': [
|
||||
'DynamicLibrary',
|
||||
'Pointer',
|
||||
'Struct',
|
||||
]
|
||||
};
|
||||
|
||||
|
@ -93,10 +98,97 @@ class CoreTypes {
|
|||
Class _pragmaClass;
|
||||
Field _pragmaName;
|
||||
Field _pragmaOptions;
|
||||
Constructor _pragmaConstructor;
|
||||
|
||||
Library _ffiLibrary;
|
||||
Class _ffiPointerClass;
|
||||
Procedure _ffiPointerLoadProcedure;
|
||||
Procedure _ffiPointerStoreProcedure;
|
||||
Procedure _ffiPointerCastProcedure;
|
||||
Procedure _ffiPointerOffsetByProcedure;
|
||||
Procedure _ffiPointerAsFunctionProcedure;
|
||||
Field _ffiStructField;
|
||||
Class _ffiDynamicLibraryClass;
|
||||
Procedure _ffiDynamicLibraryLookupFunctionProcedure;
|
||||
Procedure _ffiAllocateProcedure;
|
||||
Procedure _ffiSizeOfProcedure;
|
||||
Procedure _ffiFromFunctionProcedure;
|
||||
Class _ffiNativeFunctionClass;
|
||||
|
||||
CoreTypes(Component component)
|
||||
: index = new LibraryIndex.coreLibraries(component);
|
||||
|
||||
Library get ffiLibrary {
|
||||
return _ffiLibrary ??= index.getLibrary('dart:ffi');
|
||||
}
|
||||
|
||||
Class get ffiPointerClass {
|
||||
return _ffiPointerClass ??= index.getClass('dart:ffi', 'Pointer');
|
||||
}
|
||||
|
||||
Procedure get ffiPointerLoadProcedure {
|
||||
return _ffiPointerLoadProcedure ??=
|
||||
index.getMember('dart:ffi', 'Pointer', 'load');
|
||||
}
|
||||
|
||||
Procedure get ffiPointerStoreProcedure {
|
||||
return _ffiPointerStoreProcedure ??=
|
||||
index.getMember('dart:ffi', 'Pointer', 'store');
|
||||
}
|
||||
|
||||
Procedure get ffiPointerCastProcedure {
|
||||
return _ffiPointerCastProcedure ??=
|
||||
index.getMember('dart:ffi', 'Pointer', 'cast');
|
||||
}
|
||||
|
||||
Procedure get ffiPointerOffsetByProcedure {
|
||||
return _ffiPointerOffsetByProcedure ??=
|
||||
index.getMember('dart:ffi', 'Pointer', 'offsetBy');
|
||||
}
|
||||
|
||||
Procedure get ffiPointerAsFunctionProcedure {
|
||||
return _ffiPointerAsFunctionProcedure ??=
|
||||
index.getMember('dart:ffi', 'Pointer', 'asFunction');
|
||||
}
|
||||
|
||||
Field get ffiStructField {
|
||||
return _ffiStructField ??= index.getTopLevelMember('dart:ffi', 'struct');
|
||||
}
|
||||
|
||||
Class get ffiDynamicLibraryClass {
|
||||
return _ffiDynamicLibraryClass ??=
|
||||
index.getClass('dart:ffi', 'DynamicLibrary');
|
||||
}
|
||||
|
||||
Procedure get ffiDynamicLibraryLookupFunctionProcedure {
|
||||
return _ffiDynamicLibraryLookupFunctionProcedure ??=
|
||||
index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction');
|
||||
}
|
||||
|
||||
Procedure get ffiAllocateProcedure {
|
||||
return _ffiAllocateProcedure ??=
|
||||
index.getTopLevelMember('dart:ffi', 'allocate');
|
||||
}
|
||||
|
||||
Procedure get ffiSizeOfProcedure {
|
||||
return _ffiSizeOfProcedure ??=
|
||||
index.getTopLevelMember('dart:ffi', 'sizeOf');
|
||||
}
|
||||
|
||||
Procedure get ffiFromFunctionProcedure {
|
||||
return _ffiFromFunctionProcedure ??=
|
||||
index.getTopLevelMember('dart:ffi', 'fromFunction');
|
||||
}
|
||||
|
||||
Class get ffiNativeFunctionClass {
|
||||
return _ffiNativeFunctionClass ??=
|
||||
index.getClass('dart:ffi', 'NativeFunction');
|
||||
}
|
||||
|
||||
Class ffiNativeTypeClass(String name) {
|
||||
return index.getClass('dart:ffi', name);
|
||||
}
|
||||
|
||||
Procedure get asyncErrorWrapperHelperProcedure {
|
||||
return _asyncErrorWrapperHelperProcedure ??=
|
||||
index.getTopLevelMember('dart:async', '_asyncErrorWrapperHelper');
|
||||
|
@ -312,6 +404,10 @@ class CoreTypes {
|
|||
return _pragmaOptions ??= index.getMember('dart:core', 'pragma', 'options');
|
||||
}
|
||||
|
||||
Constructor get pragmaConstructor {
|
||||
return _pragmaConstructor ??= index.getMember('dart:core', 'pragma', '_');
|
||||
}
|
||||
|
||||
Class get stackTraceClass {
|
||||
return _stackTraceClass ??= index.getClass('dart:core', 'StackTrace');
|
||||
}
|
||||
|
|
197
pkg/kernel/lib/transformations/ffi.dart
Normal file
197
pkg/kernel/lib/transformations/ffi.dart
Normal file
|
@ -0,0 +1,197 @@
|
|||
// 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.
|
||||
|
||||
// This file contains logic which is shared between the ffi_definition and
|
||||
// ffi_use_site transformers.
|
||||
|
||||
library kernel.transformations.ffi;
|
||||
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
import 'package:kernel/type_environment.dart' show TypeEnvironment;
|
||||
|
||||
import '../ast.dart';
|
||||
import '../core_types.dart';
|
||||
import '../target/targets.dart' show DiagnosticReporter;
|
||||
|
||||
/// Represents the (instantiated) ffi.NativeType.
|
||||
enum NativeType {
|
||||
kPointer,
|
||||
kNativeFunction,
|
||||
kInt8,
|
||||
kInt16,
|
||||
kInt32,
|
||||
kInt64,
|
||||
kUint8,
|
||||
kUint16,
|
||||
kUint32,
|
||||
kUnit64,
|
||||
kIntptr,
|
||||
kFloat,
|
||||
kDouble,
|
||||
kVoid
|
||||
}
|
||||
|
||||
const NativeType kNativeTypeIntStart = NativeType.kInt8;
|
||||
const NativeType kNativeTypeIntEnd = NativeType.kIntptr;
|
||||
|
||||
/// The [NativeType] class names, indexed by [NativeType].
|
||||
const List<String> nativeTypeClassNames = [
|
||||
'Pointer',
|
||||
'NativeFunction',
|
||||
'Int8',
|
||||
'Int16',
|
||||
'Int32',
|
||||
'Int64',
|
||||
'Uint8',
|
||||
'Uint16',
|
||||
'Uint32',
|
||||
'Uint64',
|
||||
'IntPtr',
|
||||
'Float',
|
||||
'Double',
|
||||
'Void'
|
||||
];
|
||||
|
||||
const int UNKNOWN = 0;
|
||||
const int WORD_SIZE = -1;
|
||||
|
||||
/// The [NativeType] sizes in bytes, indexed by [NativeType].
|
||||
const List<int> nativeTypeSizes = [
|
||||
WORD_SIZE, // Pointer
|
||||
UNKNOWN, // NativeFunction
|
||||
1, // Int8
|
||||
2, // Int16
|
||||
4, // Int32
|
||||
8, // Int64
|
||||
1, // Uint8
|
||||
2, // Uint16
|
||||
4, // Uint32
|
||||
8, // Uint64
|
||||
WORD_SIZE, // IntPtr
|
||||
4, // Float
|
||||
8, // Double
|
||||
UNKNOWN, // Void
|
||||
];
|
||||
|
||||
/// [FfiTransformer] contains logic which is shared between
|
||||
/// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
|
||||
class FfiTransformer extends Transformer {
|
||||
final TypeEnvironment env;
|
||||
final ClassHierarchy hierarchy;
|
||||
final DiagnosticReporter diagnosticReporter;
|
||||
|
||||
final Class intClass;
|
||||
final Class doubleClass;
|
||||
final Constructor pragmaConstructor;
|
||||
|
||||
final Library ffiLibrary;
|
||||
final Class nativeFunctionClass;
|
||||
final Class pointerClass;
|
||||
final Procedure castMethod;
|
||||
final Procedure loadMethod;
|
||||
final Procedure storeMethod;
|
||||
final Procedure offsetByMethod;
|
||||
final Procedure asFunctionMethod;
|
||||
final Procedure lookupFunctionMethod;
|
||||
final Procedure fromFunctionMethod;
|
||||
final Field structField;
|
||||
|
||||
/// Classes corresponding to [NativeType], indexed by [NativeType].
|
||||
final List<Class> nativeTypesClasses;
|
||||
|
||||
FfiTransformer(this.hierarchy, CoreTypes coreTypes, this.diagnosticReporter)
|
||||
: env = new TypeEnvironment(coreTypes, hierarchy),
|
||||
intClass = coreTypes.intClass,
|
||||
doubleClass = coreTypes.doubleClass,
|
||||
ffiLibrary = coreTypes.ffiLibrary,
|
||||
nativeFunctionClass = coreTypes.ffiNativeFunctionClass,
|
||||
pointerClass = coreTypes.ffiPointerClass,
|
||||
castMethod = coreTypes.ffiPointerCastProcedure,
|
||||
loadMethod = coreTypes.ffiPointerLoadProcedure,
|
||||
storeMethod = coreTypes.ffiPointerStoreProcedure,
|
||||
offsetByMethod = coreTypes.ffiPointerOffsetByProcedure,
|
||||
asFunctionMethod = coreTypes.ffiPointerAsFunctionProcedure,
|
||||
lookupFunctionMethod =
|
||||
coreTypes.ffiDynamicLibraryLookupFunctionProcedure,
|
||||
fromFunctionMethod = coreTypes.ffiFromFunctionProcedure,
|
||||
structField = coreTypes.ffiStructField,
|
||||
pragmaConstructor = coreTypes.pragmaConstructor,
|
||||
nativeTypesClasses =
|
||||
nativeTypeClassNames.map(coreTypes.ffiNativeTypeClass).toList() {}
|
||||
|
||||
/// Computes the Dart type corresponding to a ffi.[NativeType], returns null
|
||||
/// if it is not a valid NativeType.
|
||||
///
|
||||
/// [Int8] -> [int]
|
||||
/// [Int16] -> [int]
|
||||
/// [Int32] -> [int]
|
||||
/// [Int64] -> [int]
|
||||
/// [Uint8] -> [int]
|
||||
/// [Uint16] -> [int]
|
||||
/// [Uint32] -> [int]
|
||||
/// [Uint64] -> [int]
|
||||
/// [IntPtr] -> [int]
|
||||
/// [Double] -> [double]
|
||||
/// [Float] -> [double]
|
||||
/// [Pointer]<T> -> [Pointer]<T>
|
||||
/// T extends [Pointer] -> T
|
||||
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
|
||||
/// where DartRepresentationOf(Tn) -> Sn
|
||||
DartType convertNativeTypeToDartType(DartType nativeType) {
|
||||
if (nativeType is! InterfaceType) {
|
||||
return null;
|
||||
}
|
||||
Class nativeClass = (nativeType as InterfaceType).classNode;
|
||||
if (env.isSubtypeOf(
|
||||
InterfaceType(nativeClass), InterfaceType(pointerClass))) {
|
||||
return nativeType;
|
||||
}
|
||||
NativeType nativeType_ = getType(nativeClass);
|
||||
if (nativeType_ == null) {
|
||||
return null;
|
||||
}
|
||||
if (kNativeTypeIntStart.index <= nativeType_.index &&
|
||||
nativeType_.index <= kNativeTypeIntEnd.index) {
|
||||
return InterfaceType(intClass);
|
||||
}
|
||||
if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) {
|
||||
return InterfaceType(doubleClass);
|
||||
}
|
||||
if (nativeType_ == NativeType.kNativeFunction) {
|
||||
DartType fun = (nativeType as InterfaceType).typeArguments[0];
|
||||
if (fun is FunctionType) {
|
||||
if (fun.namedParameters.isNotEmpty) return null;
|
||||
if (fun.positionalParameters.length != fun.requiredParameterCount)
|
||||
return null;
|
||||
if (fun.typeParameters.length != 0) return null;
|
||||
DartType returnType = convertNativeTypeToDartType(fun.returnType);
|
||||
if (returnType == null) return null;
|
||||
List<DartType> argumentTypes = fun.positionalParameters
|
||||
.map(this.convertNativeTypeToDartType)
|
||||
.toList();
|
||||
if (argumentTypes.contains(null)) return null;
|
||||
return FunctionType(argumentTypes, returnType);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
NativeType getType(Class c) {
|
||||
int index = nativeTypesClasses.indexOf(c);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return NativeType.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains replaced members, of which all the call sites need to be replaced.
|
||||
///
|
||||
/// [ReplacedMembers] is populated by _FfiDefinitionTransformer and consumed by
|
||||
/// _FfiUseSiteTransformer.
|
||||
class ReplacedMembers {
|
||||
final Map<Field, Procedure> replacedGetters;
|
||||
final Map<Field, Procedure> replacedSetters;
|
||||
ReplacedMembers(this.replacedGetters, this.replacedSetters);
|
||||
}
|
372
pkg/kernel/lib/transformations/ffi_definitions.dart
Normal file
372
pkg/kernel/lib/transformations/ffi_definitions.dart
Normal file
|
@ -0,0 +1,372 @@
|
|||
// 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.
|
||||
|
||||
library kernel.transformations.ffi_definitions;
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:front_end/src/api_unstable/vm.dart'
|
||||
show
|
||||
templateFfiFieldAnnotation,
|
||||
templateFfiStructAnnotation,
|
||||
templateFfiTypeMismatch,
|
||||
templateFfiFieldInitializer;
|
||||
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
|
||||
import '../ast.dart';
|
||||
import '../core_types.dart';
|
||||
import '../target/targets.dart' show DiagnosticReporter;
|
||||
|
||||
import 'ffi.dart'
|
||||
show
|
||||
ReplacedMembers,
|
||||
NativeType,
|
||||
FfiTransformer,
|
||||
nativeTypeSizes,
|
||||
WORD_SIZE;
|
||||
|
||||
/// Checks and expands the dart:ffi @struct and field annotations.
|
||||
///
|
||||
/// Sample input:
|
||||
/// @ffi.struct
|
||||
/// class Coord extends ffi.Pointer<Void> {
|
||||
/// @ffi.Double()
|
||||
/// double x;
|
||||
///
|
||||
/// @ffi.Double()
|
||||
/// double y;
|
||||
///
|
||||
/// @ffi.Pointer()
|
||||
/// Coord next;
|
||||
///
|
||||
/// external static int sizeOf();
|
||||
/// }
|
||||
///
|
||||
/// Sample output:
|
||||
/// class Coordinate extends ffi.Pointer<ffi.Void> {
|
||||
/// ffi.Pointer<ffi.Double> get _xPtr => cast();
|
||||
/// set x(double v) => _xPtr.store(v);
|
||||
/// double get x => _xPtr.load();
|
||||
///
|
||||
/// ffi.Pointer<ffi.Double> get _yPtr =>
|
||||
/// offsetBy(ffi.sizeOf<ffi.Double>() * 1).cast();
|
||||
/// set y(double v) => _yPtr.store(v);
|
||||
/// double get y => _yPtr.load();
|
||||
///
|
||||
/// ffi.Pointer<Coordinate> get _nextPtr =>
|
||||
/// offsetBy(ffi.sizeOf<ffi.Double>() * 2).cast();
|
||||
/// set next(Coordinate v) => _nextPtr.store(v);
|
||||
/// Coordinate get next => _nextPtr.load();
|
||||
///
|
||||
/// static int sizeOf() => 24;
|
||||
/// }
|
||||
ReplacedMembers transformLibraries(
|
||||
CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy,
|
||||
List<Library> libraries,
|
||||
DiagnosticReporter diagnosticReporter) {
|
||||
final transformer =
|
||||
new _FfiDefinitionTransformer(hierarchy, coreTypes, diagnosticReporter);
|
||||
libraries.forEach(transformer.visitLibrary);
|
||||
return ReplacedMembers(
|
||||
transformer.replacedGetters, transformer.replacedSetters);
|
||||
}
|
||||
|
||||
/// Checks and expands the dart:ffi @struct and field annotations.
|
||||
class _FfiDefinitionTransformer extends FfiTransformer {
|
||||
Map<Field, Procedure> replacedGetters = {};
|
||||
Map<Field, Procedure> replacedSetters = {};
|
||||
|
||||
_FfiDefinitionTransformer(ClassHierarchy hierarchy, CoreTypes coreTypes,
|
||||
DiagnosticReporter diagnosticReporter)
|
||||
: super(hierarchy, coreTypes, diagnosticReporter) {}
|
||||
|
||||
@override
|
||||
visitClass(Class node) {
|
||||
if (node == pointerClass || !hierarchy.isSubtypeOf(node, pointerClass)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Because subtypes of Pointer are only allocated by allocate<Pointer<..>>()
|
||||
// and fromAddress<Pointer<..>>() which are not recognized as constructor
|
||||
// calls, we need to prevent these classes from being tree shaken out.
|
||||
_preventTreeShaking(node);
|
||||
|
||||
_checkFieldAnnotations(node);
|
||||
_checkConstructors(node);
|
||||
|
||||
bool isStruct = _checkStructAnnotation(node);
|
||||
if (isStruct) {
|
||||
int size = _replaceFields(node);
|
||||
_replaceSizeOfMethod(node, size);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
bool _checkStructAnnotation(Class node) {
|
||||
bool isStruct = _hasAnnotation(node);
|
||||
if (!isStruct && node.fields.isNotEmpty) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiStructAnnotation.withArguments(node.name),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.fileUri);
|
||||
}
|
||||
return isStruct;
|
||||
}
|
||||
|
||||
void _checkFieldAnnotations(Class node) {
|
||||
for (Field f in node.fields) {
|
||||
if (f.initializer is! NullLiteral) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiFieldInitializer.withArguments(f.name.name),
|
||||
f.fileOffset,
|
||||
f.name.name.length,
|
||||
f.fileUri);
|
||||
}
|
||||
List<NativeType> annos = _getAnnotations(f).toList();
|
||||
if (annos.length != 1) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiFieldAnnotation.withArguments(f.name.name),
|
||||
f.fileOffset,
|
||||
f.name.name.length,
|
||||
f.fileUri);
|
||||
} else {
|
||||
DartType dartType = f.type;
|
||||
DartType nativeType =
|
||||
InterfaceType(nativeTypesClasses[annos.first.index]);
|
||||
DartType shouldBeDartType = convertNativeTypeToDartType(nativeType);
|
||||
if (!env.isSubtypeOf(dartType, shouldBeDartType)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiTypeMismatch.withArguments(
|
||||
dartType, shouldBeDartType, nativeType),
|
||||
f.fileOffset,
|
||||
1,
|
||||
f.location.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _checkConstructors(Class node) {
|
||||
List<Initializer> toRemove = [];
|
||||
for (Constructor c in node.constructors) {
|
||||
for (Initializer i in c.initializers) {
|
||||
if (i is FieldInitializer) {
|
||||
toRemove.add(i);
|
||||
diagnosticReporter.report(
|
||||
templateFfiFieldInitializer.withArguments(i.field.name.name),
|
||||
i.fileOffset,
|
||||
1,
|
||||
i.location.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove initializers referring to fields to prevent cascading errors.
|
||||
for (Initializer i in toRemove) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the field offsets in the struct and replaces the fields with
|
||||
/// getters and setters using these offsets.
|
||||
///
|
||||
/// Returns the total size of the struct.
|
||||
int _replaceFields(Class node) {
|
||||
List<Field> fields = [];
|
||||
List<NativeType> types = [];
|
||||
|
||||
for (Field f in node.fields) {
|
||||
List<NativeType> annos = _getAnnotations(f).toList();
|
||||
if (annos.length == 1) {
|
||||
NativeType t = annos.first;
|
||||
fields.add(f);
|
||||
types.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
List<int> offsets = _calculateOffsets(types);
|
||||
int size = _calculateSize(offsets, types);
|
||||
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
List<Procedure> methods =
|
||||
_generateMethodsForField(fields[i], types[i], offsets[i]);
|
||||
for (Procedure p in methods) {
|
||||
node.addMember(p);
|
||||
}
|
||||
}
|
||||
|
||||
for (Field f in fields) {
|
||||
f.remove();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// Sample output:
|
||||
/// ffi.Pointer<ffi.Double> get _xPtr => cast();
|
||||
/// double get x => _xPtr.load();
|
||||
/// set x(double v) => _xPtr.store(v);
|
||||
List<Procedure> _generateMethodsForField(
|
||||
Field field, NativeType type, int offset) {
|
||||
DartType nativeType = type == NativeType.kPointer
|
||||
? field.type
|
||||
: InterfaceType(nativeTypesClasses[type.index]);
|
||||
DartType pointerType = InterfaceType(pointerClass, [nativeType]);
|
||||
Name pointerName = Name('#_ptr_${field.name.name}');
|
||||
|
||||
// Sample output for primitives:
|
||||
// ffi.Pointer<ffi.Double> get _xPtr => cast<ffi.Pointer<ffi.Double>>();
|
||||
// Sample output for structs:
|
||||
// ffi.Pointer<Coordinate> get _xPtr => offsetBy(16).cast<...>();
|
||||
Expression offsetExpression = ThisExpression();
|
||||
if (offset != 0) {
|
||||
offsetExpression = MethodInvocation(offsetExpression, offsetByMethod.name,
|
||||
Arguments([IntLiteral(offset)]), offsetByMethod);
|
||||
}
|
||||
Procedure pointerGetter = Procedure(
|
||||
pointerName,
|
||||
ProcedureKind.Getter,
|
||||
FunctionNode(
|
||||
ReturnStatement(MethodInvocation(offsetExpression, castMethod.name,
|
||||
Arguments([], types: [pointerType]), castMethod)),
|
||||
returnType: pointerType));
|
||||
|
||||
// Sample output:
|
||||
// double get x => _xPtr.load<double>();
|
||||
Procedure getter = Procedure(
|
||||
field.name,
|
||||
ProcedureKind.Getter,
|
||||
FunctionNode(
|
||||
ReturnStatement(MethodInvocation(
|
||||
PropertyGet(ThisExpression(), pointerName, pointerGetter),
|
||||
loadMethod.name,
|
||||
Arguments([], types: [field.type]),
|
||||
loadMethod)),
|
||||
returnType: field.type));
|
||||
|
||||
// Sample output:
|
||||
// set x(double v) => _xPtr.store(v);
|
||||
VariableDeclaration argument = VariableDeclaration('#v', type: field.type);
|
||||
Procedure setter = Procedure(
|
||||
field.name,
|
||||
ProcedureKind.Setter,
|
||||
FunctionNode(
|
||||
ReturnStatement(MethodInvocation(
|
||||
PropertyGet(ThisExpression(), pointerName, pointerGetter),
|
||||
storeMethod.name,
|
||||
Arguments([VariableGet(argument)]),
|
||||
storeMethod)),
|
||||
returnType: VoidType(),
|
||||
positionalParameters: [argument]));
|
||||
|
||||
replacedGetters[field] = getter;
|
||||
replacedSetters[field] = setter;
|
||||
|
||||
return [pointerGetter, getter, setter];
|
||||
}
|
||||
|
||||
/// Sample input:
|
||||
/// external static int sizeOf();
|
||||
///
|
||||
/// Sample output:
|
||||
/// static int sizeOf() => 24;
|
||||
void _replaceSizeOfMethod(Class struct, int size) {
|
||||
Procedure sizeOf = _findProcedure(struct, 'sizeOf');
|
||||
if (sizeOf == null || !sizeOf.isExternal || !sizeOf.isStatic) {
|
||||
return;
|
||||
}
|
||||
|
||||
// replace in place to avoid going over use sites
|
||||
sizeOf.function = FunctionNode(ReturnStatement(IntLiteral(size)),
|
||||
returnType: InterfaceType(intClass));
|
||||
sizeOf.isExternal = false;
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): move to VM, take into account architecture
|
||||
// https://github.com/dart-lang/sdk/issues/35768
|
||||
int _sizeInBytes(NativeType t) {
|
||||
int size = nativeTypeSizes[t.index];
|
||||
if (size == WORD_SIZE) {
|
||||
size = 8;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int _align(int offset, int size) {
|
||||
int remainder = offset % size;
|
||||
if (remainder != 0) {
|
||||
offset -= remainder;
|
||||
offset += size;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): move to VM, take into account architecture
|
||||
// https://github.com/dart-lang/sdk/issues/35768
|
||||
List<int> _calculateOffsets(List<NativeType> types) {
|
||||
int offset = 0;
|
||||
List<int> offsets = [];
|
||||
for (NativeType t in types) {
|
||||
int size = _sizeInBytes(t);
|
||||
offset = _align(offset, size);
|
||||
offsets.add(offset);
|
||||
offset += size;
|
||||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): move to VM, take into account architecture
|
||||
// https://github.com/dart-lang/sdk/issues/35768
|
||||
int _calculateSize(List<int> offsets, List<NativeType> types) {
|
||||
if (offsets.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
int largestElement = types.map((e) => _sizeInBytes(e)).reduce(math.max);
|
||||
int highestOffsetIndex = types.length - 1;
|
||||
int highestOffset = offsets[highestOffsetIndex];
|
||||
int highestOffsetSize = _sizeInBytes(types[highestOffsetIndex]);
|
||||
return _align(highestOffset + highestOffsetSize, largestElement);
|
||||
}
|
||||
|
||||
bool _hasAnnotation(Class node) {
|
||||
for (Expression e in node.annotations) {
|
||||
if (e is StaticGet) {
|
||||
if (e.target == structField) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _preventTreeShaking(Class node) {
|
||||
node.addAnnotation(ConstructorInvocation(
|
||||
pragmaConstructor, Arguments([StringLiteral("vm:entry-point")])));
|
||||
}
|
||||
|
||||
NativeType _getFieldType(Class c) {
|
||||
NativeType fieldType = getType(c);
|
||||
|
||||
if (fieldType == NativeType.kVoid) {
|
||||
// Fields cannot have Void types.
|
||||
return null;
|
||||
}
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
Iterable<NativeType> _getAnnotations(Field node) {
|
||||
return node.annotations
|
||||
.whereType<ConstructorInvocation>()
|
||||
.map((expr) => expr.target.parent)
|
||||
.map((klass) => _getFieldType(klass))
|
||||
.where((type) => type != null);
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds procedure with name, otherwise returns null.
|
||||
Procedure _findProcedure(Class c, String name) =>
|
||||
c.procedures.firstWhere((p) => p.name.name == name, orElse: () => null);
|
270
pkg/kernel/lib/transformations/ffi_use_sites.dart
Normal file
270
pkg/kernel/lib/transformations/ffi_use_sites.dart
Normal file
|
@ -0,0 +1,270 @@
|
|||
// 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.
|
||||
|
||||
library kernel.transformations.ffi_use_sites;
|
||||
|
||||
import 'package:front_end/src/api_unstable/vm.dart'
|
||||
show
|
||||
templateFfiTypeInvalid,
|
||||
templateFfiTypeMismatch,
|
||||
templateFfiTypeUnsized,
|
||||
templateFfiNotStatic;
|
||||
|
||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||
|
||||
import '../ast.dart';
|
||||
import '../core_types.dart';
|
||||
import '../target/targets.dart' show DiagnosticReporter;
|
||||
|
||||
import 'ffi.dart'
|
||||
show
|
||||
ReplacedMembers,
|
||||
NativeType,
|
||||
kNativeTypeIntStart,
|
||||
kNativeTypeIntEnd,
|
||||
FfiTransformer;
|
||||
|
||||
/// Checks and replaces calls to dart:ffi struct fields and methods.
|
||||
void transformLibraries(
|
||||
CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy,
|
||||
List<Library> libraries,
|
||||
DiagnosticReporter diagnosticReporter,
|
||||
ReplacedMembers replacedFields) {
|
||||
final transformer = new _FfiUseSiteTransformer(
|
||||
hierarchy,
|
||||
coreTypes,
|
||||
diagnosticReporter,
|
||||
replacedFields.replacedGetters,
|
||||
replacedFields.replacedSetters);
|
||||
libraries.forEach(transformer.visitLibrary);
|
||||
}
|
||||
|
||||
/// Checks and replaces calls to dart:ffi struct fields and methods.
|
||||
class _FfiUseSiteTransformer extends FfiTransformer {
|
||||
final Map<Field, Procedure> replacedGetters;
|
||||
final Map<Field, Procedure> replacedSetters;
|
||||
|
||||
_FfiUseSiteTransformer(
|
||||
ClassHierarchy hierarchy,
|
||||
CoreTypes coreTypes,
|
||||
DiagnosticReporter diagnosticReporter,
|
||||
this.replacedGetters,
|
||||
this.replacedSetters)
|
||||
: super(hierarchy, coreTypes, diagnosticReporter) {}
|
||||
|
||||
@override
|
||||
visitClass(Class node) {
|
||||
env.thisType = InterfaceType(node);
|
||||
try {
|
||||
return super.visitClass(node);
|
||||
} finally {
|
||||
env.thisType = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
visitPropertyGet(PropertyGet node) {
|
||||
super.visitPropertyGet(node);
|
||||
|
||||
Procedure replacedWith = replacedGetters[node.interfaceTarget];
|
||||
if (replacedWith != null) {
|
||||
node = PropertyGet(node.receiver, replacedWith.name, replacedWith);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
visitPropertySet(PropertySet node) {
|
||||
super.visitPropertySet(node);
|
||||
|
||||
Procedure replacedWith = replacedSetters[node.interfaceTarget];
|
||||
if (replacedWith != null) {
|
||||
node = PropertySet(
|
||||
node.receiver, replacedWith.name, node.value, replacedWith);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
visitStaticInvocation(StaticInvocation node) {
|
||||
super.visitStaticInvocation(node);
|
||||
|
||||
Member target = node.target;
|
||||
try {
|
||||
if (target == fromFunctionMethod) {
|
||||
DartType nativeType =
|
||||
InterfaceType(nativeFunctionClass, [node.arguments.types[0]]);
|
||||
Expression func = node.arguments.positional[0];
|
||||
DartType dartType = func.getStaticType(env);
|
||||
|
||||
_ensureIsStatic(func);
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
}
|
||||
} catch (_FfiStaticTypeError) {}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@override
|
||||
visitMethodInvocation(MethodInvocation node) {
|
||||
super.visitMethodInvocation(node);
|
||||
|
||||
Member target = node.interfaceTarget;
|
||||
try {
|
||||
if (target == lookupFunctionMethod) {
|
||||
DartType nativeType =
|
||||
InterfaceType(nativeFunctionClass, [node.arguments.types[0]]);
|
||||
DartType dartType = node.arguments.types[1];
|
||||
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
} else if (target == asFunctionMethod) {
|
||||
if (node.enclosingLibrary == ffiLibrary) {
|
||||
// Library code of dart:ffi uses asFunction to implement
|
||||
// lookupFunction. Since we treat lookupFunction as well, this call
|
||||
// can be generic and still support AOT.
|
||||
return node;
|
||||
}
|
||||
|
||||
DartType dartType = node.arguments.types[0];
|
||||
DartType pointerType = node.receiver.getStaticType(env);
|
||||
DartType nativeType = _pointerTypeGetTypeArg(pointerType);
|
||||
|
||||
_ensureNativeTypeValid(pointerType, node);
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
} else if (target == loadMethod) {
|
||||
// TODO(dacoharkes): should load and store permitted to be generic?
|
||||
// https://github.com/dart-lang/sdk/issues/35902
|
||||
DartType dartType = node.arguments.types[0];
|
||||
DartType pointerType = node.receiver.getStaticType(env);
|
||||
DartType nativeType = _pointerTypeGetTypeArg(pointerType);
|
||||
|
||||
_ensureNativeTypeValid(pointerType, node);
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeSized(nativeType, node, target.name);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
} else if (target == storeMethod) {
|
||||
// TODO(dacoharkes): should load and store permitted to be generic?
|
||||
// https://github.com/dart-lang/sdk/issues/35902
|
||||
DartType dartType = node.arguments.positional[0].getStaticType(env);
|
||||
DartType pointerType = node.receiver.getStaticType(env);
|
||||
DartType nativeType = _pointerTypeGetTypeArg(pointerType);
|
||||
|
||||
_ensureNativeTypeValid(pointerType, node);
|
||||
_ensureNativeTypeValid(nativeType, node);
|
||||
_ensureNativeTypeSized(nativeType, node, target.name);
|
||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||
}
|
||||
} catch (_FfiStaticTypeError) {}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
DartType _pointerTypeGetTypeArg(DartType pointerType) {
|
||||
if (pointerType is InterfaceType) {
|
||||
InterfaceType superType =
|
||||
hierarchy.getTypeAsInstanceOf(pointerType, pointerClass);
|
||||
return superType?.typeArguments[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _ensureNativeTypeToDartType(
|
||||
DartType nativeType, DartType dartType, Expression node) {
|
||||
DartType shouldBeDartType = convertNativeTypeToDartType(nativeType);
|
||||
if (dartType != shouldBeDartType) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiTypeMismatch.withArguments(
|
||||
dartType, shouldBeDartType, nativeType),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location.file);
|
||||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
void _ensureNativeTypeValid(DartType nativeType, Expression node) {
|
||||
if (!_nativeTypeValid(nativeType)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiTypeInvalid.withArguments(nativeType),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location.file);
|
||||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
/// The Dart type system does not enforce that NativeFunction return and
|
||||
/// parameter types are only NativeTypes, so we need to check this.
|
||||
bool _nativeTypeValid(DartType nativeType) {
|
||||
return convertNativeTypeToDartType(nativeType) != null;
|
||||
}
|
||||
|
||||
void _ensureNativeTypeSized(
|
||||
DartType nativeType, Expression node, Name targetName) {
|
||||
if (!_nativeTypeSized(nativeType)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiTypeUnsized.withArguments(targetName.name, nativeType),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location.file);
|
||||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsized NativeTypes do not support [sizeOf] because their size is unknown.
|
||||
/// Consequently, [allocate], [Pointer.load], [Pointer.store], and
|
||||
/// [Pointer.elementAt] are not available.
|
||||
bool _nativeTypeSized(DartType nativeType) {
|
||||
if (!(nativeType is InterfaceType)) {
|
||||
return false;
|
||||
}
|
||||
Class nativeClass = (nativeType as InterfaceType).classNode;
|
||||
if (env.isSubtypeOf(
|
||||
InterfaceType(nativeClass), InterfaceType(pointerClass))) {
|
||||
return true;
|
||||
}
|
||||
NativeType nativeType_ = getType(nativeClass);
|
||||
if (nativeType_ == null) {
|
||||
return false;
|
||||
}
|
||||
if (kNativeTypeIntStart.index <= nativeType_.index &&
|
||||
nativeType_.index <= kNativeTypeIntEnd.index) {
|
||||
return true;
|
||||
}
|
||||
if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) {
|
||||
return true;
|
||||
}
|
||||
if (nativeType_ == NativeType.kPointer) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _ensureIsStatic(Expression node) {
|
||||
if (!_isStatic(node)) {
|
||||
diagnosticReporter.report(
|
||||
templateFfiNotStatic.withArguments(fromFunctionMethod.name.name),
|
||||
node.fileOffset,
|
||||
1,
|
||||
node.location.file);
|
||||
throw _FfiStaticTypeError();
|
||||
}
|
||||
}
|
||||
|
||||
bool _isStatic(Expression node) {
|
||||
if (node is! StaticGet) return false;
|
||||
|
||||
return (node as StaticGet).target is Procedure;
|
||||
}
|
||||
}
|
||||
|
||||
/// Used internally for abnormal control flow to prevent cascading error
|
||||
/// messages.
|
||||
class _FfiStaticTypeError implements Exception {}
|
|
@ -9,6 +9,12 @@ import 'package:kernel/ast.dart';
|
|||
import 'package:kernel/class_hierarchy.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:kernel/transformations/ffi.dart' as transformFfi
|
||||
show ReplacedMembers;
|
||||
import 'package:kernel/transformations/ffi_definitions.dart'
|
||||
as transformFfiDefinitions show transformLibraries;
|
||||
import 'package:kernel/transformations/ffi_use_sites.dart'
|
||||
as transformFfiUseSites show transformLibraries;
|
||||
import 'package:kernel/transformations/mixin_full_resolution.dart'
|
||||
as transformMixins show transformLibraries;
|
||||
import 'package:kernel/transformations/constants.dart' show ConstantsBackend;
|
||||
|
@ -68,6 +74,7 @@ class VmTarget extends Target {
|
|||
'dart:nativewrappers',
|
||||
'dart:io',
|
||||
'dart:cli',
|
||||
'dart:ffi',
|
||||
];
|
||||
|
||||
@override
|
||||
|
@ -82,6 +89,13 @@ class VmTarget extends Target {
|
|||
doSuperResolution: false /* resolution is done in Dart VM */);
|
||||
logger?.call("Transformed mixin applications");
|
||||
|
||||
transformFfi.ReplacedMembers replacedFields =
|
||||
transformFfiDefinitions.transformLibraries(
|
||||
coreTypes, hierarchy, libraries, diagnosticReporter);
|
||||
transformFfiUseSites.transformLibraries(
|
||||
coreTypes, hierarchy, libraries, diagnosticReporter, replacedFields);
|
||||
logger?.call("Transformed ffi annotations");
|
||||
|
||||
// TODO(kmillikin): Make this run on a per-method basis.
|
||||
transformAsync.transformLibraries(coreTypes, libraries);
|
||||
logger?.call("Transformed async methods");
|
||||
|
|
|
@ -1004,6 +1004,50 @@ shared_library("entrypoints_verification_test_extension") {
|
|||
}
|
||||
}
|
||||
|
||||
shared_library("ffi_test_dynamic_library") {
|
||||
deps = [
|
||||
":dart",
|
||||
]
|
||||
sources = [
|
||||
"ffi_test_dynamic_library.cc",
|
||||
]
|
||||
include_dirs = [ ".." ]
|
||||
defines = [
|
||||
# The only effect of DART_SHARED_LIB is to export the Dart API.
|
||||
"DART_SHARED_LIB",
|
||||
]
|
||||
if (is_linux || is_android) {
|
||||
cflags = [ "-fPIC" ]
|
||||
}
|
||||
if (is_win) {
|
||||
libs = [ "dart.lib" ]
|
||||
abs_root_out_dir = rebase_path(root_out_dir)
|
||||
ldflags = [ "/LIBPATH:$abs_root_out_dir" ]
|
||||
}
|
||||
}
|
||||
|
||||
shared_library("ffi_test_functions") {
|
||||
deps = [
|
||||
":dart",
|
||||
]
|
||||
sources = [
|
||||
"ffi_test_functions.cc",
|
||||
]
|
||||
include_dirs = [ ".." ]
|
||||
defines = [
|
||||
# The only effect of DART_SHARED_LIB is to export the Dart API.
|
||||
"DART_SHARED_LIB",
|
||||
]
|
||||
if (is_linux || is_android) {
|
||||
cflags = [ "-fPIC" ]
|
||||
}
|
||||
if (is_win) {
|
||||
libs = [ "dart.lib" ]
|
||||
abs_root_out_dir = rebase_path(root_out_dir)
|
||||
ldflags = [ "/LIBPATH:$abs_root_out_dir" ]
|
||||
}
|
||||
}
|
||||
|
||||
shared_library("sample_extension") {
|
||||
deps = [
|
||||
":dart",
|
||||
|
|
13
runtime/bin/ffi_test_dynamic_library.cc
Normal file
13
runtime/bin/ffi_test_dynamic_library.cc
Normal file
|
@ -0,0 +1,13 @@
|
|||
// 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.
|
||||
|
||||
#include "include/dart_api.h"
|
||||
|
||||
DART_EXPORT int return42() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
DART_EXPORT double timesFour(double d) {
|
||||
return d * 4.0;
|
||||
}
|
368
runtime/bin/ffi_test_functions.cc
Normal file
368
runtime/bin/ffi_test_functions.cc
Normal file
|
@ -0,0 +1,368 @@
|
|||
// 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.
|
||||
|
||||
// This file contains test functions for the dart:ffi test cases.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "include/dart_api.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// Sums two ints and adds 42.
|
||||
// Simple function to test trampolines.
|
||||
// Also used for testing argument exception on passing null instead of a Dart
|
||||
// int.
|
||||
DART_EXPORT int32_t SumPlus42(int32_t a, int32_t b) {
|
||||
std::cout << "SumPlus42(" << a << ", " << b << ")\n";
|
||||
int32_t retval = 42 + a + b;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Performs some computation on various sized signed ints.
|
||||
// Used for testing value ranges for signed ints.
|
||||
DART_EXPORT int64_t IntComputation(int8_t a, int16_t b, int32_t c, int64_t d) {
|
||||
std::cout << "IntComputation(" << static_cast<int>(a) << ", " << b << ", "
|
||||
<< c << ", " << d << ")\n";
|
||||
int64_t retval = d - c + b - a;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Performs some computation on various sized unsigned ints.
|
||||
// Used for testing value ranges for unsigned ints.
|
||||
DART_EXPORT int64_t UintComputation(uint8_t a,
|
||||
uint16_t b,
|
||||
uint32_t c,
|
||||
uint64_t d) {
|
||||
std::cout << "UintComputation(" << static_cast<int>(a) << ", " << b << ", "
|
||||
<< c << ", " << d << ")\n";
|
||||
uint64_t retval = d - c + b - a;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Multiplies pointer sized int by three.
|
||||
// Used for testing pointer sized parameter and return value.
|
||||
DART_EXPORT intptr_t Times3(intptr_t a) {
|
||||
std::cout << "Times3(" << a << ")\n";
|
||||
intptr_t retval = a * 3;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Multiples a double by 1.337.
|
||||
// Used for testing double parameter and return value.
|
||||
// Also used for testing argument exception on passing null instead of a Dart
|
||||
// double.
|
||||
DART_EXPORT double Times1_337Double(double a) {
|
||||
std::cout << "Times1_337Double(" << a << ")\n";
|
||||
double retval = a * 1.337;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Multiples a float by 1.337.
|
||||
// Used for testing float parameter and return value.
|
||||
DART_EXPORT float Times1_337Float(float a) {
|
||||
std::cout << "Times1_337Float(" << a << ")\n";
|
||||
float retval = a * 1.337f;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Sums many ints.
|
||||
// Used for testing calling conventions. With so many doubles we are using all
|
||||
// normal parameter registers and some stack slots.
|
||||
DART_EXPORT intptr_t SumManyInts(intptr_t a,
|
||||
intptr_t b,
|
||||
intptr_t c,
|
||||
intptr_t d,
|
||||
intptr_t e,
|
||||
intptr_t f,
|
||||
intptr_t g,
|
||||
intptr_t h,
|
||||
intptr_t i,
|
||||
intptr_t j) {
|
||||
std::cout << "SumManyInts(" << a << ", " << b << ", " << c << ", " << d
|
||||
<< ", " << e << ", " << f << ", " << g << ", " << h << ", " << i
|
||||
<< ", " << j << ")\n";
|
||||
intptr_t retval = a + b + c + d + e + f + g + h + i + j;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Sums many doubles.
|
||||
// Used for testing calling conventions. With so many doubles we are using all
|
||||
// xmm parameter registers and some stack slots.
|
||||
DART_EXPORT double SumManyDoubles(double a,
|
||||
double b,
|
||||
double c,
|
||||
double d,
|
||||
double e,
|
||||
double f,
|
||||
double g,
|
||||
double h,
|
||||
double i,
|
||||
double j) {
|
||||
std::cout << "SumManyDoubles(" << a << ", " << b << ", " << c << ", " << d
|
||||
<< ", " << e << ", " << f << ", " << g << ", " << h << ", " << i
|
||||
<< ", " << j << ")\n";
|
||||
double retval = a + b + c + d + e + f + g + h + i + j;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Sums many numbers.
|
||||
// Used for testing calling conventions. With so many parameters we are using
|
||||
// both registers and stack slots.
|
||||
DART_EXPORT double SumManyNumbers(int a,
|
||||
float b,
|
||||
int c,
|
||||
double d,
|
||||
int e,
|
||||
float f,
|
||||
int g,
|
||||
double h,
|
||||
int i,
|
||||
float j,
|
||||
int k,
|
||||
double l,
|
||||
int m,
|
||||
float n,
|
||||
int o,
|
||||
double p,
|
||||
int q,
|
||||
float r,
|
||||
int s,
|
||||
double t) {
|
||||
std::cout << "SumManyNumbers(" << a << ", " << b << ", " << c << ", " << d
|
||||
<< ", " << e << ", " << f << ", " << g << ", " << h << ", " << i
|
||||
<< ", " << j << ", " << k << ", " << l << ", " << m << ", " << n
|
||||
<< ", " << o << ", " << p << ", " << q << ", " << r << ", " << s
|
||||
<< ", " << t << ")\n";
|
||||
double retval = a + b + c + d + e + f + g + h + i + j + k + l + m + n + o +
|
||||
p + q + r + s + t;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Assigns 1337 to the second element and returns the address of that element.
|
||||
// Used for testing Pointer parameters and return values.
|
||||
DART_EXPORT int64_t* Assign1337Index1(int64_t* a) {
|
||||
std::cout << "Assign1337Index1(" << a << ")\n";
|
||||
std::cout << "val[0] = " << a[0] << "\n";
|
||||
std::cout << "val[1] = " << a[1] << "\n";
|
||||
a[1] = 1337;
|
||||
std::cout << "val[1] = " << a[1] << "\n";
|
||||
int64_t* retval = a + 1;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct Coord {
|
||||
double x;
|
||||
double y;
|
||||
Coord* next;
|
||||
};
|
||||
|
||||
// Transposes Coordinate by (10, 10) and returns next Coordinate.
|
||||
// Used for testing struct pointer parameter, struct pointer return value,
|
||||
// struct field access, and struct pointer field dereference.
|
||||
DART_EXPORT Coord* TransposeCoordinate(Coord* coord) {
|
||||
std::cout << "TransposeCoordinate(" << coord << " {" << coord->x << ", "
|
||||
<< coord->y << ", " << coord->next << "})\n";
|
||||
coord->x = coord->x + 10.0;
|
||||
coord->y = coord->y + 10.0;
|
||||
std::cout << "returning " << coord->next << "\n";
|
||||
return coord->next;
|
||||
}
|
||||
|
||||
// Takes a Coordinate array and returns a Coordinate pointer to the next
|
||||
// element.
|
||||
// Used for testing struct arrays.
|
||||
DART_EXPORT Coord* CoordinateElemAt1(Coord* coord) {
|
||||
std::cout << "CoordinateElemAt1(" << coord << ")\n";
|
||||
std::cout << "sizeof(Coord): " << sizeof(Coord) << "\n";
|
||||
std::cout << "coord[0] = {" << coord[0].x << ", " << coord[0].y << ", "
|
||||
<< coord[0].next << "}\n";
|
||||
std::cout << "coord[1] = {" << coord[1].x << ", " << coord[1].y << ", "
|
||||
<< coord[1].next << "}\n";
|
||||
Coord* retval = coord + 1;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
typedef Coord* (*CoordUnOp)(Coord* coord);
|
||||
|
||||
// Takes a Coordinate Function(Coordinate) and applies it three times to a
|
||||
// Coordinate.
|
||||
// Used for testing function pointers with structs.
|
||||
DART_EXPORT Coord* CoordinateUnOpTrice(CoordUnOp unop, Coord* coord) {
|
||||
std::cout << "CoordinateUnOpTrice(" << unop << ", " << coord << ")\n";
|
||||
Coord* retval = unop(unop(unop(coord)));
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
typedef intptr_t (*IntptrBinOp)(intptr_t a, intptr_t b);
|
||||
|
||||
// Returns a closure.
|
||||
// Note this closure is not properly marked as DART_EXPORT or extern "C".
|
||||
// Used for testing passing a pointer to a closure to Dart.
|
||||
// TODO(dacoharkes): is this a supported use case?
|
||||
DART_EXPORT IntptrBinOp IntptrAdditionClosure() {
|
||||
std::cout << "IntptrAdditionClosure()\n";
|
||||
IntptrBinOp retval = [](intptr_t a, intptr_t b) { return a + b; };
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Applies an intptr binop function to 42 and 74.
|
||||
// Used for testing passing a function pointer to C.
|
||||
DART_EXPORT intptr_t ApplyTo42And74(IntptrBinOp binop) {
|
||||
std::cout << "ApplyTo42And74()\n";
|
||||
intptr_t retval = binop(42, 74);
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Returns next element in the array, unless a null pointer is passed.
|
||||
// When a null pointer is passed, a null pointer is returned.
|
||||
// Used for testing null pointers.
|
||||
DART_EXPORT int64_t* NullableInt64ElemAt1(int64_t* a) {
|
||||
std::cout << "NullableInt64ElemAt1(" << a << ")\n";
|
||||
int64_t* retval;
|
||||
if (a) {
|
||||
std::cout << "not null pointer, address: " << a << "\n";
|
||||
retval = a + 1;
|
||||
} else {
|
||||
std::cout << "null pointer, address: " << a << "\n";
|
||||
retval = nullptr;
|
||||
}
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct VeryLargeStruct {
|
||||
int8_t a;
|
||||
int16_t b;
|
||||
int32_t c;
|
||||
int64_t d;
|
||||
uint8_t e;
|
||||
uint16_t f;
|
||||
uint32_t g;
|
||||
uint64_t h;
|
||||
intptr_t i;
|
||||
float j;
|
||||
double k;
|
||||
VeryLargeStruct* parent;
|
||||
intptr_t numChildren;
|
||||
VeryLargeStruct* children;
|
||||
int8_t smallLastField;
|
||||
};
|
||||
|
||||
// Sums the fields of a very large struct, including the first field (a) from
|
||||
// the parent and children.
|
||||
// Used for testing alignment and padding in structs.
|
||||
DART_EXPORT int64_t SumVeryLargeStruct(VeryLargeStruct* vls) {
|
||||
std::cout << "SumVeryLargeStruct(" << vls << ")\n";
|
||||
std::cout << "offsetof(a): " << offsetof(VeryLargeStruct, a) << "\n";
|
||||
std::cout << "offsetof(b): " << offsetof(VeryLargeStruct, b) << "\n";
|
||||
std::cout << "offsetof(c): " << offsetof(VeryLargeStruct, c) << "\n";
|
||||
std::cout << "offsetof(d): " << offsetof(VeryLargeStruct, d) << "\n";
|
||||
std::cout << "offsetof(e): " << offsetof(VeryLargeStruct, e) << "\n";
|
||||
std::cout << "offsetof(f): " << offsetof(VeryLargeStruct, f) << "\n";
|
||||
std::cout << "offsetof(g): " << offsetof(VeryLargeStruct, g) << "\n";
|
||||
std::cout << "offsetof(h): " << offsetof(VeryLargeStruct, h) << "\n";
|
||||
std::cout << "offsetof(i): " << offsetof(VeryLargeStruct, i) << "\n";
|
||||
std::cout << "offsetof(j): " << offsetof(VeryLargeStruct, j) << "\n";
|
||||
std::cout << "offsetof(k): " << offsetof(VeryLargeStruct, k) << "\n";
|
||||
std::cout << "offsetof(parent): " << offsetof(VeryLargeStruct, parent)
|
||||
<< "\n";
|
||||
std::cout << "offsetof(numChildren): "
|
||||
<< offsetof(VeryLargeStruct, numChildren) << "\n";
|
||||
std::cout << "offsetof(children): " << offsetof(VeryLargeStruct, children)
|
||||
<< "\n";
|
||||
std::cout << "offsetof(smallLastField): "
|
||||
<< offsetof(VeryLargeStruct, smallLastField) << "\n";
|
||||
std::cout << "sizeof(VeryLargeStruct): " << sizeof(VeryLargeStruct) << "\n";
|
||||
|
||||
std::cout << "vls->a: " << static_cast<int>(vls->a) << "\n";
|
||||
std::cout << "vls->b: " << vls->b << "\n";
|
||||
std::cout << "vls->c: " << vls->c << "\n";
|
||||
std::cout << "vls->d: " << vls->d << "\n";
|
||||
std::cout << "vls->e: " << static_cast<int>(vls->e) << "\n";
|
||||
std::cout << "vls->f: " << vls->f << "\n";
|
||||
std::cout << "vls->g: " << vls->g << "\n";
|
||||
std::cout << "vls->h: " << vls->h << "\n";
|
||||
std::cout << "vls->i: " << vls->i << "\n";
|
||||
std::cout << "vls->j: " << vls->j << "\n";
|
||||
std::cout << "vls->k: " << vls->k << "\n";
|
||||
std::cout << "vls->parent: " << vls->parent << "\n";
|
||||
std::cout << "vls->numChildren: " << vls->numChildren << "\n";
|
||||
std::cout << "vls->children: " << vls->children << "\n";
|
||||
std::cout << "vls->smallLastField: " << static_cast<int>(vls->smallLastField)
|
||||
<< "\n";
|
||||
|
||||
int64_t retval = 0;
|
||||
retval += 0x0L + vls->a;
|
||||
retval += vls->b;
|
||||
retval += vls->c;
|
||||
retval += vls->d;
|
||||
retval += vls->e;
|
||||
retval += vls->f;
|
||||
retval += vls->g;
|
||||
retval += vls->h;
|
||||
retval += vls->i;
|
||||
retval += vls->j;
|
||||
retval += vls->k;
|
||||
retval += vls->smallLastField;
|
||||
std::cout << retval << "\n";
|
||||
if (vls->parent) {
|
||||
std::cout << "has parent\n";
|
||||
retval += vls->parent->a;
|
||||
}
|
||||
std::cout << "has " << vls->numChildren << " children\n";
|
||||
for (int i = 0; i < vls->numChildren; i++) {
|
||||
retval += vls->children[i].a;
|
||||
}
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Sums numbers of various sizes.
|
||||
// Used for testing truncation and sign extension of non 64 bit parameters.
|
||||
DART_EXPORT int64_t SumSmallNumbers(int8_t a,
|
||||
int16_t b,
|
||||
int32_t c,
|
||||
uint8_t d,
|
||||
uint16_t e,
|
||||
uint32_t f) {
|
||||
std::cout << "SumSmallNumbers(" << static_cast<int>(a) << ", " << b << ", "
|
||||
<< c << ", " << static_cast<int>(d) << ", " << e << ", " << f
|
||||
<< ")\n";
|
||||
int64_t retval = 0;
|
||||
retval += a;
|
||||
retval += b;
|
||||
retval += c;
|
||||
retval += d;
|
||||
retval += e;
|
||||
retval += f;
|
||||
std::cout << "returning " << retval << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Checks whether the float is between 1336.0f and 1338.0f.
|
||||
// Used for testing rounding of Dart Doubles to floats in Pointer.store().
|
||||
DART_EXPORT uint8_t IsRoughly1337(float* a) {
|
||||
std::cout << "IsRoughly1337(" << a[0] << ")\n";
|
||||
uint8_t retval = (1336.0f < a[0] && a[0] < 1338.0f) ? 1 : 0;
|
||||
std::cout << "returning " << static_cast<int>(retval) << "\n";
|
||||
return retval;
|
||||
}
|
||||
|
||||
} // namespace dart
|
680
runtime/lib/ffi.cc
Normal file
680
runtime/lib/ffi.cc
Normal file
|
@ -0,0 +1,680 @@
|
|||
// 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.
|
||||
|
||||
#include "include/dart_api.h"
|
||||
#include "vm/bootstrap_natives.h"
|
||||
#include "vm/class_finalizer.h"
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/log.h"
|
||||
#include "vm/native_arguments.h"
|
||||
#include "vm/native_entry.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/symbols.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// The following functions are runtime checks on type arguments.
|
||||
// Some checks are also performed in kernel transformation, these are asserts.
|
||||
// Some checks are only performed at runtime to allow for generic code, these
|
||||
// throw ArgumentExceptions.
|
||||
|
||||
static void ThrowTypeArgumentError(const AbstractType& type_arg,
|
||||
const char* expected) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"Type argument (%s) should be a %s",
|
||||
String::Handle(type_arg.UserVisibleName()).ToCString(), expected));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
|
||||
static bool IsPointerType(const AbstractType& type) {
|
||||
// Do a fast check for predefined types.
|
||||
classid_t type_cid = type.type_class_id();
|
||||
if (RawObject::IsFfiPointerClassId(type_cid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do a slow check for subtyping.
|
||||
const Class& pointer_class =
|
||||
Class::Handle(Isolate::Current()->object_store()->ffi_pointer_class());
|
||||
AbstractType& pointer_type =
|
||||
AbstractType::Handle(pointer_class.DeclarationType());
|
||||
pointer_type ^= pointer_type.InstantiateFrom(Object::null_type_arguments(),
|
||||
Object::null_type_arguments(),
|
||||
kNoneFree, NULL, Heap::kNew);
|
||||
ASSERT(pointer_type.IsInstantiated());
|
||||
ASSERT(type.IsInstantiated());
|
||||
return type.IsSubtypeOf(pointer_type, Heap::kNew);
|
||||
}
|
||||
|
||||
static bool IsConcreteNativeType(const AbstractType& type) {
|
||||
// Do a fast check for predefined types.
|
||||
classid_t type_cid = type.type_class_id();
|
||||
if (RawObject::IsFfiNativeTypeTypeClassId(type_cid)) {
|
||||
return false;
|
||||
}
|
||||
if (RawObject::IsFfiTypeClassId(type_cid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do a slow check for subtyping.
|
||||
const Class& native_type_class = Class::Handle(
|
||||
Isolate::Current()->object_store()->ffi_native_type_class());
|
||||
AbstractType& native_type_type =
|
||||
AbstractType::Handle(native_type_class.DeclarationType());
|
||||
return type.IsSubtypeOf(native_type_type, Heap::kNew);
|
||||
}
|
||||
|
||||
static void CheckIsConcreteNativeType(const AbstractType& type) {
|
||||
if (!IsConcreteNativeType(type)) {
|
||||
ThrowTypeArgumentError(type, "concrete sub type of NativeType");
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsNativeFunction(const AbstractType& type_arg) {
|
||||
classid_t type_cid = type_arg.type_class_id();
|
||||
return RawObject::IsFfiTypeNativeFunctionClassId(type_cid);
|
||||
}
|
||||
|
||||
static void CheckSized(const AbstractType& type_arg) {
|
||||
classid_t type_cid = type_arg.type_class_id();
|
||||
if (RawObject::IsFfiTypeVoidClassId(type_cid) ||
|
||||
RawObject::IsFfiTypeNativeFunctionClassId(type_cid)) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"%s does not have a predefined size (@unsized). "
|
||||
"Unsized NativeTypes do not support [sizeOf] because their size "
|
||||
"is unknown. "
|
||||
"Consequently, [allocate], [Pointer.load], [Pointer.store], and "
|
||||
"[Pointer.elementAt] are not available.",
|
||||
String::Handle(type_arg.UserVisibleName()).ToCString()));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that a dart type correspond to a [NativeType].
|
||||
// Because this is checked already in a kernel transformation, it does not throw
|
||||
// an ArgumentException but a boolean which should be asserted.
|
||||
//
|
||||
// [Int8] -> [int]
|
||||
// [Int16] -> [int]
|
||||
// [Int32] -> [int]
|
||||
// [Int64] -> [int]
|
||||
// [Uint8] -> [int]
|
||||
// [Uint16] -> [int]
|
||||
// [Uint32] -> [int]
|
||||
// [Uint64] -> [int]
|
||||
// [IntPtr] -> [int]
|
||||
// [Double] -> [double]
|
||||
// [Float] -> [double]
|
||||
// [Pointer]<T> -> [Pointer]<T>
|
||||
// T extends [Pointer] -> T
|
||||
// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
|
||||
// where DartRepresentationOf(Tn) -> Sn
|
||||
static bool DartAndCTypeCorrespond(const AbstractType& native_type,
|
||||
const AbstractType& dart_type) {
|
||||
classid_t native_type_cid = native_type.type_class_id();
|
||||
if (RawObject::IsFfiTypeIntClassId(native_type_cid)) {
|
||||
return dart_type.IsSubtypeOf(AbstractType::Handle(Type::IntType()),
|
||||
Heap::kNew);
|
||||
}
|
||||
if (RawObject::IsFfiTypeDoubleClassId(native_type_cid)) {
|
||||
return dart_type.IsSubtypeOf(AbstractType::Handle(Type::Double()),
|
||||
Heap::kNew);
|
||||
}
|
||||
if (RawObject::IsFfiPointerClassId(native_type_cid)) {
|
||||
return native_type.Equals(dart_type) || dart_type.IsNullType();
|
||||
}
|
||||
if (RawObject::IsFfiTypeNativeFunctionClassId(native_type_cid)) {
|
||||
if (!dart_type.IsFunctionType()) {
|
||||
return false;
|
||||
}
|
||||
TypeArguments& nativefunction_type_args =
|
||||
TypeArguments::Handle(native_type.arguments());
|
||||
AbstractType& nativefunction_type_arg =
|
||||
AbstractType::Handle(nativefunction_type_args.TypeAt(0));
|
||||
if (!nativefunction_type_arg.IsFunctionType()) {
|
||||
return false;
|
||||
}
|
||||
Function& dart_function = Function::Handle(((Type&)dart_type).signature());
|
||||
if (dart_function.NumTypeParameters() != 0 ||
|
||||
dart_function.HasOptionalPositionalParameters() ||
|
||||
dart_function.HasOptionalNamedParameters()) {
|
||||
return false;
|
||||
}
|
||||
Function& nativefunction_function =
|
||||
Function::Handle(((Type&)nativefunction_type_arg).signature());
|
||||
if (nativefunction_function.NumTypeParameters() != 0 ||
|
||||
nativefunction_function.HasOptionalPositionalParameters() ||
|
||||
nativefunction_function.HasOptionalNamedParameters()) {
|
||||
return false;
|
||||
}
|
||||
if (!(dart_function.NumParameters() ==
|
||||
nativefunction_function.NumParameters())) {
|
||||
return false;
|
||||
}
|
||||
if (!DartAndCTypeCorrespond(
|
||||
AbstractType::Handle(nativefunction_function.result_type()),
|
||||
AbstractType::Handle(dart_function.result_type()))) {
|
||||
return false;
|
||||
}
|
||||
for (intptr_t i = 0; i < dart_function.NumParameters(); i++) {
|
||||
if (!DartAndCTypeCorrespond(
|
||||
AbstractType::Handle(nativefunction_function.ParameterTypeAt(i)),
|
||||
AbstractType::Handle(dart_function.ParameterTypeAt(i)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The following functions are runtime checks on arguments.
|
||||
|
||||
// Note that expected_from and expected_to are inclusive.
|
||||
static void CheckRange(const Integer& argument_value,
|
||||
intptr_t expected_from,
|
||||
intptr_t expected_to,
|
||||
const char* argument_name) {
|
||||
int64_t value = argument_value.AsInt64Value();
|
||||
if (value < expected_from || expected_to < value) {
|
||||
Exceptions::ThrowRangeError(argument_name, argument_value, expected_from,
|
||||
expected_to);
|
||||
}
|
||||
}
|
||||
|
||||
static const Pointer& AsPointer(const Instance& instance) {
|
||||
if (!instance.IsPointer()) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"Expected a Pointer object but found %s", instance.ToCString()));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
return Pointer::Cast(instance);
|
||||
}
|
||||
|
||||
static const Integer& AsInteger(const Instance& instance) {
|
||||
if (!instance.IsInteger()) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"Expected an int but found %s", instance.ToCString()));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
return Integer::Cast(instance);
|
||||
}
|
||||
|
||||
static const Double& AsDouble(const Instance& instance) {
|
||||
if (!instance.IsDouble()) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"Expected a double but found %s", instance.ToCString()));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
return Double::Cast(instance);
|
||||
}
|
||||
|
||||
// Native data types sizes in bytes
|
||||
|
||||
static const size_t kSizeUnknown = 0;
|
||||
|
||||
static const intptr_t kNumElementSizes = kFfiVoidCid - kFfiPointerCid + 1;
|
||||
|
||||
static const size_t element_size_table[kNumElementSizes] = {
|
||||
sizeof(intptr_t), // kFfiPointerCid
|
||||
kSizeUnknown, // kFfiNativeFunctionCid
|
||||
1, // kFfiInt8Cid
|
||||
2, // kFfiInt16Cid
|
||||
4, // kFfiInt32Cid
|
||||
8, // kFfiInt64Cid
|
||||
1, // kFfiUint8Cid
|
||||
2, // kFfiUint16Cid
|
||||
4, // kFfiUint32Cid
|
||||
8, // kFfiUint64Cid
|
||||
sizeof(intptr_t), // kFfiIntPtrCid
|
||||
4, // kFfiFloatCid
|
||||
8, // kFfiDoubleCid
|
||||
kSizeUnknown, // kFfiVoidCid
|
||||
};
|
||||
|
||||
static size_t ElementSizeInBytes(intptr_t class_id) {
|
||||
ASSERT(RawObject::IsFfiTypeClassId(class_id));
|
||||
ASSERT(class_id != kFfiNativeFunctionCid);
|
||||
ASSERT(class_id != kFfiVoidCid);
|
||||
intptr_t index = class_id - kFfiPointerCid;
|
||||
return element_size_table[index];
|
||||
}
|
||||
|
||||
// The remainder of this file implements the dart:ffi native methods.
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_allocate, 1, 1) {
|
||||
// TODO(dacoharkes): When we have a way of determining the size of structs in
|
||||
// the VM, change the signature so we can allocate structs, subtype of
|
||||
// Pointer. https://github.com/dart-lang/sdk/issues/35782
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
|
||||
CheckIsConcreteNativeType(type_arg);
|
||||
CheckSized(type_arg);
|
||||
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, argCount, arguments->NativeArgAt(0));
|
||||
int64_t count = argCount.AsInt64Value();
|
||||
classid_t type_cid = type_arg.type_class_id();
|
||||
int64_t max_count = INTPTR_MAX / ElementSizeInBytes(type_cid);
|
||||
CheckRange(argCount, 1, max_count, "count");
|
||||
|
||||
size_t size = ElementSizeInBytes(type_cid) * count;
|
||||
uint8_t* memory = reinterpret_cast<uint8_t*>(malloc(size));
|
||||
if (memory == NULL) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"allocating (%" Pd ") bytes of memory failed", size));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
|
||||
RawPointer* result = Pointer::New(type_arg, memory);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_fromAddress, 1, 1) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
TypeArguments& type_args = TypeArguments::Handle(type_arg.arguments());
|
||||
AbstractType& native_type = AbstractType::Handle(
|
||||
type_args.TypeAtNullSafe(Pointer::kNativeTypeArgPos));
|
||||
CheckIsConcreteNativeType(native_type);
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, arg_ptr, arguments->NativeArgAt(0));
|
||||
|
||||
uint8_t* address = reinterpret_cast<uint8_t*>(arg_ptr.AsInt64Value());
|
||||
// TODO(dacoharkes): should this return NULL if addres is 0?
|
||||
// https://github.com/dart-lang/sdk/issues/35756
|
||||
|
||||
RawPointer* result =
|
||||
Pointer::New(native_type, address, type_arg.type_class_id());
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_elementAt, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, index, arguments->NativeArgAt(1));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
CheckSized(pointer_type_arg);
|
||||
|
||||
classid_t class_id = pointer_type_arg.type_class_id();
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
uint8_t* address_new =
|
||||
address + index.AsInt64Value() * ElementSizeInBytes(class_id);
|
||||
RawPointer* result = Pointer::New(pointer_type_arg, address_new);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_offsetBy, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, offset, arguments->NativeArgAt(1));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
uint8_t* address_new = address + offset.AsInt64Value();
|
||||
RawPointer* result = Pointer::New(pointer_type_arg, address_new);
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_cast, 1, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
TypeArguments& type_args = TypeArguments::Handle(type_arg.arguments());
|
||||
AbstractType& native_type = AbstractType::Handle(
|
||||
type_args.TypeAtNullSafe(Pointer::kNativeTypeArgPos));
|
||||
CheckIsConcreteNativeType(native_type);
|
||||
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
RawPointer* result =
|
||||
Pointer::New(native_type, address, type_arg.type_class_id());
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_free, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
free(address);
|
||||
pointer.SetCMemoryAddress(0);
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_address, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
intptr_t int_ptr = reinterpret_cast<intptr_t>(address);
|
||||
return Integer::NewFromUint64(int_ptr);
|
||||
}
|
||||
|
||||
static RawInstance* BoxLoadPointer(uint8_t* address,
|
||||
const AbstractType& instance_type_arg,
|
||||
intptr_t type_cid) {
|
||||
// TODO(dacoharkes): should this return NULL if addres is 0?
|
||||
// https://github.com/dart-lang/sdk/issues/35756
|
||||
if (address == 0) { // 0 is the c++ null pointer
|
||||
return Instance::null();
|
||||
}
|
||||
AbstractType& type_arg =
|
||||
AbstractType::Handle(TypeArguments::Handle(instance_type_arg.arguments())
|
||||
.TypeAt(Pointer::kNativeTypeArgPos));
|
||||
return Pointer::New(type_arg, address, type_cid);
|
||||
}
|
||||
|
||||
static RawInstance* LoadValue(uint8_t* address,
|
||||
const AbstractType& instance_type_arg) {
|
||||
classid_t type_cid = instance_type_arg.type_class_id();
|
||||
switch (type_cid) {
|
||||
case kFfiInt8Cid:
|
||||
return Integer::New(*reinterpret_cast<int8_t*>(address));
|
||||
case kFfiInt16Cid:
|
||||
return Integer::New(*reinterpret_cast<int16_t*>(address));
|
||||
case kFfiInt32Cid:
|
||||
return Integer::New(*reinterpret_cast<int32_t*>(address));
|
||||
case kFfiInt64Cid:
|
||||
return Integer::New(*reinterpret_cast<int64_t*>(address));
|
||||
case kFfiUint8Cid:
|
||||
return Integer::NewFromUint64(*reinterpret_cast<uint8_t*>(address));
|
||||
case kFfiUint16Cid:
|
||||
return Integer::NewFromUint64(*reinterpret_cast<uint16_t*>(address));
|
||||
case kFfiUint32Cid:
|
||||
return Integer::NewFromUint64(*reinterpret_cast<uint32_t*>(address));
|
||||
case kFfiUint64Cid:
|
||||
return Integer::NewFromUint64(*reinterpret_cast<uint64_t*>(address));
|
||||
case kFfiIntPtrCid:
|
||||
return Integer::New(*reinterpret_cast<intptr_t*>(address));
|
||||
case kFfiFloatCid:
|
||||
return Double::New(*reinterpret_cast<float_t*>(address));
|
||||
case kFfiDoubleCid:
|
||||
return Double::New(*reinterpret_cast<double_t*>(address));
|
||||
case kFfiPointerCid:
|
||||
default:
|
||||
ASSERT(IsPointerType(instance_type_arg));
|
||||
return BoxLoadPointer(*reinterpret_cast<uint8_t**>(address),
|
||||
instance_type_arg, type_cid);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_load, 1, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
CheckSized(pointer_type_arg);
|
||||
ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg));
|
||||
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
return LoadValue(address, pointer_type_arg);
|
||||
}
|
||||
|
||||
static void StoreValue(const Pointer& pointer,
|
||||
classid_t type_cid,
|
||||
const Instance& new_value) {
|
||||
uint8_t* address = pointer.GetCMemoryAddress();
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
switch (type_cid) {
|
||||
case kFfiInt8Cid:
|
||||
*reinterpret_cast<int8_t*>(address) = AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiInt16Cid:
|
||||
*reinterpret_cast<int16_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiInt32Cid:
|
||||
*reinterpret_cast<int32_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiInt64Cid:
|
||||
*reinterpret_cast<int64_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiUint8Cid:
|
||||
*reinterpret_cast<uint8_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiUint16Cid:
|
||||
*reinterpret_cast<uint16_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiUint32Cid:
|
||||
*reinterpret_cast<uint32_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiUint64Cid:
|
||||
*reinterpret_cast<uint64_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiIntPtrCid:
|
||||
*reinterpret_cast<intptr_t*>(address) =
|
||||
AsInteger(new_value).AsInt64Value();
|
||||
break;
|
||||
case kFfiFloatCid:
|
||||
*reinterpret_cast<float*>(address) = AsDouble(new_value).value();
|
||||
break;
|
||||
case kFfiDoubleCid:
|
||||
*reinterpret_cast<double*>(address) = AsDouble(new_value).value();
|
||||
break;
|
||||
case kFfiPointerCid:
|
||||
default: {
|
||||
ASSERT(IsPointerType(pointer_type_arg));
|
||||
uint8_t* new_value_unwrapped = nullptr;
|
||||
if (!new_value.IsNull()) {
|
||||
ASSERT(new_value.IsPointer());
|
||||
new_value_unwrapped = AsPointer(new_value).GetCMemoryAddress();
|
||||
// TODO(dacoharkes): should this return NULL if addres is 0?
|
||||
// https://github.com/dart-lang/sdk/issues/35756
|
||||
}
|
||||
*reinterpret_cast<uint8_t**>(address) = new_value_unwrapped;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_store, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
GET_NATIVE_ARGUMENT(Instance, new_value, arguments->NativeArgAt(1));
|
||||
AbstractType& arg_type = AbstractType::Handle(new_value.GetType(Heap::kNew));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
CheckSized(pointer_type_arg);
|
||||
ASSERT(DartAndCTypeCorrespond(pointer_type_arg, arg_type));
|
||||
|
||||
classid_t type_cid = pointer_type_arg.type_class_id();
|
||||
StoreValue(pointer, type_cid, new_value);
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
CheckIsConcreteNativeType(type_arg);
|
||||
CheckSized(type_arg);
|
||||
|
||||
classid_t type_cid = type_arg.type_class_id();
|
||||
return Smi::New(ElementSizeInBytes(type_cid));
|
||||
}
|
||||
|
||||
// Generates assembly to trampoline from Dart into C++.
|
||||
//
|
||||
// Attaches assembly code to the function with the folling features:
|
||||
// - unboxes arguments
|
||||
// - puts the arguments on the c stack
|
||||
// - invokes the c function
|
||||
// - reads the the result
|
||||
// - boxes the result and returns it.
|
||||
//
|
||||
// It inspects the signature to know what to box/unbox
|
||||
// Parameter `function` has the Dart types in its signature
|
||||
// Parameter `c_signature` has the C++ types in its signature
|
||||
static RawCode* TrampolineCode(const Function& function,
|
||||
const Function& c_signature) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
// Currently we generate the trampoline when calling asFunction(), this means
|
||||
// the ffi cannot be used in AOT.
|
||||
// In order make it work in AOT we need to:
|
||||
// - collect all asFunction signatures ahead of time
|
||||
// - generate trampolines for those
|
||||
// - store these in the object store
|
||||
// - and read these from the object store when calling asFunction()
|
||||
// https://github.com/dart-lang/sdk/issues/35765
|
||||
UNREACHABLE();
|
||||
#elif !defined(TARGET_ARCH_X64)
|
||||
// https://github.com/dart-lang/sdk/issues/35774
|
||||
UNREACHABLE();
|
||||
#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
// https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android
|
||||
// https://github.com/dart-lang/sdk/issues/35771 Windows
|
||||
// https://github.com/dart-lang/sdk/issues/35772 Arm64
|
||||
// https://github.com/dart-lang/sdk/issues/35773 DBC
|
||||
UNREACHABLE();
|
||||
#else
|
||||
extern void GenerateFfiTrampoline(Assembler * assembler,
|
||||
const Function& signature);
|
||||
ObjectPoolBuilder object_pool_builder;
|
||||
Assembler assembler(&object_pool_builder);
|
||||
GenerateFfiTrampoline(&assembler, c_signature);
|
||||
const Code& code = Code::Handle(Code::FinalizeCode(
|
||||
function, nullptr, &assembler, Code::PoolAttachment::kAttachPool));
|
||||
code.set_exception_handlers(
|
||||
ExceptionHandlers::Handle(ExceptionHandlers::New(0)));
|
||||
return code.raw();
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): Cache the trampolines.
|
||||
// We can possibly address simultaniously with 'precaching' in AOT.
|
||||
static RawFunction* TrampolineFunction(const Function& dart_signature,
|
||||
const Function& c_signature) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const String& name =
|
||||
String::ZoneHandle(Symbols::New(Thread::Current(), "FfiTrampoline"));
|
||||
const Library& lib = Library::Handle(Library::FfiLibrary());
|
||||
const Class& owner_class = Class::Handle(lib.toplevel_class());
|
||||
Function& function = Function::ZoneHandle(
|
||||
zone, Function::New(name, RawFunction::kFfiTrampoline,
|
||||
true, // is_static
|
||||
false, // is_const
|
||||
false, // is_abstract
|
||||
false, // is_external
|
||||
true, // is_native
|
||||
owner_class, TokenPosition::kMinSource));
|
||||
|
||||
function.set_num_fixed_parameters(dart_signature.num_fixed_parameters());
|
||||
function.set_result_type(AbstractType::Handle(dart_signature.result_type()));
|
||||
function.set_parameter_types(Array::Handle(dart_signature.parameter_types()));
|
||||
|
||||
const Code& code = Code::Handle(TrampolineCode(function, c_signature));
|
||||
function.AttachCode(code);
|
||||
|
||||
return function.raw();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_asFunction, 1, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||
AbstractType& pointer_type_arg =
|
||||
AbstractType::Handle(pointer.type_argument());
|
||||
ASSERT(IsNativeFunction(pointer_type_arg));
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
ASSERT(DartAndCTypeCorrespond(pointer_type_arg, type_arg));
|
||||
|
||||
Function& dart_signature = Function::Handle(Type::Cast(type_arg).signature());
|
||||
TypeArguments& nativefunction_type_args =
|
||||
TypeArguments::Handle(pointer_type_arg.arguments());
|
||||
AbstractType& nativefunction_type_arg =
|
||||
AbstractType::Handle(nativefunction_type_args.TypeAt(0));
|
||||
Function& c_signature =
|
||||
Function::Handle(Type::Cast(nativefunction_type_arg).signature());
|
||||
Function& function =
|
||||
Function::Handle(TrampolineFunction(dart_signature, c_signature));
|
||||
|
||||
// Set the c function pointer in the context of the closure rather than in
|
||||
// the function so that we can reuse the function for each c function with
|
||||
// the same signature.
|
||||
Context& context = Context::Handle(Context::New(1));
|
||||
context.SetAt(0,
|
||||
Object::Handle(Integer::NewFromUint64(
|
||||
reinterpret_cast<intptr_t>(pointer.GetCMemoryAddress()))));
|
||||
|
||||
RawClosure* raw_closure =
|
||||
Closure::New(Object::null_type_arguments(), Object::null_type_arguments(),
|
||||
function, context, Heap::kOld);
|
||||
|
||||
return raw_closure;
|
||||
}
|
||||
|
||||
// Generates assembly to trampoline from C++ back into Dart.
|
||||
static void* GenerateFfiInverseTrampoline(const Function& signature,
|
||||
void* dart_entry_point) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
UNREACHABLE();
|
||||
#elif !defined(TARGET_ARCH_X64)
|
||||
// https://github.com/dart-lang/sdk/issues/35774
|
||||
UNREACHABLE();
|
||||
#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
// https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android
|
||||
// https://github.com/dart-lang/sdk/issues/35771 Windows
|
||||
// https://github.com/dart-lang/sdk/issues/35772 Arm64
|
||||
// https://github.com/dart-lang/sdk/issues/35773 DBC
|
||||
UNREACHABLE();
|
||||
#else
|
||||
extern void GenerateFfiInverseTrampoline(
|
||||
Assembler * assembler, const Function& signature, void* dart_entry_point);
|
||||
ObjectPoolBuilder object_pool_builder;
|
||||
Assembler assembler(&object_pool_builder);
|
||||
GenerateFfiInverseTrampoline(&assembler, signature, dart_entry_point);
|
||||
const Code& code = Code::Handle(
|
||||
Code::FinalizeCode("inverse trampoline", nullptr, &assembler,
|
||||
Code::PoolAttachment::kAttachPool, false));
|
||||
|
||||
uword entryPoint = code.EntryPoint();
|
||||
|
||||
return reinterpret_cast<void*>(entryPoint);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): Implement this feature.
|
||||
// https://github.com/dart-lang/sdk/issues/35761
|
||||
// For now, it always returns Pointer with address 0.
|
||||
DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 1) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
|
||||
|
||||
Function& c_signature = Function::Handle(((Type&)type_arg).signature());
|
||||
|
||||
Function& func = Function::Handle(closure.function());
|
||||
Code& code = Code::Handle(func.EnsureHasCode());
|
||||
void* entryPoint = reinterpret_cast<void*>(code.EntryPoint());
|
||||
|
||||
THR_Print("Ffi_fromFunction: %s\n", type_arg.ToCString());
|
||||
THR_Print("Ffi_fromFunction: %s\n", c_signature.ToCString());
|
||||
THR_Print("Ffi_fromFunction: %s\n", closure.ToCString());
|
||||
THR_Print("Ffi_fromFunction: %s\n", func.ToCString());
|
||||
THR_Print("Ffi_fromFunction: %s\n", code.ToCString());
|
||||
THR_Print("Ffi_fromFunction: %p\n", entryPoint);
|
||||
THR_Print("Ffi_fromFunction: %" Pd "\n", code.Size());
|
||||
|
||||
void* address = GenerateFfiInverseTrampoline(c_signature, entryPoint);
|
||||
|
||||
TypeArguments& type_args = TypeArguments::Handle(zone);
|
||||
type_args = TypeArguments::New(1);
|
||||
type_args.SetTypeAt(Pointer::kNativeTypeArgPos, type_arg);
|
||||
type_args ^= type_args.Canonicalize();
|
||||
|
||||
Class& native_function_class = Class::Handle(
|
||||
Isolate::Current()->class_table()->At(kFfiNativeFunctionCid));
|
||||
native_function_class.EnsureIsFinalized(Thread::Current());
|
||||
|
||||
Type& native_function_type = Type::Handle(
|
||||
Type::New(native_function_class, type_args, TokenPosition::kNoSource));
|
||||
native_function_type ^=
|
||||
ClassFinalizer::FinalizeType(Class::Handle(), native_function_type);
|
||||
native_function_type ^= native_function_type.Canonicalize();
|
||||
|
||||
address = 0; // https://github.com/dart-lang/sdk/issues/35761
|
||||
|
||||
Pointer& result = Pointer::Handle(
|
||||
Pointer::New(native_function_type, reinterpret_cast<uint8_t*>(address)));
|
||||
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
} // namespace dart
|
114
runtime/lib/ffi_dynamic_library.cc
Normal file
114
runtime/lib/ffi_dynamic_library.cc
Normal file
|
@ -0,0 +1,114 @@
|
|||
// 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.
|
||||
|
||||
#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
// TODO(dacoharkes): implement dynamic libraries for other targets.
|
||||
// see
|
||||
// - runtime/vm/native_symbol.h
|
||||
// - runtime/vm/native_symbol_linux.cc
|
||||
// - runtime/bin/extensions.h (but we cannot import from bin)
|
||||
// - runtime/bin/extensions_linux.cc
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include "include/dart_api.h"
|
||||
#include "vm/bootstrap_natives.h"
|
||||
#include "vm/exceptions.h"
|
||||
#include "vm/native_entry.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
// Concatenates a NULL terminated array of strings.
|
||||
// The returned string is scope allocated.
|
||||
// TODO(dacoharkes): Can we share this with runtime/bin/extensions.cc?
|
||||
const char* Concatenate(const char** strings) {
|
||||
int size = 1; // null termination.
|
||||
for (int i = 0; strings[i] != NULL; i++) {
|
||||
size += strlen(strings[i]);
|
||||
}
|
||||
char* result = reinterpret_cast<char*>(Dart_ScopeAllocate(size));
|
||||
int index = 0;
|
||||
for (int i = 0; strings[i] != NULL; i++) {
|
||||
index += snprintf(result + index, size - index, "%s", strings[i]);
|
||||
}
|
||||
ASSERT(index == size - 1);
|
||||
ASSERT(result[size - 1] == '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): Can we share this with runtime/bin/extensions.cc?
|
||||
const char* LibraryPath(const char* library_name) {
|
||||
const char* library_prefix = "lib";
|
||||
#if defined(TARGET_OS_LINUX)
|
||||
const char* library_extension = "so";
|
||||
#elif defined(TARGET_OS_MACOS)
|
||||
const char* library_extension = "dylib";
|
||||
#else
|
||||
const char* library_extension = "";
|
||||
UNREACHABLE();
|
||||
#endif
|
||||
|
||||
const char* path_components[] = {
|
||||
library_prefix, library_name, ".", library_extension, NULL,
|
||||
};
|
||||
|
||||
return Concatenate(path_components);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, argName, arguments->NativeArgAt(0));
|
||||
|
||||
#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
dlerror(); // Clear any errors.
|
||||
void* handle = dlopen(LibraryPath(argName.ToCString()), RTLD_LAZY);
|
||||
if (handle == nullptr) {
|
||||
char* error = dlerror();
|
||||
const String& msg = String::Handle(
|
||||
String::NewFormatted("Failed to load dynamic library(%s)", error));
|
||||
Exceptions::ThrowArgumentError(msg);
|
||||
}
|
||||
|
||||
return DynamicLibrary::New(handle);
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
|
||||
arguments->NativeArgAt(1));
|
||||
|
||||
#if !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
UNREACHABLE();
|
||||
#else
|
||||
void* handle = dlib.GetHandle();
|
||||
|
||||
dlerror(); // Clear any errors.
|
||||
uint8_t* pointer =
|
||||
reinterpret_cast<uint8_t*>(dlsym(handle, argSymbolName.ToCString()));
|
||||
char* error;
|
||||
if ((error = dlerror()) != NULL) {
|
||||
const String& msg = String::Handle(
|
||||
String::NewFormatted("Failed to lookup symbol (%s)", error));
|
||||
Exceptions::ThrowArgumentError(msg);
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): should this return NULL if addres is 0?
|
||||
// https://github.com/dart-lang/sdk/issues/35756
|
||||
RawPointer* result = Pointer::New(type_arg, pointer);
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
|
||||
|
||||
intptr_t handle = reinterpret_cast<intptr_t>(dlib.GetHandle());
|
||||
return Integer::NewFromUint64(handle);
|
||||
}
|
||||
|
||||
} // namespace dart
|
35
runtime/lib/ffi_dynamic_library_patch.dart
Normal file
35
runtime/lib/ffi_dynamic_library_patch.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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.
|
||||
|
||||
import "dart:_internal" show patch;
|
||||
|
||||
DynamicLibrary _open(String name) native "Ffi_dl_open";
|
||||
|
||||
@patch
|
||||
class DynamicLibrary {
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
factory DynamicLibrary.open(String name) {
|
||||
return _open(name);
|
||||
}
|
||||
|
||||
@patch
|
||||
Pointer<T> lookup<T extends NativeType>(String symbolName)
|
||||
native "Ffi_dl_lookup";
|
||||
|
||||
// TODO(dacoharkes): Expose this to users, or extend Pointer?
|
||||
// https://github.com/dart-lang/sdk/issues/35881
|
||||
int getHandle() native "Ffi_dl_getHandle";
|
||||
|
||||
@patch
|
||||
bool operator ==(other) {
|
||||
if (other == null) return false;
|
||||
return getHandle() == other.getHandle();
|
||||
}
|
||||
|
||||
@patch
|
||||
int get hashCode {
|
||||
return getHandle().hashCode;
|
||||
}
|
||||
}
|
69
runtime/lib/ffi_native_type_patch.dart
Normal file
69
runtime/lib/ffi_native_type_patch.dart
Normal file
|
@ -0,0 +1,69 @@
|
|||
// 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.
|
||||
|
||||
import "dart:_internal" show patch;
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class NativeType {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class _NativeInteger extends NativeType {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class _NativeDouble extends NativeType {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Int8 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Int16 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Int32 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Int64 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Uint8 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Uint16 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Uint32 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Uint64 extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class IntPtr extends _NativeInteger {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Float extends _NativeDouble {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Double extends _NativeDouble {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Void extends NativeType {}
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class NativeFunction<T extends Function> extends NativeType {}
|
55
runtime/lib/ffi_patch.dart
Normal file
55
runtime/lib/ffi_patch.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
// 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.
|
||||
|
||||
import "dart:_internal" show patch;
|
||||
|
||||
@patch
|
||||
Pointer<T> allocate<T extends NativeType>({int count: 1}) native "Ffi_allocate";
|
||||
|
||||
@patch
|
||||
T fromAddress<T extends Pointer>(int ptr) native "Ffi_fromAddress";
|
||||
|
||||
@patch
|
||||
int sizeOf<T extends NativeType>() native "Ffi_sizeOf";
|
||||
|
||||
@patch
|
||||
Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||
@DartRepresentationOf("T") Function f) native "Ffi_fromFunction";
|
||||
|
||||
@patch
|
||||
@pragma("vm:entry-point")
|
||||
class Pointer<T extends NativeType> {
|
||||
@patch
|
||||
void store(Object value) native "Ffi_store";
|
||||
|
||||
@patch
|
||||
R load<R>() native "Ffi_load";
|
||||
|
||||
@patch
|
||||
int get address native "Ffi_address";
|
||||
|
||||
// Note this could also be implmented without an extra native as offsetBy
|
||||
// (elementSize()*index). This would be 2 native calls rather than one. What
|
||||
// would be better?
|
||||
@patch
|
||||
Pointer<T> elementAt(int index) native "Ffi_elementAt";
|
||||
|
||||
// Note this could also be implmented without an extra native as
|
||||
// fromAddress(address). This would be 2 native calls rather than one.
|
||||
// What would be better?
|
||||
@patch
|
||||
Pointer<T> offsetBy(int offsetInBytes) native "Ffi_offsetBy";
|
||||
|
||||
// Note this could also be implemented without an extra native as
|
||||
// fromAddress(address). This would be 2 native calls rather than one.
|
||||
// What would be better?
|
||||
@patch
|
||||
U cast<U extends Pointer>() native "Ffi_cast";
|
||||
|
||||
@patch
|
||||
R asFunction<R extends Function>() native "Ffi_asFunction";
|
||||
|
||||
@patch
|
||||
void free() native "Ffi_free";
|
||||
}
|
17
runtime/lib/ffi_sources.gni
Normal file
17
runtime/lib/ffi_sources.gni
Normal file
|
@ -0,0 +1,17 @@
|
|||
# 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.
|
||||
|
||||
# Sources visible via dart:ffi library.
|
||||
ffi_runtime_cc_files = [
|
||||
"ffi.cc",
|
||||
"ffi_dynamic_library.cc",
|
||||
]
|
||||
|
||||
ffi_runtime_dart_files = [
|
||||
"ffi_patch.dart",
|
||||
"ffi_dynamic_library_patch.dart",
|
||||
"ffi_native_type_patch.dart",
|
||||
]
|
||||
|
||||
ffi_runtime_sources = ffi_runtime_cc_files + ffi_runtime_dart_files
|
|
@ -8,6 +8,7 @@ import("../../sdk/lib/collection/collection_sources.gni")
|
|||
import("../../sdk/lib/convert/convert_sources.gni")
|
||||
import("../../sdk/lib/core/core_sources.gni")
|
||||
import("../../sdk/lib/developer/developer_sources.gni")
|
||||
import("../../sdk/lib/ffi/ffi_sources.gni")
|
||||
import("../../sdk/lib/internal/internal_sources.gni")
|
||||
import("../../sdk/lib/isolate/isolate_sources.gni")
|
||||
import("../../sdk/lib/math/math_sources.gni")
|
||||
|
@ -16,13 +17,15 @@ import("../../sdk/lib/profiler/profiler_sources.gni")
|
|||
import("../../sdk/lib/typed_data/typed_data_sources.gni")
|
||||
import("../../sdk/lib/vmservice/vmservice_sources.gni")
|
||||
import("../../utils/compile_platform.gni")
|
||||
import("../bin/io_sources.gni")
|
||||
import("../bin/cli_sources.gni")
|
||||
import("../bin/io_sources.gni")
|
||||
import("../configs.gni")
|
||||
import("../lib/async_sources.gni")
|
||||
import("../lib/collection_sources.gni")
|
||||
import("../lib/convert_sources.gni")
|
||||
import("../lib/core_sources.gni")
|
||||
import("../lib/developer_sources.gni")
|
||||
import("../lib/ffi_sources.gni")
|
||||
import("../lib/internal_sources.gni")
|
||||
import("../lib/isolate_sources.gni")
|
||||
import("../lib/math_sources.gni")
|
||||
|
@ -30,7 +33,6 @@ import("../lib/mirrors_sources.gni")
|
|||
import("../lib/profiler_sources.gni")
|
||||
import("../lib/typed_data_sources.gni")
|
||||
import("../lib/vmservice_sources.gni")
|
||||
import("../configs.gni")
|
||||
import("../runtime_args.gni")
|
||||
import("compiler/compiler_sources.gni")
|
||||
import("heap/heap_sources.gni")
|
||||
|
@ -84,12 +86,13 @@ library_for_all_configs("libdart_vm") {
|
|||
library_for_all_configs("libdart_lib") {
|
||||
target_type = "source_set"
|
||||
include_dirs = [ ".." ]
|
||||
allsources = async_runtime_sources + collection_runtime_sources +
|
||||
convert_runtime_sources + core_runtime_sources +
|
||||
developer_runtime_sources + internal_runtime_sources +
|
||||
isolate_runtime_sources + math_runtime_sources +
|
||||
mirrors_runtime_sources + profiler_runtime_sources +
|
||||
typed_data_runtime_sources + vmservice_runtime_sources
|
||||
allsources =
|
||||
async_runtime_sources + collection_runtime_sources +
|
||||
convert_runtime_sources + core_runtime_sources +
|
||||
developer_runtime_sources + internal_runtime_sources +
|
||||
isolate_runtime_sources + math_runtime_sources + mirrors_runtime_sources +
|
||||
profiler_runtime_sources + typed_data_runtime_sources +
|
||||
vmservice_runtime_sources + ffi_runtime_sources
|
||||
sources = [ "bootstrap.cc" ] + rebase_path(allsources, ".", "../lib")
|
||||
snapshot_sources = []
|
||||
nosnapshot_sources = []
|
||||
|
|
|
@ -100,6 +100,11 @@ void Bootstrap::SetupNativeResolver() {
|
|||
library.set_native_entry_resolver(resolver);
|
||||
library.set_native_entry_symbol_resolver(symbol_resolver);
|
||||
|
||||
library = Library::FfiLibrary();
|
||||
ASSERT(!library.IsNull());
|
||||
library.set_native_entry_resolver(resolver);
|
||||
library.set_native_entry_symbol_resolver(symbol_resolver);
|
||||
|
||||
library = Library::InternalLibrary();
|
||||
ASSERT(!library.IsNull());
|
||||
library.set_native_entry_resolver(resolver);
|
||||
|
|
|
@ -350,7 +350,22 @@ namespace dart {
|
|||
V(VMService_CancelStream, 1) \
|
||||
V(VMService_RequestAssets, 0) \
|
||||
V(VMService_DecodeAssets, 1) \
|
||||
V(VMService_spawnUriNotify, 2)
|
||||
V(VMService_spawnUriNotify, 2) \
|
||||
V(Ffi_allocate, 1) \
|
||||
V(Ffi_free, 1) \
|
||||
V(Ffi_load, 1) \
|
||||
V(Ffi_store, 2) \
|
||||
V(Ffi_address, 1) \
|
||||
V(Ffi_fromAddress, 1) \
|
||||
V(Ffi_elementAt, 2) \
|
||||
V(Ffi_offsetBy, 2) \
|
||||
V(Ffi_cast, 1) \
|
||||
V(Ffi_sizeOf, 0) \
|
||||
V(Ffi_asFunction, 1) \
|
||||
V(Ffi_fromFunction, 1) \
|
||||
V(Ffi_dl_open, 1) \
|
||||
V(Ffi_dl_lookup, 2) \
|
||||
V(Ffi_dl_getHandle, 1)
|
||||
|
||||
// List of bootstrap native entry points used in the dart:mirror library.
|
||||
#define MIRRORS_BOOTSTRAP_NATIVE_LIST(V) \
|
||||
|
|
|
@ -1385,6 +1385,7 @@ void ClassFinalizer::VerifyImplicitFieldOffsets() {
|
|||
String& name = String::Handle(zone);
|
||||
String& expected_name = String::Handle(zone);
|
||||
Error& error = Error::Handle(zone);
|
||||
TypeParameter& type_param = TypeParameter::Handle(zone);
|
||||
|
||||
// First verify field offsets of all the TypedDataView classes.
|
||||
for (intptr_t cid = kTypedDataInt8ArrayViewCid;
|
||||
|
@ -1443,6 +1444,15 @@ void ClassFinalizer::VerifyImplicitFieldOffsets() {
|
|||
name ^= field.name();
|
||||
expected_name ^= String::New("_data");
|
||||
ASSERT(String::EqualsIgnoringPrivateKey(name, expected_name));
|
||||
|
||||
// Now verify field offsets of 'Pointer' class.
|
||||
cls = class_table.At(kFfiPointerCid);
|
||||
error = cls.EnsureIsFinalized(thread);
|
||||
ASSERT(error.IsNull());
|
||||
ASSERT(cls.NumOwnTypeArguments() == 1);
|
||||
type_param ^= TypeParameter::RawCast(
|
||||
TypeArguments::Handle(cls.type_parameters()).TypeAt(0));
|
||||
ASSERT(Pointer::kNativeTypeArgPos == type_param.index());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace dart {
|
|||
V(ClosureData) \
|
||||
V(SignatureData) \
|
||||
V(RedirectionData) \
|
||||
V(FfiTrampolineData) \
|
||||
V(Field) \
|
||||
V(Script) \
|
||||
V(Library) \
|
||||
|
@ -65,6 +66,8 @@ namespace dart {
|
|||
V(Float64x2) \
|
||||
V(TypedData) \
|
||||
V(ExternalTypedData) \
|
||||
V(Pointer) \
|
||||
V(DynamicLibrary) \
|
||||
V(Capability) \
|
||||
V(ReceivePort) \
|
||||
V(SendPort) \
|
||||
|
@ -102,6 +105,27 @@ namespace dart {
|
|||
V(Int32x4Array) \
|
||||
V(Float64x2Array)
|
||||
|
||||
#define CLASS_LIST_FFI_TYPE_MARKER(V) \
|
||||
V(Int8) \
|
||||
V(Int16) \
|
||||
V(Int32) \
|
||||
V(Int64) \
|
||||
V(Uint8) \
|
||||
V(Uint16) \
|
||||
V(Uint32) \
|
||||
V(Uint64) \
|
||||
V(IntPtr) \
|
||||
V(Float) \
|
||||
V(Double) \
|
||||
V(Void)
|
||||
|
||||
#define CLASS_LIST_FFI(V) \
|
||||
V(Pointer) \
|
||||
V(NativeFunction) \
|
||||
CLASS_LIST_FFI_TYPE_MARKER(V) \
|
||||
V(NativeType) \
|
||||
V(DynamicLibrary)
|
||||
|
||||
#define DART_CLASS_LIST_TYPED_DATA(V) \
|
||||
V(Int8) \
|
||||
V(Uint8) \
|
||||
|
@ -152,6 +176,10 @@ enum ClassId {
|
|||
CLASS_LIST(DEFINE_OBJECT_KIND)
|
||||
#undef DEFINE_OBJECT_KIND
|
||||
|
||||
#define DEFINE_OBJECT_KIND(clazz) kFfi##clazz##Cid,
|
||||
CLASS_LIST_FFI(DEFINE_OBJECT_KIND)
|
||||
#undef DEFINE_OBJECT_KIND
|
||||
|
||||
// clang-format off
|
||||
#define DEFINE_OBJECT_KIND(clazz) kTypedData##clazz##Cid,
|
||||
CLASS_LIST_TYPED_DATA(DEFINE_OBJECT_KIND)
|
||||
|
|
|
@ -474,6 +474,10 @@ class Assembler : public AssemblerBase {
|
|||
// for proper unwinding of Dart frames (use --generate_gdb_symbols and -O0).
|
||||
void movq(Register dst, Register src) { EmitQ(src, dst, 0x89); }
|
||||
|
||||
void movq(XmmRegister dst, Register src) {
|
||||
EmitQ(dst, src, 0x6E, 0x0F, 0x66);
|
||||
}
|
||||
|
||||
void movd(XmmRegister dst, Register src) {
|
||||
EmitL(dst, src, 0x6E, 0x0F, 0x66);
|
||||
}
|
||||
|
|
|
@ -2030,6 +2030,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
|
|||
return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function);
|
||||
case RawFunction::kSignatureFunction:
|
||||
case RawFunction::kIrregexpFunction:
|
||||
case RawFunction::kFfiTrampoline:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
|
|
@ -383,6 +383,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
|
|||
break;
|
||||
case RawFunction::kSignatureFunction:
|
||||
case RawFunction::kIrregexpFunction:
|
||||
case RawFunction::kFfiTrampoline:
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (needs_expr_temp_) {
|
||||
|
|
|
@ -103,6 +103,9 @@ static void PrecompilationModeHandler(bool value) {
|
|||
FLAG_background_compilation = false;
|
||||
FLAG_collect_code = false;
|
||||
FLAG_enable_mirrors = false;
|
||||
// TODO(dacoharkes): Ffi support in AOT
|
||||
// https://github.com/dart-lang/sdk/issues/35765
|
||||
FLAG_enable_ffi = false;
|
||||
FLAG_fields_may_be_reset = true;
|
||||
FLAG_interpret_irregexp = true;
|
||||
FLAG_lazy_dispatchers = false;
|
||||
|
|
27
runtime/vm/constants_x64.cc
Normal file
27
runtime/vm/constants_x64.cc
Normal file
|
@ -0,0 +1,27 @@
|
|||
// 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.
|
||||
|
||||
#include "vm/constants_x64.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
#if defined(_WIN64)
|
||||
const Register CallingConventions::ArgumentRegisters[] = {
|
||||
CallingConventions::kArg1Reg, CallingConventions::kArg2Reg,
|
||||
CallingConventions::kArg3Reg, CallingConventions::kArg4Reg};
|
||||
|
||||
const XmmRegister CallingConventions::XmmArgumentRegisters[] = {
|
||||
XmmRegister::XMM0, XmmRegister::XMM1, XmmRegister::XMM2, XmmRegister::XMM3};
|
||||
#else
|
||||
const Register CallingConventions::ArgumentRegisters[] = {
|
||||
CallingConventions::kArg1Reg, CallingConventions::kArg2Reg,
|
||||
CallingConventions::kArg3Reg, CallingConventions::kArg4Reg,
|
||||
CallingConventions::kArg5Reg, CallingConventions::kArg6Reg};
|
||||
|
||||
const XmmRegister CallingConventions::XmmArgumentRegisters[] = {
|
||||
XmmRegister::XMM0, XmmRegister::XMM1, XmmRegister::XMM2, XmmRegister::XMM3,
|
||||
XmmRegister::XMM4, XmmRegister::XMM5, XmmRegister::XMM6, XmmRegister::XMM7};
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
|
@ -5,6 +5,9 @@
|
|||
#ifndef RUNTIME_VM_CONSTANTS_X64_H_
|
||||
#define RUNTIME_VM_CONSTANTS_X64_H_
|
||||
|
||||
#include "platform/assert.h"
|
||||
#include "platform/globals.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
enum Register {
|
||||
|
@ -149,6 +152,20 @@ class CallingConventions {
|
|||
static const Register kArg2Reg = RDX;
|
||||
static const Register kArg3Reg = R8;
|
||||
static const Register kArg4Reg = R9;
|
||||
static const Register ArgumentRegisters[];
|
||||
static const intptr_t kArgumentRegisters =
|
||||
R(kArg1Reg) | R(kArg2Reg) | R(kArg3Reg) | R(kArg4Reg);
|
||||
static const intptr_t kNumArgRegs = 4;
|
||||
|
||||
static const XmmRegister XmmArgumentRegisters[];
|
||||
static const intptr_t kXmmArgumentRegisters =
|
||||
R(XMM0) | R(XMM1) | R(XMM2) | R(XMM3);
|
||||
static const intptr_t kNumXmmArgRegs = 4;
|
||||
|
||||
// can ArgumentRegisters[i] and XmmArgumentRegisters[i] both be used at the
|
||||
// same time? (Windows no, rest yes)
|
||||
static const bool kArgumentIntRegXorXmmReg = true;
|
||||
|
||||
static const intptr_t kShadowSpaceBytes = 4 * kWordSize;
|
||||
|
||||
static const intptr_t kVolatileCpuRegisters =
|
||||
|
@ -164,6 +181,8 @@ class CallingConventions {
|
|||
R(XMM6) | R(XMM7) | R(XMM8) | R(XMM9) | R(XMM10) | R(XMM11) | R(XMM12) |
|
||||
R(XMM13) | R(XMM14) | R(XMM15);
|
||||
|
||||
static const XmmRegister xmmFirstNonParameterReg = XMM4;
|
||||
|
||||
// Windows x64 ABI specifies that small objects are passed in registers.
|
||||
// Otherwise they are passed by reference.
|
||||
static const size_t kRegisterTransferLimit = 16;
|
||||
|
@ -177,6 +196,22 @@ class CallingConventions {
|
|||
static const Register kArg4Reg = RCX;
|
||||
static const Register kArg5Reg = R8;
|
||||
static const Register kArg6Reg = R9;
|
||||
static const Register ArgumentRegisters[];
|
||||
static const intptr_t kArgumentRegisters = R(kArg1Reg) | R(kArg2Reg) |
|
||||
R(kArg3Reg) | R(kArg4Reg) |
|
||||
R(kArg5Reg) | R(kArg6Reg);
|
||||
static const intptr_t kNumArgRegs = 6;
|
||||
|
||||
static const XmmRegister XmmArgumentRegisters[];
|
||||
static const intptr_t kXmmArgumentRegisters = R(XMM0) | R(XMM1) | R(XMM2) |
|
||||
R(XMM3) | R(XMM4) | R(XMM5) |
|
||||
R(XMM6) | R(XMM7);
|
||||
static const intptr_t kNumXmmArgRegs = 8;
|
||||
|
||||
// can ArgumentRegisters[i] and XmmArgumentRegisters[i] both be used at the
|
||||
// same time? (Windows no, rest yes)
|
||||
static const bool kArgumentIntRegXorXmmReg = false;
|
||||
|
||||
static const intptr_t kShadowSpaceBytes = 0;
|
||||
|
||||
static const intptr_t kVolatileCpuRegisters = R(RAX) | R(RCX) | R(RDX) |
|
||||
|
@ -192,6 +227,8 @@ class CallingConventions {
|
|||
R(RBX) | R(R12) | R(R13) | R(R14) | R(R15);
|
||||
|
||||
static const intptr_t kCalleeSaveXmmRegisters = 0;
|
||||
|
||||
static const XmmRegister xmmFirstNonParameterReg = XMM8;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4774,6 +4774,10 @@ RawString* Api::GetEnvironmentValue(Thread* thread, const String& name) {
|
|||
return Symbols::False().raw();
|
||||
}
|
||||
|
||||
if (!Api::ffiEnabled() && name.Equals(Symbols::DartLibraryFfi())) {
|
||||
return Symbols::False().raw();
|
||||
}
|
||||
|
||||
if (name.Equals(Symbols::DartVMProduct())) {
|
||||
#ifdef PRODUCT
|
||||
return Symbols::True().raw();
|
||||
|
|
|
@ -294,6 +294,25 @@ class Api : AllStatic {
|
|||
|
||||
static RawString* GetEnvironmentValue(Thread* thread, const String& name);
|
||||
|
||||
static bool ffiEnabled() {
|
||||
// dart:ffi is not implemented for the following configurations
|
||||
#if !defined(TARGET_ARCH_X64)
|
||||
// https://github.com/dart-lang/sdk/issues/35774
|
||||
return false;
|
||||
#elif !defined(TARGET_OS_LINUX) && !defined(TARGET_OS_MACOS)
|
||||
// https://github.com/dart-lang/sdk/issues/35760 Arm32 && Android
|
||||
// https://github.com/dart-lang/sdk/issues/35771 Windows
|
||||
// https://github.com/dart-lang/sdk/issues/35772 Arm64
|
||||
// https://github.com/dart-lang/sdk/issues/35773 DBC
|
||||
return false;
|
||||
#else
|
||||
// dart:ffi is also not implemented for precompiled in which case
|
||||
// FLAG_enable_ffi is set to false by --precompilation.
|
||||
// Once dart:ffi is supported on all targets, only users will set this flag
|
||||
return FLAG_enable_ffi;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
static Dart_Handle InitNewHandle(Thread* thread, RawObject* raw);
|
||||
|
||||
|
|
564
runtime/vm/ffi_trampoline_stubs_x64.cc
Normal file
564
runtime/vm/ffi_trampoline_stubs_x64.cc
Normal file
|
@ -0,0 +1,564 @@
|
|||
// 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.
|
||||
|
||||
// TODO(dacoharkes): Move this into compiler namespace.
|
||||
|
||||
#include "vm/globals.h"
|
||||
|
||||
#include "vm/stub_code.h"
|
||||
|
||||
#if defined(TARGET_ARCH_X64) && !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#include "vm/compiler/assembler/assembler.h"
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/compiler/backend/flow_graph_compiler.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/constants_x64.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/heap/heap.h"
|
||||
#include "vm/heap/scavenger.h"
|
||||
#include "vm/instructions.h"
|
||||
#include "vm/object_store.h"
|
||||
#include "vm/resolver.h"
|
||||
#include "vm/stack_frame.h"
|
||||
#include "vm/tags.h"
|
||||
#include "vm/type_testing_stubs.h"
|
||||
|
||||
#define __ assembler->
|
||||
|
||||
namespace dart {
|
||||
|
||||
static Representation TypeRepresentation(const AbstractType& result_type) {
|
||||
switch (result_type.type_class_id()) {
|
||||
case kFfiFloatCid:
|
||||
case kFfiDoubleCid:
|
||||
return kUnboxedDouble;
|
||||
case kFfiInt8Cid:
|
||||
case kFfiInt16Cid:
|
||||
case kFfiInt32Cid:
|
||||
case kFfiInt64Cid:
|
||||
case kFfiUint8Cid:
|
||||
case kFfiUint16Cid:
|
||||
case kFfiUint32Cid:
|
||||
case kFfiUint64Cid:
|
||||
case kFfiIntPtrCid:
|
||||
case kFfiPointerCid:
|
||||
default: // Subtypes of Pointer.
|
||||
return kUnboxedInt64;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a Ffi [signature] to a list of Representations.
|
||||
// Note that this ignores first argument (receiver) which is dynamic.
|
||||
static ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||
const Function& signature) {
|
||||
intptr_t num_arguments = signature.num_fixed_parameters() - 1;
|
||||
auto result = new ZoneGrowableArray<Representation>(num_arguments);
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
AbstractType& arg_type =
|
||||
AbstractType::Handle(signature.ParameterTypeAt(i + 1));
|
||||
result->Add(TypeRepresentation(arg_type));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Takes a list of argument representations, and converts it to a list of
|
||||
// argument locations based on calling convention.
|
||||
static ZoneGrowableArray<Location>* ArgumentLocations(
|
||||
const ZoneGrowableArray<Representation>& arg_representations) {
|
||||
intptr_t num_arguments = arg_representations.length();
|
||||
auto result = new ZoneGrowableArray<Location>(num_arguments);
|
||||
result->FillWith(Location(), 0, num_arguments);
|
||||
Location* data = result->data();
|
||||
|
||||
// Loop through all arguments and assign a register or a stack location.
|
||||
intptr_t int_regs_used = 0;
|
||||
intptr_t xmm_regs_used = 0;
|
||||
intptr_t nth_stack_argument = 0;
|
||||
bool on_stack;
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
on_stack = true;
|
||||
switch (arg_representations.At(i)) {
|
||||
case kUnboxedInt64:
|
||||
if (int_regs_used < CallingConventions::kNumArgRegs) {
|
||||
data[i] = Location::RegisterLocation(
|
||||
CallingConventions::ArgumentRegisters[int_regs_used]);
|
||||
int_regs_used++;
|
||||
if (CallingConventions::kArgumentIntRegXorXmmReg) {
|
||||
xmm_regs_used++;
|
||||
}
|
||||
on_stack = false;
|
||||
}
|
||||
break;
|
||||
case kUnboxedDouble:
|
||||
if (xmm_regs_used < CallingConventions::kNumXmmArgRegs) {
|
||||
data[i] = Location::FpuRegisterLocation(
|
||||
CallingConventions::XmmArgumentRegisters[xmm_regs_used]);
|
||||
xmm_regs_used++;
|
||||
if (CallingConventions::kArgumentIntRegXorXmmReg) {
|
||||
int_regs_used++;
|
||||
}
|
||||
on_stack = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (on_stack) {
|
||||
data[i] = Location::StackSlot(nth_stack_argument, RSP);
|
||||
nth_stack_argument++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static intptr_t NumStackArguments(
|
||||
const ZoneGrowableArray<Location>& locations) {
|
||||
intptr_t num_arguments = locations.length();
|
||||
intptr_t num_stack_arguments = 0;
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
if (locations.At(i).IsStackSlot()) {
|
||||
num_stack_arguments++;
|
||||
}
|
||||
}
|
||||
return num_stack_arguments;
|
||||
}
|
||||
|
||||
// Input parameters:
|
||||
// Register reg : a Null, or something else
|
||||
static void GenerateNotNullCheck(Assembler* assembler, Register reg) {
|
||||
Label not_null;
|
||||
Address throw_null_pointer_address =
|
||||
Address(THR, Thread::OffsetFromThread(&kArgumentNullErrorRuntimeEntry));
|
||||
|
||||
__ CompareObject(reg, Object::null_object());
|
||||
__ j(NOT_EQUAL, ¬_null, Assembler::kNearJump);
|
||||
|
||||
// TODO(dacoharkes): Create the message here and use
|
||||
// kArgumentErrorRuntimeEntry to report which argument was null.
|
||||
__ movq(CODE_REG, Address(THR, Thread::call_to_runtime_stub_offset()));
|
||||
__ movq(RBX, throw_null_pointer_address);
|
||||
__ movq(R10, Immediate(0));
|
||||
__ call(Address(THR, Thread::call_to_runtime_entry_point_offset()));
|
||||
|
||||
__ Bind(¬_null);
|
||||
}
|
||||
|
||||
// Saves an int64 in the thread so GC does not trip.
|
||||
//
|
||||
// Input parameters:
|
||||
// Register src : a C int64
|
||||
static void GenerateSaveInt64GCSafe(Assembler* assembler, Register src) {
|
||||
__ movq(Address(THR, Thread::unboxed_int64_runtime_arg_offset()), src);
|
||||
}
|
||||
|
||||
// Loads an int64 from the thread.
|
||||
static void GenerateLoadInt64GCSafe(Assembler* assembler, Register dst) {
|
||||
__ movq(dst, Address(THR, Thread::unboxed_int64_runtime_arg_offset()));
|
||||
}
|
||||
|
||||
// Takes a Dart int and converts it to a C int64.
|
||||
//
|
||||
// Input parameters:
|
||||
// Register reg : a Dart Null, Smi, or Mint
|
||||
// Output parameters:
|
||||
// Register reg : a C int64
|
||||
// Invariant: keeps ArgumentRegisters and XmmArgumentRegisters intact
|
||||
void GenerateMarshalInt64(Assembler* assembler, Register reg) {
|
||||
ASSERT(reg != TMP);
|
||||
ASSERT((1 << TMP & CallingConventions::kArgumentRegisters) == 0);
|
||||
Label done, not_smi;
|
||||
|
||||
// Exception on Null
|
||||
GenerateNotNullCheck(assembler, reg);
|
||||
|
||||
// Smi or Mint?
|
||||
__ movq(TMP, reg);
|
||||
__ testq(TMP, Immediate(kSmiTagMask));
|
||||
__ j(NOT_ZERO, ¬_smi, Assembler::kNearJump);
|
||||
|
||||
// Smi
|
||||
__ SmiUntag(reg);
|
||||
__ jmp(&done, Assembler::kNearJump);
|
||||
|
||||
// Mint
|
||||
__ Bind(¬_smi);
|
||||
__ movq(reg, FieldAddress(reg, Mint::value_offset()));
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
// Takes a C int64 and converts it to a Dart int.
|
||||
//
|
||||
// Input parameters:
|
||||
// RAX : a C int64
|
||||
// Output paramaters:
|
||||
// RAX : a Dart Smi or Mint
|
||||
static void GenerateUnmarshalInt64(Assembler* assembler) {
|
||||
const Class& mint_class =
|
||||
Class::ZoneHandle(Isolate::Current()->object_store()->mint_class());
|
||||
ASSERT(!mint_class.IsNull());
|
||||
const auto& mint_allocation_stub =
|
||||
Code::ZoneHandle(StubCode::GetAllocationStubForClass(mint_class));
|
||||
ASSERT(!mint_allocation_stub.IsNull());
|
||||
Label done;
|
||||
|
||||
// Try whether it fits in a Smi.
|
||||
__ movq(TMP, RAX);
|
||||
__ SmiTag(RAX);
|
||||
__ j(NO_OVERFLOW, &done, Assembler::kNearJump);
|
||||
|
||||
// Mint
|
||||
// Backup result value (to avoid GC).
|
||||
GenerateSaveInt64GCSafe(assembler, TMP);
|
||||
|
||||
// Allocate object (can call into runtime).
|
||||
__ Call(mint_allocation_stub);
|
||||
|
||||
// Store result value.
|
||||
GenerateLoadInt64GCSafe(assembler, TMP);
|
||||
__ movq(FieldAddress(RAX, Mint::value_offset()), TMP);
|
||||
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
// Takes a Dart double and converts it into a C double.
|
||||
//
|
||||
// Input parameters:
|
||||
// Register reg : a Dart Null or Double
|
||||
// Output parameters:
|
||||
// XmmRegister xmm_reg : a C double
|
||||
// Invariant: keeps ArgumentRegisters and other XmmArgumentRegisters intact
|
||||
static void GenerateMarshalDouble(Assembler* assembler,
|
||||
Register reg,
|
||||
XmmRegister xmm_reg) {
|
||||
ASSERT((1 << reg & CallingConventions::kArgumentRegisters) == 0);
|
||||
|
||||
// Throw a Dart Exception on Null.
|
||||
GenerateNotNullCheck(assembler, reg);
|
||||
|
||||
__ movq(reg, FieldAddress(reg, Double::value_offset()));
|
||||
__ movq(xmm_reg, reg);
|
||||
}
|
||||
|
||||
// Takes a C double and converts it into a Dart double.
|
||||
//
|
||||
// Input parameters:
|
||||
// XMM0 : a C double
|
||||
// Output parameters:
|
||||
// RAX : a Dart Double
|
||||
static void GenerateUnmarshalDouble(Assembler* assembler) {
|
||||
const auto& double_class =
|
||||
Class::ZoneHandle(Isolate::Current()->object_store()->double_class());
|
||||
ASSERT(!double_class.IsNull());
|
||||
const auto& double_allocation_stub =
|
||||
Code::ZoneHandle(StubCode::GetAllocationStubForClass(double_class));
|
||||
ASSERT(!double_allocation_stub.IsNull());
|
||||
|
||||
// Backup result value (to avoid GC).
|
||||
__ movq(RAX, XMM0);
|
||||
GenerateSaveInt64GCSafe(assembler, RAX);
|
||||
|
||||
// Allocate object (can call into runtime).
|
||||
__ Call(double_allocation_stub);
|
||||
|
||||
// Store the result value.
|
||||
GenerateLoadInt64GCSafe(assembler, TMP);
|
||||
__ movq(FieldAddress(RAX, Double::value_offset()), TMP);
|
||||
}
|
||||
|
||||
// Takes a Dart double and converts into a C float.
|
||||
//
|
||||
// Input parameters:
|
||||
// Register reg : a Dart double
|
||||
// Output parameters:
|
||||
// XmmRegister xxmReg : a C float
|
||||
// Invariant: keeps ArgumentRegisters and other XmmArgumentRegisters intact
|
||||
static void GenerateMarshalFloat(Assembler* assembler,
|
||||
Register reg,
|
||||
XmmRegister xmm_reg) {
|
||||
ASSERT((1 << reg & CallingConventions::kArgumentRegisters) == 0);
|
||||
|
||||
GenerateMarshalDouble(assembler, reg, xmm_reg);
|
||||
|
||||
__ cvtsd2ss(xmm_reg, xmm_reg);
|
||||
}
|
||||
|
||||
// Takes a C float and converts it into a Dart double.
|
||||
//
|
||||
// Input parameters:
|
||||
// XMM0 : a C float
|
||||
// Output paramaters:
|
||||
// RAX : a Dart Double
|
||||
static void GenerateUnmarshalFloat(Assembler* assembler) {
|
||||
__ cvtss2sd(XMM0, XMM0);
|
||||
GenerateUnmarshalDouble(assembler);
|
||||
}
|
||||
|
||||
// Takes a Dart ffi.Pointer and converts it into a C pointer.
|
||||
//
|
||||
// Input parameters:
|
||||
// Register reg : a Dart ffi.Pointer or Null
|
||||
// Output parameters:
|
||||
// Register reg : a C pointer
|
||||
static void GenerateMarshalPointer(Assembler* assembler, Register reg) {
|
||||
Label done, not_null;
|
||||
|
||||
__ CompareObject(reg, Object::null_object());
|
||||
__ j(NOT_EQUAL, ¬_null, Assembler::kNearJump);
|
||||
|
||||
// If null, the address is 0.
|
||||
__ movq(reg, Immediate(0));
|
||||
__ jmp(&done);
|
||||
|
||||
// If not null but a Pointer, load the address.
|
||||
__ Bind(¬_null);
|
||||
__ movq(reg, FieldAddress(reg, Pointer::address_offset()));
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
// Takes a C pointer and converts it into a Dart ffi.Pointer or Null.
|
||||
//
|
||||
// Input parameters:
|
||||
// RAX : a C pointer
|
||||
// Outpot paramaters:
|
||||
// RAX : a Dart ffi.Pointer or Null
|
||||
static void GenerateUnmarshalPointer(Assembler* assembler,
|
||||
Address closure_dart,
|
||||
const Class& pointer_class) {
|
||||
Label done, not_null;
|
||||
ASSERT(!pointer_class.IsNull());
|
||||
const auto& pointer_allocation_stub =
|
||||
Code::ZoneHandle(StubCode::GetAllocationStubForClass(pointer_class));
|
||||
ASSERT(!pointer_allocation_stub.IsNull());
|
||||
|
||||
// If the address is 0, return a Dart Null.
|
||||
__ cmpq(RAX, Immediate(0));
|
||||
__ j(NOT_EQUAL, ¬_null, Assembler::kNearJump);
|
||||
__ LoadObject(RAX, Object::null_object());
|
||||
__ jmp(&done);
|
||||
|
||||
// Backup result value (to avoid GC).
|
||||
__ Bind(¬_null);
|
||||
GenerateSaveInt64GCSafe(assembler, RAX);
|
||||
|
||||
// Allocate object (can call into runtime).
|
||||
__ movq(TMP, closure_dart);
|
||||
__ movq(TMP, FieldAddress(TMP, Closure::function_offset()));
|
||||
__ movq(TMP, FieldAddress(TMP, Function::result_type_offset()));
|
||||
__ pushq(FieldAddress(TMP, Type::arguments_offset()));
|
||||
__ Call(pointer_allocation_stub);
|
||||
__ popq(TMP); // Pop type arguments.
|
||||
|
||||
// Store the result value.
|
||||
GenerateLoadInt64GCSafe(assembler, RDX);
|
||||
__ movq(FieldAddress(RAX, Pointer::address_offset()), RDX);
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
static void GenerateMarshalArgument(Assembler* assembler,
|
||||
const AbstractType& arg_type,
|
||||
Register reg,
|
||||
XmmRegister xmm_reg) {
|
||||
switch (arg_type.type_class_id()) {
|
||||
case kFfiInt8Cid:
|
||||
case kFfiInt16Cid:
|
||||
case kFfiInt32Cid:
|
||||
case kFfiInt64Cid:
|
||||
case kFfiUint8Cid:
|
||||
case kFfiUint16Cid:
|
||||
case kFfiUint32Cid:
|
||||
case kFfiUint64Cid:
|
||||
case kFfiIntPtrCid:
|
||||
// TODO(dacoharkes): Truncate and sign extend 8 bit and 16 bit, and write
|
||||
// tests. https://github.com/dart-lang/sdk/issues/35787
|
||||
GenerateMarshalInt64(assembler, reg);
|
||||
return;
|
||||
case kFfiFloatCid:
|
||||
GenerateMarshalFloat(assembler, reg, xmm_reg);
|
||||
return;
|
||||
case kFfiDoubleCid:
|
||||
GenerateMarshalDouble(assembler, reg, xmm_reg);
|
||||
return;
|
||||
case kFfiPointerCid:
|
||||
default: // Subtypes of Pointer.
|
||||
GenerateMarshalPointer(assembler, reg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateUnmarshalResult(Assembler* assembler,
|
||||
const AbstractType& result_type,
|
||||
Address closure_dart) {
|
||||
switch (result_type.type_class_id()) {
|
||||
case kFfiInt8Cid:
|
||||
case kFfiInt16Cid:
|
||||
case kFfiInt32Cid:
|
||||
case kFfiInt64Cid:
|
||||
case kFfiUint8Cid:
|
||||
case kFfiUint16Cid:
|
||||
case kFfiUint32Cid:
|
||||
case kFfiUint64Cid:
|
||||
case kFfiIntPtrCid:
|
||||
GenerateUnmarshalInt64(assembler);
|
||||
return;
|
||||
case kFfiFloatCid:
|
||||
GenerateUnmarshalFloat(assembler);
|
||||
return;
|
||||
case kFfiDoubleCid:
|
||||
GenerateUnmarshalDouble(assembler);
|
||||
return;
|
||||
case kFfiPointerCid:
|
||||
default: // subtypes of Pointer
|
||||
break;
|
||||
}
|
||||
Class& cls = Class::ZoneHandle(Thread::Current()->zone(),
|
||||
Type::Cast(result_type).type_class());
|
||||
|
||||
GenerateUnmarshalPointer(assembler, closure_dart, cls);
|
||||
}
|
||||
|
||||
// Generates a assembly for dart:ffi trampolines:
|
||||
// - marshal arguments
|
||||
// - put the arguments in registers and on the c stack
|
||||
// - invoke the c function
|
||||
// - (c result register is the same as dart, so keep in place)
|
||||
// - unmarshal c result
|
||||
// - return
|
||||
//
|
||||
// Input parameters:
|
||||
// RSP + kWordSize * num_arguments : closure.
|
||||
// RSP + kWordSize * (num_arguments - 1) : arg 1.
|
||||
// RSP + kWordSize * (num_arguments - 2) : arg 2.
|
||||
// RSP + kWordSize : arg n.
|
||||
// After entering stub:
|
||||
// RBP = RSP (before stub) - kWordSize
|
||||
// RBP + kWordSize * (num_arguments + 1) : closure.
|
||||
// RBP + kWordSize * num_arguments : arg 1.
|
||||
// RBP + kWordSize * (num_arguments - 1) : arg 2.
|
||||
// RBP + kWordSize * 2 : arg n.
|
||||
//
|
||||
// TODO(dacoharkes): Test truncation on non 64 bits ints and floats.
|
||||
void GenerateFfiTrampoline(Assembler* assembler, const Function& signature) {
|
||||
ZoneGrowableArray<Representation>* arg_representations =
|
||||
ArgumentRepresentations(signature);
|
||||
ZoneGrowableArray<Location>* arg_locations =
|
||||
ArgumentLocations(*arg_representations);
|
||||
|
||||
intptr_t num_dart_arguments = signature.num_fixed_parameters();
|
||||
intptr_t num_arguments = num_dart_arguments - 1; // ignore closure
|
||||
|
||||
__ EnterStubFrame();
|
||||
|
||||
// Save exit frame information to enable stack walking as we are about
|
||||
// to transition to Dart VM C++ code.
|
||||
__ movq(Address(THR, Thread::top_exit_frame_info_offset()), RBP);
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
Label ok;
|
||||
// Check that we are always entering from Dart code.
|
||||
__ movq(TMP, Immediate(VMTag::kDartCompiledTagId));
|
||||
__ cmpq(TMP, Assembler::VMTagAddress());
|
||||
__ j(EQUAL, &ok, Assembler::kNearJump);
|
||||
__ Stop("Not coming from Dart code.");
|
||||
__ Bind(&ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Reserve space for arguments and align frame before entering C++ world.
|
||||
__ subq(RSP, Immediate(NumStackArguments(*arg_locations) * kWordSize));
|
||||
if (OS::ActivationFrameAlignment() > 1) {
|
||||
__ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
|
||||
}
|
||||
|
||||
// Prepare address for calling the C function.
|
||||
Address closure_dart = Address(RBP, (num_dart_arguments + 1) * kWordSize);
|
||||
__ movq(RBX, closure_dart);
|
||||
__ movq(RBX, FieldAddress(RBX, Closure::context_offset()));
|
||||
__ movq(RBX, FieldAddress(RBX, Context::variable_offset(0)));
|
||||
GenerateMarshalInt64(assembler, RBX); // Address is a Smi or Mint.
|
||||
|
||||
// Marshal arguments and store in the right register.
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
Representation rep = arg_representations->At(i);
|
||||
Location loc = arg_locations->At(i);
|
||||
|
||||
// We do marshalling in the the target register or in RAX.
|
||||
Register reg = loc.IsRegister() ? loc.reg() : RAX;
|
||||
// For doubles and floats we use target xmm register or first non param reg.
|
||||
FpuRegister xmm_reg = loc.IsFpuRegister()
|
||||
? loc.fpu_reg()
|
||||
: CallingConventions::xmmFirstNonParameterReg;
|
||||
|
||||
// Load parameter from Dart stack.
|
||||
__ movq(reg, Address(RBP, (num_arguments + 1 - i) * kWordSize));
|
||||
|
||||
// Marshal argument.
|
||||
AbstractType& arg_type =
|
||||
AbstractType::Handle(signature.ParameterTypeAt(i + 1));
|
||||
GenerateMarshalArgument(assembler, arg_type, reg, xmm_reg);
|
||||
|
||||
// Store marshalled argument where c expects value.
|
||||
if (loc.IsStackSlot()) {
|
||||
if (rep == kUnboxedDouble) {
|
||||
__ movq(reg, xmm_reg);
|
||||
}
|
||||
__ movq(loc.ToStackSlotAddress(), reg);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark that the thread is executing VM code.
|
||||
__ movq(Assembler::VMTagAddress(), RBX);
|
||||
|
||||
__ CallCFunction(RBX);
|
||||
|
||||
// Mark that the thread is executing Dart code.
|
||||
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartCompiledTagId));
|
||||
|
||||
// Unmarshal result.
|
||||
AbstractType& return_type = AbstractType::Handle(signature.result_type());
|
||||
GenerateUnmarshalResult(assembler, return_type, closure_dart);
|
||||
|
||||
// Reset exit frame information in Isolate structure.
|
||||
__ movq(Address(THR, Thread::top_exit_frame_info_offset()), Immediate(0));
|
||||
|
||||
__ LeaveStubFrame();
|
||||
|
||||
__ ret();
|
||||
}
|
||||
|
||||
void GenerateFfiInverseTrampoline(Assembler* assembler,
|
||||
const Function& signature,
|
||||
void* dart_entry_point) {
|
||||
ZoneGrowableArray<Representation>* arg_representations =
|
||||
ArgumentRepresentations(signature);
|
||||
ZoneGrowableArray<Location>* arg_locations =
|
||||
ArgumentLocations(*arg_representations);
|
||||
|
||||
intptr_t num_dart_arguments = signature.num_fixed_parameters();
|
||||
intptr_t num_arguments = num_dart_arguments - 1; // Ignore closure.
|
||||
|
||||
// TODO(dacoharkes): Implement this.
|
||||
// https://github.com/dart-lang/sdk/issues/35761
|
||||
// Look at StubCode::GenerateInvokeDartCodeStub.
|
||||
|
||||
__ int3();
|
||||
|
||||
for (intptr_t i = 0; i < num_arguments; i++) {
|
||||
Register reg = arg_locations->At(i).reg();
|
||||
__ SmiTag(reg);
|
||||
}
|
||||
|
||||
__ movq(RBX, Immediate(reinterpret_cast<intptr_t>(dart_entry_point)));
|
||||
|
||||
__ int3();
|
||||
|
||||
__ call(RBX);
|
||||
|
||||
__ int3();
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined(TARGET_ARCH_X64) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
@ -92,6 +92,7 @@ constexpr bool kDartPrecompiledRuntime = false;
|
|||
"Compile expressions with the Kernel front-end.") \
|
||||
P(enable_mirrors, bool, true, \
|
||||
"Disable to make importing dart:mirrors an error.") \
|
||||
P(enable_ffi, bool, true, "Disable to make importing dart:ffi an error.") \
|
||||
P(fields_may_be_reset, bool, false, \
|
||||
"Don't optimize away static field initialization") \
|
||||
C(force_clone_compiler_objects, false, false, bool, false, \
|
||||
|
|
|
@ -1165,6 +1165,10 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
|
|||
target_library.url() == Symbols::DartMirrors().raw()) {
|
||||
H.ReportError("import of dart:mirrors with --enable-mirrors=false");
|
||||
}
|
||||
if (!Api::ffiEnabled() &&
|
||||
target_library.url() == Symbols::DartFfi().raw()) {
|
||||
H.ReportError("import of dart:ffi with --enable-ffi=false");
|
||||
}
|
||||
String& prefix = H.DartSymbolPlain(dependency_helper.name_index_);
|
||||
ns = Namespace::New(target_library, show_names, hide_names);
|
||||
if (dependency_helper.flags_ & LibraryDependencyHelper::Export) {
|
||||
|
|
|
@ -155,7 +155,7 @@ class NativeArguments {
|
|||
// null vector represents infinite list of dynamics
|
||||
return Type::dynamic_type().raw();
|
||||
}
|
||||
return TypeArguments::Handle(NativeTypeArgs()).TypeAt(index);
|
||||
return type_args.TypeAt(index);
|
||||
}
|
||||
|
||||
void SetReturn(const Object& value) const { *retval_ = value.raw(); }
|
||||
|
|
|
@ -20,6 +20,14 @@
|
|||
|
||||
namespace dart {
|
||||
|
||||
void DartNativeThrowTypeArgumentCountException(int num_type_args,
|
||||
int num_type_args_expected) {
|
||||
const String& error = String::Handle(String::NewFormatted(
|
||||
"Wrong number of type arguments (%i), expected %i type arguments",
|
||||
num_type_args, num_type_args_expected));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
|
||||
void DartNativeThrowArgumentException(const Instance& instance) {
|
||||
const Array& __args__ = Array::Handle(Array::New(1));
|
||||
__args__.SetAt(0, instance);
|
||||
|
|
|
@ -73,9 +73,22 @@ class String;
|
|||
static RawObject* DN_Helper##name(Isolate* isolate, Thread* thread, \
|
||||
Zone* zone, NativeArguments* arguments)
|
||||
|
||||
// Helper that throws an argument exception.
|
||||
// Helpers that throw an argument exception.
|
||||
void DartNativeThrowTypeArgumentCountException(int num_type_args,
|
||||
int num_type_args_expected);
|
||||
void DartNativeThrowArgumentException(const Instance& instance);
|
||||
|
||||
// Native should throw an exception if the wrong number of type arguments is
|
||||
// passed.
|
||||
#define NATIVE_TYPE_ARGUMENT_COUNT(expected) \
|
||||
int __num_type_arguments = arguments->NativeTypeArgCount(); \
|
||||
if (__num_type_arguments != expected) { \
|
||||
DartNativeThrowTypeArgumentCountException(__num_type_arguments, expected); \
|
||||
}
|
||||
|
||||
#define GET_NATIVE_TYPE_ARGUMENT(name, value) \
|
||||
AbstractType& name = AbstractType::Handle(value);
|
||||
|
||||
// Natives should throw an exception if an illegal argument or null is passed.
|
||||
// type name = value.
|
||||
#define GET_NON_NULL_NATIVE_ARGUMENT(type, name, value) \
|
||||
|
|
|
@ -128,6 +128,8 @@ RawClass* Object::closure_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
|||
RawClass* Object::signature_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
RawClass* Object::redirection_data_class_ =
|
||||
reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
RawClass* Object::ffi_trampoline_data_class_ =
|
||||
reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
RawClass* Object::field_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
RawClass* Object::script_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
RawClass* Object::library_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
|
@ -582,6 +584,9 @@ void Object::Init(Isolate* isolate) {
|
|||
cls = Class::New<RedirectionData>();
|
||||
redirection_data_class_ = cls.raw();
|
||||
|
||||
cls = Class::New<FfiTrampolineData>();
|
||||
ffi_trampoline_data_class_ = cls.raw();
|
||||
|
||||
cls = Class::New<Field>();
|
||||
field_class_ = cls.raw();
|
||||
|
||||
|
@ -957,6 +962,7 @@ void Object::Cleanup() {
|
|||
closure_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
signature_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
redirection_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
ffi_trampoline_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
field_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
script_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
library_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
|
||||
|
@ -1056,6 +1062,7 @@ void Object::FinalizeVMIsolate(Isolate* isolate) {
|
|||
SET_CLASS_NAME(closure_data, ClosureData);
|
||||
SET_CLASS_NAME(signature_data, SignatureData);
|
||||
SET_CLASS_NAME(redirection_data, RedirectionData);
|
||||
SET_CLASS_NAME(ffi_trampoline_data, FfiTrampolineData);
|
||||
SET_CLASS_NAME(field, Field);
|
||||
SET_CLASS_NAME(script, Script);
|
||||
SET_CLASS_NAME(library, LibraryClass);
|
||||
|
@ -1814,6 +1821,51 @@ RawError* Object::Init(Isolate* isolate,
|
|||
type_args = type_args.Canonicalize();
|
||||
object_store->set_type_argument_string_string(type_args);
|
||||
|
||||
lib = Library::LookupLibrary(thread, Symbols::DartFfi());
|
||||
if (lib.IsNull()) {
|
||||
lib = Library::NewLibraryHelper(Symbols::DartFfi(), true);
|
||||
lib.SetLoadRequested();
|
||||
lib.Register(thread);
|
||||
}
|
||||
object_store->set_bootstrap_library(ObjectStore::kFfi, lib);
|
||||
|
||||
cls = Class::New<Instance>(kFfiNativeTypeCid);
|
||||
cls.set_num_type_arguments(0);
|
||||
cls.set_num_own_type_arguments(0);
|
||||
cls.set_is_prefinalized();
|
||||
pending_classes.Add(cls);
|
||||
object_store->set_ffi_native_type_class(cls);
|
||||
RegisterClass(cls, Symbols::FfiNativeType(), lib);
|
||||
|
||||
#define REGISTER_FFI_TYPE_MARKER(clazz) \
|
||||
cls = Class::New<Instance>(kFfi##clazz##Cid); \
|
||||
cls.set_num_type_arguments(0); \
|
||||
cls.set_num_own_type_arguments(0); \
|
||||
cls.set_is_prefinalized(); \
|
||||
pending_classes.Add(cls); \
|
||||
RegisterClass(cls, Symbols::Ffi##clazz(), lib);
|
||||
CLASS_LIST_FFI_TYPE_MARKER(REGISTER_FFI_TYPE_MARKER);
|
||||
#undef REGISTER_FFI_TYPE_MARKER
|
||||
|
||||
cls = Class::New<Instance>(kFfiNativeFunctionCid);
|
||||
cls.set_type_arguments_field_offset(Pointer::type_arguments_offset());
|
||||
cls.set_num_type_arguments(1);
|
||||
cls.set_num_own_type_arguments(1);
|
||||
cls.set_is_prefinalized();
|
||||
pending_classes.Add(cls);
|
||||
RegisterClass(cls, Symbols::FfiNativeFunction(), lib);
|
||||
|
||||
cls = Class::NewPointerClass(kFfiPointerCid);
|
||||
object_store->set_ffi_pointer_class(cls);
|
||||
pending_classes.Add(cls);
|
||||
RegisterClass(cls, Symbols::FfiPointer(), lib);
|
||||
|
||||
cls = Class::New<DynamicLibrary>(kFfiDynamicLibraryCid);
|
||||
cls.set_instance_size(DynamicLibrary::InstanceSize());
|
||||
cls.set_is_prefinalized();
|
||||
pending_classes.Add(cls);
|
||||
RegisterClass(cls, Symbols::FfiDynamicLibrary(), lib);
|
||||
|
||||
// Finish the initialization by compiling the bootstrap scripts containing
|
||||
// the base interfaces and the implementation of the internal classes.
|
||||
const Error& error = Error::Handle(
|
||||
|
@ -1898,6 +1950,20 @@ RawError* Object::Init(Isolate* isolate,
|
|||
CLASS_LIST_TYPED_DATA(REGISTER_EXT_TYPED_DATA_CLASS);
|
||||
#undef REGISTER_EXT_TYPED_DATA_CLASS
|
||||
|
||||
cls = Class::New<Instance>(kFfiNativeTypeCid);
|
||||
object_store->set_ffi_native_type_class(cls);
|
||||
|
||||
#define REGISTER_FFI_CLASS(clazz) cls = Class::New<Instance>(kFfi##clazz##Cid);
|
||||
CLASS_LIST_FFI_TYPE_MARKER(REGISTER_FFI_CLASS);
|
||||
#undef REGISTER_FFI_CLASS
|
||||
|
||||
cls = Class::New<Instance>(kFfiNativeFunctionCid);
|
||||
|
||||
cls = Class::NewPointerClass(kFfiPointerCid);
|
||||
object_store->set_ffi_pointer_class(cls);
|
||||
|
||||
cls = Class::New<DynamicLibrary>(kFfiDynamicLibraryCid);
|
||||
|
||||
cls = Class::New<Instance>(kByteBufferCid);
|
||||
|
||||
cls = Class::New<Integer>();
|
||||
|
@ -3660,6 +3726,17 @@ RawClass* Class::NewExternalTypedDataClass(intptr_t class_id) {
|
|||
return result.raw();
|
||||
}
|
||||
|
||||
RawClass* Class::NewPointerClass(intptr_t class_id) {
|
||||
ASSERT(RawObject::IsFfiPointerClassId(class_id));
|
||||
intptr_t instance_size = Pointer::InstanceSize();
|
||||
Class& result = Class::Handle(New<Pointer>(class_id));
|
||||
result.set_instance_size(instance_size);
|
||||
result.set_type_arguments_field_offset(Pointer::type_arguments_offset());
|
||||
result.set_next_field_offset(Pointer::NextFieldOffset());
|
||||
result.set_is_prefinalized();
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
void Class::set_name(const String& value) const {
|
||||
ASSERT(raw_ptr()->name_ == String::null());
|
||||
ASSERT(value.IsSymbol());
|
||||
|
@ -3733,6 +3810,11 @@ RawString* Class::GenerateUserVisibleName() const {
|
|||
case kExternalTypedDataFloat64ArrayCid:
|
||||
return Symbols::Float64List().raw();
|
||||
|
||||
case kFfiPointerCid:
|
||||
return Symbols::FfiPointer().raw();
|
||||
case kFfiDynamicLibraryCid:
|
||||
return Symbols::FfiDynamicLibrary().raw();
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
case kNullCid:
|
||||
return Symbols::Null().raw();
|
||||
|
@ -3754,6 +3836,8 @@ RawString* Class::GenerateUserVisibleName() const {
|
|||
return Symbols::SignatureData().raw();
|
||||
case kRedirectionDataCid:
|
||||
return Symbols::RedirectionData().raw();
|
||||
case kFfiTrampolineDataCid:
|
||||
return Symbols::FfiTrampolineData().raw();
|
||||
case kFieldCid:
|
||||
return Symbols::Field().raw();
|
||||
case kScriptCid:
|
||||
|
@ -5014,9 +5098,19 @@ intptr_t TypeArguments::Length() const {
|
|||
}
|
||||
|
||||
RawAbstractType* TypeArguments::TypeAt(intptr_t index) const {
|
||||
ASSERT(!IsNull());
|
||||
return *TypeAddr(index);
|
||||
}
|
||||
|
||||
RawAbstractType* TypeArguments::TypeAtNullSafe(intptr_t index) const {
|
||||
if (IsNull()) {
|
||||
// null vector represents infinite list of dynamics
|
||||
return Type::dynamic_type().raw();
|
||||
}
|
||||
ASSERT((index >= 0) && (index < Length()));
|
||||
return TypeAt(index);
|
||||
}
|
||||
|
||||
void TypeArguments::SetTypeAt(intptr_t index, const AbstractType& value) const {
|
||||
ASSERT(!IsCanonical());
|
||||
StorePointer(TypeAddr(index), value.raw());
|
||||
|
@ -5884,9 +5978,11 @@ RawType* Function::ExistingSignatureType() const {
|
|||
ASSERT(!obj.IsNull());
|
||||
if (IsSignatureFunction()) {
|
||||
return SignatureData::Cast(obj).signature_type();
|
||||
} else {
|
||||
ASSERT(IsClosureFunction());
|
||||
} else if (IsClosureFunction()) {
|
||||
return ClosureData::Cast(obj).signature_type();
|
||||
} else {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
return FfiTrampolineData::Cast(obj).signature_type();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5937,9 +6033,11 @@ void Function::SetSignatureType(const Type& value) const {
|
|||
if (IsSignatureFunction()) {
|
||||
SignatureData::Cast(obj).set_signature_type(value);
|
||||
ASSERT(!value.IsCanonical() || (value.signature() == this->raw()));
|
||||
} else {
|
||||
ASSERT(IsClosureFunction());
|
||||
} else if (IsClosureFunction()) {
|
||||
ClosureData::Cast(obj).set_signature_type(value);
|
||||
} else {
|
||||
ASSERT(IsFfiTrampoline());
|
||||
FfiTrampolineData::Cast(obj).set_signature_type(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6074,6 +6172,7 @@ void Function::SetRedirectionTarget(const Function& target) const {
|
|||
// native function: Array[0] = String native name
|
||||
// Array[1] = Function implicit closure function
|
||||
// regular function: Function for implicit closure function
|
||||
// ffi trampoline function: FfiTrampolineData (Dart->C)
|
||||
void Function::set_data(const Object& value) const {
|
||||
StorePointer(&raw_ptr()->data_, value.raw());
|
||||
}
|
||||
|
@ -6387,7 +6486,8 @@ intptr_t Function::NumImplicitParameters() const {
|
|||
}
|
||||
if ((k == RawFunction::kClosureFunction) ||
|
||||
(k == RawFunction::kImplicitClosureFunction) ||
|
||||
(k == RawFunction::kSignatureFunction)) {
|
||||
(k == RawFunction::kSignatureFunction) ||
|
||||
(k == RawFunction::kFfiTrampoline)) {
|
||||
return 1; // Closure object.
|
||||
}
|
||||
if (!is_static()) {
|
||||
|
@ -7076,6 +7176,10 @@ RawFunction* Function::New(const String& name,
|
|||
const SignatureData& data =
|
||||
SignatureData::Handle(SignatureData::New(space));
|
||||
result.set_data(data);
|
||||
} else if (kind == RawFunction::kFfiTrampoline) {
|
||||
const FfiTrampolineData& data =
|
||||
FfiTrampolineData::Handle(FfiTrampolineData::New());
|
||||
result.set_data(data);
|
||||
} else {
|
||||
// Functions other than signature functions have no reason to be allocated
|
||||
// in new space.
|
||||
|
@ -8015,6 +8119,27 @@ const char* RedirectionData::ToCString() const {
|
|||
target_fun.IsNull() ? "null" : target_fun.ToCString());
|
||||
}
|
||||
|
||||
void FfiTrampolineData::set_signature_type(const Type& value) const {
|
||||
StorePointer(&raw_ptr()->signature_type_, value.raw());
|
||||
}
|
||||
|
||||
RawFfiTrampolineData* FfiTrampolineData::New() {
|
||||
ASSERT(Object::ffi_trampoline_data_class() != Class::null());
|
||||
RawObject* raw =
|
||||
Object::Allocate(FfiTrampolineData::kClassId,
|
||||
FfiTrampolineData::InstanceSize(), Heap::kOld);
|
||||
return reinterpret_cast<RawFfiTrampolineData*>(raw);
|
||||
}
|
||||
|
||||
const char* FfiTrampolineData::ToCString() const {
|
||||
Type& signature_type = Type::Handle(this->signature_type());
|
||||
String& signature_type_name =
|
||||
String::Handle(signature_type.UserVisibleName());
|
||||
return OS::SCreate(
|
||||
Thread::Current()->zone(), "TrampolineData: signature=%s",
|
||||
signature_type_name.IsNull() ? "null" : signature_type_name.ToCString());
|
||||
}
|
||||
|
||||
RawField* Field::CloneFromOriginal() const {
|
||||
return this->Clone(*this);
|
||||
}
|
||||
|
@ -11181,6 +11306,10 @@ RawLibrary* Library::DeveloperLibrary() {
|
|||
return Isolate::Current()->object_store()->developer_library();
|
||||
}
|
||||
|
||||
RawLibrary* Library::FfiLibrary() {
|
||||
return Isolate::Current()->object_store()->ffi_library();
|
||||
}
|
||||
|
||||
RawLibrary* Library::InternalLibrary() {
|
||||
return Isolate::Current()->object_store()->_internal_library();
|
||||
}
|
||||
|
@ -20688,6 +20817,75 @@ const char* ExternalTypedData::ToCString() const {
|
|||
return "ExternalTypedData";
|
||||
}
|
||||
|
||||
RawPointer* Pointer::New(const AbstractType& type_arg,
|
||||
uint8_t* c_memory_address,
|
||||
intptr_t cid,
|
||||
Heap::Space space) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
TypeArguments& type_args = TypeArguments::Handle(zone);
|
||||
type_args = TypeArguments::New(1);
|
||||
type_args.SetTypeAt(Pointer::kNativeTypeArgPos, type_arg);
|
||||
type_args ^= type_args.Canonicalize();
|
||||
|
||||
const Class& cls = Class::Handle(Isolate::Current()->class_table()->At(cid));
|
||||
cls.EnsureIsFinalized(Thread::Current());
|
||||
|
||||
Pointer& result = Pointer::Handle(zone);
|
||||
result ^= Object::Allocate(cid, Pointer::InstanceSize(), space);
|
||||
NoSafepointScope no_safepoint;
|
||||
result.SetTypeArguments(type_args);
|
||||
result.SetCMemoryAddress(c_memory_address);
|
||||
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
const char* Pointer::ToCString() const {
|
||||
TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments());
|
||||
String& type_args_name = String::Handle(type_args.UserVisibleName());
|
||||
return OS::SCreate(Thread::Current()->zone(), "Pointer%s: address=%p",
|
||||
type_args_name.ToCString(), GetCMemoryAddress());
|
||||
}
|
||||
|
||||
RawDynamicLibrary* DynamicLibrary::New(void* handle, Heap::Space space) {
|
||||
DynamicLibrary& result = DynamicLibrary::Handle();
|
||||
result ^= Object::Allocate(kFfiDynamicLibraryCid,
|
||||
DynamicLibrary::InstanceSize(), space);
|
||||
NoSafepointScope no_safepoint;
|
||||
result.SetHandle(handle);
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
bool Pointer::IsPointer(const Instance& obj) {
|
||||
ASSERT(!obj.IsNull());
|
||||
|
||||
// fast path for predefined classes
|
||||
intptr_t cid = obj.raw()->GetClassId();
|
||||
if (RawObject::IsFfiPointerClassId(cid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// slow check for subtyping
|
||||
const Class& pointer_class = Class::ZoneHandle(
|
||||
Isolate::Current()->object_store()->ffi_pointer_class());
|
||||
AbstractType& pointer_type =
|
||||
AbstractType::Handle(pointer_class.DeclarationType());
|
||||
pointer_type ^= pointer_type.InstantiateFrom(Object::null_type_arguments(),
|
||||
Object::null_type_arguments(),
|
||||
kNoneFree, NULL, Heap::kNew);
|
||||
AbstractType& type = AbstractType::Handle(obj.GetType(Heap::kNew));
|
||||
return type.IsSubtypeOf(pointer_type, Heap::kNew);
|
||||
}
|
||||
|
||||
bool Instance::IsPointer() const {
|
||||
return Pointer::IsPointer(*this);
|
||||
}
|
||||
|
||||
const char* DynamicLibrary::ToCString() const {
|
||||
return OS::SCreate(Thread::Current()->zone(), "DynamicLibrary: handle=%p",
|
||||
GetHandle());
|
||||
}
|
||||
|
||||
RawCapability* Capability::New(uint64_t id, Heap::Space space) {
|
||||
Capability& result = Capability::Handle();
|
||||
{
|
||||
|
|
|
@ -431,6 +431,9 @@ class Object {
|
|||
static RawClass* closure_data_class() { return closure_data_class_; }
|
||||
static RawClass* signature_data_class() { return signature_data_class_; }
|
||||
static RawClass* redirection_data_class() { return redirection_data_class_; }
|
||||
static RawClass* ffi_trampoline_data_class() {
|
||||
return ffi_trampoline_data_class_;
|
||||
}
|
||||
static RawClass* field_class() { return field_class_; }
|
||||
static RawClass* script_class() { return script_class_; }
|
||||
static RawClass* library_class() { return library_class_; }
|
||||
|
@ -678,6 +681,8 @@ class Object {
|
|||
static RawClass* closure_data_class_; // Class of ClosureData vm obj.
|
||||
static RawClass* signature_data_class_; // Class of SignatureData vm obj.
|
||||
static RawClass* redirection_data_class_; // Class of RedirectionData vm obj.
|
||||
static RawClass* ffi_trampoline_data_class_; // Class of FfiTrampolineData
|
||||
// vm obj.
|
||||
static RawClass* field_class_; // Class of the Field vm object.
|
||||
static RawClass* script_class_; // Class of the Script vm object.
|
||||
static RawClass* library_class_; // Class of the Library vm object.
|
||||
|
@ -1257,6 +1262,9 @@ class Class : public Object {
|
|||
// Allocate the raw ExternalTypedData classes.
|
||||
static RawClass* NewExternalTypedDataClass(intptr_t class_id);
|
||||
|
||||
// Allocate the raw Pointer classes.
|
||||
static RawClass* NewPointerClass(intptr_t class_id);
|
||||
|
||||
// Register code that has used CHA for optimization.
|
||||
// TODO(srdjan): Also register kind of CHA optimization (e.g.: leaf class,
|
||||
// leaf method, ...).
|
||||
|
@ -2125,6 +2133,10 @@ class Function : public Object {
|
|||
|
||||
static intptr_t code_offset() { return OFFSET_OF(RawFunction, code_); }
|
||||
|
||||
static intptr_t result_type_offset() {
|
||||
return OFFSET_OF(RawFunction, result_type_);
|
||||
}
|
||||
|
||||
static intptr_t entry_point_offset() {
|
||||
return OFFSET_OF(RawFunction, entry_point_);
|
||||
}
|
||||
|
@ -2551,6 +2563,14 @@ class Function : public Object {
|
|||
RawFunction::kSignatureFunction;
|
||||
}
|
||||
|
||||
// Returns true if this function represents an ffi trampoline.
|
||||
bool IsFfiTrampoline() const { return kind() == RawFunction::kFfiTrampoline; }
|
||||
static bool IsFfiTrampoline(RawFunction* function) {
|
||||
NoSafepointScope no_safepoint;
|
||||
return KindBits::decode(function->ptr()->kind_tag_) ==
|
||||
RawFunction::kFfiTrampoline;
|
||||
}
|
||||
|
||||
bool IsAsyncFunction() const { return modifier() == RawFunction::kAsync; }
|
||||
|
||||
bool IsAsyncClosure() const {
|
||||
|
@ -2952,6 +2972,25 @@ class RedirectionData : public Object {
|
|||
|
||||
enum class EntryPointPragma { kAlways, kNever, kGetterOnly, kSetterOnly };
|
||||
|
||||
class FfiTrampolineData : public Object {
|
||||
public:
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(RawFfiTrampolineData));
|
||||
}
|
||||
|
||||
private:
|
||||
// Signature type of this closure function.
|
||||
RawType* signature_type() const { return raw_ptr()->signature_type_; }
|
||||
void set_signature_type(const Type& value) const;
|
||||
|
||||
static RawFfiTrampolineData* New();
|
||||
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData, Object);
|
||||
friend class Class;
|
||||
friend class Function;
|
||||
friend class HeapProfiler;
|
||||
};
|
||||
|
||||
class Field : public Object {
|
||||
public:
|
||||
RawField* Original() const;
|
||||
|
@ -3702,6 +3741,7 @@ class Library : public Object {
|
|||
static RawLibrary* CoreLibrary();
|
||||
static RawLibrary* CollectionLibrary();
|
||||
static RawLibrary* DeveloperLibrary();
|
||||
static RawLibrary* FfiLibrary();
|
||||
static RawLibrary* InternalLibrary();
|
||||
static RawLibrary* IsolateLibrary();
|
||||
static RawLibrary* MathLibrary();
|
||||
|
@ -5735,6 +5775,12 @@ class Instance : public Object {
|
|||
static intptr_t DataOffsetFor(intptr_t cid);
|
||||
static intptr_t ElementSizeFor(intptr_t cid);
|
||||
|
||||
// Pointers may be subtyped, but their subtypes may not get extra fields.
|
||||
// The subtype runtime representation has exactly the same object layout,
|
||||
// only the class_id is different. So, it is safe to use subtype instances in
|
||||
// Pointer handles.
|
||||
virtual bool IsPointer() const;
|
||||
|
||||
static intptr_t NextFieldOffset() { return sizeof(RawInstance); }
|
||||
|
||||
protected:
|
||||
|
@ -5775,6 +5821,7 @@ class Instance : public Object {
|
|||
friend class ByteBuffer;
|
||||
friend class Class;
|
||||
friend class Closure;
|
||||
friend class Pointer;
|
||||
friend class DeferredObject;
|
||||
friend class RegExp;
|
||||
friend class SnapshotWriter;
|
||||
|
@ -5853,6 +5900,7 @@ class TypeArguments : public Instance {
|
|||
|
||||
intptr_t Length() const;
|
||||
RawAbstractType* TypeAt(intptr_t index) const;
|
||||
RawAbstractType* TypeAtNullSafe(intptr_t index) const;
|
||||
static intptr_t type_at_offset(intptr_t index) {
|
||||
return OFFSET_OF_RETURNED_VALUE(RawTypeArguments, types) +
|
||||
index * kWordSize;
|
||||
|
@ -8407,6 +8455,81 @@ class ByteBuffer : public AllStatic {
|
|||
};
|
||||
};
|
||||
|
||||
class Pointer : public Instance {
|
||||
public:
|
||||
static RawPointer* New(const AbstractType& type_arg,
|
||||
uint8_t* c_memory_address,
|
||||
intptr_t class_id = kFfiPointerCid,
|
||||
Heap::Space space = Heap::kNew);
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(RawPointer));
|
||||
}
|
||||
|
||||
static bool IsPointer(const Instance& obj);
|
||||
|
||||
uint8_t* GetCMemoryAddress() const {
|
||||
ASSERT(!IsNull());
|
||||
return raw_ptr()->c_memory_address_;
|
||||
}
|
||||
|
||||
void SetCMemoryAddress(uint8_t* value) const {
|
||||
StoreNonPointer(&raw_ptr()->c_memory_address_, value);
|
||||
}
|
||||
|
||||
static intptr_t type_arguments_offset() {
|
||||
return OFFSET_OF(RawPointer, type_arguments_);
|
||||
}
|
||||
|
||||
static intptr_t address_offset() {
|
||||
return OFFSET_OF(RawPointer, c_memory_address_);
|
||||
}
|
||||
|
||||
static intptr_t NextFieldOffset() { return sizeof(RawPointer); }
|
||||
|
||||
static const intptr_t kNativeTypeArgPos = 0;
|
||||
|
||||
// Fetches the NativeType type argument.
|
||||
RawAbstractType* type_argument() const {
|
||||
TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments());
|
||||
return type_args.TypeAtNullSafe(Pointer::kNativeTypeArgPos);
|
||||
}
|
||||
|
||||
private:
|
||||
HEAP_OBJECT_IMPLEMENTATION(Pointer, Instance);
|
||||
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
class DynamicLibrary : public Instance {
|
||||
public:
|
||||
static RawDynamicLibrary* New(void* handle, Heap::Space space = Heap::kNew);
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(RawDynamicLibrary));
|
||||
}
|
||||
|
||||
static bool IsDynamicLibrary(const Instance& obj) {
|
||||
ASSERT(!obj.IsNull());
|
||||
intptr_t cid = obj.raw()->GetClassId();
|
||||
return RawObject::IsFfiDynamicLibraryClassId(cid);
|
||||
}
|
||||
|
||||
void* GetHandle() const {
|
||||
ASSERT(!IsNull());
|
||||
return raw_ptr()->handle_;
|
||||
}
|
||||
|
||||
void SetHandle(void* value) const {
|
||||
StoreNonPointer(&raw_ptr()->handle_, value);
|
||||
}
|
||||
|
||||
private:
|
||||
FINAL_HEAP_OBJECT_IMPLEMENTATION(DynamicLibrary, Instance);
|
||||
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
// Corresponds to
|
||||
// - "new Map()",
|
||||
// - non-const map literals, and
|
||||
|
|
|
@ -349,6 +349,10 @@ void RedirectionData::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
|||
Object::PrintJSONImpl(stream, ref);
|
||||
}
|
||||
|
||||
void FfiTrampolineData::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
Object::PrintJSONImpl(stream, ref);
|
||||
}
|
||||
|
||||
void Field::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
JSONObject jsobj(stream);
|
||||
Class& cls = Class::Handle(Owner());
|
||||
|
@ -1416,6 +1420,20 @@ void ExternalTypedData::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
|||
}
|
||||
}
|
||||
|
||||
void Pointer::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
// TODO(dacoharkes): what is the JSONStream used for?
|
||||
// should it fail because it's not supported?
|
||||
// or should it print something reasonable as default?
|
||||
Instance::PrintJSONImpl(stream, ref);
|
||||
}
|
||||
|
||||
void DynamicLibrary::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
// TODO(dacoharkes): what is the JSONStream used for?
|
||||
// should it fail because it's not supported?
|
||||
// or should it print something reasonable as default?
|
||||
Instance::PrintJSONImpl(stream, ref);
|
||||
}
|
||||
|
||||
void Capability::PrintJSONImpl(JSONStream* stream, bool ref) const {
|
||||
Instance::PrintJSONImpl(stream, ref);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class ObjectPointerVisitor;
|
|||
M(Collection, collection) \
|
||||
M(Convert, convert) \
|
||||
M(Developer, developer) \
|
||||
M(Ffi, ffi) \
|
||||
M(Internal, _internal) \
|
||||
M(Isolate, isolate) \
|
||||
M(Math, math) \
|
||||
|
@ -89,6 +90,7 @@ class ObjectPointerVisitor;
|
|||
RW(Library, collection_library) \
|
||||
RW(Library, convert_library) \
|
||||
RW(Library, developer_library) \
|
||||
RW(Library, ffi_library) \
|
||||
RW(Library, _internal_library) \
|
||||
RW(Library, isolate_library) \
|
||||
RW(Library, math_library) \
|
||||
|
@ -137,6 +139,8 @@ class ObjectPointerVisitor;
|
|||
RW(Array, code_order_table) \
|
||||
RW(Array, obfuscation_map) \
|
||||
RW(GrowableObjectArray, changed_in_last_reload) \
|
||||
RW(Class, ffi_pointer_class) \
|
||||
RW(Class, ffi_native_type_class) \
|
||||
// Please remember the last entry must be referred in the 'to' function below.
|
||||
|
||||
// The object store is a per isolate instance which stores references to
|
||||
|
@ -229,7 +233,7 @@ class ObjectStore {
|
|||
DECLARE_OBJECT_STORE_FIELD)
|
||||
#undef DECLARE_OBJECT_STORE_FIELD
|
||||
RawObject** to() {
|
||||
return reinterpret_cast<RawObject**>(&changed_in_last_reload_);
|
||||
return reinterpret_cast<RawObject**>(&ffi_pointer_class_);
|
||||
}
|
||||
RawObject** to_snapshot(Snapshot::Kind kind) {
|
||||
switch (kind) {
|
||||
|
|
|
@ -147,6 +147,9 @@ intptr_t RawObject::HeapSizeFromClass() const {
|
|||
break;
|
||||
}
|
||||
#undef SIZE_FROM_CLASS
|
||||
case kFfiPointerCid:
|
||||
instance_size = Pointer::InstanceSize();
|
||||
break;
|
||||
case kTypeArgumentsCid: {
|
||||
const RawTypeArguments* raw_array =
|
||||
reinterpret_cast<const RawTypeArguments*>(this);
|
||||
|
@ -282,6 +285,16 @@ intptr_t RawObject::VisitPointersPredefined(ObjectPointerVisitor* visitor,
|
|||
break;
|
||||
}
|
||||
#undef RAW_VISITPOINTERS
|
||||
case kFfiPointerCid: {
|
||||
RawPointer* raw_obj = reinterpret_cast<RawPointer*>(this);
|
||||
size = RawPointer::VisitPointerPointers(raw_obj, visitor);
|
||||
break;
|
||||
}
|
||||
case kFfiDynamicLibraryCid: {
|
||||
RawDynamicLibrary* raw_obj = reinterpret_cast<RawDynamicLibrary*>(this);
|
||||
size = RawDynamicLibrary::VisitDynamicLibraryPointers(raw_obj, visitor);
|
||||
break;
|
||||
}
|
||||
case kFreeListElement: {
|
||||
uword addr = RawObject::ToAddr(this);
|
||||
FreeListElement* element = reinterpret_cast<FreeListElement*>(addr);
|
||||
|
@ -395,6 +408,7 @@ COMPRESSED_VISITOR(Closure)
|
|||
REGULAR_VISITOR(ClosureData)
|
||||
REGULAR_VISITOR(SignatureData)
|
||||
REGULAR_VISITOR(RedirectionData)
|
||||
REGULAR_VISITOR(FfiTrampolineData)
|
||||
REGULAR_VISITOR(Field)
|
||||
REGULAR_VISITOR(Script)
|
||||
REGULAR_VISITOR(Library)
|
||||
|
@ -439,6 +453,8 @@ NULL_VISITOR(Float64x2)
|
|||
NULL_VISITOR(Bool)
|
||||
NULL_VISITOR(Capability)
|
||||
NULL_VISITOR(SendPort)
|
||||
REGULAR_VISITOR(Pointer)
|
||||
NULL_VISITOR(DynamicLibrary)
|
||||
VARIABLE_NULL_VISITOR(Instructions, Instructions::Size(raw_obj))
|
||||
VARIABLE_NULL_VISITOR(PcDescriptors, raw_obj->ptr()->length_)
|
||||
VARIABLE_NULL_VISITOR(CodeSourceMap, raw_obj->ptr()->length_)
|
||||
|
|
|
@ -344,6 +344,11 @@ class RawObject {
|
|||
CLASS_LIST_TYPED_DATA(DEFINE_IS_CID)
|
||||
#undef DEFINE_IS_CID
|
||||
|
||||
#define DEFINE_IS_CID(clazz) \
|
||||
bool IsFfi##clazz() const { return ((GetClassId() == kFfi##clazz##Cid)); }
|
||||
CLASS_LIST_FFI(DEFINE_IS_CID)
|
||||
#undef DEFINE_IS_CID
|
||||
|
||||
bool IsStringInstance() const { return IsStringClassId(GetClassId()); }
|
||||
bool IsRawNull() const { return GetClassId() == kNullCid; }
|
||||
bool IsDartInstance() const {
|
||||
|
@ -471,6 +476,15 @@ class RawObject {
|
|||
static bool IsTypedDataClassId(intptr_t index);
|
||||
static bool IsTypedDataViewClassId(intptr_t index);
|
||||
static bool IsExternalTypedDataClassId(intptr_t index);
|
||||
static bool IsFfiNativeTypeTypeClassId(intptr_t index);
|
||||
static bool IsFfiPointerClassId(intptr_t index);
|
||||
static bool IsFfiTypeClassId(intptr_t index);
|
||||
static bool IsFfiTypeIntClassId(intptr_t index);
|
||||
static bool IsFfiTypeDoubleClassId(intptr_t index);
|
||||
static bool IsFfiTypeVoidClassId(intptr_t index);
|
||||
static bool IsFfiTypeNativeFunctionClassId(intptr_t index);
|
||||
static bool IsFfiDynamicLibraryClassId(intptr_t index);
|
||||
static bool IsFfiClassId(intptr_t index);
|
||||
static bool IsInternalVMdefinedClassId(intptr_t index);
|
||||
static bool IsVariableSizeClassId(intptr_t index);
|
||||
static bool IsImplicitFieldClassId(intptr_t index);
|
||||
|
@ -664,7 +678,9 @@ class RawObject {
|
|||
friend class CidRewriteVisitor;
|
||||
friend class Closure;
|
||||
friend class Code;
|
||||
friend class Pointer;
|
||||
friend class Double;
|
||||
friend class DynamicLibrary;
|
||||
friend class ForwardPointersVisitor; // StorePointer
|
||||
friend class FreeListElement;
|
||||
friend class Function;
|
||||
|
@ -855,6 +871,7 @@ class RawFunction : public RawObject {
|
|||
kDynamicInvocationForwarder, // represents forwarder which performs type
|
||||
// checks for arguments of a dynamic
|
||||
// invocation.
|
||||
kFfiTrampoline,
|
||||
};
|
||||
|
||||
enum AsyncModifier {
|
||||
|
@ -1002,6 +1019,15 @@ class RawRedirectionData : public RawObject {
|
|||
RawObject** to_snapshot(Snapshot::Kind kind) { return to(); }
|
||||
};
|
||||
|
||||
class RawFfiTrampolineData : public RawObject {
|
||||
private:
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData);
|
||||
|
||||
VISIT_FROM(RawObject*, signature_type_);
|
||||
RawType* signature_type_;
|
||||
VISIT_TO(RawObject*, signature_type_);
|
||||
};
|
||||
|
||||
class RawField : public RawObject {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Field);
|
||||
|
||||
|
@ -2213,6 +2239,24 @@ class RawExternalTypedData : public RawInstance {
|
|||
friend class RawBytecode;
|
||||
};
|
||||
|
||||
class RawPointer : public RawInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Pointer);
|
||||
VISIT_FROM(RawCompressed, type_arguments_)
|
||||
RawTypeArguments* type_arguments_;
|
||||
VISIT_TO(RawCompressed, type_arguments_)
|
||||
uint8_t* c_memory_address_;
|
||||
|
||||
friend class Pointer;
|
||||
};
|
||||
|
||||
class RawDynamicLibrary : public RawInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(DynamicLibrary);
|
||||
VISIT_NOTHING();
|
||||
void* handle_;
|
||||
|
||||
friend class DynamicLibrary;
|
||||
};
|
||||
|
||||
// VM implementations of the basic types in the isolate.
|
||||
class RawCapability : public RawInstance {
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(Capability);
|
||||
|
@ -2483,6 +2527,56 @@ inline bool RawObject::IsExternalTypedDataClassId(intptr_t index) {
|
|||
index <= kExternalTypedDataFloat64x2ArrayCid);
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiNativeTypeTypeClassId(intptr_t index) {
|
||||
return index == kFfiNativeTypeCid;
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiTypeClassId(intptr_t index) {
|
||||
// Make sure this is updated when new Ffi types are added.
|
||||
COMPILE_ASSERT(kFfiNativeFunctionCid == kFfiPointerCid + 1 &&
|
||||
kFfiInt8Cid == kFfiPointerCid + 2 &&
|
||||
kFfiInt16Cid == kFfiPointerCid + 3 &&
|
||||
kFfiInt32Cid == kFfiPointerCid + 4 &&
|
||||
kFfiInt64Cid == kFfiPointerCid + 5 &&
|
||||
kFfiUint8Cid == kFfiPointerCid + 6 &&
|
||||
kFfiUint16Cid == kFfiPointerCid + 7 &&
|
||||
kFfiUint32Cid == kFfiPointerCid + 8 &&
|
||||
kFfiUint64Cid == kFfiPointerCid + 9 &&
|
||||
kFfiIntPtrCid == kFfiPointerCid + 10 &&
|
||||
kFfiFloatCid == kFfiPointerCid + 11 &&
|
||||
kFfiDoubleCid == kFfiPointerCid + 12 &&
|
||||
kFfiVoidCid == kFfiPointerCid + 13);
|
||||
return (index >= kFfiPointerCid && index <= kFfiVoidCid);
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiTypeIntClassId(intptr_t index) {
|
||||
return (index >= kFfiInt8Cid && index <= kFfiIntPtrCid);
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiTypeDoubleClassId(intptr_t index) {
|
||||
return (index >= kFfiFloatCid && index <= kFfiDoubleCid);
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiPointerClassId(intptr_t index) {
|
||||
return index == kFfiPointerCid;
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiTypeVoidClassId(intptr_t index) {
|
||||
return index == kFfiVoidCid;
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiTypeNativeFunctionClassId(intptr_t index) {
|
||||
return index == kFfiNativeFunctionCid;
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiClassId(intptr_t index) {
|
||||
return (index >= kFfiPointerCid && index <= kFfiVoidCid);
|
||||
}
|
||||
|
||||
inline bool RawObject::IsFfiDynamicLibraryClassId(intptr_t index) {
|
||||
return index == kFfiDynamicLibraryCid;
|
||||
}
|
||||
|
||||
inline bool RawObject::IsInternalVMdefinedClassId(intptr_t index) {
|
||||
return ((index < kNumPredefinedCids) &&
|
||||
!RawObject::IsImplicitFieldClassId(index));
|
||||
|
|
|
@ -186,7 +186,11 @@ namespace dart {
|
|||
F(WeakProperty, key_) \
|
||||
F(WeakProperty, value_) \
|
||||
F(MirrorReference, referent_) \
|
||||
F(UserTag, label_)
|
||||
F(UserTag, label_) \
|
||||
F(Pointer, type_arguments_) \
|
||||
F(Pointer, c_memory_address_) \
|
||||
F(DynamicLibrary, handle_) \
|
||||
F(FfiTrampolineData, signature_type_)
|
||||
|
||||
OffsetsTable::OffsetsTable(Zone* zone) : cached_offsets_(zone) {
|
||||
for (intptr_t i = 0; offsets_table[i].class_id != -1; ++i) {
|
||||
|
|
|
@ -467,6 +467,22 @@ void RawRedirectionData::WriteTo(SnapshotWriter* writer,
|
|||
UNREACHABLE();
|
||||
}
|
||||
|
||||
RawFfiTrampolineData* FfiTrampolineData::ReadFrom(SnapshotReader* reader,
|
||||
intptr_t object_id,
|
||||
intptr_t tags,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
UNREACHABLE();
|
||||
return FfiTrampolineData::null();
|
||||
}
|
||||
|
||||
void RawFfiTrampolineData::WriteTo(SnapshotWriter* writer,
|
||||
intptr_t object_id,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
RawFunction* Function::ReadFrom(SnapshotReader* reader,
|
||||
intptr_t object_id,
|
||||
intptr_t tags,
|
||||
|
@ -1967,6 +1983,38 @@ void RawExternalTypedData::WriteTo(SnapshotWriter* writer,
|
|||
IsolateMessageTypedDataFinalizer);
|
||||
}
|
||||
|
||||
RawPointer* Pointer::ReadFrom(SnapshotReader* reader,
|
||||
intptr_t object_id,
|
||||
intptr_t tags,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
FATAL("Snapshotting Pointers is not supported");
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RawPointer::WriteTo(SnapshotWriter* writer,
|
||||
intptr_t object_id,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
FATAL("Snapshotting Pointers is not supported");
|
||||
}
|
||||
|
||||
RawDynamicLibrary* DynamicLibrary::ReadFrom(SnapshotReader* reader,
|
||||
intptr_t object_id,
|
||||
intptr_t tags,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
FATAL("Snapshotting DynamicLibraries is not supported");
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RawDynamicLibrary::WriteTo(SnapshotWriter* writer,
|
||||
intptr_t object_id,
|
||||
Snapshot::Kind kind,
|
||||
bool as_reference) {
|
||||
FATAL("Snapshotting DynamicLibraries is not supported");
|
||||
}
|
||||
|
||||
RawCapability* Capability::ReadFrom(SnapshotReader* reader,
|
||||
intptr_t object_id,
|
||||
intptr_t tags,
|
||||
|
|
|
@ -195,6 +195,11 @@ DEFINE_RUNTIME_ENTRY(NullErrorWithSelector, 1) {
|
|||
NullErrorHelper(zone, selector);
|
||||
}
|
||||
|
||||
DEFINE_RUNTIME_ENTRY(ArgumentNullError, 0) {
|
||||
const String& error = String::Handle(String::New("argument value is null"));
|
||||
Exceptions::ThrowArgumentError(error);
|
||||
}
|
||||
|
||||
DEFINE_RUNTIME_ENTRY(ArgumentError, 1) {
|
||||
const Instance& value = Instance::CheckedHandle(zone, arguments.ArgAt(0));
|
||||
Exceptions::ThrowArgumentError(value);
|
||||
|
|
|
@ -41,6 +41,7 @@ namespace dart {
|
|||
V(RangeError) \
|
||||
V(NullError) \
|
||||
V(NullErrorWithSelector) \
|
||||
V(ArgumentNullError) \
|
||||
V(ArgumentError) \
|
||||
V(ArgumentErrorUnboxedInt64) \
|
||||
V(IntegerDivisionByZeroException) \
|
||||
|
|
|
@ -4752,6 +4752,13 @@ static bool GetDefaultClassesAliases(Thread* thread, JSONStream* js) {
|
|||
}
|
||||
CLASS_LIST_TYPED_DATA(DEFINE_ADD_MAP_KEY)
|
||||
#undef DEFINE_ADD_MAP_KEY
|
||||
#define DEFINE_ADD_MAP_KEY(clazz) \
|
||||
{ \
|
||||
JSONArray internals(&map, #clazz); \
|
||||
DEFINE_ADD_VALUE_F_CID(Ffi##clazz) \
|
||||
}
|
||||
CLASS_LIST_FFI(DEFINE_ADD_MAP_KEY)
|
||||
#undef DEFINE_ADD_MAP_KEY
|
||||
#undef DEFINE_ADD_VALUE_F_CID
|
||||
#undef DEFINE_ADD_VALUE_F
|
||||
|
||||
|
|
|
@ -482,6 +482,10 @@ RawObject* SnapshotReader::ReadObjectImpl(intptr_t header_value,
|
|||
pobj_ = ExternalTypedData::ReadFrom(this, object_id, tags, kind_, true);
|
||||
break;
|
||||
}
|
||||
#undef SNAPSHOT_READ
|
||||
#define SNAPSHOT_READ(clazz) case kFfi##clazz##Cid:
|
||||
|
||||
CLASS_LIST_FFI(SNAPSHOT_READ) { UNREACHABLE(); }
|
||||
#undef SNAPSHOT_READ
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -1174,6 +1178,10 @@ void SnapshotWriter::WriteMarkedObjectImpl(RawObject* raw,
|
|||
raw_obj->WriteTo(this, object_id, kind_, as_reference);
|
||||
return;
|
||||
}
|
||||
#undef SNAPSHOT_WRITE
|
||||
#define SNAPSHOT_WRITE(clazz) case kFfi##clazz##Cid:
|
||||
|
||||
CLASS_LIST_FFI(SNAPSHOT_WRITE) { UNREACHABLE(); }
|
||||
#undef SNAPSHOT_WRITE
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -48,6 +48,7 @@ class RawContext;
|
|||
class RawContextScope;
|
||||
class RawDouble;
|
||||
class RawExceptionHandlers;
|
||||
class RawFfiTrampolineData;
|
||||
class RawField;
|
||||
class RawFloat32x4;
|
||||
class RawFloat64x2;
|
||||
|
|
|
@ -179,6 +179,7 @@ class ObjectPointerVisitor;
|
|||
V(ClosureData, "ClosureData") \
|
||||
V(SignatureData, "SignatureData") \
|
||||
V(RedirectionData, "RedirectionData") \
|
||||
V(FfiTrampolineData, "FfiTrampolineData") \
|
||||
V(Field, "Field") \
|
||||
V(Script, "Script") \
|
||||
V(LibraryClass, "Library") \
|
||||
|
@ -373,6 +374,8 @@ class ObjectPointerVisitor;
|
|||
V(DartCore, "dart:core") \
|
||||
V(DartCollection, "dart:collection") \
|
||||
V(DartDeveloper, "dart:developer") \
|
||||
V(DartFfi, "dart:ffi") \
|
||||
V(DartFfiLibName, "ffi") \
|
||||
V(DartInternal, "dart:_internal") \
|
||||
V(DartIsolate, "dart:isolate") \
|
||||
V(DartMirrors, "dart:mirrors") \
|
||||
|
@ -441,6 +444,7 @@ class ObjectPointerVisitor;
|
|||
V(_ensureScheduleImmediate, "_ensureScheduleImmediate") \
|
||||
V(DartLibrary, "dart.library.") \
|
||||
V(DartLibraryMirrors, "dart.library.mirrors") \
|
||||
V(DartLibraryFfi, "dart.library.ffi") \
|
||||
V(_name, "_name") \
|
||||
V(name, "name") \
|
||||
V(options, "options") \
|
||||
|
@ -460,7 +464,23 @@ class ObjectPointerVisitor;
|
|||
V(Get, "get") \
|
||||
V(Set, "set") \
|
||||
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \
|
||||
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation")
|
||||
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation") \
|
||||
V(FfiPointer, "Pointer") \
|
||||
V(FfiNativeFunction, "NativeFunction") \
|
||||
V(FfiInt8, "Int8") \
|
||||
V(FfiInt16, "Int16") \
|
||||
V(FfiInt32, "Int32") \
|
||||
V(FfiInt64, "Int64") \
|
||||
V(FfiUint8, "Uint8") \
|
||||
V(FfiUint16, "Uint16") \
|
||||
V(FfiUint32, "Uint32") \
|
||||
V(FfiUint64, "Uint64") \
|
||||
V(FfiIntPtr, "IntPtr") \
|
||||
V(FfiFloat, "Float") \
|
||||
V(FfiDouble, "Double") \
|
||||
V(FfiVoid, "Void") \
|
||||
V(FfiNativeType, "NativeType") \
|
||||
V(FfiDynamicLibrary, "DynamicLibrary")
|
||||
|
||||
// Contains a list of frequently used strings in a canonicalized form. This
|
||||
// list is kept in the vm_isolate in order to share the copy across isolates
|
||||
|
|
|
@ -47,6 +47,7 @@ vm_sources = [
|
|||
"constants_dbc.h",
|
||||
"constants_ia32.h",
|
||||
"constants_kbc.h",
|
||||
"constants_x64.cc",
|
||||
"constants_x64.h",
|
||||
"cpu.h",
|
||||
"cpu_arm.cc",
|
||||
|
@ -91,6 +92,7 @@ vm_sources = [
|
|||
"dwarf.h",
|
||||
"exceptions.cc",
|
||||
"exceptions.h",
|
||||
"ffi_trampoline_stubs_x64.cc",
|
||||
"finalizable_data.h",
|
||||
"fixed_cache.h",
|
||||
"flag_list.h",
|
||||
|
|
40
samples/ffi/coordinate.dart
Normal file
40
samples/ffi/coordinate.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
// 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.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Sample struct for dart:ffi library.
|
||||
@ffi.struct
|
||||
class Coordinate extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
double x;
|
||||
|
||||
@ffi.Double()
|
||||
double y;
|
||||
|
||||
@ffi.Pointer()
|
||||
Coordinate next;
|
||||
|
||||
// Implementation generated by @ffi.struct annotation.
|
||||
external static int sizeOf();
|
||||
|
||||
Coordinate offsetBy(int offsetInBytes) =>
|
||||
super.offsetBy(offsetInBytes).cast();
|
||||
|
||||
Coordinate elementAt(int index) => offsetBy(sizeOf() * index);
|
||||
|
||||
static Coordinate allocate({int count: 1}) =>
|
||||
ffi.allocate<ffi.Uint8>(count: count * sizeOf()).cast();
|
||||
|
||||
/// Allocate a new [Coordinate] in C memory and populate its fields.
|
||||
factory Coordinate(double x, double y, Coordinate next) {
|
||||
Coordinate result = Coordinate.allocate()
|
||||
..x = x
|
||||
..y = y
|
||||
..next = next;
|
||||
return result;
|
||||
}
|
||||
}
|
274
samples/ffi/sample_ffi_data.dart
Normal file
274
samples/ffi/sample_ffi_data.dart
Normal file
|
@ -0,0 +1,274 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
main(List<String> arguments) {
|
||||
print('start main');
|
||||
|
||||
{
|
||||
// basic operation: allocate, get, set, and free
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.store(42);
|
||||
int pValue = p.load();
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// undefined behavior before set
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
int pValue = p.load();
|
||||
print('If not set, returns garbage: ${pValue}');
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// pointers can be created from an address
|
||||
ffi.Pointer<ffi.Int64> pHelper = ffi.allocate();
|
||||
pHelper.store(1337);
|
||||
|
||||
int address = pHelper.address;
|
||||
print('Address: ${address}');
|
||||
|
||||
ffi.Pointer<ffi.Int64> p = ffi.fromAddress(address);
|
||||
print('${p.runtimeType} value: ${p.load<int>()}');
|
||||
|
||||
pHelper.free();
|
||||
}
|
||||
|
||||
{
|
||||
// address is zeroed out after free
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.free();
|
||||
print('After free, address is zero: ${p.address}');
|
||||
}
|
||||
|
||||
{
|
||||
// pointer arithmetic can be done with element offsets or bytes
|
||||
ffi.Pointer<ffi.Int64> p1 = ffi.allocate<ffi.Int64>(count: 2);
|
||||
print('p1 address: ${p1.address}');
|
||||
|
||||
ffi.Pointer<ffi.Int64> p2 = p1.elementAt(1);
|
||||
print('p1.elementAt(1) address: ${p2.address}');
|
||||
p2.store(100);
|
||||
|
||||
ffi.Pointer<ffi.Int64> p3 = p1.offsetBy(8);
|
||||
print('p1.offsetBy(8) address: ${p3.address}');
|
||||
print('p1.offsetBy(8) value: ${p3.load<int>()}');
|
||||
p1.free();
|
||||
}
|
||||
|
||||
{
|
||||
// allocating too much throws an exception
|
||||
try {
|
||||
int maxMint = 9223372036854775807; // 2^63 - 1
|
||||
ffi.allocate<ffi.Int64>(count: maxMint);
|
||||
} on RangeError {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
try {
|
||||
int maxInt1_8 = 1152921504606846975; // 2^60 -1
|
||||
ffi.allocate<ffi.Int64>(count: maxInt1_8);
|
||||
} on ArgumentError {
|
||||
print('Expected exception on allocating too much');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// pointers can be cast into another type
|
||||
// resulting in the corresponding bits read
|
||||
ffi.Pointer<ffi.Int64> p1 = ffi.allocate();
|
||||
p1.store(9223372036854775807); // 2^63 - 1
|
||||
|
||||
ffi.Pointer<ffi.Int32> p2 = p1.cast();
|
||||
print('${p2.runtimeType} value: ${p2.load<int>()}'); // -1
|
||||
|
||||
ffi.Pointer<ffi.Int32> p3 = p2.elementAt(1);
|
||||
print('${p3.runtimeType} value: ${p3.load<int>()}'); // 2^31 - 1
|
||||
|
||||
p1.free();
|
||||
}
|
||||
|
||||
{
|
||||
// data can be tightly packed in memory
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate(count: 8);
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
p.elementAt(i).store(i * 3);
|
||||
}
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
print('p.elementAt($i) value: ${p.elementAt(i).load<int>()}');
|
||||
}
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// exception on storing a value that does not fit
|
||||
ffi.Pointer<ffi.Int32> p11 = ffi.allocate();
|
||||
|
||||
try {
|
||||
p11.store(9223372036854775807);
|
||||
} on ArgumentError {
|
||||
print('Expected exception on calling set with a value that does not fit');
|
||||
}
|
||||
|
||||
p11.free();
|
||||
}
|
||||
|
||||
{
|
||||
// doubles
|
||||
ffi.Pointer<ffi.Double> p = ffi.allocate();
|
||||
p.store(3.14159265359);
|
||||
print('${p.runtimeType} value: ${p.load<double>()}');
|
||||
p.store(3.14);
|
||||
print('${p.runtimeType} value: ${p.load<double>()}');
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// floats
|
||||
ffi.Pointer<ffi.Float> p = ffi.allocate();
|
||||
p.store(3.14159265359);
|
||||
print('${p.runtimeType} value: ${p.load<double>()}');
|
||||
p.store(3.14);
|
||||
print('${p.runtimeType} value: ${p.load<double>()}');
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// ffi.IntPtr varies in size based on whether the platform is 32 or 64 bit
|
||||
// addresses of pointers fit in this size
|
||||
ffi.Pointer<ffi.IntPtr> p = ffi.allocate();
|
||||
int p14addr = p.address;
|
||||
p.store(p14addr);
|
||||
int pValue = p.load();
|
||||
print('${p.runtimeType} value: ${pValue}');
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// void pointers are unsized
|
||||
// the size of the element it is pointing to is undefined
|
||||
// this means they cannot be ffi.allocated, read, or written
|
||||
// this would would fail to compile:
|
||||
// ffi.allocate<ffi.Void>();
|
||||
|
||||
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.Void> p2 = p1.cast();
|
||||
print('${p2.runtimeType} address: ${p2.address}');
|
||||
|
||||
// this fails to compile, we cannot read something unsized
|
||||
// p2.load<int>();
|
||||
|
||||
// this fails to compile, we cannot write something unsized
|
||||
// p2.store(1234);
|
||||
|
||||
p1.free();
|
||||
}
|
||||
|
||||
{
|
||||
// pointer to a pointer to something
|
||||
ffi.Pointer<ffi.Int16> pHelper = ffi.allocate();
|
||||
pHelper.store(17);
|
||||
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int16>> p = ffi.allocate();
|
||||
|
||||
// storing into a pointer pointer automatically unboxes
|
||||
p.store(pHelper);
|
||||
|
||||
// reading from a pointer pointer automatically boxes
|
||||
ffi.Pointer<ffi.Int16> pHelper2 = p.load();
|
||||
print('${pHelper2.runtimeType} value: ${pHelper2.load<int>()}');
|
||||
|
||||
int pValue = p.load<ffi.Pointer<ffi.Int16>>().load();
|
||||
print('${p.runtimeType} value\'s value: ${pValue}');
|
||||
|
||||
p.free();
|
||||
pHelper.free();
|
||||
}
|
||||
|
||||
{
|
||||
// the pointer to pointer types must match up
|
||||
ffi.Pointer<ffi.Int8> pHelper = ffi.allocate();
|
||||
pHelper.store(123);
|
||||
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int16>> p = ffi.allocate();
|
||||
|
||||
// this fails to compile due to type mismatch
|
||||
// p.store(pHelper);
|
||||
|
||||
pHelper.free();
|
||||
p.free();
|
||||
}
|
||||
|
||||
{
|
||||
// null pointer in Dart points to address 0 in c++
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int8>> pointerToPointer = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int8> value = null;
|
||||
pointerToPointer.store(value);
|
||||
value = pointerToPointer.load();
|
||||
print("Loading a pointer to the 0 address is null: ${value}");
|
||||
pointerToPointer.free();
|
||||
}
|
||||
|
||||
{
|
||||
// sizeof returns element size in bytes
|
||||
print('sizeOf<ffi.Double>(): ${ffi.sizeOf<ffi.Double>()}');
|
||||
print('sizeOf<ffi.Int16>(): ${ffi.sizeOf<ffi.Int16>()}');
|
||||
print('sizeOf<ffi.IntPtr>(): ${ffi.sizeOf<ffi.IntPtr>()}');
|
||||
}
|
||||
|
||||
{
|
||||
// only concrete sub types of NativeType can be ffi.allocated
|
||||
// this would fail to compile:
|
||||
// ffi.allocate();
|
||||
}
|
||||
|
||||
{
|
||||
// only concrete sub types of NativeType can be asked for size
|
||||
// this would fail to compile:
|
||||
// ffi.sizeOf();
|
||||
}
|
||||
|
||||
{
|
||||
// with ffi.IntPtr pointers, one can manually setup aribtrary data
|
||||
// structres in C memory.
|
||||
|
||||
void createChain(ffi.Pointer<ffi.IntPtr> head, int length, int value) {
|
||||
if (length == 0) {
|
||||
head.store(value);
|
||||
return;
|
||||
}
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.allocate<ffi.IntPtr>();
|
||||
head.store(next.address);
|
||||
createChain(next, length - 1, value);
|
||||
}
|
||||
|
||||
int getChainValue(ffi.Pointer<ffi.IntPtr> head, int length) {
|
||||
if (length == 0) {
|
||||
return head.load();
|
||||
}
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.fromAddress(head.load());
|
||||
return getChainValue(next, length - 1);
|
||||
}
|
||||
|
||||
void freeChain(ffi.Pointer<ffi.IntPtr> head, int length) {
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.fromAddress(head.load());
|
||||
head.free();
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
freeChain(next, length - 1);
|
||||
}
|
||||
|
||||
int length = 10;
|
||||
ffi.Pointer<ffi.IntPtr> head = ffi.allocate();
|
||||
createChain(head, length, 512);
|
||||
int tailValue = getChainValue(head, length);
|
||||
print('tailValue: ${tailValue}');
|
||||
freeChain(head, length);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
}
|
21
samples/ffi/sample_ffi_dynamic_library.dart
Normal file
21
samples/ffi/sample_ffi_dynamic_library.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
typedef NativeDoubleUnOp = ffi.Double Function(ffi.Double);
|
||||
|
||||
typedef DoubleUnOp = double Function(double);
|
||||
|
||||
main(List<String> arguments) {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
print(l);
|
||||
print(l.runtimeType);
|
||||
|
||||
var timesFour = l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour");
|
||||
print(timesFour);
|
||||
print(timesFour.runtimeType);
|
||||
|
||||
print(timesFour(3.0));
|
||||
}
|
267
samples/ffi/sample_ffi_functions.dart
Normal file
267
samples/ffi/sample_ffi_functions.dart
Normal file
|
@ -0,0 +1,267 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
typedef NativeUnaryOp = ffi.Int32 Function(ffi.Int32);
|
||||
typedef NativeBinaryOp = ffi.Int32 Function(ffi.Int32, ffi.Int32);
|
||||
typedef UnaryOp = int Function(int);
|
||||
typedef BinaryOp = int Function(int, int);
|
||||
typedef GenericBinaryOp<T> = int Function(int, T);
|
||||
typedef NativeQuadOpSigned = ffi.Int64 Function(
|
||||
ffi.Int64, ffi.Int32, ffi.Int16, ffi.Int8);
|
||||
typedef NativeQuadOpUnsigned = ffi.Uint64 Function(
|
||||
ffi.Uint64, ffi.Uint32, ffi.Uint16, ffi.Uint8);
|
||||
typedef NativeFunc4 = ffi.IntPtr Function(ffi.IntPtr);
|
||||
typedef NativeDoubleUnaryOp = ffi.Double Function(ffi.Double);
|
||||
typedef NativeFloatUnaryOp = ffi.Float Function(ffi.Float);
|
||||
typedef NativeOctenaryOp = ffi.IntPtr Function(
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr);
|
||||
typedef NativeDoubleOctenaryOp = ffi.Double Function(
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double);
|
||||
typedef NativeVigesimalOp = ffi.Double Function(
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double);
|
||||
typedef Int64PointerUnOp = ffi.Pointer<ffi.Int64> Function(
|
||||
ffi.Pointer<ffi.Int64>);
|
||||
typedef QuadOp = int Function(int, int, int, int);
|
||||
typedef DoubleUnaryOp = double Function(double);
|
||||
typedef OctenaryOp = int Function(
|
||||
int, int, int, int, int, int, int, int, int, int);
|
||||
typedef DoubleOctenaryOp = double Function(double, double, double, double,
|
||||
double, double, double, double, double, double);
|
||||
typedef VigesimalOp = double Function(
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double);
|
||||
|
||||
main(List<String> arguments) {
|
||||
print('start main');
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
{
|
||||
// int32 bin op
|
||||
BinaryOp sumPlus42 =
|
||||
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
|
||||
|
||||
var result = sumPlus42(3, 17);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// various size arguments
|
||||
QuadOp intComputation = ffiTestFunctions
|
||||
.lookupFunction<NativeQuadOpSigned, QuadOp>("IntComputation");
|
||||
var result = intComputation(125, 250, 500, 1000);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
|
||||
var mint = 0x7FFFFFFFFFFFFFFF; // 2 ^ 63 - 1
|
||||
result = intComputation(1, 1, 0, mint);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// unsigned int parameters
|
||||
QuadOp uintComputation = ffiTestFunctions
|
||||
.lookupFunction<NativeQuadOpUnsigned, QuadOp>("UintComputation");
|
||||
var result = uintComputation(0xFF, 0xFFFF, 0xFFFFFFFF, -1);
|
||||
result = uintComputation(1, 1, 0, -1);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
print(-0xFF + 0xFFFF - 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
{
|
||||
// architecture size argument
|
||||
ffi.Pointer<ffi.NativeFunction<NativeFunc4>> p =
|
||||
ffiTestFunctions.lookup("Times3");
|
||||
UnaryOp f6 = p.asFunction();
|
||||
var result = f6(1337);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// function with double
|
||||
DoubleUnaryOp times1_337Double = ffiTestFunctions
|
||||
.lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
|
||||
var result = times1_337Double(2.0);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// function with float
|
||||
DoubleUnaryOp times1_337Float = ffiTestFunctions
|
||||
.lookupFunction<NativeFloatUnaryOp, DoubleUnaryOp>("Times1_337Float");
|
||||
var result = times1_337Float(1000.0);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// function with many arguments: arguments get passed in registers and stack
|
||||
OctenaryOp sumManyInts = ffiTestFunctions
|
||||
.lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
|
||||
var result = sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// function with many double arguments
|
||||
DoubleOctenaryOp sumManyDoubles = ffiTestFunctions.lookupFunction<
|
||||
NativeDoubleOctenaryOp, DoubleOctenaryOp>("SumManyDoubles");
|
||||
var result =
|
||||
sumManyDoubles(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// function with many arguments, ints and doubles mixed
|
||||
VigesimalOp sumManyNumbers = ffiTestFunctions
|
||||
.lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
|
||||
var result = sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11,
|
||||
12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
}
|
||||
|
||||
{
|
||||
// pass an array / pointer as argument
|
||||
Int64PointerUnOp assign1337Index1 = ffiTestFunctions
|
||||
.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
|
||||
ffi.Pointer<ffi.Int64> p2 = ffi.allocate(count: 2);
|
||||
p2.store(42);
|
||||
p2.elementAt(1).store(1000);
|
||||
print(p2.elementAt(1).address.toRadixString(16));
|
||||
print(p2.elementAt(1).load<int>());
|
||||
ffi.Pointer<ffi.Int64> result = assign1337Index1(p2);
|
||||
print(p2.elementAt(1).load<int>());
|
||||
print(assign1337Index1);
|
||||
print(assign1337Index1.runtimeType);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
print(result.address.toRadixString(16));
|
||||
print(result.load<int>());
|
||||
}
|
||||
|
||||
{
|
||||
// passing in null for an int argument throws a null pointer exception
|
||||
BinaryOp sumPlus42 =
|
||||
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
|
||||
|
||||
int x = null;
|
||||
try {
|
||||
sumPlus42(43, x);
|
||||
} on ArgumentError {
|
||||
print('Expected exception on passing null for int');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// passing in null for a double argument throws a null pointer exception
|
||||
DoubleUnaryOp times1_337Double = ffiTestFunctions
|
||||
.lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
|
||||
|
||||
double x = null;
|
||||
try {
|
||||
times1_337Double(x);
|
||||
} on ArgumentError {
|
||||
print('Expected exception on passing null for double');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// passing in null for an int argument throws a null pointer exception
|
||||
VigesimalOp sumManyNumbers = ffiTestFunctions
|
||||
.lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
|
||||
|
||||
int x = null;
|
||||
try {
|
||||
sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13,
|
||||
14.0, 15, 16.0, 17, 18.0, x, 20.0);
|
||||
} on ArgumentError {
|
||||
print('Expected exception on passing null for int');
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// passing in null for a pointer argument results in a nullptr in c
|
||||
Int64PointerUnOp nullableInt64ElemAt1 =
|
||||
ffiTestFunctions.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>(
|
||||
"NullableInt64ElemAt1");
|
||||
|
||||
ffi.Pointer<ffi.Int64> result = nullableInt64ElemAt1(null);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
|
||||
ffi.Pointer<ffi.Int64> p2 = ffi.allocate(count: 2);
|
||||
result = nullableInt64ElemAt1(p2);
|
||||
print(result);
|
||||
print(result.runtimeType);
|
||||
p2.free();
|
||||
}
|
||||
|
||||
print("end main");
|
||||
}
|
80
samples/ffi/sample_ffi_functions_callbacks.dart
Normal file
80
samples/ffi/sample_ffi_functions_callbacks.dart
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import 'coordinate.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Coordinate Function(Coordinate);
|
||||
|
||||
typedef CoordinateTrice = Coordinate Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>>, Coordinate);
|
||||
|
||||
typedef BinaryOp = int Function(int, int);
|
||||
typedef NativeIntptrBinOp = ffi.IntPtr Function(ffi.IntPtr, ffi.IntPtr);
|
||||
typedef NativeIntptrBinOpLookup
|
||||
= ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> Function();
|
||||
|
||||
typedef NativeApplyTo42And74Type = ffi.IntPtr Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>>);
|
||||
|
||||
typedef ApplyTo42And74Type = int Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>>);
|
||||
|
||||
int myPlus(int a, int b) {
|
||||
print("myPlus");
|
||||
print(a);
|
||||
print(b);
|
||||
return a + b;
|
||||
}
|
||||
|
||||
main(List<String> arguments) {
|
||||
print('start main');
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
{
|
||||
// pass a c pointer to a c function as an argument to a c function
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>>
|
||||
transposeCoordinatePointer =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
ffi.Pointer<ffi.NativeFunction<CoordinateTrice>> p2 =
|
||||
ffiTestFunctions.lookup("CoordinateUnOpTrice");
|
||||
CoordinateTrice coordinateUnOpTrice = p2.asFunction();
|
||||
Coordinate c1 = Coordinate(10.0, 20.0, null);
|
||||
c1.next = c1;
|
||||
Coordinate result = coordinateUnOpTrice(transposeCoordinatePointer, c1);
|
||||
print(result.runtimeType);
|
||||
print(result.x);
|
||||
print(result.y);
|
||||
}
|
||||
|
||||
{
|
||||
// return a c pointer to a c function from a c function
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOpLookup>> p14 =
|
||||
ffiTestFunctions.lookup("IntptrAdditionClosure");
|
||||
NativeIntptrBinOpLookup intptrAdditionClosure = p14.asFunction();
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> intptrAdditionPointer =
|
||||
intptrAdditionClosure();
|
||||
BinaryOp intptrAddition = intptrAdditionPointer.asFunction();
|
||||
print(intptrAddition(10, 27));
|
||||
}
|
||||
|
||||
{
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> pointer =
|
||||
ffi.fromFunction(myPlus);
|
||||
print(pointer);
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<NativeApplyTo42And74Type>> p17 =
|
||||
ffiTestFunctions.lookup("ApplyTo42And74");
|
||||
ApplyTo42And74Type applyTo42And74 = p17.asFunction();
|
||||
|
||||
// int result = applyTo42And74(pointer);
|
||||
// print(result);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
}
|
64
samples/ffi/sample_ffi_functions_structs.dart
Normal file
64
samples/ffi/sample_ffi_functions_structs.dart
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import 'coordinate.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Coordinate Function(Coordinate);
|
||||
|
||||
main(List<String> arguments) {
|
||||
print('start main');
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
{
|
||||
// pass a struct to a c function and get a struct as return value
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate(10.0, 20.0, null);
|
||||
Coordinate c2 = Coordinate(42.0, 84.0, c1);
|
||||
c1.next = c2;
|
||||
|
||||
Coordinate result = f1(c1);
|
||||
|
||||
print(c1.x);
|
||||
print(c1.y);
|
||||
|
||||
print(result.runtimeType);
|
||||
|
||||
print(result.x);
|
||||
print(result.y);
|
||||
}
|
||||
|
||||
{
|
||||
// pass an array of structs to a c funtion
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate.allocate(count: 3);
|
||||
Coordinate c2 = c1.elementAt(1);
|
||||
Coordinate c3 = c1.elementAt(2);
|
||||
c1.x = 10.0;
|
||||
c1.y = 10.0;
|
||||
c1.next = c3;
|
||||
c2.x = 20.0;
|
||||
c2.y = 20.0;
|
||||
c2.next = c1;
|
||||
c3.x = 30.0;
|
||||
c3.y = 30.0;
|
||||
c3.next = c2;
|
||||
|
||||
Coordinate result = f1(c1);
|
||||
|
||||
print(result.x);
|
||||
print(result.y);
|
||||
}
|
||||
|
||||
print("end main");
|
||||
}
|
63
samples/ffi/sample_ffi_structs.dart
Normal file
63
samples/ffi/sample_ffi_structs.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import 'coordinate.dart';
|
||||
|
||||
main(List<String> arguments) {
|
||||
print('start main');
|
||||
|
||||
{
|
||||
// allocates each coordinate separately in c memory
|
||||
Coordinate c1 = Coordinate(10.0, 10.0, null);
|
||||
Coordinate c2 = Coordinate(20.0, 20.0, c1);
|
||||
Coordinate c3 = Coordinate(30.0, 30.0, c2);
|
||||
c1.next = c3;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
for (var i in [0, 1, 2, 3, 4]) {
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
c1.free();
|
||||
c2.free();
|
||||
c3.free();
|
||||
}
|
||||
|
||||
{
|
||||
// allocates coordinates consecutively in c memory
|
||||
Coordinate c1 = Coordinate.allocate(count: 3);
|
||||
Coordinate c2 = c1.elementAt(1);
|
||||
Coordinate c3 = c1.elementAt(2);
|
||||
c1.x = 10.0;
|
||||
c1.y = 10.0;
|
||||
c1.next = c3;
|
||||
c2.x = 20.0;
|
||||
c2.y = 20.0;
|
||||
c2.next = c1;
|
||||
c3.x = 30.0;
|
||||
c3.y = 30.0;
|
||||
c3.next = c2;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
for (var i in [0, 1, 2, 3, 4]) {
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
print("${currentCoordinate.x}; ${currentCoordinate.y}");
|
||||
}
|
||||
|
||||
c1.free();
|
||||
}
|
||||
|
||||
{
|
||||
Coordinate c = Coordinate(10, 10, null);
|
||||
print(c is Coordinate);
|
||||
print(c is ffi.Pointer<ffi.Void>);
|
||||
print(c is ffi.Pointer);
|
||||
c.free();
|
||||
}
|
||||
|
||||
print("end main");
|
||||
}
|
|
@ -183,6 +183,7 @@ _full_sdk_libraries = [
|
|||
"convert",
|
||||
"core",
|
||||
"developer",
|
||||
"ffi",
|
||||
"html",
|
||||
"_http",
|
||||
"indexed_db",
|
||||
|
|
|
@ -63,6 +63,11 @@ const Map<String, LibraryInfo> libraries = const {
|
|||
categories: "Client,Server,Embedded",
|
||||
maturity: Maturity.UNSTABLE,
|
||||
dart2jsPatchPath: "_internal/js_runtime/lib/developer_patch.dart"),
|
||||
"ffi": const LibraryInfo("ffi/ffi.dart",
|
||||
categories: "Server",
|
||||
// TODO(dacoharkes): Update maturity when we release dart:ffi.
|
||||
// https://github.com/dart-lang/sdk/issues/34452
|
||||
maturity: Maturity.EXPERIMENTAL),
|
||||
"html": const LibraryInfo("html/dart2js/html_dart2js.dart",
|
||||
categories: "Client",
|
||||
maturity: Maturity.WEB_STABLE,
|
||||
|
|
50
sdk/lib/ffi/annotations.dart
Normal file
50
sdk/lib/ffi/annotations.dart
Normal file
|
@ -0,0 +1,50 @@
|
|||
// 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.
|
||||
|
||||
part of dart.ffi;
|
||||
|
||||
class Struct {
|
||||
const Struct();
|
||||
}
|
||||
|
||||
/// This Dart class represents a C struct.
|
||||
///
|
||||
/// Fields in this struct, annotated with a subtype of [NativeType], are
|
||||
/// automatically transformed into wrappers to access the fields of the struct
|
||||
/// in C memory.
|
||||
///
|
||||
/// Fields without a [NativeType] annotation are not supported.
|
||||
const struct = const Struct();
|
||||
|
||||
class DartRepresentationOf {
|
||||
/// Represents the Dart type corresponding to a [NativeType].
|
||||
///
|
||||
/// [Int8] -> [int]
|
||||
/// [Int16] -> [int]
|
||||
/// [Int32] -> [int]
|
||||
/// [Int64] -> [int]
|
||||
/// [Uint8] -> [int]
|
||||
/// [Uint16] -> [int]
|
||||
/// [Uint32] -> [int]
|
||||
/// [Uint64] -> [int]
|
||||
/// [IntPtr] -> [int]
|
||||
/// [Double] -> [double]
|
||||
/// [Float] -> [double]
|
||||
/// [Pointer]<T> -> [Pointer]<T>
|
||||
/// T extends [Pointer] -> T
|
||||
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
|
||||
/// where DartRepresentationOf(Tn) -> Sn
|
||||
const DartRepresentationOf(String nativeType);
|
||||
}
|
||||
|
||||
class Unsized {
|
||||
const Unsized();
|
||||
}
|
||||
|
||||
/// This [NativeType] does not have predefined size.
|
||||
///
|
||||
/// Unsized NativeTypes do not support [sizeOf] because their size is unknown.
|
||||
/// Consequently, [allocate], [Pointer.load], [Pointer.store], and
|
||||
/// [Pointer.elementAt] are not available.
|
||||
const unsized = const Unsized();
|
32
sdk/lib/ffi/dynamic_library.dart
Normal file
32
sdk/lib/ffi/dynamic_library.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
part of dart.ffi;
|
||||
|
||||
/// Represents a dynamically loaded C library.
|
||||
class DynamicLibrary {
|
||||
/// Loads a dynamic library file. This is the equivalent of dlopen.
|
||||
///
|
||||
/// Throws an [ArgumentError] if loading the dynamic library fails.
|
||||
///
|
||||
/// Note that it loads the functions in the library lazily (RTLD_LAZY).
|
||||
external factory DynamicLibrary.open(String name);
|
||||
|
||||
/// Looks up a symbol in the [DynamicLibrary] and returns its address in
|
||||
/// memory. Equivalent of dlsym.
|
||||
///
|
||||
/// Throws an [ArgumentError] if it fails to lookup the symbol.
|
||||
external Pointer<T> lookup<T extends NativeType>(String symbolName);
|
||||
|
||||
/// Helper that combines lookup and cast to a Dart function.
|
||||
F lookupFunction<T extends Function, F extends Function>(String symbolName) {
|
||||
return lookup<NativeFunction<T>>(symbolName)?.asFunction<F>();
|
||||
}
|
||||
|
||||
/// Dynamic libraries are equal if they load the same library.
|
||||
external bool operator ==(other);
|
||||
|
||||
/// The hash code for a DynamicLibrary only depends on the loaded library
|
||||
external int get hashCode;
|
||||
}
|
102
sdk/lib/ffi/ffi.dart
Normal file
102
sdk/lib/ffi/ffi.dart
Normal file
|
@ -0,0 +1,102 @@
|
|||
// 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
|
||||
|
||||
library dart.ffi;
|
||||
|
||||
part "native_type.dart";
|
||||
part "annotations.dart";
|
||||
part "dynamic_library.dart";
|
||||
|
||||
/// Allocate [count] elements of type [T] on the C heap with malloc() and return
|
||||
/// a pointer to the newly allocated memory.
|
||||
///
|
||||
/// Note that the memory are uninitialized.
|
||||
///
|
||||
/// TODO(dacoharkes): change signature to T allocate<T extends Pointer>() ?
|
||||
/// This would enable us to allocate structs. However how do we know the size of
|
||||
/// structs? https://github.com/dart-lang/sdk/issues/35782
|
||||
external Pointer<T> allocate<T extends NativeType>({int count: 1});
|
||||
|
||||
/// Construction from raw value
|
||||
external T fromAddress<T extends Pointer>(int ptr);
|
||||
|
||||
/// number of bytes used by native type T
|
||||
external int sizeOf<T extends NativeType>();
|
||||
|
||||
/// Convert Dart function to a C function pointer, automatically marshalling
|
||||
/// the arguments and return value
|
||||
///
|
||||
/// Note: this is not implemented, always returns Pointer with address 0.
|
||||
///
|
||||
/// TODO(dacoharkes): Implement this feature.
|
||||
/// https://github.com/dart-lang/sdk/issues/35761
|
||||
external Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||
@DartRepresentationOf("T") Function f);
|
||||
|
||||
/*
|
||||
/// TODO(dacoharkes): Implement this feature.
|
||||
/// https://github.com/dart-lang/sdk/issues/35770
|
||||
/// Return a pointer object that has a finalizer attached to it. When this
|
||||
/// pointer object is collected by GC the given finalizer is invoked.
|
||||
///
|
||||
/// Note: the pointer object passed to the finalizer is not the same as
|
||||
/// the pointer object that is returned from [finalizable] - it points
|
||||
/// to the same memory region but has different identity.
|
||||
external Pointer<T> finalizable<T extends NativeType>(
|
||||
Pointer<T> p, void finalizer(Pointer<T> ptr));
|
||||
*/
|
||||
|
||||
/// Represents a pointer into the native C memory.
|
||||
class Pointer<T extends NativeType> extends NativeType {
|
||||
const Pointer();
|
||||
|
||||
/// Store a Dart value into this location.
|
||||
///
|
||||
/// The [value] is automatically marshalled into its C representation.
|
||||
/// Note that ints which do not fit in [T] are truncated and sign extended,
|
||||
/// and doubles stored into Pointer<[Float]> lose precision.
|
||||
external void store(@DartRepresentationOf("T") Object value);
|
||||
|
||||
/// Load a Dart value from this location.
|
||||
///
|
||||
/// The value is automatically unmarshalled from its C representation.
|
||||
external R load<@DartRepresentationOf("T") R>();
|
||||
|
||||
/// Access to the raw pointer value.
|
||||
external int get address;
|
||||
|
||||
/// Pointer arithmetic (takes element size into account).
|
||||
external Pointer<T> elementAt(int index);
|
||||
|
||||
/// Pointer arithmetic (byte offset).
|
||||
///
|
||||
/// TODO(dacoharkes): remove this?
|
||||
/// https://github.com/dart-lang/sdk/issues/35883
|
||||
external Pointer<T> offsetBy(int offsetInBytes);
|
||||
|
||||
/// Cast Pointer<T> to a (subtype of) Pointer<V>.
|
||||
external U cast<U extends Pointer>();
|
||||
|
||||
/// Convert to Dart function, automatically marshalling the arguments
|
||||
/// and return value.
|
||||
///
|
||||
/// Can only be called on [Pointer]<[NativeFunction]>.
|
||||
external R asFunction<@DartRepresentationOf("T") R extends Function>();
|
||||
|
||||
/// Free memory on the C heap pointed to by this pointer with free().
|
||||
///
|
||||
/// Note that this zeros out the address.
|
||||
external void free();
|
||||
|
||||
/// Equality for Pointers only depends on their address.
|
||||
bool operator ==(other) {
|
||||
if (other == null) return false;
|
||||
return address == other.address;
|
||||
}
|
||||
|
||||
/// The hash code for a Pointer only depends on its address.
|
||||
int get hashCode {
|
||||
return address.hashCode;
|
||||
}
|
||||
}
|
12
sdk/lib/ffi/ffi_sources.gni
Normal file
12
sdk/lib/ffi/ffi_sources.gni
Normal file
|
@ -0,0 +1,12 @@
|
|||
# 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.
|
||||
|
||||
ffi_sdk_sources = [
|
||||
"ffi.dart",
|
||||
|
||||
# The above file needs to be first as it lists the parts below.
|
||||
"annotations.dart",
|
||||
"dynamic_library.dart",
|
||||
"native_type.dart"
|
||||
]
|
133
sdk/lib/ffi/native_type.dart
Normal file
133
sdk/lib/ffi/native_type.dart
Normal file
|
@ -0,0 +1,133 @@
|
|||
// 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.
|
||||
|
||||
part of dart.ffi;
|
||||
|
||||
/// [NativeType]'s subtypes represent a native type in C.
|
||||
///
|
||||
/// [NativeType]'s subtypes are not constructible in the Dart code and serve
|
||||
/// purely as markers in type signatures.
|
||||
class NativeType {
|
||||
const NativeType();
|
||||
}
|
||||
|
||||
/// [_NativeInteger]'s subtypes represent a native integer in C.
|
||||
///
|
||||
/// [_NativeInteger]'s subtypes are not constructible in the Dart code and serve
|
||||
/// purely as markers in type signatures.
|
||||
class _NativeInteger extends NativeType {
|
||||
const _NativeInteger();
|
||||
}
|
||||
|
||||
/// [_NativeDouble]'s subtypes represent a native float or double in C.
|
||||
///
|
||||
/// [_NativeDouble]'s subtypes are not constructible in the Dart code and serve
|
||||
/// purely as markers in type signatures.
|
||||
class _NativeDouble extends NativeType {
|
||||
const _NativeDouble();
|
||||
}
|
||||
|
||||
/// Represents a native signed 8 bit integer in C.
|
||||
///
|
||||
/// [Int8] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
class Int8 extends _NativeInteger {
|
||||
const Int8();
|
||||
}
|
||||
|
||||
/// Represents a native signed 16 bit integer in C.
|
||||
///
|
||||
/// [Int16] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
class Int16 extends _NativeInteger {
|
||||
const Int16();
|
||||
}
|
||||
|
||||
/// Represents a native signed 32 bit integer in C.
|
||||
///
|
||||
/// [Int32] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
class Int32 extends _NativeInteger {
|
||||
const Int32();
|
||||
}
|
||||
|
||||
/// Represents a native signed 64 bit integer in C.
|
||||
///
|
||||
/// [Int64] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
class Int64 extends _NativeInteger {
|
||||
const Int64();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 8 bit integer in C.
|
||||
///
|
||||
/// [Uint8] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
class Uint8 extends _NativeInteger {
|
||||
const Uint8();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 16 bit integer in C.
|
||||
///
|
||||
/// [Uint16] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class Uint16 extends _NativeInteger {
|
||||
const Uint16();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 32 bit integer in C.
|
||||
///
|
||||
/// [Uint32] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class Uint32 extends _NativeInteger {
|
||||
const Uint32();
|
||||
}
|
||||
|
||||
/// Represents a native unsigned 64 bit integer in C.
|
||||
///
|
||||
/// [Uint64] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class Uint64 extends _NativeInteger {
|
||||
const Uint64();
|
||||
}
|
||||
|
||||
/// Represents a native pointer-sized integer in C.
|
||||
///
|
||||
/// [IntPtr] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class IntPtr extends _NativeInteger {
|
||||
const IntPtr();
|
||||
}
|
||||
|
||||
/// Represents a native 32 bit float in C.
|
||||
///
|
||||
/// [Float] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class Float extends _NativeDouble {
|
||||
const Float();
|
||||
}
|
||||
|
||||
/// Represents a native 64 bit double in C.
|
||||
///
|
||||
/// [Double] is not constructible in the Dart code and serves purely as marker
|
||||
/// in type signatures.
|
||||
class Double extends _NativeDouble {
|
||||
const Double();
|
||||
}
|
||||
|
||||
/// Represents a void type in C.
|
||||
///
|
||||
/// [Void] is not constructible in the Dart code and serves purely as marker in
|
||||
/// type signatures.
|
||||
@unsized
|
||||
class Void extends NativeType {
|
||||
const Void();
|
||||
}
|
||||
|
||||
/// Represents a function type in C.
|
||||
///
|
||||
/// [NativeFunction] is not constructible in the Dart code and serves purely as
|
||||
/// marker in type signatures.
|
||||
@unsized
|
||||
class NativeFunction<T extends Function> extends NativeType {}
|
|
@ -63,6 +63,14 @@
|
|||
],
|
||||
"uri": "collection/collection.dart"
|
||||
},
|
||||
"ffi": {
|
||||
"patches": [
|
||||
"../../runtime/lib/ffi_dynamic_library_patch.dart",
|
||||
"../../runtime/lib/ffi_native_type_patch.dart",
|
||||
"../../runtime/lib/ffi_patch.dart"
|
||||
],
|
||||
"uri": "ffi/ffi.dart"
|
||||
},
|
||||
"typed_data": {
|
||||
"patches": "../../runtime/lib/typed_data_patch.dart",
|
||||
"uri": "typed_data/typed_data.dart"
|
||||
|
|
|
@ -87,6 +87,13 @@ vm:
|
|||
- "../../runtime/lib/profiler.dart"
|
||||
- "../../runtime/lib/timeline.dart"
|
||||
|
||||
ffi:
|
||||
uri: "ffi/ffi.dart"
|
||||
patches:
|
||||
- "../../runtime/lib/ffi_dynamic_library_patch.dart"
|
||||
- "../../runtime/lib/ffi_native_type_patch.dart"
|
||||
- "../../runtime/lib/ffi_patch.dart"
|
||||
|
||||
_http:
|
||||
uri: "_http/http.dart"
|
||||
|
||||
|
|
40
tests/standalone_2/ffi/coordinate.dart
Normal file
40
tests/standalone_2/ffi/coordinate.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
// 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.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Sample struct for dart:ffi library.
|
||||
@ffi.struct
|
||||
class Coordinate extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
double x;
|
||||
|
||||
@ffi.Double()
|
||||
double y;
|
||||
|
||||
@ffi.Pointer()
|
||||
Coordinate next;
|
||||
|
||||
/// generated by @ffi.struct annotation
|
||||
external static int sizeOf();
|
||||
|
||||
Coordinate offsetBy(int offsetInBytes) =>
|
||||
super.offsetBy(offsetInBytes).cast();
|
||||
|
||||
Coordinate elementAt(int index) => offsetBy(sizeOf() * index);
|
||||
|
||||
static Coordinate allocate({int count: 1}) =>
|
||||
ffi.allocate<ffi.Uint8>(count: count * sizeOf()).cast();
|
||||
|
||||
/// Allocate a new [Coordinate] in C memory and populate its fields.
|
||||
factory Coordinate(double x, double y, Coordinate next) {
|
||||
Coordinate result = Coordinate.allocate()
|
||||
..x = x
|
||||
..y = y
|
||||
..next = next;
|
||||
return result;
|
||||
}
|
||||
}
|
20
tests/standalone_2/ffi/coordinate_bare.dart
Normal file
20
tests/standalone_2/ffi/coordinate_bare.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
|
||||
library FfiTestCoordinateBare;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Stripped down sample struct for dart:ffi library.
|
||||
@ffi.struct
|
||||
class Coordinate extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
double x;
|
||||
|
||||
@ffi.Double()
|
||||
double y;
|
||||
|
||||
@ffi.Pointer()
|
||||
Coordinate next;
|
||||
}
|
44
tests/standalone_2/ffi/coordinate_manual.dart
Normal file
44
tests/standalone_2/ffi/coordinate_manual.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
// 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.
|
||||
|
||||
library FfiTestCoordinateManual;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Sample struct for dart:ffi library without use of ffi annotations.
|
||||
class Coordinate extends ffi.Pointer<ffi.Void> {
|
||||
ffi.Pointer<ffi.Double> get _xPtr => cast();
|
||||
set x(double v) => _xPtr.store(v);
|
||||
double get x => _xPtr.load();
|
||||
|
||||
ffi.Pointer<ffi.Double> get _yPtr =>
|
||||
offsetBy(ffi.sizeOf<ffi.Double>() * 1).cast();
|
||||
set y(double v) => _yPtr.store(v);
|
||||
double get y => _yPtr.load();
|
||||
|
||||
ffi.Pointer<Coordinate> get _nextPtr =>
|
||||
offsetBy(ffi.sizeOf<ffi.Double>() * 2).cast();
|
||||
set next(Coordinate v) => _nextPtr.store(v);
|
||||
Coordinate get next => _nextPtr.load();
|
||||
|
||||
static int sizeOf() =>
|
||||
ffi.sizeOf<ffi.Double>() * 2 + ffi.sizeOf<ffi.IntPtr>();
|
||||
|
||||
Coordinate offsetBy(int offsetInBytes) =>
|
||||
super.offsetBy(offsetInBytes).cast();
|
||||
|
||||
Coordinate elementAt(int index) => offsetBy(sizeOf() * index);
|
||||
|
||||
static Coordinate allocate({int count: 1}) =>
|
||||
ffi.allocate<ffi.Uint8>(count: count * sizeOf()).cast();
|
||||
|
||||
/// Allocate a new [Coordinate] in C memory and populate its fields.
|
||||
factory Coordinate(double x, double y, Coordinate next) {
|
||||
Coordinate result = Coordinate.allocate()
|
||||
..x = x
|
||||
..y = y
|
||||
..next = next;
|
||||
return result;
|
||||
}
|
||||
}
|
32
tests/standalone_2/ffi/cstring.dart
Normal file
32
tests/standalone_2/ffi/cstring.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
// 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.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Sample non-struct subtype of Pointer for dart:ffi library.
|
||||
class CString extends ffi.Pointer<ffi.Uint8> {
|
||||
CString elementAt(int index) => super.elementAt(index).cast();
|
||||
|
||||
String fromUtf8() {
|
||||
List<int> units = [];
|
||||
int len = 0;
|
||||
while (true) {
|
||||
int char = elementAt(len++).load<int>();
|
||||
if (char == 0) break;
|
||||
units.add(char);
|
||||
}
|
||||
return Utf8Decoder().convert(units);
|
||||
}
|
||||
|
||||
factory CString.toUtf8(String s) {
|
||||
CString result = ffi.allocate<ffi.Uint8>(count: s.length + 1).cast();
|
||||
List<int> units = Utf8Encoder().convert(s);
|
||||
for (int i = 0; i < s.length; i++) result.elementAt(i).store(units[i]);
|
||||
result.elementAt(s.length).store(0);
|
||||
return result;
|
||||
}
|
||||
}
|
29
tests/standalone_2/ffi/data_not_asan_test.dart
Normal file
29
tests/standalone_2/ffi/data_not_asan_test.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi primitive data pointers.
|
||||
// This test tries to allocate too much memory on purpose to test the Exception
|
||||
// thrown on malloc failing.
|
||||
// This malloc also triggers an asan alarm, so this test is in a separate file
|
||||
// which is excluded in asan mode.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void main() {
|
||||
testPointerAllocateTooLarge();
|
||||
}
|
||||
|
||||
/// This test is skipped in asan mode.
|
||||
void testPointerAllocateTooLarge() {
|
||||
int maxInt = 9223372036854775807; // 2^63 - 1
|
||||
Expect.throws(
|
||||
() => ffi.allocate<ffi.Int64>(count: maxInt)); // does not fit in range
|
||||
int maxInt1_8 = 1152921504606846975; // 2^60 -1
|
||||
Expect.throws(
|
||||
() => ffi.allocate<ffi.Int64>(count: maxInt1_8)); // not enough memory
|
||||
}
|
492
tests/standalone_2/ffi/data_test.dart
Normal file
492
tests/standalone_2/ffi/data_test.dart
Normal file
|
@ -0,0 +1,492 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi primitive data pointers.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void main() {
|
||||
testPointerBasic();
|
||||
testPointerFromPointer();
|
||||
testPointerPointerArithmetic();
|
||||
testPointerPointerArithmeticSizes();
|
||||
testPointerAllocateNonPositive();
|
||||
testPointerCast();
|
||||
testCastGeneric();
|
||||
testCastGeneric2();
|
||||
testCastNativeType();
|
||||
testCondensedNumbersInt8();
|
||||
testCondensedNumbersFloat();
|
||||
testRangeInt8();
|
||||
testRangeUint8();
|
||||
testRangeInt16();
|
||||
testRangeUint16();
|
||||
testRangeInt32();
|
||||
testRangeUint32();
|
||||
testRangeInt64();
|
||||
testRangeUint64();
|
||||
testRangeIntPtr();
|
||||
testFloat();
|
||||
testDouble();
|
||||
testVoid();
|
||||
testPointerPointer();
|
||||
testPointerPointerNull();
|
||||
testPointerStoreNull();
|
||||
testSizeOf();
|
||||
testPointerChain(1000);
|
||||
testTypeTest();
|
||||
testToString();
|
||||
testEquality();
|
||||
testAllocateGeneric();
|
||||
testAllocateVoid();
|
||||
testAllocateNativeFunction();
|
||||
testAllocateNativeType();
|
||||
testSizeOfGeneric();
|
||||
testSizeOfVoid();
|
||||
testSizeOfNativeFunction();
|
||||
testSizeOfNativeType();
|
||||
testFreeZeroOut();
|
||||
}
|
||||
|
||||
void testPointerBasic() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.store(42);
|
||||
Expect.equals(42, p.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testPointerFromPointer() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.store(1337);
|
||||
int ptr = p.address;
|
||||
ffi.Pointer<ffi.Int64> p2 = ffi.fromAddress(ptr);
|
||||
Expect.equals(1337, p2.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testPointerPointerArithmetic() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate(count: 2);
|
||||
ffi.Pointer<ffi.Int64> p2 = p.elementAt(1);
|
||||
p2.store(100);
|
||||
ffi.Pointer<ffi.Int64> p3 = p.offsetBy(8);
|
||||
Expect.equals(100, p3.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testPointerPointerArithmeticSizes() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate(count: 2);
|
||||
ffi.Pointer<ffi.Int64> p2 = p.elementAt(1);
|
||||
int addr = p.address;
|
||||
Expect.equals(addr + 8, p2.address);
|
||||
p.free();
|
||||
|
||||
ffi.Pointer<ffi.Int32> p3 = ffi.allocate(count: 2);
|
||||
ffi.Pointer<ffi.Int32> p4 = p3.elementAt(1);
|
||||
addr = p3.address;
|
||||
Expect.equals(addr + 4, p4.address);
|
||||
p3.free();
|
||||
}
|
||||
|
||||
void testPointerAllocateNonPositive() {
|
||||
Expect.throws(() => ffi.allocate<ffi.Int8>(count: 0));
|
||||
Expect.throws(() => ffi.allocate<ffi.Int8>(count: -1));
|
||||
}
|
||||
|
||||
void testPointerCast() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int32> p2 = p.cast(); // gets the correct type args back
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testCastGeneric() {
|
||||
ffi.Pointer<T> generic<T extends ffi.NativeType>(ffi.Pointer<ffi.Int16> p) {
|
||||
return p.cast();
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.Int16> p = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int64> p2 = generic(p);
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testCastGeneric2() {
|
||||
ffi.Pointer<ffi.Int64> generic<T extends ffi.NativeType>(ffi.Pointer<T> p) {
|
||||
return p.cast();
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.Int16> p = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int64> p2 = generic(p);
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testCastNativeType() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
Expect.throws(() {
|
||||
p.cast<ffi.Pointer>();
|
||||
});
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testCondensedNumbersInt8() {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate(count: 8);
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
p.elementAt(i).store(i * 3);
|
||||
}
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
Expect.equals(i * 3, p.elementAt(i).load<int>());
|
||||
}
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testCondensedNumbersFloat() {
|
||||
ffi.Pointer<ffi.Float> p = ffi.allocate(count: 8);
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
p.elementAt(i).store(1.511366173271439e-13);
|
||||
}
|
||||
for (var i in [0, 1, 2, 3, 4, 5, 6, 7]) {
|
||||
Expect.equals(1.511366173271439e-13, p.elementAt(i).load<double>());
|
||||
}
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeInt8() {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
p.store(127);
|
||||
Expect.equals(127, p.load<int>());
|
||||
p.store(-128);
|
||||
Expect.equals(-128, p.load<int>());
|
||||
|
||||
Expect.equals(0x0000000000000080, 128);
|
||||
Expect.equals(0xFFFFFFFFFFFFFF80, -128);
|
||||
p.store(128);
|
||||
Expect.equals(-128, p.load<int>()); // truncated and sign extended
|
||||
|
||||
Expect.equals(0xFFFFFFFFFFFFFF7F, -129);
|
||||
Expect.equals(0x000000000000007F, 127);
|
||||
p.store(-129);
|
||||
Expect.equals(127, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeUint8() {
|
||||
ffi.Pointer<ffi.Uint8> p = ffi.allocate();
|
||||
p.store(255);
|
||||
Expect.equals(255, p.load<int>());
|
||||
p.store(0);
|
||||
Expect.equals(0, p.load<int>());
|
||||
|
||||
Expect.equals(0x0000000000000000, 0);
|
||||
Expect.equals(0x0000000000000100, 256);
|
||||
p.store(256);
|
||||
Expect.equals(0, p.load<int>()); // truncated
|
||||
|
||||
Expect.equals(0xFFFFFFFFFFFFFFFF, -1);
|
||||
Expect.equals(0x00000000000000FF, 255);
|
||||
p.store(-1);
|
||||
Expect.equals(255, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeInt16() {
|
||||
ffi.Pointer<ffi.Int16> p = ffi.allocate();
|
||||
p.store(0x7FFF);
|
||||
Expect.equals(0x7FFF, p.load<int>());
|
||||
p.store(-0x8000);
|
||||
Expect.equals(-0x8000, p.load<int>());
|
||||
p.store(0x8000);
|
||||
Expect.equals(
|
||||
0xFFFFFFFFFFFF8000, p.load<int>()); // truncated and sign extended
|
||||
p.store(-0x8001);
|
||||
Expect.equals(0x7FFF, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeUint16() {
|
||||
ffi.Pointer<ffi.Uint16> p = ffi.allocate();
|
||||
p.store(0xFFFF);
|
||||
Expect.equals(0xFFFF, p.load<int>());
|
||||
p.store(0);
|
||||
Expect.equals(0, p.load<int>());
|
||||
p.store(0x10000);
|
||||
Expect.equals(0, p.load<int>()); // truncated
|
||||
p.store(-1);
|
||||
Expect.equals(0xFFFF, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeInt32() {
|
||||
ffi.Pointer<ffi.Int32> p = ffi.allocate();
|
||||
p.store(0x7FFFFFFF);
|
||||
Expect.equals(0x7FFFFFFF, p.load<int>());
|
||||
p.store(-0x80000000);
|
||||
Expect.equals(-0x80000000, p.load<int>());
|
||||
p.store(0x80000000);
|
||||
Expect.equals(
|
||||
0xFFFFFFFF80000000, p.load<int>()); // truncated and sign extended
|
||||
p.store(-0x80000001);
|
||||
Expect.equals(0x7FFFFFFF, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeUint32() {
|
||||
ffi.Pointer<ffi.Uint32> p = ffi.allocate();
|
||||
p.store(0xFFFFFFFF);
|
||||
Expect.equals(0xFFFFFFFF, p.load<int>());
|
||||
p.store(0);
|
||||
Expect.equals(0, p.load<int>());
|
||||
p.store(0x100000000);
|
||||
Expect.equals(0, p.load<int>()); // truncated
|
||||
p.store(-1);
|
||||
Expect.equals(0xFFFFFFFF, p.load<int>()); // truncated
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeInt64() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.store(0x7FFFFFFFFFFFFFFF); // 2 ^ 63 - 1
|
||||
Expect.equals(0x7FFFFFFFFFFFFFFF, p.load<int>());
|
||||
p.store(-0x8000000000000000); // -2 ^ 63
|
||||
Expect.equals(-0x8000000000000000, p.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeUint64() {
|
||||
ffi.Pointer<ffi.Uint64> p = ffi.allocate();
|
||||
p.store(0x7FFFFFFFFFFFFFFF); // 2 ^ 63 - 1
|
||||
Expect.equals(0x7FFFFFFFFFFFFFFF, p.load<int>());
|
||||
p.store(-0x8000000000000000); // -2 ^ 63 interpreted as 2 ^ 63
|
||||
Expect.equals(-0x8000000000000000, p.load<int>());
|
||||
|
||||
// Dart allows interpreting bits both signed and unsigned
|
||||
Expect.equals(0xFFFFFFFFFFFFFFFF, -1);
|
||||
p.store(-1); // -1 interpreted as 2 ^ 64 - 1
|
||||
Expect.equals(-1, p.load<int>());
|
||||
Expect.equals(0xFFFFFFFFFFFFFFFF, p.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testRangeIntPtr() {
|
||||
ffi.Pointer<ffi.IntPtr> p = ffi.allocate();
|
||||
int pAddr = p.address;
|
||||
p.store(pAddr); // its own address should fit
|
||||
p.store(0x7FFFFFFF); // and 32 bit addresses should fit
|
||||
Expect.equals(0x7FFFFFFF, p.load<int>());
|
||||
p.store(-0x80000000);
|
||||
Expect.equals(-0x80000000, p.load<int>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testFloat() {
|
||||
ffi.Pointer<ffi.Float> p = ffi.allocate();
|
||||
p.store(1.511366173271439e-13);
|
||||
Expect.equals(1.511366173271439e-13, p.load<double>());
|
||||
p.store(1.4260258159703532e-105); // float does not have enough precision
|
||||
Expect.notEquals(1.4260258159703532e-105, p.load<double>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testDouble() {
|
||||
ffi.Pointer<ffi.Double> p = ffi.allocate();
|
||||
p.store(1.4260258159703532e-105);
|
||||
Expect.equals(1.4260258159703532e-105, p.load<double>());
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testVoid() {
|
||||
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.Void> p2 = p1.cast(); // make this dart pointer opaque
|
||||
p2.address; // we can print the address
|
||||
p2.free();
|
||||
}
|
||||
|
||||
void testPointerPointer() {
|
||||
ffi.Pointer<ffi.Int16> p = ffi.allocate();
|
||||
p.store(17);
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int16>> p2 = ffi.allocate();
|
||||
p2.store(p);
|
||||
Expect.equals(17, p2.load<ffi.Pointer<ffi.Int16>>().load<int>());
|
||||
p2.free();
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testPointerPointerNull() {
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int8>> pointerToPointer = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int8> value = null;
|
||||
pointerToPointer.store(value);
|
||||
value = pointerToPointer.load();
|
||||
Expect.isNull(value);
|
||||
value = ffi.allocate();
|
||||
pointerToPointer.store(value);
|
||||
value = pointerToPointer.load();
|
||||
Expect.isNotNull(value);
|
||||
value.free();
|
||||
value = null;
|
||||
pointerToPointer.store(value);
|
||||
value = pointerToPointer.load();
|
||||
Expect.isNull(value);
|
||||
pointerToPointer.free();
|
||||
}
|
||||
|
||||
void testPointerStoreNull() {
|
||||
int i = null;
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
Expect.throws(() => p.store(i));
|
||||
p.free();
|
||||
double d = null;
|
||||
ffi.Pointer<ffi.Float> p2 = ffi.allocate();
|
||||
Expect.throws(() => p2.store(d));
|
||||
p2.free();
|
||||
}
|
||||
|
||||
void testSizeOf() {
|
||||
Expect.equals(1, ffi.sizeOf<ffi.Int8>());
|
||||
Expect.equals(2, ffi.sizeOf<ffi.Int16>());
|
||||
Expect.equals(4, ffi.sizeOf<ffi.Int32>());
|
||||
Expect.equals(8, ffi.sizeOf<ffi.Int64>());
|
||||
Expect.equals(1, ffi.sizeOf<ffi.Uint8>());
|
||||
Expect.equals(2, ffi.sizeOf<ffi.Uint16>());
|
||||
Expect.equals(4, ffi.sizeOf<ffi.Uint32>());
|
||||
Expect.equals(8, ffi.sizeOf<ffi.Uint64>());
|
||||
Expect.equals(
|
||||
true, 4 == ffi.sizeOf<ffi.IntPtr>() || 8 == ffi.sizeOf<ffi.IntPtr>());
|
||||
Expect.equals(4, ffi.sizeOf<ffi.Float>());
|
||||
Expect.equals(8, ffi.sizeOf<ffi.Double>());
|
||||
}
|
||||
|
||||
// note: stack overflows at around 15k calls
|
||||
void testPointerChain(int length) {
|
||||
void createChain(ffi.Pointer<ffi.IntPtr> head, int length, int value) {
|
||||
if (length == 0) {
|
||||
head.store(value);
|
||||
return;
|
||||
}
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.allocate();
|
||||
head.store(next.address);
|
||||
createChain(next, length - 1, value);
|
||||
}
|
||||
|
||||
int getChainValue(ffi.Pointer<ffi.IntPtr> head, int length) {
|
||||
if (length == 0) {
|
||||
return head.load();
|
||||
}
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.fromAddress(head.load());
|
||||
return getChainValue(next, length - 1);
|
||||
}
|
||||
|
||||
void freeChain(ffi.Pointer<ffi.IntPtr> head, int length) {
|
||||
ffi.Pointer<ffi.IntPtr> next = ffi.fromAddress(head.load());
|
||||
head.free();
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
freeChain(next, length - 1);
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.IntPtr> head = ffi.allocate();
|
||||
createChain(head, length, 512);
|
||||
int tailValue = getChainValue(head, length);
|
||||
Expect.equals(512, tailValue);
|
||||
freeChain(head, length);
|
||||
}
|
||||
|
||||
void testTypeTest() {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
Expect.isTrue(p is ffi.Pointer);
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testToString() {
|
||||
ffi.Pointer<ffi.Int16> p = ffi.allocate();
|
||||
Expect.stringEquals(
|
||||
"Pointer<Int16>: address=0x", p.toString().substring(0, 26));
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testEquality() {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.fromAddress(12345678);
|
||||
ffi.Pointer<ffi.Int8> p2 = ffi.fromAddress(12345678);
|
||||
Expect.equals(p, p2);
|
||||
Expect.equals(p.hashCode, p2.hashCode);
|
||||
ffi.Pointer<ffi.Int16> p3 = p.cast();
|
||||
Expect.equals(p, p3);
|
||||
Expect.equals(p.hashCode, p3.hashCode);
|
||||
Expect.notEquals(p, null);
|
||||
Expect.notEquals(null, p);
|
||||
ffi.Pointer<ffi.Int8> p4 = p.offsetBy(1337);
|
||||
Expect.notEquals(p, p4);
|
||||
}
|
||||
|
||||
typedef Int8UnOp = ffi.Int8 Function(ffi.Int8);
|
||||
|
||||
void testAllocateGeneric() {
|
||||
ffi.Pointer<T> generic<T extends ffi.NativeType>() {
|
||||
ffi.Pointer<T> pointer;
|
||||
pointer = ffi.allocate();
|
||||
return pointer;
|
||||
}
|
||||
|
||||
ffi.Pointer p = generic<ffi.Int64>();
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testAllocateVoid() {
|
||||
Expect.throws(() {
|
||||
ffi.Pointer<ffi.Void> p = ffi.allocate();
|
||||
});
|
||||
}
|
||||
|
||||
void testAllocateNativeFunction() {
|
||||
Expect.throws(() {
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.allocate();
|
||||
});
|
||||
}
|
||||
|
||||
void testAllocateNativeType() {
|
||||
Expect.throws(() {
|
||||
ffi.allocate();
|
||||
});
|
||||
}
|
||||
|
||||
void testSizeOfGeneric() {
|
||||
int generic<T extends ffi.Pointer>() {
|
||||
int size;
|
||||
size = ffi.sizeOf<T>();
|
||||
return size;
|
||||
}
|
||||
|
||||
int size = generic<ffi.Pointer<ffi.Int64>>();
|
||||
Expect.equals(8, size);
|
||||
}
|
||||
|
||||
void testSizeOfVoid() {
|
||||
Expect.throws(() {
|
||||
ffi.sizeOf<ffi.Void>();
|
||||
});
|
||||
}
|
||||
|
||||
void testSizeOfNativeFunction() {
|
||||
Expect.throws(() {
|
||||
ffi.sizeOf<ffi.NativeFunction<Int8UnOp>>();
|
||||
});
|
||||
}
|
||||
|
||||
void testSizeOfNativeType() {
|
||||
Expect.throws(() {
|
||||
ffi.sizeOf();
|
||||
});
|
||||
}
|
||||
|
||||
void testFreeZeroOut() {
|
||||
// at least one of these pointers should have address != 0 on all platforms
|
||||
ffi.Pointer<ffi.Int8> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int8> p2 = ffi.allocate();
|
||||
Expect.notEquals(0, p1.address & p2.address);
|
||||
p1.free();
|
||||
p2.free();
|
||||
Expect.equals(0, p1.address);
|
||||
Expect.equals(0, p2.address);
|
||||
}
|
63
tests/standalone_2/ffi/dynamic_library_test.dart
Normal file
63
tests/standalone_2/ffi/dynamic_library_test.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi dynamic library loading.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
testOpen();
|
||||
testOpenError();
|
||||
testLookup();
|
||||
testLookupError();
|
||||
testToString();
|
||||
testEquality();
|
||||
}
|
||||
|
||||
void testOpen() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Expect.notEquals(null, l);
|
||||
}
|
||||
|
||||
void testOpenError() {
|
||||
Expect.throws(
|
||||
() => ffi.DynamicLibrary.open("doesnotexistforsurelibrary123409876"));
|
||||
}
|
||||
|
||||
typedef NativeDoubleUnOp = ffi.Double Function(ffi.Double);
|
||||
|
||||
typedef DoubleUnOp = double Function(double);
|
||||
|
||||
void testLookup() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
var timesFour = l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour");
|
||||
Expect.approxEquals(12.0, timesFour(3));
|
||||
}
|
||||
|
||||
void testLookupError() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Expect.throws(() => l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>(
|
||||
"functionnamethatdoesnotexistforsure749237593845"));
|
||||
}
|
||||
|
||||
void testToString() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Expect.stringEquals(
|
||||
"DynamicLibrary: handle=0x", l.toString().substring(0, 25));
|
||||
}
|
||||
|
||||
void testEquality() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
ffi.DynamicLibrary l2 = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Expect.equals(l, l2);
|
||||
Expect.equals(l.hashCode, l2.hashCode);
|
||||
Expect.notEquals(l, null);
|
||||
Expect.notEquals(null, l);
|
||||
ffi.DynamicLibrary l3 = ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
Expect.notEquals(l, l3);
|
||||
}
|
20
tests/standalone_2/ffi/enable_ffi_test.dart
Normal file
20
tests/standalone_2/ffi/enable_ffi_test.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing the --enable-ffi=false flag.
|
||||
//
|
||||
// VMOptions=--enable-ffi=false
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void main() {
|
||||
ffi.Pointer<ffi.Int64> p = ffi.allocate();
|
||||
p.store(42);
|
||||
Expect.equals(42, p.load<int>());
|
||||
p.free();
|
||||
}
|
90
tests/standalone_2/ffi/function_callbacks_test.dart
Normal file
90
tests/standalone_2/ffi/function_callbacks_test.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi function pointers with callbacks.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import 'coordinate.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Coordinate Function(Coordinate);
|
||||
|
||||
typedef CoordinateTrice = Coordinate Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>>, Coordinate);
|
||||
|
||||
void main() {
|
||||
testFunctionWithFunctionPointer();
|
||||
testNativeFunctionWithFunctionPointer();
|
||||
testFromFunction();
|
||||
}
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
/// pass a pointer to a c function as an argument to a c function
|
||||
void testFunctionWithFunctionPointer() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>>
|
||||
transposeCoordinatePointer =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<CoordinateTrice>> p2 =
|
||||
ffiTestFunctions.lookup("CoordinateUnOpTrice");
|
||||
CoordinateTrice coordinateUnOpTrice = p2.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate(10.0, 20.0, null);
|
||||
c1.next = c1;
|
||||
|
||||
Coordinate result = coordinateUnOpTrice(transposeCoordinatePointer, c1);
|
||||
|
||||
print(result.runtimeType);
|
||||
print(result.x);
|
||||
print(result.y);
|
||||
|
||||
c1.free();
|
||||
}
|
||||
|
||||
typedef BinaryOp = int Function(int, int);
|
||||
|
||||
typedef NativeIntptrBinOp = ffi.IntPtr Function(ffi.IntPtr, ffi.IntPtr);
|
||||
|
||||
typedef NativeIntptrBinOpLookup
|
||||
= ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> Function();
|
||||
|
||||
void testNativeFunctionWithFunctionPointer() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOpLookup>> p1 =
|
||||
ffiTestFunctions.lookup("IntptrAdditionClosure");
|
||||
NativeIntptrBinOpLookup intptrAdditionClosure = p1.asFunction();
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> intptrAdditionPointer =
|
||||
intptrAdditionClosure();
|
||||
BinaryOp intptrAddition = intptrAdditionPointer.asFunction();
|
||||
Expect.equals(37, intptrAddition(10, 27));
|
||||
}
|
||||
|
||||
int myPlus(int a, int b) => a + b;
|
||||
|
||||
typedef NativeApplyTo42And74Type = ffi.IntPtr Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>>);
|
||||
|
||||
typedef ApplyTo42And74Type = int Function(
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>>);
|
||||
|
||||
void testFromFunction() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeIntptrBinOp>> pointer =
|
||||
ffi.fromFunction(myPlus);
|
||||
Expect.isNotNull(pointer);
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<NativeApplyTo42And74Type>> p17 =
|
||||
ffiTestFunctions.lookup("ApplyTo42And74");
|
||||
ApplyTo42And74Type applyTo42And74 = p17.asFunction();
|
||||
|
||||
// TODO(dacoharkes): implement this
|
||||
|
||||
// int result = applyTo42And74(pointer);
|
||||
// print(result);
|
||||
}
|
116
tests/standalone_2/ffi/function_structs_test.dart
Normal file
116
tests/standalone_2/ffi/function_structs_test.dart
Normal file
|
@ -0,0 +1,116 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi function pointers with struct
|
||||
// arguments.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import 'coordinate.dart';
|
||||
import 'very_large_struct.dart';
|
||||
|
||||
typedef NativeCoordinateOp = Coordinate Function(Coordinate);
|
||||
|
||||
void main() {
|
||||
testFunctionWithStruct();
|
||||
testFunctionWithStructArray();
|
||||
testFunctionWithVeryLargeStruct();
|
||||
}
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
/// pass a struct to a c function and get a struct as return value
|
||||
void testFunctionWithStruct() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("TransposeCoordinate");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate(10.0, 20.0, null);
|
||||
Coordinate c2 = Coordinate(42.0, 84.0, c1);
|
||||
c1.next = c2;
|
||||
|
||||
Coordinate result = f1(c1);
|
||||
|
||||
Expect.approxEquals(20.0, c1.x);
|
||||
Expect.approxEquals(30.0, c1.y);
|
||||
|
||||
Expect.approxEquals(42.0, result.x);
|
||||
Expect.approxEquals(84.0, result.y);
|
||||
|
||||
c1.free();
|
||||
c2.free();
|
||||
}
|
||||
|
||||
/// pass an array of structs to a c funtion
|
||||
void testFunctionWithStructArray() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeCoordinateOp>> p1 =
|
||||
ffiTestFunctions.lookup("CoordinateElemAt1");
|
||||
NativeCoordinateOp f1 = p1.asFunction();
|
||||
|
||||
Coordinate c1 = Coordinate.allocate(count: 3);
|
||||
Coordinate c2 = c1.elementAt(1);
|
||||
Coordinate c3 = c1.elementAt(2);
|
||||
c1.x = 10.0;
|
||||
c1.y = 10.0;
|
||||
c1.next = c3;
|
||||
c2.x = 20.0;
|
||||
c2.y = 20.0;
|
||||
c2.next = c1;
|
||||
c3.x = 30.0;
|
||||
c3.y = 30.0;
|
||||
c3.next = c2;
|
||||
|
||||
Coordinate result = f1(c1);
|
||||
Expect.approxEquals(20.0, result.x);
|
||||
Expect.approxEquals(20.0, result.y);
|
||||
|
||||
c1.free();
|
||||
}
|
||||
|
||||
typedef VeryLargeStructSum = int Function(VeryLargeStruct);
|
||||
typedef NativeVeryLargeStructSum = ffi.Int64 Function(VeryLargeStruct);
|
||||
|
||||
void testFunctionWithVeryLargeStruct() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeVeryLargeStructSum>> p1 =
|
||||
ffiTestFunctions.lookup("SumVeryLargeStruct");
|
||||
VeryLargeStructSum f = p1.asFunction();
|
||||
|
||||
VeryLargeStruct vls1 = VeryLargeStruct.allocate(count: 2);
|
||||
VeryLargeStruct vls2 = vls1.elementAt(1);
|
||||
List<VeryLargeStruct> structs = [vls1, vls2];
|
||||
for (VeryLargeStruct struct in structs) {
|
||||
struct.a = 1;
|
||||
struct.b = 2;
|
||||
struct.c = 4;
|
||||
struct.d = 8;
|
||||
struct.e = 16;
|
||||
struct.f = 32;
|
||||
struct.g = 64;
|
||||
struct.h = 128;
|
||||
struct.i = 256;
|
||||
struct.j = 512;
|
||||
struct.k = 1024;
|
||||
struct.smallLastField = 1;
|
||||
}
|
||||
vls1.parent = vls2;
|
||||
vls1.numChidlren = 2;
|
||||
vls1.children = vls1;
|
||||
vls2.parent = vls2;
|
||||
vls2.parent = null;
|
||||
vls2.numChidlren = 0;
|
||||
vls2.children = null;
|
||||
|
||||
int result = f(vls1);
|
||||
Expect.equals(2051, result);
|
||||
|
||||
result = f(vls2);
|
||||
Expect.equals(2048, result);
|
||||
|
||||
vls1.free();
|
||||
}
|
284
tests/standalone_2/ffi/function_test.dart
Normal file
284
tests/standalone_2/ffi/function_test.dart
Normal file
|
@ -0,0 +1,284 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi function pointers.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
void main() {
|
||||
testNativeFunctionFromCast();
|
||||
testNativeFunctionFromLookup();
|
||||
test64bitInterpretations();
|
||||
testTruncation();
|
||||
testNativeFunctionDoubles();
|
||||
testNativeFunctionFloats();
|
||||
testNativeFunctionManyArguments1();
|
||||
testNativeFunctionManyArguments2();
|
||||
testNativeFunctionManyArguments3();
|
||||
testNativeFunctionPointer();
|
||||
testNullInt();
|
||||
testNullDouble();
|
||||
testNullManyArgs();
|
||||
testNullPointers();
|
||||
testFloatRounding();
|
||||
}
|
||||
|
||||
ffi.DynamicLibrary ffiTestFunctions =
|
||||
ffi.DynamicLibrary.open("ffi_test_functions");
|
||||
|
||||
typedef NativeBinaryOp = ffi.Int32 Function(ffi.Int32, ffi.Int32);
|
||||
typedef UnaryOp = int Function(int);
|
||||
typedef BinaryOp = int Function(int, int);
|
||||
typedef GenericBinaryOp<T> = int Function(int, T);
|
||||
|
||||
void testNativeFunctionFromCast() {
|
||||
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.NativeFunction<NativeBinaryOp>> p2 = p1.cast();
|
||||
BinaryOp f = p2.asFunction<BinaryOp>();
|
||||
BinaryOp f2 = p2.asFunction<GenericBinaryOp<int>>();
|
||||
p1.free();
|
||||
}
|
||||
|
||||
typedef NativeQuadOpSigned = ffi.Int64 Function(
|
||||
ffi.Int64, ffi.Int32, ffi.Int16, ffi.Int8);
|
||||
typedef QuadOp = int Function(int, int, int, int);
|
||||
typedef NativeQuadOpUnsigned = ffi.Uint64 Function(
|
||||
ffi.Uint64, ffi.Uint32, ffi.Uint16, ffi.Uint8);
|
||||
|
||||
void testNativeFunctionFromLookup() {
|
||||
BinaryOp sumPlus42 =
|
||||
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
|
||||
Expect.equals(49, sumPlus42(3, 4));
|
||||
|
||||
QuadOp intComputation = ffiTestFunctions
|
||||
.lookupFunction<NativeQuadOpSigned, QuadOp>("IntComputation");
|
||||
Expect.equals(625, intComputation(125, 250, 500, 1000));
|
||||
|
||||
Expect.equals(
|
||||
0x7FFFFFFFFFFFFFFF, intComputation(0, 0, 0, 0x7FFFFFFFFFFFFFFF));
|
||||
Expect.equals(
|
||||
-0x8000000000000000, intComputation(0, 0, 0, -0x8000000000000000));
|
||||
}
|
||||
|
||||
void test64bitInterpretations() {
|
||||
QuadOp uintComputation = ffiTestFunctions
|
||||
.lookupFunction<NativeQuadOpUnsigned, QuadOp>("UintComputation");
|
||||
|
||||
// 2 ^ 63 - 1
|
||||
Expect.equals(
|
||||
0x7FFFFFFFFFFFFFFF, uintComputation(0, 0, 0, 0x7FFFFFFFFFFFFFFF));
|
||||
// -2 ^ 63 interpreted as 2 ^ 63
|
||||
Expect.equals(
|
||||
-0x8000000000000000, uintComputation(0, 0, 0, -0x8000000000000000));
|
||||
// -1 interpreted as 2 ^ 64 - 1
|
||||
Expect.equals(-1, uintComputation(0, 0, 0, -1));
|
||||
}
|
||||
|
||||
typedef NativeSenaryOp = ffi.Int64 Function(
|
||||
ffi.Int8, ffi.Int16, ffi.Int32, ffi.Uint8, ffi.Uint16, ffi.Uint32);
|
||||
typedef SenaryOp = int Function(int, int, int, int, int, int);
|
||||
|
||||
void testTruncation() {
|
||||
SenaryOp sumSmallNumbers = ffiTestFunctions
|
||||
.lookupFunction<NativeSenaryOp, SenaryOp>("SumSmallNumbers");
|
||||
|
||||
// TODO(dacoharkes): implement truncation and sign extension in trampolines
|
||||
// for values smaller than 32 bits.
|
||||
sumSmallNumbers(128, 0, 0, 0, 0, 0);
|
||||
sumSmallNumbers(-129, 0, 0, 0, 0, 0);
|
||||
sumSmallNumbers(0, 0, 0, 256, 0, 0);
|
||||
sumSmallNumbers(0, 0, 0, -1, 0, 0);
|
||||
|
||||
sumSmallNumbers(0, 0x8000, 0, 0, 0, 0);
|
||||
sumSmallNumbers(0, 0xFFFFFFFFFFFF7FFF, 0, 0, 0, 0);
|
||||
sumSmallNumbers(0, 0, 0, 0, 0x10000, 0);
|
||||
sumSmallNumbers(0, 0, 0, 0, -1, 0);
|
||||
|
||||
Expect.equals(0xFFFFFFFF80000000, sumSmallNumbers(0, 0, 0x80000000, 0, 0, 0));
|
||||
Expect.equals(
|
||||
0x000000007FFFFFFF, sumSmallNumbers(0, 0, 0xFFFFFFFF7FFFFFFF, 0, 0, 0));
|
||||
Expect.equals(0, sumSmallNumbers(0, 0, 0, 0, 0, 0x100000000));
|
||||
Expect.equals(0xFFFFFFFF, sumSmallNumbers(0, 0, 0, 0, 0, -1));
|
||||
}
|
||||
|
||||
typedef NativeDoubleUnaryOp = ffi.Double Function(ffi.Double);
|
||||
typedef DoubleUnaryOp = double Function(double);
|
||||
|
||||
void testNativeFunctionDoubles() {
|
||||
DoubleUnaryOp times1_337Double = ffiTestFunctions
|
||||
.lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
|
||||
Expect.approxEquals(2.0 * 1.337, times1_337Double(2.0));
|
||||
}
|
||||
|
||||
typedef NativeFloatUnaryOp = ffi.Float Function(ffi.Float);
|
||||
|
||||
void testNativeFunctionFloats() {
|
||||
DoubleUnaryOp times1_337Float = ffiTestFunctions
|
||||
.lookupFunction<NativeFloatUnaryOp, DoubleUnaryOp>("Times1_337Float");
|
||||
Expect.approxEquals(1337.0, times1_337Float(1000.0));
|
||||
}
|
||||
|
||||
typedef NativeOctenaryOp = ffi.IntPtr Function(
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr,
|
||||
ffi.IntPtr);
|
||||
typedef OctenaryOp = int Function(
|
||||
int, int, int, int, int, int, int, int, int, int);
|
||||
|
||||
void testNativeFunctionManyArguments1() {
|
||||
OctenaryOp sumManyInts = ffiTestFunctions
|
||||
.lookupFunction<NativeOctenaryOp, OctenaryOp>("SumManyInts");
|
||||
Expect.equals(55, sumManyInts(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
|
||||
}
|
||||
|
||||
typedef NativeDoubleOctenaryOp = ffi.Double Function(
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double,
|
||||
ffi.Double);
|
||||
typedef DoubleOctenaryOp = double Function(double, double, double, double,
|
||||
double, double, double, double, double, double);
|
||||
|
||||
void testNativeFunctionManyArguments2() {
|
||||
DoubleOctenaryOp sumManyDoubles =
|
||||
ffiTestFunctions.lookupFunction<NativeDoubleOctenaryOp, DoubleOctenaryOp>(
|
||||
"SumManyDoubles");
|
||||
Expect.approxEquals(
|
||||
55.0, sumManyDoubles(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0));
|
||||
}
|
||||
|
||||
typedef NativeVigesimalOp = ffi.Double Function(
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double,
|
||||
ffi.IntPtr,
|
||||
ffi.Float,
|
||||
ffi.IntPtr,
|
||||
ffi.Double);
|
||||
typedef VigesimalOp = double Function(
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double,
|
||||
int,
|
||||
double);
|
||||
|
||||
void testNativeFunctionManyArguments3() {
|
||||
VigesimalOp sumManyNumbers = ffiTestFunctions
|
||||
.lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
|
||||
Expect.approxEquals(
|
||||
210.0,
|
||||
sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13,
|
||||
14.0, 15, 16.0, 17, 18.0, 19, 20.0));
|
||||
}
|
||||
|
||||
typedef Int64PointerUnOp = ffi.Pointer<ffi.Int64> Function(
|
||||
ffi.Pointer<ffi.Int64>);
|
||||
|
||||
void testNativeFunctionPointer() {
|
||||
Int64PointerUnOp assign1337Index1 = ffiTestFunctions
|
||||
.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>("Assign1337Index1");
|
||||
ffi.Pointer<ffi.Int64> p2 = ffi.allocate(count: 2);
|
||||
p2.store(42);
|
||||
p2.elementAt(1).store(1000);
|
||||
ffi.Pointer<ffi.Int64> result = assign1337Index1(p2);
|
||||
Expect.equals(1337, result.load<int>());
|
||||
Expect.equals(1337, p2.elementAt(1).load<int>());
|
||||
Expect.equals(p2.elementAt(1).address, result.address);
|
||||
p2.free();
|
||||
}
|
||||
|
||||
void testNullInt() {
|
||||
BinaryOp sumPlus42 =
|
||||
ffiTestFunctions.lookupFunction<NativeBinaryOp, BinaryOp>("SumPlus42");
|
||||
|
||||
Expect.throws(() => sumPlus42(43, null));
|
||||
}
|
||||
|
||||
void testNullDouble() {
|
||||
DoubleUnaryOp times1_337Double = ffiTestFunctions
|
||||
.lookupFunction<NativeDoubleUnaryOp, DoubleUnaryOp>("Times1_337Double");
|
||||
Expect.throws(() => times1_337Double(null));
|
||||
}
|
||||
|
||||
void testNullManyArgs() {
|
||||
VigesimalOp sumManyNumbers = ffiTestFunctions
|
||||
.lookupFunction<NativeVigesimalOp, VigesimalOp>("SumManyNumbers");
|
||||
Expect.throws(() => sumManyNumbers(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0,
|
||||
11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, null, 20.0));
|
||||
}
|
||||
|
||||
void testNullPointers() {
|
||||
Int64PointerUnOp nullableInt64ElemAt1 =
|
||||
ffiTestFunctions.lookupFunction<Int64PointerUnOp, Int64PointerUnOp>(
|
||||
"NullableInt64ElemAt1");
|
||||
|
||||
ffi.Pointer<ffi.Int64> result = nullableInt64ElemAt1(null);
|
||||
Expect.isNull(result);
|
||||
|
||||
ffi.Pointer<ffi.Int64> p2 = ffi.allocate(count: 2);
|
||||
result = nullableInt64ElemAt1(p2);
|
||||
Expect.isNotNull(result);
|
||||
p2.free();
|
||||
}
|
||||
|
||||
typedef NativeFloatPointerToBool = ffi.Uint8 Function(ffi.Pointer<ffi.Float>);
|
||||
typedef FloatPointerToBool = int Function(ffi.Pointer<ffi.Float>);
|
||||
|
||||
void testFloatRounding() {
|
||||
FloatPointerToBool isRoughly1337 = ffiTestFunctions.lookupFunction<
|
||||
NativeFloatPointerToBool, FloatPointerToBool>("IsRoughly1337");
|
||||
|
||||
ffi.Pointer<ffi.Float> p2 = ffi.allocate();
|
||||
p2.store(1337.0);
|
||||
|
||||
int result = isRoughly1337(p2);
|
||||
Expect.equals(1, result);
|
||||
|
||||
p2.free();
|
||||
}
|
365
tests/standalone_2/ffi/static_checks_test.dart
Normal file
365
tests/standalone_2/ffi/static_checks_test.dart
Normal file
|
@ -0,0 +1,365 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi extra checks
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
void main() {
|
||||
testGetGeneric();
|
||||
testGetGeneric2();
|
||||
testGetVoid();
|
||||
testGetNativeFunction();
|
||||
testGetNativeType();
|
||||
testGetTypeMismatch();
|
||||
testSetGeneric();
|
||||
testSetGeneric2();
|
||||
testSetVoid();
|
||||
testSetNativeFunction();
|
||||
testSetNativeType();
|
||||
testSetTypeMismatch();
|
||||
testAsFunctionGeneric();
|
||||
testAsFunctionGeneric2();
|
||||
testAsFunctionWrongNativeFunctionSignature();
|
||||
testAsFunctionTypeMismatch();
|
||||
testFromFunctionGeneric();
|
||||
testFromFunctionGeneric2();
|
||||
testFromFunctionWrongNativeFunctionSignature();
|
||||
testFromFunctionTypeMismatch();
|
||||
testFromFunctionClosure();
|
||||
testFromFunctionTearOff();
|
||||
testLookupFunctionGeneric();
|
||||
testLookupFunctionGeneric2();
|
||||
testLookupFunctionWrongNativeFunctionSignature();
|
||||
testLookupFunctionTypeMismatch();
|
||||
testNativeFunctionSignatureInvalidReturn();
|
||||
testNativeFunctionSignatureInvalidParam();
|
||||
testNativeFunctionSignatureInvalidOptionalNamed();
|
||||
testNativeFunctionSignatureInvalidOptionalPositional();
|
||||
}
|
||||
|
||||
typedef Int8UnOp = ffi.Int8 Function(ffi.Int8);
|
||||
typedef IntUnOp = int Function(int);
|
||||
|
||||
void testGetGeneric() {
|
||||
int generic(ffi.Pointer p) {
|
||||
int result;
|
||||
result = p.load<int>(); //# 20: compile-time error
|
||||
return result;
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
p.store(123);
|
||||
ffi.Pointer loseType = p;
|
||||
generic(loseType);
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testGetGeneric2() {
|
||||
T generic<T extends Object>() {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
p.store(123);
|
||||
T result;
|
||||
result = p.load<T>(); //# 21: compile-time error
|
||||
p.free();
|
||||
return result;
|
||||
}
|
||||
|
||||
generic<int>();
|
||||
}
|
||||
|
||||
void testGetVoid() {
|
||||
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.Void> p2 = p1.cast();
|
||||
|
||||
p2.load<int>(); //# 22: compile-time error
|
||||
|
||||
p1.free();
|
||||
}
|
||||
|
||||
void testGetNativeFunction() {
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.fromAddress(1337);
|
||||
IntUnOp f = p.load(); //# 23: compile-time error
|
||||
}
|
||||
|
||||
void testGetNativeType() {
|
||||
// Is it possible to obtain a ffi.Pointer<ffi.NativeType> at all?
|
||||
}
|
||||
|
||||
void testGetTypeMismatch() {
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int16>> p = ffi.allocate();
|
||||
ffi.Pointer<ffi.Int16> typedNull = null;
|
||||
p.store(typedNull);
|
||||
|
||||
// this fails to compile due to type mismatch
|
||||
ffi.Pointer<ffi.Int8> p2 = p.load(); //# 25: compile-time error
|
||||
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testSetGeneric() {
|
||||
void generic(ffi.Pointer p) {
|
||||
p.store(123); //# 26: compile-time error
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
p.store(123);
|
||||
ffi.Pointer loseType = p;
|
||||
generic(loseType);
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testSetGeneric2() {
|
||||
void generic<T extends Object>(T arg) {
|
||||
ffi.Pointer<ffi.Int8> p = ffi.allocate();
|
||||
p.store(arg); //# 27: compile-time error
|
||||
p.free();
|
||||
}
|
||||
|
||||
generic<int>(123);
|
||||
}
|
||||
|
||||
void testSetVoid() {
|
||||
ffi.Pointer<ffi.IntPtr> p1 = ffi.allocate();
|
||||
ffi.Pointer<ffi.Void> p2 = p1.cast();
|
||||
|
||||
p2.store(1234); //# 28: compile-time error
|
||||
|
||||
p1.free();
|
||||
}
|
||||
|
||||
void testSetNativeFunction() {
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.fromAddress(1337);
|
||||
IntUnOp f = (a) => a + 1;
|
||||
p.store(f); //# 29: compile-time error
|
||||
}
|
||||
|
||||
void testSetNativeType() {
|
||||
// Is it possible to obtain a ffi.Pointer<ffi.NativeType> at all?
|
||||
}
|
||||
|
||||
void testSetTypeMismatch() {
|
||||
// the pointer to pointer types must match up
|
||||
ffi.Pointer<ffi.Int8> pHelper = ffi.allocate();
|
||||
pHelper.store(123);
|
||||
|
||||
ffi.Pointer<ffi.Pointer<ffi.Int16>> p = ffi.allocate();
|
||||
|
||||
// this fails to compile due to type mismatch
|
||||
p.store(pHelper); //# 40: compile-time error
|
||||
|
||||
pHelper.free();
|
||||
p.free();
|
||||
}
|
||||
|
||||
void testAsFunctionGeneric() {
|
||||
T generic<T extends Function>() {
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.fromAddress(1337);
|
||||
Function f;
|
||||
f = p.asFunction<T>(); //# 11: compile-time error
|
||||
return f;
|
||||
}
|
||||
|
||||
generic<IntUnOp>();
|
||||
}
|
||||
|
||||
void testAsFunctionGeneric2() {
|
||||
generic(ffi.Pointer<ffi.NativeFunction> p) {
|
||||
Function f;
|
||||
f = p.asFunction<IntUnOp>(); //# 12: compile-time error
|
||||
return f;
|
||||
}
|
||||
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.fromAddress(1337);
|
||||
generic(p);
|
||||
}
|
||||
|
||||
void testAsFunctionWrongNativeFunctionSignature() {
|
||||
ffi.Pointer<ffi.NativeFunction<IntUnOp>> p;
|
||||
Function f = p.asFunction<IntUnOp>(); //# 13: compile-time error
|
||||
}
|
||||
|
||||
typedef IntBinOp = int Function(int, int);
|
||||
|
||||
void testAsFunctionTypeMismatch() {
|
||||
ffi.Pointer<ffi.NativeFunction<Int8UnOp>> p = ffi.fromAddress(1337);
|
||||
IntBinOp f = p.asFunction(); //# 14: compile-time error
|
||||
}
|
||||
|
||||
typedef NativeDoubleUnOp = ffi.Double Function(ffi.Double);
|
||||
typedef DoubleUnOp = double Function(double);
|
||||
|
||||
double myTimesThree(double d) => d * 3;
|
||||
|
||||
int myTimesFour(int i) => i * 4;
|
||||
|
||||
void testFromFunctionGeneric() {
|
||||
ffi.Pointer<ffi.NativeFunction> generic<T extends Function>(T f) {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeDoubleUnOp>> result;
|
||||
result = ffi.fromFunction(f); //# 70: compile-time error
|
||||
return result;
|
||||
}
|
||||
|
||||
generic(myTimesThree);
|
||||
}
|
||||
|
||||
void testFromFunctionGeneric2() {
|
||||
ffi.Pointer<ffi.NativeFunction<T>> generic<T extends Function>() {
|
||||
ffi.Pointer<ffi.NativeFunction<T>> result;
|
||||
result = ffi.fromFunction(myTimesThree); //# 71: compile-time error
|
||||
return result;
|
||||
}
|
||||
|
||||
generic<NativeDoubleUnOp>();
|
||||
}
|
||||
|
||||
void testFromFunctionWrongNativeFunctionSignature() {
|
||||
ffi.fromFunction<IntUnOp>(myTimesFour); //# 72: compile-time error
|
||||
}
|
||||
|
||||
void testFromFunctionTypeMismatch() {
|
||||
ffi.Pointer<ffi.NativeFunction<NativeDoubleUnOp>> p;
|
||||
p = ffi.fromFunction(myTimesFour); //# 73: compile-time error
|
||||
}
|
||||
|
||||
void testFromFunctionClosure() {
|
||||
DoubleUnOp someClosure = (double z) => z / 27.0;
|
||||
ffi.Pointer<ffi.NativeFunction<NativeDoubleUnOp>> p;
|
||||
p = ffi.fromFunction(someClosure); //# 74: compile-time error
|
||||
}
|
||||
|
||||
class X {
|
||||
double tearoff(double d) => d / 27.0;
|
||||
}
|
||||
|
||||
DoubleUnOp fld = null;
|
||||
|
||||
void testFromFunctionTearOff() {
|
||||
fld = X().tearoff;
|
||||
ffi.Pointer<ffi.NativeFunction<NativeDoubleUnOp>> p;
|
||||
p = ffi.fromFunction(fld); //# 75: compile-time error
|
||||
}
|
||||
|
||||
void testLookupFunctionGeneric() {
|
||||
Function generic<T extends Function>() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Function result;
|
||||
result = l.lookupFunction<T, DoubleUnOp>("cos"); //# 15: compile-time error
|
||||
return result;
|
||||
}
|
||||
|
||||
generic<NativeDoubleUnOp>();
|
||||
}
|
||||
|
||||
void testLookupFunctionGeneric2() {
|
||||
Function generic<T extends Function>() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
Function result;
|
||||
result = //# 16: compile-time error
|
||||
l.lookupFunction<NativeDoubleUnOp, T>("cos"); //# 16: compile-time error
|
||||
return result;
|
||||
}
|
||||
|
||||
generic<DoubleUnOp>();
|
||||
}
|
||||
|
||||
void testLookupFunctionWrongNativeFunctionSignature() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
l.lookupFunction<IntUnOp, IntUnOp>("cos"); //# 17: compile-time error
|
||||
}
|
||||
|
||||
void testLookupFunctionTypeMismatch() {
|
||||
ffi.DynamicLibrary l = ffi.DynamicLibrary.open("ffi_test_dynamic_library");
|
||||
l.lookupFunction<NativeDoubleUnOp, IntUnOp>("cos"); //# 18: compile-time error
|
||||
}
|
||||
|
||||
// TODO(dacoharkes): make the next 4 test compile errors
|
||||
typedef Invalid1 = int Function(ffi.Int8);
|
||||
typedef Invalid2 = ffi.Int8 Function(int);
|
||||
typedef Invalid3 = ffi.Int8 Function({ffi.Int8 named});
|
||||
typedef Invalid4 = ffi.Int8 Function([ffi.Int8 positional]);
|
||||
|
||||
void testNativeFunctionSignatureInvalidReturn() {
|
||||
// ffi.Pointer<ffi.NativeFunction<Invalid1>> p = ffi.fromAddress(999);
|
||||
}
|
||||
|
||||
void testNativeFunctionSignatureInvalidParam() {
|
||||
// ffi.Pointer<ffi.NativeFunction<Invalid2>> p = ffi.fromAddress(999);
|
||||
}
|
||||
|
||||
void testNativeFunctionSignatureInvalidOptionalNamed() {
|
||||
// ffi.Pointer<ffi.NativeFunction<Invalid3>> p = ffi.fromAddress(999);
|
||||
}
|
||||
|
||||
void testNativeFunctionSignatureInvalidOptionalPositional() {
|
||||
// ffi.Pointer<ffi.NativeFunction<Invalid4>> p = ffi.fromAddress(999);
|
||||
}
|
||||
|
||||
// error on missing field annotation
|
||||
@ffi.struct
|
||||
class TestStruct extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
double x;
|
||||
|
||||
double y; //# 50: compile-time error
|
||||
}
|
||||
|
||||
// error on missing struct annotation
|
||||
class TestStruct2 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double() //# 51: compile-time error
|
||||
double x; //# 51: compile-time error
|
||||
}
|
||||
|
||||
// error on missing annotation on subtype
|
||||
@ffi.struct
|
||||
class TestStruct3 extends TestStruct {
|
||||
double z; //# 52: compile-time error
|
||||
}
|
||||
|
||||
// error on double annotation
|
||||
@ffi.struct
|
||||
class TestStruct4 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
@ffi.Double() //# 53: compile-time error
|
||||
double z;
|
||||
}
|
||||
|
||||
// error on annotation not matching up
|
||||
@ffi.struct
|
||||
class TestStruct5 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Int64() //# 54: compile-time error
|
||||
double z; //# 54: compile-time error
|
||||
}
|
||||
|
||||
// error on annotation not matching up
|
||||
@ffi.struct
|
||||
class TestStruct6 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Void() //# 55: compile-time error
|
||||
double z; //# 55: compile-time error
|
||||
}
|
||||
|
||||
// error on annotation not matching up
|
||||
@ffi.struct
|
||||
class TestStruct7 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.NativeType() //# 56: compile-time error
|
||||
double z; //# 56: compile-time error
|
||||
}
|
||||
|
||||
// error on field initializer on field
|
||||
@ffi.struct
|
||||
class TestStruct8 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double() //# 57: compile-time error
|
||||
double z = 10.0; //# 57: compile-time error
|
||||
}
|
||||
|
||||
// error on field initializer in constructor
|
||||
@ffi.struct
|
||||
class TestStruct9 extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Double()
|
||||
double z;
|
||||
|
||||
TestStruct9() : z = 0.0 {} //# 58: compile-time error
|
||||
}
|
136
tests/standalone_2/ffi/structs_test.dart
Normal file
136
tests/standalone_2/ffi/structs_test.dart
Normal file
|
@ -0,0 +1,136 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi struct pointers.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import 'coordinate_bare.dart' as bare;
|
||||
import 'coordinate_manual.dart' as manual;
|
||||
import 'coordinate.dart';
|
||||
|
||||
void main() {
|
||||
testStructAllocate();
|
||||
testStructFromAddress();
|
||||
testStructWithNulls();
|
||||
testBareStruct();
|
||||
testManualStruct();
|
||||
testTypeTest();
|
||||
}
|
||||
|
||||
/// allocates each coordinate separately in c memory
|
||||
void testStructAllocate() {
|
||||
Coordinate c1 = Coordinate(10.0, 10.0, null);
|
||||
Coordinate c2 = Coordinate(20.0, 20.0, c1);
|
||||
Coordinate c3 = Coordinate(30.0, 30.0, c2);
|
||||
c1.next = c3;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(30.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(20.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
|
||||
c1.free();
|
||||
c2.free();
|
||||
c3.free();
|
||||
}
|
||||
|
||||
/// allocates coordinates consecutively in c memory
|
||||
void testStructFromAddress() {
|
||||
Coordinate c1 = Coordinate.allocate(count: 3);
|
||||
Coordinate c2 = c1.elementAt(1);
|
||||
Coordinate c3 = c1.elementAt(2);
|
||||
c1.x = 10.0;
|
||||
c1.y = 10.0;
|
||||
c1.next = c3;
|
||||
c2.x = 20.0;
|
||||
c2.y = 20.0;
|
||||
c2.next = c1;
|
||||
c3.x = 30.0;
|
||||
c3.y = 30.0;
|
||||
c3.next = c2;
|
||||
|
||||
Coordinate currentCoordinate = c1;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(30.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(20.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
|
||||
c1.free();
|
||||
}
|
||||
|
||||
void testStructWithNulls() {
|
||||
Coordinate coordinate = Coordinate(10.0, 10.0, null);
|
||||
Expect.isNull(coordinate.next);
|
||||
coordinate.next = coordinate;
|
||||
Expect.isNotNull(coordinate.next);
|
||||
coordinate.next = null;
|
||||
Expect.isNull(coordinate.next);
|
||||
coordinate.free();
|
||||
}
|
||||
|
||||
void testBareStruct() {
|
||||
int structSize = ffi.sizeOf<ffi.Double>() * 2 + ffi.sizeOf<ffi.IntPtr>();
|
||||
bare.Coordinate c1 = ffi.allocate<ffi.Uint8>(count: structSize * 3).cast();
|
||||
bare.Coordinate c2 = c1.offsetBy(structSize).cast();
|
||||
bare.Coordinate c3 = c1.offsetBy(structSize * 2).cast();
|
||||
c1.x = 10.0;
|
||||
c1.y = 10.0;
|
||||
c1.next = c3;
|
||||
c2.x = 20.0;
|
||||
c2.y = 20.0;
|
||||
c2.next = c1;
|
||||
c3.x = 30.0;
|
||||
c3.y = 30.0;
|
||||
c3.next = c2;
|
||||
|
||||
bare.Coordinate currentCoordinate = c1;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(30.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(20.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
|
||||
c1.free();
|
||||
}
|
||||
|
||||
void testManualStruct() {
|
||||
manual.Coordinate c1 = manual.Coordinate(10.0, 10.0, null);
|
||||
manual.Coordinate c2 = manual.Coordinate(20.0, 20.0, c1);
|
||||
manual.Coordinate c3 = manual.Coordinate(30.0, 30.0, c2);
|
||||
c1.next = c3;
|
||||
|
||||
manual.Coordinate currentCoordinate = c1;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(30.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(20.0, currentCoordinate.x);
|
||||
currentCoordinate = currentCoordinate.next;
|
||||
Expect.equals(10.0, currentCoordinate.x);
|
||||
|
||||
c1.free();
|
||||
c2.free();
|
||||
c3.free();
|
||||
}
|
||||
|
||||
void testTypeTest() {
|
||||
Coordinate c = Coordinate(10, 10, null);
|
||||
Expect.isTrue(c is ffi.Pointer);
|
||||
Expect.isTrue(c is ffi.Pointer<ffi.Void>);
|
||||
c.free();
|
||||
}
|
19
tests/standalone_2/ffi/subtype_test.dart
Normal file
19
tests/standalone_2/ffi/subtype_test.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
// 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.
|
||||
//
|
||||
// Dart test program for testing dart:ffi Pointer subtypes.
|
||||
|
||||
library FfiTest;
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import 'cstring.dart';
|
||||
|
||||
void main() {
|
||||
CString cs = CString.toUtf8("hello world!");
|
||||
|
||||
Expect.equals("hello world!", cs.fromUtf8());
|
||||
|
||||
cs.free();
|
||||
}
|
67
tests/standalone_2/ffi/very_large_struct.dart
Normal file
67
tests/standalone_2/ffi/very_large_struct.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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.
|
||||
|
||||
library FfiTestCoordinateBare;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
/// Large sample struct for dart:ffi library.
|
||||
@ffi.struct
|
||||
class VeryLargeStruct extends ffi.Pointer<ffi.Void> {
|
||||
@ffi.Int8()
|
||||
int a;
|
||||
|
||||
@ffi.Int16()
|
||||
int b;
|
||||
|
||||
@ffi.Int32()
|
||||
int c;
|
||||
|
||||
@ffi.Int64()
|
||||
int d;
|
||||
|
||||
@ffi.Uint8()
|
||||
int e;
|
||||
|
||||
@ffi.Uint16()
|
||||
int f;
|
||||
|
||||
@ffi.Uint32()
|
||||
int g;
|
||||
|
||||
@ffi.Uint64()
|
||||
int h;
|
||||
|
||||
@ffi.IntPtr()
|
||||
int i;
|
||||
|
||||
@ffi.Float()
|
||||
double j;
|
||||
|
||||
@ffi.Double()
|
||||
double k;
|
||||
|
||||
@ffi.Pointer()
|
||||
VeryLargeStruct parent;
|
||||
|
||||
@ffi.IntPtr()
|
||||
int numChidlren;
|
||||
|
||||
@ffi.Pointer()
|
||||
VeryLargeStruct children;
|
||||
|
||||
@ffi.Int8()
|
||||
int smallLastField;
|
||||
|
||||
// generated by @ffi.struct annotation
|
||||
external static int sizeOf();
|
||||
|
||||
VeryLargeStruct offsetBy(int offsetInBytes) =>
|
||||
super.offsetBy(offsetInBytes).cast();
|
||||
|
||||
VeryLargeStruct elementAt(int index) => offsetBy(sizeOf() * index);
|
||||
|
||||
static VeryLargeStruct allocate({int count: 1}) =>
|
||||
ffi.allocate<ffi.Uint8>(count: count * sizeOf()).cast();
|
||||
}
|
|
@ -6,6 +6,9 @@
|
|||
link_natives_lazily_test: SkipByDesign # Not supported.
|
||||
no_allow_absolute_addresses_test: SkipByDesign # Not supported.
|
||||
|
||||
[ $builder_tag == asan ]
|
||||
ffi/data_not_asan_test: Skip # this test tries to allocate too much memory on purpose
|
||||
|
||||
[ $compiler == app_jit ]
|
||||
full_coverage_test: Skip # Platform.executable
|
||||
io/code_collection_test: Skip # Platform.executable
|
||||
|
@ -22,7 +25,12 @@ io/test_extension_test: Skip # Platform.executable
|
|||
io/test_runner_test: RuntimeError # Issue 33168
|
||||
regress_26031_test: Skip # Platform.resolvedExecutable
|
||||
|
||||
[ $runtime == dart_precompiled ]
|
||||
ffi: RuntimeError # https://github.com/dart-lang/sdk/issues/35765
|
||||
ffi/static_checks_test: Skip # https://github.com/dart-lang/sdk/issues/35765
|
||||
|
||||
[ $runtime == vm ]
|
||||
ffi/enable_ffi_test: Fail # test designed to fail: --enable-ffi=false with import dart:ffi
|
||||
io/test_runner_test: Skip # Spawns a process which runs in Dart2 mode.
|
||||
|
||||
[ $system == android ]
|
||||
|
@ -89,6 +97,9 @@ io/http_server_close_response_after_error_test: Pass, Timeout # Issue 28370: tim
|
|||
[ $runtime == dart_precompiled && $system == linux && ($arch == simarm || $arch == simarm64 || $arch == x64) ]
|
||||
io/stdout_stderr_non_blocking_test: Pass, Timeout # Issue 35192
|
||||
|
||||
[ $runtime != dart_precompiled && $runtime != vm ]
|
||||
ffi: SkipByDesign # ffi is only supported on vm
|
||||
|
||||
[ $runtime == vm && !$checked && !$strong ]
|
||||
io/file_constructor_test: RuntimeError
|
||||
|
||||
|
@ -111,3 +122,6 @@ io/skipping_dart2js_compilations_test: Skip # Spawns process in Dart2 mode.
|
|||
full_coverage_test: Skip # TODO(vegorov) SIMDBC interpreter doesn't support coverage yet.
|
||||
link_natives_lazily_test: SkipByDesign # SIMDBC interpreter doesn't support lazy linking of natives.
|
||||
no_lazy_dispatchers_test: SkipByDesign # SIMDBC interpreter doesn't support --no_lazy_dispatchers
|
||||
|
||||
[ $arch != x64 || $system != linux && $system != macos ]
|
||||
ffi: Skip # ffi not yet supported on other systems than linux/macos 64
|
||||
|
|
Loading…
Reference in a new issue