[vm/ffi] Stop reifying type argument in Pointer
All `Pointer`s will now have the `Never` type argument at runtime. TEST=tests/ffi(_2)/* Bug: https://github.com/dart-lang/sdk/issues/49935 Change-Id: I83c8bba9461c2cab22992ba2e3cf42b7b5f43c36 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,vm-kernel-mac-debug-x64-try,dart-sdk-mac-arm64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-ffi-android-debug-arm64c-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272201 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
091a292d82
commit
827d166ea0
@@ -89,6 +89,13 @@
|
||||
[#34233]: https://github.com/dart-lang/sdk/issues/34233
|
||||
[`ServiceExtensionResponse`]: https://api.dart.dev/stable/2.17.6/dart-developer/ServiceExtensionResponse-class.html#constants
|
||||
|
||||
#### `dart:ffi`
|
||||
|
||||
- **Breaking change** [#49935][]: The runtime type argument of `Pointer` has
|
||||
changed to `Never` in preparation of completely removing the runtime type
|
||||
argument. `Pointer.toString` has changed to not report any type argument.
|
||||
|
||||
[#49935]: https://github.com/dart-lang/sdk/issues/49935
|
||||
|
||||
#### `dart:html`
|
||||
|
||||
|
||||
+1
-2
@@ -122,7 +122,6 @@ DEFINE_NATIVE_ENTRY(Ffi_nativeCallbackFunction, 1, 2) {
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
const Function& function =
|
||||
Function::CheckedHandle(zone, arguments->NativeArg0());
|
||||
|
||||
@@ -164,7 +163,7 @@ DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) {
|
||||
}
|
||||
#endif
|
||||
|
||||
return Pointer::New(type_arg, entry_point);
|
||||
return Pointer::New(entry_point);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(DartNativeApiFunctionPointer, 0, 1) {
|
||||
|
||||
@@ -180,8 +180,6 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_executableLibrary, 0, 0) {
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(DynamicLibrary, dlib, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, argSymbolName,
|
||||
arguments->NativeArgAt(1));
|
||||
@@ -197,7 +195,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
|
||||
free(error);
|
||||
Exceptions::ThrowArgumentError(msg);
|
||||
}
|
||||
return Pointer::New(type_arg, pointer);
|
||||
return Pointer::New(pointer);
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Ffi_dl_getHandle, 0, 1) {
|
||||
@@ -291,8 +289,7 @@ static intptr_t FfiResolve(Dart_Handle asset_handle,
|
||||
|
||||
// Bootstrap to get the FFI Native resolver through a `native` call.
|
||||
DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
|
||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||
return Pointer::New(type_arg, reinterpret_cast<intptr_t>(FfiResolve));
|
||||
return Pointer::New(reinterpret_cast<intptr_t>(FfiResolve));
|
||||
}
|
||||
|
||||
#endif // defined(USING_SIMULATOR) || \
|
||||
|
||||
@@ -1324,29 +1324,15 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
||||
body +=
|
||||
Box(LoadIndexedInstr::RepresentationOfArrayElement(typed_data_cid));
|
||||
if (kind == MethodRecognizer::kFfiLoadPointer) {
|
||||
const auto class_table = thread_->isolate_group()->class_table();
|
||||
ASSERT(class_table->HasValidClassAt(kPointerCid));
|
||||
const auto& pointer_class =
|
||||
Class::ZoneHandle(H.zone(), class_table->At(kPointerCid));
|
||||
Class::ZoneHandle(Z, IG->object_store()->ffi_pointer_class());
|
||||
const auto& type_arguments = TypeArguments::ZoneHandle(
|
||||
Z, IG->object_store()->type_argument_never());
|
||||
|
||||
// We find the reified type to use for the pointer allocation.
|
||||
//
|
||||
// Call sites to this recognized method are guaranteed to pass a
|
||||
// Pointer<Pointer<X>> as RawParameterVariable(0). This function
|
||||
// will return a Pointer<X> object - for which we inspect the
|
||||
// reified type on the argument.
|
||||
//
|
||||
// The following is safe to do, as (1) we are guaranteed to have a
|
||||
// Pointer<Pointer<X>> as argument, and (2) the bound on the pointer
|
||||
// type parameter guarantees X is an interface type.
|
||||
// We do not reify Pointer type arguments
|
||||
ASSERT(function.NumTypeParameters() == 1);
|
||||
LocalVariable* address = MakeTemporary();
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(
|
||||
Slot::GetTypeArgumentsSlotFor(thread_, pointer_class));
|
||||
body += LoadNativeField(Slot::GetTypeArgumentsIndexSlot(
|
||||
thread_, Pointer::kNativeTypeArgPos));
|
||||
body += LoadNativeField(Slot::Type_arguments());
|
||||
body += Constant(type_arguments);
|
||||
body += AllocateObject(TokenPosition::kNoSource, pointer_class, 1);
|
||||
LocalVariable* pointer = MakeTemporary();
|
||||
body += LoadLocal(pointer);
|
||||
@@ -1381,37 +1367,6 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
||||
LocalVariable* arg_offset = parsed_function_->RawParameterVariable(1);
|
||||
LocalVariable* arg_value = parsed_function_->RawParameterVariable(2);
|
||||
|
||||
if (kind == MethodRecognizer::kFfiStorePointer) {
|
||||
// Do type check before anything untagged is on the stack.
|
||||
const auto class_table = thread_->isolate_group()->class_table();
|
||||
ASSERT(class_table->HasValidClassAt(kPointerCid));
|
||||
const auto& pointer_class =
|
||||
Class::ZoneHandle(H.zone(), class_table->At(kPointerCid));
|
||||
const auto& pointer_type_param =
|
||||
TypeParameter::ZoneHandle(pointer_class.TypeParameterAt(0));
|
||||
|
||||
// But we type check it as a method on a generic class at runtime.
|
||||
body += LoadLocal(arg_value); // value.
|
||||
body += Constant(pointer_type_param); // dst_type.
|
||||
// We pass the Pointer type argument as instantiator_type_args.
|
||||
//
|
||||
// Call sites to this recognized method are guaranteed to pass a
|
||||
// Pointer<Pointer<X>> as RawParameterVariable(0). This function
|
||||
// will takes a Pointer<X> object - for which we inspect the
|
||||
// reified type on the argument.
|
||||
//
|
||||
// The following is safe to do, as (1) we are guaranteed to have a
|
||||
// Pointer<Pointer<X>> as argument, and (2) the bound on the pointer
|
||||
// type parameter guarantees X is an interface type.
|
||||
body += LoadLocal(arg_pointer);
|
||||
body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
|
||||
body += LoadNativeField(
|
||||
Slot::GetTypeArgumentsSlotFor(thread_, pointer_class));
|
||||
body += NullConstant(); // function_type_args.
|
||||
body += AssertAssignable(TokenPosition::kNoSource, Symbols::Empty());
|
||||
body += Drop();
|
||||
}
|
||||
|
||||
ASSERT_EQUAL(function.NumParameters(), 3);
|
||||
body += LoadLocal(arg_offset);
|
||||
body += CheckNullOptimized(String::ZoneHandle(Z, function.name()));
|
||||
@@ -1448,14 +1403,14 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
||||
body += NullConstant();
|
||||
} break;
|
||||
case MethodRecognizer::kFfiFromAddress: {
|
||||
const auto class_table = thread_->isolate_group()->class_table();
|
||||
ASSERT(class_table->HasValidClassAt(kPointerCid));
|
||||
const auto& pointer_class =
|
||||
Class::ZoneHandle(H.zone(), class_table->At(kPointerCid));
|
||||
Class::ZoneHandle(Z, IG->object_store()->ffi_pointer_class());
|
||||
const auto& type_arguments = TypeArguments::ZoneHandle(
|
||||
Z, IG->object_store()->type_argument_never());
|
||||
|
||||
ASSERT(function.NumTypeParameters() == 1);
|
||||
ASSERT_EQUAL(function.NumParameters(), 1);
|
||||
body += LoadLocal(parsed_function_->RawTypeArgumentsVariable());
|
||||
body += Constant(type_arguments);
|
||||
body += AllocateObject(TokenPosition::kNoSource, pointer_class, 1);
|
||||
body += LoadLocal(MakeTemporary()); // Duplicate Pointer.
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0)); // Address.
|
||||
@@ -4306,15 +4261,17 @@ Fragment FlowGraphBuilder::NativeReturn(
|
||||
return Fragment(instr).closed();
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::FfiPointerFromAddress(const Type& result_type) {
|
||||
Fragment FlowGraphBuilder::FfiPointerFromAddress() {
|
||||
LocalVariable* address = MakeTemporary();
|
||||
LocalVariable* result = parsed_function_->expression_temp_var();
|
||||
|
||||
Class& result_class = Class::ZoneHandle(Z, result_type.type_class());
|
||||
Class& result_class =
|
||||
Class::ZoneHandle(Z, IG->object_store()->ffi_pointer_class());
|
||||
// This class might only be instantiated as a return type of ffi calls.
|
||||
result_class.EnsureIsFinalized(thread_);
|
||||
|
||||
TypeArguments& args = TypeArguments::ZoneHandle(Z, result_type.arguments());
|
||||
TypeArguments& args =
|
||||
TypeArguments::ZoneHandle(Z, IG->object_store()->type_argument_never());
|
||||
|
||||
// A kernel transform for FFI in the front-end ensures that type parameters
|
||||
// do not appear in the type arguments to a any Pointer classes in an FFI
|
||||
@@ -4656,8 +4613,7 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
|
||||
Fragment body;
|
||||
if (marshaller.IsPointer(arg_index)) {
|
||||
body += Box(kUnboxedFfiIntPtr);
|
||||
body += FfiPointerFromAddress(
|
||||
Type::CheckedHandle(Z, marshaller.CType(arg_index)));
|
||||
body += FfiPointerFromAddress();
|
||||
} else if (marshaller.IsHandle(arg_index)) {
|
||||
body += UnwrapHandle();
|
||||
} else if (marshaller.IsVoid(arg_index)) {
|
||||
|
||||
@@ -285,8 +285,8 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
// Converts 0 to false and the rest to true.
|
||||
Fragment IntToBool();
|
||||
|
||||
// Creates an ffi.Pointer holding a given address (TOS).
|
||||
Fragment FfiPointerFromAddress(const Type& result_type);
|
||||
// Creates an ffi.Pointer holding a given address.
|
||||
Fragment FfiPointerFromAddress();
|
||||
|
||||
// Pushes an (unboxed) bogus value returned when a native -> Dart callback
|
||||
// throws an exception.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+9
-11
@@ -2330,6 +2330,10 @@ ErrorPtr Object::Init(IsolateGroup* isolate_group,
|
||||
type.SetIsFinalized();
|
||||
type ^= type.Canonicalize(thread, nullptr);
|
||||
object_store->set_never_type(type);
|
||||
type_args = TypeArguments::New(1);
|
||||
type_args.SetTypeAt(0, type);
|
||||
type_args = type_args.Canonicalize(thread, nullptr);
|
||||
object_store->set_type_argument_never(type_args);
|
||||
|
||||
// Create and cache commonly used type arguments <int>, <double>,
|
||||
// <String>, <String, dynamic> and <String, String>.
|
||||
@@ -25689,16 +25693,12 @@ const char* ExternalTypedData::ToCString() const {
|
||||
return cls.ScrubbedNameCString();
|
||||
}
|
||||
|
||||
PointerPtr Pointer::New(const AbstractType& type_arg,
|
||||
uword native_address,
|
||||
Heap::Space space) {
|
||||
PointerPtr Pointer::New(uword native_address, Heap::Space space) {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
|
||||
TypeArguments& type_args = TypeArguments::Handle(zone);
|
||||
type_args = TypeArguments::New(1);
|
||||
type_args.SetTypeAt(Pointer::kNativeTypeArgPos, type_arg);
|
||||
type_args = type_args.Canonicalize(thread, nullptr);
|
||||
TypeArguments& type_args = TypeArguments::Handle(
|
||||
zone, IsolateGroup::Current()->object_store()->type_argument_never());
|
||||
|
||||
const Class& cls =
|
||||
Class::Handle(IsolateGroup::Current()->class_table()->At(kPointerCid));
|
||||
@@ -25714,10 +25714,8 @@ PointerPtr Pointer::New(const AbstractType& type_arg,
|
||||
}
|
||||
|
||||
const char* Pointer::ToCString() const {
|
||||
TypeArguments& type_args = TypeArguments::Handle(GetTypeArguments());
|
||||
String& type_args_name = String::Handle(type_args.UserVisibleName());
|
||||
return OS::SCreate(Thread::Current()->zone(), "Pointer%s: address=0x%" Px,
|
||||
type_args_name.ToCString(), NativeAddress());
|
||||
return OS::SCreate(Thread::Current()->zone(), "Pointer: address=0x%" Px,
|
||||
NativeAddress());
|
||||
}
|
||||
|
||||
DynamicLibraryPtr DynamicLibrary::New(void* handle, Heap::Space space) {
|
||||
|
||||
+1
-3
@@ -11263,9 +11263,7 @@ class ByteBuffer : public AllStatic {
|
||||
|
||||
class Pointer : public Instance {
|
||||
public:
|
||||
static PointerPtr New(const AbstractType& type_arg,
|
||||
uword native_address,
|
||||
Heap::Space space = Heap::kNew);
|
||||
static PointerPtr New(uword native_address, Heap::Space space = Heap::kNew);
|
||||
|
||||
static intptr_t InstanceSize() {
|
||||
return RoundedAllocationSize(sizeof(UntaggedPointer));
|
||||
|
||||
@@ -103,6 +103,7 @@ class ObjectPointerVisitor;
|
||||
RW(TypeArguments, type_argument_int) \
|
||||
RW(TypeArguments, type_argument_legacy_int) \
|
||||
RW(TypeArguments, type_argument_double) \
|
||||
RW(TypeArguments, type_argument_never) \
|
||||
RW(TypeArguments, type_argument_string) \
|
||||
RW(TypeArguments, type_argument_legacy_string) \
|
||||
RW(TypeArguments, type_argument_string_dynamic) \
|
||||
|
||||
@@ -5154,10 +5154,7 @@ static void NativeFinalizer_TwoEntriesCrossGen(
|
||||
queue_length_start = aq.queue()->Length();
|
||||
}
|
||||
|
||||
ObjectStore* object_store = thread->isolate_group()->object_store();
|
||||
const auto& void_type = Type::Handle(object_store->never_type());
|
||||
const auto& callback = Pointer::Handle(Pointer::New(
|
||||
void_type,
|
||||
reinterpret_cast<uword>(&NativeFinalizer_TwoEntriesCrossGen_Finalizer),
|
||||
spaces[3]));
|
||||
|
||||
@@ -5195,14 +5192,14 @@ static void NativeFinalizer_TwoEntriesCrossGen(
|
||||
|
||||
auto& value1 = String::Handle();
|
||||
auto& detach1 = String::Handle();
|
||||
const auto& token1 = Pointer::Handle(Pointer::New(
|
||||
void_type, reinterpret_cast<uword>(&token1_memory), spaces[3]));
|
||||
const auto& token1 = Pointer::Handle(
|
||||
Pointer::New(reinterpret_cast<uword>(&token1_memory), spaces[3]));
|
||||
entry1.set_token(token1);
|
||||
|
||||
auto& value2 = String::Handle();
|
||||
auto& detach2 = String::Handle();
|
||||
const auto& token2 = Pointer::Handle(Pointer::New(
|
||||
void_type, reinterpret_cast<uword>(&token2_memory), spaces[4]));
|
||||
const auto& token2 = Pointer::Handle(
|
||||
Pointer::New(reinterpret_cast<uword>(&token2_memory), spaces[4]));
|
||||
entry2.set_token(token2);
|
||||
entry2.set_detach(detach2);
|
||||
|
||||
|
||||
@@ -1400,9 +1400,12 @@ class UntaggedFfiTrampolineData : public UntaggedObject {
|
||||
private:
|
||||
RAW_HEAP_OBJECT_IMPLEMENTATION(FfiTrampolineData);
|
||||
|
||||
// Not traced. We don't need this info after precompilation, and FFI
|
||||
// trampolines are not supported in JIT snapshots.
|
||||
COMPRESSED_POINTER_FIELD(FunctionTypePtr, c_signature)
|
||||
|
||||
COMPRESSED_POINTER_FIELD(TypePtr, signature_type)
|
||||
VISIT_FROM(signature_type)
|
||||
COMPRESSED_POINTER_FIELD(FunctionTypePtr, c_signature)
|
||||
|
||||
// Target Dart method for callbacks, otherwise null.
|
||||
COMPRESSED_POINTER_FIELD(FunctionPtr, callback_target)
|
||||
|
||||
@@ -398,11 +398,10 @@ void testTypeTest() {
|
||||
|
||||
void testToString() {
|
||||
Pointer<Int16> p = calloc();
|
||||
Expect.stringEquals(
|
||||
"Pointer<Int16>: address=0x", p.toString().substring(0, 26));
|
||||
Expect.stringEquals("Pointer: address=0x", p.toString().substring(0, 19));
|
||||
calloc.free(p);
|
||||
Pointer<Int64> p2 = Pointer.fromAddress(0x123abc);
|
||||
Expect.stringEquals("Pointer<Int64>: address=0x123abc", p2.toString());
|
||||
Expect.stringEquals("Pointer: address=0x123abc", p2.toString());
|
||||
}
|
||||
|
||||
void testEquality() {
|
||||
|
||||
@@ -94,18 +94,15 @@ void store3() {
|
||||
final Pointer<Pointer<Int8>> a = calloc<Pointer<Int8>>();
|
||||
final Pointer<NativeType> b = calloc<Int8>().cast<Pointer<NativeType>>();
|
||||
|
||||
// Failing implicit downcast of argument at runtime.
|
||||
// Should fail now at runtime, should statically be rejected when NNBD lands.
|
||||
Expect.throws(() {
|
||||
a.value = b as Pointer<Int8>;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
a.value = b as Pointer<Int8>;
|
||||
|
||||
calloc.free(a);
|
||||
calloc.free(b);
|
||||
}
|
||||
|
||||
void store4() {
|
||||
// Reified as Pointer<Pointer<Int8>> at runtime.
|
||||
// Type arguments are not reified.
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<Int8>>();
|
||||
|
||||
final Pointer<Int8> b = calloc<Int8>();
|
||||
@@ -134,10 +131,8 @@ void store6() {
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<Int8>>();
|
||||
final Pointer<NativeType> b = calloc<Int8>().cast<Pointer<NativeType>>();
|
||||
|
||||
// Fails on type check of argument.
|
||||
Expect.throws(() {
|
||||
a.value = b;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
a.value = b;
|
||||
|
||||
calloc.free(a);
|
||||
calloc.free(b);
|
||||
@@ -156,7 +151,7 @@ void store7() {
|
||||
void store8() {
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<NativeType>>();
|
||||
|
||||
// Reified as Pointer<Int8> at runtime.
|
||||
// Type arguments are not reified.
|
||||
final Pointer<NativeType> b = calloc<Int8>();
|
||||
|
||||
a.value = b;
|
||||
@@ -219,11 +214,9 @@ void load4() {
|
||||
void load5() {
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<NativeType>>();
|
||||
|
||||
// Failing implicit downcast of return value at runtime.
|
||||
// Should fail now at runtime, should statically be rejected when NNBD lands.
|
||||
Expect.throws(() {
|
||||
Pointer<Int8> b = a.value as Pointer<Int8>;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
// ignore: unused_local_variable
|
||||
Pointer<Int8> b = a.value as Pointer<Int8>;
|
||||
|
||||
calloc.free(a);
|
||||
}
|
||||
|
||||
@@ -193,9 +193,8 @@ void callbackParamImplicitDowncast1() {
|
||||
final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerParamOp,
|
||||
CallbackNaTyPointerParamOpDart>(callbackParamOpName);
|
||||
final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
|
||||
Expect.throws(() {
|
||||
callback(fp as Pointer<NativeFunction<NaTyPointerParamOp>>);
|
||||
});
|
||||
// Pointer type arguments are not reified, any cast will succeed.
|
||||
callback(fp as Pointer<NativeFunction<NaTyPointerParamOp>>);
|
||||
}
|
||||
|
||||
void callbackParamSubtype1() {
|
||||
|
||||
@@ -400,11 +400,10 @@ void testTypeTest() {
|
||||
|
||||
void testToString() {
|
||||
Pointer<Int16> p = calloc();
|
||||
Expect.stringEquals(
|
||||
"Pointer<Int16>: address=0x", p.toString().substring(0, 26));
|
||||
Expect.stringEquals("Pointer: address=0x", p.toString().substring(0, 19));
|
||||
calloc.free(p);
|
||||
Pointer<Int64> p2 = Pointer.fromAddress(0x123abc);
|
||||
Expect.stringEquals("Pointer<Int64>: address=0x123abc", p2.toString());
|
||||
Expect.stringEquals("Pointer: address=0x123abc", p2.toString());
|
||||
}
|
||||
|
||||
void testEquality() {
|
||||
|
||||
@@ -96,18 +96,15 @@ void store3() {
|
||||
final Pointer<Pointer<Int8>> a = calloc<Pointer<Int8>>();
|
||||
final Pointer<NativeType> b = calloc<Int8>().cast<Pointer<NativeType>>();
|
||||
|
||||
// Failing implicit downcast of argument at runtime.
|
||||
// Should fail now at runtime, should statically be rejected when NNBD lands.
|
||||
Expect.throws(() {
|
||||
a.value = b;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
a.value = b;
|
||||
|
||||
calloc.free(a);
|
||||
calloc.free(b);
|
||||
}
|
||||
|
||||
void store4() {
|
||||
// Reified as Pointer<Pointer<Int8>> at runtime.
|
||||
// Type arguments are not reified.
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<Int8>>();
|
||||
|
||||
final Pointer<Int8> b = calloc<Int8>();
|
||||
@@ -119,11 +116,9 @@ void store4() {
|
||||
}
|
||||
|
||||
void store5() {
|
||||
// Reified as Pointer<Pointer<Int8>> at runtime.
|
||||
// Type arguments are not reified.
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<Int8>>();
|
||||
|
||||
final Pointer<NativeType> b =
|
||||
calloc<Int8>(); // Reified as Pointer<Int8> at runtime.
|
||||
final Pointer<NativeType> b = calloc<Int8>();
|
||||
|
||||
a.value = b;
|
||||
|
||||
@@ -136,10 +131,8 @@ void store6() {
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<Int8>>();
|
||||
final Pointer<NativeType> b = calloc<Int8>().cast<Pointer<NativeType>>();
|
||||
|
||||
// Fails on type check of argument.
|
||||
Expect.throws(() {
|
||||
a.value = b;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
a.value = b;
|
||||
|
||||
calloc.free(a);
|
||||
calloc.free(b);
|
||||
@@ -221,11 +214,8 @@ void load4() {
|
||||
void load5() {
|
||||
final Pointer<Pointer<NativeType>> a = calloc<Pointer<NativeType>>();
|
||||
|
||||
// Failing implicit downcast of return value at runtime.
|
||||
// Should fail now at runtime, should statically be rejected when NNBD lands.
|
||||
Expect.throws(() {
|
||||
Pointer<Int8> b = a.value;
|
||||
});
|
||||
// Type arguments are not reified.
|
||||
Pointer<Int8> b = a.value;
|
||||
|
||||
calloc.free(a);
|
||||
}
|
||||
|
||||
@@ -195,9 +195,8 @@ void callbackParamImplicitDowncast1() {
|
||||
final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerParamOp,
|
||||
CallbackNaTyPointerParamOpDart>(callbackParamOpName);
|
||||
final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
|
||||
Expect.throws(() {
|
||||
callback(fp);
|
||||
});
|
||||
// Pointer type arguments are not reified, any cast will succeed.
|
||||
callback(fp);
|
||||
}
|
||||
|
||||
void callbackParamSubtype1() {
|
||||
|
||||
Reference in New Issue
Block a user