f9160e5c0b
Dynamic modules may make dynamic calls to compiled methods that had no dynamic calls in the original whole world compilation. If this happens, then dynamically create an interpreted dynamic invocation forwarder that then appropiately checks and delegates to the compiled function. TEST=co19/LibTest Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try Change-Id: I283ca501f50118606650cf434b8debe30a43f676 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494520 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
192 lines
7.7 KiB
C++
192 lines
7.7 KiB
C++
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/resolver.h"
|
|
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/log.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
|
|
|
|
static FunctionPtr ResolveDynamicAnyArgsWithCustomLookup(
|
|
Zone* zone,
|
|
const Class& receiver_class,
|
|
const String& function_name,
|
|
bool allow_add,
|
|
std::function<FunctionPtr(Class&, const String&)> lookup) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
|
|
// No methods can be added in the precompiled runtime unless dynamic
|
|
// modules are enabled. In this case, calls from dynamic modules may
|
|
// necessitate the creation of (interpreted) forwarders, even for
|
|
// compiled code.
|
|
ASSERT(!allow_add);
|
|
#endif
|
|
|
|
if (FLAG_trace_resolving) {
|
|
THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
|
|
receiver_class.NameCString(Object::kInternalName));
|
|
}
|
|
|
|
const bool is_dyn_call =
|
|
Function::IsDynamicInvocationForwarderName(function_name);
|
|
const String* const demangled_name =
|
|
is_dyn_call
|
|
? &String::Handle(
|
|
zone,
|
|
Function::DemangleDynamicInvocationForwarderName(function_name))
|
|
: &function_name;
|
|
const String& dispatcher_name =
|
|
Function::DropImplicitCallPrefix(function_name);
|
|
|
|
const bool is_getter = Field::IsGetterName(*demangled_name);
|
|
const String* const method_name_to_extract =
|
|
is_getter ? &String::Handle(zone, Field::NameFromGetter(*demangled_name))
|
|
: nullptr;
|
|
|
|
Thread* thread = Thread::Current();
|
|
Function& function = Function::Handle(zone);
|
|
for (auto& cls = Class::Handle(zone, receiver_class.ptr()); !cls.IsNull();
|
|
cls = cls.SuperClass()) {
|
|
if (is_dyn_call) {
|
|
// If a dyn:* forwarder already exists, return it.
|
|
function = cls.GetInvocationDispatcher(
|
|
dispatcher_name, Array::null_array(),
|
|
UntaggedFunction::kDynamicInvocationForwarder,
|
|
/*create_if_absent=*/false);
|
|
if (!function.IsNull()) return function.ptr();
|
|
}
|
|
|
|
ASSERT(cls.is_finalized());
|
|
{
|
|
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
function = lookup(cls, *demangled_name);
|
|
}
|
|
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
|
|
if (allow_add && is_dyn_call && !function.IsNull()) {
|
|
// In JIT mode or if dynamic modules are enabled, lazily create a dyn:*
|
|
// forwarder if one is required.
|
|
function = function.GetDynamicInvocationForwarder(dispatcher_name);
|
|
}
|
|
#endif
|
|
if (!function.IsNull()) return function.ptr();
|
|
|
|
// Getter invocation might be an attempted closurization of a method that
|
|
// does not already have an implicit closure function or method extractor.
|
|
if (is_getter) {
|
|
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
function = lookup(cls, *method_name_to_extract);
|
|
}
|
|
if (!function.IsNull()) {
|
|
// Only create method extractors if adding new methods is allowed.
|
|
if (!allow_add) return Function::null();
|
|
// Don't create method extractors in the precompiler, as it creates those
|
|
// based on metadata (see Precompiler::CheckForNewDynamicFunctions).
|
|
if (FLAG_precompiled_mode) return Function::null();
|
|
// Use GetMethodExtractor in case a method extractor was created between
|
|
// the earlier attempted resolution of [*demangled_name] and now.
|
|
return function.GetMethodExtractor(*demangled_name);
|
|
}
|
|
}
|
|
if (is_getter && receiver_class.IsRecordClass()) {
|
|
// Only create record field getters if adding new methods is allowed.
|
|
if (!allow_add) return Function::null();
|
|
// Don't create record field getters in the precompiler.
|
|
if (FLAG_precompiled_mode) return Function::null();
|
|
return receiver_class.GetRecordFieldGetter(*demangled_name);
|
|
}
|
|
return Function::null();
|
|
}
|
|
|
|
static FunctionPtr ResolveDynamicForReceiverClassWithCustomLookup(
|
|
const Class& receiver_class,
|
|
const String& function_name,
|
|
const ArgumentsDescriptor& args_desc,
|
|
bool allow_add,
|
|
std::function<FunctionPtr(Class&, const String&)> lookup) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
|
|
Function& function = Function::Handle(
|
|
zone, ResolveDynamicAnyArgsWithCustomLookup(
|
|
zone, receiver_class, function_name, allow_add, lookup));
|
|
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
if (!function.IsNull() && function.signature() == FunctionType::null()) {
|
|
// FfiTrampolines are the only functions that can still be called
|
|
// dynamically without going through a dynamic invocation forwarder.
|
|
RELEASE_ASSERT(!Function::IsDynamicInvocationForwarderName(function_name) &&
|
|
!function.IsFfiCallbackTrampoline());
|
|
// The signature for this function was dropped in the precompiler, which
|
|
// means it is not a possible target for a dynamic call in the program.
|
|
// That means we're resolving an UnlinkedCall for an InstanceCall to
|
|
// a known interface. Since there's no overloading in Dart, the type checker
|
|
// has already checked the validity of the arguments at compile time.
|
|
return function.ptr();
|
|
}
|
|
#endif
|
|
|
|
if (function.IsNull() || !function.AreValidArguments(args_desc, nullptr)) {
|
|
// 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);
|
|
}
|
|
THR_Print("ResolveDynamic error '%s': %s.\n", function_name.ToCString(),
|
|
error_message.ToCString());
|
|
}
|
|
return Function::null();
|
|
}
|
|
return function.ptr();
|
|
}
|
|
|
|
FunctionPtr Resolver::ResolveDynamicForReceiverClass(
|
|
const Class& receiver_class,
|
|
const String& function_name,
|
|
const ArgumentsDescriptor& args_desc,
|
|
bool allow_add) {
|
|
return ResolveDynamicForReceiverClassWithCustomLookup(
|
|
receiver_class, function_name, args_desc, allow_add,
|
|
std::mem_fn(&Class::LookupDynamicFunctionUnsafe));
|
|
}
|
|
|
|
FunctionPtr Resolver::ResolveFunction(Zone* zone,
|
|
const Class& receiver_class,
|
|
const String& function_name) {
|
|
return ResolveDynamicAnyArgsWithCustomLookup(
|
|
zone, receiver_class, function_name, /*allow_add=*/false,
|
|
std::mem_fn(static_cast<FunctionPtr (Class::*)(const String&) const>(
|
|
&Class::LookupFunctionReadLocked)));
|
|
}
|
|
|
|
FunctionPtr Resolver::ResolveDynamicFunction(Zone* zone,
|
|
const Class& receiver_class,
|
|
const String& function_name) {
|
|
return ResolveDynamicAnyArgsWithCustomLookup(
|
|
zone, receiver_class, function_name, /*allow_add=*/false,
|
|
std::mem_fn(static_cast<FunctionPtr (Class::*)(const String&) const>(
|
|
&Class::LookupDynamicFunctionUnsafe)));
|
|
}
|
|
|
|
FunctionPtr Resolver::ResolveDynamicAnyArgs(Zone* zone,
|
|
const Class& receiver_class,
|
|
const String& function_name,
|
|
bool allow_add) {
|
|
return ResolveDynamicAnyArgsWithCustomLookup(
|
|
zone, receiver_class, function_name, allow_add,
|
|
std::mem_fn(&Class::LookupDynamicFunctionUnsafe));
|
|
}
|
|
|
|
} // namespace dart
|