[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:
Daco Harkes 2022-11-28 09:45:45 +00:00 committed by Commit Queue
parent 091a292d82
commit 827d166ea0
17 changed files with 372 additions and 437 deletions

View file

@ -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`

View file

@ -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) {

View file

@ -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) || \

View file

@ -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)) {

View file

@ -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

View file

@ -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) {

View file

@ -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));

View file

@ -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) \

View file

@ -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);

View file

@ -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)

View file

@ -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() {

View file

@ -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);
}

View file

@ -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() {

View file

@ -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() {

View file

@ -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);
}

View file

@ -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() {