From 597cd06aec7f64b3defd482e84f5d75bef15705b Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Tue, 8 Oct 2019 16:49:41 +0000 Subject: [PATCH] [vm/ffi] Pointer load and store as extension methods Issue: https://github.com/dart-lang/sdk/issues/37773 Change-Id: I836d6305b613cf05590d872874f4517831be3e08 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-dartkb-linux-debug-simarm64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-dartkb-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-mac-debug-simdbc64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-reload-mac-release-simdbc64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-precomp-mac-release-simarm_x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118992 Reviewed-by: Samir Jindel Reviewed-by: Martin Kustermann --- pkg/vm/lib/transformations/ffi_use_sites.dart | 4 + runtime/lib/ffi.cc | 20 +- runtime/tools/ffi/sdk_lib_ffi_generator.dart | 195 ++++++++ runtime/vm/compiler/frontend/kernel_to_il.cc | 6 + runtime/vm/object.cc | 1 - sdk/lib/_internal/vm/lib/ffi_patch.dart | 203 ++++++++ sdk/lib/ffi/ffi.dart | 445 +++++++++++++++++- sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart | 203 ++++++++ sdk_nnbd/lib/ffi/ffi.dart | 445 +++++++++++++++++- tests/ffi/extension_methods_test.dart | 81 ++++ 10 files changed, 1578 insertions(+), 25 deletions(-) create mode 100644 runtime/tools/ffi/sdk_lib_ffi_generator.dart create mode 100644 tests/ffi/extension_methods_test.dart diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart index f3f74da32e3..463d4dc571d 100644 --- a/pkg/vm/lib/transformations/ffi_use_sites.dart +++ b/pkg/vm/lib/transformations/ffi_use_sites.dart @@ -364,6 +364,10 @@ class _FfiUseSiteTransformer extends FfiTransformer { // this rewiring. final DartType pointerType = node.receiver.getStaticType(env); final DartType nativeType = _pointerTypeGetTypeArg(pointerType); + if (nativeType is TypeParameterType) { + // Do not rewire generic invocations. + return node; + } final Class nativeClass = (nativeType as InterfaceType).classNode; final NativeType nt = getType(nativeClass); if (optimizedTypes.contains(nt)) { diff --git a/runtime/lib/ffi.cc b/runtime/lib/ffi.cc index b751f760689..43eb321e519 100644 --- a/runtime/lib/ffi.cc +++ b/runtime/lib/ffi.cc @@ -50,15 +50,6 @@ static void CheckSized(const AbstractType& type_arg) { // The following functions are runtime checks on arguments. -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( @@ -294,16 +285,10 @@ CLASS_LIST_FFI_NUMERIC(DEFINE_NATIVE_ENTRY_STORE) DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 2) { GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0)); - GET_NATIVE_ARGUMENT(Instance, new_value, arguments->NativeArgAt(1)); + GET_NON_NULL_NATIVE_ARGUMENT(Pointer, new_value, arguments->NativeArgAt(1)); AbstractType& pointer_type_arg = AbstractType::Handle(pointer.type_argument()); - if (new_value.IsNull()) { - const String& error = String::Handle( - String::NewFormatted("Argument to Pointer.store is null.")); - Exceptions::ThrowArgumentError(error); - } - auto& new_value_type = AbstractType::Handle(zone, new_value.GetType(Heap::kNew)); if (!new_value_type.IsSubtypeOf(pointer_type_arg, Heap::kNew)) { @@ -315,9 +300,8 @@ DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 2) { } ASSERT(IsPointerType(pointer_type_arg)); - ASSERT(new_value.IsPointer()); uword* slot = reinterpret_cast(pointer.NativeAddress()); - *slot = AsPointer(new_value).NativeAddress(); + *slot = new_value.NativeAddress(); return Object::null(); } diff --git a/runtime/tools/ffi/sdk_lib_ffi_generator.dart b/runtime/tools/ffi/sdk_lib_ffi_generator.dart new file mode 100644 index 00000000000..11e5ed46e36 --- /dev/null +++ b/runtime/tools/ffi/sdk_lib_ffi_generator.dart @@ -0,0 +1,195 @@ +// 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 generates the extension methods public API and the extension +// methods patch file for all integers, double, and float. +// The PointerPointer and PointerStruct extension are written by hand since +// those are not repetitive. + +import 'dart:io'; + +// +// Configuration. +// + +const Map nativeToDartType = { + "Int8": "int", + "Int16": "int", + "Int32": "int", + "Int64": "int", + "Uint8": "int", + "Uint16": "int", + "Uint32": "int", + "Uint64": "int", + "IntPtr": "int", + "Float": "double", + "Double": "double", +}; + +// +// Generator. +// + +main(List arguments) { + final parsedArgs = parseArguments(arguments); + + generate(parsedArgs.path, "ffi.g.dart", generatePublicExtension); + generate(parsedArgs.path, "ffi_patch.g.dart", generatePatchExtension); +} + +void generate(Uri path, String fileName, + Function(StringBuffer, String, String) generator) { + final buffer = StringBuffer(); + generateHeader(buffer); + nativeToDartType.forEach((String nativeType, String dartType) { + generator(buffer, nativeType, dartType); + }); + generateFooter(buffer); + + final fullPath = path.resolve(fileName).path; + new File(fullPath).writeAsStringSync(buffer.toString()); + final fmtResult = Process.runSync(dartfmtPath().path, ["-w", fullPath]); + if (fmtResult.exitCode != 0) { + throw Exception( + "Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n"); + } + print("Generated $fullPath."); +} + +void generateHeader(StringBuffer buffer) { + final header = """ +// +// The following code is generated, do not edit by hand. +// +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. +// + +"""; + + buffer.write(header); +} + +void generatePublicExtension( + StringBuffer buffer, String nativeType, String dartType) { + final storeTrunctateInt = """ + /// Note that ints which do not fit in [$nativeType] are truncated. +"""; + + final storeTrunctateDouble = """ + /// Note that doubles stored into Pointer<[Float]> lose precision. +"""; + + final storeTruncate = + isInt(nativeType) ? storeTrunctateInt : storeTrunctateDouble; + + final loadSignExtendInt = """ + /// Note that ints are signextended. +"""; + + final loadSignExtend = isInt(nativeType) ? loadSignExtendInt : ""; + + buffer.write(""" +/// Extension on [Pointer] specialized for the type argument [$nativeType]. +extension ${nativeType}Pointer on Pointer<$nativeType> { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. +$loadSignExtend /// + /// Note that [address] needs to be aligned to the size of [$nativeType]. + external $dartType get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. +$storeTruncate /// + /// Note that [address] needs to be aligned to the size of [$nativeType]. + external void set value($dartType value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. +$loadSignExtend /// + /// Note that [address] needs to be aligned to the size of [$nativeType]. + external $dartType operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. +$storeTruncate /// + /// Note that [address] needs to be aligned to the size of [$nativeType]. + external void operator []=(int index, $dartType value); +} + +"""); +} + +void generatePatchExtension( + StringBuffer buffer, String nativeType, String dartType) { + buffer.write(""" +extension ${nativeType}Pointer on Pointer<$nativeType> { + @patch + $dartType get value => _load$nativeType(this); + + @patch + void set value($dartType value) => _store$nativeType(this, value); + + @patch + $dartType operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, $dartType value) => this.elementAt(index).value = value; +} + +"""); +} + +void generateFooter(StringBuffer buffer) { + final footer = """ +// +// End of generated code. +// +"""; + + buffer.write(footer); +} + +// +// Helper functions. +// + +bool isInt(String type) => type.startsWith("Int") || type.startsWith("Uint"); + +class Arguments { + final Uri path; + Arguments(this.path); +} + +Arguments parseArguments(List arguments) { + final parsedArgs = Map(); + String flag = null; + for (final String arg in arguments) { + if (flag == "path") { + parsedArgs[flag] = Uri.parse(arg); + flag = null; + } else if (arg == "-p" || arg == "--path") { + flag = "path"; + } else { + throw Exception("Unknown argument: $arg"); + } + } + + Uri path = parsedArgs["path"]; + if (path == null) { + path = Platform.script; + print("No path provided, generating files next to generator."); + } + + return Arguments(path); +} + +Uri dartfmtPath() { + // TODO(dacoharkes): Use "../../../tools/sdks/dart-sdk/bin/dartfmt" when the + // pinned fully supports extension methods. + return Uri.parse("dartfmt"); +} diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc index 300bf4b3218..3d5f31398f5 100644 --- a/runtime/vm/compiler/frontend/kernel_to_il.cc +++ b/runtime/vm/compiler/frontend/kernel_to_il.cc @@ -1074,6 +1074,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( ASSERT(function.NumParameters() == 1); body += LoadLocal(parsed_function_->RawParameterVariable(0)); // Pointer. + body += CheckNullOptimized(TokenPosition::kNoSource, + String::ZoneHandle(Z, function.name())); body += LoadNativeField(Slot::Pointer_c_memory_address()); body += UnboxTruncate(kUnboxedIntPtr); // Truncating, so signed is ok. body += ConvertIntptrToUntagged(); // Requires signed intptr. @@ -1169,6 +1171,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( // But we type check it as a method on a generic class at runtime. body += LoadLocal(arg_value); body += LoadLocal(arg_pointer); + body += CheckNullOptimized(TokenPosition::kNoSource, + String::ZoneHandle(Z, function.name())); // We pass the Pointer type argument as instantiator_type_args. // // Call sites to this recognized method are guaranteed to pass a @@ -1189,6 +1193,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod( ASSERT(function.NumParameters() == 2); body += LoadLocal(arg_pointer); // Pointer. + body += CheckNullOptimized(TokenPosition::kNoSource, + String::ZoneHandle(Z, function.name())); body += LoadNativeField(Slot::Pointer_c_memory_address()); body += UnboxTruncate(kUnboxedIntPtr); // Truncating, so signed is ok. body += ConvertIntptrToUntagged(); // Requires signed intptr. diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 1eaf8fe3751..96817b10a28 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -21494,7 +21494,6 @@ RawDynamicLibrary* DynamicLibrary::New(void* handle, Heap::Space space) { } bool Pointer::IsPointer(const Instance& obj) { - ASSERT(!obj.IsNull()); return RawObject::IsFfiPointerClassId(obj.raw()->GetClassId()); } diff --git a/sdk/lib/_internal/vm/lib/ffi_patch.dart b/sdk/lib/_internal/vm/lib/ffi_patch.dart index 49ac805b447..e071ea0f908 100644 --- a/sdk/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk/lib/_internal/vm/lib/ffi_patch.dart @@ -147,6 +147,8 @@ int _abi() // allocating a Pointer with in elementAt/offsetBy. Allocating these pointers // and GCing new spaces takes a lot of the benchmark time. The next speedup is // getting rid of these allocations by inlining these functions. +// +// TODO(37773): Change _loadInt8 etc to take an index. int _loadInt8(Pointer pointer) native "Ffi_loadInt8"; int _loadInt16(Pointer pointer) native "Ffi_loadInt16"; @@ -236,3 +238,204 @@ Pointer _elementAtDouble(Pointer pointer, int index) => Pointer> _elementAtPointer( Pointer> pointer, int index) => Pointer.fromAddress(pointer.address + _intPtrSize * index); + +// +// The following code is generated, do not edit by hand. +// +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. +// + +extension Int8Pointer on Pointer { + @patch + int get value => _loadInt8(this); + + @patch + void set value(int value) => _storeInt8(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int16Pointer on Pointer { + @patch + int get value => _loadInt16(this); + + @patch + void set value(int value) => _storeInt16(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int32Pointer on Pointer { + @patch + int get value => _loadInt32(this); + + @patch + void set value(int value) => _storeInt32(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int64Pointer on Pointer { + @patch + int get value => _loadInt64(this); + + @patch + void set value(int value) => _storeInt64(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint8Pointer on Pointer { + @patch + int get value => _loadUint8(this); + + @patch + void set value(int value) => _storeUint8(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint16Pointer on Pointer { + @patch + int get value => _loadUint16(this); + + @patch + void set value(int value) => _storeUint16(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint32Pointer on Pointer { + @patch + int get value => _loadUint32(this); + + @patch + void set value(int value) => _storeUint32(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint64Pointer on Pointer { + @patch + int get value => _loadUint64(this); + + @patch + void set value(int value) => _storeUint64(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension IntPtrPointer on Pointer { + @patch + int get value => _loadIntPtr(this); + + @patch + void set value(int value) => _storeIntPtr(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension FloatPointer on Pointer { + @patch + double get value => _loadFloat(this); + + @patch + void set value(double value) => _storeFloat(this, value); + + @patch + double operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, double value) => + this.elementAt(index).value = value; +} + +extension DoublePointer on Pointer { + @patch + double get value => _loadDouble(this); + + @patch + void set value(double value) => _storeDouble(this, value); + + @patch + double operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, double value) => + this.elementAt(index).value = value; +} + +// +// End of generated code. +// + +extension PointerPointer on Pointer> { + @patch + Pointer get value => _loadPointer(this); + + @patch + void set value(Pointer value) => _storePointer(this, value); + + @patch + Pointer operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, Pointer value) => + this.elementAt(index).value = value; +} + +extension StructPointer on Pointer { + @patch + T get ref => _loadStruct(this); + + @patch + T operator [](int index) { + Pointer offsetPointer = this.elementAt(index); + return offsetPointer.ref; + } +} diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart index 8b9b37a71d2..27e5949bf3b 100644 --- a/sdk/lib/ffi/ffi.dart +++ b/sdk/lib/ffi/ffi.dart @@ -63,11 +63,14 @@ class Pointer extends NativeType { /// Store a Dart value into this location. /// - /// The [value] is automatically marshalled into its native representation. + /// The `value` is automatically marshalled into its native representation. /// Note that ints which do not fit in [T] are truncated and sign extended, /// and doubles stored into Pointer<[Float]> lose precision. /// - /// Note that `address` needs to be aligned to the size of `T`. + /// Note that `this.address` needs to be aligned to the size of `T`. + /// + /// Deprecated, use `pointer[...] =` and `pointer.value =` instead. + @deprecated external void store(@DartRepresentationOf("T") Object value); /// Load a Dart value from this location. @@ -76,7 +79,10 @@ class Pointer extends NativeType { /// Loading a [Struct] reference returns a reference backed by native memory /// (the same pointer as it's loaded from). /// - /// Note that `address` needs to be aligned to the size of `T`. + /// Note that `this.address` needs to be aligned to the size of `T`. + /// + /// Deprecated, use `pointer[...]` and `pointer.value` instead. + @deprecated external R load<@DartRepresentationOf("T") R>(); /// Access to the raw pointer value. @@ -144,3 +150,436 @@ class Pointer extends NativeType { return address.hashCode; } } + +// +// The following code is generated, do not edit by hand. +// +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. +// + +/// Extension on [Pointer] specialized for the type argument [Int8]. +extension Int8Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int16]. +extension Int16Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int32]. +extension Int32Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int64]. +extension Int64Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint8]. +extension Uint8Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint16]. +extension Uint16Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint32]. +extension Uint32Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint64]. +extension Uint64Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [IntPtr]. +extension IntPtrPointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [IntPtr] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [IntPtr] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Float]. +extension FloatPointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external double get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external void set value(double value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external double operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external void operator []=(int index, double value); +} + +/// Extension on [Pointer] specialized for the type argument [Double]. +extension DoublePointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external double get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external void set value(double value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external double operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external void operator []=(int index, double value); +} + +// +// End of generated code. +// + +extension PointerPointer on Pointer> { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external Pointer get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external void set value(Pointer value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external Pointer operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external void operator []=(int index, Pointer value); +} + +extension StructPointer on Pointer { + /// Create a reference backed by native memory (the same pointer as it's loaded from). + /// + /// Note that [address] needs to be aligned to the size of [T]. + external T get ref; + + /// Create a reference backed by native memory offset by [index]. + /// + /// Note that [address] needs to be aligned to the size of [T]. + external T operator [](int index); +} diff --git a/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart b/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart index 2b3aa44cd65..de727735e50 100644 --- a/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart +++ b/sdk_nnbd/lib/_internal/vm/lib/ffi_patch.dart @@ -149,6 +149,8 @@ int _abi() // allocating a Pointer with in elementAt/offsetBy. Allocating these pointers // and GCing new spaces takes a lot of the benchmark time. The next speedup is // getting rid of these allocations by inlining these functions. +// +// TODO(37773): Change _loadInt8 etc to take an index. int _loadInt8(Pointer pointer) native "Ffi_loadInt8"; int _loadInt16(Pointer pointer) native "Ffi_loadInt16"; @@ -238,3 +240,204 @@ Pointer _elementAtDouble(Pointer pointer, int index) => Pointer> _elementAtPointer( Pointer> pointer, int index) => Pointer.fromAddress(pointer.address + _intPtrSize * index); + +// +// The following code is generated, do not edit by hand. +// +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. +// + +extension Int8Pointer on Pointer { + @patch + int get value => _loadInt8(this); + + @patch + void set value(int value) => _storeInt8(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int16Pointer on Pointer { + @patch + int get value => _loadInt16(this); + + @patch + void set value(int value) => _storeInt16(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int32Pointer on Pointer { + @patch + int get value => _loadInt32(this); + + @patch + void set value(int value) => _storeInt32(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Int64Pointer on Pointer { + @patch + int get value => _loadInt64(this); + + @patch + void set value(int value) => _storeInt64(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint8Pointer on Pointer { + @patch + int get value => _loadUint8(this); + + @patch + void set value(int value) => _storeUint8(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint16Pointer on Pointer { + @patch + int get value => _loadUint16(this); + + @patch + void set value(int value) => _storeUint16(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint32Pointer on Pointer { + @patch + int get value => _loadUint32(this); + + @patch + void set value(int value) => _storeUint32(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension Uint64Pointer on Pointer { + @patch + int get value => _loadUint64(this); + + @patch + void set value(int value) => _storeUint64(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension IntPtrPointer on Pointer { + @patch + int get value => _loadIntPtr(this); + + @patch + void set value(int value) => _storeIntPtr(this, value); + + @patch + int operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, int value) => + this.elementAt(index).value = value; +} + +extension FloatPointer on Pointer { + @patch + double get value => _loadFloat(this); + + @patch + void set value(double value) => _storeFloat(this, value); + + @patch + double operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, double value) => + this.elementAt(index).value = value; +} + +extension DoublePointer on Pointer { + @patch + double get value => _loadDouble(this); + + @patch + void set value(double value) => _storeDouble(this, value); + + @patch + double operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, double value) => + this.elementAt(index).value = value; +} + +// +// End of generated code. +// + +extension PointerPointer on Pointer> { + @patch + Pointer get value => _loadPointer(this); + + @patch + void set value(Pointer value) => _storePointer(this, value); + + @patch + Pointer operator [](int index) => this.elementAt(index).value; + + @patch + void operator []=(int index, Pointer value) => + this.elementAt(index).value = value; +} + +extension StructPointer on Pointer { + @patch + T get ref => _loadStruct(this); + + @patch + T operator [](int index) { + Pointer offsetPointer = this.elementAt(index); + return offsetPointer.ref; + } +} diff --git a/sdk_nnbd/lib/ffi/ffi.dart b/sdk_nnbd/lib/ffi/ffi.dart index 5a011f9ddf1..128b762c1db 100644 --- a/sdk_nnbd/lib/ffi/ffi.dart +++ b/sdk_nnbd/lib/ffi/ffi.dart @@ -65,11 +65,14 @@ class Pointer extends NativeType { /// Store a Dart value into this location. /// - /// The [value] is automatically marshalled into its native representation. + /// The `value` is automatically marshalled into its native representation. /// Note that ints which do not fit in [T] are truncated and sign extended, /// and doubles stored into Pointer<[Float]> lose precision. /// - /// Note that `address` needs to be aligned to the size of `T`. + /// Note that `this.address` needs to be aligned to the size of `T`. + /// + /// Deprecated, use `pointer[...] =` and `pointer.value =` instead. + @deprecated external void store(@DartRepresentationOf("T") Object value); /// Load a Dart value from this location. @@ -78,7 +81,10 @@ class Pointer extends NativeType { /// Loading a [Struct] reference returns a reference backed by native memory /// (the same pointer as it's loaded from). /// - /// Note that `address` needs to be aligned to the size of `T`. + /// Note that `this.address` needs to be aligned to the size of `T`. + /// + /// Deprecated, use `pointer[...]` and `pointer.value` instead. + @deprecated external R load<@DartRepresentationOf("T") R>(); /// Access to the raw pointer value. @@ -146,3 +152,436 @@ class Pointer extends NativeType { return address.hashCode; } } + +// +// The following code is generated, do not edit by hand. +// +// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`. +// + +/// Extension on [Pointer] specialized for the type argument [Int8]. +extension Int8Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int8]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int16]. +extension Int16Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int16]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int32]. +extension Int32Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int32]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Int64]. +extension Int64Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Int64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Int64]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint8]. +extension Uint8Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint8] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint8]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint16]. +extension Uint16Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint16] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint16]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint32]. +extension Uint32Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint32] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint32]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Uint64]. +extension Uint64Pointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [Uint64] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [Uint64]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [IntPtr]. +extension IntPtrPointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external int get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [IntPtr] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external void set value(int value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// Note that ints are signextended. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external int operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that ints which do not fit in [IntPtr] are truncated. + /// + /// Note that [address] needs to be aligned to the size of [IntPtr]. + external void operator []=(int index, int value); +} + +/// Extension on [Pointer] specialized for the type argument [Float]. +extension FloatPointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external double get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external void set value(double value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external double operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Float]. + external void operator []=(int index, double value); +} + +/// Extension on [Pointer] specialized for the type argument [Double]. +extension DoublePointer on Pointer { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external double get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external void set value(double value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external double operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// Note that doubles stored into Pointer<[Float]> lose precision. + /// + /// Note that [address] needs to be aligned to the size of [Double]. + external void operator []=(int index, double value); +} + +// +// End of generated code. +// + +extension PointerPointer on Pointer> { + /// Load a Dart value from this location. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external Pointer get value; + + /// Store a Dart value into this location. + /// + /// The [value] is automatically marshalled into its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external void set value(Pointer value); + + /// Load a Dart value from this location offset by [index]. + /// + /// The value is automatically unmarshalled from its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external Pointer operator [](int index); + + /// Store a Dart value into this location offset by [index]. + /// + /// The [value] is automatically marshalled into its native representation. + /// + /// Note that [address] needs to be aligned to the size of [Pointer]. + external void operator []=(int index, Pointer value); +} + +extension StructPointer on Pointer { + /// Create a reference backed by native memory (the same pointer as it's loaded from). + /// + /// Note that [address] needs to be aligned to the size of [T]. + external T get ref; + + /// Create a reference backed by native memory offset by [index]. + /// + /// Note that [address] needs to be aligned to the size of [T]. + external T operator [](int index); +} diff --git a/tests/ffi/extension_methods_test.dart b/tests/ffi/extension_methods_test.dart new file mode 100644 index 00000000000..c7be31ceadb --- /dev/null +++ b/tests/ffi/extension_methods_test.dart @@ -0,0 +1,81 @@ +// 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'; + +import "package:expect/expect.dart"; + +main(List arguments) { + for (int i = 0; i < 100; i++) { + testStoreLoad(); + testNullReceivers(); + testNullArguments(); + testReifiedGeneric(); + } +} + +testStoreLoad() { + final p = Pointer.allocate(count: 2); + p.value = 10; + Expect.equals(10, p.value); + p[1] = 20; + Expect.equals(20, p[1]); + + final p1 = Pointer.allocate(count: 2); + p1.value = 10.0; + Expect.approxEquals(10.0, p1.value); + p1[1] = 20.0; + Expect.approxEquals(20.0, p1[1]); + p1.free(); + + final p2 = Pointer>.allocate(count: 2); + p2.value = p; + Expect.equals(p, p2.value); + p2[1] = p; + Expect.equals(p, p2[1]); + p2.free(); + p.free(); + + final p3 = Pointer.allocate(); + Foo foo = p3.ref; + foo.a = 1; + Expect.equals(1, foo.a); + p3.free(); +} + +/// With extension methods, the receiver position can be null. +testNullReceivers() { + Pointer p = Pointer.allocate(); + + Pointer p4 = null; + Expect.throws(() => Expect.equals(10, p4.value)); + Expect.throws(() => p4.value = 10); + + Pointer> p5 = null; + Expect.throws(() => Expect.equals(10, p5.value)); + Expect.throws(() => p5.value = p); + + Pointer p6 = null; + Expect.throws(() => Expect.equals(10, p6.ref)); + + p.free(); +} + +testNullArguments() { + Pointer p = Pointer.allocate(); + Expect.throws(() => p.value = null); + p.free(); +} + +testReifiedGeneric() { + final p = Pointer>.allocate(); + Pointer> p2 = p; + Expect.isTrue(p2.value is Pointer); + p.free(); +} + +class Foo extends Struct { + @Int8() + int a; +}