[vm/ffi] Add `DynamicLibrary.close()`
This adds the `close()` method to `DynamicLibrary`, which uses
`dlclose()` on Unix and `FreeLibrary` on Windows to close a dynamic FFI
library.
TEST=tests/ffi/dylib_close_test.dart
Closes https://github.com/dart-lang/sdk/issues/40159
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-riscv64-try,vm-ffi-qemu-linux-release-arm-try,dart2wasm-linux-d8-try,dart2wasm-linux-chrome-try,vm-reload-rollback-linux-debug-x64-try,vm-reload-linux-debug-x64-try,vm-win-debug-x64c-try,vm-win-debug-x64-try,vm-aot-android-release-arm_x64-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-asan-linux-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-x64-try,vm-aot-mac-release-arm64-try,vm-aot-win-debug-x64c-try,vm-aot-win-release-x64-try
CoreLibraryReviewExempt: `dart:ffi` VM only API.
Change-Id: I73af98677b481902fe548bdcee56964a0195faf0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/307680
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/lib/ffi_dynamic_library.cc b/runtime/lib/ffi_dynamic_library.cc
index 0df6a41..09e159f 100644
--- a/runtime/lib/ffi_dynamic_library.cc
+++ b/runtime/lib/ffi_dynamic_library.cc
@@ -55,6 +55,9 @@
DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) {
SimulatorUnsupported();
}
+DEFINE_NATIVE_ENTRY(Ffi_dl_close, 0, 1) {
+ SimulatorUnsupported();
+}
DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
SimulatorUnsupported();
}
@@ -167,20 +170,46 @@
free(error);
Exceptions::ThrowArgumentError(msg);
}
- return DynamicLibrary::New(handle);
+ return DynamicLibrary::New(handle, true);
}
DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
- return DynamicLibrary::New(RTLD_DEFAULT);
+ return DynamicLibrary::New(RTLD_DEFAULT, false);
#else
- return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr);
+ return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr, false);
#endif
}
DEFINE_NATIVE_ENTRY(Ffi_dl_executableLibrary, 0, 0) {
- return DynamicLibrary::New(LoadDynamicLibrary(nullptr));
+ return DynamicLibrary::New(LoadDynamicLibrary(nullptr), false);
+}
+
+DEFINE_NATIVE_ENTRY(Ffi_dl_close, 0, 1) {
+ GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
+ if (dlib.IsClosed()) {
+ // Already closed, nothing to do
+ } else if (!dlib.CanBeClosed()) {
+ const String& msg = String::Handle(
+ String::New("DynamicLibrary.process() and DynamicLibrary.executable() "
+ "can't be closed."));
+ Exceptions::ThrowStateError(msg);
+ } else {
+ void* handle = dlib.GetHandle();
+ char* error = nullptr;
+ Utils::UnloadDynamicLibrary(handle, &error);
+
+ if (error == nullptr) {
+ dlib.SetClosed(true);
+ } else {
+ const String& msg = String::Handle(String::New(error));
+ free(error);
+ Exceptions::ThrowStateError(msg);
+ }
+ }
+
+ return Object::null();
}
DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
@@ -188,6 +217,12 @@
GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
arguments->NativeArgAt(1));
+ if (dlib.IsClosed()) {
+ const String& msg =
+ String::Handle(String::New("Cannot lookup symbols in closed library."));
+ Exceptions::ThrowStateError(msg);
+ }
+
void* handle = dlib.GetHandle();
char* error = nullptr;
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index bfe2979..0cabfa0 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -365,6 +365,7 @@
V(Ffi_asFunctionInternal, 2) \
V(Ffi_pointerFromFunction, 1) \
V(Ffi_dl_open, 1) \
+ V(Ffi_dl_close, 1) \
V(Ffi_dl_lookup, 2) \
V(Ffi_dl_getHandle, 1) \
V(Ffi_dl_providesSymbol, 2) \
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 4810d21..705d180 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -632,7 +632,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -1345,7 +1345,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -2047,7 +2047,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -2762,7 +2762,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -3471,7 +3471,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x10;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -4182,7 +4182,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x10;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -4886,7 +4886,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -5600,7 +5600,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -6297,7 +6297,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -7002,7 +7002,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -7696,7 +7696,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -8403,7 +8403,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -9104,7 +9104,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x10;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -9807,7 +9807,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x10;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -10503,7 +10503,7 @@
CompressedStackMaps_PayloadHeaderSize = 0x4;
static constexpr dart::compiler::target::word Context_header_size = 0xc;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
-static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0x8;
+static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize = 0xc;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -11209,7 +11209,7 @@
static constexpr dart::compiler::target::word Context_header_size = 0x18;
static constexpr dart::compiler::target::word Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -11977,7 +11977,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0xc;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x8;
+ 0xc;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -12760,7 +12760,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -13548,7 +13548,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -14333,7 +14333,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x10;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -15120,7 +15120,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x10;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -15904,7 +15904,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0xc;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x8;
+ 0xc;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -16688,7 +16688,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -17464,7 +17464,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0xc;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x8;
+ 0xc;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -18238,7 +18238,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -19017,7 +19017,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -19793,7 +19793,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x10;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -20571,7 +20571,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x10;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
@@ -21346,7 +21346,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0xc;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x8;
+ 0xc;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x14;
static constexpr dart::compiler::target::word
@@ -22121,7 +22121,7 @@
static constexpr dart::compiler::target::word AOT_Context_header_size = 0x18;
static constexpr dart::compiler::target::word AOT_Double_InstanceSize = 0x10;
static constexpr dart::compiler::target::word AOT_DynamicLibrary_InstanceSize =
- 0x10;
+ 0x18;
static constexpr dart::compiler::target::word
AOT_ExternalOneByteString_InstanceSize = 0x20;
static constexpr dart::compiler::target::word
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index b5d3173..56565d0 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -1059,6 +1059,12 @@
Exceptions::ThrowByType(Exceptions::kArgument, args);
}
+void Exceptions::ThrowStateError(const Instance& arg) {
+ const Array& args = Array::Handle(Array::New(1));
+ args.SetAt(0, arg);
+ Exceptions::ThrowByType(Exceptions::kState, args);
+}
+
void Exceptions::ThrowRangeError(const char* argument_name,
const Integer& argument_value,
intptr_t expected_from,
@@ -1126,6 +1132,10 @@
class_name = &Symbols::ArgumentError();
constructor_name = &Symbols::DotValue();
break;
+ case kState:
+ library = Library::CoreLibrary();
+ class_name = &Symbols::StateError();
+ break;
case kIntegerDivisionByZeroException:
library = Library::CoreLibrary();
class_name = &Symbols::IntegerDivisionByZeroException();
diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h
index 1573440..6983c0c 100644
--- a/runtime/vm/exceptions.h
+++ b/runtime/vm/exceptions.h
@@ -55,6 +55,7 @@
kRangeMsg,
kArgument,
kArgumentValue,
+ kState,
kIntegerDivisionByZeroException,
kNoSuchMethod,
kFormat,
@@ -78,6 +79,7 @@
DART_NORETURN static void ThrowOOM();
DART_NORETURN static void ThrowStackOverflow();
DART_NORETURN static void ThrowArgumentError(const Instance& arg);
+ DART_NORETURN static void ThrowStateError(const Instance& arg);
DART_NORETURN static void ThrowRangeError(const char* argument_name,
const Integer& argument_value,
intptr_t expected_from,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index ea0f243b..2bd5bbd 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -26397,13 +26397,17 @@
NativeAddress());
}
-DynamicLibraryPtr DynamicLibrary::New(void* handle, Heap::Space space) {
+DynamicLibraryPtr DynamicLibrary::New(void* handle,
+ bool canBeClosed,
+ Heap::Space space) {
DynamicLibrary& result = DynamicLibrary::Handle();
result ^=
Object::Allocate(kDynamicLibraryCid, DynamicLibrary::InstanceSize(),
space, DynamicLibrary::ContainsCompressedPointers());
NoSafepointScope no_safepoint;
result.SetHandle(handle);
+ result.SetClosed(false);
+ result.SetCanBeClosed(canBeClosed);
return result.ptr();
}
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 58590ff..5bd082d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -11793,7 +11793,9 @@
class DynamicLibrary : public Instance {
public:
- static DynamicLibraryPtr New(void* handle, Heap::Space space = Heap::kNew);
+ static DynamicLibraryPtr New(void* handle,
+ bool canBeClosed,
+ Heap::Space space = Heap::kNew);
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(UntaggedDynamicLibrary));
@@ -11814,6 +11816,25 @@
StoreNonPointer(&untag()->handle_, value);
}
+ bool CanBeClosed() const {
+ ASSERT(!IsNull());
+ return untag()->canBeClosed_;
+ }
+
+ void SetCanBeClosed(bool value) const {
+ ASSERT(!IsNull());
+ StoreNonPointer(&untag()->canBeClosed_, value);
+ }
+
+ bool IsClosed() const {
+ ASSERT(!IsNull());
+ return untag()->isClosed_;
+ }
+
+ void SetClosed(bool value) const {
+ StoreNonPointer(&untag()->isClosed_, value);
+ }
+
private:
FINAL_HEAP_OBJECT_IMPLEMENTATION(DynamicLibrary, Instance);
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index f0ca60d..cac0992 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -3301,6 +3301,8 @@
RAW_HEAP_OBJECT_IMPLEMENTATION(DynamicLibrary);
VISIT_NOTHING();
void* handle_;
+ bool isClosed_;
+ bool canBeClosed_;
friend class DynamicLibrary;
};
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 51fb5d6..b68748c 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -231,6 +231,8 @@
F(Pointer, data_) \
F(Pointer, type_arguments_) \
F(DynamicLibrary, handle_) \
+ F(DynamicLibrary, isClosed_) \
+ F(DynamicLibrary, canBeClosed_) \
F(FfiTrampolineData, c_signature_) \
F(FfiTrampolineData, callback_target_) \
F(FfiTrampolineData, callback_exceptional_return_) \
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 86013ad..548af80 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -24,6 +24,7 @@
V(ApiError, "ApiError") \
V(ArgDescVar, ":arg_desc") \
V(ArgumentError, "ArgumentError") \
+ V(StateError, "StateError") \
V(AssertionError, "_AssertionError") \
V(AssignIndexToken, "[]=") \
V(AsyncStarMoveNextHelper, "_asyncStarMoveNextHelper") \
diff --git a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
index a79a1c9..cee78e2 100644
--- a/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
+++ b/sdk/lib/_internal/vm/lib/ffi_dynamic_library_patch.dart
@@ -40,6 +40,10 @@
external int getHandle();
@patch
+ @pragma("vm:external-name", "Ffi_dl_close")
+ external void close();
+
+ @patch
bool operator ==(Object other) {
if (other is! DynamicLibrary) return false;
DynamicLibrary otherLib = other;
diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart
index 368891c..fa8147f 100644
--- a/sdk/lib/core/errors.dart
+++ b/sdk/lib/core/errors.dart
@@ -571,6 +571,7 @@
/// actions. The message should be descriptive.
class StateError extends Error {
final String message;
+ @pragma("vm:entry-point")
StateError(this.message);
String toString() => "Bad state: $message";
}
diff --git a/sdk/lib/ffi/dynamic_library.dart b/sdk/lib/ffi/dynamic_library.dart
index 2d919dd..da7a786 100644
--- a/sdk/lib/ffi/dynamic_library.dart
+++ b/sdk/lib/ffi/dynamic_library.dart
@@ -48,6 +48,19 @@
@Since('2.14')
external bool providesSymbol(String symbolName);
+ /// Closes this dynamic library.
+ ///
+ /// After calling [close], this library object can no longer be used for
+ /// lookups. Further, this information is forwarded to the operating system,
+ /// which may unload the library if there are no remaining references to it
+ /// in the current process.
+ ///
+ /// Depending on whether another reference to this library has been opened,
+ /// pointers and functions previously returned by [lookup] and
+ /// [DynamicLibraryExtension.lookupFunction] may become invalid as well.
+ @Since('3.1')
+ external void close();
+
/// Dynamic libraries are equal if they load the same library.
external bool operator ==(Object other);
diff --git a/tests/ffi/dylib_close_test.dart b/tests/ffi/dylib_close_test.dart
new file mode 100644
index 0000000..13ddd63
--- /dev/null
+++ b/tests/ffi/dylib_close_test.dart
@@ -0,0 +1,35 @@
+// 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 'dart:io';
+
+import 'package:expect/expect.dart';
+
+import 'dylib_utils.dart';
+
+void main() {
+ testClose();
+ testUnclosable();
+}
+
+void testClose() {
+ final lib = dlopenPlatformSpecific("ffi_test_functions");
+ lib.lookup('ReturnMaxUint8');
+
+ lib.close();
+ Expect.throwsStateError(
+ () => lib.lookup('ReturnMaxUint8'), 'Illegal lookup in closed library');
+ lib.close(); // Duplicate close should not crash.
+}
+
+void testUnclosable() {
+ final proc = DynamicLibrary.process();
+ final exec = DynamicLibrary.executable();
+
+ Expect.throwsStateError(proc.close);
+ Expect.throwsStateError(exec.close);
+}
diff --git a/tests/ffi_2/dylib_close_test.dart b/tests/ffi_2/dylib_close_test.dart
new file mode 100644
index 0000000..8814825
--- /dev/null
+++ b/tests/ffi_2/dylib_close_test.dart
@@ -0,0 +1,37 @@
+// 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
+
+// @dart=2.9
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+import 'dylib_utils.dart';
+
+void main() {
+ testClose();
+ testUnclosable();
+}
+
+void testClose() {
+ final lib = dlopenPlatformSpecific("ffi_test_functions");
+ lib.lookup('ReturnMaxUint8');
+
+ lib.close();
+ Expect.throwsStateError(
+ () => lib.lookup('ReturnMaxUint8'), 'Illegal lookup in closed library');
+ lib.close(); // Duplicate close should not crash.
+}
+
+void testUnclosable() {
+ final proc = DynamicLibrary.process();
+ final exec = DynamicLibrary.executable();
+
+ Expect.throwsStateError(proc.close);
+ Expect.throwsStateError(exec.close);
+}