diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart index 30aab7d6467..aa2140b5441 100644 --- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart +++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart @@ -18,6 +18,7 @@ class FfiVerifier extends RecursiveAstVisitor { static const _allocateExtensionMethodName = 'call'; static const _allocatorExtensionName = 'AllocatorAlloc'; static const _dartFfiLibraryName = 'dart.ffi'; + static const _opaqueClassName = 'Opaque'; static const List _primitiveIntegerNativeTypes = [ 'Int8', @@ -54,7 +55,7 @@ class FfiVerifier extends RecursiveAstVisitor { @override void visitClassDeclaration(ClassDeclaration node) { inStruct = false; - // Only the Struct class may be extended. + // Only the Allocator, Opaque and Struct class may be extended. ExtendsClause extendsClause = node.extendsClause; if (extendsClause != null) { final TypeName superclass = extendsClause.superclass; @@ -62,7 +63,8 @@ class FfiVerifier extends RecursiveAstVisitor { final className = superclass.name.staticElement.name; if (className == _structClassName) { inStruct = true; - } else if (className != _allocatorClassName) { + } else if (className != _allocatorClassName && + className != _opaqueClassName) { _errorReporter.reportErrorForNode( FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS, superclass.name, @@ -245,6 +247,21 @@ class FfiVerifier extends RecursiveAstVisitor { return false; } + /// Returns `true` iff [nativeType] is a opaque type, i.e. a subtype of `Opaque`. + bool _isOpaqueClass(DartType nativeType) { + if (nativeType is InterfaceType) { + final superType = nativeType.element.supertype; + if (superType == null) { + return false; + } + final superClassElement = superType.element; + if (superClassElement.library.name == _dartFfiLibraryName) { + return superClassElement.name == _opaqueClassName; + } + } + return false; + } + /// Return `true` if the given [element] represents the class `Pointer`. bool _isPointer(Element element) => element.name == 'Pointer' && element.library.name == _dartFfiLibraryName; @@ -345,6 +362,9 @@ class FfiVerifier extends RecursiveAstVisitor { } return true; } + if (_isOpaqueClass(nativeType)) { + return true; + } } else if (nativeType is FunctionType) { return _isValidFfiNativeFunctionType(nativeType); } diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.expect index 7121f548592..0b7ee1a711c 100644 --- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.expect +++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.expect @@ -41,4 +41,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect index 906fb3f4fb5..ce991f15fe2 100644 --- a/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general_nnbd_opt_out/ffi_sample.dart.weak.transformed.expect @@ -72,4 +72,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.expect index 0b6b078b28d..d0832bc305d 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.expect @@ -35,4 +35,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect index 92815a2ef0d..4719e507148 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect @@ -66,4 +66,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.expect index 0b6b078b28d..d0832bc305d 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.expect @@ -35,4 +35,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect index 92815a2ef0d..4719e507148 100644 --- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect @@ -66,4 +66,4 @@ constants { Constructor coverage from constants: org-dartlang-testcase:///ffi_sample.dart: -- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:116:9) +- Double. (from org-dartlang-sdk:///sdk/lib/ffi/native_type.dart:122:9) diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart index 0e05d237121..17ad5f5fee9 100644 --- a/pkg/vm/lib/transformations/ffi.dart +++ b/pkg/vm/lib/transformations/ffi.dart @@ -34,6 +34,7 @@ enum NativeType { kFloat, kDouble, kVoid, + kOpaque, kStruct, kHandle, } @@ -60,6 +61,7 @@ const List nativeTypeClassNames = [ 'Float', 'Double', 'Void', + 'Opaque', 'Struct', 'Handle' ]; @@ -86,6 +88,7 @@ const List nativeTypeSizes = [ 4, // Float 8, // Double UNKNOWN, // Void + UNKNOWN, // Opaque UNKNOWN, // Struct WORD_SIZE, // Handle ]; @@ -207,6 +210,7 @@ class FfiTransformer extends Transformer { final Library ffiLibrary; final Class allocatorClass; final Class nativeFunctionClass; + final Class opaqueClass; final Class pointerClass; final Class structClass; final Procedure allocateMethod; @@ -263,6 +267,7 @@ class FfiTransformer extends Transformer { ffiLibrary = index.getLibrary('dart:ffi'), allocatorClass = index.getClass('dart:ffi', 'Allocator'), nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'), + opaqueClass = index.getClass('dart:ffi', 'Opaque'), pointerClass = index.getClass('dart:ffi', 'Pointer'), structClass = index.getClass('dart:ffi', 'Struct'), allocateMethod = index.getMember('dart:ffi', 'AllocatorAlloc', 'call'), diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart index 65041ae2278..7bfd6a241d4 100644 --- a/pkg/vm/lib/transformations/ffi_use_sites.dart +++ b/pkg/vm/lib/transformations/ffi_use_sites.dart @@ -488,10 +488,15 @@ class _FfiUseSiteTransformer extends FfiTransformer { Class _extendsOrImplementsSealedClass(Class klass) { final Class superClass = klass.supertype?.classNode; - // The Struct class can be extended, but subclasses of Struct cannot be (nor - // implemented). - if (klass != structClass && hierarchy.isSubtypeOf(klass, structClass)) { - return superClass != structClass ? superClass : null; + // The Opaque and Struct classes can be extended, but subclasses + // cannot be (nor implemented). + if (klass != opaqueClass && + klass != structClass && + (hierarchy.isSubtypeOf(klass, opaqueClass) || + hierarchy.isSubtypeOf(klass, structClass))) { + return superClass != opaqueClass && superClass != structClass + ? superClass + : null; } if (!nativeTypesClasses.contains(klass)) { diff --git a/samples/ffi/async/sample_async_callback.dart b/samples/ffi/async/sample_async_callback.dart index 59ae31b527f..4901c550296 100644 --- a/samples/ffi/async/sample_async_callback.dart +++ b/samples/ffi/async/sample_async_callback.dart @@ -105,7 +105,7 @@ final stopWorkSimulator = final executeCallback = dl.lookupFunction), void Function(Pointer)>('ExecuteCallback'); -class Work extends Struct {} +class Work extends Opaque {} Future asyncSleep(int ms) { return new Future.delayed(Duration(milliseconds: ms)); diff --git a/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart b/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart index cab2cb0dd3f..39de01d78e1 100644 --- a/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart +++ b/samples/ffi/resource_management/pool_isolate_shutdown_sample.dart @@ -86,4 +86,4 @@ final releaseResource = ffiTestDynamicLibrary.lookupFunction< void Function(Pointer)>("ReleaseResource"); /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples/ffi/resource_management/pool_sample.dart b/samples/ffi/resource_management/pool_sample.dart index 00ee1aec920..22a579ca98a 100644 --- a/samples/ffi/resource_management/pool_sample.dart +++ b/samples/ffi/resource_management/pool_sample.dart @@ -110,4 +110,4 @@ main() { } /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples/ffi/resource_management/pool_zoned_sample.dart b/samples/ffi/resource_management/pool_zoned_sample.dart index 05f179468b6..42a481f4155 100644 --- a/samples/ffi/resource_management/pool_zoned_sample.dart +++ b/samples/ffi/resource_management/pool_zoned_sample.dart @@ -111,4 +111,4 @@ main() { } /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples/ffi/sqlite/lib/src/bindings/types.dart b/samples/ffi/sqlite/lib/src/bindings/types.dart index 494cdef11a1..40edc87e44a 100644 --- a/samples/ffi/sqlite/lib/src/bindings/types.dart +++ b/samples/ffi/sqlite/lib/src/bindings/types.dart @@ -13,7 +13,7 @@ import "dart:ffi"; /// is its destructor. There are many other interfaces (such as /// [sqlite3_prepare_v2()], [sqlite3_create_function()], and /// [sqlite3_busy_timeout()] to name but three) that are methods on an -class Database extends Struct {} +class Database extends Opaque {} /// SQL Statement Object /// @@ -36,7 +36,7 @@ class Database extends Struct {} /// /// Refer to documentation on individual methods above for additional /// information. -class Statement extends Struct {} +class Statement extends Opaque {} /// Dynamically Typed Value Object /// @@ -72,4 +72,4 @@ class Statement extends Struct {} /// [sqlite3_result_value()] and [sqlite3_bind_value()]. /// The [sqlite3_value_blob | sqlite3_value_type()] family of /// interfaces require protected sqlite3_value objects. -class Value extends Struct {} +class Value extends Opaque {} diff --git a/samples_2/ffi/async/sample_async_callback.dart b/samples_2/ffi/async/sample_async_callback.dart index 5e6d79af4dd..2405a8a3fc8 100644 --- a/samples_2/ffi/async/sample_async_callback.dart +++ b/samples_2/ffi/async/sample_async_callback.dart @@ -107,7 +107,7 @@ final stopWorkSimulator = final executeCallback = dl.lookupFunction), void Function(Pointer)>('ExecuteCallback'); -class Work extends Struct {} +class Work extends Opaque {} Future asyncSleep(int ms) { return new Future.delayed(Duration(milliseconds: ms)); diff --git a/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart b/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart index 8d3401bd41f..4c8eb0ffaee 100644 --- a/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart +++ b/samples_2/ffi/resource_management/pool_isolate_shutdown_sample.dart @@ -88,4 +88,4 @@ final releaseResource = ffiTestDynamicLibrary.lookupFunction< void Function(Pointer)>("ReleaseResource"); /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples_2/ffi/resource_management/pool_sample.dart b/samples_2/ffi/resource_management/pool_sample.dart index 87c5e397142..ff141560ff2 100644 --- a/samples_2/ffi/resource_management/pool_sample.dart +++ b/samples_2/ffi/resource_management/pool_sample.dart @@ -112,4 +112,4 @@ main() { } /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples_2/ffi/resource_management/pool_zoned_sample.dart b/samples_2/ffi/resource_management/pool_zoned_sample.dart index 0b4e8ff5370..087a8d07abc 100644 --- a/samples_2/ffi/resource_management/pool_zoned_sample.dart +++ b/samples_2/ffi/resource_management/pool_zoned_sample.dart @@ -113,4 +113,4 @@ main() { } /// Represents some opaque resource being managed by a library. -class SomeResource extends Struct {} +class SomeResource extends Opaque {} diff --git a/samples_2/ffi/sqlite/lib/src/bindings/types.dart b/samples_2/ffi/sqlite/lib/src/bindings/types.dart index f6a1736ffee..763f47eae74 100644 --- a/samples_2/ffi/sqlite/lib/src/bindings/types.dart +++ b/samples_2/ffi/sqlite/lib/src/bindings/types.dart @@ -15,7 +15,7 @@ import "dart:ffi"; /// is its destructor. There are many other interfaces (such as /// [sqlite3_prepare_v2()], [sqlite3_create_function()], and /// [sqlite3_busy_timeout()] to name but three) that are methods on an -class Database extends Struct {} +class Database extends Opaque {} /// SQL Statement Object /// @@ -38,7 +38,7 @@ class Database extends Struct {} /// /// Refer to documentation on individual methods above for additional /// information. -class Statement extends Struct {} +class Statement extends Opaque {} /// Dynamically Typed Value Object /// @@ -74,4 +74,4 @@ class Statement extends Struct {} /// [sqlite3_result_value()] and [sqlite3_bind_value()]. /// The [sqlite3_value_blob | sqlite3_value_type()] family of /// interfaces require protected sqlite3_value objects. -class Value extends Struct {} +class Value extends Opaque {} diff --git a/sdk/lib/ffi/ffi.dart b/sdk/lib/ffi/ffi.dart index 01a407f48cf..bce5feb5c77 100644 --- a/sdk/lib/ffi/ffi.dart +++ b/sdk/lib/ffi/ffi.dart @@ -559,7 +559,7 @@ extension NativePort on SendPort { } /// Opaque, not exposing it's members. -class Dart_CObject extends Struct {} +class Dart_CObject extends Opaque {} typedef Dart_NativeMessageHandler = Void Function(Int64, Pointer); diff --git a/sdk/lib/ffi/native_type.dart b/sdk/lib/ffi/native_type.dart index e3193423912..31e4b72d85f 100644 --- a/sdk/lib/ffi/native_type.dart +++ b/sdk/lib/ffi/native_type.dart @@ -12,6 +12,12 @@ abstract class NativeType { const NativeType(); } +/// [Opaque]'s subtypes represent opaque types in C. +/// +/// [Opaque]'s subtypes are not constructible in the Dart code and serve purely +/// as markers in type signatures. +abstract class Opaque extends NativeType {} + /// [_NativeInteger]'s subtypes represent a native integer in C. /// /// [_NativeInteger]'s subtypes are not constructible in the Dart code and serve diff --git a/tests/ffi/vmspecific_static_checks_test.dart b/tests/ffi/vmspecific_static_checks_test.dart index 9b7940ef957..02819f675ed 100644 --- a/tests/ffi/vmspecific_static_checks_test.dart +++ b/tests/ffi/vmspecific_static_checks_test.dart @@ -477,6 +477,8 @@ class IPointer implements Pointer {} //# 814: compile-time error class IStruct implements Struct {} //# 815: compile-time error +class IOpaque implements Opaque {} //# 816: compile-time error + class MyClass { int x; MyClass(this.x); diff --git a/tests/ffi_2/vmspecific_static_checks_test.dart b/tests/ffi_2/vmspecific_static_checks_test.dart index 967e16b3129..46321192c78 100644 --- a/tests/ffi_2/vmspecific_static_checks_test.dart +++ b/tests/ffi_2/vmspecific_static_checks_test.dart @@ -477,6 +477,8 @@ class IPointer implements Pointer {} //# 814: compile-time error class IStruct implements Struct {} //# 815: compile-time error +class IOpaque implements Opaque {} //# 816: compile-time error + class MyClass { int x; MyClass(this.x);