| // Copyright (c) 2013, 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/object_store.h" |
| |
| #include "vm/dart_entry.h" |
| #include "vm/exceptions.h" |
| #include "vm/isolate.h" |
| #include "vm/object.h" |
| #include "vm/raw_object.h" |
| #include "vm/resolver.h" |
| #include "vm/stub_code.h" |
| #include "vm/symbols.h" |
| #include "vm/visitor.h" |
| |
| namespace dart { |
| |
| void IsolateObjectStore::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| ASSERT(visitor != NULL); |
| visitor->set_gc_root_type("isolate_object store"); |
| visitor->VisitPointers(from(), to()); |
| visitor->clear_gc_root_type(); |
| } |
| |
| void IsolateObjectStore::Init() { |
| for (ObjectPtr* current = from(); current <= to(); current++) { |
| *current = Object::null(); |
| } |
| } |
| |
| #ifndef PRODUCT |
| void IsolateObjectStore::PrintToJSONObject(JSONObject* jsobj) { |
| jsobj->AddProperty("type", "_IsolateObjectStore"); |
| |
| { |
| JSONObject fields(jsobj, "fields"); |
| Object& value = Object::Handle(); |
| |
| static const char* const names[] = { |
| #define EMIT_FIELD_NAME(type, name) #name "_", |
| ISOLATE_OBJECT_STORE_FIELD_LIST(EMIT_FIELD_NAME, EMIT_FIELD_NAME) |
| #undef EMIT_FIELD_NAME |
| }; |
| ObjectPtr* current = from(); |
| intptr_t i = 0; |
| while (current <= to()) { |
| value = *current; |
| fields.AddProperty(names[i], value); |
| current++; |
| i++; |
| } |
| ASSERT(i == ARRAY_SIZE(names)); |
| } |
| } |
| #endif // !PRODUCT |
| |
| static StackTracePtr CreatePreallocatedStackTrace(Zone* zone) { |
| const Array& code_array = Array::Handle( |
| zone, Array::New(StackTrace::kPreallocatedStackdepth, Heap::kOld)); |
| const TypedData& pc_offset_array = TypedData::Handle( |
| zone, TypedData::New(kUintPtrCid, StackTrace::kPreallocatedStackdepth, |
| Heap::kOld)); |
| const StackTrace& stack_trace = |
| StackTrace::Handle(zone, StackTrace::New(code_array, pc_offset_array)); |
| // Expansion of inlined functions requires additional memory at run time, |
| // avoid it. |
| stack_trace.set_expand_inlined(false); |
| return stack_trace.ptr(); |
| } |
| |
| ErrorPtr IsolateObjectStore::PreallocateObjects(const Object& out_of_memory) { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| Zone* zone = thread->zone(); |
| ASSERT(isolate != NULL && isolate->isolate_object_store() == this); |
| ASSERT(preallocated_stack_trace() == StackTrace::null()); |
| resume_capabilities_ = GrowableObjectArray::New(); |
| exit_listeners_ = GrowableObjectArray::New(); |
| error_listeners_ = GrowableObjectArray::New(); |
| |
| // Allocate pre-allocated unhandled exception object initialized with the |
| // pre-allocated OutOfMemoryError. |
| const StackTrace& preallocated_stack_trace = |
| StackTrace::Handle(zone, CreatePreallocatedStackTrace(zone)); |
| set_preallocated_stack_trace(preallocated_stack_trace); |
| set_preallocated_unhandled_exception(UnhandledException::Handle( |
| zone, UnhandledException::New(Instance::Cast(out_of_memory), |
| preallocated_stack_trace))); |
| const UnwindError& preallocated_unwind_error = |
| UnwindError::Handle(zone, UnwindError::New(String::Handle( |
| zone, String::New("isolate is exiting")))); |
| set_preallocated_unwind_error(preallocated_unwind_error); |
| |
| return Error::null(); |
| } |
| |
| ObjectStore::ObjectStore() |
| : |
| #define EMIT_FIELD_INIT(type, name) name##_(nullptr), |
| OBJECT_STORE_FIELD_LIST(EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT, |
| EMIT_FIELD_INIT) |
| #undef EMIT_FIELD_INIT |
| // Just to prevent a trailing comma. |
| unused_field_(0) { |
| for (ObjectPtr* current = from(); current <= to(); current++) { |
| *current = Object::null(); |
| } |
| } |
| |
| ObjectStore::~ObjectStore() {} |
| |
| void ObjectStore::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| ASSERT(visitor != NULL); |
| visitor->set_gc_root_type("object store"); |
| visitor->VisitPointers(from(), to()); |
| visitor->clear_gc_root_type(); |
| } |
| |
| void ObjectStore::InitStubs() { |
| #define DO(member, name) set_##member(StubCode::name()); |
| OBJECT_STORE_STUB_CODE_LIST(DO) |
| #undef DO |
| } |
| |
| #ifndef PRODUCT |
| void ObjectStore::PrintToJSONObject(JSONObject* jsobj) { |
| jsobj->AddProperty("type", "_ObjectStore"); |
| |
| { |
| JSONObject fields(jsobj, "fields"); |
| Object& value = Object::Handle(); |
| static const char* const names[] = { |
| #define EMIT_FIELD_NAME(type, name) #name "_", |
| OBJECT_STORE_FIELD_LIST( |
| EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, |
| EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, EMIT_FIELD_NAME, |
| EMIT_FIELD_NAME) |
| #undef EMIT_FIELD_NAME |
| }; |
| ObjectPtr* current = from(); |
| intptr_t i = 0; |
| while (current <= to()) { |
| value = *current; |
| fields.AddProperty(names[i], value); |
| current++; |
| i++; |
| } |
| ASSERT(i == ARRAY_SIZE(names)); |
| } |
| } |
| #endif // !PRODUCT |
| |
| static InstancePtr AllocateObjectByClassName(const Library& library, |
| const String& class_name) { |
| const Class& cls = Class::Handle(library.LookupClassAllowPrivate(class_name)); |
| ASSERT(!cls.IsNull()); |
| return Instance::New(cls); |
| } |
| |
| ErrorPtr ObjectStore::PreallocateObjects() { |
| Thread* thread = Thread::Current(); |
| IsolateGroup* isolate_group = thread->isolate_group(); |
| // Either we are the object store on isolate group, or isolate group has no |
| // object store and we are the object store on the isolate. |
| ASSERT(isolate_group != nullptr && isolate_group->object_store() == this); |
| |
| if (this->stack_overflow() != Instance::null()) { |
| ASSERT(this->out_of_memory() != Instance::null()); |
| return Error::null(); |
| } |
| ASSERT(this->stack_overflow() == Instance::null()); |
| ASSERT(this->out_of_memory() == Instance::null()); |
| |
| this->closure_functions_ = GrowableObjectArray::New(); |
| |
| Object& result = Object::Handle(); |
| const Library& library = Library::Handle(Library::CoreLibrary()); |
| |
| result = AllocateObjectByClassName(library, Symbols::StackOverflowError()); |
| if (result.IsError()) { |
| return Error::Cast(result).ptr(); |
| } |
| set_stack_overflow(Instance::Cast(result)); |
| |
| result = AllocateObjectByClassName(library, Symbols::OutOfMemoryError()); |
| if (result.IsError()) { |
| return Error::Cast(result).ptr(); |
| } |
| set_out_of_memory(Instance::Cast(result)); |
| |
| return Error::null(); |
| } |
| |
| FunctionPtr ObjectStore::PrivateObjectLookup(const String& name) { |
| const Library& core_lib = Library::Handle(core_library()); |
| const String& mangled = String::ZoneHandle(core_lib.PrivateName(name)); |
| const Class& cls = Class::Handle(object_class()); |
| Thread* thread = Thread::Current(); |
| const auto& error = cls.EnsureIsFinalized(thread); |
| ASSERT(error == Error::null()); |
| const Function& result = Function::Handle( |
| Resolver::ResolveDynamicFunction(thread->zone(), cls, mangled)); |
| ASSERT(!result.IsNull()); |
| return result.ptr(); |
| } |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| static void DisableDebuggingAndInlining(const Function& function) { |
| if (FLAG_async_debugger) { |
| function.set_is_debuggable(false); |
| function.set_is_inlinable(false); |
| } |
| } |
| #endif // DART_PRECOMPILED_RUNTIME |
| |
| void ObjectStore::InitKnownObjects() { |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| Class& cls = Class::Handle(zone); |
| const Library& collection_lib = Library::Handle(zone, collection_library()); |
| cls = collection_lib.LookupClassAllowPrivate(Symbols::_LinkedHashSet()); |
| ASSERT(!cls.IsNull()); |
| set_linked_hash_set_class(cls); |
| |
| #ifdef DART_PRECOMPILED_RUNTIME |
| // The rest of these objects are only needed for code generation. |
| return; |
| #else |
| auto isolate_group = thread->isolate_group(); |
| ASSERT(isolate_group != nullptr && isolate_group->object_store() == this); |
| |
| const Library& async_lib = Library::Handle(zone, async_library()); |
| ASSERT(!async_lib.IsNull()); |
| cls = async_lib.LookupClass(Symbols::Future()); |
| ASSERT(!cls.IsNull()); |
| set_future_class(cls); |
| cls = async_lib.LookupClass(Symbols::Completer()); |
| ASSERT(!cls.IsNull()); |
| set_completer_class(cls); |
| |
| String& function_name = String::Handle(zone); |
| Function& function = Function::Handle(zone); |
| |
| function_name = async_lib.PrivateName(Symbols::AsyncStarMoveNextHelper()); |
| ASSERT(!function_name.IsNull()); |
| function = Resolver::ResolveStatic(async_lib, Object::null_string(), |
| function_name, 0, 1, Object::null_array()); |
| ASSERT(!function.IsNull()); |
| set_async_star_move_next_helper(function); |
| |
| function_name = async_lib.PrivateName(Symbols::_CompleteOnAsyncReturn()); |
| ASSERT(!function_name.IsNull()); |
| function = Resolver::ResolveStatic(async_lib, Object::null_string(), |
| function_name, 0, 3, Object::null_array()); |
| ASSERT(!function.IsNull()); |
| set_complete_on_async_return(function); |
| DisableDebuggingAndInlining(function); |
| |
| function_name = |
| async_lib.PrivateName(Symbols::_CompleteWithNoFutureOnAsyncReturn()); |
| ASSERT(!function_name.IsNull()); |
| function = Resolver::ResolveStatic(async_lib, Object::null_string(), |
| function_name, 0, 3, Object::null_array()); |
| ASSERT(!function.IsNull()); |
| set_complete_with_no_future_on_async_return(function); |
| DisableDebuggingAndInlining(function); |
| |
| function_name = async_lib.PrivateName(Symbols::_CompleteOnAsyncError()); |
| ASSERT(!function_name.IsNull()); |
| function = Resolver::ResolveStatic(async_lib, Object::null_string(), |
| function_name, 0, 4, Object::null_array()); |
| ASSERT(!function.IsNull()); |
| set_complete_on_async_error(function); |
| DisableDebuggingAndInlining(function); |
| |
| cls = |
| async_lib.LookupClassAllowPrivate(Symbols::_AsyncStarStreamController()); |
| ASSERT(!cls.IsNull()); |
| set_async_star_stream_controller(cls); |
| |
| if (FLAG_async_debugger) { |
| // Disable debugging and inlining of all functions on the |
| // _AsyncStarStreamController class. |
| const Array& functions = Array::Handle(zone, cls.current_functions()); |
| for (intptr_t i = 0; i < functions.Length(); i++) { |
| function ^= functions.At(i); |
| if (function.IsNull()) { |
| break; |
| } |
| DisableDebuggingAndInlining(function); |
| } |
| } |
| |
| const Library& core_lib = Library::Handle(zone, core_library()); |
| cls = core_lib.LookupClassAllowPrivate(Symbols::_CompileTimeError()); |
| ASSERT(!cls.IsNull()); |
| set_compiletime_error_class(cls); |
| |
| cls = core_lib.LookupClassAllowPrivate(Symbols::Pragma()); |
| ASSERT(!cls.IsNull()); |
| set_pragma_class(cls); |
| RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null()); |
| set_pragma_name(Field::Handle(zone, cls.LookupField(Symbols::name()))); |
| set_pragma_options(Field::Handle(zone, cls.LookupField(Symbols::options()))); |
| |
| cls = core_lib.LookupClassAllowPrivate(Symbols::_GrowableList()); |
| ASSERT(!cls.IsNull()); |
| RELEASE_ASSERT(cls.EnsureIsFinalized(thread) == Error::null()); |
| growable_list_factory_ = |
| cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory()); |
| ASSERT(growable_list_factory_ != Function::null()); |
| |
| cls = core_lib.LookupClassAllowPrivate(Symbols::Error()); |
| ASSERT(!cls.IsNull()); |
| set_error_class(cls); |
| |
| cls = core_lib.LookupClassAllowPrivate(Symbols::Expando()); |
| ASSERT(!cls.IsNull()); |
| set_expando_class(cls); |
| |
| // Cache the core private functions used for fast instance of checks. |
| simple_instance_of_function_ = |
| PrivateObjectLookup(Symbols::_simpleInstanceOf()); |
| simple_instance_of_true_function_ = |
| PrivateObjectLookup(Symbols::_simpleInstanceOfTrue()); |
| simple_instance_of_false_function_ = |
| PrivateObjectLookup(Symbols::_simpleInstanceOfFalse()); |
| |
| // Ensure AddSmiSmiCheckForFastSmiStubs run by the background compiler |
| // will not create new functions. |
| const Class& smi_class = Class::Handle(zone, this->smi_class()); |
| RELEASE_ASSERT(smi_class.EnsureIsFinalized(thread) == Error::null()); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::Plus()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::Minus()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::Equals()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::LAngleBracket()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::RAngleBracket()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::BitAnd()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::BitOr()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| function_name = |
| Function::CreateDynamicInvocationForwarderName(Symbols::Star()); |
| Resolver::ResolveDynamicAnyArgs(zone, smi_class, function_name); |
| #endif |
| } |
| |
| void ObjectStore::LazyInitCoreMembers() { |
| auto* const thread = Thread::Current(); |
| SafepointWriteRwLocker locker(thread, |
| thread->isolate_group()->program_lock()); |
| if (list_class_.load() == Type::null()) { |
| ASSERT(non_nullable_list_rare_type_.load() == Type::null()); |
| ASSERT(non_nullable_map_rare_type_.load() == Type::null()); |
| ASSERT(enum_index_field_.load() == Field::null()); |
| ASSERT(enum_name_field_.load() == Field::null()); |
| ASSERT(_object_equals_function_.load() == Function::null()); |
| ASSERT(_object_hash_code_function_.load() == Function::null()); |
| ASSERT(_object_to_string_function_.load() == Function::null()); |
| |
| auto* const zone = thread->zone(); |
| const auto& core_lib = Library::Handle(zone, Library::CoreLibrary()); |
| auto& cls = Class::Handle(zone); |
| |
| cls = core_lib.LookupClass(Symbols::List()); |
| ASSERT(!cls.IsNull()); |
| list_class_.store(cls.ptr()); |
| |
| auto& type = Type::Handle(zone); |
| type ^= cls.RareType(); |
| non_nullable_list_rare_type_.store(type.ptr()); |
| |
| cls = core_lib.LookupClass(Symbols::Map()); |
| ASSERT(!cls.IsNull()); |
| type ^= cls.RareType(); |
| non_nullable_map_rare_type_.store(type.ptr()); |
| |
| auto& field = Field::Handle(zone); |
| |
| cls = core_lib.LookupClassAllowPrivate(Symbols::_Enum()); |
| ASSERT(!cls.IsNull()); |
| const auto& error = cls.EnsureIsFinalized(thread); |
| ASSERT(error == Error::null()); |
| |
| field = cls.LookupInstanceField(Symbols::Index()); |
| ASSERT(!field.IsNull()); |
| enum_index_field_.store(field.ptr()); |
| |
| field = cls.LookupInstanceFieldAllowPrivate(Symbols::_name()); |
| ASSERT(!field.IsNull()); |
| enum_name_field_.store(field.ptr()); |
| |
| auto& function = Function::Handle(zone); |
| |
| function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectHashCode()); |
| ASSERT(!function.IsNull()); |
| _object_hash_code_function_.store(function.ptr()); |
| |
| function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectEquals()); |
| ASSERT(!function.IsNull()); |
| _object_equals_function_.store(function.ptr()); |
| |
| function = core_lib.LookupFunctionAllowPrivate(Symbols::_objectToString()); |
| ASSERT(!function.IsNull()); |
| _object_to_string_function_.store(function.ptr()); |
| } |
| } |
| |
| void ObjectStore::LazyInitAsyncMembers() { |
| auto* const thread = Thread::Current(); |
| SafepointWriteRwLocker locker(thread, |
| thread->isolate_group()->program_lock()); |
| if (non_nullable_future_rare_type_.load() == Type::null()) { |
| ASSERT(non_nullable_future_never_type_.load() == Type::null()); |
| ASSERT(nullable_future_null_type_.load() == Type::null()); |
| |
| auto* const zone = thread->zone(); |
| const auto& cls = Class::Handle(zone, future_class()); |
| ASSERT(!cls.IsNull()); |
| |
| auto& type_args = TypeArguments::Handle(zone); |
| auto& type = Type::Handle(zone); |
| type = never_type(); |
| ASSERT(!type.IsNull()); |
| type_args = TypeArguments::New(1); |
| type_args.SetTypeAt(0, type); |
| type = Type::New(cls, type_args, Nullability::kNonNullable); |
| type.SetIsFinalized(); |
| type ^= type.Canonicalize(thread, nullptr); |
| non_nullable_future_never_type_.store(type.ptr()); |
| |
| type = null_type(); |
| ASSERT(!type.IsNull()); |
| type_args = TypeArguments::New(1); |
| type_args.SetTypeAt(0, type); |
| type = Type::New(cls, type_args, Nullability::kNullable); |
| type.SetIsFinalized(); |
| type ^= type.Canonicalize(thread, nullptr); |
| nullable_future_null_type_.store(type.ptr()); |
| |
| type ^= cls.RareType(); |
| non_nullable_future_rare_type_.store(type.ptr()); |
| } |
| } |
| |
| void ObjectStore::LazyInitFfiMembers() { |
| auto* const thread = Thread::Current(); |
| SafepointWriteRwLocker locker(thread, |
| thread->isolate_group()->program_lock()); |
| // TODO(http://dartbug.com/47777): Implement finalizers. |
| } |
| |
| void ObjectStore::LazyInitIsolateMembers() { |
| auto* const thread = Thread::Current(); |
| SafepointWriteRwLocker locker(thread, |
| thread->isolate_group()->program_lock()); |
| if (lookup_port_handler_.load() == Type::null()) { |
| ASSERT(lookup_open_ports_.load() == Type::null()); |
| ASSERT(handle_message_function_.load() == Type::null()); |
| |
| auto* const zone = thread->zone(); |
| const auto& isolate_lib = Library::Handle(zone, Library::IsolateLibrary()); |
| auto& cls = Class::Handle(zone); |
| auto& function = Function::Handle(zone); |
| |
| cls = isolate_lib.LookupClassAllowPrivate(Symbols::_RawReceivePortImpl()); |
| ASSERT(!cls.IsNull()); |
| const auto& error = cls.EnsureIsFinalized(thread); |
| ASSERT(error == Error::null()); |
| |
| function = cls.LookupFunctionAllowPrivate(Symbols::_lookupHandler()); |
| ASSERT(!function.IsNull()); |
| lookup_port_handler_.store(function.ptr()); |
| |
| function = cls.LookupFunctionAllowPrivate(Symbols::_lookupOpenPorts()); |
| ASSERT(!function.IsNull()); |
| lookup_open_ports_.store(function.ptr()); |
| |
| function = cls.LookupFunctionAllowPrivate(Symbols::_handleMessage()); |
| ASSERT(!function.IsNull()); |
| handle_message_function_.store(function.ptr()); |
| } |
| } |
| |
| void ObjectStore::LazyInitInternalMembers() { |
| auto* const thread = Thread::Current(); |
| SafepointWriteRwLocker locker(thread, |
| thread->isolate_group()->program_lock()); |
| if (symbol_class_.load() == Type::null()) { |
| ASSERT(symbol_name_field_.load() == Field::null()); |
| |
| auto* const zone = thread->zone(); |
| auto& cls = Class::Handle(zone); |
| auto& field = Field::Handle(zone); |
| |
| const auto& internal_lib = |
| Library::Handle(zone, Library::InternalLibrary()); |
| cls = internal_lib.LookupClass(Symbols::Symbol()); |
| ASSERT(!cls.IsNull()); |
| const auto& error = cls.EnsureIsFinalized(thread); |
| ASSERT(error == Error::null()); |
| symbol_class_.store(cls.ptr()); |
| |
| field = cls.LookupInstanceFieldAllowPrivate(Symbols::_name()); |
| ASSERT(!field.IsNull()); |
| symbol_name_field_.store(field.ptr()); |
| } |
| } |
| |
| } // namespace dart |