ac8d2056a3
The assertions which tried to assert that we only use
StackFrameIterator to walk frames of the current thread was incorrect.
We already have cases where other threads will walk the stack of the
mutator thread, see below for an example where this can happen.
Thread::VisitObjectPointers was incorrectly passing Thread::Current() to
the StackFrameIterator instead of 'this'. (Code in thread_registry.cc will
loop over a number of threads and calls VisitObjectPointers on them)
Mutator thread:
0 pthread_cond_wait@@GLIBC_2.3.2
1 dart::Monitor::WaitMicros
2 dart::Monitor::Wait
3 dart::MonitorLocker::Wait
4 dart::ThreadBarrier::Sync
5 dart::GCMarker::MarkObjects
6 dart::PageSpace::MarkSweep
7 dart::Heap::CollectOldSpaceGarbage
8 dart::Heap::CollectNewSpaceGarbage
9 dart::Heap::CollectGarbage
10 dart::DN_HelperObject_<native>
11 dart::BootstrapNatives::<native>
<dart frames>
MarkTask thread:
1 dart::EntryFrame::VisitObjectPointers
2 dart::Thread::VisitObjectPointers <---- Walks mutator thread stack
3 dart::ThreadRegistry::VisitObjectPointers <---- Iterates over a number of threads
4 dart::Isolate::VisitStackPointers
5 dart::Isolate::VisitObjectPointers
6 dart::GCMarker::IterateRoots
7 dart::MarkTask::Run
8 dart::ThreadPool::Worker::Loop
9 dart::ThreadPool::Worker::Main
10 dart::ThreadStart
R=rmacnak@google.com
Review-Url: https://codereview.chromium.org/2845053003 .
364 lines
14 KiB
C++
364 lines
14 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(DartAsync_fatal, 1) {
|
|
// The dart:async 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:async: %s\n", msg);
|
|
_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()) {
|
|
// 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());
|
|
const bool is_super_call =
|
|
((invocation_type.Value() >> InvocationMirror::kCallShift) &
|
|
InvocationMirror::kCallMask) == InvocationMirror::kSuper;
|
|
if (!is_super_call) {
|
|
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(Heap::kNew);
|
|
}
|
|
|
|
|
|
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(Heap::kNew));
|
|
const AbstractType& right_type =
|
|
AbstractType::Handle(right.GetType(Heap::kNew));
|
|
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 TypeArguments& function_type_arguments =
|
|
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(2));
|
|
const AbstractType& type =
|
|
AbstractType::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, function_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(Heap::kNew));
|
|
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(thread,
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
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_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));
|
|
ASSERT(type.IsFinalized());
|
|
ASSERT(!type.IsMalformed());
|
|
ASSERT(!type.IsMalbounded());
|
|
ASSERT(type.IsInstantiated());
|
|
Error& bound_error = Error::Handle(zone, Error::null());
|
|
const bool is_instance_of =
|
|
instance.IsInstanceOf(type, Object::null_type_arguments(),
|
|
Object::null_type_arguments(), &bound_error);
|
|
if (!is_instance_of && !bound_error.IsNull()) {
|
|
// Throw a dynamic type error only if the instanceof test fails.
|
|
DartFrameIterator iterator(thread,
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
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_as, 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));
|
|
AbstractType& type =
|
|
AbstractType::CheckedHandle(zone, arguments->NativeArgAt(3));
|
|
ASSERT(type.IsFinalized());
|
|
ASSERT(!type.IsMalformed());
|
|
ASSERT(!type.IsMalbounded());
|
|
Error& bound_error = Error::Handle(zone);
|
|
const bool is_instance_of =
|
|
instance.IsNull() ||
|
|
instance.IsInstanceOf(type, instantiator_type_arguments,
|
|
function_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(Heap::kNew));
|
|
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(thread,
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
ASSERT(caller_frame != NULL);
|
|
const TokenPosition location = caller_frame->GetTokenPos();
|
|
const AbstractType& instance_type =
|
|
AbstractType::Handle(zone, instance.GetType(Heap::kNew));
|
|
if (!type.IsInstantiated()) {
|
|
// Instantiate type before reporting the error.
|
|
type = type.InstantiateFrom(instantiator_type_arguments,
|
|
function_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
|