Revert "[vm] Repair the resolver abstraction, take 2."

This reverts commit d79787771e.

Reason for revert:

This caused significant performance regressions on our protobuf_decode* benchmarks (probably on more benchmarks, but we don't yet have results due to some perf infra issue).

To avoid letting the regressions land for downstream users we'll revert it.

We should measure performance impact before landing this again.

Original change's description:
> [vm] Repair the resolver abstraction, take 2.
> 
>  - 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: Ib05b431a289d847785032dda46e1bbcb524b7343
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98428
> Commit-Queue: Ryan Macnak <rmacnak@google.com>
> Reviewed-by: Régis Crelier <regis@google.com>

TBR=rmacnak@google.com,alexmarkov@google.com,regis@google.com

Change-Id: I4f0f0a0d7c6018fc5968aafdce30f6d2e7495059
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99140
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann 2019-04-11 09:57:25 +00:00
parent 7cdba2e711
commit aa9ce0f122
10 changed files with 248 additions and 279 deletions

View file

@ -248,7 +248,22 @@ RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr,
field_ = cls_.LookupFieldAllowPrivate(function_name2_);
if (!function_.IsNull() && !function_.is_static()) {
// Maybe this was a method extractor.
// TODO(rmacnak)
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();
}
}
}
}
if (field_.IsNull() && is_getter) {

View file

@ -499,7 +499,10 @@ FlowGraph::ToCheck FlowGraph::CheckForInstanceCall(
if (receiver_maybe_null) {
const Class& null_class =
Class::Handle(zone(), isolate()->object_store()->null_class());
if (Resolver::HasDefinition(zone(), null_class, method_name)) {
const Function& target = Function::Handle(
zone(),
Resolver::ResolveDynamicAnyArgs(zone(), null_class, method_name));
if (!target.IsNull()) {
return ToCheck::kCheckCid;
}
}

View file

@ -310,12 +310,11 @@ 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(zone, thread->isolate()->object_store()->null_class());
if (!Resolver::HasDefinition(zone, null_class, function_name)) {
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()) {
// If the selector is not defined on Null, we can propagate non-nullness.
CompileType* type = TypeOf(receiver);
if (type->is_nullable()) {

View file

@ -267,14 +267,7 @@ RawObject* DartEntry::InvokeClosure(const Array& arguments,
}
// No compatible method or getter so invoke noSuchMethod.
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,
return InvokeNoSuchMethod(instance, Symbols::Call(), arguments,
arguments_descriptor);
}

View file

@ -47,7 +47,6 @@ 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();

View file

@ -3111,11 +3111,13 @@ RawFunction* Function::GetDynamicInvocationForwarder(
}
// Check if function actually needs a dynamic invocation forwarder.
if ((kind() == RawFunction::kInvokeFieldDispatcher) ||
!kernel::NeedsDynamicInvocationForwarder(*this)) {
if (!kernel::NeedsDynamicInvocationForwarder(*this)) {
result = raw();
} else if (allow_add) {
result = CreateDynamicInvocationForwarder(mangled_name);
}
if (allow_add) {
owner.AddInvocationDispatcher(mangled_name, Array::null_array(), result);
}
@ -3340,7 +3342,7 @@ RawObject* Class::InvokeGetter(const String& getter_name,
if (field.IsNull() || field.IsUninitialized()) {
const String& internal_getter_name =
String::Handle(zone, Field::GetterSymbol(getter_name));
String::Handle(zone, Field::GetterName(getter_name));
Function& getter =
Function::Handle(zone, LookupStaticFunction(internal_getter_name));
@ -3397,7 +3399,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::SetterSymbol(setter_name));
String::Handle(zone, Field::SetterName(setter_name));
if (!field.IsNull() && check_is_entrypoint) {
CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly));
@ -3457,15 +3459,13 @@ RawObject* Class::InvokeSetter(const String& setter_name,
return value.raw();
}
RawObject* Class::Invoke(const String& function_name_in,
RawObject* Class::Invoke(const String& function_name,
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;
@ -10972,12 +10972,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::GetterSymbol(getter_name));
String::Handle(Field::GetterName(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::GetterSymbol(getter_name));
String::Handle(Field::GetterName(getter_name));
obj = LookupLocalOrReExportObject(internal_getter_name);
if (obj.IsFunction()) {
getter = Function::Cast(obj).raw();
@ -11028,7 +11028,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::SetterSymbol(setter_name));
String::Handle(Field::SetterName(setter_name));
AbstractType& setter_type = AbstractType::Handle();
AbstractType& argument_type = AbstractType::Handle(value.GetType(Heap::kOld));
if (obj.IsField()) {
@ -11086,22 +11086,16 @@ RawObject* Library::InvokeSetter(const String& setter_name,
return DartEntry::InvokeFunction(setter, args);
}
RawObject* Library::Invoke(const String& function_name_in,
RawObject* Library::Invoke(const String& function_name,
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(zone);
Object& obj =
Object::Handle(zone, LookupLocalOrReExportObject(function_name));
Function& function = Function::Handle();
Object& obj = Object::Handle(LookupLocalOrReExportObject(function_name));
if (obj.IsFunction()) {
function ^= obj.raw();
}
@ -11112,39 +11106,37 @@ RawObject* Library::Invoke(const String& function_name_in,
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(
zone, InvokeGetter(function_name, false, respect_reflectable,
check_is_entrypoint));
const Object& getter_result = Object::Handle(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(zone, Array::New(numArgs + 1));
Object& temp = Object::Handle(zone);
const Array& call_args = Array::Handle(Array::New(numArgs + 1));
Object& temp = Object::Handle();
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(zone, ArgumentsDescriptor::New(
kTypeArgsLen, call_args.Length(), arg_names));
Array::Handle(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(
zone, ArgumentsDescriptor::New(kTypeArgsLen, args.Length(), arg_names));
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(zone,
Class::Handle(zone, toplevel_class()).RareType()),
AbstractType::Handle(Class::Handle(toplevel_class()).RareType()),
function_name, args, arg_names, InvocationMirror::kTopLevel,
InvocationMirror::kMethod);
}
@ -13478,14 +13470,20 @@ 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();
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));
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
if (NumberOfChecksIs(0)) {
GrowableArray<intptr_t> class_ids(2);
@ -15902,14 +15900,9 @@ RawObject* Instance::InvokeGetter(const String& getter_name,
}
const String& 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));
String::Handle(zone, Field::GetterName(getter_name));
Function& function = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
if (!function.IsNull() && check_is_entrypoint) {
// The getter must correspond to either an entry-point field or a getter
@ -15927,12 +15920,7 @@ RawObject* Instance::InvokeGetter(const String& getter_name,
// Check for method extraction when method extractors are not created.
if (function.IsNull() && !FLAG_lazy_dispatchers) {
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);
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull() && check_is_entrypoint) {
CHECK_ERROR(function.VerifyClosurizedEntryPoint());
@ -15945,6 +15933,8 @@ 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(
@ -15968,14 +15958,9 @@ RawObject* Instance::InvokeSetter(const String& setter_name,
}
const String& 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));
String::Handle(zone, Field::SetterName(setter_name));
const Function& setter = Function::Handle(
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name));
if (check_is_entrypoint) {
// The setter must correspond to either an entry-point field or a setter
@ -15991,6 +15976,8 @@ 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);
@ -16002,51 +15989,46 @@ RawObject* Instance::InvokeSetter(const String& setter_name,
type_args);
}
RawObject* Instance::Invoke(const String& function_name_in,
RawObject* Instance::Invoke(const String& function_name,
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));
Zone* zone = Thread::Current()->zone();
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::ResolveDynamicForReceiverClass(
klass, function_name, ArgumentsDescriptor(args_descriptor)));
zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
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() && !FLAG_lazy_dispatchers) {
if (function.IsNull()) {
// Didn't find a method: try to find a getter and invoke call on its result.
const String& 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));
String::Handle(zone, Field::GetterName(function_name));
function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
if (!function.IsNull()) {
if (check_is_entrypoint) {
CHECK_ERROR(EntryPointFieldInvocationError(function_name));
}
ASSERT(function.kind() != RawFunction::kMethodExtractor);
// Invoke the getter.
const Array& getter_args =
Array::Handle(zone, Array::New(kGetterNumArgs));
const int kNumArgs = 1;
const Array& getter_args = Array::Handle(zone, Array::New(kNumArgs));
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,
@ -22031,12 +22013,6 @@ 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(

View file

@ -713,15 +713,13 @@ void ICData::Reset(Zone* zone) const {
if (IsImmutable()) {
return;
}
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()));
Zone* zone = Thread::Current()->zone();
const String& name = String::Handle(target_name());
const Class& smi_class = Class::Handle(Smi::Class());
const Function& smi_op_target = Function::Handle(
zone,
Resolver::ResolveDynamicForReceiverClass(smi_class, name, args_desc));
Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
GrowableArray<intptr_t> class_ids(2);
Function& target = Function::Handle(zone);
Function& target = Function::Handle();
GetCheckAt(0, &class_ids, &target);
if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
(class_ids[1] == kSmiCid)) {
@ -729,7 +727,7 @@ void ICData::Reset(Zone* zone) const {
// count.
ClearCountAt(0);
WriteSentinelAt(1);
const Array& array = Array::Handle(zone, entries());
const Array& array = Array::Handle(entries());
array.Truncate(2 * TestEntryLength());
return;
}

View file

@ -16,20 +16,16 @@ namespace dart {
DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
// 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.
// 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.
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);
}
@ -42,54 +38,46 @@ 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,
args_desc, allow_add));
zone,
ResolveDynamicAnyArgs(zone, receiver_class, function_name, allow_add));
if (function.IsNull() || !function.AreValidArguments(args_desc, NULL)) {
if (FLAG_lazy_dispatchers) {
String& demangled = String::Handle(zone);
if (Function::IsDynamicInvocationForwarderName(function_name)) {
demangled =
Function::DemangleDynamicInvocationForwarderName(function_name);
} else {
demangled = function_name.raw();
}
function = receiver_class.GetInvocationDispatcher(
demangled, args_desc.array(), RawFunction::kNoSuchMethodDispatcher,
allow_add);
// 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()) {
function.set_is_reflectable(true);
// Obtain more detailed error message.
function.AreValidArguments(args_desc, &error_message);
}
} else {
function = Function::null();
THR_Print("ResolveDynamic error '%s': %s.\n", function_name.ToCString(),
error_message.ToCString());
}
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,
const ArgumentsDescriptor& args_desc,
bool allow_add) {
RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
const Class& receiver_class,
const String& function_name,
bool allow_add) {
Class& cls = Class::Handle(zone, receiver_class.raw());
String& demangled = String::Handle(zone);
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);
}
if (Function::IsDynamicInvocationForwarderName(function_name)) {
demangled ^=
Function::DemangleDynamicInvocationForwarderName(function_name);
@ -109,118 +97,42 @@ RawFunction* Resolver::ResolveDynamicAnyArgs(
// 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,
args_desc, allow_add);
return !function.IsNull() ? function.raw()
: ResolveDynamicAnyArgs(zone, receiver_class,
demangled, allow_add);
#else
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);
function =
ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
return function.IsNull() ? function.raw()
: 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();
}
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);
// 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);
return function.raw();
} else {
return Function::null();
}
}
}
cls = cls.SuperClass();
}
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;
return function.raw();
}
RawFunction* Resolver::ResolveStatic(const Library& library,

View file

@ -34,16 +34,11 @@ class Resolver : public AllStatic {
const ArgumentsDescriptor& args_desc,
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);
// 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);
// Resolve specified dart static function. If library.IsNull, use
// either application library or core library if no application library
@ -73,15 +68,6 @@ 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

View file

@ -478,7 +478,7 @@ DEFINE_RUNTIME_ENTRY(GetFieldForDispatch, 2) {
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
const String& name = String::CheckedHandle(zone, arguments.ArgAt(1));
const Class& receiver_class = Class::Handle(zone, receiver.clazz());
const String& getter_name = String::Handle(zone, Field::GetterSymbol(name));
const String& getter_name = String::Handle(zone, Field::GetterName(name));
const int kTypeArgsLen = 0;
const int kNumArguments = 1;
ArgumentsDescriptor args_desc(Array::Handle(
@ -997,6 +997,78 @@ 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,
@ -1037,6 +1109,11 @@ 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);
@ -1186,8 +1263,7 @@ static bool IsSingleTarget(Isolate* isolate,
intptr_t lower_cid,
intptr_t upper_cid,
const Function& target,
const String& name,
const ArgumentsDescriptor& args_desc) {
const String& name) {
Class& cls = Class::Handle(zone);
ClassTable* table = isolate->class_table();
Function& other_target = Function::Handle(zone);
@ -1196,8 +1272,8 @@ static bool IsSingleTarget(Isolate* isolate,
cls = table->At(cid);
if (cls.is_abstract()) continue;
if (!cls.is_allocated()) continue;
other_target = Resolver::ResolveDynamicForReceiverClass(
cls, name, args_desc, false /* allow_add */);
other_target =
Resolver::ResolveDynamicAnyArgs(zone, cls, name, false /* allow_add */);
if (other_target.raw() != target.raw()) {
return false;
}
@ -1249,6 +1325,9 @@ 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 {
@ -1270,7 +1349,7 @@ DEFINE_RUNTIME_ENTRY(SingleTargetMiss, 1) {
}
if (IsSingleTarget(isolate, zone, unchecked_lower, unchecked_upper,
target_function, name, args_desc)) {
target_function, name)) {
cache.set_lower_limit(lower);
cache.set_upper_limit(upper);
// Return the ICData. The single target stub will jump to continue in the
@ -1320,6 +1399,9 @@ 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 {
@ -1400,6 +1482,9 @@ 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 {
@ -1416,8 +1501,7 @@ DEFINE_RUNTIME_ENTRY(MonomorphicMiss, 1) {
upper = old_expected_cid.Value();
}
if (IsSingleTarget(isolate, zone, lower, upper, target_function, name,
args_desc)) {
if (IsSingleTarget(isolate, zone, lower, upper, target_function, name)) {
const SingleTargetCache& cache =
SingleTargetCache::Handle(SingleTargetCache::New());
const Code& code = Code::Handle(target_function.CurrentCode());
@ -1478,6 +1562,7 @@ 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);
@ -1569,7 +1654,10 @@ 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.
ASSERT(!target_function.IsNull() && FLAG_lazy_dispatchers);
if (target_function.IsNull()) {
target_function = InlineCacheMissHelper(receiver, arg_desc, target_name);
}
ASSERT(!target_function.IsNull());
arguments.SetReturn(target_function);
#endif
}