f4ec20abac
Start by removing all get:runtimeType overrides in the patch files to have a single point computing the runtime type - Object.get:runtimeType. Handle string, double and integer types inside both intrinsic and runtime call to unify their handling and guarantee that code works even with intrinsifier disabled. With overrides removed we can easily check that get:runtimeType is unique function name within the application that is being precompiled and use that to convert InstanceCall(get:runtimeType, ...) into StaticCall even nothing is known about the receiver. This enables us to check if both left side and right side of comparison are StaticCall(Object.get:runtimeType, ...) when specializing InstanceCall(==, x, y). If they are we convert InstanceCall(==, StaticCall(get:runtimeType, a), StaticCall(get:runtimeType, b)) into StaticCall(Object._hasSameRuntimeType, a, b). A canonicalization rule will later delete unused get:runtimeType invocations. Object._hasSameRuntimeType is implemented in C++ and intrinsified. It operates without creating new runtime types (except for Closures - where it does for simplicity). Cases of different class ids (i.e. a.[cid] != b.[cid]) and non-parameterized types are handled completely in the intrinsic. The rest is handled in the runtime code. Microbenchmarking results: Same parameterized classes: 15x improvement Different parameterized classes: 300x improvement Different/same non-parameterized classes: 2x improvement BUG= R=fschneider@google.com, regis@google.com Review URL: https://codereview.chromium.org/2379733002 .
415 lines
15 KiB
C++
415 lines
15 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.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(bool, trace_type_checks);
|
|
|
|
// Helper function in stacktrace.cc.
|
|
void _printCurrentStacktrace();
|
|
|
|
DEFINE_NATIVE_ENTRY(DartCore_fatal, 1) {
|
|
// The core library code entered an unrecoverable state.
|
|
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
|
const char* msg = instance.ToCString();
|
|
OS::PrintErr("Fatal error in dart:core\n");
|
|
_printCurrentStacktrace();
|
|
FATAL(msg);
|
|
return Object::null();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_equals, 1) {
|
|
// Implemented in the flow graph builder.
|
|
UNREACHABLE();
|
|
return Object::null();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_getHash, 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.
|
|
Heap* heap = isolate->heap();
|
|
ASSERT(arguments->NativeArgAt(0)->IsDartInstance());
|
|
return Smi::New(heap->GetHash(arguments->NativeArgAt(0)));
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_setHash, 2) {
|
|
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Smi, hash, arguments->NativeArgAt(1));
|
|
Heap* heap = isolate->heap();
|
|
heap->SetHash(instance.raw(), hash.Value());
|
|
return Object::null();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_toString, 1) {
|
|
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
|
if (instance.IsString()) {
|
|
return instance.raw();
|
|
}
|
|
const char* c_str = instance.ToCString();
|
|
return String::New(c_str);
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_noSuchMethod, 6) {
|
|
const Instance& instance = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Bool, is_method, arguments->NativeArgAt(1));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, member_name, arguments->NativeArgAt(2));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Smi, invocation_type, arguments->NativeArgAt(3));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, func_args, arguments->NativeArgAt(4));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(
|
|
Instance, func_named_args, arguments->NativeArgAt(5));
|
|
const Array& dart_arguments = Array::Handle(Array::New(6));
|
|
dart_arguments.SetAt(0, instance);
|
|
dart_arguments.SetAt(1, member_name);
|
|
dart_arguments.SetAt(2, invocation_type);
|
|
dart_arguments.SetAt(3, func_args);
|
|
dart_arguments.SetAt(4, func_named_args);
|
|
|
|
if (is_method.value() &&
|
|
(((invocation_type.Value() >> InvocationMirror::kCallShift) &
|
|
InvocationMirror::kCallMask) != InvocationMirror::kSuper)) {
|
|
// Report if a function with same name (but different arguments) has been
|
|
// found.
|
|
Function& function = Function::Handle();
|
|
if (instance.IsClosure()) {
|
|
function = Closure::Cast(instance).function();
|
|
} else {
|
|
Class& instance_class = Class::Handle(instance.clazz());
|
|
function = instance_class.LookupDynamicFunction(member_name);
|
|
while (function.IsNull()) {
|
|
instance_class = instance_class.SuperClass();
|
|
if (instance_class.IsNull()) break;
|
|
function = instance_class.LookupDynamicFunction(member_name);
|
|
}
|
|
}
|
|
if (!function.IsNull()) {
|
|
const intptr_t total_num_parameters = function.NumParameters();
|
|
const Array& array = Array::Handle(Array::New(total_num_parameters - 1));
|
|
// Skip receiver.
|
|
for (int i = 1; i < total_num_parameters; i++) {
|
|
array.SetAt(i - 1, String::Handle(function.ParameterNameAt(i)));
|
|
}
|
|
dart_arguments.SetAt(5, array);
|
|
}
|
|
}
|
|
Exceptions::ThrowByType(Exceptions::kNoSuchMethod, dart_arguments);
|
|
return Object::null();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_runtimeType, 1) {
|
|
const Instance& instance = Instance::CheckedHandle(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();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_haveSameRuntimeType, 2) {
|
|
const Instance& left = Instance::CheckedHandle(arguments->NativeArgAt(0));
|
|
const Instance& right = Instance::CheckedHandle(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());
|
|
const AbstractType& right_type = AbstractType::Handle(right.GetType());
|
|
return Bool::Get(left_type.raw() == right_type.raw()).raw();
|
|
}
|
|
|
|
if (!cls.IsGeneric()) {
|
|
return Bool::True().raw();
|
|
}
|
|
|
|
const TypeArguments& left_type_arguments =
|
|
TypeArguments::Handle(left.GetTypeArguments());
|
|
const TypeArguments& right_type_arguments =
|
|
TypeArguments::Handle(right.GetTypeArguments());
|
|
return Bool::Get(left_type_arguments.Equals(right_type_arguments)).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOf, 4) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const TypeArguments& instantiator_type_arguments =
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
const AbstractType& type =
|
|
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(2));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(3));
|
|
ASSERT(type.IsFinalized());
|
|
ASSERT(!type.IsMalformed());
|
|
ASSERT(!type.IsMalbounded());
|
|
Error& bound_error = Error::Handle(zone, Error::null());
|
|
const bool is_instance_of = instance.IsInstanceOf(type,
|
|
instantiator_type_arguments,
|
|
&bound_error);
|
|
if (FLAG_trace_type_checks) {
|
|
const char* result_str = is_instance_of ? "true" : "false";
|
|
OS::Print("Native Object.instanceOf: result %s\n", result_str);
|
|
const AbstractType& instance_type =
|
|
AbstractType::Handle(zone, instance.GetType());
|
|
OS::Print(" instance type: %s\n",
|
|
String::Handle(zone, instance_type.Name()).ToCString());
|
|
OS::Print(" test type: %s\n",
|
|
String::Handle(zone, type.Name()).ToCString());
|
|
if (!bound_error.IsNull()) {
|
|
OS::Print(" bound error: %s\n", bound_error.ToErrorCString());
|
|
}
|
|
}
|
|
if (!is_instance_of && !bound_error.IsNull()) {
|
|
// Throw a dynamic type error only if the instanceof test fails.
|
|
DartFrameIterator iterator;
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
ASSERT(caller_frame != NULL);
|
|
const TokenPosition location = caller_frame->GetTokenPos();
|
|
String& bound_error_message = String::Handle(
|
|
zone, String::New(bound_error.ToErrorCString()));
|
|
Exceptions::CreateAndThrowTypeError(
|
|
location, AbstractType::Handle(zone), AbstractType::Handle(zone),
|
|
Symbols::Empty(), bound_error_message);
|
|
UNREACHABLE();
|
|
}
|
|
return Bool::Get(negate.value() ? !is_instance_of : is_instance_of).raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_simpleInstanceOf, 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));
|
|
const TypeArguments& instantiator_type_arguments =
|
|
TypeArguments::Handle(TypeArguments::null());
|
|
ASSERT(type.IsFinalized());
|
|
ASSERT(!type.IsMalformed());
|
|
ASSERT(!type.IsMalbounded());
|
|
Error& bound_error = Error::Handle(zone, Error::null());
|
|
const bool is_instance_of = instance.IsInstanceOf(type,
|
|
instantiator_type_arguments,
|
|
&bound_error);
|
|
if (!is_instance_of && !bound_error.IsNull()) {
|
|
// Throw a dynamic type error only if the instanceof test fails.
|
|
DartFrameIterator iterator;
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
ASSERT(caller_frame != NULL);
|
|
const TokenPosition location = caller_frame->GetTokenPos();
|
|
String& bound_error_message = String::Handle(
|
|
zone, String::New(bound_error.ToErrorCString()));
|
|
Exceptions::CreateAndThrowTypeError(
|
|
location, AbstractType::Handle(zone), AbstractType::Handle(zone),
|
|
Symbols::Empty(), bound_error_message);
|
|
UNREACHABLE();
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOfNum, 2) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
bool is_instance_of = instance.IsNumber();
|
|
if (negate.value()) {
|
|
is_instance_of = !is_instance_of;
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOfInt, 2) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
bool is_instance_of = instance.IsInteger();
|
|
if (negate.value()) {
|
|
is_instance_of = !is_instance_of;
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOfSmi, 2) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
bool is_instance_of = instance.IsSmi();
|
|
if (negate.value()) {
|
|
is_instance_of = !is_instance_of;
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOfDouble, 2) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
bool is_instance_of = instance.IsDouble();
|
|
if (negate.value()) {
|
|
is_instance_of = !is_instance_of;
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_instanceOfString, 2) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const Bool& negate = Bool::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
bool is_instance_of = instance.IsString();
|
|
if (negate.value()) {
|
|
is_instance_of = !is_instance_of;
|
|
}
|
|
return Bool::Get(is_instance_of).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Object_as, 3) {
|
|
const Instance& instance =
|
|
Instance::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
const TypeArguments& instantiator_type_arguments =
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(1));
|
|
AbstractType& type =
|
|
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(2));
|
|
ASSERT(type.IsFinalized());
|
|
ASSERT(!type.IsMalformed());
|
|
ASSERT(!type.IsMalbounded());
|
|
Error& bound_error = Error::Handle(zone);
|
|
if (instance.IsNull()) {
|
|
return instance.raw();
|
|
}
|
|
const bool is_instance_of = instance.IsInstanceOf(type,
|
|
instantiator_type_arguments,
|
|
&bound_error);
|
|
if (FLAG_trace_type_checks) {
|
|
const char* result_str = is_instance_of ? "true" : "false";
|
|
OS::Print("Object.as: result %s\n", result_str);
|
|
const AbstractType& instance_type =
|
|
AbstractType::Handle(zone, instance.GetType());
|
|
OS::Print(" instance type: %s\n",
|
|
String::Handle(zone, instance_type.Name()).ToCString());
|
|
OS::Print(" cast type: %s\n",
|
|
String::Handle(zone, type.Name()).ToCString());
|
|
if (!bound_error.IsNull()) {
|
|
OS::Print(" bound error: %s\n", bound_error.ToErrorCString());
|
|
}
|
|
}
|
|
if (!is_instance_of) {
|
|
DartFrameIterator iterator;
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
ASSERT(caller_frame != NULL);
|
|
const TokenPosition location = caller_frame->GetTokenPos();
|
|
const AbstractType& instance_type =
|
|
AbstractType::Handle(zone, instance.GetType());
|
|
if (!type.IsInstantiated()) {
|
|
// Instantiate type before reporting the error.
|
|
type = type.InstantiateFrom(instantiator_type_arguments, NULL,
|
|
NULL, NULL, Heap::kNew);
|
|
// Note that the instantiated type may be malformed.
|
|
}
|
|
if (bound_error.IsNull()) {
|
|
Exceptions::CreateAndThrowTypeError(
|
|
location, instance_type, type,
|
|
Symbols::InTypeCast(), Object::null_string());
|
|
} else {
|
|
ASSERT(isolate->type_checks());
|
|
const String& bound_error_message =
|
|
String::Handle(zone, String::New(bound_error.ToErrorCString()));
|
|
Exceptions::CreateAndThrowTypeError(
|
|
location, instance_type, AbstractType::Handle(zone),
|
|
Symbols::Empty(), bound_error_message);
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
return instance.raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(AbstractType_toString, 1) {
|
|
const AbstractType& type =
|
|
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
return type.UserVisibleName();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(LibraryPrefix_invalidateDependentCode, 1) {
|
|
const LibraryPrefix& prefix =
|
|
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
prefix.InvalidateDependentCode();
|
|
return Bool::Get(true).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(LibraryPrefix_load, 1) {
|
|
const LibraryPrefix& prefix =
|
|
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
bool hasCompleted = prefix.LoadLibrary();
|
|
return Bool::Get(hasCompleted).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(LibraryPrefix_loadError, 1) {
|
|
const LibraryPrefix& prefix =
|
|
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
// Currently all errors are Dart instances, e.g. I/O errors
|
|
// created by deferred loading code. LanguageErrors from
|
|
// failed loading or finalization attempts are propagated and result
|
|
// in the isolate's death.
|
|
const Instance& error = Instance::Handle(zone, prefix.LoadError());
|
|
return error.raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(LibraryPrefix_isLoaded, 1) {
|
|
const LibraryPrefix& prefix =
|
|
LibraryPrefix::CheckedHandle(zone, arguments->NativeArgAt(0));
|
|
return Bool::Get(prefix.is_loaded()).raw();
|
|
}
|
|
|
|
|
|
DEFINE_NATIVE_ENTRY(Internal_inquireIs64Bit, 0) {
|
|
#if defined(ARCH_IS_64_BIT)
|
|
return Bool::True().raw();
|
|
#else
|
|
return Bool::False().raw();
|
|
#endif // defined(ARCH_IS_64_BIT)
|
|
}
|
|
|
|
} // namespace dart
|