From 3cfcc5a8e5d91eaaa25a054196d5fe4f9f53c528 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Wed, 20 Jan 2021 21:37:11 +0000 Subject: [PATCH] Revert "[vm/ffi] Roll `package:ffi` to `Allocator` and `Opaque`" Revert submission 177862 Reason for revert: breaks g3 without https://github.com/flutter/engine/pull/23808 Reverted Changes: I50b3b4c31:[vm/ffi] Roll `package:ffi` to `Allocator` and `Op... I3f5b08c08:[vm/ffi] Change `Pointer.ref` to... I6141c193b:[vm/ffi] Disallow empty structs Change-Id: I128e7b096faea650e0ba0850a411eed3e97c00e6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180185 Reviewed-by: Daco Harkes --- DEPS | 4 +- .../FfiBoringssl/dart/FfiBoringssl.dart | 1 + benchmarks/FfiBoringssl/dart/calloc.dart | 109 +++++++++++++++++ .../FfiBoringssl/dart2/FfiBoringssl.dart | 1 + benchmarks/FfiBoringssl/dart2/calloc.dart | 111 ++++++++++++++++++ benchmarks/FfiCall/dart/FfiCall.dart | 1 + benchmarks/FfiCall/dart/calloc.dart | 109 +++++++++++++++++ benchmarks/FfiCall/dart2/FfiCall.dart | 1 + benchmarks/FfiCall/dart2/calloc.dart | 111 ++++++++++++++++++ benchmarks/FfiMemory/dart/FfiMemory.dart | 2 + benchmarks/FfiMemory/dart/calloc.dart | 109 +++++++++++++++++ benchmarks/FfiMemory/dart2/FfiMemory.dart | 2 + benchmarks/FfiMemory/dart2/calloc.dart | 111 ++++++++++++++++++ benchmarks/FfiStruct/dart/FfiStruct.dart | 2 + benchmarks/FfiStruct/dart/calloc.dart | 109 +++++++++++++++++ benchmarks/FfiStruct/dart2/FfiStruct.dart | 2 + benchmarks/FfiStruct/dart2/calloc.dart | 111 ++++++++++++++++++ ...t_api_create_lightweight_isolate_test.dart | 1 + runtime/tests/vm/dart/regress_41971_test.dart | 2 + .../vm/dart/thread_priority_macos_test.dart | 2 + ...t_api_create_lightweight_isolate_test.dart | 1 + .../tests/vm/dart_2/regress_41971_test.dart | 2 + .../vm/dart_2/thread_priority_macos_test.dart | 2 + samples/ffi/calloc.dart | 109 +++++++++++++++++ samples/ffi/resource_management/pool.dart | 2 + .../resource_management/unmanaged_sample.dart | 1 + samples/ffi/sample_ffi_bitfield.dart | 2 + samples/ffi/sample_ffi_data.dart | 2 + samples/ffi/sample_ffi_functions.dart | 1 + .../ffi/sample_ffi_functions_callbacks.dart | 1 + samples/ffi/sample_ffi_functions_structs.dart | 1 + samples/ffi/sample_ffi_structs.dart | 1 + samples/ffi/sqlite/lib/sqlite.dart | 2 + samples/ffi/sqlite/lib/src/database.dart | 2 + samples/ffi/sqlite/lib/src/ffi/arena.dart | 2 + samples/ffi/sqlite/lib/src/ffi/calloc.dart | 109 +++++++++++++++++ samples_2/ffi/calloc.dart | 111 ++++++++++++++++++ samples_2/ffi/resource_management/pool.dart | 2 + .../resource_management/unmanaged_sample.dart | 1 + samples_2/ffi/sample_ffi_bitfield.dart | 2 + samples_2/ffi/sample_ffi_data.dart | 2 + samples_2/ffi/sample_ffi_functions.dart | 1 + .../ffi/sample_ffi_functions_callbacks.dart | 1 + .../ffi/sample_ffi_functions_structs.dart | 1 + samples_2/ffi/sample_ffi_structs.dart | 1 + samples_2/ffi/sqlite/lib/sqlite.dart | 2 + samples_2/ffi/sqlite/lib/src/database.dart | 2 + samples_2/ffi/sqlite/lib/src/ffi/arena.dart | 2 + samples_2/ffi/sqlite/lib/src/ffi/calloc.dart | 111 ++++++++++++++++++ tests/ffi/aliasing_test.dart | 1 + tests/ffi/calloc.dart | 109 +++++++++++++++++ tests/ffi/calloc_test.dart | 2 +- tests/ffi/data_not_asan_test.dart | 2 + tests/ffi/data_test.dart | 1 + tests/ffi/extension_methods_test.dart | 2 + tests/ffi/external_typed_data_test.dart | 2 + ...backs_structs_by_value_generated_test.dart | 1 + ...ction_callbacks_structs_by_value_test.dart | 1 + ...ction_structs_by_value_generated_test.dart | 1 + tests/ffi/function_structs_test.dart | 1 + tests/ffi/function_test.dart | 1 + .../structs_by_value_tests_generator.dart | 2 + tests/ffi/regress_37254_test.dart | 2 + tests/ffi/regress_39885_test.dart | 2 + tests/ffi/regress_43693_test.dart | 1 + tests/ffi/structs_nested_test.dart | 1 + tests/ffi/structs_nnbd_workaround_test.dart | 1 + tests/ffi/structs_test.dart | 1 + tests/ffi/variance_function_test.dart | 1 + tests/ffi/vmspecific_enable_ffi_test.dart | 2 + tests/ffi/vmspecific_static_checks_test.dart | 1 + tests/ffi_2/aliasing_test.dart | 1 + tests/ffi_2/calloc.dart | 109 +++++++++++++++++ tests/ffi_2/calloc_test.dart | 2 +- tests/ffi_2/data_not_asan_test.dart | 2 + tests/ffi_2/data_test.dart | 1 + tests/ffi_2/extension_methods_test.dart | 2 + tests/ffi_2/external_typed_data_test.dart | 2 + ...backs_structs_by_value_generated_test.dart | 1 + ...ction_callbacks_structs_by_value_test.dart | 1 + ...ction_structs_by_value_generated_test.dart | 1 + tests/ffi_2/function_structs_test.dart | 1 + tests/ffi_2/function_test.dart | 1 + .../structs_by_value_tests_generator.dart | 2 + tests/ffi_2/null_test.dart | 1 + tests/ffi_2/regress_37254_test.dart | 2 + tests/ffi_2/regress_39885_test.dart | 2 + tests/ffi_2/regress_43693_test.dart | 1 + tests/ffi_2/structs_nested_test.dart | 1 + tests/ffi_2/structs_test.dart | 1 + tests/ffi_2/variance_function_test.dart | 1 + tests/ffi_2/vmspecific_enable_ffi_test.dart | 2 + .../ffi_2/vmspecific_static_checks_test.dart | 1 + 93 files changed, 1652 insertions(+), 4 deletions(-) create mode 100644 benchmarks/FfiBoringssl/dart/calloc.dart create mode 100644 benchmarks/FfiBoringssl/dart2/calloc.dart create mode 100644 benchmarks/FfiCall/dart/calloc.dart create mode 100644 benchmarks/FfiCall/dart2/calloc.dart create mode 100644 benchmarks/FfiMemory/dart/calloc.dart create mode 100644 benchmarks/FfiMemory/dart2/calloc.dart create mode 100644 benchmarks/FfiStruct/dart/calloc.dart create mode 100644 benchmarks/FfiStruct/dart2/calloc.dart create mode 100644 samples/ffi/calloc.dart create mode 100644 samples/ffi/sqlite/lib/src/ffi/calloc.dart create mode 100644 samples_2/ffi/calloc.dart create mode 100644 samples_2/ffi/sqlite/lib/src/ffi/calloc.dart create mode 100644 tests/ffi/calloc.dart create mode 100644 tests/ffi_2/calloc.dart diff --git a/DEPS b/DEPS index 3908cea250c..87fb1079506 100644 --- a/DEPS +++ b/DEPS @@ -102,7 +102,7 @@ vars = { "chromedriver_tag": "83.0.4103.39", "dartdoc_rev" : "9e61a4f11091aaa8998525a2692b14148dc24ab5", - "ffi_rev": "ad6700de10ca3af16f0c3d9ff8aa15d2bd7cd21c", + "ffi_rev": "31352979f261f7c6ea88fa0a2cfb0fdd004c38fb", "fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba", "file_rev": "0e09370f581ab6388d46fda4cdab66638c0171a1", "glob_rev": "7c0ef8d4fa086f6b185c4dd724b700e7d7ad8f79", @@ -115,7 +115,7 @@ vars = { "http_throttle_tag" : "1.0.2", "icu_rev" : "79326efe26e5440f530963704c3c0ff965b3a4ac", "idl_parser_rev": "5fb1ebf49d235b5a70c9f49047e83b0654031eb7", - "intl_tag": "ade5a936a1de62e7cd04c3ea956c02bd491d7868", + "intl_tag": "0.17.0-nullsafety", "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1", "json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d", "linter_tag": "0.1.128", diff --git a/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart index 8781c43542d..3249be45c40 100644 --- a/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart +++ b/benchmarks/FfiBoringssl/dart/FfiBoringssl.dart @@ -11,6 +11,7 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'digest.dart'; import 'types.dart'; diff --git a/benchmarks/FfiBoringssl/dart/calloc.dart b/benchmarks/FfiBoringssl/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiBoringssl/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart b/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart index 635fa574ba6..c749745d740 100644 --- a/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart +++ b/benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart @@ -13,6 +13,7 @@ import 'dart:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'digest.dart'; import 'types.dart'; diff --git a/benchmarks/FfiBoringssl/dart2/calloc.dart b/benchmarks/FfiBoringssl/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiBoringssl/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiCall/dart/FfiCall.dart b/benchmarks/FfiCall/dart/FfiCall.dart index ce48f506aaa..d33e12093d4 100644 --- a/benchmarks/FfiCall/dart/FfiCall.dart +++ b/benchmarks/FfiCall/dart/FfiCall.dart @@ -13,6 +13,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; import 'dlopen_helper.dart'; // diff --git a/benchmarks/FfiCall/dart/calloc.dart b/benchmarks/FfiCall/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiCall/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiCall/dart2/FfiCall.dart b/benchmarks/FfiCall/dart2/FfiCall.dart index 3e6f29759c7..3da29b4a6b3 100644 --- a/benchmarks/FfiCall/dart2/FfiCall.dart +++ b/benchmarks/FfiCall/dart2/FfiCall.dart @@ -15,6 +15,7 @@ import 'dart:io'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; import 'dlopen_helper.dart'; // diff --git a/benchmarks/FfiCall/dart2/calloc.dart b/benchmarks/FfiCall/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiCall/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiMemory/dart/FfiMemory.dart b/benchmarks/FfiMemory/dart/FfiMemory.dart index b776aa87751..423fa740181 100644 --- a/benchmarks/FfiMemory/dart/FfiMemory.dart +++ b/benchmarks/FfiMemory/dart/FfiMemory.dart @@ -14,6 +14,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Pointer store. // diff --git a/benchmarks/FfiMemory/dart/calloc.dart b/benchmarks/FfiMemory/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiMemory/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiMemory/dart2/FfiMemory.dart b/benchmarks/FfiMemory/dart2/FfiMemory.dart index 5beff0a6356..d8d41e2d387 100644 --- a/benchmarks/FfiMemory/dart2/FfiMemory.dart +++ b/benchmarks/FfiMemory/dart2/FfiMemory.dart @@ -16,6 +16,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Pointer store. // diff --git a/benchmarks/FfiMemory/dart2/calloc.dart b/benchmarks/FfiMemory/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiMemory/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiStruct/dart/FfiStruct.dart b/benchmarks/FfiStruct/dart/FfiStruct.dart index 4fbc61ca769..a4a8f149630 100644 --- a/benchmarks/FfiStruct/dart/FfiStruct.dart +++ b/benchmarks/FfiStruct/dart/FfiStruct.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Struct field store (plus Pointer elementAt and load). // diff --git a/benchmarks/FfiStruct/dart/calloc.dart b/benchmarks/FfiStruct/dart/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/benchmarks/FfiStruct/dart/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/benchmarks/FfiStruct/dart2/FfiStruct.dart b/benchmarks/FfiStruct/dart2/FfiStruct.dart index 87f98de9b3d..6f4e17ce6be 100644 --- a/benchmarks/FfiStruct/dart2/FfiStruct.dart +++ b/benchmarks/FfiStruct/dart2/FfiStruct.dart @@ -14,6 +14,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:benchmark_harness/benchmark_harness.dart'; +import 'calloc.dart'; + // // Struct field store (plus Pointer elementAt and load). // diff --git a/benchmarks/FfiStruct/dart2/calloc.dart b/benchmarks/FfiStruct/dart2/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/benchmarks/FfiStruct/dart2/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/runtime/tests/vm/dart/isolates/dart_api_create_lightweight_isolate_test.dart b/runtime/tests/vm/dart/isolates/dart_api_create_lightweight_isolate_test.dart index 250b4d92f09..ecc33a10d64 100644 --- a/runtime/tests/vm/dart/isolates/dart_api_create_lightweight_isolate_test.dart +++ b/runtime/tests/vm/dart/isolates/dart_api_create_lightweight_isolate_test.dart @@ -14,6 +14,7 @@ import 'dart:isolate'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../../tests/ffi/calloc.dart'; import '../../../../../tests/ffi/dylib_utils.dart'; final bool isAOT = Platform.executable.contains('dart_precompiled_runtime'); diff --git a/runtime/tests/vm/dart/regress_41971_test.dart b/runtime/tests/vm/dart/regress_41971_test.dart index 59212add513..7979d0d1e59 100644 --- a/runtime/tests/vm/dart/regress_41971_test.dart +++ b/runtime/tests/vm/dart/regress_41971_test.dart @@ -11,6 +11,8 @@ import 'dart:ffi'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../tests/ffi/calloc.dart'; + class X { int field; X(this.field); diff --git a/runtime/tests/vm/dart/thread_priority_macos_test.dart b/runtime/tests/vm/dart/thread_priority_macos_test.dart index 75e87d47faa..473c1ce267c 100644 --- a/runtime/tests/vm/dart/thread_priority_macos_test.dart +++ b/runtime/tests/vm/dart/thread_priority_macos_test.dart @@ -10,6 +10,8 @@ import 'dart:io'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../tests/ffi/calloc.dart'; + // pthread_t pthread_self() typedef PthreadSelfFT = int Function(); typedef PthreadSelfNFT = IntPtr Function(); diff --git a/runtime/tests/vm/dart_2/isolates/dart_api_create_lightweight_isolate_test.dart b/runtime/tests/vm/dart_2/isolates/dart_api_create_lightweight_isolate_test.dart index b92f0432e86..bcd62700f2b 100644 --- a/runtime/tests/vm/dart_2/isolates/dart_api_create_lightweight_isolate_test.dart +++ b/runtime/tests/vm/dart_2/isolates/dart_api_create_lightweight_isolate_test.dart @@ -14,6 +14,7 @@ import 'dart:isolate'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../../tests/ffi/calloc.dart'; import '../../../../../tests/ffi/dylib_utils.dart'; final bool isAOT = Platform.executable.contains('dart_precompiled_runtime'); diff --git a/runtime/tests/vm/dart_2/regress_41971_test.dart b/runtime/tests/vm/dart_2/regress_41971_test.dart index 59212add513..7979d0d1e59 100644 --- a/runtime/tests/vm/dart_2/regress_41971_test.dart +++ b/runtime/tests/vm/dart_2/regress_41971_test.dart @@ -11,6 +11,8 @@ import 'dart:ffi'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../tests/ffi/calloc.dart'; + class X { int field; X(this.field); diff --git a/runtime/tests/vm/dart_2/thread_priority_macos_test.dart b/runtime/tests/vm/dart_2/thread_priority_macos_test.dart index f1574cf9761..fdce726a362 100644 --- a/runtime/tests/vm/dart_2/thread_priority_macos_test.dart +++ b/runtime/tests/vm/dart_2/thread_priority_macos_test.dart @@ -10,6 +10,8 @@ import 'dart:io'; import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; +import '../../../../tests/ffi/calloc.dart'; + // pthread_t pthread_self() typedef PthreadSelfFT = int Function(); typedef PthreadSelfNFT = IntPtr Function(); diff --git a/samples/ffi/calloc.dart b/samples/ffi/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/samples/ffi/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/samples/ffi/resource_management/pool.dart b/samples/ffi/resource_management/pool.dart index e37fd227236..14e07f4d623 100644 --- a/samples/ffi/resource_management/pool.dart +++ b/samples/ffi/resource_management/pool.dart @@ -9,6 +9,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import '../calloc.dart'; + /// An [Allocator] which frees all allocations at the same time. /// /// The pool allows you to allocate heap memory, but ignores calls to [free]. diff --git a/samples/ffi/resource_management/unmanaged_sample.dart b/samples/ffi/resource_management/unmanaged_sample.dart index 5ddb6aee2b7..04c20cbd245 100644 --- a/samples/ffi/resource_management/unmanaged_sample.dart +++ b/samples/ffi/resource_management/unmanaged_sample.dart @@ -10,6 +10,7 @@ import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; import 'utf8_helpers.dart'; +import '../calloc.dart'; import '../dylib_utils.dart'; main() { diff --git a/samples/ffi/sample_ffi_bitfield.dart b/samples/ffi/sample_ffi_bitfield.dart index 4508bedc038..67ce69e2862 100644 --- a/samples/ffi/sample_ffi_bitfield.dart +++ b/samples/ffi/sample_ffi_bitfield.dart @@ -7,6 +7,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:expect/expect.dart'; +import 'calloc.dart'; + /// typedef struct { /// unsigned int bold : 1; /// unsigned int underline : 2; diff --git a/samples/ffi/sample_ffi_data.dart b/samples/ffi/sample_ffi_data.dart index fd930f12697..e5b04970e5c 100644 --- a/samples/ffi/sample_ffi_data.dart +++ b/samples/ffi/sample_ffi_data.dart @@ -5,6 +5,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; + main() { print('start main'); diff --git a/samples/ffi/sample_ffi_functions.dart b/samples/ffi/sample_ffi_functions.dart index 4ff28cd8e90..5038ba816dd 100644 --- a/samples/ffi/sample_ffi_functions.dart +++ b/samples/ffi/sample_ffi_functions.dart @@ -6,6 +6,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'dylib_utils.dart'; typedef NativeUnaryOp = Int32 Function(Int32); diff --git a/samples/ffi/sample_ffi_functions_callbacks.dart b/samples/ffi/sample_ffi_functions_callbacks.dart index 4de68189d00..de6d05ac9c8 100644 --- a/samples/ffi/sample_ffi_functions_callbacks.dart +++ b/samples/ffi/sample_ffi_functions_callbacks.dart @@ -6,6 +6,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; import 'dylib_utils.dart'; diff --git a/samples/ffi/sample_ffi_functions_structs.dart b/samples/ffi/sample_ffi_functions_structs.dart index 206c925f59f..918504078d4 100644 --- a/samples/ffi/sample_ffi_functions_structs.dart +++ b/samples/ffi/sample_ffi_functions_structs.dart @@ -6,6 +6,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; import 'dylib_utils.dart'; diff --git a/samples/ffi/sample_ffi_structs.dart b/samples/ffi/sample_ffi_structs.dart index 0e7858c5eb7..f6de82cd36c 100644 --- a/samples/ffi/sample_ffi_structs.dart +++ b/samples/ffi/sample_ffi_structs.dart @@ -6,6 +6,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; main() { diff --git a/samples/ffi/sqlite/lib/sqlite.dart b/samples/ffi/sqlite/lib/sqlite.dart index d6bac5bed63..1e60f9fec1e 100644 --- a/samples/ffi/sqlite/lib/sqlite.dart +++ b/samples/ffi/sqlite/lib/sqlite.dart @@ -8,3 +8,5 @@ library sqlite; export "src/database.dart"; + +export "src/ffi/calloc.dart" show calloc; diff --git a/samples/ffi/sqlite/lib/src/database.dart b/samples/ffi/sqlite/lib/src/database.dart index b98bf250b82..5031027f79f 100644 --- a/samples/ffi/sqlite/lib/src/database.dart +++ b/samples/ffi/sqlite/lib/src/database.dart @@ -15,6 +15,8 @@ import "bindings/types.dart" hide Database; import "bindings/constants.dart"; import "collections/closable_iterator.dart"; +import 'ffi/calloc.dart'; + /// [Database] represents an open connection to a SQLite database. /// /// All functions against a database may throw [SQLiteError]. diff --git a/samples/ffi/sqlite/lib/src/ffi/arena.dart b/samples/ffi/sqlite/lib/src/ffi/arena.dart index 290c2fe8853..5369a22bbaf 100644 --- a/samples/ffi/sqlite/lib/src/ffi/arena.dart +++ b/samples/ffi/sqlite/lib/src/ffi/arena.dart @@ -7,6 +7,8 @@ import "dart:ffi"; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; + /// [Arena] manages allocated C memory. /// /// Arenas are zoned. diff --git a/samples/ffi/sqlite/lib/src/ffi/calloc.dart b/samples/ffi/sqlite/lib/src/ffi/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/samples/ffi/sqlite/lib/src/ffi/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/samples_2/ffi/calloc.dart b/samples_2/ffi/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/samples_2/ffi/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/samples_2/ffi/resource_management/pool.dart b/samples_2/ffi/resource_management/pool.dart index ddb502677f5..d4610b01502 100644 --- a/samples_2/ffi/resource_management/pool.dart +++ b/samples_2/ffi/resource_management/pool.dart @@ -11,6 +11,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import '../calloc.dart'; + /// An [Allocator] which frees all allocations at the same time. /// /// The pool allows you to allocate heap memory, but ignores calls to [free]. diff --git a/samples_2/ffi/resource_management/unmanaged_sample.dart b/samples_2/ffi/resource_management/unmanaged_sample.dart index fa5a60bb7ce..e73f7cc6a73 100644 --- a/samples_2/ffi/resource_management/unmanaged_sample.dart +++ b/samples_2/ffi/resource_management/unmanaged_sample.dart @@ -12,6 +12,7 @@ import 'package:expect/expect.dart'; import 'package:ffi/ffi.dart'; import 'utf8_helpers.dart'; +import '../calloc.dart'; import '../dylib_utils.dart'; main() { diff --git a/samples_2/ffi/sample_ffi_bitfield.dart b/samples_2/ffi/sample_ffi_bitfield.dart index 43ac4256dc1..78c41dfe526 100644 --- a/samples_2/ffi/sample_ffi_bitfield.dart +++ b/samples_2/ffi/sample_ffi_bitfield.dart @@ -9,6 +9,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:expect/expect.dart'; +import 'calloc.dart'; + /// typedef struct { /// unsigned int bold : 1; /// unsigned int underline : 2; diff --git a/samples_2/ffi/sample_ffi_data.dart b/samples_2/ffi/sample_ffi_data.dart index ba82bec7c8b..2d22011557d 100644 --- a/samples_2/ffi/sample_ffi_data.dart +++ b/samples_2/ffi/sample_ffi_data.dart @@ -7,6 +7,8 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; + main() { print('start main'); diff --git a/samples_2/ffi/sample_ffi_functions.dart b/samples_2/ffi/sample_ffi_functions.dart index 02dfc502780..1bf8ecc0063 100644 --- a/samples_2/ffi/sample_ffi_functions.dart +++ b/samples_2/ffi/sample_ffi_functions.dart @@ -8,6 +8,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'dylib_utils.dart'; typedef NativeUnaryOp = Int32 Function(Int32); diff --git a/samples_2/ffi/sample_ffi_functions_callbacks.dart b/samples_2/ffi/sample_ffi_functions_callbacks.dart index 83d652e8dfa..1d9dc121e51 100644 --- a/samples_2/ffi/sample_ffi_functions_callbacks.dart +++ b/samples_2/ffi/sample_ffi_functions_callbacks.dart @@ -8,6 +8,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; import 'dylib_utils.dart'; diff --git a/samples_2/ffi/sample_ffi_functions_structs.dart b/samples_2/ffi/sample_ffi_functions_structs.dart index 1b780bed4f4..41b608d7759 100644 --- a/samples_2/ffi/sample_ffi_functions_structs.dart +++ b/samples_2/ffi/sample_ffi_functions_structs.dart @@ -8,6 +8,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; import 'dylib_utils.dart'; diff --git a/samples_2/ffi/sample_ffi_structs.dart b/samples_2/ffi/sample_ffi_structs.dart index f2ba9166283..94b19cceb4f 100644 --- a/samples_2/ffi/sample_ffi_structs.dart +++ b/samples_2/ffi/sample_ffi_structs.dart @@ -8,6 +8,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; main() { diff --git a/samples_2/ffi/sqlite/lib/sqlite.dart b/samples_2/ffi/sqlite/lib/sqlite.dart index 825e4347f1c..0e0669bfa71 100644 --- a/samples_2/ffi/sqlite/lib/sqlite.dart +++ b/samples_2/ffi/sqlite/lib/sqlite.dart @@ -10,3 +10,5 @@ library sqlite; export "src/database.dart"; + +export "src/ffi/calloc.dart" show calloc; diff --git a/samples_2/ffi/sqlite/lib/src/database.dart b/samples_2/ffi/sqlite/lib/src/database.dart index 91bcfe67921..687fea87e99 100644 --- a/samples_2/ffi/sqlite/lib/src/database.dart +++ b/samples_2/ffi/sqlite/lib/src/database.dart @@ -17,6 +17,8 @@ import "bindings/types.dart" hide Database; import "bindings/constants.dart"; import "collections/closable_iterator.dart"; +import 'ffi/calloc.dart'; + /// [Database] represents an open connection to a SQLite database. /// /// All functions against a database may throw [SQLiteError]. diff --git a/samples_2/ffi/sqlite/lib/src/ffi/arena.dart b/samples_2/ffi/sqlite/lib/src/ffi/arena.dart index 56b6feaae10..39e303558aa 100644 --- a/samples_2/ffi/sqlite/lib/src/ffi/arena.dart +++ b/samples_2/ffi/sqlite/lib/src/ffi/arena.dart @@ -9,6 +9,8 @@ import "dart:ffi"; import 'package:ffi/ffi.dart'; +import 'calloc.dart'; + /// [Arena] manages allocated C memory. /// /// Arenas are zoned. diff --git a/samples_2/ffi/sqlite/lib/src/ffi/calloc.dart b/samples_2/ffi/sqlite/lib/src/ffi/calloc.dart new file mode 100644 index 00000000000..c6be280de0b --- /dev/null +++ b/samples_2/ffi/sqlite/lib/src/ffi/calloc.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +// @dart=2.9 + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/tests/ffi/aliasing_test.dart b/tests/ffi/aliasing_test.dart index 5cace63f9fd..946e1e4b05d 100644 --- a/tests/ffi/aliasing_test.dart +++ b/tests/ffi/aliasing_test.dart @@ -13,6 +13,7 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; +import 'calloc.dart'; import 'ffi_test_helpers.dart'; void main() { diff --git a/tests/ffi/calloc.dart b/tests/ffi/calloc.dart new file mode 100644 index 00000000000..e17238a91b1 --- /dev/null +++ b/tests/ffi/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int? alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/tests/ffi/calloc_test.dart b/tests/ffi/calloc_test.dart index 56f1b973a7c..b8b9bbff449 100644 --- a/tests/ffi/calloc_test.dart +++ b/tests/ffi/calloc_test.dart @@ -5,8 +5,8 @@ import 'dart:ffi'; import 'package:expect/expect.dart'; -import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; void main() { diff --git a/tests/ffi/data_not_asan_test.dart b/tests/ffi/data_not_asan_test.dart index b791d716762..69257d6cb28 100644 --- a/tests/ffi/data_not_asan_test.dart +++ b/tests/ffi/data_not_asan_test.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; +import 'calloc.dart'; + void main() { testPointerAllocateTooLarge(); testPointerAllocateNegative(); diff --git a/tests/ffi/data_test.dart b/tests/ffi/data_test.dart index 49c9f3613d3..47b88c50e00 100644 --- a/tests/ffi/data_test.dart +++ b/tests/ffi/data_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'ffi_test_helpers.dart'; void main() { diff --git a/tests/ffi/extension_methods_test.dart b/tests/ffi/extension_methods_test.dart index b8f187e8e37..5c688e0e938 100644 --- a/tests/ffi/extension_methods_test.dart +++ b/tests/ffi/extension_methods_test.dart @@ -7,6 +7,8 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main(List arguments) { for (int i = 0; i < 100; i++) { testStoreLoad(); diff --git a/tests/ffi/external_typed_data_test.dart b/tests/ffi/external_typed_data_test.dart index 17f76eba649..1327dea6fbd 100644 --- a/tests/ffi/external_typed_data_test.dart +++ b/tests/ffi/external_typed_data_test.dart @@ -9,6 +9,8 @@ import 'dart:typed_data'; import 'package:expect/expect.dart'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main() { testInt8Load(); testInt8Store(); diff --git a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart index b3b6e176e36..785f03acd2b 100644 --- a/tests/ffi/function_callbacks_structs_by_value_generated_test.dart +++ b/tests/ffi/function_callbacks_structs_by_value_generated_test.dart @@ -16,6 +16,7 @@ import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; import 'callback_tests_utils.dart'; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi/function_callbacks_structs_by_value_test.dart b/tests/ffi/function_callbacks_structs_by_value_test.dart index 3273bbd8f69..5e9399a547d 100644 --- a/tests/ffi/function_callbacks_structs_by_value_test.dart +++ b/tests/ffi/function_callbacks_structs_by_value_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi/function_structs_by_value_generated_test.dart b/tests/ffi/function_structs_by_value_generated_test.dart index 1cfd869f47a..ba988414f57 100644 --- a/tests/ffi/function_structs_by_value_generated_test.dart +++ b/tests/ffi/function_structs_by_value_generated_test.dart @@ -15,6 +15,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); diff --git a/tests/ffi/function_structs_test.dart b/tests/ffi/function_structs_test.dart index 764b644a5ee..ade55fb8e1f 100644 --- a/tests/ffi/function_structs_test.dart +++ b/tests/ffi/function_structs_test.dart @@ -14,6 +14,7 @@ import 'dylib_utils.dart'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'coordinate.dart'; import 'very_large_struct.dart'; diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart index d5fd3c0f1c5..077be64c23f 100644 --- a/tests/ffi/function_test.dart +++ b/tests/ffi/function_test.dart @@ -18,6 +18,7 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; void main() { diff --git a/tests/ffi/generator/structs_by_value_tests_generator.dart b/tests/ffi/generator/structs_by_value_tests_generator.dart index 81118025e42..7bd7c83c363 100644 --- a/tests/ffi/generator/structs_by_value_tests_generator.dart +++ b/tests/ffi/generator/structs_by_value_tests_generator.dart @@ -749,6 +749,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); @@ -801,6 +802,7 @@ import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; import 'callback_tests_utils.dart'; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi/regress_37254_test.dart b/tests/ffi/regress_37254_test.dart index 0c2829a83d1..62c71dbcc1b 100644 --- a/tests/ffi/regress_37254_test.dart +++ b/tests/ffi/regress_37254_test.dart @@ -65,6 +65,8 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + // ===== a.value = b ====== // The tests follow table cells left to right, top to bottom. void store1() { diff --git a/tests/ffi/regress_39885_test.dart b/tests/ffi/regress_39885_test.dart index 77de64f3ec9..ef73ca75cfc 100644 --- a/tests/ffi/regress_39885_test.dart +++ b/tests/ffi/regress_39885_test.dart @@ -6,6 +6,8 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main() { final data = calloc(3); for (int i = 0; i < 3; ++i) { diff --git a/tests/ffi/regress_43693_test.dart b/tests/ffi/regress_43693_test.dart index 47e50152360..64b48bfa275 100644 --- a/tests/ffi/regress_43693_test.dart +++ b/tests/ffi/regress_43693_test.dart @@ -9,6 +9,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:expect/expect.dart'; +import 'calloc.dart'; import 'dylib_utils.dart'; class Struct43693 extends Struct { diff --git a/tests/ffi/structs_nested_test.dart b/tests/ffi/structs_nested_test.dart index a7c1255e625..2d5208d97a2 100644 --- a/tests/ffi/structs_nested_test.dart +++ b/tests/ffi/structs_nested_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); diff --git a/tests/ffi/structs_nnbd_workaround_test.dart b/tests/ffi/structs_nnbd_workaround_test.dart index 33f560ea5a0..06c6f29d1d3 100644 --- a/tests/ffi/structs_nnbd_workaround_test.dart +++ b/tests/ffi/structs_nnbd_workaround_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'coordinate_nnbd_workaround.dart'; void main() { diff --git a/tests/ffi/structs_test.dart b/tests/ffi/structs_test.dart index ede23f99b84..6bc1ed1a7a7 100644 --- a/tests/ffi/structs_test.dart +++ b/tests/ffi/structs_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'coordinate_bare.dart' as bare; import 'coordinate.dart'; import 'ffi_test_helpers.dart'; diff --git a/tests/ffi/variance_function_test.dart b/tests/ffi/variance_function_test.dart index 04c812ce27b..3db0147abad 100644 --- a/tests/ffi/variance_function_test.dart +++ b/tests/ffi/variance_function_test.dart @@ -15,6 +15,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; typedef Int64PointerParamOpDart = void Function(Pointer); diff --git a/tests/ffi/vmspecific_enable_ffi_test.dart b/tests/ffi/vmspecific_enable_ffi_test.dart index 69ae0aaa364..ce906071f16 100644 --- a/tests/ffi/vmspecific_enable_ffi_test.dart +++ b/tests/ffi/vmspecific_enable_ffi_test.dart @@ -10,6 +10,8 @@ import 'dart:ffi'; //# 01: compile-time error import 'package:ffi/ffi.dart'; //# 01: compile-time error +import 'calloc.dart'; //# 01: compile-time error + void main() { Pointer p = //# 01: compile-time error calloc(); //# 01: compile-time error diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart index 117eac7df50..ff194bdaf70 100644 --- a/tests/ffi/vmspecific_static_checks_test.dart +++ b/tests/ffi/vmspecific_static_checks_test.dart @@ -10,6 +10,7 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; void main() { diff --git a/tests/ffi_2/aliasing_test.dart b/tests/ffi_2/aliasing_test.dart index 5cace63f9fd..946e1e4b05d 100644 --- a/tests/ffi_2/aliasing_test.dart +++ b/tests/ffi_2/aliasing_test.dart @@ -13,6 +13,7 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; +import 'calloc.dart'; import 'ffi_test_helpers.dart'; void main() { diff --git a/tests/ffi_2/calloc.dart b/tests/ffi_2/calloc.dart new file mode 100644 index 00000000000..a433e39cd9a --- /dev/null +++ b/tests/ffi_2/calloc.dart @@ -0,0 +1,109 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(https://dartbug.com/44621): Remove this copy when package:ffi can be +// rolled. We need to wait until the `Allocator` interface has rolled into +// Flutter. + +import 'dart:ffi'; +import 'dart:io'; + +final DynamicLibrary stdlib = Platform.isWindows + ? DynamicLibrary.open('kernel32.dll') + : DynamicLibrary.process(); + +typedef PosixCallocNative = Pointer Function(IntPtr num, IntPtr size); +typedef PosixCalloc = Pointer Function(int num, int size); +final PosixCalloc posixCalloc = + stdlib.lookupFunction('calloc'); + +typedef PosixFreeNative = Void Function(Pointer); +typedef PosixFree = void Function(Pointer); +final PosixFree posixFree = + stdlib.lookupFunction('free'); + +typedef WinGetProcessHeapFn = Pointer Function(); +final WinGetProcessHeapFn winGetProcessHeap = stdlib + .lookupFunction('GetProcessHeap'); +final Pointer processHeap = winGetProcessHeap(); + +typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr); +typedef WinHeapAlloc = Pointer Function(Pointer, int, int); +final WinHeapAlloc winHeapAlloc = + stdlib.lookupFunction('HeapAlloc'); + +typedef WinHeapFreeNative = Int32 Function( + Pointer heap, Uint32 flags, Pointer memory); +typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory); +final WinHeapFree winHeapFree = + stdlib.lookupFunction('HeapFree'); + +const int HEAP_ZERO_MEMORY = 8; + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +class _CallocAllocator implements Allocator { + const _CallocAllocator(); + + /// Allocates [byteCount] bytes of zero-initialized of memory on the native + /// heap. + /// + /// For POSIX-based systems, this uses `calloc`. On Windows, it uses + /// `HeapAlloc` against the default public heap. + /// + /// Throws an [ArgumentError] if the number of bytes or alignment cannot be + /// satisfied. + // TODO: Stop ignoring alignment if it's large, for example for SSE data. + @override + Pointer allocate(int byteCount, {int alignment}) { + Pointer result; + if (Platform.isWindows) { + result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount) + .cast(); + } else { + result = posixCalloc(byteCount, 1).cast(); + } + if (result.address == 0) { + throw ArgumentError('Could not allocate $byteCount bytes.'); + } + return result; + } + + /// Releases memory allocated on the native heap. + /// + /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree` + /// against the default public heap. It may only be used against pointers + /// allocated in a manner equivalent to [allocate]. + /// + /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be + /// freed. + /// + // TODO(dartbug.com/36855): Once we have a ffi.Bool type we can use it instead + // of testing the return integer to be non-zero. + @override + void free(Pointer pointer) { + if (Platform.isWindows) { + if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) { + throw ArgumentError('Could not free $pointer.'); + } + } else { + posixFree(pointer); + } + } +} + +/// Manages memory on the native heap. +/// +/// Initializes newly allocated memory to zero. Use [malloc] for unintialized +/// memory allocation. +/// +/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses +/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default +/// public heap. +const Allocator calloc = _CallocAllocator(); diff --git a/tests/ffi_2/calloc_test.dart b/tests/ffi_2/calloc_test.dart index 56f1b973a7c..b8b9bbff449 100644 --- a/tests/ffi_2/calloc_test.dart +++ b/tests/ffi_2/calloc_test.dart @@ -5,8 +5,8 @@ import 'dart:ffi'; import 'package:expect/expect.dart'; -import 'package:ffi/ffi.dart'; +import 'calloc.dart'; import 'coordinate.dart'; void main() { diff --git a/tests/ffi_2/data_not_asan_test.dart b/tests/ffi_2/data_not_asan_test.dart index b791d716762..69257d6cb28 100644 --- a/tests/ffi_2/data_not_asan_test.dart +++ b/tests/ffi_2/data_not_asan_test.dart @@ -12,6 +12,8 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; +import 'calloc.dart'; + void main() { testPointerAllocateTooLarge(); testPointerAllocateNegative(); diff --git a/tests/ffi_2/data_test.dart b/tests/ffi_2/data_test.dart index 6c3c488476c..8fca1a1b28b 100644 --- a/tests/ffi_2/data_test.dart +++ b/tests/ffi_2/data_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'ffi_test_helpers.dart'; void main() { diff --git a/tests/ffi_2/extension_methods_test.dart b/tests/ffi_2/extension_methods_test.dart index 64a6237a420..3524a7f646b 100644 --- a/tests/ffi_2/extension_methods_test.dart +++ b/tests/ffi_2/extension_methods_test.dart @@ -7,6 +7,8 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main(List arguments) { for (int i = 0; i < 100; i++) { testStoreLoad(); diff --git a/tests/ffi_2/external_typed_data_test.dart b/tests/ffi_2/external_typed_data_test.dart index 3c4c71a0aaf..18e8e3c61f5 100644 --- a/tests/ffi_2/external_typed_data_test.dart +++ b/tests/ffi_2/external_typed_data_test.dart @@ -9,6 +9,8 @@ import 'dart:typed_data'; import 'package:expect/expect.dart'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main() { testInt8Load(); testInt8Store(); diff --git a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart index 5e6d12f8d39..e11d9629007 100644 --- a/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart +++ b/tests/ffi_2/function_callbacks_structs_by_value_generated_test.dart @@ -16,6 +16,7 @@ import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; import 'callback_tests_utils.dart'; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi_2/function_callbacks_structs_by_value_test.dart b/tests/ffi_2/function_callbacks_structs_by_value_test.dart index 3273bbd8f69..5e9399a547d 100644 --- a/tests/ffi_2/function_callbacks_structs_by_value_test.dart +++ b/tests/ffi_2/function_callbacks_structs_by_value_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi_2/function_structs_by_value_generated_test.dart b/tests/ffi_2/function_structs_by_value_generated_test.dart index e95fc7a2a9a..6867406a74d 100644 --- a/tests/ffi_2/function_structs_by_value_generated_test.dart +++ b/tests/ffi_2/function_structs_by_value_generated_test.dart @@ -15,6 +15,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); diff --git a/tests/ffi_2/function_structs_test.dart b/tests/ffi_2/function_structs_test.dart index c003fd448bf..6bb3e56b5f6 100644 --- a/tests/ffi_2/function_structs_test.dart +++ b/tests/ffi_2/function_structs_test.dart @@ -12,6 +12,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'coordinate.dart'; import 'dylib_utils.dart'; import 'very_large_struct.dart'; diff --git a/tests/ffi_2/function_test.dart b/tests/ffi_2/function_test.dart index d5fd3c0f1c5..b547ffe6916 100644 --- a/tests/ffi_2/function_test.dart +++ b/tests/ffi_2/function_test.dart @@ -19,6 +19,7 @@ import "package:ffi/ffi.dart"; import "package:expect/expect.dart"; import 'dylib_utils.dart'; +import 'calloc.dart'; void main() { for (int i = 0; i < 100; ++i) { diff --git a/tests/ffi_2/generator/structs_by_value_tests_generator.dart b/tests/ffi_2/generator/structs_by_value_tests_generator.dart index 81118025e42..7bd7c83c363 100644 --- a/tests/ffi_2/generator/structs_by_value_tests_generator.dart +++ b/tests/ffi_2/generator/structs_by_value_tests_generator.dart @@ -749,6 +749,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); @@ -801,6 +802,7 @@ import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; import 'callback_tests_utils.dart'; +import 'calloc.dart'; // Reuse the struct classes. import 'function_structs_by_value_generated_test.dart'; diff --git a/tests/ffi_2/null_test.dart b/tests/ffi_2/null_test.dart index ec7b83712ef..fb51f947f07 100644 --- a/tests/ffi_2/null_test.dart +++ b/tests/ffi_2/null_test.dart @@ -20,6 +20,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; import 'ffi_test_helpers.dart'; diff --git a/tests/ffi_2/regress_37254_test.dart b/tests/ffi_2/regress_37254_test.dart index cf2378b72be..e73562d8b02 100644 --- a/tests/ffi_2/regress_37254_test.dart +++ b/tests/ffi_2/regress_37254_test.dart @@ -65,6 +65,8 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + // ===== a.value = b ====== // The tests follow table cells left to right, top to bottom. void store1() { diff --git a/tests/ffi_2/regress_39885_test.dart b/tests/ffi_2/regress_39885_test.dart index 77de64f3ec9..ef73ca75cfc 100644 --- a/tests/ffi_2/regress_39885_test.dart +++ b/tests/ffi_2/regress_39885_test.dart @@ -6,6 +6,8 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; + main() { final data = calloc(3); for (int i = 0; i < 3; ++i) { diff --git a/tests/ffi_2/regress_43693_test.dart b/tests/ffi_2/regress_43693_test.dart index 01b7cfd4b54..b589b0c5aed 100644 --- a/tests/ffi_2/regress_43693_test.dart +++ b/tests/ffi_2/regress_43693_test.dart @@ -9,6 +9,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:expect/expect.dart'; +import 'calloc.dart'; import 'dylib_utils.dart'; class Struct43693 extends Struct { diff --git a/tests/ffi_2/structs_nested_test.dart b/tests/ffi_2/structs_nested_test.dart index 522458fd52a..3a69edbf2c5 100644 --- a/tests/ffi_2/structs_nested_test.dart +++ b/tests/ffi_2/structs_nested_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions"); diff --git a/tests/ffi_2/structs_test.dart b/tests/ffi_2/structs_test.dart index 80b9f67e1ed..275905648ec 100644 --- a/tests/ffi_2/structs_test.dart +++ b/tests/ffi_2/structs_test.dart @@ -11,6 +11,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'coordinate_bare.dart' as bare; import 'coordinate.dart'; import 'ffi_test_helpers.dart'; diff --git a/tests/ffi_2/variance_function_test.dart b/tests/ffi_2/variance_function_test.dart index 210543a0a8c..f53cdfcbf36 100644 --- a/tests/ffi_2/variance_function_test.dart +++ b/tests/ffi_2/variance_function_test.dart @@ -15,6 +15,7 @@ import 'dart:ffi'; import "package:expect/expect.dart"; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; typedef Int64PointerParamOpDart = void Function(Pointer); diff --git a/tests/ffi_2/vmspecific_enable_ffi_test.dart b/tests/ffi_2/vmspecific_enable_ffi_test.dart index 69ae0aaa364..ce906071f16 100644 --- a/tests/ffi_2/vmspecific_enable_ffi_test.dart +++ b/tests/ffi_2/vmspecific_enable_ffi_test.dart @@ -10,6 +10,8 @@ import 'dart:ffi'; //# 01: compile-time error import 'package:ffi/ffi.dart'; //# 01: compile-time error +import 'calloc.dart'; //# 01: compile-time error + void main() { Pointer p = //# 01: compile-time error calloc(); //# 01: compile-time error diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart index 2fabfda8b9b..8883658dde1 100644 --- a/tests/ffi_2/vmspecific_static_checks_test.dart +++ b/tests/ffi_2/vmspecific_static_checks_test.dart @@ -10,6 +10,7 @@ import 'dart:ffi'; import "package:ffi/ffi.dart"; +import 'calloc.dart'; import 'dylib_utils.dart'; void main() {