[vm/ffi] Pointer.asTypedList finalizer
TEST=tests/ffi/external_typed_data_finalizer_test.dart Closes: https://github.com/dart-lang/sdk/issues/50507 CoreLibraryReviewExempt: https://github.com/dart-lang/sdk/issues/52261 Change-Id: I1a82dcca15961b28c0de64637970fe38a39286e5 Cq-Include-Trybots: luci.dart.try:vm-asan-linux-release-x64-try,vm-aot-asan-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-qemu-linux-release-arm-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-aot-win-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-aot-mac-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/301001 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
e98d824b5e
commit
e5bb28bc4d
@@ -8,6 +8,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "platform/globals.h"
|
||||
#include "platform/memory_sanitizer.h"
|
||||
@@ -1316,4 +1317,40 @@ DART_EXPORT bool IsNull(Dart_Handle object) {
|
||||
return Dart_IsNull(object);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct RefCountedResource {
|
||||
void* resource;
|
||||
intptr_t refcount;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// We only ever have one ref counted resource in our test, use global lock.
|
||||
std::mutex ref_counted_resource_mutex;
|
||||
|
||||
DART_EXPORT RefCountedResource* AllocateRefcountedResource() {
|
||||
auto peer =
|
||||
static_cast<RefCountedResource*>(malloc(sizeof(RefCountedResource)));
|
||||
auto resource = malloc(128);
|
||||
peer->resource = resource;
|
||||
peer->refcount = 0; // We're not going to count the reference here.
|
||||
return peer;
|
||||
}
|
||||
|
||||
DART_EXPORT void IncreaseRefcount(RefCountedResource* peer) {
|
||||
ref_counted_resource_mutex.lock();
|
||||
peer->refcount++;
|
||||
ref_counted_resource_mutex.unlock();
|
||||
}
|
||||
|
||||
// And delete if zero.
|
||||
DART_EXPORT void DecreaseRefcount(RefCountedResource* peer) {
|
||||
ref_counted_resource_mutex.lock();
|
||||
peer->refcount--;
|
||||
if (peer->refcount <= 0) {
|
||||
free(peer->resource);
|
||||
free(peer);
|
||||
}
|
||||
ref_counted_resource_mutex.unlock();
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -189,4 +189,27 @@ DEFINE_FFI_NATIVE_ENTRY(FinalizerEntry_SetExternalSize,
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
struct AsTypedListFinalizerData {
|
||||
void (*callback)(void*);
|
||||
void* token;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerAllocateData, void*, ()) {
|
||||
return malloc(sizeof(AsTypedListFinalizerData));
|
||||
};
|
||||
|
||||
void AsTypedListFinalizerCallback(void* peer) {
|
||||
const auto* data = reinterpret_cast<AsTypedListFinalizerData*>(peer);
|
||||
data->callback(data->token);
|
||||
free(peer);
|
||||
}
|
||||
|
||||
DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerCallbackPointer,
|
||||
void*,
|
||||
()) {
|
||||
return reinterpret_cast<void*>(&AsTypedListFinalizerCallback);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -39,30 +39,51 @@ main(List<String> arguments) {
|
||||
final args = argParser().parse(arguments);
|
||||
Uri path = Uri.parse(args['path']);
|
||||
|
||||
generate(path, "ffi.g.dart", generatePublicExtension);
|
||||
generate(path, "ffi_patch.g.dart", generatePatchExtension);
|
||||
update(Uri.file('sdk/lib/ffi/ffi.dart'), generatePublicExtension);
|
||||
update(Uri.file('sdk/lib/_internal/vm/lib/ffi_patch.dart'),
|
||||
generatePatchExtension);
|
||||
}
|
||||
|
||||
void generate(Uri path, String fileName,
|
||||
Function(StringBuffer, Config, String) generator) {
|
||||
void update(Uri fileName, Function(StringBuffer, Config, String) generator) {
|
||||
final file = File.fromUri(fileName);
|
||||
if (!file.existsSync()) {
|
||||
print('$fileName does not exist, run from the root of the SDK.');
|
||||
return;
|
||||
}
|
||||
|
||||
final fileContents = file.readAsStringSync();
|
||||
final split1 = fileContents.split(header);
|
||||
if (split1.length != 2) {
|
||||
print('$fileName has unexpected contents.');
|
||||
print(split1.length);
|
||||
return;
|
||||
}
|
||||
final split2 = split1[1].split(footer);
|
||||
if (split2.length != 2) {
|
||||
print('$fileName has unexpected contents 2.');
|
||||
print(split2.length);
|
||||
return;
|
||||
}
|
||||
|
||||
final buffer = StringBuffer();
|
||||
generateHeader(buffer);
|
||||
buffer.write(split1[0]);
|
||||
buffer.write(header);
|
||||
configuration.forEach((Config c) => generator(buffer, c, "Pointer"));
|
||||
configuration.forEach((Config c) => generator(buffer, c, "Array"));
|
||||
generateFooter(buffer);
|
||||
buffer.write(footer);
|
||||
buffer.write(split2[1]);
|
||||
|
||||
final fullPath = path.resolve(fileName).path;
|
||||
File(fullPath).writeAsStringSync(buffer.toString());
|
||||
final fmtResult = Process.runSync(dartPath().path, ["format", fullPath]);
|
||||
file.writeAsStringSync(buffer.toString());
|
||||
final fmtResult =
|
||||
Process.runSync(dartPath().path, ["format", fileName.toFilePath()]);
|
||||
if (fmtResult.exitCode != 0) {
|
||||
throw Exception(
|
||||
"Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n");
|
||||
}
|
||||
print("Generated $fullPath.");
|
||||
print("Updated $fileName.");
|
||||
}
|
||||
|
||||
void generateHeader(StringBuffer buffer) {
|
||||
const header = """
|
||||
const header = """
|
||||
//
|
||||
// The following code is generated, do not edit by hand.
|
||||
//
|
||||
@@ -71,6 +92,7 @@ void generateHeader(StringBuffer buffer) {
|
||||
|
||||
""";
|
||||
|
||||
void generateHeader(StringBuffer buffer) {
|
||||
buffer.write(header);
|
||||
}
|
||||
|
||||
@@ -173,7 +195,15 @@ void generatePublicExtension(
|
||||
///
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
$alignment external $typedListType asTypedList(int length);
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
$alignment external $typedListType asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
""";
|
||||
|
||||
if (container == "Pointer") {
|
||||
@@ -228,12 +258,20 @@ void generatePatchExtension(
|
||||
? ""
|
||||
: """
|
||||
@patch
|
||||
$typedListType asTypedList(int length) {
|
||||
$typedListType asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<$nativeType>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, $elementSize);
|
||||
_checkPointerAlignment(address, $elementSize);
|
||||
return _asExternalTypedData$nativeType(this, length);
|
||||
final result = _asExternalTypedData$nativeType(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, $sizeTimes length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
""";
|
||||
|
||||
@@ -279,13 +317,13 @@ extension ${nativeType}Array on Array<$nativeType> {
|
||||
}
|
||||
}
|
||||
|
||||
void generateFooter(StringBuffer buffer) {
|
||||
final footer = """
|
||||
final footer = """
|
||||
//
|
||||
// End of generated code.
|
||||
//
|
||||
""";
|
||||
|
||||
void generateFooter(StringBuffer buffer) {
|
||||
buffer.write(footer);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ void Bootstrap::SetupNativeResolver() {
|
||||
ASSERT(!library.IsNull());
|
||||
library.set_native_entry_resolver(resolver);
|
||||
library.set_native_entry_symbol_resolver(symbol_resolver);
|
||||
library.set_ffi_native_resolver(ffi_native_resolver);
|
||||
|
||||
library = Library::InternalLibrary();
|
||||
ASSERT(!library.IsNull());
|
||||
|
||||
@@ -466,7 +466,9 @@ namespace dart {
|
||||
V(VariableMirror_type, 2)
|
||||
|
||||
#define BOOTSTRAP_FFI_NATIVE_LIST(V) \
|
||||
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t))
|
||||
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t)) \
|
||||
V(Pointer_asTypedListFinalizerAllocateData, void*, ()) \
|
||||
V(Pointer_asTypedListFinalizerCallbackPointer, void*, ())
|
||||
|
||||
class BootstrapNatives : public AllStatic {
|
||||
public:
|
||||
@@ -474,7 +476,7 @@ class BootstrapNatives : public AllStatic {
|
||||
int argument_count,
|
||||
bool* auto_setup_scope);
|
||||
|
||||
// For use with @FfiNative.
|
||||
// For use with @Native.
|
||||
static void* LookupFfiNative(const char* name, uintptr_t argument_count);
|
||||
|
||||
static const uint8_t* Symbol(Dart_NativeFunction nf);
|
||||
|
||||
@@ -1207,7 +1207,8 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
|
||||
if (!Api::IsFfiEnabled() &&
|
||||
target_library.url() == Symbols::DartFfi().ptr() &&
|
||||
library->url() != Symbols::DartCore().ptr() &&
|
||||
library->url() != Symbols::DartInternal().ptr()) {
|
||||
library->url() != Symbols::DartInternal().ptr() &&
|
||||
library->url() != Symbols::DartFfi().ptr()) {
|
||||
H.ReportError(
|
||||
"import of dart:ffi is not supported in the current Dart runtime");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// All imports must be in all FFI patch files to not depend on the order
|
||||
// the patches are applied.
|
||||
import 'dart:_internal';
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -38,7 +39,7 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
|
||||
}
|
||||
|
||||
void attach(
|
||||
Finalizable value,
|
||||
Object value,
|
||||
Pointer<Void> token, {
|
||||
Object? detach,
|
||||
int? externalSize,
|
||||
@@ -102,3 +103,43 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
|
||||
finalizer._removeEntries();
|
||||
}
|
||||
}
|
||||
|
||||
@Native<Pointer<NativeFinalizerFunction> Function()>(
|
||||
symbol: 'Pointer_asTypedListFinalizerCallbackPointer')
|
||||
external Pointer<NativeFinalizerFunction>
|
||||
_asTypedListFinalizerCallbackPointer();
|
||||
|
||||
final Pointer<NativeFinalizerFunction> _asTypedListFinalizerCallback =
|
||||
_asTypedListFinalizerCallbackPointer();
|
||||
|
||||
final _asTypedListFinalizer = _NativeFinalizer(_asTypedListFinalizerCallback);
|
||||
|
||||
final class _AsTypedListFinalizerData extends Struct {
|
||||
external Pointer<NativeFinalizerFunction> callback;
|
||||
external Pointer<Void> token;
|
||||
}
|
||||
|
||||
@patch
|
||||
void _attachAsTypedListFinalizer(
|
||||
Pointer<NativeFinalizerFunction> callback,
|
||||
Object typedList,
|
||||
Pointer pointer,
|
||||
int? externalSize,
|
||||
) {
|
||||
final data = _allocateData();
|
||||
data.ref.callback = callback;
|
||||
data.ref.token = pointer.cast();
|
||||
_asTypedListFinalizer.attach(
|
||||
typedList,
|
||||
data.cast(),
|
||||
externalSize: externalSize,
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure we use the `malloc` that corresponds to the `free` used inside
|
||||
// `_asTypedListFinalizerCallback` in the VM.
|
||||
@Native<Pointer<_AsTypedListFinalizerData> Function()>(
|
||||
symbol: 'Pointer_asTypedListFinalizerAllocateData',
|
||||
isLeaf: true,
|
||||
)
|
||||
external Pointer<_AsTypedListFinalizerData> _allocateData();
|
||||
|
||||
@@ -451,12 +451,20 @@ extension Int8Pointer on Pointer<Int8> {
|
||||
Pointer<Int8> elementAt(int index) => Pointer.fromAddress(address + index);
|
||||
|
||||
@patch
|
||||
Int8List asTypedList(int length) {
|
||||
Int8List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Int8>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 1);
|
||||
_checkPointerAlignment(address, 1);
|
||||
return _asExternalTypedDataInt8(this, length);
|
||||
final result = _asExternalTypedDataInt8(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,12 +486,20 @@ extension Int16Pointer on Pointer<Int16> {
|
||||
Pointer.fromAddress(address + 2 * index);
|
||||
|
||||
@patch
|
||||
Int16List asTypedList(int length) {
|
||||
Int16List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Int16>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 2);
|
||||
_checkPointerAlignment(address, 2);
|
||||
return _asExternalTypedDataInt16(this, length);
|
||||
final result = _asExternalTypedDataInt16(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 2 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,12 +521,20 @@ extension Int32Pointer on Pointer<Int32> {
|
||||
Pointer.fromAddress(address + 4 * index);
|
||||
|
||||
@patch
|
||||
Int32List asTypedList(int length) {
|
||||
Int32List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Int32>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 4);
|
||||
_checkPointerAlignment(address, 4);
|
||||
return _asExternalTypedDataInt32(this, length);
|
||||
final result = _asExternalTypedDataInt32(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 4 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,12 +556,20 @@ extension Int64Pointer on Pointer<Int64> {
|
||||
Pointer.fromAddress(address + 8 * index);
|
||||
|
||||
@patch
|
||||
Int64List asTypedList(int length) {
|
||||
Int64List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Int64>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 8);
|
||||
_checkPointerAlignment(address, 8);
|
||||
return _asExternalTypedDataInt64(this, length);
|
||||
final result = _asExternalTypedDataInt64(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 8 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,12 +590,20 @@ extension Uint8Pointer on Pointer<Uint8> {
|
||||
Pointer<Uint8> elementAt(int index) => Pointer.fromAddress(address + index);
|
||||
|
||||
@patch
|
||||
Uint8List asTypedList(int length) {
|
||||
Uint8List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Uint8>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 1);
|
||||
_checkPointerAlignment(address, 1);
|
||||
return _asExternalTypedDataUint8(this, length);
|
||||
final result = _asExternalTypedDataUint8(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,12 +625,20 @@ extension Uint16Pointer on Pointer<Uint16> {
|
||||
Pointer.fromAddress(address + 2 * index);
|
||||
|
||||
@patch
|
||||
Uint16List asTypedList(int length) {
|
||||
Uint16List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Uint16>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 2);
|
||||
_checkPointerAlignment(address, 2);
|
||||
return _asExternalTypedDataUint16(this, length);
|
||||
final result = _asExternalTypedDataUint16(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 2 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,12 +660,20 @@ extension Uint32Pointer on Pointer<Uint32> {
|
||||
Pointer.fromAddress(address + 4 * index);
|
||||
|
||||
@patch
|
||||
Uint32List asTypedList(int length) {
|
||||
Uint32List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Uint32>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 4);
|
||||
_checkPointerAlignment(address, 4);
|
||||
return _asExternalTypedDataUint32(this, length);
|
||||
final result = _asExternalTypedDataUint32(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 4 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,12 +695,20 @@ extension Uint64Pointer on Pointer<Uint64> {
|
||||
Pointer.fromAddress(address + 8 * index);
|
||||
|
||||
@patch
|
||||
Uint64List asTypedList(int length) {
|
||||
Uint64List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Uint64>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 8);
|
||||
_checkPointerAlignment(address, 8);
|
||||
return _asExternalTypedDataUint64(this, length);
|
||||
final result = _asExternalTypedDataUint64(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 8 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,12 +730,20 @@ extension FloatPointer on Pointer<Float> {
|
||||
Pointer.fromAddress(address + 4 * index);
|
||||
|
||||
@patch
|
||||
Float32List asTypedList(int length) {
|
||||
Float32List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Float>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 4);
|
||||
_checkPointerAlignment(address, 4);
|
||||
return _asExternalTypedDataFloat(this, length);
|
||||
final result = _asExternalTypedDataFloat(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 4 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,12 +765,20 @@ extension DoublePointer on Pointer<Double> {
|
||||
Pointer.fromAddress(address + 8 * index);
|
||||
|
||||
@patch
|
||||
Float64List asTypedList(int length) {
|
||||
Float64List asTypedList(
|
||||
int length, {
|
||||
Pointer<NativeFinalizerFunction>? finalizer,
|
||||
Pointer<Void>? token,
|
||||
}) {
|
||||
ArgumentError.checkNotNull(this, "Pointer<Double>");
|
||||
ArgumentError.checkNotNull(length, "length");
|
||||
_checkExternalTypedDataLength(length, 8);
|
||||
_checkPointerAlignment(address, 8);
|
||||
return _asExternalTypedDataDouble(this, length);
|
||||
final result = _asExternalTypedDataDouble(this, length);
|
||||
if (finalizer != null) {
|
||||
_attachAsTypedListFinalizer(finalizer, result, token ?? this, 8 * length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import "dart:async" show Timer;
|
||||
import "dart:core" hide Symbol;
|
||||
import "dart:ffi" show Pointer, Struct, Union, IntPtr, Handle, Void, FfiNative;
|
||||
import "dart:ffi" show Pointer, Struct, Union, IntPtr, Handle, Void, Native;
|
||||
import "dart:isolate" show SendPort;
|
||||
import "dart:typed_data" show Int32List, Uint8List;
|
||||
|
||||
@@ -425,7 +425,8 @@ class FinalizerEntry {
|
||||
external int get externalSize;
|
||||
|
||||
/// Update the external size.
|
||||
@FfiNative<Void Function(Handle, IntPtr)>('FinalizerEntry_SetExternalSize')
|
||||
@Native<Void Function(Handle, IntPtr)>(
|
||||
symbol: 'FinalizerEntry_SetExternalSize')
|
||||
external void setExternalSize(int externalSize);
|
||||
}
|
||||
|
||||
|
||||
+90
-10
@@ -193,7 +193,15 @@ extension Int8Pointer on Pointer<Int8> {
|
||||
///
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
external Int8List asTypedList(int length);
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
external Int8List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int16].
|
||||
@@ -235,8 +243,16 @@ extension Int16Pointer on Pointer<Int16> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 2-byte aligned.
|
||||
external Int16List asTypedList(int length);
|
||||
external Int16List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int32].
|
||||
@@ -278,8 +294,16 @@ extension Int32Pointer on Pointer<Int32> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 4-byte aligned.
|
||||
external Int32List asTypedList(int length);
|
||||
external Int32List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Int64].
|
||||
@@ -312,8 +336,16 @@ extension Int64Pointer on Pointer<Int64> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 8-byte aligned.
|
||||
external Int64List asTypedList(int length);
|
||||
external Int64List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint8].
|
||||
@@ -348,7 +380,15 @@ extension Uint8Pointer on Pointer<Uint8> {
|
||||
///
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
external Uint8List asTypedList(int length);
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
external Uint8List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint16].
|
||||
@@ -390,8 +430,16 @@ extension Uint16Pointer on Pointer<Uint16> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 2-byte aligned.
|
||||
external Uint16List asTypedList(int length);
|
||||
external Uint16List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint32].
|
||||
@@ -433,8 +481,16 @@ extension Uint32Pointer on Pointer<Uint32> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 4-byte aligned.
|
||||
external Uint32List asTypedList(int length);
|
||||
external Uint32List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Uint64].
|
||||
@@ -467,8 +523,16 @@ extension Uint64Pointer on Pointer<Uint64> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 8-byte aligned.
|
||||
external Uint64List asTypedList(int length);
|
||||
external Uint64List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Float].
|
||||
@@ -510,8 +574,16 @@ extension FloatPointer on Pointer<Float> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 4-byte aligned.
|
||||
external Float32List asTypedList(int length);
|
||||
external Float32List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Double].
|
||||
@@ -544,8 +616,16 @@ extension DoublePointer on Pointer<Double> {
|
||||
/// The user has to ensure the memory range is accessible while using the
|
||||
/// returned list.
|
||||
///
|
||||
/// If provided, [finalizer] will be run on the pointer once the typed list
|
||||
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
|
||||
/// the this pointer itself will be passed.
|
||||
///
|
||||
/// The [address] must be 8-byte aligned.
|
||||
external Float64List asTypedList(int length);
|
||||
external Float64List asTypedList(
|
||||
int length, {
|
||||
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
|
||||
@Since('3.1') Pointer<Void>? token,
|
||||
});
|
||||
}
|
||||
|
||||
/// Extension on [Pointer] specialized for the type argument [Bool].
|
||||
|
||||
@@ -387,3 +387,11 @@ abstract final class NativeFinalizer {
|
||||
/// object become inaccessible.
|
||||
void detach(Object detach);
|
||||
}
|
||||
|
||||
// To make dart2wasm compile without patch file.
|
||||
external void _attachAsTypedListFinalizer(
|
||||
Pointer<NativeFinalizerFunction> callback,
|
||||
Object typedList,
|
||||
Pointer pointer,
|
||||
int? externalSize,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedObjects=ffi_test_functions
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'dylib_utils.dart';
|
||||
|
||||
main() {
|
||||
// Force dlopen so @Native lookups in DynamicLibrary.process() succeed.
|
||||
dlopenGlobalPlatformSpecific('ffi_test_functions');
|
||||
|
||||
testAsTypedList();
|
||||
testRefcounted();
|
||||
}
|
||||
|
||||
void testAsTypedList() {
|
||||
const length = 10;
|
||||
final ptr = calloc(length, sizeOf<Int16>()).cast<Int16>();
|
||||
final typedList = ptr.asTypedList(length, finalizer: freePointer);
|
||||
print(typedList);
|
||||
}
|
||||
|
||||
@Native<Pointer<Void> Function(IntPtr num, IntPtr size)>(isLeaf: true)
|
||||
external Pointer<Void> calloc(int num, int size);
|
||||
|
||||
final freePointer = DynamicLibrary.process()
|
||||
.lookup<NativeFunction<Void Function(Pointer<Void>)>>('free');
|
||||
|
||||
void testRefcounted() {
|
||||
final peer = allocateRefcountedResource();
|
||||
final resource = peer.ref.resource;
|
||||
final typedList1 = resource.asTypedList(128,
|
||||
finalizer: decreaseRefcountPointer.cast(), token: peer.cast());
|
||||
increaseRefcount(peer);
|
||||
print(typedList1);
|
||||
final typedList2 = resource.asTypedList(128,
|
||||
finalizer: decreaseRefcountPointer.cast(), token: peer.cast());
|
||||
increaseRefcount(peer);
|
||||
print(typedList2);
|
||||
}
|
||||
|
||||
@Native<Pointer<RefCountedResource> Function()>(
|
||||
symbol: 'AllocateRefcountedResource', isLeaf: true)
|
||||
external Pointer<RefCountedResource> allocateRefcountedResource();
|
||||
|
||||
@Native<Void Function(Pointer<RefCountedResource>)>(
|
||||
symbol: 'IncreaseRefcount', isLeaf: true)
|
||||
external void increaseRefcount(Pointer<RefCountedResource> peer);
|
||||
|
||||
final decreaseRefcountPointer = DynamicLibrary.process()
|
||||
.lookup<NativeFunction<Void Function(Pointer<RefCountedResource>)>>(
|
||||
'DecreaseRefcount');
|
||||
|
||||
final class RefCountedResource extends Struct {
|
||||
external Pointer<Int8> resource;
|
||||
|
||||
@IntPtr()
|
||||
external int refcount;
|
||||
}
|
||||
@@ -42,6 +42,9 @@ var denylist = [
|
||||
// Don't try to invoke FFI Natives on simulator.
|
||||
// TODO(http://dartbug.com/48365): Support FFI in simulators.
|
||||
'dart._internal.FinalizerEntry.setExternalSize',
|
||||
|
||||
// Don't instantiate structs with bogus memory.
|
||||
'dart.ffi._AsTypedListFinalizerData',
|
||||
];
|
||||
|
||||
bool isDenylisted(Symbol qualifiedSymbol) {
|
||||
|
||||
@@ -44,6 +44,9 @@ var denylist = [
|
||||
// Don't try to invoke FFI Natives on simulator.
|
||||
// TODO(http://dartbug.com/48365): Support FFI in simulators.
|
||||
'dart._internal.FinalizerEntry.setExternalSize',
|
||||
|
||||
// Don't instantiate structs with bogus memory.
|
||||
'dart.ffi._AsTypedListFinalizerData',
|
||||
];
|
||||
|
||||
bool isDenylisted(Symbol qualifiedSymbol) {
|
||||
|
||||
Reference in New Issue
Block a user