1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-05 09:20:04 +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:
Daco Harkes 2019-02-13 12:42:47 +00:00 committed by Samir Jindel
parent 671865cd1a
commit 7d46d4b5cb
89 changed files with 6792 additions and 52 deletions

4
.gitignore vendored
View File

@ -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

View File

@ -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",
]

View File

@ -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,

View File

@ -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;

View File

@ -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});
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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');
}

View 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);
}

View 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);

View 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 {}

View File

@ -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");

View File

@ -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",

View 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;
}

View 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
View 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

View 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

View 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;
}
}

View 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 {}

View 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";
}

View 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

View File

@ -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 = []

View File

@ -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);

View File

@ -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) \

View File

@ -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
}

View File

@ -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)

View File

@ -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);
}

View File

@ -2030,6 +2030,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function);
case RawFunction::kSignatureFunction:
case RawFunction::kIrregexpFunction:
case RawFunction::kFfiTrampoline:
break;
}
UNREACHABLE();

View File

@ -383,6 +383,7 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
break;
case RawFunction::kSignatureFunction:
case RawFunction::kIrregexpFunction:
case RawFunction::kFfiTrampoline:
UNREACHABLE();
}
if (needs_expr_temp_) {

View File

@ -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;

View 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

View File

@ -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

View File

@ -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();

View File

@ -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);

View 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, &not_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(&not_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, &not_smi, Assembler::kNearJump);
// Smi
__ SmiUntag(reg);
__ jmp(&done, Assembler::kNearJump);
// Mint
__ Bind(&not_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, &not_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(&not_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, &not_null, Assembler::kNearJump);
__ LoadObject(RAX, Object::null_object());
__ jmp(&done);
// Backup result value (to avoid GC).
__ Bind(&not_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)

View File

@ -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, \

View File

@ -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) {

View File

@ -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(); }

View File

@ -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);

View File

@ -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) \

View File

@ -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();
{

View File

@ -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

View File

@ -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);
}

View File

@ -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) {

View File

@ -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_)

View File

@ -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));

View File

@ -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) {

View File

@ -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,

View File

@ -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);

View File

@ -41,6 +41,7 @@ namespace dart {
V(RangeError) \
V(NullError) \
V(NullErrorWithSelector) \
V(ArgumentNullError) \
V(ArgumentError) \
V(ArgumentErrorUnboxedInt64) \
V(IntegerDivisionByZeroException) \

View File

@ -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

View File

@ -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;

View File

@ -48,6 +48,7 @@ class RawContext;
class RawContextScope;
class RawDouble;
class RawExceptionHandlers;
class RawFfiTrampolineData;
class RawField;
class RawFloat32x4;
class RawFloat64x2;

View File

@ -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

View File

@ -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",

View 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;
}
}

View 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");
}

View 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));
}

View 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");
}

View 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");
}

View 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");
}

View 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");
}

View File

@ -183,6 +183,7 @@ _full_sdk_libraries = [
"convert",
"core",
"developer",
"ffi",
"html",
"_http",
"indexed_db",

View File

@ -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,

View 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();

View 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
View 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;
}
}

View 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"
]

View 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 {}

View File

@ -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"

View File

@ -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"

View 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;
}
}

View 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;
}

View 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;
}
}

View 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;
}
}

View 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
}

View 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);
}

View 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);
}

View 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();
}

View 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);
}

View 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();
}

View 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();
}

View 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
}

View 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();
}

View 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();
}

View 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();
}

View File

@ -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