| // 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/assembler.h" |
| #include "vm/cpu.h" |
| #include "vm/bigint_operations.h" |
| #include "vm/bit_vector.h" |
| #include "vm/bootstrap.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/code_generator.h" |
| #include "vm/code_observers.h" |
| #include "vm/code_patcher.h" |
| #include "vm/compiler.h" |
| #include "vm/compiler_stats.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/disassembler.h" |
| #include "vm/double_conversion.h" |
| #include "vm/exceptions.h" |
| #include "vm/flow_graph_builder.h" |
| #include "vm/flow_graph_compiler.h" |
| #include "vm/growable_array.h" |
| #include "vm/hash_table.h" |
| #include "vm/heap.h" |
| #include "vm/intermediate_language.h" |
| #include "vm/intrinsifier.h" |
| #include "vm/object_id_ring.h" |
| #include "vm/object_store.h" |
| #include "vm/parser.h" |
| #include "vm/report.h" |
| #include "vm/reusable_handles.h" |
| #include "vm/runtime_entry.h" |
| #include "vm/scopes.h" |
| #include "vm/stack_frame.h" |
| #include "vm/symbols.h" |
| #include "vm/tags.h" |
| #include "vm/timer.h" |
| #include "vm/unicode.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(int, huge_method_cutoff_in_code_size, 200000, |
| "Huge method cutoff in unoptimized code size (in bytes)."); |
| DEFINE_FLAG(int, huge_method_cutoff_in_tokens, 20000, |
| "Huge method cutoff in tokens: Disables optimizations for huge methods."); |
| DEFINE_FLAG(bool, overlap_type_arguments, true, |
| "When possible, partially or fully overlap the type arguments of a type " |
| "with the type arguments of its super type."); |
| 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, trace_disabling_optimized_code, false, |
| "Trace disabling optimized code."); |
| DEFINE_FLAG(bool, throw_on_javascript_int_overflow, false, |
| "Throw an exception when the result of an integer calculation will not " |
| "fit into a javascript integer."); |
| DEFINE_FLAG(bool, use_field_guards, true, "Guard field cids."); |
| DEFINE_FLAG(bool, use_lib_cache, true, "Use library name cache"); |
| DEFINE_FLAG(bool, trace_field_guards, false, "Trace changes in field's cids."); |
| |
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, error_on_bad_override); |
| DECLARE_FLAG(bool, trace_compiler); |
| DECLARE_FLAG(bool, trace_deoptimization); |
| DECLARE_FLAG(bool, trace_deoptimization_verbose); |
| DECLARE_FLAG(bool, verbose_stacktrace); |
| DECLARE_FLAG(charp, coverage_dir); |
| DECLARE_FLAG(bool, write_protect_code); |
| |
| static const char* kGetterPrefix = "get:"; |
| static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix); |
| static const char* kSetterPrefix = "set:"; |
| static const intptr_t kSetterPrefixLength = strlen(kSetterPrefix); |
| |
| 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 |
| Object* Object::null_object_ = NULL; |
| Array* Object::null_array_ = NULL; |
| String* Object::null_string_ = NULL; |
| Instance* Object::null_instance_ = NULL; |
| TypeArguments* Object::null_type_arguments_ = NULL; |
| Array* Object::empty_array_ = NULL; |
| Array* Object::zero_array_ = NULL; |
| PcDescriptors* Object::empty_descriptors_ = NULL; |
| Instance* Object::sentinel_ = NULL; |
| Instance* Object::transition_sentinel_ = NULL; |
| Instance* Object::unknown_constant_ = NULL; |
| Instance* Object::non_constant_ = NULL; |
| Bool* Object::bool_true_ = NULL; |
| Bool* Object::bool_false_ = NULL; |
| Smi* Object::smi_illegal_cid_ = NULL; |
| LanguageError* Object::snapshot_writer_error_ = NULL; |
| LanguageError* Object::branch_offset_error_ = NULL; |
| |
| RawObject* Object::null_ = reinterpret_cast<RawObject*>(RAW_NULL); |
| RawClass* Object::class_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::dynamic_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::void_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawType* Object::dynamic_type_ = reinterpret_cast<RawType*>(RAW_NULL); |
| RawType* Object::void_type_ = reinterpret_cast<RawType*>(RAW_NULL); |
| RawClass* Object::unresolved_class_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::type_arguments_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::patch_class_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::function_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::closure_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::redirection_data_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::field_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::literal_token_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::token_stream_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::script_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::library_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::namespace_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::code_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::instructions_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::pc_descriptors_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::stackmap_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::var_descriptors_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::exception_handlers_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::deopt_info_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::context_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::icdata_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::megamorphic_cache_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::subtypetestcache_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::api_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::language_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::unhandled_exception_class_ = |
| reinterpret_cast<RawClass*>(RAW_NULL); |
| RawClass* Object::unwind_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
| |
| |
| const double MegamorphicCache::kLoadFactor = 0.75; |
| |
| |
| // The following functions are marked as invisible, meaning they will be hidden |
| // in the stack trace and will be hidden from reflective access. |
| // (Library, class name, method name) |
| // Additionally, private functions in dart:* that are native or constructors are |
| // marked as invisible by the parser. |
| #define INVISIBLE_LIST(V) \ |
| V(CoreLibrary, int, _throwFormatException) \ |
| V(CoreLibrary, int, _parse) \ |
| |
| static void MarkFunctionAsInvisible(const Library& lib, |
| const char* class_name, |
| const char* function_name) { |
| ASSERT(!lib.IsNull()); |
| const Class& cls = Class::Handle( |
| lib.LookupClassAllowPrivate(String::Handle(String::New(class_name)))); |
| ASSERT(!cls.IsNull()); |
| const Function& function = |
| Function::Handle( |
| cls.LookupFunctionAllowPrivate( |
| String::Handle(String::New(function_name)))); |
| ASSERT(!function.IsNull()); |
| function.set_is_visible(false); |
| } |
| |
| |
| static void MarkInvisibleFunctions() { |
| #define MARK_FUNCTION(lib, class_name, function_name) \ |
| MarkFunctionAsInvisible(Library::Handle(Library::lib()), \ |
| #class_name, #function_name); \ |
| |
| INVISIBLE_LIST(MARK_FUNCTION) |
| #undef MARK_FUNCTION |
| } |
| |
| |
| // 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@6be832b -> _ReceivePortImpl |
| // _ReceivePortImpl@6be832b._internal@6be832b -> _ReceivePortImpl._internal |
| // _C@0x2b4ab9cc&_E@0x2b4ab9cc&_F@0x2b4ab9cc -> _C&_E&_F |
| // |
| // The trailing . on the default constructor name is dropped: |
| // |
| // List. -> List |
| // |
| // And so forth: |
| // |
| // get:foo@6be832b -> foo |
| // _MyClass@6b3832b. -> _MyClass |
| // _MyClass@6b3832b.named -> _MyClass.named |
| // |
| RawString* String::IdentifierPrettyName(const String& name) { |
| if (name.Equals(Symbols::TopLevel())) { |
| // Name of invisible top-level class. |
| return Symbols::Empty().raw(); |
| } |
| |
| // First remove all private name mangling. |
| String& unmangled_name = String::Handle(Symbols::Empty().raw()); |
| String& segment = String::Handle(); |
| intptr_t start_pos = 0; |
| for (intptr_t i = 0; i < name.Length(); i++) { |
| if (name.CharAt(i) == '@' && |
| (i+1) < name.Length() && |
| (name.CharAt(i+1) >= '0') && |
| (name.CharAt(i+1) <= '9')) { |
| // Append the current segment to the unmangled name. |
| segment = String::SubString(name, start_pos, (i - start_pos)); |
| unmangled_name = String::Concat(unmangled_name, segment); |
| |
| // 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. |
| } |
| } |
| if (start_pos == 0) { |
| // No name unmangling needed, reuse the name that was passed in. |
| unmangled_name = name.raw(); |
| } else if (name.Length() != start_pos) { |
| // Append the last segment. |
| segment = String::SubString(name, start_pos, (name.Length() - start_pos)); |
| unmangled_name = String::Concat(unmangled_name, segment); |
| } |
| |
| intptr_t len = unmangled_name.Length(); |
| 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.CharAt(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.CharAt(0) == 's') { |
| is_setter = true; |
| } |
| start = i + 1; |
| } else if (unmangled_name.CharAt(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 unmangled_name.raw(); |
| } |
| |
| // Drop the trailing dot if needed. |
| intptr_t end = ((dot_pos + 1) == len) ? dot_pos : len; |
| |
| const String& result = |
| String::Handle(String::SubString(unmangled_name, start, (end - start))); |
| |
| if (is_setter) { |
| // Setters need to end with '='. |
| return String::Concat(result, Symbols::Equals()); |
| } |
| |
| return result.raw(); |
| } |
| |
| |
| RawString* String::IdentifierPrettyNameRetainPrivate(const String& name) { |
| 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(); |
| } |
| |
| |
| template<typename type> |
| static bool IsSpecialCharacter(type value) { |
| return ((value == '"') || |
| (value == '\n') || |
| (value == '\f') || |
| (value == '\b') || |
| (value == '\t') || |
| (value == '\v') || |
| (value == '\r') || |
| (value == '\\') || |
| (value == '$')); |
| } |
| |
| |
| static bool IsAsciiPrintChar(int32_t code_point) { |
| return (code_point >= ' ') && (code_point <= '~'); |
| } |
| |
| |
| 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<typename type> |
| 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::InitOnce() { |
| // TODO(iposva): NoGCScope needs to be added here. |
| ASSERT(class_class() == null_); |
| // Initialize the static vtable values. |
| { |
| Object fake_object; |
| Smi fake_smi; |
| Object::handle_vtable_ = fake_object.vtable(); |
| Smi::handle_vtable_ = fake_smi.vtable(); |
| } |
| |
| Isolate* isolate = Isolate::Current(); |
| Heap* heap = isolate->heap(); |
| |
| // Allocate the read only object handles here. |
| null_object_ = Object::ReadOnlyHandle(); |
| null_array_ = Array::ReadOnlyHandle(); |
| null_string_ = String::ReadOnlyHandle(); |
| null_instance_ = Instance::ReadOnlyHandle(); |
| null_type_arguments_ = TypeArguments::ReadOnlyHandle(); |
| empty_array_ = Array::ReadOnlyHandle(); |
| zero_array_ = Array::ReadOnlyHandle(); |
| empty_descriptors_ = PcDescriptors::ReadOnlyHandle(); |
| sentinel_ = Instance::ReadOnlyHandle(); |
| transition_sentinel_ = Instance::ReadOnlyHandle(); |
| unknown_constant_ = Instance::ReadOnlyHandle(); |
| non_constant_ = Instance::ReadOnlyHandle(); |
| bool_true_ = Bool::ReadOnlyHandle(); |
| bool_false_ = Bool::ReadOnlyHandle(); |
| smi_illegal_cid_ = Smi::ReadOnlyHandle(); |
| snapshot_writer_error_ = LanguageError::ReadOnlyHandle(); |
| branch_offset_error_ = LanguageError::ReadOnlyHandle(); |
| |
| |
| // 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<RawInstance*>(address + kHeapObjectTag); |
| // The call below is using 'null_' to initialize itself. |
| InitializeObject(address, kNullCid, Instance::InstanceSize()); |
| } |
| |
| *null_object_ = Object::null(); |
| *null_array_ = Array::null(); |
| *null_string_ = String::null(); |
| *null_instance_ = Instance::null(); |
| *null_type_arguments_ = TypeArguments::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<RawClass*>(address + kHeapObjectTag); |
| InitializeObject(address, Class::kClassId, size); |
| |
| Class fake; |
| // Initialization from Class::New<Class>. |
| // 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_type_arguments_field_offset_in_words(Class::kNoTypeArguments); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_num_native_fields(0); |
| cls.InitEmptyFields(); |
| isolate->RegisterClass(cls); |
| } |
| |
| // Allocate and initialize the null class. |
| cls = Class::New<Instance>(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<FreeListElement::FakeInstance>(kFreeListElement); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_finalized(); |
| cls.set_is_type_finalized(); |
| |
| // 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<UnresolvedClass>(); |
| unresolved_class_class_ = cls.raw(); |
| |
| cls = Class::New<TypeArguments>(); |
| type_arguments_class_ = cls.raw(); |
| |
| cls = Class::New<PatchClass>(); |
| patch_class_class_ = cls.raw(); |
| |
| cls = Class::New<Function>(); |
| function_class_ = cls.raw(); |
| |
| cls = Class::New<ClosureData>(); |
| closure_data_class_ = cls.raw(); |
| |
| cls = Class::New<RedirectionData>(); |
| redirection_data_class_ = cls.raw(); |
| |
| cls = Class::New<Field>(); |
| field_class_ = cls.raw(); |
| |
| cls = Class::New<LiteralToken>(); |
| literal_token_class_ = cls.raw(); |
| |
| cls = Class::New<TokenStream>(); |
| token_stream_class_ = cls.raw(); |
| |
| cls = Class::New<Script>(); |
| script_class_ = cls.raw(); |
| |
| cls = Class::New<Library>(); |
| library_class_ = cls.raw(); |
| |
| cls = Class::New<Namespace>(); |
| namespace_class_ = cls.raw(); |
| |
| cls = Class::New<Code>(); |
| code_class_ = cls.raw(); |
| |
| cls = Class::New<Instructions>(); |
| instructions_class_ = cls.raw(); |
| |
| cls = Class::New<PcDescriptors>(); |
| pc_descriptors_class_ = cls.raw(); |
| |
| cls = Class::New<Stackmap>(); |
| stackmap_class_ = cls.raw(); |
| |
| cls = Class::New<LocalVarDescriptors>(); |
| var_descriptors_class_ = cls.raw(); |
| |
| cls = Class::New<ExceptionHandlers>(); |
| exception_handlers_class_ = cls.raw(); |
| |
| cls = Class::New<DeoptInfo>(); |
| deopt_info_class_ = cls.raw(); |
| |
| cls = Class::New<Context>(); |
| context_class_ = cls.raw(); |
| |
| cls = Class::New<ContextScope>(); |
| context_scope_class_ = cls.raw(); |
| |
| cls = Class::New<ICData>(); |
| icdata_class_ = cls.raw(); |
| |
| cls = Class::New<MegamorphicCache>(); |
| megamorphic_cache_class_ = cls.raw(); |
| |
| cls = Class::New<SubtypeTestCache>(); |
| subtypetestcache_class_ = cls.raw(); |
| |
| cls = Class::New<ApiError>(); |
| api_error_class_ = cls.raw(); |
| |
| cls = Class::New<LanguageError>(); |
| language_error_class_ = cls.raw(); |
| |
| cls = Class::New<UnhandledException>(); |
| unhandled_exception_class_ = cls.raw(); |
| |
| cls = Class::New<UnwindError>(); |
| unwind_error_class_ = cls.raw(); |
| |
| ASSERT(class_class() != null_); |
| |
| // Pre-allocate classes in the vm isolate so that we can for example create a |
| // symbol table and populate it with some frequently used strings as symbols. |
| cls = Class::New<Array>(); |
| isolate->object_store()->set_array_class(cls); |
| cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| cls.set_num_type_arguments(1); |
| cls.set_num_own_type_arguments(1); |
| cls = Class::New<Array>(kImmutableArrayCid); |
| isolate->object_store()->set_immutable_array_class(cls); |
| cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| cls.set_num_type_arguments(1); |
| cls.set_num_own_type_arguments(1); |
| cls = Class::NewStringClass(kOneByteStringCid); |
| isolate->object_store()->set_one_byte_string_class(cls); |
| cls = Class::NewStringClass(kTwoByteStringCid); |
| isolate->object_store()->set_two_byte_string_class(cls); |
| |
| // Allocate and initialize the empty_array instance. |
| { |
| uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld); |
| InitializeObject(address, kArrayCid, Array::InstanceSize(0)); |
| Array::initializeHandle( |
| empty_array_, |
| reinterpret_cast<RawArray*>(address + kHeapObjectTag)); |
| empty_array_->raw_ptr()->length_ = Smi::New(0); |
| } |
| |
| // Allocate and initialize the zero_array instance. |
| { |
| uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld); |
| InitializeObject(address, kArrayCid, Array::InstanceSize(1)); |
| Array::initializeHandle( |
| zero_array_, |
| reinterpret_cast<RawArray*>(address + kHeapObjectTag)); |
| zero_array_->raw_ptr()->length_ = Smi::New(1); |
| zero_array_->raw_ptr()->data()[0] = Smi::New(0); |
| } |
| |
| // Allocate and initialize the empty_descriptors instance. |
| { |
| uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld); |
| InitializeObject(address, kPcDescriptorsCid, |
| PcDescriptors::InstanceSize(0)); |
| PcDescriptors::initializeHandle( |
| empty_descriptors_, |
| reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag)); |
| empty_descriptors_->raw_ptr()->length_ = 0; |
| } |
| |
| |
| cls = Class::New<Instance>(kDynamicCid); |
| cls.set_is_abstract(); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_type_finalized(); |
| cls.set_is_finalized(); |
| dynamic_class_ = cls.raw(); |
| |
| cls = Class::New<Instance>(kVoidCid); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_type_finalized(); |
| cls.set_is_finalized(); |
| void_class_ = cls.raw(); |
| |
| cls = Class::New<Type>(); |
| cls.set_is_type_finalized(); |
| cls.set_is_finalized(); |
| isolate->object_store()->set_type_class(cls); |
| |
| cls = dynamic_class_; |
| dynamic_type_ = Type::NewNonParameterizedType(cls); |
| |
| cls = void_class_; |
| void_type_ = Type::NewNonParameterizedType(cls); |
| |
| // Allocate and initialize singleton true and false boolean objects. |
| cls = Class::New<Bool>(); |
| isolate->object_store()->set_bool_class(cls); |
| *bool_true_ = Bool::New(true); |
| *bool_false_ = Bool::New(false); |
| |
| *smi_illegal_cid_ = Smi::New(kIllegalCid); |
| |
| String& error_str = String::Handle(); |
| error_str = String::New("SnapshotWriter Error", Heap::kOld); |
| *snapshot_writer_error_ = LanguageError::New(error_str, |
| Report::kError, |
| Heap::kOld); |
| error_str = String::New("Branch offset overflow", Heap::kOld); |
| *branch_offset_error_ = LanguageError::New(error_str, |
| Report::kBailout, |
| Heap::kOld); |
| |
| ASSERT(!null_object_->IsSmi()); |
| ASSERT(!null_array_->IsSmi()); |
| ASSERT(null_array_->IsArray()); |
| ASSERT(!null_string_->IsSmi()); |
| ASSERT(null_string_->IsString()); |
| ASSERT(!null_instance_->IsSmi()); |
| ASSERT(null_instance_->IsInstance()); |
| ASSERT(!null_type_arguments_->IsSmi()); |
| ASSERT(null_type_arguments_->IsTypeArguments()); |
| ASSERT(!empty_array_->IsSmi()); |
| ASSERT(empty_array_->IsArray()); |
| ASSERT(!zero_array_->IsSmi()); |
| ASSERT(zero_array_->IsArray()); |
| ASSERT(!sentinel_->IsSmi()); |
| ASSERT(sentinel_->IsInstance()); |
| ASSERT(!transition_sentinel_->IsSmi()); |
| ASSERT(transition_sentinel_->IsInstance()); |
| ASSERT(!unknown_constant_->IsSmi()); |
| ASSERT(unknown_constant_->IsInstance()); |
| ASSERT(!non_constant_->IsSmi()); |
| ASSERT(non_constant_->IsInstance()); |
| ASSERT(!bool_true_->IsSmi()); |
| ASSERT(bool_true_->IsBool()); |
| ASSERT(!bool_false_->IsSmi()); |
| ASSERT(bool_false_->IsBool()); |
| ASSERT(smi_illegal_cid_->IsSmi()); |
| ASSERT(!snapshot_writer_error_->IsSmi()); |
| ASSERT(snapshot_writer_error_->IsLanguageError()); |
| ASSERT(!branch_offset_error_->IsSmi()); |
| ASSERT(branch_offset_error_->IsLanguageError()); |
| } |
| |
| |
| #define SET_CLASS_NAME(class_name, name) \ |
| cls = class_name##_class(); \ |
| cls.set_name(Symbols::name()); \ |
| |
| void Object::RegisterSingletonClassNames() { |
| Class& cls = Class::Handle(); |
| |
| // Set up names for all VM singleton classes. |
| SET_CLASS_NAME(class, Class); |
| SET_CLASS_NAME(dynamic, Dynamic); |
| SET_CLASS_NAME(void, Void); |
| SET_CLASS_NAME(unresolved_class, UnresolvedClass); |
| SET_CLASS_NAME(type_arguments, TypeArguments); |
| SET_CLASS_NAME(patch_class, PatchClass); |
| SET_CLASS_NAME(function, Function); |
| SET_CLASS_NAME(closure_data, ClosureData); |
| SET_CLASS_NAME(redirection_data, RedirectionData); |
| SET_CLASS_NAME(field, Field); |
| SET_CLASS_NAME(literal_token, LiteralToken); |
| SET_CLASS_NAME(token_stream, TokenStream); |
| SET_CLASS_NAME(script, Script); |
| SET_CLASS_NAME(library, LibraryClass); |
| SET_CLASS_NAME(namespace, Namespace); |
| SET_CLASS_NAME(code, Code); |
| SET_CLASS_NAME(instructions, Instructions); |
| SET_CLASS_NAME(pc_descriptors, PcDescriptors); |
| SET_CLASS_NAME(stackmap, Stackmap); |
| SET_CLASS_NAME(var_descriptors, LocalVarDescriptors); |
| SET_CLASS_NAME(exception_handlers, ExceptionHandlers); |
| SET_CLASS_NAME(deopt_info, DeoptInfo); |
| SET_CLASS_NAME(context, Context); |
| SET_CLASS_NAME(context_scope, ContextScope); |
| SET_CLASS_NAME(icdata, ICData); |
| SET_CLASS_NAME(megamorphic_cache, MegamorphicCache); |
| SET_CLASS_NAME(subtypetestcache, SubtypeTestCache); |
| SET_CLASS_NAME(api_error, ApiError); |
| SET_CLASS_NAME(language_error, LanguageError); |
| SET_CLASS_NAME(unhandled_exception, UnhandledException); |
| SET_CLASS_NAME(unwind_error, UnwindError); |
| |
| // Set up names for object array and one byte string class which are |
| // pre-allocated in the vm isolate also. |
| cls = Dart::vm_isolate()->object_store()->array_class(); |
| cls.set_name(Symbols::_List()); |
| cls = Dart::vm_isolate()->object_store()->one_byte_string_class(); |
| cls.set_name(Symbols::OneByteString()); |
| } |
| |
| |
| // Make unused space in an object whose type has been transformed safe |
| // for traversing during GC. |
| // The unused part of the transformed object is marked as an TypedDataInt8Array |
| // object. |
| void Object::MakeUnusedSpaceTraversable(const Object& obj, |
| intptr_t original_size, |
| intptr_t used_size) { |
| ASSERT(Isolate::Current()->no_gc_scope_depth() > 0); |
| ASSERT(!obj.IsNull()); |
| ASSERT(original_size >= used_size); |
| if (original_size > used_size) { |
| intptr_t leftover_size = original_size - used_size; |
| |
| uword addr = RawObject::ToAddr(obj.raw()) + used_size; |
| if (leftover_size >= TypedData::InstanceSize(0)) { |
| // Update the leftover space as an TypedDataInt8Array object. |
| RawTypedData* raw = |
| reinterpret_cast<RawTypedData*>(RawObject::FromAddr(addr)); |
| uword tags = 0; |
| tags = RawObject::SizeTag::update(leftover_size, tags); |
| tags = RawObject::ClassIdTag::update(kTypedDataInt8ArrayCid, tags); |
| raw->ptr()->tags_ = tags; |
| intptr_t leftover_len = (leftover_size - TypedData::InstanceSize(0)); |
| ASSERT(TypedData::InstanceSize(leftover_len) == leftover_size); |
| raw->ptr()->length_ = Smi::New(leftover_len); |
| } else { |
| // Update the leftover space as a basic object. |
| ASSERT(leftover_size == Object::InstanceSize()); |
| RawObject* raw = reinterpret_cast<RawObject*>(RawObject::FromAddr(addr)); |
| uword tags = 0; |
| tags = RawObject::SizeTag::update(leftover_size, tags); |
| tags = RawObject::ClassIdTag::update(kInstanceCid, tags); |
| raw->ptr()->tags_ = tags; |
| } |
| } |
| } |
| |
| |
| void Object::VerifyBuiltinVtables() { |
| #if defined(DEBUG) |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate != NULL); |
| Class& cls = Class::Handle(isolate, Class::null()); |
| for (intptr_t cid = (kIllegalCid + 1); cid < kNumPredefinedCids; cid++) { |
| if (isolate->class_table()->HasValidClassAt(cid)) { |
| cls ^= isolate->class_table()->At(cid); |
| ASSERT(builtin_vtables_[cid] == cls.raw_ptr()->handle_vtable_); |
| } |
| } |
| ASSERT(builtin_vtables_[kFreeListElement] == 0); |
| #endif |
| } |
| |
| |
| void Object::RegisterClass(const Class& cls, |
| const String& name, |
| const Library& lib) { |
| ASSERT(name.Length() > 0); |
| ASSERT(name.CharAt(0) != '_'); |
| cls.set_name(name); |
| lib.AddClass(cls); |
| } |
| |
| |
| void Object::RegisterPrivateClass(const Class& cls, |
| const String& public_class_name, |
| const Library& lib) { |
| ASSERT(public_class_name.Length() > 0); |
| ASSERT(public_class_name.CharAt(0) == '_'); |
| String& str = String::Handle(); |
| str = lib.PrivateName(public_class_name); |
| cls.set_name(str); |
| lib.AddClass(cls); |
| } |
| |
| |
| RawError* Object::Init(Isolate* isolate) { |
| TIMERSCOPE(isolate, time_bootstrap); |
| ObjectStore* object_store = isolate->object_store(); |
| |
| Class& cls = Class::Handle(isolate); |
| Type& type = Type::Handle(isolate); |
| Array& array = Array::Handle(isolate); |
| Library& lib = Library::Handle(isolate); |
| |
| // All RawArray fields will be initialized to an empty array, therefore |
| // initialize array class first. |
| cls = Class::New<Array>(); |
| object_store->set_array_class(cls); |
| |
| // Array and ImmutableArray are the only VM classes that are parameterized. |
| // Since they are pre-finalized, CalculateFieldOffsets() is not called, so we |
| // need to set the offset of their type_arguments_ field, which is explicitly |
| // declared in RawArray. |
| cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| cls.set_num_type_arguments(1); |
| cls.set_num_own_type_arguments(1); |
| |
| // Set up the growable object array class (Has to be done after the array |
| // class is setup as one of its field is an array object). |
| cls = Class::New<GrowableObjectArray>(); |
| object_store->set_growable_object_array_class(cls); |
| cls.set_type_arguments_field_offset( |
| GrowableObjectArray::type_arguments_offset()); |
| cls.set_num_type_arguments(1); |
| cls.set_num_own_type_arguments(1); |
| |
| // canonical_type_arguments_ are Smi terminated. |
| // Last element contains the count of used slots. |
| const intptr_t kInitialCanonicalTypeArgumentsSize = 4; |
| array = Array::New(kInitialCanonicalTypeArgumentsSize + 1); |
| array.SetAt(kInitialCanonicalTypeArgumentsSize, |
| Smi::Handle(isolate, Smi::New(0))); |
| object_store->set_canonical_type_arguments(array); |
| |
| // Setup type class early in the process. |
| cls = Class::New<Type>(); |
| object_store->set_type_class(cls); |
| |
| cls = Class::New<TypeRef>(); |
| object_store->set_type_ref_class(cls); |
| |
| cls = Class::New<TypeParameter>(); |
| object_store->set_type_parameter_class(cls); |
| |
| cls = Class::New<BoundedType>(); |
| object_store->set_bounded_type_class(cls); |
| |
| cls = Class::New<MixinAppType>(); |
| object_store->set_mixin_app_type_class(cls); |
| |
| cls = Class::New<LibraryPrefix>(); |
| object_store->set_library_prefix_class(cls); |
| |
| // Pre-allocate the OneByteString class needed by the symbol table. |
| cls = Class::NewStringClass(kOneByteStringCid); |
| object_store->set_one_byte_string_class(cls); |
| |
| // Pre-allocate the TwoByteString class needed by the symbol table. |
| cls = Class::NewStringClass(kTwoByteStringCid); |
| object_store->set_two_byte_string_class(cls); |
| |
| // Setup the symbol table for the symbols created in the isolate. |
| Symbols::SetupSymbolTable(isolate); |
| |
| // Set up the libraries array before initializing the core library. |
| const GrowableObjectArray& libraries = GrowableObjectArray::Handle( |
| isolate, GrowableObjectArray::New(Heap::kOld)); |
| object_store->set_libraries(libraries); |
| |
| // Pre-register the core library. |
| Library::InitCoreLibrary(isolate); |
| |
| // Basic infrastructure has been setup, initialize the class dictionary. |
| const Library& core_lib = Library::Handle(isolate, Library::CoreLibrary()); |
| ASSERT(!core_lib.IsNull()); |
| |
| const GrowableObjectArray& pending_classes = |
| GrowableObjectArray::Handle(isolate, GrowableObjectArray::New()); |
| object_store->set_pending_classes(pending_classes); |
| |
| Context& context = Context::Handle(isolate, Context::New(0, Heap::kOld)); |
| object_store->set_empty_context(context); |
| |
| // Now that the symbol table is initialized and that the core dictionary as |
| // well as the core implementation dictionary have been setup, preallocate |
| // remaining classes and register them by name in the dictionaries. |
| String& name = String::Handle(isolate); |
| cls = object_store->array_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::_List(), core_lib); |
| pending_classes.Add(cls); |
| // We cannot use NewNonParameterizedType(cls), because Array is parameterized. |
| type ^= Type::New(Object::Handle(isolate, cls.raw()), |
| TypeArguments::Handle(isolate), |
| Scanner::kNoSourcePos); |
| type.SetIsFinalized(); |
| type ^= type.Canonicalize(); |
| object_store->set_array_type(type); |
| |
| cls = object_store->growable_object_array_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::_GrowableList(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Array>(kImmutableArrayCid); |
| object_store->set_immutable_array_class(cls); |
| cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| cls.set_num_type_arguments(1); |
| cls.set_num_own_type_arguments(1); |
| ASSERT(object_store->immutable_array_class() != object_store->array_class()); |
| cls.set_is_prefinalized(); |
| RegisterPrivateClass(cls, Symbols::_ImmutableList(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->one_byte_string_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::OneByteString(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->two_byte_string_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::TwoByteString(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::NewStringClass(kExternalOneByteStringCid); |
| object_store->set_external_one_byte_string_class(cls); |
| RegisterPrivateClass(cls, Symbols::ExternalOneByteString(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::NewStringClass(kExternalTwoByteStringCid); |
| object_store->set_external_two_byte_string_class(cls); |
| RegisterPrivateClass(cls, Symbols::ExternalTwoByteString(), core_lib); |
| pending_classes.Add(cls); |
| |
| // Pre-register the isolate library so the native class implementations |
| // can be hooked up before compiling it. |
| Library& isolate_lib = |
| Library::Handle(isolate, Library::LookupLibrary(Symbols::DartIsolate())); |
| if (isolate_lib.IsNull()) { |
| isolate_lib = Library::NewLibraryHelper(Symbols::DartIsolate(), true); |
| isolate_lib.SetLoadRequested(); |
| isolate_lib.Register(); |
| isolate->object_store()->set_bootstrap_library(ObjectStore::kIsolate, |
| isolate_lib); |
| } |
| ASSERT(!isolate_lib.IsNull()); |
| ASSERT(isolate_lib.raw() == Library::IsolateLibrary()); |
| |
| cls = Class::New<Capability>(); |
| RegisterPrivateClass(cls, Symbols::_CapabilityImpl(), isolate_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<ReceivePort>(); |
| RegisterPrivateClass(cls, Symbols::_RawReceivePortImpl(), isolate_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<SendPort>(); |
| RegisterPrivateClass(cls, Symbols::_SendPortImpl(), isolate_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Stacktrace>(); |
| object_store->set_stacktrace_class(cls); |
| RegisterClass(cls, Symbols::StackTrace(), core_lib); |
| pending_classes.Add(cls); |
| // Super type set below, after Object is allocated. |
| |
| cls = Class::New<JSRegExp>(); |
| RegisterPrivateClass(cls, Symbols::JSSyntaxRegExp(), core_lib); |
| pending_classes.Add(cls); |
| |
| // Initialize the base interfaces used by the core VM classes. |
| |
| // Allocate and initialize the pre-allocated classes in the core library. |
| // The script and token index of these pre-allocated classes is set up in |
| // the parser when the corelib script is compiled (see |
| // Parser::ParseClassDefinition). |
| cls = Class::New<Instance>(kInstanceCid); |
| object_store->set_object_class(cls); |
| cls.set_name(Symbols::Object()); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| core_lib.AddClass(cls); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_object_type(type); |
| |
| cls = Class::New<Bool>(); |
| object_store->set_bool_class(cls); |
| RegisterClass(cls, Symbols::Bool(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Instance>(kNullCid); |
| object_store->set_null_class(cls); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| RegisterClass(cls, Symbols::Null(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->library_prefix_class(); |
| ASSERT(!cls.IsNull()); |
| RegisterPrivateClass(cls, Symbols::_LibraryPrefix(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->type_class(); |
| RegisterPrivateClass(cls, Symbols::Type(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->type_ref_class(); |
| RegisterPrivateClass(cls, Symbols::TypeRef(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->type_parameter_class(); |
| RegisterPrivateClass(cls, Symbols::TypeParameter(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->bounded_type_class(); |
| RegisterPrivateClass(cls, Symbols::BoundedType(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = object_store->mixin_app_type_class(); |
| RegisterPrivateClass(cls, Symbols::MixinAppType(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Integer>(); |
| object_store->set_integer_implementation_class(cls); |
| RegisterPrivateClass(cls, Symbols::IntegerImplementation(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Smi>(); |
| object_store->set_smi_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Smi(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Mint>(); |
| object_store->set_mint_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Mint(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Bigint>(); |
| object_store->set_bigint_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Bigint(), core_lib); |
| pending_classes.Add(cls); |
| |
| cls = Class::New<Double>(); |
| object_store->set_double_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Double(), core_lib); |
| pending_classes.Add(cls); |
| |
| // Abstract super class for all signature classes. |
| cls = Class::New<Instance>(kIllegalCid); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| RegisterPrivateClass(cls, Symbols::FunctionImpl(), core_lib); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_function_impl_type(type); |
| |
| cls = Class::New<WeakProperty>(); |
| object_store->set_weak_property_class(cls); |
| RegisterPrivateClass(cls, Symbols::_WeakProperty(), core_lib); |
| |
| // Pre-register the mirrors library so we can place the vm class |
| // MirrorReference there rather than the core library. |
| lib = Library::LookupLibrary(Symbols::DartMirrors()); |
| if (lib.IsNull()) { |
| lib = Library::NewLibraryHelper(Symbols::DartMirrors(), true); |
| lib.SetLoadRequested(); |
| lib.Register(); |
| isolate->object_store()->set_bootstrap_library(ObjectStore::kMirrors, |
| lib); |
| } |
| ASSERT(!lib.IsNull()); |
| ASSERT(lib.raw() == Library::MirrorsLibrary()); |
| |
| cls = Class::New<MirrorReference>(); |
| RegisterPrivateClass(cls, Symbols::_MirrorReference(), lib); |
| |
| // Pre-register the profiler library so we can place the vm class |
| // UserTag there rather than the core library. |
| lib = Library::LookupLibrary(Symbols::DartProfiler()); |
| if (lib.IsNull()) { |
| lib = Library::NewLibraryHelper(Symbols::DartProfiler(), true); |
| lib.SetLoadRequested(); |
| lib.Register(); |
| isolate->object_store()->set_bootstrap_library(ObjectStore::kProfiler, |
| lib); |
| } |
| ASSERT(!lib.IsNull()); |
| ASSERT(lib.raw() == Library::ProfilerLibrary()); |
| |
| lib = Library::LookupLibrary(Symbols::DartProfiler()); |
| ASSERT(!lib.IsNull()); |
| cls = Class::New<UserTag>(); |
| RegisterPrivateClass(cls, Symbols::_UserTag(), lib); |
| pending_classes.Add(cls); |
| |
| // Setup some default native field classes which can be extended for |
| // specifying native fields in dart classes. |
| Library::InitNativeWrappersLibrary(isolate); |
| ASSERT(isolate->object_store()->native_wrappers_library() != Library::null()); |
| |
| // Pre-register the typed_data library so the native class implementations |
| // can be hooked up before compiling it. |
| lib = Library::LookupLibrary(Symbols::DartTypedData()); |
| if (lib.IsNull()) { |
| lib = Library::NewLibraryHelper(Symbols::DartTypedData(), true); |
| lib.SetLoadRequested(); |
| lib.Register(); |
| isolate->object_store()->set_bootstrap_library(ObjectStore::kTypedData, |
| lib); |
| } |
| ASSERT(!lib.IsNull()); |
| ASSERT(lib.raw() == Library::TypedDataLibrary()); |
| const intptr_t typed_data_class_array_length = |
| RawObject::NumberOfTypedDataClasses(); |
| Array& typed_data_classes = |
| Array::Handle(Array::New(typed_data_class_array_length)); |
| int index = 0; |
| #define REGISTER_TYPED_DATA_CLASS(clazz) \ |
| cls = Class::NewTypedDataClass(kTypedData##clazz##Cid); \ |
| index = kTypedData##clazz##Cid - kTypedDataInt8ArrayCid; \ |
| typed_data_classes.SetAt(index, cls); \ |
| RegisterPrivateClass(cls, Symbols::_##clazz(), lib); \ |
| |
| CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_CLASS); |
| #undef REGISTER_TYPED_DATA_CLASS |
| #define REGISTER_TYPED_DATA_VIEW_CLASS(clazz) \ |
| cls = Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid); \ |
| index = kTypedData##clazz##ViewCid - kTypedDataInt8ArrayCid; \ |
| typed_data_classes.SetAt(index, cls); \ |
| RegisterPrivateClass(cls, Symbols::_##clazz##View(), lib); \ |
| pending_classes.Add(cls); \ |
| |
| CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_VIEW_CLASS); |
| cls = Class::NewTypedDataViewClass(kByteDataViewCid); |
| index = kByteDataViewCid - kTypedDataInt8ArrayCid; |
| typed_data_classes.SetAt(index, cls); |
| RegisterPrivateClass(cls, Symbols::_ByteDataView(), lib); |
| pending_classes.Add(cls); |
| #undef REGISTER_TYPED_DATA_VIEW_CLASS |
| #define REGISTER_EXT_TYPED_DATA_CLASS(clazz) \ |
| cls = Class::NewExternalTypedDataClass(kExternalTypedData##clazz##Cid); \ |
| index = kExternalTypedData##clazz##Cid - kTypedDataInt8ArrayCid; \ |
| typed_data_classes.SetAt(index, cls); \ |
| RegisterPrivateClass(cls, Symbols::_External##clazz(), lib); \ |
| |
| CLASS_LIST_TYPED_DATA(REGISTER_EXT_TYPED_DATA_CLASS); |
| #undef REGISTER_EXT_TYPED_DATA_CLASS |
| // Register Float32x4 and Int32x4 in the object store. |
| cls = Class::New<Float32x4>(); |
| object_store->set_float32x4_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Float32x4(), lib); |
| cls = Class::New<Int32x4>(); |
| object_store->set_int32x4_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Int32x4(), lib); |
| cls = Class::New<Float64x2>(); |
| object_store->set_float64x2_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Float64x2(), lib); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Float32x4(), lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_float32x4_type(type); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Int32x4(), lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_int32x4_type(type); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Float64x2(), lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_float64x2_type(type); |
| |
| object_store->set_typed_data_classes(typed_data_classes); |
| |
| // Set the super type of class Stacktrace to Object type so that the |
| // 'toString' method is implemented. |
| cls = object_store->stacktrace_class(); |
| type = object_store->object_type(); |
| cls.set_super_type(type); |
| |
| // Abstract class that represents the Dart class Function. |
| cls = Class::New<Instance>(kIllegalCid); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| RegisterClass(cls, Symbols::Function(), core_lib); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_function_type(type); |
| |
| cls = Class::New<Number>(); |
| RegisterClass(cls, Symbols::Number(), core_lib); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_number_type(type); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Int(), core_lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_int_type(type); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Double(), core_lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_double_type(type); |
| |
| name = Symbols::New("String"); |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, name, core_lib); |
| cls.set_num_type_arguments(0); |
| cls.set_num_own_type_arguments(0); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_string_type(type); |
| |
| cls = object_store->bool_class(); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_bool_type(type); |
| |
| cls = object_store->smi_class(); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_smi_type(type); |
| |
| cls = object_store->mint_class(); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_mint_type(type); |
| |
| // The classes 'void' and 'dynamic' are phoney classes to make type checking |
| // more regular; they live in the VM isolate. The class 'void' is not |
| // registered in the class dictionary because its name is a reserved word. |
| // The class 'dynamic' is registered in the class dictionary because its name |
| // is a built-in identifier (this is wrong). |
| // The corresponding types are stored in the object store. |
| cls = object_store->null_class(); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_null_type(type); |
| |
| // Consider removing when/if Null becomes an ordinary class. |
| type = object_store->object_type(); |
| cls.set_super_type(type); |
| |
| // Finish the initialization by compiling the bootstrap scripts containing the |
| // base interfaces and the implementation of the internal classes. |
| const Error& error = Error::Handle(Bootstrap::LoadandCompileScripts()); |
| if (!error.IsNull()) { |
| return error.raw(); |
| } |
| |
| ClassFinalizer::VerifyBootstrapClasses(); |
| MarkInvisibleFunctions(); |
| |
| // Set up the intrinsic state of all functions (core, math and typed data). |
| Intrinsifier::InitializeState(); |
| |
| // Set up recognized state of all functions (core, math and typed data). |
| MethodRecognizer::InitializeState(); |
| |
| // Adds static const fields (class ids) to the class 'ClassID'); |
| lib = Library::LookupLibrary(Symbols::DartInternal()); |
| ASSERT(!lib.IsNull()); |
| cls = lib.LookupClassAllowPrivate(Symbols::ClassID()); |
| ASSERT(!cls.IsNull()); |
| Field& field = Field::Handle(isolate); |
| Smi& value = Smi::Handle(isolate); |
| String& field_name = String::Handle(isolate); |
| |
| #define CLASS_LIST_WITH_NULL(V) \ |
| V(Null) \ |
| CLASS_LIST_NO_OBJECT(V) |
| |
| #define ADD_SET_FIELD(clazz) \ |
| field_name = Symbols::New("cid"#clazz); \ |
| field = Field::New(field_name, true, false, true, false, cls, 0); \ |
| value = Smi::New(k##clazz##Cid); \ |
| field.set_value(value); \ |
| field.set_type(Type::Handle(Type::IntType())); \ |
| cls.AddField(field); \ |
| |
| CLASS_LIST_WITH_NULL(ADD_SET_FIELD) |
| #undef ADD_SET_FIELD |
| |
| return Error::null(); |
| } |
| |
| |
| void Object::InitFromSnapshot(Isolate* isolate) { |
| TIMERSCOPE(isolate, time_bootstrap); |
| ObjectStore* object_store = isolate->object_store(); |
| |
| Class& cls = Class::Handle(); |
| |
| // Set up empty classes in the object store, these will get |
| // initialized correctly when we read from the snapshot. |
| // This is done to allow bootstrapping of reading classes from the snapshot. |
| cls = Class::New<Instance>(kInstanceCid); |
| object_store->set_object_class(cls); |
| |
| cls = Class::New<LibraryPrefix>(); |
| object_store->set_library_prefix_class(cls); |
| |
| cls = Class::New<Type>(); |
| object_store->set_type_class(cls); |
| |
| cls = Class::New<TypeRef>(); |
| object_store->set_type_ref_class(cls); |
| |
| cls = Class::New<TypeParameter>(); |
| object_store->set_type_parameter_class(cls); |
| |
| cls = Class::New<BoundedType>(); |
| object_store->set_bounded_type_class(cls); |
| |
| cls = Class::New<MixinAppType>(); |
| object_store->set_mixin_app_type_class(cls); |
| |
| cls = Class::New<Array>(); |
| object_store->set_array_class(cls); |
| |
| cls = Class::New<Array>(kImmutableArrayCid); |
| object_store->set_immutable_array_class(cls); |
| |
| cls = Class::New<GrowableObjectArray>(); |
| object_store->set_growable_object_array_class(cls); |
| |
| cls = Class::New<Float32x4>(); |
| object_store->set_float32x4_class(cls); |
| |
| cls = Class::New<Int32x4>(); |
| object_store->set_int32x4_class(cls); |
| |
| cls = Class::New<Float64x2>(); |
| object_store->set_float64x2_class(cls); |
| |
| #define REGISTER_TYPED_DATA_CLASS(clazz) \ |
| cls = Class::NewTypedDataClass(kTypedData##clazz##Cid); |
| CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_CLASS); |
| #undef REGISTER_TYPED_DATA_CLASS |
| #define REGISTER_TYPED_DATA_VIEW_CLASS(clazz) \ |
| cls = Class::NewTypedDataViewClass(kTypedData##clazz##ViewCid); |
| CLASS_LIST_TYPED_DATA(REGISTER_TYPED_DATA_VIEW_CLASS); |
| cls = Class::NewTypedDataViewClass(kByteDataViewCid); |
| #undef REGISTER_TYPED_DATA_VIEW_CLASS |
| #define REGISTER_EXT_TYPED_DATA_CLASS(clazz) \ |
| cls = Class::NewExternalTypedDataClass(kExternalTypedData##clazz##Cid); |
| CLASS_LIST_TYPED_DATA(REGISTER_EXT_TYPED_DATA_CLASS); |
| #undef REGISTER_EXT_TYPED_DATA_CLASS |
| |
| cls = Class::New<Integer>(); |
| object_store->set_integer_implementation_class(cls); |
| |
| cls = Class::New<Smi>(); |
| object_store->set_smi_class(cls); |
| |
| cls = Class::New<Mint>(); |
| object_store->set_mint_class(cls); |
| |
| cls = Class::New<Double>(); |
| object_store->set_double_class(cls); |
| |
| cls = Class::New<Bigint>(); |
| object_store->set_bigint_class(cls); |
| |
| cls = Class::NewStringClass(kOneByteStringCid); |
| object_store->set_one_byte_string_class(cls); |
| |
| cls = Class::NewStringClass(kTwoByteStringCid); |
| object_store->set_two_byte_string_class(cls); |
| |
| cls = Class::NewStringClass(kExternalOneByteStringCid); |
| object_store->set_external_one_byte_string_class(cls); |
| |
| cls = Class::NewStringClass(kExternalTwoByteStringCid); |
| object_store->set_external_two_byte_string_class(cls); |
| |
| cls = Class::New<Bool>(); |
| object_store->set_bool_class(cls); |
| |
| cls = Class::New<Instance>(kNullCid); |
| object_store->set_null_class(cls); |
| |
| cls = Class::New<Capability>(); |
| cls = Class::New<ReceivePort>(); |
| cls = Class::New<SendPort>(); |
| |
| cls = Class::New<Stacktrace>(); |
| object_store->set_stacktrace_class(cls); |
| |
| cls = Class::New<JSRegExp>(); |
| |
| // Some classes are not stored in the object store. Yet we still need to |
| // create their Class object so that they get put into the class_table |
| // (as a side effect of Class::New()). |
| cls = Class::New<Number>(); |
| |
| cls = Class::New<WeakProperty>(); |
| object_store->set_weak_property_class(cls); |
| |
| cls = Class::New<MirrorReference>(); |
| |
| cls = Class::New<UserTag>(); |
| } |
| |
| |
| void Object::Print() const { |
| OS::Print("%s\n", ToCString()); |
| } |
| |
| |
| void Object::PrintJSON(JSONStream* stream, bool ref) const { |
| if (IsNull()) { |
| JSONObject jsobj(stream); |
| jsobj.AddProperty("type", ref ? "@Null" : "Null"); |
| jsobj.AddProperty("id", "objects/null"); |
| jsobj.AddProperty("valueAsString", "null"); |
| } else { |
| PrintJSONImpl(stream, ref); |
| } |
| } |
| |
| |
| RawString* Object::DictionaryName() const { |
| return String::null(); |
| } |
| |
| |
| void Object::InitializeObject(uword address, intptr_t class_id, intptr_t size) { |
| // TODO(iposva): Get a proper halt instruction from the assembler which |
| // would be needed here for code objects. |
| uword initial_value = reinterpret_cast<uword>(null_); |
| uword cur = address; |
| uword end = address + size; |
| while (cur < end) { |
| *reinterpret_cast<uword*>(cur) = initial_value; |
| cur += kWordSize; |
| } |
| uword tags = 0; |
| ASSERT(class_id != kIllegalCid); |
| tags = RawObject::ClassIdTag::update(class_id, tags); |
| tags = RawObject::SizeTag::update(size, tags); |
| reinterpret_cast<RawObject*>(address)->tags_ = tags; |
| } |
| |
| |
| void Object::CheckHandle() const { |
| #if defined(DEBUG) |
| if (raw_ != Object::null()) { |
| if ((reinterpret_cast<uword>(raw_) & kSmiTagMask) == kSmiTag) { |
| ASSERT(vtable() == Smi::handle_vtable_); |
| return; |
| } |
| intptr_t cid = raw_->GetClassId(); |
| if (cid >= kNumPredefinedCids) { |
| cid = kInstanceCid; |
| } |
| ASSERT(vtable() == builtin_vtables_[cid]); |
| if (FLAG_verify_handles) { |
| Isolate* isolate = Isolate::Current(); |
| Heap* isolate_heap = isolate->heap(); |
| Heap* vm_isolate_heap = Dart::vm_isolate()->heap(); |
| ASSERT(isolate_heap->Contains(RawObject::ToAddr(raw_)) || |
| vm_isolate_heap->Contains(RawObject::ToAddr(raw_))); |
| } |
| } |
| #endif |
| } |
| |
| |
| RawObject* Object::Allocate(intptr_t cls_id, |
| intptr_t size, |
| Heap::Space space) { |
| ASSERT(Utils::IsAligned(size, kObjectAlignment)); |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate->no_callback_scope_depth() == 0); |
| Heap* heap = isolate->heap(); |
| |
| uword address = heap->Allocate(size, space); |
| if (address == 0) { |
| // Use the preallocated out of memory exception to avoid calling |
| // into dart code or allocating any code. |
| const Instance& exception = |
| Instance::Handle(isolate->object_store()->out_of_memory()); |
| Exceptions::Throw(isolate, exception); |
| UNREACHABLE(); |
| } |
| if (space == Heap::kNew) { |
| isolate->class_table()->UpdateAllocatedNew(cls_id, size); |
| } else { |
| isolate->class_table()->UpdateAllocatedOld(cls_id, size); |
| } |
| NoGCScope no_gc; |
| InitializeObject(address, cls_id, size); |
| RawObject* raw_obj = reinterpret_cast<RawObject*>(address + kHeapObjectTag); |
| ASSERT(cls_id == RawObject::ClassIdTag::decode(raw_obj->ptr()->tags_)); |
| return raw_obj; |
| } |
| |
| |
| class StoreBufferUpdateVisitor : public ObjectPointerVisitor { |
| public: |
| explicit StoreBufferUpdateVisitor(Isolate* isolate, RawObject* obj) : |
| ObjectPointerVisitor(isolate), old_obj_(obj) { |
| ASSERT(old_obj_->IsOldObject()); |
| } |
| |
| void VisitPointers(RawObject** first, RawObject** last) { |
| for (RawObject** curr = first; curr <= last; ++curr) { |
| RawObject* raw_obj = *curr; |
| if (raw_obj->IsHeapObject() && raw_obj->IsNewObject()) { |
| old_obj_->SetRememberedBit(); |
| isolate()->store_buffer()->AddObject(old_obj_); |
| // Remembered this object. There is no need to continue searching. |
| return; |
| } |
| } |
| } |
| |
| private: |
| RawObject* old_obj_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StoreBufferUpdateVisitor); |
| }; |
| |
| |
| bool Object::IsReadOnlyHandle() const { |
| return Dart::IsReadOnlyHandle(reinterpret_cast<uword>(this)); |
| } |
| |
| |
| bool Object::IsNotTemporaryScopedHandle() const { |
| return (IsZoneHandle() || IsReadOnlyHandle()); |
| } |
| |
| |
| |
| RawObject* Object::Clone(const Object& src, Heap::Space space) { |
| const Class& cls = Class::Handle(src.clazz()); |
| intptr_t size = src.raw()->Size(); |
| RawObject* raw_obj = Object::Allocate(cls.id(), size, space); |
| NoGCScope no_gc; |
| memmove(raw_obj->ptr(), src.raw()->ptr(), size); |
| if ((space == Heap::kOld) && !raw_obj->IsRemembered()) { |
| StoreBufferUpdateVisitor visitor(Isolate::Current(), raw_obj); |
| raw_obj->VisitPointers(&visitor); |
| } |
| return raw_obj; |
| } |
| |
| |
| RawString* Class::Name() const { |
| // TODO(turnidge): This assert fails for the fake kFreeListElement class. |
| // Fix this. |
| ASSERT(raw_ptr()->name_ != String::null()); |
| return raw_ptr()->name_; |
| } |
| |
| |
| RawString* Class::PrettyName() const { |
| return GeneratePrettyName(); |
| } |
| |
| |
| RawString* Class::UserVisibleName() const { |
| ASSERT(raw_ptr()->user_name_ != String::null()); |
| return raw_ptr()->user_name_; |
| } |
| |
| |
| RawType* Class::SignatureType() const { |
| ASSERT(IsSignatureClass()); |
| const Function& function = Function::Handle(signature_function()); |
| ASSERT(!function.IsNull()); |
| if (function.signature_class() != raw()) { |
| // This class is a function type alias. Return the canonical signature type. |
| const Class& canonical_class = Class::Handle(function.signature_class()); |
| return canonical_class.SignatureType(); |
| } |
| // Return the first canonical signature type if already computed at class |
| // finalization time. The optimizer may canonicalize instantiated function |
| // types of the same signature class, but these will be added after the |
| // uninstantiated signature class at index 0. |
| Array& signature_types = Array::Handle(); |
| signature_types ^= canonical_types(); |
| if (signature_types.IsNull()) { |
| set_canonical_types(empty_array()); |
| signature_types ^= canonical_types(); |
| } |
| // The canonical_types array is initialized to the empty array. |
| ASSERT(!signature_types.IsNull()); |
| if (signature_types.Length() > 0) { |
| Type& signature_type = Type::Handle(); |
| signature_type ^= signature_types.At(0); |
| ASSERT(!signature_type.IsNull()); |
| return signature_type.raw(); |
| } |
| // A signature class extends class Instance and is parameterized in the same |
| // way as the owner class of its non-static signature function. |
| // It is not type parameterized if its signature function is static. |
| // See Class::NewSignatureClass() for the setup of its type parameters. |
| // During type finalization, the type arguments of the super class of the |
| // owner class of its signature function will be prepended to the type |
| // argument vector. Therefore, we only need to set the type arguments |
| // matching the type parameters here. |
| const TypeArguments& signature_type_arguments = |
| TypeArguments::Handle(type_parameters()); |
| const Type& signature_type = Type::Handle( |
| Type::New(*this, signature_type_arguments, token_pos())); |
| |
| // Return the still unfinalized signature type. |
| ASSERT(!signature_type.IsFinalized()); |
| return signature_type.raw(); |
| } |
| |
| |
| RawAbstractType* Class::RareType() const { |
| const Type& type = Type::Handle(Type::New( |
| *this, |
| Object::null_type_arguments(), |
| Scanner::kNoSourcePos)); |
| return ClassFinalizer::FinalizeType(*this, |
| type, |
| ClassFinalizer::kCanonicalize); |
| } |
| |
| |
| RawAbstractType* Class::DeclarationType() const { |
| const TypeArguments& args = TypeArguments::Handle(type_parameters()); |
| const Type& type = Type::Handle(Type::New( |
| *this, |
| args, |
| Scanner::kNoSourcePos)); |
| return ClassFinalizer::FinalizeType(*this, |
| type, |
| ClassFinalizer::kCanonicalize); |
| } |
| |
| |
| template <class FakeObject> |
| RawClass* Class::New() { |
| ASSERT(Object::class_class() != Class::null()); |
| Class& result = Class::Handle(); |
| { |
| RawObject* raw = Object::Allocate(Class::kClassId, |
| Class::InstanceSize(), |
| Heap::kOld); |
| NoGCScope no_gc; |
| result ^= raw; |
| } |
| FakeObject fake; |
| result.set_handle_vtable(fake.vtable()); |
| result.set_instance_size(FakeObject::InstanceSize()); |
| result.set_next_field_offset(FakeObject::NextFieldOffset()); |
| COMPILE_ASSERT((FakeObject::kClassId != kInstanceCid)); |
| result.set_id(FakeObject::kClassId); |
| result.set_state_bits(0); |
| if (FakeObject::kClassId < kInstanceCid) { |
| // VM internal classes are done. There is no finalization needed or |
| // possible in this case. |
| result.set_is_finalized(); |
| } else { |
| // VM backed classes are almost ready: run checks and resolve class |
| // references, but do not recompute size. |
| result.set_is_prefinalized(); |
| } |
| result.set_type_arguments_field_offset_in_words(kNoTypeArguments); |
| result.set_num_type_arguments(0); |
| result.set_num_own_type_arguments(0); |
| result.set_num_native_fields(0); |
| result.set_token_pos(Scanner::kNoSourcePos); |
| result.InitEmptyFields(); |
| Isolate::Current()->RegisterClass(result); |
| return result.raw(); |
| } |
| |
| |
| |
| static void ReportTooManyTypeArguments(const Class& cls) { |
| Report::MessageF(Report::kError, |
| Script::Handle(cls.script()), |
| cls.token_pos(), |
| "too many type parameters declared in class '%s' or in its " |
| "super classes", |
| String::Handle(cls.Name()).ToCString()); |
| UNREACHABLE(); |
| } |
| |
| |
| void Class::set_num_type_arguments(intptr_t value) const { |
| if (!Utils::IsInt(16, value)) { |
| ReportTooManyTypeArguments(*this); |
| } |
| raw_ptr()->num_type_arguments_ = value; |
| } |
| |
| |
| void Class::set_num_own_type_arguments(intptr_t value) const { |
| if (!Utils::IsInt(16, value)) { |
| ReportTooManyTypeArguments(*this); |
| } |
| raw_ptr()->num_own_type_arguments_ = value; |
| } |
| |
| |
| // Initialize class fields of type Array with empty array. |
| void Class::InitEmptyFields() { |
| if (Object::empty_array().raw() == Array::null()) { |
| // The empty array has not been initialized yet. |
| return; |
| } |
| StorePointer(&raw_ptr()->interfaces_, Object::empty_array().raw()); |
| StorePointer(&raw_ptr()->constants_, Object::empty_array().raw()); |
| StorePointer(&raw_ptr()->functions_, Object::empty_array().raw()); |
| StorePointer(&raw_ptr()->fields_, Object::empty_array().raw()); |
| StorePointer(&raw_ptr()->invocation_dispatcher_cache_, |
| Object::empty_array().raw()); |
| } |
| |
| |
| RawArray* Class::OffsetToFieldMap() const { |
| Array& array = Array::Handle(raw_ptr()->offset_in_words_to_field_); |
| if (array.IsNull()) { |
| ASSERT(is_finalized()); |
| const intptr_t length = raw_ptr()->instance_size_in_words_; |
| array = Array::New(length, Heap::kOld); |
| Class& cls = Class::Handle(this->raw()); |
| Array& fields = Array::Handle(); |
| Field& f = Field::Handle(); |
| while (!cls.IsNull()) { |
| fields = cls.fields(); |
| for (intptr_t i = 0; i < fields.Length(); ++i) { |
| f ^= fields.At(i); |
| if (!f.is_static()) { |
| array.SetAt(f.Offset() >> kWordSizeLog2, f); |
| } |
| } |
| cls = cls.SuperClass(); |
| } |
| StorePointer(&raw_ptr()->offset_in_words_to_field_, array.raw()); |
| } |
| return array.raw(); |
| } |
| |
| |
| bool Class::HasInstanceFields() const { |
| const Array& field_array = Array::Handle(fields()); |
| Field& field = Field::Handle(); |
| for (intptr_t i = 0; i < field_array.Length(); ++i) { |
| field ^= field_array.At(i); |
| if (!field.is_static()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| void Class::SetFunctions(const Array& value) const { |
| ASSERT(!value.IsNull()); |
| #if defined(DEBUG) |
| // Verify that all the functions in the array have this class as owner. |
| Function& func = Function::Handle(); |
| intptr_t len = value.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| func ^= value.At(i); |
| ASSERT(func.Owner() == raw()); |
| } |
| #endif |
| StorePointer(&raw_ptr()->functions_, value.raw()); |
| } |
| |
| |
| void Class::AddFunction(const Function& function) const { |
| const Array& arr = Array::Handle(functions()); |
| const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1)); |
| new_arr.SetAt(arr.Length(), function); |
| SetFunctions(new_arr); |
| } |
| |
| |
| intptr_t Class::FindFunctionIndex(const Function& needle) const { |
| Isolate* isolate = Isolate::Current(); |
| if (EnsureIsFinalized(isolate) != Error::null()) { |
| return -1; |
| } |
| REUSABLE_ARRAY_HANDLESCOPE(isolate); |
| REUSABLE_FUNCTION_HANDLESCOPE(isolate); |
| Array& funcs = isolate->ArrayHandle(); |
| Function& function = isolate->FunctionHandle(); |
| funcs ^= functions(); |
| ASSERT(!funcs.IsNull()); |
| const intptr_t len = funcs.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| function ^= funcs.At(i); |
| if (function.raw() == needle.raw()) { |
| return i; |
| } |
| } |
| // No function found. |
| return -1; |
| } |
| |
| |
| RawFunction* Class::FunctionFromIndex(intptr_t idx) const { |
| const Array& funcs = Array::Handle(functions()); |
| if ((idx < 0) || (idx >= funcs.Length())) { |
| return Function::null(); |
| } |
| Function& func = Function::Handle(); |
| func ^= funcs.At(idx); |
| ASSERT(!func.IsNull()); |
| return func.raw(); |
| } |
| |
| |
| RawFunction* Class::ImplicitClosureFunctionFromIndex(intptr_t idx) const { |
| const Array& funcs = Array::Handle(functions()); |
| if ((idx < 0) || (idx >= funcs.Length())) { |
| return Function::null(); |
| } |
| Function& func = Function::Handle(); |
| func ^= funcs.At(idx); |
| ASSERT(!func.IsNull()); |
| if (!func.HasImplicitClosureFunction()) { |
| return Function::null(); |
| } |
| const Function& closure_func = |
| Function::Handle(func.ImplicitClosureFunction()); |
| ASSERT(!closure_func.IsNull()); |
| return closure_func.raw(); |
| } |
| |
| |
| intptr_t Class::FindImplicitClosureFunctionIndex(const Function& needle) const { |
| Isolate* isolate = Isolate::Current(); |
| if (EnsureIsFinalized(isolate) != Error::null()) { |
| return -1; |
| } |
| REUSABLE_ARRAY_HANDLESCOPE(isolate); |
| REUSABLE_FUNCTION_HANDLESCOPE(isolate); |
| Array& funcs = isolate->ArrayHandle(); |
| Function& function = isolate->FunctionHandle(); |
| funcs ^= functions(); |
| ASSERT(!funcs.IsNull()); |
| Function& implicit_closure = Function::Handle(isolate); |
| const intptr_t len = funcs.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| function ^= funcs.At(i); |
| implicit_closure ^= function.implicit_closure_function(); |
| if (implicit_closure.IsNull()) { |
| // Skip non-implicit closure functions. |
| continue; |
| } |
| if (needle.raw() == implicit_closure.raw()) { |
| return i; |
| } |
| } |
| // No function found. |
| return -1; |
| } |
| |
| |
| |
| intptr_t Class::FindInvocationDispatcherFunctionIndex( |
| const Function& needle) const { |
| Isolate* isolate = Isolate::Current(); |
| if (EnsureIsFinalized(isolate) != Error::null()) { |
| return -1; |
| } |
| REUSABLE_ARRAY_HANDLESCOPE(isolate); |
| REUSABLE_OBJECT_HANDLESCOPE(isolate); |
| Array& funcs = isolate->ArrayHandle(); |
| Object& object = isolate->ObjectHandle(); |
| funcs ^= invocation_dispatcher_cache(); |
| ASSERT(!funcs.IsNull()); |
| const intptr_t len = funcs.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| object = funcs.At(i); |
| // The invocation_dispatcher_cache is a table with some entries that |
| // are functions. |
| if (object.IsFunction()) { |
| if (Function::Cast(object).raw() == needle.raw()) { |
| return i; |
| } |
| } |
| } |
| // No function found. |
| return -1; |
| } |
| |
| |
| |
| RawFunction* Class::InvocationDispatcherFunctionFromIndex(intptr_t idx) const { |
| Isolate* isolate = Isolate::Current(); |
| REUSABLE_ARRAY_HANDLESCOPE(isolate); |
| REUSABLE_OBJECT_HANDLESCOPE(isolate); |
| Array& dispatcher_cache = isolate->ArrayHandle(); |
| Object& object = isolate->ObjectHandle(); |
| dispatcher_cache ^= invocation_dispatcher_cache(); |
| object = dispatcher_cache.At(idx); |
| if (!object.IsFunction()) { |
| return Function::null(); |
| } |
| return Function::Cast(object).raw(); |
| } |
| |
| |
| void Class::AddClosureFunction(const Function& function) const { |
| GrowableObjectArray& closures = |
| GrowableObjectArray::Handle(raw_ptr()->closure_functions_); |
| if (closures.IsNull()) { |
| closures = GrowableObjectArray::New(4); |
| StorePointer(&raw_ptr()->closure_functions_, closures.raw()); |
| } |
| ASSERT(function.IsNonImplicitClosureFunction()); |
| ASSERT(function.Owner() == this->raw()); |
| closures.Add(function); |
| } |
| |
| |
| // Lookup the innermost closure function that contains token at token_pos. |
| RawFunction* Class::LookupClosureFunction(intptr_t token_pos) const { |
| if (raw_ptr()->closure_functions_ == GrowableObjectArray::null()) { |
| return Function::null(); |
| } |
| const GrowableObjectArray& closures = |
| GrowableObjectArray::Handle(raw_ptr()->closure_functions_); |
| Function& closure = Function::Handle(); |
| intptr_t num_closures = closures.Length(); |
| intptr_t best_fit_token_pos = -1; |
| intptr_t best_fit_index = -1; |
| for (intptr_t i = 0; i < num_closures; i++) { |
| closure ^= closures.At(i); |
| ASSERT(!closure.IsNull()); |
| if ((closure.token_pos() <= token_pos) && |
| (token_pos <= closure.end_token_pos()) && |
| (best_fit_token_pos < closure.token_pos())) { |
| best_fit_index = i; |
| best_fit_token_pos = closure.token_pos(); |
| } |
| } |
| closure = Function::null(); |
| if (best_fit_index >= 0) { |
| closure ^= closures.At(best_fit_index); |
| } |
| return closure.raw(); |
| } |
| |
| intptr_t Class::FindClosureIndex(const Function& needle) const { |
| if (closures() == GrowableObjectArray::null()) { |
| return -1; |
| } |
| Isolate* isolate = Isolate::Current(); |
| const GrowableObjectArray& closures_array = |
| GrowableObjectArray::Handle(isolate, closures()); |
| REUSABLE_FUNCTION_HANDLESCOPE(isolate); |
| Function& closure = isolate->FunctionHandle(); |
| intptr_t num_closures = closures_array.Length(); |
| for (intptr_t i = 0; i < num_closures; i++) { |
| closure ^= closures_array.At(i); |
| ASSERT(!closure.IsNull()); |
| if (closure.raw() == needle.raw()) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| |
| RawFunction* Class::ClosureFunctionFromIndex(intptr_t idx) const { |
| const GrowableObjectArray& closures_array = |
| GrowableObjectArray::Handle(closures()); |
| if ((idx < 0) || (idx >= closures_array.Length())) { |
| return Function::null(); |
| } |
| Function& func = Function::Handle(); |
| func ^= closures_array.At(idx); |
| ASSERT(!func.IsNull()); |
| return func.raw(); |
| } |
| |
| |
| void Class::set_signature_function(const Function& value) const { |
| ASSERT(value.IsClosureFunction() || value.IsSignatureFunction()); |
| StorePointer(&raw_ptr()->signature_function_, value.raw()); |
| } |
| |
| |
| void Class::set_state_bits(intptr_t bits) const { |
| raw_ptr()->state_bits_ = static_cast<uint16_t>(bits); |
| } |
| |
| |
| void Class::set_library(const Library& value) const { |
| StorePointer(&raw_ptr()->library_, value.raw()); |
| } |
| |
| |
| void Class::set_type_parameters(const TypeArguments& value) const { |
| StorePointer(&raw_ptr()->type_parameters_, value.raw()); |
| } |
| |
| |
| intptr_t Class::NumTypeParameters(Isolate* isolate) const { |
| if (IsMixinApplication() && !is_mixin_type_applied()) { |
| ClassFinalizer::ApplyMixinType(*this); |
| } |
| if (type_parameters() == TypeArguments::null()) { |
| return 0; |
| } |
| REUSABLE_TYPE_ARGUMENTS_HANDLESCOPE(isolate); |
| TypeArguments& type_params = isolate->TypeArgumentsHandle(); |
| type_params = type_parameters(); |
| return type_params.Length(); |
| } |
| |
| |
| intptr_t Class::NumOwnTypeArguments() const { |
| // Return cached value if already calculated. |
| if (num_own_type_arguments() != kUnknownNumTypeArguments) { |
| return num_own_type_arguments(); |
| } |
| Isolate* isolate = Isolate::Current(); |
| const intptr_t num_type_params = NumTypeParameters(); |
| if (!FLAG_overlap_type_arguments || |
| (num_type_params == 0) || |
| (super_type() == AbstractType::null()) || |
| (super_type() == isolate->object_store()->object_type())) { |
| set_num_own_type_arguments(num_type_params); |
| return num_type_params; |
| } |
| ASSERT(!IsMixinApplication() || is_mixin_type_applied()); |
| const AbstractType& sup_type = AbstractType::Handle(isolate, super_type()); |
| const TypeArguments& sup_type_args = |
| TypeArguments::Handle(isolate, sup_type.arguments()); |
| if (sup_type_args.IsNull()) { |
| // The super type is raw or the super class is non generic. |
| // In either case, overlapping is not possible. |
| set_num_own_type_arguments(num_type_params); |
| return num_type_params; |
| } |
| const intptr_t num_sup_type_args = sup_type_args.Length(); |
| // At this point, the super type may or may not be finalized. In either case, |
| // the result of this function must remain the same. |
| // The value of num_sup_type_args may increase when the super type is |
| // finalized, but the last num_sup_type_args type arguments will not be |
| // modified by finalization, only shifted to higher indices in the vector. |
| // They may however get wrapped in a BoundedType, which we skip. |
| // The super type may not even be resolved yet. This is not necessary, since |
| // we only check for matching type parameters, which are resolved by default. |
| const TypeArguments& type_params = |
| TypeArguments::Handle(isolate, type_parameters()); |
| // Determine the maximum overlap of a prefix of the vector consisting of the |
| // type parameters of this class with a suffix of the vector consisting of the |
| // type arguments of the super type of this class. |
| // The number of own type arguments of this class is the number of its type |
| // parameters minus the number of type arguments in the overlap. |
| // Attempt to overlap the whole vector of type parameters; reduce the size |
| // of the vector (keeping the first type parameter) until it fits or until |
| // its size is zero. |
| TypeParameter& type_param = TypeParameter::Handle(isolate); |
| AbstractType& sup_type_arg = AbstractType::Handle(isolate); |
| for (intptr_t num_overlapping_type_args = |
| (num_type_params < num_sup_type_args) ? |
| num_type_params : num_sup_type_args; |
| num_overlapping_type_args > 0; num_overlapping_type_args--) { |
| intptr_t i = 0; |
| for (; i < num_overlapping_type_args; i++) { |
| type_param ^= type_params.TypeAt(i); |
| sup_type_arg = sup_type_args.TypeAt( |
| num_sup_type_args - num_overlapping_type_args + i); |
| // BoundedType can nest in case the finalized super type has bounded type |
| // arguments that overlap multiple times in its own super class chain. |
| while (sup_type_arg.IsBoundedType()) { |
| sup_type_arg = BoundedType::Cast(sup_type_arg).type(); |
| } |
| if (!type_param.Equals(sup_type_arg)) break; |
| } |
| if (i == num_overlapping_type_args) { |
| // Overlap found. |
| set_num_own_type_arguments(num_type_params - num_overlapping_type_args); |
| return num_type_params - num_overlapping_type_args; |
| } |
| } |
| // No overlap found. |
| set_num_own_type_arguments(num_type_params); |
| return num_type_params; |
| } |
| |
| |
| intptr_t Class::NumTypeArguments() const { |
| // Return cached value if already calculated. |
| if (num_type_arguments() != kUnknownNumTypeArguments) { |
| return num_type_arguments(); |
| } |
| // To work properly, this call requires the super class of this class to be |
| // resolved, which is checked by the type_class() call on the super type. |
| // Note that calling type_class() on a MixinAppType fails. |
| Isolate* isolate = Isolate::Current(); |
| Class& cls = Class::Handle(isolate); |
| AbstractType& sup_type = AbstractType::Handle(isolate); |
| cls = raw(); |
| intptr_t num_type_args = 0; |
| do { |
| if (cls.IsSignatureClass()) { |
| Function& signature_fun = Function::Handle(isolate); |
| signature_fun ^= cls.signature_function(); |
| if (!signature_fun.is_static() && |
| !signature_fun.HasInstantiatedSignature()) { |
| cls = signature_fun.Owner(); |
| } |
| } |
| // Calling NumOwnTypeArguments() on a mixin application class will setup the |
| // type parameters if not already done. |
| num_type_args += cls.NumOwnTypeArguments(); |
| // Super type of Object class is null. |
| if ((cls.super_type() == AbstractType::null()) || |
| (cls.super_type() == isolate->object_store()->object_type())) { |
| break; |
| } |
| sup_type = cls.super_type(); |
| ClassFinalizer::ResolveTypeClass(cls, sup_type); |
| cls = sup_type.type_class(); |
| } while (true); |
| set_num_type_arguments(num_type_args); |
| return num_type_args; |
| } |
| |
| |
| RawClass* Class::SuperClass() const { |
| if (super_type() == AbstractType::null()) { |
| return Class::null(); |
| } |
| const AbstractType& sup_type = AbstractType::Handle(super_type()); |
| return sup_type.type_class(); |
| } |
| |
| |
| void Class::set_super_type(const AbstractType& value) const { |
| ASSERT(value.IsNull() || |
| (value.IsType() && !value.IsDynamicType()) || |
| value.IsMixinAppType()); |
| StorePointer(&raw_ptr()->super_type_, value.raw()); |
| } |
| |
| |
| // Return a TypeParameter if the type_name is a type parameter of this class. |
| // Return null otherwise. |
| RawTypeParameter* Class::LookupTypeParameter(const String& type_name) const { |
| ASSERT(!type_name.IsNull()); |
| Isolate* isolate = Isolate::Current(); |
| REUSABLE_TYPE_ARGUMENTS_HANDLESCOPE(isolate); |
| REUSABLE_TYPE_PARAMETER_HANDLESCOPE(isolate); |
| REUSABLE_STRING_HANDLESCOPE(isolate); |
| TypeArguments& type_params = isolate->TypeArgumentsHandle(); |
| TypeParameter& type_param = isolate->TypeParameterHandle(); |
| String& type_param_name = isolate->StringHandle(); |
| |
| type_params ^= type_parameters(); |
| if (!type_params.IsNull()) { |
| const intptr_t num_type_params = type_params.Length(); |
| for (intptr_t i = 0; i < num_type_params; i++) { |
| type_param ^= type_params.TypeAt(i); |
| type_param_name = type_param.name(); |
| if (type_param_name.Equals(type_name)) { |
| return type_param.raw(); |
| } |
| } |
| } |
| return TypeParameter::null(); |
| } |
| |
| |
| void Class::CalculateFieldOffsets() const { |
| Array& flds = Array::Handle(fields()); |
| const Class& super = Class::Handle(SuperClass()); |
| intptr_t offset = 0; |
| intptr_t type_args_field_offset = kNoTypeArguments; |
| if (super.IsNull()) { |
| offset = Instance::NextFieldOffset(); |
| ASSERT(offset > 0); |
| } else { |
| ASSERT(super.is_finalized() || super.is_prefinalized()); |
| type_args_field_offset = super.type_arguments_field_offset(); |
| offset = super.next_field_offset(); |
| ASSERT(offset > 0); |
| // We should never call CalculateFieldOffsets for native wrapper |
| // classes, assert this. |
| ASSERT(num_native_fields() == 0); |
| set_num_native_fields(super.num_native_fields()); |
| } |
| // If the super class is parameterized, use the same type_arguments field, |
| // otherwise, if this class is the first in the super chain to be |
| // parameterized, introduce a new type_arguments field. |
| if (type_args_field_offset == kNoTypeArguments) { |
| const TypeArguments& type_params = TypeArguments::Handle(type_parameters()); |
| if (!type_params.IsNull()) { |
| ASSERT(type_params.Length() > 0); |
| // The instance needs a type_arguments field. |
| type_args_field_offset = offset; |
| offset += kWordSize; |
| } |
| } |
| set_type_arguments_field_offset(type_args_field_offset); |
| ASSERT(offset > 0); |
| Field& field = Field::Handle(); |
| intptr_t len = flds.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| field ^= flds.At(i); |
| // Offset is computed only for instance fields. |
| if (!field.is_static()) { |
| ASSERT(field.Offset() == 0); |
| field.SetOffset(offset); |
| offset += kWordSize; |
| } |
| } |
| set_instance_size(RoundedAllocationSize(offset)); |
| set_next_field_offset(offset); |
| } |
| |
| |
| RawFunction* Class::GetInvocationDispatcher(const String& target_name, |
| const Array& args_desc, |
| RawFunction::Kind kind) const { |
| enum { |
| kNameIndex = 0, |
| kArgsDescIndex, |
| kFunctionIndex, |
| kEntrySize |
| }; |
| |
| ASSERT(kind == RawFunction::kNoSuchMethodDispatcher || |
| kind == RawFunction::kInvokeFieldDispatcher); |
| Function& dispatcher = Function::Handle(); |
| Array& cache = Array::Handle(invocation_dispatcher_cache()); |
| ASSERT(!cache.IsNull()); |
| String& name = String::Handle(); |
| Array& desc = Array::Handle(); |
| intptr_t i = 0; |
| for (; i < cache.Length(); i += kEntrySize) { |
| name ^= cache.At(i + kNameIndex); |
| if (name.IsNull()) break; // Reached last entry. |
| if (!name.Equals(target_name)) continue; |
| desc ^= cache.At(i + kArgsDescIndex); |
| if (desc.raw() != args_desc.raw()) continue; |
| dispatcher ^= cache.At(i + kFunctionIndex); |
| if (dispatcher.kind() == kind) { |
| // Found match. |
| ASSERT(dispatcher.IsFunction()); |
| break; |
| } |
| } |
| |
| if (dispatcher.IsNull()) { |
| if (i == cache.Length()) { |
| // Allocate new larger cache. |
| intptr_t new_len = (cache.Length() == 0) |
| ? static_cast<intptr_t>(kEntrySize) |
| : cache.Length() * 2; |
| cache ^= Array::Grow(cache, new_len); |
| set_invocation_dispatcher_cache(cache); |
| } |
| dispatcher ^= CreateInvocationDispatcher(target_name, args_desc, kind); |
| cache.SetAt(i + kNameIndex, target_name); |
| cache.SetAt(i + kArgsDescIndex, args_desc); |
| cache.SetAt(i + kFunctionIndex, dispatcher); |
| } |
| return dispatcher.raw(); |
| } |
| |
| |
| RawFunction* Class::CreateInvocationDispatcher(const String& target_name, |
| const Array& args_desc, |
| RawFunction::Kind kind) const { |
| Function& invocation = Function::Handle( |
| Function::New(String::Handle(Symbols::New(target_name)), |
| kind, |
| false, // Not static. |
| false, // Not const. |
| false, // Not abstract. |
| false, // Not external. |
| false, // Not native. |
| *this, |
| 0)); // No token position. |
| ArgumentsDescriptor desc(args_desc); |
| invocation.set_num_fixed_parameters(desc.PositionalCount()); |
| invocation.SetNumOptionalParameters(desc.NamedCount(), |
| false); // Not positional. |
| invocation.set_parameter_types(Array::Handle(Array::New(desc.Count(), |
| Heap::kOld))); |
| invocation.set_parameter_names(Array::Handle(Array::New(desc.Count(), |
| Heap::kOld))); |
| // Receiver. |
| invocation.SetParameterTypeAt(0, Type::Handle(Type::DynamicType())); |
| invocation.SetParameterNameAt(0, Symbols::This()); |
| // Remaining positional parameters. |
| intptr_t i = 1; |
| for (; i < desc.PositionalCount(); i++) { |
| invocation.SetParameterTypeAt(i, Type::Handle(Type::DynamicType())); |
| char name[64]; |
| OS::SNPrint(name, 64, ":p%" Pd, i); |
| invocation.SetParameterNameAt(i, String::Handle(Symbols::New(name))); |
| } |
| |
| // Named parameters. |
| for (; i < desc.Count(); i++) { |
| invocation.SetParameterTypeAt(i, Type::Handle(Type::DynamicType())); |
| intptr_t index = i - desc.PositionalCount(); |
| invocation.SetParameterNameAt(i, String::Handle(desc.NameAt(index))); |
| } |
| invocation.set_result_type(Type::Handle(Type::DynamicType())); |
| invocation.set_is_visible(false); // Not visible in stack trace. |
| invocation.set_saved_args_desc(args_desc); |
| |
| return invocation.raw(); |
| } |
| |
| |
| RawArray* Class::invocation_dispatcher_cache() const { |
| return raw_ptr()->invocation_dispatcher_cache_; |
| } |
| |
| |
| void Class::set_invocation_dispatcher_cache(const Array& cache) const { |
| StorePointer(&raw_ptr()->invocation_dispatcher_cache_, cache.raw()); |
| } |
| |
| |
| void Class::Finalize() const { |
| ASSERT(!is_finalized()); |
| // Prefinalized classes have a VM internal representation and no Dart fields. |
| // Their instance size is precomputed and field offsets are known. |
| if (!is_prefinalized()) { |
| // Compute offsets of instance fields and instance size. |
| CalculateFieldOffsets(); |
| } |
| set_is_finalized(); |
| } |
| |
| |
| // Helper class to handle an array of code weak properties. Implements |
| // registration and disabling of stored code objects. |
| class WeakCodeReferences : public ValueObject { |
| public: |
| explicit WeakCodeReferences(const Array& value) : array_(value) {} |
| virtual ~WeakCodeReferences() {} |
| |
| void Register(const Code& value) { |
| if (!array_.IsNull()) { |
| // Try to find and reuse cleared WeakProperty to avoid allocating new one. |
| WeakProperty& weak_property = WeakProperty::Handle(); |
| for (intptr_t i = 0; i < array_.Length(); i++) { |
| weak_property ^= array_.At(i); |
| if (weak_property.key() == Code::null()) { |
| // Empty property found. Reuse it. |
| weak_property.set_key(value); |
| return; |
| } |
| } |
| } |
| |
| const WeakProperty& weak_property = WeakProperty::Handle( |
| WeakProperty::New(Heap::kOld)); |
| weak_property.set_key(value); |
| |
| intptr_t length = array_.IsNull() ? 0 : array_.Length(); |
| const Array& new_array = Array::Handle( |
| Array::Grow(array_, length + 1, Heap::kOld)); |
| new_array.SetAt(length, weak_property); |
| UpdateArrayTo(new_array); |
| } |
| |
| virtual void UpdateArrayTo(const Array& array) = 0; |
| virtual void ReportDeoptimization(const Code& code) = 0; |
| virtual void ReportSwitchingCode(const Code& code) = 0; |
| |
| static bool IsOptimizedCode(const Array& dependent_code, const Code& code) { |
| if (!code.is_optimized()) { |
| return false; |
| } |
| WeakProperty& weak_property = WeakProperty::Handle(); |
| for (intptr_t i = 0; i < dependent_code.Length(); i++) { |
| weak_property ^= dependent_code.At(i); |
| if (code.raw() == weak_property.key()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void DisableCode() { |
| const Array& code_objects = Array::Handle(array_.raw()); |
| if (code_objects.IsNull()) { |
| return; |
| } |
| UpdateArrayTo(Object::null_array()); |
| // Disable all code on stack. |
| Code& code = Code::Handle(); |
| { |
| DartFrameIterator iterator; |
| StackFrame* frame = iterator.NextFrame(); |
| while (frame != NULL) { |
| code = frame->LookupDartCode(); |
| if (IsOptimizedCode(code_objects, code)) { |
| ReportDeoptimization(code); |
| DeoptimizeAt(code, frame->pc()); |
| } |
| frame = iterator.NextFrame(); |
| } |
| } |
| |
| // Switch functions that use dependent code to unoptimized code. |
| WeakProperty& weak_property = WeakProperty::Handle(); |
| Function& function = Function::Handle(); |
| for (intptr_t i = 0; i < code_objects.Length(); i++) { |
| weak_property ^= code_objects.At(i); |
| code ^= weak_property.key(); |
| if (code.IsNull()) { |
| // Code was garbage collected already. |
| continue; |
| } |
| |
| function ^= code.function(); |
| // If function uses dependent code switch it to unoptimized. |
| if (code.is_optimized() && (function.CurrentCode() == code.raw())) { |
| ReportSwitchingCode(code); |
| function.SwitchToUnoptimizedCode(); |
| } else if (function.unoptimized_code() == code.raw()) { |
| ReportSwitchingCode(code); |
| function.ClearICData(); |
| // Remove the code object from the function. The next time the |
| // function is invoked, it will be compiled again. |
| function.ClearCode(); |
| // Invalidate the old code object so existing references to it |
| // (from optimized code) will fail when invoked. |
| if (!CodePatcher::IsEntryPatched(code)) { |
| CodePatcher::PatchEntry(code); |
| } |
| } else { |
| // Make non-OSR code non-entrant. |
| if (code.GetEntryPatchPc() != 0) { |
| if (!CodePatcher::IsEntryPatched(code)) { |
| ReportSwitchingCode(code); |
| CodePatcher::PatchEntry(code); |
| } |
| } |
| } |
| } |
| } |
| |
| private: |
| const Array& array_; |
| DISALLOW_COPY_AND_ASSIGN(WeakCodeReferences); |
| }; |
| |
| |
| class CHACodeArray : public WeakCodeReferences { |
| public: |
| explicit CHACodeArray(const Class& cls) |
| : WeakCodeReferences(Array::Handle(cls.cha_codes())), cls_(cls) { |
| } |
| |
| virtual void UpdateArrayTo(const Array& value) { |
| cls_.set_cha_codes(value); |
| } |
| |
| virtual void ReportDeoptimization(const Code& code) { |
| if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { |
| Function& function = Function::Handle(code.function()); |
| OS::PrintErr("Deoptimizing %s because CHA optimized (%s).\n", |
| function.ToFullyQualifiedCString(), |
| cls_.ToCString()); |
| } |
| } |
| |
| virtual void ReportSwitchingCode(const Code& code) { |
| if (FLAG_trace_deoptimization || FLAG_trace_deoptimization_verbose) { |
| Function& function = Function::Handle(code.function()); |
| OS::PrintErr("Switching %s to unoptimized code because CHA invalid" |
| " (%s)\n", |
| function.ToFullyQualifiedCString(), |
| cls_.ToCString()); |
| } |
| } |
| |
| private: |
| const Class& cls_; |
| DISALLOW_COPY_AND_ASSIGN(CHACodeArray); |
| }; |
| |
| |
| void Class::RegisterCHACode(const Code& code) { |
| ASSERT(code.is_optimized()); |
| CHACodeArray a(*this); |
| a.Register(code); |
| } |
| |
| |
| void Class::DisableCHAOptimizedCode() { |
| CHACodeArray a(*this); |
| a.DisableCode(); |
| } |
| |
| |
| void Class::set_cha_codes(const Array& cache) const { |
| StorePointer(&raw_ptr()->cha_codes_, cache.raw()); |
| } |
| |
| |
| // Apply the members from the patch class to the original class. |
| bool Class::ApplyPatch(const Class& patch, Error* error) const { |
| ASSERT(error != NULL); |
| ASSERT(!is_finalized()); |
| // Shared handles used during the iteration. |
| String& member_name = String::Handle(); |
| |
| const PatchClass& patch_class = |
| PatchClass::Handle(PatchClass::New(*this, patch)); |
| |
| Array& orig_list = Array::Handle(functions()); |
| intptr_t orig_len = orig_list.Length(); |
| Array& patch_list = Array::Handle(patch.functions()); |
| intptr_t patch_len = patch_list.Length(); |
| |
| // TODO(iposva): Verify that only patching existing methods and adding only |
| // new private methods. |
| Function& func = Function::Handle(); |
| Function& orig_func = Function::Handle(); |
| // Lookup the original implicit constructor, if any. |
| member_name = Name(); |
| member_name = String::Concat(member_name, Symbols::Dot()); |
| Function& orig_implicit_ctor = Function::Handle(LookupFunction(member_name)); |
| if (!orig_implicit_ctor.IsNull() && |
| !orig_implicit_ctor.IsImplicitConstructor()) { |
| // Not an implicit constructor, but a user declared one. |
| orig_implicit_ctor = Function::null(); |
| } |
| const GrowableObjectArray& new_functions = GrowableObjectArray::Handle( |
| GrowableObjectArray::New(orig_len)); |
| for (intptr_t i = 0; i < orig_len; i++) { |
| orig_func ^= orig_list.At(i); |
| member_name ^= orig_func.name(); |
| func = patch.LookupFunction(member_name); |
| if (func.IsNull()) { |
| // Non-patched function is preserved, all patched functions are added in |
| // the loop below. |
| // However, an implicitly created constructor should not be preserved if |
| // the patch provides a constructor or a factory. Wait for now. |
| if (orig_func.raw() != orig_implicit_ctor.raw()) { |
| new_functions.Add(orig_func); |
| } |
| } else if (func.UserVisibleSignature() != |
| orig_func.UserVisibleSignature()) { |
| // Compare user visible signatures to ignore different implicit parameters |
| // when patching a constructor with a factory. |
| *error = LanguageError::NewFormatted( |
| *error, // No previous error. |
| Script::Handle(patch.script()), |
| func.token_pos(), |
| Report::kError, |
| Heap::kNew, |
| "signature mismatch: '%s'", member_name.ToCString()); |
| return false; |
| } |
| } |
| for (intptr_t i = 0; i < patch_len; i++) { |
| func ^= patch_list.At(i); |
| if (func.IsConstructor() || func.IsFactory()) { |
| // Do not preserve the original implicit constructor, if any. |
| orig_implicit_ctor = Function::null(); |
| } |
| func.set_owner(patch_class); |
| new_functions.Add(func); |
| } |
| if (!orig_implicit_ctor.IsNull()) { |
| // Preserve the original implicit constructor. |
| new_functions.Add(orig_implicit_ctor); |
| } |
| Array& new_list = Array::Handle(Array::MakeArray(new_functions)); |
| SetFunctions(new_list); |
| |
| // Merge the two list of fields. Raise an error when duplicates are found or |
| // when a public field is being added. |
| orig_list = fields(); |
| orig_len = orig_list.Length(); |
| patch_list = patch.fields(); |
| patch_len = patch_list.Length(); |
| |
| Field& field = Field::Handle(); |
| Field& orig_field = Field::Handle(); |
| new_list = Array::New(patch_len + orig_len); |
| for (intptr_t i = 0; i < patch_len; i++) { |
| field ^= patch_list.At(i); |
| field.set_owner(*this); |
| member_name = field.name(); |
| // TODO(iposva): Verify non-public fields only. |
| |
| // Verify no duplicate additions. |
| orig_field ^= LookupField(member_name); |
| if (!orig_field.IsNull()) { |
| *error = LanguageError::NewFormatted( |
| *error, // No previous error. |
| Script::Handle(patch.script()), |
| field.token_pos(), |
| Report::kError, |
| Heap::kNew, |
| "duplicate field: %s", member_name.ToCString()); |
| return false; |
| } |
| new_list.SetAt(i, field); |
| } |
| for (intptr_t i = 0; i < orig_len; i++) { |
| field ^= orig_list.At(i); |
| new_list.SetAt(patch_len + i, field); |
| } |
| SetFields(new_list); |
| |
| // The functions and fields in the patch class are no longer needed. |
| patch.SetFunctions(Object::empty_array()); |
| patch.SetFields(Object::empty_array()); |
| return true; |
| } |
| |
| |
| static RawString* BuildClosureSource(const Array& formal_params, |
| const String& expr) { |
| const GrowableObjectArray& src_pieces = |
| GrowableObjectArray::Handle(GrowableObjectArray::New()); |
| String& piece = String::Handle(); |
| src_pieces.Add(Symbols::LParen()); |
| // Add formal parameters. |
| intptr_t num_formals = formal_params.Length(); |
| for (intptr_t i = 0; i < num_formals; i++) { |
| if (i > 0) { |
| src_pieces.Add(Symbols::CommaSpace()); |
| } |
| piece ^= formal_params.At(i); |
| src_pieces.Add(piece); |
| } |
| src_pieces.Add(Symbols::RParenArrow()); |
| src_pieces.Add(expr); |
| src_pieces.Add(Symbols::Semicolon()); |
| return String::ConcatAll(Array::Handle(Array::MakeArray(src_pieces))); |
| } |
| |
| |
| static RawFunction* EvaluateHelper(const Class& cls, |
| const String& expr, |
| const Array& param_names, |
| bool is_static) { |
| const String& func_src = |
| String::Handle(BuildClosureSource(param_names, expr)); |
| Script& script = Script::Handle(); |
| script = Script::New(Symbols::Empty(), func_src, RawScript::kSourceTag); |
| // In order to tokenize the source, we need to get the key to mangle |
| // private names from the library from which the class originates. |
| const Library& lib = Library::Handle(cls.library()); |
| ASSERT(!lib.IsNull()); |
| const String& lib_key = String::Handle(lib.private_key()); |
| script.Tokenize(lib_key); |
| |
| const Function& func = Function::Handle( |
| Function::NewEvalFunction(cls, script, is_static)); |
| func.set_result_type(Type::Handle(Type::DynamicType())); |
| const intptr_t num_implicit_params = is_static ? 0 : 1; |
| func.set_num_fixed_parameters(num_implicit_params + param_names.Length()); |
| func.SetNumOptionalParameters(0, true); |
| func.SetIsOptimizable(false); |
| return func.raw(); |
| } |
| |
| |
| RawObject* Class::Evaluate(const String& expr, |
| const Array& param_names, |
| const Array& param_values) const { |
| const Function& eval_func = |
| Function::Handle(EvaluateHelper(*this, expr, param_names, true)); |
|