dart-sdk/runtime/lib/object.cc
Alexander Markov 3675fd25f4 [vm] Packed representation of record shape
The representation of record shape in record instances and record
types is changed from a pair

  int num_fields
  Array field_names

to a single integer - packed bitfield

  int num_fields(16)
  int field_names_index(kSmiBits-16)

where field names index is an index in the array available from
ObjectStore.

With the new representation of record shapes:
1) Size of record instances is reduced.
2) Number of comparisons for a shape test reduced from 2 to 1
(shape test is used during type checks).
3) A few operations removed from Record.hashCode.
4) Type testing stubs (TTS) are now supported for record types with
named fields. Previously it was not possible to check shape of records
with named fields in TTS as TTS cannot access object pool and cannot
load field names array).

TEST=existing

Issue: https://github.com/dart-lang/sdk/issues/49719

Change-Id: I7cdcbb53938aba5d561cd24dc99530395dbbea7e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/276201
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2022-12-20 20:11:48 +00:00

645 lines
24 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_graph.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.
intptr_t hash = GetHash(isolate, arguments->NativeArgAt(0));
if (LIKELY(hash != 0)) {
return Smi::New(hash);
}
const Instance& instance =
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
return instance.IdentityHashCode(arguments->thread());
}
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.IsAbstractType()) {
return Type::DartTypeType();
} else if (IsArrayClassId(instance.GetClassId())) {
const auto& cls = Class::Handle(
zone, thread->isolate_group()->object_store()->list_class());
const auto& type_arguments =
TypeArguments::Handle(zone, instance.GetTypeArguments());
const auto& type = Type::Handle(
zone,
Type::New(cls, type_arguments, Nullability::kNonNullable, Heap::kNew));
type.SetIsFinalized();
return type.Canonicalize(thread, nullptr);
}
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);
} else if (IsStringClassId(left_cid)) {
return IsStringClassId(right_cid);
} else if (IsTypeClassId(left_cid)) {
return IsTypeClassId(right_cid);
} else if (IsArrayClassId(left_cid)) {
if (!IsArrayClassId(right_cid)) {
return false;
}
// Still need to check type arguments.
} else {
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);
}
if (left_cid == kRecordCid) {
const auto& left_record = Record::Cast(left);
const auto& right_record = Record::Cast(right);
if (left_record.shape() != right_record.shape()) {
return false;
}
Instance& left_field = Instance::Handle(zone);
Instance& right_field = Instance::Handle(zone);
const intptr_t num_fields = left_record.num_fields();
for (intptr_t i = 0; i < num_fields; ++i) {
left_field ^= left_record.FieldAt(i);
right_field ^= right_record.FieldAt(i);
if (!HaveSameRuntimeTypeHelper(zone, left_field, right_field)) {
return false;
}
}
return true;
}
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(AbstractType_getHashCode, 0, 1) {
const AbstractType& type =
AbstractType::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(AbstractType_equality, 0, 2) {
const AbstractType& type =
AbstractType::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(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(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_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_collectAllGarbage, 0, 0) {
isolate->group()->heap()->CollectAllGarbage(GCReason::kDebugging,
/*compact=*/true);
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_writeHeapSnapshotToFile, 0, 1) {
#if defined(DART_ENABLE_HEAP_SNAPSHOT_WRITER)
const String& filename =
String::CheckedHandle(zone, arguments->NativeArgAt(0));
{
FileHeapSnapshotWriter file_writer(thread, filename.ToCString());
HeapSnapshotWriter writer(thread, &file_writer);
writer.Write();
}
#else
Exceptions::ThrowUnsupportedError(
"Heap snapshots are only supported in non-product mode.");
#endif // !defined(PRODUCT)
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_deoptimizeFunctionsOnStack, 0, 0) {
DeoptimizeFunctionsOnStack();
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_randomInstructionsOffsetInsideAllocateObjectStub,
0,
0) {
auto& stub = Code::Handle(
zone, isolate->group()->object_store()->allocate_object_stub());
const uword entry = stub.EntryPoint();
const uword random_offset = isolate->random()->NextUInt32() % stub.Size();
// We return the offset into the isolate instructions instead of the full
// address because that fits into small Smis on 32-bit architectures or
// compressed pointer builds.
const uword instructions_start =
reinterpret_cast<uword>(isolate->source()->snapshot_instructions);
ASSERT(entry >= instructions_start);
return Smi::New((entry - instructions_start) + random_offset);
}
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 (level == InvocationMirror::Level::kTopLevel) {
if (receiver.IsString()) return receiver.ptr();
ASSERT(receiver.IsNull());
return String::null();
}
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