Files
sdk/runtime/lib/object.cc
T
Tess Strickland 51649b702d [vm] Allow optimized type testing stubs to be partial.
Previously, the code in the TypeCheck runtime entry assumed that if a
lazily specialized type testing stub (TTS) returned a false negative in
JIT mode, that it should always be regenerated and that regeneration
would always result in different code. In AOT mode, false negatives
instead always cause the stub to go to runtime, even if that false
negative had been seen before, because the assumption is that false
negatives shouldn't happen when the whole class hierarchy is known at
compile time.

However, even in the current implementation of optimized TTSes, there
are cases where this assumption is false. For example, the code
generated by BuildOptimizedSubclassRangeCheckWithTypeArguments allows
for provided type arguments to be type parameters. When this happens,
the type parameter is instantiated at runtime using the instantiator or
function type arguments, and the instantiated type parameter must be
identical to the result retrieved from the type arguments of the
instance. That means that if the instantiated type parameter is not the
same type, but a supertype, of the result, then a false negative is
generated.

This CL changes TypeCheck's handling of false negatives from lazily
specialized TTSes as follows: in JIT, if the regenerated stub is the
same as the old stub, or in AOT, a false negative causes the same fall
back to SubtypeTestCaches as unoptimized stubs.  This way, further
checks with the same false negative will be caught via the STC before
going to runtime, assuming the STC hasn't already filled up with false
negatives.

Currently, we only generate false negatives for reasons that will not
change when respecialization occurs due to additions to the hierarchy,
so we do not need to clear affected STCs during respecialization.
However, the previous approach to resetting STCs on reload (in
CallSiteResetter::ResetCaches) is insufficient, since there may be
caches containing reloaded types in non-reloaded code (like the TTS
invoker stub created by the TTS testing framework). Instead, clear
all caches on reload using the same ObjectVisitor as deoptimizing type
testing stubs.

Since we now have to check instruction equality to determine whether
to add to the STC, we now only replace the existing stub if the
instructions are different. This makes it easier to test whether a
TTS invocation on a false positive caused respecialization or not.

This CL also reworks the testing framework for type testing stubs,
1) creating a test case object that stores the particulars of a given
invocation, including expectations, and 2) moving most checks and
access to appropriate data structures into a state object that handles
setup and performing invocations given test cases.

TEST=vm/cc/TTS_Partial

Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-tsan-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
Change-Id: I139608c5a0f2442a85a1cf39d1c04104db7a5593
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208653
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2021-08-10 13:56:33 +00:00

591 lines
22 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/bootstrap_natives.h"
#include "lib/invocation_mirror.h"
#include "vm/code_patcher.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
#include "vm/heap/heap.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
namespace dart {
DEFINE_NATIVE_ENTRY(DartAsync_fatal, 0, 1) {
// The dart:async library code entered an unrecoverable state.
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
const char* msg = instance.ToCString();
OS::PrintErr("Fatal error in dart:async: %s\n", msg);
FATAL("%s", msg);
return Object::null();
}
DEFINE_NATIVE_ENTRY(Object_equals, 0, 1) {
// Implemented in the flow graph builder.
UNREACHABLE();
return Object::null();
}
static intptr_t GetHash(Isolate* isolate, const ObjectPtr obj) {
#if defined(HASH_IN_OBJECT_HEADER)
return Object::GetCachedHash(obj);
#else
Heap* heap = isolate->group()->heap();
ASSERT(obj->IsDartInstance());
return heap->GetHash(obj);
#endif
}
DEFINE_NATIVE_ENTRY(Object_getHash, 0, 1) {
// Please note that no handle is created for the argument.
// This is safe since the argument is only used in a tail call.
// The performance benefit is more than 5% when using hashCode.
return Smi::New(GetHash(isolate, arguments->NativeArgAt(0)));
}
DEFINE_NATIVE_ENTRY(Object_setHashIfNotSetYet, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
#if defined(HASH_IN_OBJECT_HEADER)
return Smi::New(
Object::SetCachedHashIfNotSet(arguments->NativeArgAt(0), hash.Value()));
#else
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
Heap* heap = thread->heap();
return Smi::New(heap->SetHashIfNotSet(instance.ptr(), hash.Value()));
#endif
}
DEFINE_NATIVE_ENTRY(Object_toString, 0, 1) {
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
if (instance.IsString()) {
return instance.ptr();
}
if (instance.IsAbstractType()) {
return AbstractType::Cast(instance).UserVisibleName();
}
const char* c_str = instance.ToCString();
return String::New(c_str);
}
DEFINE_NATIVE_ENTRY(Object_runtimeType, 0, 1) {
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
if (instance.IsString()) {
return Type::StringType();
} else if (instance.IsInteger()) {
return Type::IntType();
} else if (instance.IsDouble()) {
return Type::Double();
} else if (instance.IsType() || instance.IsFunctionType()) {
return Type::DartTypeType();
}
return instance.GetType(Heap::kNew);
}
static bool HaveSameRuntimeTypeHelper(Zone* zone,
const Instance& left,
const Instance& right) {
const intptr_t left_cid = left.GetClassId();
const intptr_t right_cid = right.GetClassId();
if (left_cid != right_cid) {
if (IsIntegerClassId(left_cid)) {
return IsIntegerClassId(right_cid);
}
if (IsStringClassId(left_cid)) {
return IsStringClassId(right_cid);
}
if (IsTypeClassId(left_cid)) {
return IsTypeClassId(right_cid);
}
return false;
}
if (left_cid == kClosureCid) {
const auto& left_closure = Closure::Cast(left);
const auto& right_closure = Closure::Cast(right);
// If all the components that make up the instantiated signature are equal,
// then no need to instantiate.
if (left_closure.function_type_arguments() ==
right_closure.function_type_arguments() &&
left_closure.delayed_type_arguments() ==
right_closure.delayed_type_arguments() &&
left_closure.instantiator_type_arguments() ==
right_closure.instantiator_type_arguments()) {
const auto& left_fun = Function::Handle(zone, left_closure.function());
const auto& right_fun = Function::Handle(zone, right_closure.function());
if (left_fun.signature() == right_fun.signature()) {
return true;
}
}
const AbstractType& left_type =
AbstractType::Handle(zone, left.GetType(Heap::kNew));
const AbstractType& right_type =
AbstractType::Handle(zone, right.GetType(Heap::kNew));
return left_type.IsEquivalent(right_type, TypeEquality::kSyntactical);
}
const Class& cls = Class::Handle(zone, left.clazz());
if (!cls.IsGeneric()) {
return true;
}
if (left.GetTypeArguments() == right.GetTypeArguments()) {
return true;
}
const TypeArguments& left_type_arguments =
TypeArguments::Handle(zone, left.GetTypeArguments());
const TypeArguments& right_type_arguments =
TypeArguments::Handle(zone, right.GetTypeArguments());
const intptr_t num_type_args = cls.NumTypeArguments();
const intptr_t num_type_params = cls.NumTypeParameters();
return left_type_arguments.IsSubvectorEquivalent(
right_type_arguments, num_type_args - num_type_params, num_type_params,
TypeEquality::kSyntactical);
}
DEFINE_NATIVE_ENTRY(Object_haveSameRuntimeType, 0, 2) {
const Instance& left =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
const Instance& right =
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
return Bool::Get(HaveSameRuntimeTypeHelper(zone, left, right)).ptr();
}
DEFINE_NATIVE_ENTRY(Object_instanceOf, 0, 4) {
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
const TypeArguments& instantiator_type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
const TypeArguments& function_type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(2));
const AbstractType& type =
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(3));
ASSERT(type.IsFinalized());
const bool is_instance_of = instance.IsInstanceOf(
type, instantiator_type_arguments, function_type_arguments);
if (FLAG_trace_type_checks) {
LogBlock lb;
const char* result_str = is_instance_of ? "true" : "false";
THR_Print("Native Object.instanceOf: result %s\n", result_str);
const AbstractType& instance_type =
AbstractType::Handle(zone, instance.GetType(Heap::kNew));
THR_Print(" instance type: %s\n",
String::Handle(zone, instance_type.Name()).ToCString());
THR_Print(" test type: %s\n",
String::Handle(zone, type.Name()).ToCString());
}
return Bool::Get(is_instance_of).ptr();
}
DEFINE_NATIVE_ENTRY(Object_simpleInstanceOf, 0, 2) {
// This native is only called when the right hand side passes
// SimpleInstanceOfType and it is a non-negative test.
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
const AbstractType& type =
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(1));
ASSERT(type.IsFinalized());
ASSERT(type.IsInstantiated());
const bool is_instance_of = instance.IsInstanceOf(
type, Object::null_type_arguments(), Object::null_type_arguments());
return Bool::Get(is_instance_of).ptr();
}
DEFINE_NATIVE_ENTRY(AbstractType_toString, 0, 1) {
const AbstractType& type =
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0));
return type.UserVisibleName();
}
DEFINE_NATIVE_ENTRY(Type_getHashCode, 0, 1) {
const Type& type = Type::CheckedHandle(zone, arguments->NativeArgAt(0));
intptr_t hash_val = type.Hash();
ASSERT(hash_val > 0);
ASSERT(Smi::IsValid(hash_val));
return Smi::New(hash_val);
}
DEFINE_NATIVE_ENTRY(Type_equality, 0, 2) {
const Type& type = Type::CheckedHandle(zone, arguments->NativeArgAt(0));
const Instance& other =
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
if (type.ptr() == other.ptr()) {
return Bool::True().ptr();
}
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr();
}
DEFINE_NATIVE_ENTRY(FunctionType_getHashCode, 0, 1) {
const FunctionType& type =
FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0));
intptr_t hash_val = type.Hash();
ASSERT(hash_val > 0);
ASSERT(Smi::IsValid(hash_val));
return Smi::New(hash_val);
}
DEFINE_NATIVE_ENTRY(FunctionType_equality, 0, 2) {
const FunctionType& type =
FunctionType::CheckedHandle(zone, arguments->NativeArgAt(0));
const Instance& other =
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
if (type.ptr() == other.ptr()) {
return Bool::True().ptr();
}
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).ptr();
}
DEFINE_NATIVE_ENTRY(LibraryPrefix_isLoaded, 0, 1) {
const LibraryPrefix& prefix =
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
return Bool::Get(isolate->IsPrefixLoaded(prefix)).ptr();
}
DEFINE_NATIVE_ENTRY(LibraryPrefix_setLoaded, 0, 1) {
const LibraryPrefix& prefix =
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
isolate->SetPrefixIsLoaded(prefix);
return Instance::null();
}
DEFINE_NATIVE_ENTRY(LibraryPrefix_loadingUnit, 0, 1) {
const LibraryPrefix& prefix =
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
const Library& target = Library::Handle(zone, prefix.GetLibrary(0));
const LoadingUnit& unit = LoadingUnit::Handle(zone, target.loading_unit());
return Smi::New(unit.IsNull() ? LoadingUnit::kIllegalId : unit.id());
}
DEFINE_NATIVE_ENTRY(LibraryPrefix_issueLoad, 0, 1) {
const Smi& id = Smi::CheckedHandle(zone, arguments->NativeArgAt(0));
Array& units =
Array::Handle(zone, isolate->group()->object_store()->loading_units());
if (units.IsNull()) {
// Not actually split.
const Library& lib = Library::Handle(zone, Library::CoreLibrary());
const String& sel = String::Handle(zone, String::New("_completeLoads"));
const Function& func =
Function::Handle(zone, lib.LookupFunctionAllowPrivate(sel));
ASSERT(!func.IsNull());
const Array& args = Array::Handle(zone, Array::New(3));
args.SetAt(0, id);
args.SetAt(1, String::Handle(zone));
args.SetAt(2, Bool::Get(false));
return DartEntry::InvokeFunction(func, args);
}
ASSERT(id.Value() != LoadingUnit::kIllegalId);
LoadingUnit& unit = LoadingUnit::Handle(zone);
unit ^= units.At(id.Value());
return unit.IssueLoad();
}
DEFINE_NATIVE_ENTRY(Internal_has63BitSmis, 0, 0) {
#if defined(ARCH_IS_64_BIT) && !defined(DART_COMPRESSED_POINTERS)
return Bool::True().ptr();
#else
return Bool::False().ptr();
#endif // defined(ARCH_IS_64_BIT)
}
DEFINE_NATIVE_ENTRY(Internal_unsafeCast, 0, 1) {
UNREACHABLE(); // Should be erased at Kernel translation time.
return arguments->NativeArgAt(0);
}
DEFINE_NATIVE_ENTRY(Internal_nativeEffect, 0, 1) {
UNREACHABLE();
}
DEFINE_NATIVE_ENTRY(Internal_reachabilityFence, 0, 1) {
UNREACHABLE();
}
DEFINE_NATIVE_ENTRY(Internal_collectAllGarbage, 0, 0) {
isolate->group()->heap()->CollectAllGarbage();
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_deoptimizeFunctionsOnStack, 0, 0) {
DeoptimizeFunctionsOnStack();
return Object::null();
}
static bool ExtractInterfaceTypeArgs(Zone* zone,
const Class& instance_cls,
const TypeArguments& instance_type_args,
const Class& interface_cls,
TypeArguments* interface_type_args) {
Class& cur_cls = Class::Handle(zone, instance_cls.ptr());
// The following code is a specialization of Class::IsSubtypeOf().
Array& interfaces = Array::Handle(zone);
AbstractType& interface = AbstractType::Handle(zone);
Class& cur_interface_cls = Class::Handle(zone);
TypeArguments& cur_interface_type_args = TypeArguments::Handle(zone);
while (true) {
// Additional subtyping rules related to 'FutureOr' are not applied.
if (cur_cls.ptr() == interface_cls.ptr()) {
*interface_type_args = instance_type_args.ptr();
return true;
}
interfaces = cur_cls.interfaces();
for (intptr_t i = 0; i < interfaces.Length(); i++) {
interface ^= interfaces.At(i);
ASSERT(interface.IsFinalized());
cur_interface_cls = interface.type_class();
cur_interface_type_args = interface.arguments();
if (!cur_interface_type_args.IsNull() &&
!cur_interface_type_args.IsInstantiated()) {
cur_interface_type_args = cur_interface_type_args.InstantiateFrom(
instance_type_args, Object::null_type_arguments(), kNoneFree,
Heap::kNew);
}
if (ExtractInterfaceTypeArgs(zone, cur_interface_cls,
cur_interface_type_args, interface_cls,
interface_type_args)) {
return true;
}
}
cur_cls = cur_cls.SuperClass();
if (cur_cls.IsNull()) {
return false;
}
}
}
// for documentation see pkg/dart_internal/lib/extract_type_arguments.dart
DEFINE_NATIVE_ENTRY(Internal_extractTypeArguments, 0, 2) {
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
const Instance& extract =
Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
Class& interface_cls = Class::Handle(zone);
intptr_t num_type_args = 0;
if (arguments->NativeTypeArgCount() >= 1) {
const AbstractType& function_type_arg =
AbstractType::Handle(zone, arguments->NativeTypeArgAt(0));
if (function_type_arg.IsType() &&
(function_type_arg.arguments() == TypeArguments::null())) {
interface_cls = function_type_arg.type_class();
num_type_args = interface_cls.NumTypeParameters();
}
}
if (num_type_args == 0) {
Exceptions::ThrowArgumentError(String::Handle(
zone,
String::New(
"single function type argument must specify a generic class")));
}
if (instance.IsNull()) {
Exceptions::ThrowArgumentError(instance);
}
// Function 'extract' must be generic and accept the same number of type args,
// unless we execute Dart 1.0 code.
if (extract.IsNull() || !extract.IsClosure() ||
((num_type_args > 0) && // Dart 1.0 if num_type_args == 0.
(Function::Handle(zone, Closure::Cast(extract).function())
.NumTypeParameters() != num_type_args))) {
Exceptions::ThrowArgumentError(String::Handle(
zone,
String::New("argument 'extract' is not a generic function or not one "
"accepting the correct number of type arguments")));
}
TypeArguments& extracted_type_args = TypeArguments::Handle(zone);
if (num_type_args > 0) {
// The passed instance must implement interface_cls.
TypeArguments& interface_type_args = TypeArguments::Handle(zone);
interface_type_args = TypeArguments::New(num_type_args);
Class& instance_cls = Class::Handle(zone, instance.clazz());
TypeArguments& instance_type_args = TypeArguments::Handle(zone);
if (instance_cls.NumTypeArguments() > 0) {
instance_type_args = instance.GetTypeArguments();
}
if (!ExtractInterfaceTypeArgs(zone, instance_cls, instance_type_args,
interface_cls, &interface_type_args)) {
Exceptions::ThrowArgumentError(String::Handle(
zone, String::New("type of argument 'instance' is not a subtype of "
"the function type argument")));
}
if (!interface_type_args.IsNull()) {
extracted_type_args = TypeArguments::New(num_type_args);
const intptr_t offset = interface_cls.NumTypeArguments() - num_type_args;
AbstractType& type_arg = AbstractType::Handle(zone);
for (intptr_t i = 0; i < num_type_args; i++) {
type_arg = interface_type_args.TypeAt(offset + i);
extracted_type_args.SetTypeAt(i, type_arg);
}
extracted_type_args =
extracted_type_args.Canonicalize(thread, nullptr); // Can be null.
}
}
// Call the closure 'extract'.
Array& args_desc = Array::Handle(zone);
Array& args = Array::Handle(zone);
if (extracted_type_args.IsNull()) {
args_desc = ArgumentsDescriptor::NewBoxed(0, 1);
args = Array::New(1);
args.SetAt(0, extract);
} else {
args_desc = ArgumentsDescriptor::NewBoxed(num_type_args, 1);
args = Array::New(2);
args.SetAt(0, extracted_type_args);
args.SetAt(1, extract);
}
const Object& result =
Object::Handle(zone, DartEntry::InvokeClosure(thread, args, args_desc));
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
UNREACHABLE();
}
return result.ptr();
}
DEFINE_NATIVE_ENTRY(Internal_prependTypeArguments, 0, 4) {
const TypeArguments& function_type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));
const TypeArguments& parent_type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_parent_len, arguments->NativeArgAt(2));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_len, arguments->NativeArgAt(3));
return function_type_arguments.Prepend(
zone, parent_type_arguments, smi_parent_len.Value(), smi_len.Value());
}
// Check that a set of type arguments satisfy the type parameter bounds on a
// closure.
// Arg0: Closure object
// Arg1: Type arguments to function
DEFINE_NATIVE_ENTRY(Internal_boundsCheckForPartialInstantiation, 0, 2) {
const Closure& closure =
Closure::CheckedHandle(zone, arguments->NativeArgAt(0));
const Function& target = Function::Handle(zone, closure.function());
ASSERT(target.IsGeneric()); // No need to check bounds for non-generics.
const TypeParameters& type_params =
TypeParameters::Handle(zone, target.type_parameters());
if (type_params.IsNull() || type_params.AllDynamicBounds()) {
// The function is not generic or the bounds are all dynamic.
return Object::null();
}
const TypeArguments& type_args_to_check =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
// This should be guaranteed by the front-end.
ASSERT(type_args_to_check.IsNull() ||
type_params.Length() <= type_args_to_check.Length());
// The bounds on the closure may need instantiation.
const TypeArguments& instantiator_type_args =
TypeArguments::Handle(zone, closure.instantiator_type_arguments());
const TypeArguments& function_type_args =
TypeArguments::Handle(zone, closure.function_type_arguments());
AbstractType& supertype = AbstractType::Handle(zone);
AbstractType& subtype = AbstractType::Handle(zone);
for (intptr_t i = 0; i < type_params.Length(); ++i) {
supertype = type_params.BoundAt(i);
subtype = type_args_to_check.IsNull() ? Object::dynamic_type().ptr()
: type_args_to_check.TypeAt(i);
ASSERT(!subtype.IsNull());
ASSERT(!supertype.IsNull());
// The supertype may not be instantiated.
if (!AbstractType::InstantiateAndTestSubtype(
&subtype, &supertype, instantiator_type_args, function_type_args)) {
// Throw a dynamic type error.
TokenPosition location = TokenPosition::kNoSource;
{
DartFrameIterator iterator(Thread::Current(),
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != NULL);
location = caller_frame->GetTokenPos();
}
const auto& parameter_name = String::Handle(zone, type_params.NameAt(i));
Exceptions::CreateAndThrowTypeError(location, subtype, supertype,
parameter_name);
UNREACHABLE();
}
}
return Object::null();
}
DEFINE_NATIVE_ENTRY(InvocationMirror_unpackTypeArguments, 0, 2) {
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));
const Smi& num_type_arguments =
Smi::CheckedHandle(zone, arguments->NativeArgAt(1));
bool all_dynamic = type_arguments.IsNull();
const intptr_t len =
all_dynamic ? num_type_arguments.Value() : type_arguments.Length();
const Array& type_list = Array::Handle(
zone, Array::New(len, Type::Handle(zone, Type::DartTypeType())));
AbstractType& type = AbstractType::Handle(zone);
for (intptr_t i = 0; i < len; i++) {
if (all_dynamic) {
type_list.SetAt(i, Object::dynamic_type());
} else {
type = type_arguments.TypeAt(i);
type_list.SetAt(i, type);
}
}
type_list.MakeImmutable();
return type_list.ptr();
}
DEFINE_NATIVE_ENTRY(NoSuchMethodError_existingMethodSignature, 0, 3) {
const Instance& receiver =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(String, method_name, arguments->NativeArgAt(1));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, invocation_type, arguments->NativeArgAt(2));
InvocationMirror::Level level;
InvocationMirror::Kind kind;
InvocationMirror::DecodeType(invocation_type.Value(), &level, &kind);
Function& function = Function::Handle(zone);
if (receiver.IsType()) {
const auto& cls = Class::Handle(zone, Type::Cast(receiver).type_class());
const auto& error = Error::Handle(zone, cls.EnsureIsFinalized(thread));
if (!error.IsNull()) {
Exceptions::PropagateError(error);
UNREACHABLE();
}
if (level == InvocationMirror::kConstructor) {
function = cls.LookupConstructor(method_name);
if (function.IsNull()) {
function = cls.LookupFactory(method_name);
}
} else {
function = cls.LookupStaticFunction(method_name);
}
} else if (receiver.IsClosure()) {
function = Closure::Cast(receiver).function();
} else {
auto& cls = Class::Handle(zone, receiver.clazz());
if (level == InvocationMirror::kSuper) {
cls = cls.SuperClass();
}
function = Resolver::ResolveDynamicAnyArgs(zone, cls, method_name,
/*allow_add=*/false);
}
if (!function.IsNull()) {
return function.UserVisibleSignature();
}
return String::null();
}
} // namespace dart