mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:29:48 +00:00
[vm/compiler] Make the compiler use dyn:* selectors when calling getters
We already make the callsites use dyn:* selectors for normal method calls as well as setters, this is doing the same thing for getters. Once callsites use dyn:get:* various pieces in runtime need to be adjusted to accomodate for that (e.g. NSM handling, etc). A follow-up CL will then start actually generating dyn:* getters in certain situations. Issue https://github.com/dart-lang/sdk/issues/40876 Change-Id: If219603bc0b8eb119edd08b211a8897d21ec0fb6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/154320 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com>
This commit is contained in:
parent
a48bebea1e
commit
06183779d7
|
@ -437,10 +437,11 @@ class _TraceReader {
|
|||
callNodesBySelector[targetName]?.connectTo(funNode);
|
||||
callNodesBySelector['$dynPrefix$targetName']?.connectTo(funNode);
|
||||
} else if (name.startsWith(extractorPrefix)) {
|
||||
// Handle method tear-off: [tear-off-extractor] get:foo is hit
|
||||
// by get:foo.
|
||||
callNodesBySelector[name.substring(extractorPrefix.length)]
|
||||
?.connectTo(funNode);
|
||||
// Handle method tear-off: [tear-off-extractor] get:foo can be hit
|
||||
// by dyn:get:foo and get:foo.
|
||||
final targetName = name.substring(extractorPrefix.length);
|
||||
callNodesBySelector[targetName]?.connectTo(funNode);
|
||||
callNodesBySelector['$dynPrefix$targetName']?.connectTo(funNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,8 +89,8 @@ void main() async {
|
|||
expect(retainedClasses, containsAll(['A', 'B', 'K']));
|
||||
expect(retainedFunctions, containsAll(['print', 'tearOff']));
|
||||
|
||||
final getTearOffCall =
|
||||
callGraph.dynamicCalls.firstWhere((n) => n.data == 'get:tornOff');
|
||||
final getTearOffCall = callGraph.dynamicCalls
|
||||
.firstWhere((n) => n.data == 'dyn:get:tornOff');
|
||||
expect(
|
||||
getTearOffCall.dominated.map((n) => n.data.qualifiedName),
|
||||
equals([
|
||||
|
|
|
@ -52,11 +52,11 @@ DEFINE_FLAG(int,
|
|||
static void GetUniqueDynamicTarget(Isolate* isolate,
|
||||
const String& fname,
|
||||
Object* function) {
|
||||
UniqueFunctionsSet functions_set(
|
||||
UniqueFunctionsMap functions_map(
|
||||
isolate->object_store()->unique_dynamic_targets());
|
||||
ASSERT(fname.IsSymbol());
|
||||
*function = functions_set.GetOrNull(fname);
|
||||
ASSERT(functions_set.Release().raw() ==
|
||||
*function = functions_map.GetOrNull(fname);
|
||||
ASSERT(functions_map.Release().raw() ==
|
||||
isolate->object_store()->unique_dynamic_targets());
|
||||
}
|
||||
|
||||
|
|
|
@ -1299,18 +1299,24 @@ void Precompiler::CheckForNewDynamicFunctions() {
|
|||
// hit method extractor get:foo, because it will hit an existing
|
||||
// method foo first.
|
||||
selector2 = Field::NameFromGetter(selector);
|
||||
selector3 = Symbols::Lookup(thread(), selector2);
|
||||
if (IsSent(selector3)) {
|
||||
if (IsSent(selector2)) {
|
||||
AddFunction(function);
|
||||
}
|
||||
selector2 = Function::CreateDynamicInvocationForwarderName(selector2);
|
||||
selector3 = Symbols::Lookup(thread(), selector2);
|
||||
if (IsSent(selector3)) {
|
||||
AddFunction(function);
|
||||
if (IsSent(selector2)) {
|
||||
selector2 =
|
||||
Function::CreateDynamicInvocationForwarderName(selector);
|
||||
function2 = function.GetDynamicInvocationForwarder(selector2);
|
||||
AddFunction(function2);
|
||||
}
|
||||
} else if (function.kind() == FunctionLayout::kRegularFunction) {
|
||||
selector2 = Field::LookupGetterSymbol(selector);
|
||||
if (IsSent(selector2)) {
|
||||
selector3 = String::null();
|
||||
if (!selector2.IsNull()) {
|
||||
selector3 =
|
||||
Function::CreateDynamicInvocationForwarderName(selector2);
|
||||
}
|
||||
if (IsSent(selector2) || IsSent(selector3)) {
|
||||
metadata = kernel::ProcedureAttributesOf(function, Z);
|
||||
found_metadata = true;
|
||||
|
||||
|
@ -1327,18 +1333,31 @@ void Precompiler::CheckForNewDynamicFunctions() {
|
|||
}
|
||||
}
|
||||
|
||||
if (function.kind() == FunctionLayout::kImplicitSetter ||
|
||||
function.kind() == FunctionLayout::kSetterFunction ||
|
||||
function.kind() == FunctionLayout::kRegularFunction) {
|
||||
const bool is_getter =
|
||||
function.kind() == FunctionLayout::kImplicitGetter ||
|
||||
function.kind() == FunctionLayout::kGetterFunction;
|
||||
const bool is_setter =
|
||||
function.kind() == FunctionLayout::kImplicitSetter ||
|
||||
function.kind() == FunctionLayout::kSetterFunction;
|
||||
const bool is_regular =
|
||||
function.kind() == FunctionLayout::kRegularFunction;
|
||||
if (is_getter || is_setter || is_regular) {
|
||||
selector2 = Function::CreateDynamicInvocationForwarderName(selector);
|
||||
if (IsSent(selector2)) {
|
||||
if (function.kind() == FunctionLayout::kImplicitSetter) {
|
||||
if (function.kind() == FunctionLayout::kImplicitGetter ||
|
||||
function.kind() == FunctionLayout::kImplicitSetter) {
|
||||
field = function.accessor_field();
|
||||
metadata = kernel::ProcedureAttributesOf(field, Z);
|
||||
} else if (!found_metadata) {
|
||||
metadata = kernel::ProcedureAttributesOf(function, Z);
|
||||
}
|
||||
|
||||
if (is_getter) {
|
||||
if (metadata.getter_called_dynamically) {
|
||||
function2 = function.GetDynamicInvocationForwarder(selector2);
|
||||
AddFunction(function2);
|
||||
}
|
||||
} else {
|
||||
if (metadata.method_or_setter_called_dynamically) {
|
||||
function2 = function.GetDynamicInvocationForwarder(selector2);
|
||||
AddFunction(function2);
|
||||
|
@ -1349,6 +1368,7 @@ void Precompiler::CheckForNewDynamicFunctions() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NameFunctionsTraits {
|
||||
public:
|
||||
|
@ -1376,16 +1396,38 @@ static void AddNameToFunctionsTable(Zone* zone,
|
|||
table->UpdateValue(fname, farray);
|
||||
}
|
||||
|
||||
static void AddNamesToFunctionsTable(Zone* zone,
|
||||
Table* table,
|
||||
const String& fname,
|
||||
const Function& function,
|
||||
String* mangled_name,
|
||||
Function* dyn_function) {
|
||||
AddNameToFunctionsTable(zone, table, fname, function);
|
||||
|
||||
*dyn_function = function.raw();
|
||||
if (kernel::NeedsDynamicInvocationForwarder(function)) {
|
||||
*mangled_name = function.name();
|
||||
*mangled_name =
|
||||
Function::CreateDynamicInvocationForwarderName(*mangled_name);
|
||||
*dyn_function = function.GetDynamicInvocationForwarder(*mangled_name,
|
||||
/*allow_add=*/true);
|
||||
}
|
||||
*mangled_name = Function::CreateDynamicInvocationForwarderName(fname);
|
||||
AddNameToFunctionsTable(zone, table, *mangled_name, *dyn_function);
|
||||
}
|
||||
|
||||
void Precompiler::CollectDynamicFunctionNames() {
|
||||
if (!FLAG_collect_dynamic_function_names) {
|
||||
return;
|
||||
}
|
||||
Library& lib = Library::Handle(Z);
|
||||
Class& cls = Class::Handle(Z);
|
||||
Array& functions = Array::Handle(Z);
|
||||
Function& function = Function::Handle(Z);
|
||||
String& fname = String::Handle(Z);
|
||||
Array& farray = Array::Handle(Z);
|
||||
auto& lib = Library::Handle(Z);
|
||||
auto& cls = Class::Handle(Z);
|
||||
auto& functions = Array::Handle(Z);
|
||||
auto& function = Function::Handle(Z);
|
||||
auto& fname = String::Handle(Z);
|
||||
auto& farray = Array::Handle(Z);
|
||||
auto& mangled_name = String::Handle(Z);
|
||||
auto& dyn_function = Function::Handle(Z);
|
||||
|
||||
Table table(HashTables::New<Table>(100));
|
||||
for (intptr_t i = 0; i < libraries_.Length(); i++) {
|
||||
|
@ -1394,27 +1436,34 @@ void Precompiler::CollectDynamicFunctionNames() {
|
|||
while (it.HasNext()) {
|
||||
cls = it.GetNextClass();
|
||||
functions = cls.functions();
|
||||
for (intptr_t j = 0; j < functions.Length(); j++) {
|
||||
|
||||
const intptr_t length = functions.Length();
|
||||
for (intptr_t j = 0; j < length; j++) {
|
||||
function ^= functions.At(j);
|
||||
if (function.IsDynamicFunction()) {
|
||||
fname = function.name();
|
||||
if (function.IsSetterFunction() ||
|
||||
function.IsImplicitSetterFunction()) {
|
||||
AddNameToFunctionsTable(zone(), &table, fname, function);
|
||||
AddNamesToFunctionsTable(zone(), &table, fname, function,
|
||||
&mangled_name, &dyn_function);
|
||||
} else if (function.IsGetterFunction() ||
|
||||
function.IsImplicitGetterFunction()) {
|
||||
// Enter both getter and non getter name.
|
||||
AddNameToFunctionsTable(zone(), &table, fname, function);
|
||||
AddNamesToFunctionsTable(zone(), &table, fname, function,
|
||||
&mangled_name, &dyn_function);
|
||||
fname = Field::NameFromGetter(fname);
|
||||
AddNameToFunctionsTable(zone(), &table, fname, function);
|
||||
AddNamesToFunctionsTable(zone(), &table, fname, function,
|
||||
&mangled_name, &dyn_function);
|
||||
} else if (function.IsMethodExtractor()) {
|
||||
// Skip. We already add getter names for regular methods below.
|
||||
continue;
|
||||
} else {
|
||||
// Regular function. Enter both getter and non getter name.
|
||||
AddNameToFunctionsTable(zone(), &table, fname, function);
|
||||
AddNamesToFunctionsTable(zone(), &table, fname, function,
|
||||
&mangled_name, &dyn_function);
|
||||
fname = Field::GetterName(fname);
|
||||
AddNameToFunctionsTable(zone(), &table, fname, function);
|
||||
AddNamesToFunctionsTable(zone(), &table, fname, function,
|
||||
&mangled_name, &dyn_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1424,7 +1473,8 @@ void Precompiler::CollectDynamicFunctionNames() {
|
|||
// Locate all entries with one function only
|
||||
Table::Iterator iter(&table);
|
||||
String& key = String::Handle(Z);
|
||||
UniqueFunctionsSet functions_set(HashTables::New<UniqueFunctionsSet>(20));
|
||||
String& key_demangled = String::Handle(Z);
|
||||
UniqueFunctionsMap functions_map(HashTables::New<UniqueFunctionsMap>(20));
|
||||
while (iter.MoveNext()) {
|
||||
intptr_t curr_key = iter.Current();
|
||||
key ^= table.GetKey(curr_key);
|
||||
|
@ -1432,8 +1482,25 @@ void Precompiler::CollectDynamicFunctionNames() {
|
|||
ASSERT(!farray.IsNull());
|
||||
if (farray.Length() == 1) {
|
||||
function ^= farray.At(0);
|
||||
cls = function.Owner();
|
||||
functions_set.Insert(function);
|
||||
|
||||
// It looks like there is exactly one target for the given name. Though we
|
||||
// have to be careful: e.g. A name like `dyn:get:foo` might have a target
|
||||
// `foo()`. Though the actual target would be a lazily created method
|
||||
// extractor `get:foo` for the `foo` function.
|
||||
//
|
||||
// We'd like to prevent eager creation of functions which we normally
|
||||
// create lazily.
|
||||
// => We disable unique target optimization if the target belongs to the
|
||||
// lazily created functions.
|
||||
key_demangled = key.raw();
|
||||
if (Function::IsDynamicInvocationForwarderName(key)) {
|
||||
key_demangled = Function::DemangleDynamicInvocationForwarderName(key);
|
||||
}
|
||||
if (function.name() != key.raw() &&
|
||||
function.name() != key_demangled.raw()) {
|
||||
continue;
|
||||
}
|
||||
functions_map.UpdateOrInsert(key, function);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1442,18 +1509,18 @@ void Precompiler::CollectDynamicFunctionNames() {
|
|||
get_runtime_type_is_unique_ = !farray.IsNull() && (farray.Length() == 1);
|
||||
|
||||
if (FLAG_print_unique_targets) {
|
||||
UniqueFunctionsSet::Iterator unique_iter(&functions_set);
|
||||
UniqueFunctionsMap::Iterator unique_iter(&functions_map);
|
||||
while (unique_iter.MoveNext()) {
|
||||
intptr_t curr_key = unique_iter.Current();
|
||||
function ^= functions_set.GetKey(curr_key);
|
||||
function ^= functions_map.GetPayload(curr_key, 0);
|
||||
THR_Print("* %s\n", function.ToQualifiedCString());
|
||||
}
|
||||
THR_Print("%" Pd " of %" Pd " dynamic selectors are unique\n",
|
||||
functions_set.NumOccupied(), table.NumOccupied());
|
||||
functions_map.NumOccupied(), table.NumOccupied());
|
||||
}
|
||||
|
||||
isolate()->object_store()->set_unique_dynamic_targets(
|
||||
functions_set.Release());
|
||||
functions_map.Release());
|
||||
table.Release();
|
||||
}
|
||||
|
||||
|
|
|
@ -381,26 +381,12 @@ class FunctionsTraits {
|
|||
static bool ReportStats() { return false; }
|
||||
|
||||
static bool IsMatch(const Object& a, const Object& b) {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
String& a_s = String::Handle(zone);
|
||||
String& b_s = String::Handle(zone);
|
||||
a_s = a.IsFunction() ? Function::Cast(a).name() : String::Cast(a).raw();
|
||||
b_s = b.IsFunction() ? Function::Cast(b).name() : String::Cast(b).raw();
|
||||
ASSERT(a_s.IsSymbol() && b_s.IsSymbol());
|
||||
return a_s.raw() == b_s.raw();
|
||||
return String::Cast(a).raw() == String::Cast(b).raw();
|
||||
}
|
||||
static uword Hash(const Object& obj) {
|
||||
if (obj.IsFunction()) {
|
||||
return String::Handle(Function::Cast(obj).name()).Hash();
|
||||
} else {
|
||||
ASSERT(String::Cast(obj).IsSymbol());
|
||||
return String::Cast(obj).Hash();
|
||||
}
|
||||
}
|
||||
static ObjectPtr NewKey(const Function& function) { return function.raw(); }
|
||||
static uword Hash(const Object& obj) { return String::Cast(obj).Hash(); }
|
||||
};
|
||||
|
||||
typedef UnorderedHashSet<FunctionsTraits> UniqueFunctionsSet;
|
||||
typedef UnorderedHashMap<FunctionsTraits> UniqueFunctionsMap;
|
||||
|
||||
#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
|
||||
// ObfuscationMap maps Strings to Strings.
|
||||
|
|
|
@ -2331,16 +2331,27 @@ Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) {
|
|||
instructions += CheckNull(position, receiver, getter_name);
|
||||
}
|
||||
|
||||
if (!direct_call.target_.IsNull()) {
|
||||
const String* mangled_name = &getter_name;
|
||||
const Function* direct_call_target = &direct_call.target_;
|
||||
if (H.IsRoot(itarget_name)) {
|
||||
mangled_name = &String::ZoneHandle(
|
||||
Z, Function::CreateDynamicInvocationForwarderName(getter_name));
|
||||
if (!direct_call_target->IsNull()) {
|
||||
direct_call_target = &Function::ZoneHandle(
|
||||
direct_call.target_.GetDynamicInvocationForwarder(*mangled_name));
|
||||
}
|
||||
}
|
||||
|
||||
if (!direct_call_target->IsNull()) {
|
||||
ASSERT(CompilerState::Current().is_aot());
|
||||
instructions +=
|
||||
StaticCall(position, direct_call.target_, 1, Array::null_array(),
|
||||
StaticCall(position, *direct_call_target, 1, Array::null_array(),
|
||||
ICData::kNoRebind, &result_type);
|
||||
} else {
|
||||
const intptr_t kTypeArgsLen = 0;
|
||||
const intptr_t kNumArgsChecked = 1;
|
||||
instructions +=
|
||||
InstanceCall(position, getter_name, Token::kGET, kTypeArgsLen, 1,
|
||||
InstanceCall(position, *mangled_name, Token::kGET, kTypeArgsLen, 1,
|
||||
Array::null_array(), kNumArgsChecked, *interface_target,
|
||||
*tearoff_interface_target, &result_type);
|
||||
}
|
||||
|
|
|
@ -255,15 +255,7 @@ void MethodRecognizer::Libraries(GrowableArray<Library*>* libs) {
|
|||
libs->Add(&Library::ZoneHandle(Library::FfiLibrary()));
|
||||
}
|
||||
|
||||
Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name_) {
|
||||
Thread* thread = Thread::Current();
|
||||
REUSABLE_STRING_HANDLESCOPE(thread);
|
||||
String& name = thread->StringHandle();
|
||||
name = name_.raw();
|
||||
ASSERT(name.IsSymbol());
|
||||
if (Function::IsDynamicInvocationForwarderName(name)) {
|
||||
name = Function::DemangleDynamicInvocationForwarderName(name);
|
||||
}
|
||||
static Token::Kind RecognizeTokenKindHelper(const String& name) {
|
||||
if (name.raw() == Symbols::Plus().raw()) {
|
||||
return Token::kADD;
|
||||
} else if (name.raw() == Symbols::Minus().raw()) {
|
||||
|
@ -310,6 +302,18 @@ Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name_) {
|
|||
return Token::kILLEGAL;
|
||||
}
|
||||
|
||||
Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name) {
|
||||
ASSERT(name.IsSymbol());
|
||||
if (Function::IsDynamicInvocationForwarderName(name)) {
|
||||
Thread* thread = Thread::Current();
|
||||
const auto& demangled_name = String::Handle(
|
||||
thread->zone(), Function::DemangleDynamicInvocationForwarderName(name));
|
||||
return RecognizeTokenKindHelper(demangled_name);
|
||||
} else {
|
||||
return RecognizeTokenKindHelper(name);
|
||||
}
|
||||
}
|
||||
|
||||
#define RECOGNIZE_FACTORY(symbol, class_name, constructor_name, cid, fp) \
|
||||
{Symbols::k##symbol##Id, cid, fp, #symbol ", " #cid}, // NOLINT
|
||||
|
||||
|
|
|
@ -723,7 +723,13 @@ bool NeedsDynamicInvocationForwarder(const Function& function) {
|
|||
// dynamic invocation forwarder. So dynamic invocation forwarder is only
|
||||
// needed if there are non-covariant parameters of non-top type.
|
||||
|
||||
ASSERT(!function.IsImplicitGetterFunction());
|
||||
// TODO(dartbug.com/40876): Implement dynamic invocation forwarders for
|
||||
// getters.
|
||||
if (function.IsImplicitGetterFunction() ||
|
||||
function.IsImplicitClosureFunction() || function.IsMethodExtractor()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (function.IsImplicitSetterFunction()) {
|
||||
const auto& field = Field::Handle(zone, function.accessor_field());
|
||||
return !(field.is_covariant() || field.is_generic_covariant_impl());
|
||||
|
|
|
@ -3674,6 +3674,10 @@ StringPtr Function::DemangleDynamicInvocationForwarderName(const String& name) {
|
|||
name.Length() - kDynamicPrefixLength);
|
||||
}
|
||||
|
||||
StringPtr Function::CreateDynamicInvocationForwarderName(const String& name) {
|
||||
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
FunctionPtr Function::CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const {
|
||||
|
@ -3716,10 +3720,6 @@ FunctionPtr Function::CreateDynamicInvocationForwarder(
|
|||
return forwarder.raw();
|
||||
}
|
||||
|
||||
StringPtr Function::CreateDynamicInvocationForwarderName(const String& name) {
|
||||
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
|
||||
}
|
||||
|
||||
FunctionPtr Function::GetDynamicInvocationForwarder(
|
||||
const String& mangled_name,
|
||||
bool allow_add /* = true */) const {
|
||||
|
|
|
@ -3513,9 +3513,9 @@ class Function : public Object {
|
|||
|
||||
static StringPtr DemangleDynamicInvocationForwarderName(const String& name);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
static StringPtr CreateDynamicInvocationForwarderName(const String& name);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
FunctionPtr CreateDynamicInvocationForwarder(
|
||||
const String& mangled_name) const;
|
||||
|
||||
|
|
|
@ -71,61 +71,50 @@ FunctionPtr Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
|||
}
|
||||
Function& function = Function::Handle(zone);
|
||||
|
||||
String& demangled = String::Handle(zone);
|
||||
const String& demangled = String::Handle(
|
||||
zone,
|
||||
Function::IsDynamicInvocationForwarderName(function_name)
|
||||
? Function::DemangleDynamicInvocationForwarderName(function_name)
|
||||
: function_name.raw());
|
||||
|
||||
const bool is_getter = Field::IsGetterName(function_name);
|
||||
const bool is_getter = Field::IsGetterName(demangled);
|
||||
String& demangled_getter_name = String::Handle();
|
||||
if (is_getter) {
|
||||
demangled = Field::NameFromGetter(function_name);
|
||||
demangled_getter_name = Field::NameFromGetter(demangled);
|
||||
}
|
||||
|
||||
if (Function::IsDynamicInvocationForwarderName(function_name)) {
|
||||
demangled = Function::DemangleDynamicInvocationForwarderName(function_name);
|
||||
#ifdef DART_PRECOMPILED_RUNTIME
|
||||
// In precompiled mode, the non-dynamic version of the function may be
|
||||
// tree-shaken away, so can't necessarily resolve the demanged name.
|
||||
const bool is_dyn_call = demangled.raw() != function_name.raw();
|
||||
|
||||
while (!cls.IsNull()) {
|
||||
if (is_dyn_call) {
|
||||
// Try to find a dyn:* forwarder & return it.
|
||||
function = cls.GetInvocationDispatcher(
|
||||
function_name, Array::null_array(),
|
||||
FunctionLayout::kDynamicInvocationForwarder,
|
||||
/*create_if_absent=*/false);
|
||||
if (!function.IsNull()) break;
|
||||
cls = cls.SuperClass();
|
||||
}
|
||||
// Some functions don't require dynamic invocation forwarders, for example
|
||||
// if there are no parameters or all the parameters are marked
|
||||
// `generic-covariant` (meaning there's no work for the dynamic invocation
|
||||
// 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);
|
||||
#else
|
||||
function =
|
||||
ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
|
||||
return function.IsNull() ? function.raw()
|
||||
: function.GetDynamicInvocationForwarder(
|
||||
function_name, allow_add);
|
||||
#endif
|
||||
}
|
||||
if (!function.IsNull()) return function.raw();
|
||||
|
||||
// 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 function.raw();
|
||||
}
|
||||
// Getter invocation might actually be a method extraction.
|
||||
if (is_getter && function.IsNull()) {
|
||||
function = cls.LookupDynamicFunction(demangled);
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// In JIT we might need to lazily create a dyn:* forwarder.
|
||||
if (is_dyn_call && !function.IsNull()) {
|
||||
function =
|
||||
function.GetDynamicInvocationForwarder(function_name, allow_add);
|
||||
}
|
||||
#endif
|
||||
if (!function.IsNull()) return function.raw();
|
||||
|
||||
// Getter invocation might actually be a method extraction.
|
||||
if (is_getter) {
|
||||
function = cls.LookupDynamicFunction(demangled_getter_name);
|
||||
if (!function.IsNull()) {
|
||||
if (allow_add && FLAG_lazy_dispatchers) {
|
||||
// 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);
|
||||
function = function.CreateMethodExtractor(demangled);
|
||||
return function.raw();
|
||||
} else {
|
||||
return Function::null();
|
||||
|
|
|
@ -2180,8 +2180,10 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
|
|||
|
||||
const bool is_dynamic_call =
|
||||
Function::IsDynamicInvocationForwarderName(target_name);
|
||||
String& demangled_target_name = String::Handle(zone, target_name.raw());
|
||||
if (is_dynamic_call) {
|
||||
target_name = Function::DemangleDynamicInvocationForwarderName(target_name);
|
||||
demangled_target_name =
|
||||
Function::DemangleDynamicInvocationForwarderName(target_name);
|
||||
}
|
||||
|
||||
Class& cls = Class::Handle(zone, receiver.clazz());
|
||||
|
@ -2194,8 +2196,9 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
|
|||
|
||||
#define NO_SUCH_METHOD() \
|
||||
const Object& result = Object::Handle( \
|
||||
zone, DartEntry::InvokeNoSuchMethod( \
|
||||
receiver, target_name, orig_arguments, orig_arguments_desc)); \
|
||||
zone, \
|
||||
DartEntry::InvokeNoSuchMethod(receiver, demangled_target_name, \
|
||||
orig_arguments, orig_arguments_desc)); \
|
||||
ThrowIfError(result); \
|
||||
arguments.SetReturn(result);
|
||||
|
||||
|
@ -2206,13 +2209,23 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
|
|||
zone, closure_function.ImplicitInstanceClosure(receiver)); \
|
||||
arguments.SetReturn(result);
|
||||
|
||||
const bool is_getter = Field::IsGetterName(target_name);
|
||||
const bool is_getter = Field::IsGetterName(demangled_target_name);
|
||||
if (is_getter) {
|
||||
// Tear-off of a method
|
||||
// o.foo (o.get:foo) failed, closurize o.foo() if it exists.
|
||||
String& field_name =
|
||||
String::Handle(zone, Field::NameFromGetter(target_name));
|
||||
const auto& function_name =
|
||||
String::Handle(zone, Field::NameFromGetter(demangled_target_name));
|
||||
const auto& dyn_function_name = String::Handle(
|
||||
zone, is_dynamic_call ? Function::CreateDynamicInvocationForwarderName(
|
||||
function_name)
|
||||
: function_name.raw());
|
||||
while (!cls.IsNull()) {
|
||||
function = cls.LookupDynamicFunction(field_name);
|
||||
if (is_dynamic_call) {
|
||||
function = cls.LookupDynamicFunction(dyn_function_name);
|
||||
}
|
||||
if (function.IsNull()) {
|
||||
function = cls.LookupDynamicFunction(function_name);
|
||||
}
|
||||
if (!function.IsNull()) {
|
||||
CLOSURIZE(function);
|
||||
return;
|
||||
|
@ -2222,6 +2235,7 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
|
|||
|
||||
// Fall through for noSuchMethod
|
||||
} else {
|
||||
// Call through field.
|
||||
// o.foo(...) failed, invoke noSuchMethod is foo exists but has the wrong
|
||||
// number of arguments, or try (o.foo).call(...)
|
||||
|
||||
|
@ -2247,16 +2261,38 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromCallStub, 4) {
|
|||
return;
|
||||
}
|
||||
|
||||
const String& getter_name =
|
||||
String::Handle(zone, Field::GetterName(target_name));
|
||||
// Dynamic call sites have to use the dynamic getter as well (if it was
|
||||
// created).
|
||||
const auto& getter_name =
|
||||
String::Handle(zone, Field::GetterName(demangled_target_name));
|
||||
const auto& dyn_getter_name = String::Handle(
|
||||
zone, is_dynamic_call
|
||||
? Function::CreateDynamicInvocationForwarderName(getter_name)
|
||||
: getter_name.raw());
|
||||
ArgumentsDescriptor args_desc(orig_arguments_desc);
|
||||
while (!cls.IsNull()) {
|
||||
// If there is a function with the target name but mismatched arguments
|
||||
// we need to call `receiver.noSuchMethod()`.
|
||||
function = cls.LookupDynamicFunction(target_name);
|
||||
if (!function.IsNull()) {
|
||||
ASSERT(!function.AreValidArguments(args_desc, NULL));
|
||||
break; // mismatch, invoke noSuchMethod
|
||||
}
|
||||
if (is_dynamic_call) {
|
||||
function = cls.LookupDynamicFunction(demangled_target_name);
|
||||
if (!function.IsNull()) {
|
||||
ASSERT(!function.AreValidArguments(args_desc, NULL));
|
||||
break; // mismatch, invoke noSuchMethod
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a getter we need to call-through-getter.
|
||||
if (is_dynamic_call) {
|
||||
function = cls.LookupDynamicFunction(dyn_getter_name);
|
||||
}
|
||||
if (function.IsNull()) {
|
||||
function = cls.LookupDynamicFunction(getter_name);
|
||||
}
|
||||
if (!function.IsNull()) {
|
||||
const Array& getter_arguments = Array::Handle(Array::New(1));
|
||||
getter_arguments.SetAt(0, receiver);
|
||||
|
|
Loading…
Reference in a new issue