// Copyright (c) 2012, 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.h" #include "include/dart_api.h" #include "platform/assert.h" #include "vm/bit_vector.h" #include "vm/bootstrap.h" #include "vm/class_finalizer.h" #include "vm/code_observers.h" #include "vm/compiler/aot/precompiler.h" #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/assembler/disassembler.h" #include "vm/compiler/frontend/bytecode_reader.h" #include "vm/compiler/frontend/kernel_fingerprints.h" #include "vm/compiler/frontend/kernel_translation_helper.h" #include "vm/compiler/intrinsifier.h" #include "vm/compiler/jit/compiler.h" #include "vm/compiler_stats.h" #include "vm/cpu.h" #include "vm/dart.h" #include "vm/dart_api_state.h" #include "vm/dart_entry.h" #include "vm/datastream.h" #include "vm/debugger.h" #include "vm/deopt_instructions.h" #include "vm/double_conversion.h" #include "vm/exceptions.h" #include "vm/growable_array.h" #include "vm/hash.h" #include "vm/hash_table.h" #include "vm/heap/become.h" #include "vm/heap/heap.h" #include "vm/heap/weak_code.h" #include "vm/isolate_reload.h" #include "vm/kernel.h" #include "vm/kernel_isolate.h" #include "vm/kernel_loader.h" #include "vm/native_symbol.h" #include "vm/object_store.h" #include "vm/parser.h" #include "vm/profiler.h" #include "vm/resolver.h" #include "vm/reusable_handles.h" #include "vm/runtime_entry.h" #include "vm/scopes.h" #include "vm/stack_frame.h" #include "vm/stub_code.h" #include "vm/symbols.h" #include "vm/tags.h" #include "vm/thread_registry.h" #include "vm/timeline.h" #include "vm/timer.h" #include "vm/type_table.h" #include "vm/type_testing_stubs.h" #include "vm/unicode.h" #include "vm/zone_text_buffer.h" namespace dart { DEFINE_FLAG(int, huge_method_cutoff_in_code_size, 200000, "Huge method cutoff in unoptimized code size (in bytes)."); DEFINE_FLAG( bool, show_internal_names, false, "Show names of internal classes (e.g. \"OneByteString\") in error messages " "instead of showing the corresponding interface names (e.g. \"String\")"); DEFINE_FLAG(bool, use_lib_cache, true, "Use library name cache"); DEFINE_FLAG(bool, use_exp_cache, true, "Use library exported name cache"); DEFINE_FLAG(bool, remove_script_timestamps_for_test, false, "Remove script timestamps to allow for deterministic testing."); DECLARE_FLAG(bool, show_invisible_frames); DECLARE_FLAG(bool, trace_deoptimization); DECLARE_FLAG(bool, trace_deoptimization_verbose); DECLARE_FLAG(bool, trace_reload); DECLARE_FLAG(bool, write_protect_code); static const char* const kGetterPrefix = "get:"; static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix); static const char* const kSetterPrefix = "set:"; static const intptr_t kSetterPrefixLength = strlen(kSetterPrefix); // A cache of VM heap allocated preinitialized empty ic data entry arrays. RawArray* ICData::cached_icdata_arrays_[kCachedICDataArrayCount]; cpp_vtable Object::handle_vtable_ = 0; cpp_vtable Object::builtin_vtables_[kNumPredefinedCids] = {0}; cpp_vtable Smi::handle_vtable_ = 0; // These are initialized to a value that will force a illegal memory access if // they are being used. #if defined(RAW_NULL) #error RAW_NULL should not be defined. #endif #define RAW_NULL kHeapObjectTag #define DEFINE_SHARED_READONLY_HANDLE(Type, name) \ Type* Object::name##_ = nullptr; SHARED_READONLY_HANDLES_LIST(DEFINE_SHARED_READONLY_HANDLE) #undef DEFINE_SHARED_READONLY_HANDLE RawObject* Object::null_ = reinterpret_cast(RAW_NULL); RawClass* Object::class_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::dynamic_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::void_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::unresolved_class_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::type_arguments_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::patch_class_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::function_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::closure_data_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::signature_data_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::redirection_data_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::field_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::literal_token_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::token_stream_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::script_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::library_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::namespace_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::kernel_program_info_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::code_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::instructions_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::object_pool_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::pc_descriptors_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::code_source_map_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::stackmap_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::var_descriptors_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::exception_handlers_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::context_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::context_scope_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::singletargetcache_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::unlinkedcall_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::icdata_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::megamorphic_cache_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::subtypetestcache_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::api_error_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::language_error_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::unhandled_exception_class_ = reinterpret_cast(RAW_NULL); RawClass* Object::unwind_error_class_ = reinterpret_cast(RAW_NULL); const double MegamorphicCache::kLoadFactor = 0.50; static void AppendSubString(Zone* zone, GrowableArray* segments, const char* name, intptr_t start_pos, intptr_t len) { char* segment = zone->Alloc(len + 1); // '\0'-terminated. memmove(segment, name + start_pos, len); segment[len] = '\0'; segments->Add(segment); } static const char* MergeSubStrings(Zone* zone, const GrowableArray& segments, intptr_t alloc_len) { char* result = zone->Alloc(alloc_len + 1); // '\0'-terminated intptr_t pos = 0; for (intptr_t k = 0; k < segments.length(); k++) { const char* piece = segments[k]; const intptr_t piece_len = strlen(segments[k]); memmove(result + pos, piece, piece_len); pos += piece_len; ASSERT(pos <= alloc_len); } result[pos] = '\0'; return result; } // Remove private keys, but retain getter/setter/constructor/mixin manglings. RawString* String::RemovePrivateKey(const String& name) { ASSERT(name.IsOneByteString()); GrowableArray without_key(name.Length()); intptr_t i = 0; while (i < name.Length()) { while (i < name.Length()) { uint8_t c = name.CharAt(i++); if (c == '@') break; without_key.Add(c); } while (i < name.Length()) { uint8_t c = name.CharAt(i); if ((c < '0') || (c > '9')) break; i++; } } return String::FromLatin1(without_key.data(), without_key.length()); } // Takes a vm internal name and makes it suitable for external user. // // Examples: // // Internal getter and setter prefixes are changed: // // get:foo -> foo // set:foo -> foo= // // Private name mangling is removed, possibly multiple times: // // _ReceivePortImpl@709387912 -> _ReceivePortImpl // _ReceivePortImpl@709387912._internal@709387912 -> // _ReceivePortImpl._internal // _C@6328321&_E@6328321&_F@6328321 -> _C&_E&_F // // The trailing . on the default constructor name is dropped: // // List. -> List // // And so forth: // // get:foo@6328321 -> foo // _MyClass@6328321. -> _MyClass // _MyClass@6328321.named -> _MyClass.named // RawString* String::ScrubName(const String& name) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); #if !defined(DART_PRECOMPILED_RUNTIME) if (name.Equals(Symbols::TopLevel())) { // Name of invisible top-level class. return Symbols::Empty().raw(); } #endif // !defined(DART_PRECOMPILED_RUNTIME) const char* cname = name.ToCString(); ASSERT(strlen(cname) == static_cast(name.Length())); const intptr_t name_len = name.Length(); // First remove all private name mangling. intptr_t start_pos = 0; GrowableArray unmangled_segments; intptr_t sum_segment_len = 0; for (intptr_t i = 0; i < name_len; i++) { if ((cname[i] == '@') && ((i + 1) < name_len) && (cname[i + 1] >= '0') && (cname[i + 1] <= '9')) { // Append the current segment to the unmangled name. const intptr_t segment_len = i - start_pos; sum_segment_len += segment_len; AppendSubString(zone, &unmangled_segments, cname, start_pos, segment_len); // Advance until past the name mangling. The private keys are only // numbers so we skip until the first non-number. i++; // Skip the '@'. while ((i < name.Length()) && (name.CharAt(i) >= '0') && (name.CharAt(i) <= '9')) { i++; } start_pos = i; i--; // Account for for-loop increment. } } const char* unmangled_name = NULL; if (start_pos == 0) { // No name unmangling needed, reuse the name that was passed in. unmangled_name = cname; sum_segment_len = name_len; } else if (name.Length() != start_pos) { // Append the last segment. const intptr_t segment_len = name.Length() - start_pos; sum_segment_len += segment_len; AppendSubString(zone, &unmangled_segments, cname, start_pos, segment_len); } if (unmangled_name == NULL) { // Merge unmangled_segments. unmangled_name = MergeSubStrings(zone, unmangled_segments, sum_segment_len); } #if !defined(DART_PRECOMPILED_RUNTIME) intptr_t len = sum_segment_len; intptr_t start = 0; intptr_t dot_pos = -1; // Position of '.' in the name, if any. bool is_setter = false; for (intptr_t i = start; i < len; i++) { if (unmangled_name[i] == ':') { if (start != 0) { // Reset and break. start = 0; dot_pos = -1; break; } ASSERT(start == 0); // Only one : is possible in getters or setters. if (unmangled_name[0] == 's') { is_setter = true; } start = i + 1; } else if (unmangled_name[i] == '.') { if (dot_pos != -1) { // Reset and break. start = 0; dot_pos = -1; break; } ASSERT(dot_pos == -1); // Only one dot is supported. dot_pos = i; } } if ((start == 0) && (dot_pos == -1)) { // This unmangled_name is fine as it is. return Symbols::New(thread, unmangled_name, sum_segment_len); } // Drop the trailing dot if needed. intptr_t end = ((dot_pos + 1) == len) ? dot_pos : len; unmangled_segments.Clear(); intptr_t final_len = end - start; AppendSubString(zone, &unmangled_segments, unmangled_name, start, final_len); if (is_setter) { const char* equals = Symbols::Equals().ToCString(); const intptr_t equals_len = strlen(equals); AppendSubString(zone, &unmangled_segments, equals, 0, equals_len); final_len += equals_len; } unmangled_name = MergeSubStrings(zone, unmangled_segments, final_len); #endif // !defined(DART_PRECOMPILED_RUNTIME) return Symbols::New(thread, unmangled_name); } RawString* String::ScrubNameRetainPrivate(const String& name) { #if !defined(DART_PRECOMPILED_RUNTIME) intptr_t len = name.Length(); intptr_t start = 0; intptr_t at_pos = -1; // Position of '@' in the name, if any. bool is_setter = false; for (intptr_t i = start; i < len; i++) { if (name.CharAt(i) == ':') { ASSERT(start == 0); // Only one : is possible in getters or setters. if (name.CharAt(0) == 's') { is_setter = true; } start = i + 1; } else if (name.CharAt(i) == '@') { // Setters should have only one @ so we know where to put the =. ASSERT(!is_setter || (at_pos == -1)); at_pos = i; } } if (start == 0) { // This unmangled_name is fine as it is. return name.raw(); } String& result = String::Handle(String::SubString(name, start, (len - start))); if (is_setter) { // Setters need to end with '='. if (at_pos == -1) { return String::Concat(result, Symbols::Equals()); } else { const String& pre_at = String::Handle(String::SubString(result, 0, at_pos - 4)); const String& post_at = String::Handle(String::SubString(name, at_pos, len - at_pos)); result = String::Concat(pre_at, Symbols::Equals()); result = String::Concat(result, post_at); } } return result.raw(); #endif // !defined(DART_PRECOMPILED_RUNTIME) return name.raw(); // In AOT, return argument unchanged. } template static bool IsSpecialCharacter(type value) { return ((value == '"') || (value == '\n') || (value == '\f') || (value == '\b') || (value == '\t') || (value == '\v') || (value == '\r') || (value == '\\') || (value == '$')); } static inline bool IsAsciiNonprintable(int32_t c) { return ((0 <= c) && (c < 32)) || (c == 127); } static inline bool NeedsEscapeSequence(int32_t c) { return (c == '"') || (c == '\\') || (c == '$') || IsAsciiNonprintable(c); } static int32_t EscapeOverhead(int32_t c) { if (IsSpecialCharacter(c)) { return 1; // 1 additional byte for the backslash. } else if (IsAsciiNonprintable(c)) { return 3; // 3 additional bytes to encode c as \x00. } return 0; } template static type SpecialCharacter(type value) { if (value == '"') { return '"'; } else if (value == '\n') { return 'n'; } else if (value == '\f') { return 'f'; } else if (value == '\b') { return 'b'; } else if (value == '\t') { return 't'; } else if (value == '\v') { return 'v'; } else if (value == '\r') { return 'r'; } else if (value == '\\') { return '\\'; } else if (value == '$') { return '$'; } UNREACHABLE(); return '\0'; } void Object::InitNull(Isolate* isolate) { // Should only be run by the vm isolate. ASSERT(isolate == Dart::vm_isolate()); // TODO(iposva): NoSafepointScope needs to be added here. ASSERT(class_class() == null_); Heap* heap = isolate->heap(); // Allocate and initialize the null instance. // 'null_' must be the first object allocated as it is used in allocation to // clear the object. { uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld); null_ = reinterpret_cast(address + kHeapObjectTag); // The call below is using 'null_' to initialize itself. InitializeObject(address, kNullCid, Instance::InstanceSize(), true); } } void Object::InitOnce(Isolate* isolate) { // Should only be run by the vm isolate. ASSERT(isolate == Dart::vm_isolate()); // Initialize the static vtable values. { Object fake_object; Smi fake_smi; Object::handle_vtable_ = fake_object.vtable(); Smi::handle_vtable_ = fake_smi.vtable(); } Heap* heap = isolate->heap(); // Allocate the read only object handles here. #define INITIALIZE_SHARED_READONLY_HANDLE(Type, name) \ name##_ = Type::ReadOnlyHandle(); SHARED_READONLY_HANDLES_LIST(INITIALIZE_SHARED_READONLY_HANDLE) #undef INITIALIZE_SHARED_READONLY_HANDLE *null_object_ = Object::null(); *null_array_ = Array::null(); *null_string_ = String::null(); *null_instance_ = Instance::null(); *null_function_ = Function::null(); *null_type_arguments_ = TypeArguments::null(); *empty_type_arguments_ = TypeArguments::null(); *null_abstract_type_ = AbstractType::null(); // Initialize the empty and zero array handles to null_ in order to be able to // check if the empty and zero arrays were allocated (RAW_NULL is not // available). *empty_array_ = Array::null(); *zero_array_ = Array::null(); Class& cls = Class::Handle(); // Allocate and initialize the class class. { intptr_t size = Class::InstanceSize(); uword address = heap->Allocate(size, Heap::kOld); class_class_ = reinterpret_cast(address + kHeapObjectTag); InitializeObject(address, Class::kClassId, size, true); Class fake; // Initialization from Class::New. // Directly set raw_ to break a circular dependency: SetRaw will attempt // to lookup class class in the class table where it is not registered yet. cls.raw_ = class_class_; cls.set_handle_vtable(fake.vtable()); cls.set_instance_size(Class::InstanceSize()); cls.set_next_field_offset(Class::NextFieldOffset()); cls.set_id(Class::kClassId); cls.set_state_bits(0); cls.set_is_finalized(); cls.set_is_type_finalized(); cls.set_is_cycle_free(); cls.set_type_arguments_field_offset_in_words(Class::kNoTypeArguments); cls.set_num_type_arguments(0); cls.set_num_own_type_arguments(0); cls.set_has_pragma(false); cls.set_num_native_fields(0); cls.InitEmptyFields(); isolate->RegisterClass(cls); } // Allocate and initialize the null class. cls = Class::New(kNullCid); cls.set_num_type_arguments(0); cls.set_num_own_type_arguments(0); isolate->object_store()->set_null_class(cls); // Allocate and initialize the free list element class. cls = Class::New(kFreeListElement); cls.set_num_type_arguments(0); cls.set_num_own_type_arguments(0); cls.set_is_finalized(); cls.set_is_type_finalized(); cls.set_is_cycle_free(); // Allocate and initialize the forwarding corpse class. cls = Class::New(kForwardingCorpse); cls.set_num_type_arguments(0); cls.set_num_own_type_arguments(0); cls.set_is_finalized(); cls.set_is_type_finalized(); cls.set_is_cycle_free(); // Allocate and initialize the sentinel values of Null class. { *sentinel_ ^= Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld); *transition_sentinel_ ^= Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld); } // Allocate and initialize optimizing compiler constants. { *unknown_constant_ ^= Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld); *non_constant_ ^= Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld); } // Allocate the remaining VM internal classes. cls = Class::New(); unresolved_class_class_ = cls.raw(); cls = Class::New(); type_arguments_class_ = cls.raw(); cls = Class::New(); patch_class_class_ = cls.raw(); cls = Class::New(); function_class_ = cls.raw(); cls = Class::New(); closure_data_class_ = cls.raw(); cls = Class::New(); signature_data_class_ = cls.raw(); cls = Class::New(); redirection_data_class_ = cls.raw(); cls = Class::New(); field_class_ = cls.raw(); cls = Class::New(); literal_token_class_ = cls.raw(); cls = Class::New(); token_stream_class_ = cls.raw(); cls = Class::New