mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:10:22 +00:00
3598d4340d
This also fixes issue #40259. Change-Id: I2e603e927e98270bb24b726444490993e6360f97 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134572 Commit-Queue: Régis Crelier <regis@google.com> Reviewed-by: Liam Appelbe <liama@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
489 lines
18 KiB
C++
489 lines
18 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/exceptions.h"
|
|
#include "vm/heap/heap.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.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(msg);
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_equals, 0, 1) {
|
|
// Implemented in the flow graph builder.
|
|
UNREACHABLE();
|
|
return Object::null();
|
|
}
|
|
|
|
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.
|
|
#if defined(HASH_IN_OBJECT_HEADER)
|
|
return Smi::New(Object::GetCachedHash(arguments->NativeArgAt(0)));
|
|
#else
|
|
Heap* heap = isolate->heap();
|
|
ASSERT(arguments->NativeArgAt(0)->IsDartInstance());
|
|
return Smi::New(heap->GetHash(arguments->NativeArgAt(0)));
|
|
#endif
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_setHash, 0, 2) {
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
|
|
#if defined(HASH_IN_OBJECT_HEADER)
|
|
Object::SetCachedHash(arguments->NativeArgAt(0), hash.Value());
|
|
#else
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
Heap* heap = isolate->heap();
|
|
heap->SetHash(instance.raw(), hash.Value());
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_toString, 0, 1) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
if (instance.IsString()) {
|
|
return instance.raw();
|
|
}
|
|
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();
|
|
}
|
|
return instance.GetType(Heap::kNew);
|
|
}
|
|
|
|
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));
|
|
|
|
const intptr_t left_cid = left.GetClassId();
|
|
const intptr_t right_cid = right.GetClassId();
|
|
|
|
if (left_cid != right_cid) {
|
|
if (RawObject::IsIntegerClassId(left_cid)) {
|
|
return Bool::Get(RawObject::IsIntegerClassId(right_cid)).raw();
|
|
} else if (RawObject::IsStringClassId(right_cid)) {
|
|
return Bool::Get(RawObject::IsStringClassId(right_cid)).raw();
|
|
} else {
|
|
return Bool::False().raw();
|
|
}
|
|
}
|
|
|
|
const Class& cls = Class::Handle(left.clazz());
|
|
if (cls.IsClosureClass()) {
|
|
// TODO(vegorov): provide faster implementation for closure classes.
|
|
const AbstractType& left_type =
|
|
AbstractType::Handle(left.GetType(Heap::kNew));
|
|
const AbstractType& right_type =
|
|
AbstractType::Handle(right.GetType(Heap::kNew));
|
|
return Bool::Get(
|
|
left_type.IsEquivalent(right_type, TypeEquality::kSyntactical))
|
|
.raw();
|
|
}
|
|
|
|
if (!cls.IsGeneric()) {
|
|
return Bool::True().raw();
|
|
}
|
|
|
|
if (left.GetTypeArguments() == right.GetTypeArguments()) {
|
|
return Bool::True().raw();
|
|
}
|
|
const TypeArguments& left_type_arguments =
|
|
TypeArguments::Handle(left.GetTypeArguments());
|
|
const TypeArguments& right_type_arguments =
|
|
TypeArguments::Handle(right.GetTypeArguments());
|
|
const intptr_t num_type_args = cls.NumTypeArguments();
|
|
const intptr_t num_type_params = cls.NumTypeParameters();
|
|
return Bool::Get(left_type_arguments.IsSubvectorEquivalent(
|
|
right_type_arguments, num_type_args - num_type_params,
|
|
num_type_params, TypeEquality::kSyntactical))
|
|
.raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOf, 0, 5) {
|
|
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));
|
|
const NNBDMode nnbd_mode = static_cast<NNBDMode>(
|
|
Smi::CheckedHandle(zone, arguments->NativeArgAt(4)).Value());
|
|
ASSERT(type.IsFinalized());
|
|
const bool is_instance_of = instance.IsInstanceOf(
|
|
nnbd_mode, type, instantiator_type_arguments, function_type_arguments);
|
|
if (FLAG_trace_type_checks) {
|
|
const char* result_str = is_instance_of ? "true" : "false";
|
|
OS::PrintErr("Native Object.instanceOf: result %s\n", result_str);
|
|
const AbstractType& instance_type =
|
|
AbstractType::Handle(zone, instance.GetType(Heap::kNew));
|
|
OS::PrintErr(" instance type: %s\n",
|
|
String::Handle(zone, instance_type.Name()).ToCString());
|
|
OS::PrintErr(" test type: %s\n",
|
|
String::Handle(zone, type.Name()).ToCString());
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
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());
|
|
// If the instance is not null, the result of _simpleInstanceOf does not
|
|
// depend on the nnbd mode, because we only check against a rare type,
|
|
// i.e. a class. However, we need to determine the correct nnbd mode when
|
|
// the instance is null.
|
|
// Since type literals are not imported, a legacy type indicates that the
|
|
// call originated in a legacy library. Note that the type test against a
|
|
// non-legacy type (even in a legacy library) such as dynamic, void, or Null
|
|
// yield the same result independently of the mode used.
|
|
NNBDMode mode =
|
|
type.IsLegacy() ? NNBDMode::kLegacyLib : NNBDMode::kOptedInLib;
|
|
const bool is_instance_of = instance.IsInstanceOf(
|
|
mode, type, Object::null_type_arguments(), Object::null_type_arguments());
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
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.raw() == other.raw()) {
|
|
return Bool::True().raw();
|
|
}
|
|
return Bool::Get(type.IsEquivalent(other, TypeEquality::kSyntactical)).raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Internal_inquireIs64Bit, 0, 0) {
|
|
#if defined(ARCH_IS_64_BIT)
|
|
return Bool::True().raw();
|
|
#else
|
|
return Bool::False().raw();
|
|
#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);
|
|
}
|
|
|
|
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.raw());
|
|
// 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.raw() == interface_cls.raw()) {
|
|
*interface_type_args = instance_type_args.raw();
|
|
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(
|
|
cur_cls.nnbd_mode(), instance_type_args,
|
|
Object::null_type_arguments(), kNoneFree, NULL, 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(); // 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::New(0, 1);
|
|
args = Array::New(1);
|
|
args.SetAt(0, extract);
|
|
} else {
|
|
args_desc = ArgumentsDescriptor::New(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(args, args_desc));
|
|
if (result.IsError()) {
|
|
Exceptions::PropagateError(Error::Cast(result));
|
|
UNREACHABLE();
|
|
}
|
|
return result.raw();
|
|
}
|
|
|
|
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());
|
|
const TypeArguments& bounds =
|
|
TypeArguments::Handle(zone, target.type_parameters());
|
|
|
|
// Either the bounds are all-dynamic or the function is not generic.
|
|
if (bounds.IsNull()) 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() ||
|
|
bounds.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);
|
|
TypeParameter& parameter = TypeParameter::Handle(zone);
|
|
for (intptr_t i = 0; i < bounds.Length(); ++i) {
|
|
parameter ^= bounds.TypeAt(i);
|
|
supertype = parameter.bound();
|
|
subtype = type_args_to_check.IsNull() ? Object::dynamic_type().raw()
|
|
: type_args_to_check.TypeAt(i);
|
|
|
|
ASSERT(!subtype.IsNull());
|
|
ASSERT(!supertype.IsNull());
|
|
|
|
// The supertype may not be instantiated.
|
|
// TODO(regis): What is the correct nnbd mode to use here?
|
|
if (!AbstractType::InstantiateAndTestSubtype(
|
|
NNBDMode::kLegacyLib, &subtype, &supertype, instantiator_type_args,
|
|
function_type_args)) {
|
|
// Throw a dynamic type error.
|
|
TokenPosition location;
|
|
{
|
|
DartFrameIterator iterator(Thread::Current(),
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
ASSERT(caller_frame != NULL);
|
|
location = caller_frame->GetTokenPos();
|
|
}
|
|
String& parameter_name = String::Handle(zone, parameter.Name());
|
|
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.raw();
|
|
}
|
|
|
|
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();
|
|
if (receiver.IsType()) {
|
|
Class& cls = Class::Handle(Type::Cast(receiver).type_class());
|
|
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 {
|
|
Class& cls = Class::Handle(receiver.clazz());
|
|
if (level != InvocationMirror::kSuper) {
|
|
function = cls.LookupDynamicFunction(method_name);
|
|
}
|
|
while (function.IsNull()) {
|
|
cls = cls.SuperClass();
|
|
if (cls.IsNull()) break;
|
|
function = cls.LookupDynamicFunction(method_name);
|
|
}
|
|
}
|
|
if (!function.IsNull()) {
|
|
return function.UserVisibleSignature();
|
|
}
|
|
return String::null();
|
|
}
|
|
|
|
} // namespace dart
|