[vm] Repair the resolver abstraction.
- Move resolution logic that accumulated in the runtime entries into Resolver. - Improve NoSuchMethodError for closures when --no-lazy-dispatchers - Fix concurrent modification of Null class by nullability propagation. Change-Id: Id979459bea43d318a4bb8fd904ef7f23e2711342 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97846 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Régis Crelier <regis@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
e979448597
commit
3fd124db1f
@@ -241,22 +241,7 @@ RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr,
|
||||
field_ = cls_.LookupFieldAllowPrivate(function_name2_);
|
||||
if (!function_.IsNull() && !function_.is_static()) {
|
||||
// Maybe this was a method extractor.
|
||||
function2_ =
|
||||
Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_);
|
||||
if (!function2_.IsNull()) {
|
||||
error_ = CompileFunction(function2_);
|
||||
if (error_.IsError()) {
|
||||
if (FLAG_trace_compilation_trace) {
|
||||
THR_Print(
|
||||
"Compilation trace: error compiling extractor %s for "
|
||||
"%s,%s,%s (%s)\n",
|
||||
function2_.ToCString(), uri_.ToCString(),
|
||||
class_name_.ToCString(), function_name_.ToCString(),
|
||||
Error::Cast(error_).ToErrorCString());
|
||||
}
|
||||
return error_.raw();
|
||||
}
|
||||
}
|
||||
// TODO(rmacnak)
|
||||
}
|
||||
}
|
||||
if (field_.IsNull() && is_getter) {
|
||||
|
||||
@@ -503,10 +503,7 @@ FlowGraph::ToCheck FlowGraph::CheckForInstanceCall(
|
||||
#else
|
||||
const Class& null_class =
|
||||
Class::Handle(zone(), isolate()->object_store()->null_class());
|
||||
const Function& target = Function::Handle(
|
||||
zone(),
|
||||
Resolver::ResolveDynamicAnyArgs(zone(), null_class, method_name));
|
||||
if (!target.IsNull()) {
|
||||
if (Resolver::HasDefinition(zone(), null_class, method_name)) {
|
||||
return ToCheck::kCheckCid;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -310,11 +310,12 @@ void FlowGraphTypePropagator::CheckNonNullSelector(
|
||||
// Nothing to do if type is already non-nullable.
|
||||
return;
|
||||
}
|
||||
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const Class& null_class =
|
||||
Class::Handle(Isolate::Current()->object_store()->null_class());
|
||||
const Function& target = Function::Handle(Resolver::ResolveDynamicAnyArgs(
|
||||
Thread::Current()->zone(), null_class, function_name));
|
||||
if (target.IsNull()) {
|
||||
Class::Handle(zone, thread->isolate()->object_store()->null_class());
|
||||
if (!Resolver::HasDefinition(zone, null_class, function_name)) {
|
||||
// If the selector is not defined on Null, we can propagate non-nullness.
|
||||
CompileType* type = TypeOf(receiver);
|
||||
if (type->is_nullable()) {
|
||||
|
||||
@@ -267,7 +267,14 @@ RawObject* DartEntry::InvokeClosure(const Array& arguments,
|
||||
}
|
||||
|
||||
// No compatible method or getter so invoke noSuchMethod.
|
||||
return InvokeNoSuchMethod(instance, Symbols::Call(), arguments,
|
||||
String& function_name = String::Handle(zone);
|
||||
if (instance.IsClosure()) {
|
||||
function_name = Function::Handle(zone, Closure::Cast(instance).function())
|
||||
.QualifiedUserVisibleName();
|
||||
} else {
|
||||
function_name = Symbols::Call().raw();
|
||||
}
|
||||
return InvokeNoSuchMethod(instance, function_name, arguments,
|
||||
arguments_descriptor);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class ArgumentsDescriptor : public ValueObject {
|
||||
bool MatchesNameAt(intptr_t i, const String& other) const;
|
||||
// Returns array of argument names in the arguments order.
|
||||
RawArray* GetArgumentNames() const;
|
||||
const Array& array() const { return array_; }
|
||||
|
||||
// Generated code support.
|
||||
static intptr_t type_args_len_offset();
|
||||
|
||||
+84
-60
@@ -3094,13 +3094,11 @@ RawFunction* Function::GetDynamicInvocationForwarder(
|
||||
}
|
||||
|
||||
// Check if function actually needs a dynamic invocation forwarder.
|
||||
if (!kernel::NeedsDynamicInvocationForwarder(*this)) {
|
||||
if ((kind() == RawFunction::kInvokeFieldDispatcher) ||
|
||||
!kernel::NeedsDynamicInvocationForwarder(*this)) {
|
||||
result = raw();
|
||||
} else if (allow_add) {
|
||||
result = CreateDynamicInvocationForwarder(mangled_name);
|
||||
}
|
||||
|
||||
if (allow_add) {
|
||||
owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
|
||||
}
|
||||
|
||||
@@ -3325,7 +3323,7 @@ RawObject* Class::InvokeGetter(const String& getter_name,
|
||||
|
||||
if (field.IsNull() || field.IsUninitialized()) {
|
||||
const String& internal_getter_name =
|
||||
String::Handle(zone, Field::GetterName(getter_name));
|
||||
String::Handle(zone, Field::GetterSymbol(getter_name));
|
||||
Function& getter =
|
||||
Function::Handle(zone, LookupStaticFunction(internal_getter_name));
|
||||
|
||||
@@ -3382,7 +3380,7 @@ RawObject* Class::InvokeSetter(const String& setter_name,
|
||||
// Check for real fields and user-defined setters.
|
||||
const Field& field = Field::Handle(zone, LookupStaticField(setter_name));
|
||||
const String& internal_setter_name =
|
||||
String::Handle(zone, Field::SetterName(setter_name));
|
||||
String::Handle(zone, Field::SetterSymbol(setter_name));
|
||||
|
||||
if (!field.IsNull() && check_is_entrypoint) {
|
||||
CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly));
|
||||
@@ -3442,13 +3440,15 @@ RawObject* Class::InvokeSetter(const String& setter_name,
|
||||
return value.raw();
|
||||
}
|
||||
|
||||
RawObject* Class::Invoke(const String& function_name,
|
||||
RawObject* Class::Invoke(const String& function_name_in,
|
||||
const Array& args,
|
||||
const Array& arg_names,
|
||||
bool respect_reflectable,
|
||||
bool check_is_entrypoint) const {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const String& function_name =
|
||||
String::Handle(zone, Symbols::New(thread, function_name_in));
|
||||
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
const int kTypeArgsLen = 0;
|
||||
@@ -10956,12 +10956,12 @@ RawObject* Library::InvokeGetter(const String& getter_name,
|
||||
// owner class.
|
||||
const Class& klass = Class::Handle(field.Owner());
|
||||
const String& internal_getter_name =
|
||||
String::Handle(Field::GetterName(getter_name));
|
||||
String::Handle(Field::GetterSymbol(getter_name));
|
||||
getter = klass.LookupStaticFunction(internal_getter_name);
|
||||
} else {
|
||||
// No field found. Check for a getter in the lib.
|
||||
const String& internal_getter_name =
|
||||
String::Handle(Field::GetterName(getter_name));
|
||||
String::Handle(Field::GetterSymbol(getter_name));
|
||||
obj = LookupLocalOrReExportObject(internal_getter_name);
|
||||
if (obj.IsFunction()) {
|
||||
getter = Function::Cast(obj).raw();
|
||||
@@ -11012,7 +11012,7 @@ RawObject* Library::InvokeSetter(const String& setter_name,
|
||||
bool check_is_entrypoint) const {
|
||||
Object& obj = Object::Handle(LookupLocalOrReExportObject(setter_name));
|
||||
const String& internal_setter_name =
|
||||
String::Handle(Field::SetterName(setter_name));
|
||||
String::Handle(Field::SetterSymbol(setter_name));
|
||||
AbstractType& setter_type = AbstractType::Handle();
|
||||
AbstractType& argument_type = AbstractType::Handle(value.GetType(Heap::kOld));
|
||||
if (obj.IsField()) {
|
||||
@@ -11070,16 +11070,22 @@ RawObject* Library::InvokeSetter(const String& setter_name,
|
||||
return DartEntry::InvokeFunction(setter, args);
|
||||
}
|
||||
|
||||
RawObject* Library::Invoke(const String& function_name,
|
||||
RawObject* Library::Invoke(const String& function_name_in,
|
||||
const Array& args,
|
||||
const Array& arg_names,
|
||||
bool respect_reflectable,
|
||||
bool check_is_entrypoint) const {
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const String& function_name =
|
||||
String::Handle(zone, Symbols::New(thread, function_name_in));
|
||||
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
const int kTypeArgsLen = 0;
|
||||
|
||||
Function& function = Function::Handle();
|
||||
Object& obj = Object::Handle(LookupLocalOrReExportObject(function_name));
|
||||
Function& function = Function::Handle(zone);
|
||||
Object& obj =
|
||||
Object::Handle(zone, LookupLocalOrReExportObject(function_name));
|
||||
if (obj.IsFunction()) {
|
||||
function ^= obj.raw();
|
||||
}
|
||||
@@ -11090,37 +11096,39 @@ RawObject* Library::Invoke(const String& function_name,
|
||||
|
||||
if (function.IsNull()) {
|
||||
// Didn't find a method: try to find a getter and invoke call on its result.
|
||||
const Object& getter_result = Object::Handle(InvokeGetter(
|
||||
function_name, false, respect_reflectable, check_is_entrypoint));
|
||||
const Object& getter_result = Object::Handle(
|
||||
zone, InvokeGetter(function_name, false, respect_reflectable,
|
||||
check_is_entrypoint));
|
||||
if (getter_result.raw() != Object::sentinel().raw()) {
|
||||
if (check_is_entrypoint) {
|
||||
CHECK_ERROR(EntryPointFieldInvocationError(function_name));
|
||||
}
|
||||
// Make room for the closure (receiver) in arguments.
|
||||
intptr_t numArgs = args.Length();
|
||||
const Array& call_args = Array::Handle(Array::New(numArgs + 1));
|
||||
Object& temp = Object::Handle();
|
||||
const Array& call_args = Array::Handle(zone, Array::New(numArgs + 1));
|
||||
Object& temp = Object::Handle(zone);
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
temp = args.At(i);
|
||||
call_args.SetAt(i + 1, temp);
|
||||
}
|
||||
call_args.SetAt(0, getter_result);
|
||||
const Array& call_args_descriptor_array =
|
||||
Array::Handle(ArgumentsDescriptor::New(
|
||||
kTypeArgsLen, call_args.Length(), arg_names));
|
||||
Array::Handle(zone, ArgumentsDescriptor::New(
|
||||
kTypeArgsLen, call_args.Length(), arg_names));
|
||||
// Call closure.
|
||||
return DartEntry::InvokeClosure(call_args, call_args_descriptor_array);
|
||||
}
|
||||
}
|
||||
|
||||
const Array& args_descriptor_array = Array::Handle(
|
||||
ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
|
||||
ArgumentsDescriptor args_descriptor(args_descriptor_array);
|
||||
const TypeArguments& type_args = Object::null_type_arguments();
|
||||
if (function.IsNull() || !function.AreValidArguments(args_descriptor, NULL) ||
|
||||
(respect_reflectable && !function.is_reflectable())) {
|
||||
return ThrowNoSuchMethod(
|
||||
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
|
||||
AbstractType::Handle(zone,
|
||||
Class::Handle(zone, toplevel_class()).RareType()),
|
||||
function_name, args, arg_names, InvocationMirror::kTopLevel,
|
||||
InvocationMirror::kMethod);
|
||||
}
|
||||
@@ -13444,20 +13452,14 @@ bool ICData::AddSmiSmiCheckForFastSmiStubs() const {
|
||||
bool is_smi_two_args_op = false;
|
||||
|
||||
ASSERT(NumArgsTested() == 2);
|
||||
const String& name = String::Handle(target_name());
|
||||
const Class& smi_class = Class::Handle(Smi::Class());
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
Function& smi_op_target =
|
||||
Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (smi_op_target.IsNull() &&
|
||||
Function::IsDynamicInvocationForwarderName(name)) {
|
||||
const String& demangled =
|
||||
String::Handle(Function::DemangleDynamicInvocationForwarderName(name));
|
||||
smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
|
||||
}
|
||||
#endif
|
||||
const String& name = String::Handle(zone, target_name());
|
||||
const Class& smi_class = Class::Handle(zone, Smi::Class());
|
||||
const ArgumentsDescriptor& args_desc =
|
||||
ArgumentsDescriptor(Array::Handle(zone, arguments_descriptor()));
|
||||
Function& smi_op_target = Function::Handle(
|
||||
zone,
|
||||
Resolver::ResolveDynamicForReceiverClass(smi_class, name, args_desc));
|
||||
|
||||
if (NumberOfChecksIs(0)) {
|
||||
GrowableArray<intptr_t> class_ids(2);
|
||||
@@ -15869,9 +15871,14 @@ RawObject* Instance::InvokeGetter(const String& getter_name,
|
||||
}
|
||||
|
||||
const String& internal_getter_name =
|
||||
String::Handle(zone, Field::GetterName(getter_name));
|
||||
Function& function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
|
||||
String::Handle(zone, Field::GetterSymbol(getter_name));
|
||||
const intptr_t kTypeArgsLen = 0;
|
||||
const intptr_t kNumArgs = 1;
|
||||
const ArgumentsDescriptor& args_desc = ArgumentsDescriptor(
|
||||
Array::Handle(zone, ArgumentsDescriptor::New(kTypeArgsLen, kNumArgs)));
|
||||
Function& function =
|
||||
Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
|
||||
klass, internal_getter_name, args_desc));
|
||||
|
||||
if (!function.IsNull() && check_is_entrypoint) {
|
||||
// The getter must correspond to either an entry-point field or a getter
|
||||
@@ -15889,7 +15896,12 @@ RawObject* Instance::InvokeGetter(const String& getter_name,
|
||||
|
||||
// Check for method extraction when method extractors are not created.
|
||||
if (function.IsNull() && !FLAG_lazy_dispatchers) {
|
||||
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
|
||||
const intptr_t kGetterNumArgs = 1;
|
||||
const ArgumentsDescriptor& getter_args_desc =
|
||||
ArgumentsDescriptor(Array::Handle(
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, kGetterNumArgs)));
|
||||
function = Resolver::ResolveDynamicForReceiverClass(klass, getter_name,
|
||||
getter_args_desc);
|
||||
|
||||
if (!function.IsNull() && check_is_entrypoint) {
|
||||
CHECK_ERROR(function.VerifyClosurizedEntryPoint());
|
||||
@@ -15902,8 +15914,6 @@ RawObject* Instance::InvokeGetter(const String& getter_name,
|
||||
}
|
||||
}
|
||||
|
||||
const int kTypeArgsLen = 0;
|
||||
const int kNumArgs = 1;
|
||||
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
|
||||
args.SetAt(0, *this);
|
||||
const Array& args_descriptor = Array::Handle(
|
||||
@@ -15927,9 +15937,14 @@ RawObject* Instance::InvokeSetter(const String& setter_name,
|
||||
}
|
||||
|
||||
const String& internal_setter_name =
|
||||
String::Handle(zone, Field::SetterName(setter_name));
|
||||
const Function& setter = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name));
|
||||
String::Handle(zone, Field::SetterSymbol(setter_name));
|
||||
const intptr_t kTypeArgsLen = 0;
|
||||
const intptr_t kNumArgs = 2;
|
||||
const ArgumentsDescriptor& args_desc = ArgumentsDescriptor(
|
||||
Array::Handle(zone, ArgumentsDescriptor::New(kTypeArgsLen, kNumArgs)));
|
||||
const Function& setter =
|
||||
Function::Handle(zone, Resolver::ResolveDynamicForReceiverClass(
|
||||
klass, internal_setter_name, args_desc));
|
||||
|
||||
if (check_is_entrypoint) {
|
||||
// The setter must correspond to either an entry-point field or a setter
|
||||
@@ -15945,8 +15960,6 @@ RawObject* Instance::InvokeSetter(const String& setter_name,
|
||||
}
|
||||
}
|
||||
|
||||
const int kTypeArgsLen = 0;
|
||||
const int kNumArgs = 2;
|
||||
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
|
||||
args.SetAt(0, *this);
|
||||
args.SetAt(1, value);
|
||||
@@ -15958,46 +15971,51 @@ RawObject* Instance::InvokeSetter(const String& setter_name,
|
||||
type_args);
|
||||
}
|
||||
|
||||
RawObject* Instance::Invoke(const String& function_name,
|
||||
RawObject* Instance::Invoke(const String& function_name_in,
|
||||
const Array& args,
|
||||
const Array& arg_names,
|
||||
bool respect_reflectable,
|
||||
bool check_is_entrypoint) const {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
const String& function_name =
|
||||
String::Handle(zone, Symbols::New(thread, function_name_in));
|
||||
Class& klass = Class::Handle(zone, clazz());
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
const intptr_t kTypeArgsLen = 0;
|
||||
const Array& args_descriptor = Array::Handle(
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
|
||||
Function& function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(
|
||||
klass, function_name, ArgumentsDescriptor(args_descriptor)));
|
||||
|
||||
if (!function.IsNull() && check_is_entrypoint) {
|
||||
CHECK_ERROR(function.VerifyCallEntryPoint());
|
||||
}
|
||||
|
||||
// TODO(regis): Support invocation of generic functions with type arguments.
|
||||
const int kTypeArgsLen = 0;
|
||||
const Array& args_descriptor = Array::Handle(
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
|
||||
|
||||
TypeArguments& type_args = TypeArguments::Handle(zone);
|
||||
if (klass.NumTypeArguments() > 0) {
|
||||
type_args ^= GetTypeArguments();
|
||||
}
|
||||
|
||||
if (function.IsNull()) {
|
||||
if (function.IsNull() && !FLAG_lazy_dispatchers) {
|
||||
// Didn't find a method: try to find a getter and invoke call on its result.
|
||||
const String& getter_name =
|
||||
String::Handle(zone, Field::GetterName(function_name));
|
||||
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
|
||||
String::Handle(zone, Field::GetterSymbol(function_name));
|
||||
const int kGetterNumArgs = 1;
|
||||
const Array& getter_args_descriptor = Array::Handle(
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, kGetterNumArgs));
|
||||
function = Resolver::ResolveDynamicForReceiverClass(
|
||||
klass, getter_name, ArgumentsDescriptor(getter_args_descriptor));
|
||||
if (!function.IsNull()) {
|
||||
if (check_is_entrypoint) {
|
||||
CHECK_ERROR(EntryPointFieldInvocationError(function_name));
|
||||
}
|
||||
ASSERT(function.kind() != RawFunction::kMethodExtractor);
|
||||
// Invoke the getter.
|
||||
const int kNumArgs = 1;
|
||||
const Array& getter_args = Array::Handle(zone, Array::New(kNumArgs));
|
||||
const Array& getter_args =
|
||||
Array::Handle(zone, Array::New(kGetterNumArgs));
|
||||
getter_args.SetAt(0, *this);
|
||||
const Array& getter_args_descriptor = Array::Handle(
|
||||
zone, ArgumentsDescriptor::New(kTypeArgsLen, getter_args.Length()));
|
||||
const Object& getter_result = Object::Handle(
|
||||
zone, InvokeInstanceFunction(*this, function, getter_name,
|
||||
getter_args, getter_args_descriptor,
|
||||
@@ -21982,6 +22000,12 @@ RawError* VerifyEntryPoint(
|
||||
}
|
||||
#endif
|
||||
if (!is_marked_entrypoint) {
|
||||
if (member.IsFunction() &&
|
||||
Function::Cast(member).kind() == RawFunction::kInvokeFieldDispatcher) {
|
||||
return EntryPointFieldInvocationError(
|
||||
String::Handle(Function::Cast(member).name()));
|
||||
}
|
||||
|
||||
const char* member_cstring =
|
||||
member.IsFunction()
|
||||
? OS::SCreate(
|
||||
|
||||
@@ -711,13 +711,15 @@ void ICData::Reset(Zone* zone) const {
|
||||
if (IsImmutable()) {
|
||||
return;
|
||||
}
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
const String& name = String::Handle(target_name());
|
||||
const Class& smi_class = Class::Handle(Smi::Class());
|
||||
const String& name = String::Handle(zone, target_name());
|
||||
const Class& smi_class = Class::Handle(zone, Smi::Class());
|
||||
const ArgumentsDescriptor& args_desc =
|
||||
ArgumentsDescriptor(Array::Handle(zone, arguments_descriptor()));
|
||||
const Function& smi_op_target = Function::Handle(
|
||||
Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
|
||||
zone,
|
||||
Resolver::ResolveDynamicForReceiverClass(smi_class, name, args_desc));
|
||||
GrowableArray<intptr_t> class_ids(2);
|
||||
Function& target = Function::Handle();
|
||||
Function& target = Function::Handle(zone);
|
||||
GetCheckAt(0, &class_ids, &target);
|
||||
if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
|
||||
(class_ids[1] == kSmiCid)) {
|
||||
@@ -725,7 +727,7 @@ void ICData::Reset(Zone* zone) const {
|
||||
// count.
|
||||
ClearCountAt(0);
|
||||
WriteSentinelAt(1);
|
||||
const Array& array = Array::Handle(entries());
|
||||
const Array& array = Array::Handle(zone, entries());
|
||||
array.Truncate(2 * TestEntryLength());
|
||||
return;
|
||||
}
|
||||
|
||||
+144
-56
@@ -16,16 +16,20 @@ namespace dart {
|
||||
|
||||
DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
|
||||
|
||||
// The actual names of named arguments are not checked by the dynamic resolver,
|
||||
// but by the method entry code. It is important that the dynamic resolver
|
||||
// checks that no named arguments are passed to a method that does not accept
|
||||
// them, since the entry code of such a method does not check for named
|
||||
// arguments. The dynamic resolver actually checks that a valid number of named
|
||||
// arguments is passed in.
|
||||
// Find the target of an instance call, which might be
|
||||
// - a user-defined method
|
||||
// - a method extractor (getter call to user-defined regular method)
|
||||
// - an invoke field dispatcher (regular method call to user-defined getter)
|
||||
// - a dynamic invocation forwarder (dynamic call to one of the above)
|
||||
// - a no-such-method dispatcher (no target or target with wrong number of
|
||||
// positional arguments)
|
||||
//
|
||||
// Positional arguments are checked here: the number of positional arguments
|
||||
// doesn't match the target, a no-such-method-dispatcher will be returned.
|
||||
// Named arguments are checked in the target's prologue.
|
||||
RawFunction* Resolver::ResolveDynamic(const Instance& receiver,
|
||||
const String& function_name,
|
||||
const ArgumentsDescriptor& args_desc) {
|
||||
// Figure out type of receiver first.
|
||||
const Class& cls = Class::Handle(receiver.clazz());
|
||||
return ResolveDynamicForReceiverClass(cls, function_name, args_desc);
|
||||
}
|
||||
@@ -38,46 +42,54 @@ RawFunction* Resolver::ResolveDynamicForReceiverClass(
|
||||
Thread* thread = Thread::Current();
|
||||
Zone* zone = thread->zone();
|
||||
|
||||
if (FLAG_trace_resolving) {
|
||||
THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
|
||||
String::Handle(zone, receiver_class.Name()).ToCString());
|
||||
}
|
||||
|
||||
Function& function = Function::Handle(
|
||||
zone,
|
||||
ResolveDynamicAnyArgs(zone, receiver_class, function_name, allow_add));
|
||||
zone, ResolveDynamicAnyArgs(zone, receiver_class, function_name,
|
||||
args_desc, allow_add));
|
||||
|
||||
if (function.IsNull() || !function.AreValidArguments(args_desc, NULL)) {
|
||||
// Return a null function to signal to the upper levels to dispatch to
|
||||
// "noSuchMethod" function.
|
||||
if (FLAG_trace_resolving) {
|
||||
String& error_message =
|
||||
String::Handle(zone, Symbols::New(thread, "function not found"));
|
||||
if (!function.IsNull()) {
|
||||
// Obtain more detailed error message.
|
||||
function.AreValidArguments(args_desc, &error_message);
|
||||
if (FLAG_lazy_dispatchers) {
|
||||
String& demangled = String::Handle(zone);
|
||||
if (Function::IsDynamicInvocationForwarderName(function_name)) {
|
||||
demangled =
|
||||
Function::DemangleDynamicInvocationForwarderName(function_name);
|
||||
} else {
|
||||
demangled = function_name.raw();
|
||||
}
|
||||
THR_Print("ResolveDynamic error '%s': %s.\n", function_name.ToCString(),
|
||||
error_message.ToCString());
|
||||
function = receiver_class.GetInvocationDispatcher(
|
||||
demangled, args_desc.array(), RawFunction::kNoSuchMethodDispatcher,
|
||||
allow_add);
|
||||
if (!function.IsNull()) {
|
||||
function.set_is_reflectable(true);
|
||||
}
|
||||
} else {
|
||||
function = Function::null();
|
||||
}
|
||||
return Function::null();
|
||||
}
|
||||
|
||||
// FLAG_lazy_dispatchers && allow_add -> !function.IsNull()
|
||||
ASSERT(!function.IsNull() || !FLAG_lazy_dispatchers || !allow_add);
|
||||
|
||||
if (FLAG_trace_resolving) {
|
||||
THR_Print("ResolveDynamic result: %s\n", function.ToCString());
|
||||
}
|
||||
|
||||
return function.raw();
|
||||
}
|
||||
|
||||
RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name,
|
||||
bool allow_add) {
|
||||
RawFunction* Resolver::ResolveDynamicAnyArgs(
|
||||
Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name,
|
||||
const ArgumentsDescriptor& args_desc,
|
||||
bool allow_add) {
|
||||
Class& cls = Class::Handle(zone, receiver_class.raw());
|
||||
if (FLAG_trace_resolving) {
|
||||
THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
|
||||
String::Handle(zone, cls.Name()).ToCString());
|
||||
}
|
||||
Function& function = Function::Handle(zone);
|
||||
|
||||
String& demangled = String::Handle(zone);
|
||||
|
||||
const bool is_getter = Field::IsGetterName(function_name);
|
||||
if (is_getter) {
|
||||
demangled ^= Field::NameFromGetter(function_name);
|
||||
}
|
||||
|
||||
Function& function = Function::Handle(zone);
|
||||
if (Function::IsDynamicInvocationForwarderName(function_name)) {
|
||||
demangled ^=
|
||||
Function::DemangleDynamicInvocationForwarderName(function_name);
|
||||
@@ -97,42 +109,118 @@ RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
||||
// forwarder to do, see `kernel::DynamicInvocationForwarder`). For these
|
||||
// functions, we won't have built a `dyn:` version, but it's safe to just
|
||||
// return the original version directly.
|
||||
return !function.IsNull() ? function.raw()
|
||||
: ResolveDynamicAnyArgs(zone, receiver_class,
|
||||
demangled, allow_add);
|
||||
return !function.IsNull()
|
||||
? function.raw()
|
||||
: ResolveDynamicAnyArgs(zone, receiver_class, demangled,
|
||||
args_desc, allow_add);
|
||||
#else
|
||||
function =
|
||||
ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
|
||||
return function.IsNull() ? function.raw()
|
||||
: function.GetDynamicInvocationForwarder(
|
||||
function_name, allow_add);
|
||||
function = ResolveDynamicAnyArgs(zone, receiver_class, demangled, args_desc,
|
||||
allow_add);
|
||||
|
||||
if (function.IsNull() || !function.AreValidArguments(args_desc, NULL)) {
|
||||
return Function::null();
|
||||
}
|
||||
|
||||
return function.GetDynamicInvocationForwarder(function_name, allow_add);
|
||||
#endif
|
||||
}
|
||||
|
||||
const bool is_getter = Field::IsGetterName(function_name);
|
||||
const bool is_setter = Field::IsSetterName(function_name);
|
||||
if (is_getter) {
|
||||
demangled = Field::NameFromGetter(function_name);
|
||||
} else if (!is_setter) {
|
||||
demangled = Field::GetterSymbol(function_name);
|
||||
}
|
||||
|
||||
// Now look for an instance function whose name matches function_name
|
||||
// in the class.
|
||||
while (!cls.IsNull()) {
|
||||
function ^= cls.LookupDynamicFunction(function_name);
|
||||
function = cls.LookupDynamicFunction(function_name);
|
||||
if (!function.IsNull()) {
|
||||
return function.raw();
|
||||
}
|
||||
// Getter invocation might actually be a method extraction.
|
||||
if (FLAG_lazy_dispatchers) {
|
||||
if (is_getter && function.IsNull()) {
|
||||
function ^= cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull() && allow_add) {
|
||||
// We were looking for the getter but found a method with the same
|
||||
// name. Create a method extractor and return it.
|
||||
// The extractor does not exist yet, so using GetMethodExtractor is
|
||||
// not necessary here.
|
||||
function ^= function.CreateMethodExtractor(function_name);
|
||||
if (is_getter) {
|
||||
// Getter invocation might actually be a method extraction.
|
||||
ASSERT(!Field::IsGetterName(demangled));
|
||||
function = cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull()) {
|
||||
if (FLAG_lazy_dispatchers && allow_add) {
|
||||
return function.CreateMethodExtractor(function_name);
|
||||
} else {
|
||||
return Function::null();
|
||||
}
|
||||
}
|
||||
} else if (!is_setter) {
|
||||
// Regular invocation might actually be call-through-getter.
|
||||
ASSERT(Field::IsGetterName(demangled));
|
||||
function = cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull()) {
|
||||
if (FLAG_lazy_dispatchers && allow_add) {
|
||||
bool is_reflectable = function.is_reflectable();
|
||||
function = cls.GetInvocationDispatcher(
|
||||
function_name, args_desc.array(),
|
||||
RawFunction::kInvokeFieldDispatcher, allow_add);
|
||||
function.set_is_reflectable(is_reflectable);
|
||||
return function.raw();
|
||||
} else {
|
||||
return Function::null();
|
||||
}
|
||||
}
|
||||
}
|
||||
cls = cls.SuperClass();
|
||||
}
|
||||
return function.raw();
|
||||
|
||||
return Function::null();
|
||||
}
|
||||
|
||||
bool Resolver::HasDefinition(Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name) {
|
||||
String& demangled = String::Handle(zone);
|
||||
if (Function::IsDynamicInvocationForwarderName(function_name)) {
|
||||
demangled = Function::DemangleDynamicInvocationForwarderName(function_name);
|
||||
return HasDefinition(zone, receiver_class, demangled);
|
||||
}
|
||||
|
||||
const bool is_getter = Field::IsGetterName(function_name);
|
||||
const bool is_setter = Field::IsSetterName(function_name);
|
||||
if (is_getter) {
|
||||
demangled = Field::NameFromGetter(function_name);
|
||||
} else if (!is_setter) {
|
||||
demangled = Field::GetterSymbol(function_name);
|
||||
}
|
||||
|
||||
Class& cls = Class::Handle(zone, receiver_class.raw());
|
||||
Function& function = Function::Handle(zone);
|
||||
|
||||
// Now look for an instance function whose name matches function_name
|
||||
// in the class.
|
||||
while (!cls.IsNull()) {
|
||||
function = cls.LookupDynamicFunction(function_name);
|
||||
if (!function.IsNull()) {
|
||||
return true;
|
||||
}
|
||||
if (is_getter) {
|
||||
// Getter invocation might actually be a method extraction.
|
||||
ASSERT(!Field::IsGetterName(demangled));
|
||||
function = cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull()) {
|
||||
return true;
|
||||
}
|
||||
} else if (!is_setter) {
|
||||
// Regular invocation might actually be call-through-getter.
|
||||
ASSERT(Field::IsGetterName(demangled));
|
||||
function = cls.LookupDynamicFunction(demangled);
|
||||
if (!function.IsNull()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cls = cls.SuperClass();
|
||||
}
|
||||
|
||||
// NoSuchMethod
|
||||
return false;
|
||||
}
|
||||
|
||||
RawFunction* Resolver::ResolveStatic(const Library& library,
|
||||
|
||||
+19
-5
@@ -34,11 +34,16 @@ class Resolver : public AllStatic {
|
||||
const ArgumentsDescriptor& args_desc,
|
||||
bool allow_add = true);
|
||||
|
||||
// If 'allow_add' is true we may add a function to the class during lookup.
|
||||
static RawFunction* ResolveDynamicAnyArgs(Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name,
|
||||
bool allow_add = true);
|
||||
// Answers whether the receiver class has a definition for given selector
|
||||
// other than noSuchMethod. Helper threads such as the background compiler
|
||||
// must use this instead of checking for null answer from ResolveDynamicX
|
||||
// because a helper thread cannot safely use 'allow_add = true' because it
|
||||
// may concurrently mutate a class, and 'allow_add = false' will give false
|
||||
/// negatives if lazy dispatchers are disabled or a lazy dispatcher hasn't
|
||||
// been created yet.
|
||||
static bool HasDefinition(Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name);
|
||||
|
||||
// Resolve specified dart static function. If library.IsNull, use
|
||||
// either application library or core library if no application library
|
||||
@@ -68,6 +73,15 @@ class Resolver : public AllStatic {
|
||||
intptr_t type_args_len,
|
||||
intptr_t num_arguments,
|
||||
const Array& argument_names);
|
||||
|
||||
private:
|
||||
// If 'allow_add' is true we may add a function to the class during lookup.
|
||||
static RawFunction* ResolveDynamicAnyArgs(
|
||||
Zone* zone,
|
||||
const Class& receiver_class,
|
||||
const String& function_name,
|
||||
const ArgumentsDescriptor& args_desc,
|
||||
bool allow_add = true);
|
||||
};
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -1019,78 +1019,6 @@ DEFINE_RUNTIME_ENTRY(SingleStepHandler, 0) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// An instance call of the form o.f(...) could not be resolved. Check if
|
||||
// there is a getter with the same name. If so, invoke it. If the value is
|
||||
// a closure, invoke it with the given arguments. If the value is a
|
||||
// non-closure, attempt to invoke "call" on it.
|
||||
static bool ResolveCallThroughGetter(const Instance& receiver,
|
||||
const Class& receiver_class,
|
||||
const String& target_name,
|
||||
const Array& arguments_descriptor,
|
||||
Function* result) {
|
||||
// 1. Check if there is a getter with the same name.
|
||||
const String& getter_name = String::Handle(Field::GetterName(target_name));
|
||||
const int kTypeArgsLen = 0;
|
||||
const int kNumArguments = 1;
|
||||
ArgumentsDescriptor args_desc(
|
||||
Array::Handle(ArgumentsDescriptor::New(kTypeArgsLen, kNumArguments)));
|
||||
const Function& getter =
|
||||
Function::Handle(Resolver::ResolveDynamicForReceiverClass(
|
||||
receiver_class, getter_name, args_desc));
|
||||
if (getter.IsNull() || getter.IsMethodExtractor()) {
|
||||
return false;
|
||||
}
|
||||
const Function& target_function =
|
||||
Function::Handle(receiver_class.GetInvocationDispatcher(
|
||||
target_name, arguments_descriptor,
|
||||
RawFunction::kInvokeFieldDispatcher, FLAG_lazy_dispatchers));
|
||||
ASSERT(!target_function.IsNull() || !FLAG_lazy_dispatchers);
|
||||
if (FLAG_trace_ic) {
|
||||
OS::PrintErr(
|
||||
"InvokeField IC miss: adding <%s> id:%" Pd " -> <%s>\n",
|
||||
Class::Handle(receiver.clazz()).ToCString(), receiver.GetClassId(),
|
||||
target_function.IsNull() ? "null" : target_function.ToCString());
|
||||
}
|
||||
*result = target_function.raw();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle other invocations (implicit closures, noSuchMethod).
|
||||
RawFunction* InlineCacheMissHelper(const Instance& receiver,
|
||||
const Array& args_descriptor,
|
||||
const String& target_name) {
|
||||
const Class& receiver_class = Class::Handle(receiver.clazz());
|
||||
|
||||
// Handle noSuchMethod for dyn:methodName by getting a noSuchMethod dispatcher
|
||||
// (or a call-through getter for methodName).
|
||||
if (Function::IsDynamicInvocationForwarderName(target_name)) {
|
||||
const String& demangled = String::Handle(
|
||||
Function::DemangleDynamicInvocationForwarderName(target_name));
|
||||
return InlineCacheMissHelper(receiver, args_descriptor, demangled);
|
||||
}
|
||||
|
||||
Function& result = Function::Handle();
|
||||
if (!ResolveCallThroughGetter(receiver, receiver_class, target_name,
|
||||
args_descriptor, &result)) {
|
||||
ArgumentsDescriptor desc(args_descriptor);
|
||||
const Function& target_function =
|
||||
Function::Handle(receiver_class.GetInvocationDispatcher(
|
||||
target_name, args_descriptor, RawFunction::kNoSuchMethodDispatcher,
|
||||
FLAG_lazy_dispatchers));
|
||||
if (FLAG_trace_ic) {
|
||||
OS::PrintErr(
|
||||
"NoSuchMethod IC miss: adding <%s> id:%" Pd " -> <%s>\n",
|
||||
Class::Handle(receiver.clazz()).ToCString(), receiver.GetClassId(),
|
||||
target_function.IsNull() ? "null" : target_function.ToCString());
|
||||
}
|
||||
result = target_function.raw();
|
||||
}
|
||||
// May be null if --no-lazy-dispatchers, in which case dispatch will be
|
||||
// handled by InvokeNoSuchMethodDispatcher.
|
||||
ASSERT(!result.IsNull() || !FLAG_lazy_dispatchers);
|
||||
return result.raw();
|
||||
}
|
||||
|
||||
// Perform the subtype and return constant function based on the result.
|
||||
static RawFunction* ComputeTypeCheckTarget(const Instance& receiver,
|
||||
const AbstractType& type,
|
||||
@@ -1131,11 +1059,6 @@ static RawFunction* InlineCacheMissHandler(
|
||||
String::Handle(ic_data.target_name()).ToCString(),
|
||||
receiver.ToCString());
|
||||
}
|
||||
const Array& args_descriptor =
|
||||
Array::Handle(ic_data.arguments_descriptor());
|
||||
const String& target_name = String::Handle(ic_data.target_name());
|
||||
target_function =
|
||||
InlineCacheMissHelper(receiver, args_descriptor, target_name);
|
||||
}
|
||||
if (target_function.IsNull()) {
|
||||
ASSERT(!FLAG_lazy_dispatchers);
|
||||
@@ -1285,7 +1208,8 @@ static bool IsSingleTarget(Isolate* isolate,
|
||||
intptr_t lower_cid,
|
||||
intptr_t upper_cid,
|
||||
const Function& target,
|
||||
const String& name) {
|
||||
const String& name,
|
||||
const ArgumentsDescriptor& args_desc) {
|
||||
Class& cls = Class::Handle(zone);
|
||||
ClassTable* table = isolate->class_table();
|
||||
Function& other_target = Function::Handle(zone);
|
||||
@@ -1294,8 +1218,8 @@ static bool IsSingleTarget(Isolate* isolate,
|
||||
cls = table->At(cid);
|
||||
if (cls.is_abstract()) continue;
|
||||
if (!cls.is_allocated()) continue;
|
||||
other_target =
|
||||
Resolver::ResolveDynamicAnyArgs(zone, cls, name, false /* allow_add */);
|
||||
other_target = Resolver::ResolveDynamicForReceiverClass(
|
||||
cls, name, args_desc, false /* allow_add */);
|
||||
if (other_target.raw() != target.raw()) {
|
||||
return false;
|
||||
}
|
||||
@@ -1347,9 +1271,6 @@ DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 1) {
|
||||
ArgumentsDescriptor args_desc(descriptor);
|
||||
Function& target_function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
|
||||
if (target_function.IsNull()) {
|
||||
target_function = InlineCacheMissHelper(receiver, descriptor, name);
|
||||
}
|
||||
if (target_function.IsNull()) {
|
||||
ASSERT(!FLAG_lazy_dispatchers);
|
||||
} else {
|
||||
@@ -1371,7 +1292,7 @@ DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 1) {
|
||||
}
|
||||
|
||||
if (IsSingleTarget(isolate, zone, unchecked_lower, unchecked_upper,
|
||||
target_function, name)) {
|
||||
target_function, name, args_desc)) {
|
||||
cache.set_lower_limit(lower);
|
||||
cache.set_upper_limit(upper);
|
||||
// Return the ICData. The single target stub will jump to continue in the
|
||||
@@ -1421,9 +1342,6 @@ DEFINE_RUNTIME_ENTRY(UnlinkedCall, 2) {
|
||||
ArgumentsDescriptor args_desc(descriptor);
|
||||
Function& target_function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
|
||||
if (target_function.IsNull()) {
|
||||
target_function = InlineCacheMissHelper(receiver, descriptor, name);
|
||||
}
|
||||
if (target_function.IsNull()) {
|
||||
ASSERT(!FLAG_lazy_dispatchers);
|
||||
} else {
|
||||
@@ -1504,9 +1422,6 @@ DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 1) {
|
||||
ArgumentsDescriptor args_desc(descriptor);
|
||||
Function& target_function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
|
||||
if (target_function.IsNull()) {
|
||||
target_function = InlineCacheMissHelper(receiver, descriptor, name);
|
||||
}
|
||||
if (target_function.IsNull()) {
|
||||
ASSERT(!FLAG_lazy_dispatchers);
|
||||
} else {
|
||||
@@ -1523,7 +1438,8 @@ DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 1) {
|
||||
upper = old_expected_cid.Value();
|
||||
}
|
||||
|
||||
if (IsSingleTarget(isolate, zone, lower, upper, target_function, name)) {
|
||||
if (IsSingleTarget(isolate, zone, lower, upper, target_function, name,
|
||||
args_desc)) {
|
||||
const SingleTargetCache& cache =
|
||||
SingleTargetCache::Handle(SingleTargetCache::New());
|
||||
const Code& code = Code::Handle(target_function.CurrentCode());
|
||||
@@ -1584,7 +1500,6 @@ DEFINE_RUNTIME_ENTRY(MegamorphicCacheMissHandler, 3) {
|
||||
Function& target_function = Function::Handle(
|
||||
zone, Resolver::ResolveDynamicForReceiverClass(cls, name, args_desc));
|
||||
if (target_function.IsNull()) {
|
||||
target_function = InlineCacheMissHelper(receiver, descriptor, name);
|
||||
if (target_function.IsNull()) {
|
||||
ASSERT(!FLAG_lazy_dispatchers);
|
||||
arguments.SetReturn(target_function);
|
||||
@@ -1676,10 +1591,7 @@ DEFINE_RUNTIME_ENTRY(InterpretedInterfaceCallMissHandler, 3) {
|
||||
// TODO(regis): In order to substitute 'simple_instance_of_function', the 2nd
|
||||
// arg to the call, the type, is needed.
|
||||
|
||||
if (target_function.IsNull()) {
|
||||
target_function = InlineCacheMissHelper(receiver, arg_desc, target_name);
|
||||
}
|
||||
ASSERT(!target_function.IsNull());
|
||||
ASSERT(!target_function.IsNull() && FLAG_lazy_dispatchers);
|
||||
arguments.SetReturn(target_function);
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user