| // 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/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/growable_array.h" |
| #include "vm/heap.h" |
| #include "vm/intermediate_language.h" |
| #include "vm/intrinsifier.h" |
| #include "vm/longjump.h" |
| #include "vm/object_id_ring.h" |
| #include "vm/object_store.h" |
| #include "vm/parser.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/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, 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."); |
| DECLARE_FLAG(bool, eliminate_type_checks); |
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, error_on_bad_override); |
| DECLARE_FLAG(bool, error_on_bad_type); |
| 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); |
| |
| 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; |
| AbstractTypeArguments* Object::null_abstract_type_arguments_ = NULL; |
| Array* Object::empty_array_ = 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::instantiated_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::library_prefix_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); |
| #undef 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) |
| #define INVISIBLE_LIST(V) \ |
| V(CoreLibrary, Object, _noSuchMethod) \ |
| V(CoreLibrary, Object, _as) \ |
| V(CoreLibrary, Object, _instanceOf) \ |
| V(CoreLibrary, _ObjectArray, _ObjectArray.) \ |
| V(CoreLibrary, AssertionError, _throwNew) \ |
| V(CoreLibrary, TypeError, _throwNew) \ |
| V(CoreLibrary, FallThroughError, _throwNew) \ |
| V(CoreLibrary, AbstractClassInstantiationError, _throwNew) \ |
| V(CoreLibrary, int, _throwFormatException) \ |
| V(CoreLibrary, int, _parse) \ |
| V(CoreLibrary, StackTrace, _setupFullStackTrace) \ |
| V(CoreLibrary, _OneByteString, _setAt) \ |
| V(TypedDataLibrary, _TypedList, _getInt8) \ |
| V(TypedDataLibrary, _TypedList, _setInt8) \ |
| V(TypedDataLibrary, _TypedList, _getUint8) \ |
| V(TypedDataLibrary, _TypedList, _setUint8) \ |
| V(TypedDataLibrary, _TypedList, _getInt16) \ |
| V(TypedDataLibrary, _TypedList, _setInt16) \ |
| V(TypedDataLibrary, _TypedList, _getUint16) \ |
| V(TypedDataLibrary, _TypedList, _setUint16) \ |
| V(TypedDataLibrary, _TypedList, _getInt32) \ |
| V(TypedDataLibrary, _TypedList, _setInt32) \ |
| V(TypedDataLibrary, _TypedList, _getUint32) \ |
| V(TypedDataLibrary, _TypedList, _setUint32) \ |
| V(TypedDataLibrary, _TypedList, _getInt64) \ |
| V(TypedDataLibrary, _TypedList, _setInt64) \ |
| V(TypedDataLibrary, _TypedList, _getUint64) \ |
| V(TypedDataLibrary, _TypedList, _setUint64) \ |
| V(TypedDataLibrary, _TypedList, _getFloat32) \ |
| V(TypedDataLibrary, _TypedList, _setFloat32) \ |
| V(TypedDataLibrary, _TypedList, _getFloat64) \ |
| V(TypedDataLibrary, _TypedList, _setFloat64) \ |
| V(TypedDataLibrary, _TypedList, _getFloat32x4) \ |
| V(TypedDataLibrary, _TypedList, _setFloat32x4) \ |
| |
| |
| |
| 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 |
| // |
| static RawString* 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) == '@') { |
| // 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. |
| i++; // Skip the '@'. |
| while ((i < name.Length()) && |
| (name.CharAt(i) != '&') && |
| (name.CharAt(i) != '.')) { |
| 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) == ':') { |
| 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) == '.') { |
| 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(); |
| } |
| |
| |
| template<typename type> |
| static bool IsSpecialCharacter(type value) { |
| return ((value == '"') || |
| (value == '\n') || |
| (value == '\f') || |
| (value == '\b') || |
| (value == '\t') || |
| (value == '\v') || |
| (value == '\r') || |
| (value == '\\') || |
| (value == '$')); |
| } |
| |
| |
| 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'; |
| } |
| |
| |
| static void DeleteWeakPersistentHandle(Dart_WeakPersistentHandle handle) { |
| ApiState* state = Isolate::Current()->api_state(); |
| ASSERT(state != NULL); |
| FinalizablePersistentHandle* weak_ref = |
| reinterpret_cast<FinalizablePersistentHandle*>(handle); |
| ASSERT(state->IsValidWeakPersistentHandle(handle)); |
| state->weak_persistent_handles().FreeHandle(weak_ref); |
| } |
| |
| |
| 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_abstract_type_arguments_ = AbstractTypeArguments::ReadOnlyHandle(); |
| empty_array_ = Array::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_abstract_type_arguments_ = AbstractTypeArguments::null(); |
| |
| // Initialize the empty array handle to null_ in order to be able to check |
| // if the empty array was allocated (RAW_NULL is not available). |
| *empty_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::InstanceSize()); |
| cls.set_id(Class::kClassId); |
| cls.raw_ptr()->state_bits_ = 0; |
| cls.set_is_finalized(); |
| cls.set_is_type_finalized(); |
| cls.raw_ptr()->type_arguments_field_offset_in_words_ = |
| Class::kNoTypeArguments; |
| cls.raw_ptr()->num_native_fields_ = 0; |
| cls.InitEmptyFields(); |
| isolate->RegisterClass(cls); |
| } |
| |
| // Allocate and initialize the null class. |
| cls = Class::New<Instance>(kNullCid); |
| isolate->object_store()->set_null_class(cls); |
| |
| // Allocate and initialize the free list element class. |
| cls = Class::New<FreeListElement::FakeInstance>(kFreeListElement); |
| 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<InstantiatedTypeArguments>(); |
| instantiated_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<LibraryPrefix>(); |
| library_prefix_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 = Class::New<Array>(kImmutableArrayCid); |
| isolate->object_store()->set_immutable_array_class(cls); |
| cls.set_type_arguments_field_offset(Array::type_arguments_offset()); |
| 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); |
| } |
| |
| cls = Class::New<Instance>(kDynamicCid); |
| cls.set_is_finalized(); |
| cls.set_is_type_finalized(); |
| cls.set_is_abstract(); |
| dynamic_class_ = cls.raw(); |
| |
| cls = Class::New<Instance>(kVoidCid); |
| cls.set_is_finalized(); |
| cls.set_is_type_finalized(); |
| void_class_ = cls.raw(); |
| |
| cls = Class::New<Type>(); |
| cls.set_is_finalized(); |
| cls.set_is_type_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"); |
| *snapshot_writer_error_ = LanguageError::New(error_str); |
| error_str = String::New("Branch offset overflow"); |
| *branch_offset_error_ = LanguageError::New(error_str); |
| |
| 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_abstract_type_arguments_->IsSmi()); |
| ASSERT(null_abstract_type_arguments_->IsAbstractTypeArguments()); |
| ASSERT(!empty_array_->IsSmi()); |
| ASSERT(empty_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(instantiated_type_arguments, InstantiatedTypeArguments); |
| 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(library_prefix, LibraryPrefix); |
| 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::ObjectArray()); |
| cls = Dart::vm_isolate()->object_store()->one_byte_string_class(); |
| cls.set_name(Symbols::OneByteString()); |
| } |
| |
| |
| void Object::CreateInternalMetaData() { |
| // Initialize meta data for VM internal classes. |
| Class& cls = Class::Handle(); |
| Array& fields = Array::Handle(); |
| Field& fld = Field::Handle(); |
| String& name = String::Handle(); |
| |
| // TODO(iposva): Add more of the VM classes here. |
| cls = context_class_; |
| fields = Array::New(1); |
| name = Symbols::New("@parent_"); |
| fld = Field::New(name, false, false, false, cls, 0); |
| fields.SetAt(0, fld); |
| cls.SetFields(fields); |
| } |
| |
| |
| // 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_); |
| } |
| } |
| #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(time_bootstrap); |
| ObjectStore* object_store = isolate->object_store(); |
| |
| Class& cls = Class::Handle(); |
| Type& type = Type::Handle(); |
| Array& array = Array::Handle(); |
| Library& lib = Library::Handle(); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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(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<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); |
| |
| // 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(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. |
| Library& core_lib = Library::Handle(Library::CoreLibrary()); |
| ASSERT(!core_lib.IsNull()); |
| |
| const GrowableObjectArray& pending_classes = |
| GrowableObjectArray::Handle(GrowableObjectArray::New()); |
| object_store->set_pending_classes(pending_classes); |
| |
| Context& context = Context::Handle(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(); |
| cls = object_store->array_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::ObjectArray(), core_lib); |
| pending_classes.Add(cls); |
| // We cannot use NewNonParameterizedType(cls), because Array is parameterized. |
| type ^= Type::New(Object::Handle(cls.raw()), |
| TypeArguments::Handle(), |
| Scanner::kDummyTokenIndex); |
| type.SetIsFinalized(); |
| type ^= type.Canonicalize(); |
| object_store->set_array_type(type); |
| |
| cls = object_store->growable_object_array_class(); // Was allocated above. |
| RegisterPrivateClass(cls, Symbols::GrowableObjectArray(), 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()); |
| ASSERT(object_store->immutable_array_class() != object_store->array_class()); |
| cls.set_is_prefinalized(); |
| RegisterPrivateClass(cls, Symbols::ImmutableArray(), 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); |
| |
| 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>(); |
| object_store->set_jsregexp_class(cls); |
| 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_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); |
| cls.set_is_prefinalized(); |
| object_store->set_null_class(cls); |
| RegisterClass(cls, Symbols::Null(), 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_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_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.Register(); |
| isolate->object_store()->set_bootstrap_library(ObjectStore::kMirrors, |
| lib); |
| } |
| ASSERT(!lib.IsNull()); |
| ASSERT(lib.raw() == Library::MirrorsLibrary()); |
| |
| cls = Class::New<MirrorReference>(); |
| object_store->set_mirror_reference_class(cls); |
| RegisterPrivateClass(cls, Symbols::_MirrorReference(), lib); |
| |
| // 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.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 Uint32x4 in the object store. |
| cls = Class::New<Float32x4>(); |
| object_store->set_float32x4_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Float32x4(), lib); |
| cls = Class::New<Uint32x4>(); |
| object_store->set_uint32x4_class(cls); |
| RegisterPrivateClass(cls, Symbols::_Uint32x4(), lib); |
| |
| cls = Class::New<Instance>(kIllegalCid); |
| RegisterClass(cls, Symbols::Float32x4(), lib); |
| 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::Uint32x4(), lib); |
| cls.set_is_prefinalized(); |
| pending_classes.Add(cls); |
| type = Type::NewNonParameterizedType(cls); |
| object_store->set_uint32x4_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_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_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_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_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 scalar list). |
| Intrinsifier::InitializeState(); |
| |
| return Error::null(); |
| } |
| |
| |
| void Object::InitFromSnapshot(Isolate* isolate) { |
| TIMERSCOPE(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<Type>(); |
| object_store->set_type_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<Uint32x4>(); |
| object_store->set_uint32x4_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<Stacktrace>(); |
| object_store->set_stacktrace_class(cls); |
| |
| cls = Class::New<JSRegExp>(); |
| object_store->set_jsregexp_class(cls); |
| |
| // 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>(); |
| object_store->set_mirror_reference_class(cls); |
| } |
| |
| |
| void Object::Print() const { |
| OS::Print("%s\n", ToCString()); |
| } |
| |
| |
| 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(exception); |
| UNREACHABLE(); |
| } |
| 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 { |
| ASSERT(raw_ptr()->name_ != String::null()); |
| return raw_ptr()->name_; |
| } |
| |
| |
| RawString* Class::UserVisibleName() const { |
| if (FLAG_show_internal_names) { |
| return Name(); |
| } |
| switch (id()) { |
| case kIntegerCid: |
| case kSmiCid: |
| case kMintCid: |
| case kBigintCid: |
| return Symbols::Int().raw(); |
| case kDoubleCid: |
| return Symbols::Double().raw(); |
| case kOneByteStringCid: |
| case kTwoByteStringCid: |
| case kExternalOneByteStringCid: |
| case kExternalTwoByteStringCid: |
| return Symbols::New("String"); |
| case kArrayCid: |
| case kImmutableArrayCid: |
| case kGrowableObjectArrayCid: |
| return Symbols::List().raw(); |
| case kFloat32x4Cid: |
| return Symbols::Float32x4().raw(); |
| case kUint32x4Cid: |
| return Symbols::Uint32x4().raw(); |
| case kTypedDataInt8ArrayCid: |
| case kExternalTypedDataInt8ArrayCid: |
| return Symbols::Int8List().raw(); |
| case kTypedDataUint8ArrayCid: |
| case kExternalTypedDataUint8ArrayCid: |
| return Symbols::Uint8List().raw(); |
| case kTypedDataUint8ClampedArrayCid: |
| case kExternalTypedDataUint8ClampedArrayCid: |
| return Symbols::Uint8ClampedList().raw(); |
| case kTypedDataInt16ArrayCid: |
| case kExternalTypedDataInt16ArrayCid: |
| return Symbols::Int16List().raw(); |
| case kTypedDataUint16ArrayCid: |
| case kExternalTypedDataUint16ArrayCid: |
| return Symbols::Uint16List().raw(); |
| case kTypedDataInt32ArrayCid: |
| case kExternalTypedDataInt32ArrayCid: |
| return Symbols::Int32List().raw(); |
| case kTypedDataUint32ArrayCid: |
| case kExternalTypedDataUint32ArrayCid: |
| return Symbols::Uint32List().raw(); |
| case kTypedDataInt64ArrayCid: |
| case kExternalTypedDataInt64ArrayCid: |
| return Symbols::Int64List().raw(); |
| case kTypedDataUint64ArrayCid: |
| case kExternalTypedDataUint64ArrayCid: |
| return Symbols::Uint64List().raw(); |
| case kTypedDataFloat32x4ArrayCid: |
| case kExternalTypedDataFloat32x4ArrayCid: |
| return Symbols::Float32x4List().raw(); |
| case kTypedDataFloat32ArrayCid: |
| case kExternalTypedDataFloat32ArrayCid: |
| return Symbols::Float32List().raw(); |
| case kTypedDataFloat64ArrayCid: |
| case kExternalTypedDataFloat64ArrayCid: |
| return Symbols::Float64List().raw(); |
| default: |
| if (!IsSignatureClass()) { |
| const String& name = String::Handle(Name()); |
| return IdentifierPrettyName(name); |
| } else { |
| return Name(); |
| } |
| } |
| UNREACHABLE(); |
| } |
| |
| |
| 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. |
| const Array& signature_types = Array::Handle(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(); |
| } |
| |
| |
| RawType* Class::RareType() const { |
| const Type& type = Type::Handle(Type::New( |
| *this, |
| Object::null_abstract_type_arguments(), |
| Scanner::kDummyTokenIndex)); |
| return Type::RawCast( |
| 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::InstanceSize()); |
| ASSERT((FakeObject::kClassId != kInstanceCid)); |
| result.set_id(FakeObject::kClassId); |
| result.raw_ptr()->state_bits_ = 0; |
| // VM backed classes are almost ready: run checks and resolve class |
| // references, but do not recompute size. |
| result.set_is_prefinalized(); |
| result.raw_ptr()->type_arguments_field_offset_in_words_ = kNoTypeArguments; |
| result.raw_ptr()->num_native_fields_ = 0; |
| result.raw_ptr()->token_pos_ = Scanner::kDummyTokenIndex; |
| result.InitEmptyFields(); |
| Isolate::Current()->RegisterClass(result); |
| return result.raw(); |
| } |
| |
| |
| // 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()->canonical_types_, 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()); |
| } |
| |
| |
| 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); |
| } |
| |
| |
| 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()); |
| 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(); |
| } |
| |
| |
| 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() const { |
| if (IsMixinApplication() && !is_mixin_type_applied()) { |
| ClassFinalizer::ApplyMixinType(*this); |
| } |
| if (type_parameters() == TypeArguments::null()) { |
| return 0; |
| } |
| const TypeArguments& type_params = TypeArguments::Handle(type_parameters()); |
| return type_params.Length(); |
| } |
| |
| |
| intptr_t Class::NumTypeArguments() const { |
| // 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); |
| TypeArguments& type_params = TypeArguments::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 NumTypeParameters() on a mixin application class will setup the |
| // type parameters if not already done. |
| if (cls.NumTypeParameters() > 0) { |
| type_params ^= cls.type_parameters(); |
| num_type_args += type_params.Length(); |
| } |
| // 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(); |
| cls = sup_type.type_class(); |
| } while (true); |
| return num_type_args; |
| } |
| |
| |
| bool Class::HasTypeArguments() const { |
| if (!IsSignatureClass() && (is_finalized() || is_prefinalized())) { |
| // More efficient than calling NumTypeArguments(). |
| return type_arguments_field_offset() != kNoTypeArguments; |
| } else { |
| // No need to check NumTypeArguments() if class has type parameters. |
| return (NumTypeParameters() > 0) || (NumTypeArguments() > 0); |
| } |
| } |
| |
| |
| 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.IsBoundedType() || |
| 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(); |
| ReusableHandleScope reused_handles(isolate); |
| TypeArguments& type_params = reused_handles.TypeArgumentsHandle(); |
| type_params ^= type_parameters(); |
| TypeParameter& type_param = reused_handles.TypeParameterHandle(); |
| String& type_param_name = reused_handles.StringHandle(); |
| 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 = sizeof(RawObject); |
| } 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. |
| *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(); |
| } |
| |
| |
| static RawError* FormatError(const Error& prev_error, |
| const Script& script, |
| intptr_t token_pos, |
| const char* format, ...) { |
| va_list args; |
| va_start(args, format); |
| if (prev_error.IsNull()) { |
| return Parser::FormatError(script, token_pos, "Error", format, args); |
| } else { |
| return Parser::FormatErrorWithAppend(prev_error, script, token_pos, |
| "Error", format, args); |
| } |
| } |
| |
| |
| // 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 = FormatError(*error, // No previous error. |
| Script::Handle(patch.script()), func.token_pos(), |
| "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 = FormatError(*error, // No previous error. |
| Script::Handle(patch.script()), field.token_pos(), |
| "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 RawPatchClass* MakeTempPatchClass(const Class& cls, |
| const String& expr) { |
| String& src = String::Handle(String::New("() => ")); |
| src = String::Concat(src, expr); |
| src = String::Concat(src, Symbols::Semicolon()); |
| Script& script = Script::Handle(); |
| script = Script::New(Symbols::Empty(), 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 object's 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 String& src_class_name = String::Handle(Symbols::New(":internal")); |
| const Class& src_class = Class::Handle( |
| Class::New(src_class_name, script, Scanner::kDummyTokenIndex)); |
| src_class.set_is_finalized(); |
| src_class.set_library(lib); |
| return PatchClass::New(cls, src_class); |
| } |
| |
| |
| RawObject* Class::Evaluate(const String& expr) const { |
| const PatchClass& temp_class = |
| PatchClass::Handle(MakeTempPatchClass(*this, expr)); |
| const String& eval_func_name = String::Handle(Symbols::New(":eval")); |
| const Function& eval_func = |
| Function::Handle(Function::New(eval_func_name, |
| RawFunction::kRegularFunction, |
| true, // Static. |
| false, // Not const. |
| false, // Not abstract. |
| false, // Not external. |
| temp_class, |
| 0)); |
| eval_func.set_result_type(Type::Handle(Type::DynamicType())); |
| eval_func.set_num_fixed_parameters(0); |
| eval_func.SetNumOptionalParameters(0, true); |
| eval_func.set_is_optimizable(false); |
| |
| const Array& args = Array::Handle(Array::New(0)); |
| const Object& result = |
| Object::Handle(DartEntry::InvokeFunction(eval_func, args)); |
| return result.raw(); |
| } |
| |
| |
| // Ensure that top level parsing of the class has been done. |
| RawError* Class::EnsureIsFinalized(Isolate* isolate) const { |
| // Finalized classes have already been parsed. |
| if (is_finalized()) { |
| return Error::null(); |
| } |
| ASSERT(isolate != NULL); |
| const Error& error = Error::Handle(isolate, Compiler::CompileClass(*this)); |
| if (!error.IsNull() && (isolate->long_jump_base() != NULL)) { |
| isolate->long_jump_base()->Jump(1, error); |
| UNREACHABLE(); |
| } |
| return error.raw(); |
| } |
| |
| |
| void Class::SetFields(const Array& value) const { |
| ASSERT(!value.IsNull()); |
| #if defined(DEBUG) |
| // Verify that all the fields in the array have this class as owner. |
| Field& field = Field::Handle(); |
| intptr_t len = value.Length(); |
| for (intptr_t i = 0; i < len; i++) { |
| field ^= value.At(i); |
| ASSERT(field.owner() == raw()); |
| } |
| #endif |
| // The value of static fields is already initialized to null. |
| StorePointer(&raw_ptr()->fields_, value.raw()); |
| } |
| |
| |
| template <class FakeInstance> |
| RawClass* Class::New(intptr_t index) { |
| 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; |
| } |
| FakeInstance fake; |
| ASSERT(fake.IsInstance()); |
| result.set_handle_vtable(fake.vtable()); |
| result.set_instance_size(FakeInstance::InstanceSize()); |
| result.set_next_field_offset(FakeInstance::InstanceSize()); |
| result.set_id(index); |
| result.raw_ptr()->state_bits_ = 0; |
| result.raw_ptr()->type_arguments_field_offset_in_words_ = kNoTypeArguments; |
| result.raw_ptr()->num_native_fields_ = 0; |
| result.raw_ptr()->token_pos_ = Scanner::kDummyTokenIndex; |
| result.InitEmptyFields(); |
| Isolate::Current()->RegisterClass(result); |
| return result.raw(); |
| } |
| |
| |
| RawClass* Class::New(const String& name, |
| const Script& script, |
| intptr_t token_pos) { |
| Class& result = Class::Handle(New<Instance>(kIllegalCid)); |
| result.set_name(name); |
| result.set_script(script); |
| result.set_token_pos(token_pos); |
| return result.raw(); |
| } |
| |
| |
| RawClass* Class::NewSignatureClass(const String& name, |
| const Function& signature_function, |
| const Script& script, |
| intptr_t token_pos) { |
| const Class& result = Class::Handle(New(name, script, token_pos)); |
| // Instances of a signature class can only be closures. |
| result.set_instance_size(Closure::InstanceSize()); |
| result.set_next_field_offset(Closure::InstanceSize()); |
| // Signature classes extend the _FunctionImpl class. |
| result.set_super_type(Type::Handle( |
| Isolate::Current()->object_store()->function_impl_type())); |
| result.set_is_synthesized_class(); |
| result.set_type_arguments_field_offset(Closure::type_arguments_offset()); |
| if (!signature_function.IsNull()) { |
| result.PatchSignatureFunction(signature_function); |
| } |
| return result.raw(); |
| } |
| |
| |
| void Class::PatchSignatureFunction(const Function& signature_function) const { |
| ASSERT(!signature_function.IsNull()); |
| const Class& owner_class = Class::Handle(signature_function.Owner()); |
| ASSERT(!owner_class.IsNull()); |
| TypeArguments& type_parameters = TypeArguments::Handle(); |
| // 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. |
| // In case of a function type alias, the function owner is the alias class |
| // instead of the enclosing class. |
| if (!signature_function.is_static() && |
| (owner_class.NumTypeParameters() > 0) && |
| !signature_function.HasInstantiatedSignature()) { |
| type_parameters = owner_class.type_parameters(); |
| } |
| set_signature_function(signature_function); |
| set_type_parameters(type_parameters); |
| if (owner_class.raw() == raw()) { |
| // This signature class is an alias, which cannot be the canonical |
| // signature class for this signature function. |
| ASSERT(!IsCanonicalSignatureClass()); |
| } else if (signature_function.signature_class() == Object::null()) { |
| // Make this signature class the canonical signature class. |
| signature_function.set_signature_class(*this); |
| ASSERT(IsCanonicalSignatureClass()); |
| } |
| set_is_prefinalized(); |
| } |
| |
| |
| RawClass* Class::NewNativeWrapper(const Library& library, |
| const String& name, |
| int field_count) { |
| Class& cls = Class::Handle(library.LookupClass(name)); |
| if (cls.IsNull()) { |
| cls = New(name, Script::Handle(), Scanner::kDummyTokenIndex); |
| cls.SetFields(Object::empty_array()); |
| cls.SetFunctions(Object::empty_array()); |
| // Set super class to Object. |
| cls.set_super_type(Type::Handle(Type::ObjectType())); |
| // Compute instance size. First word contains a pointer to a properly |
| // sized typed array once the first native field has been set. |
| intptr_t instance_size = sizeof(RawObject) + kWordSize; |
| cls.set_instance_size(RoundedAllocationSize(instance_size)); |
| cls.set_next_field_offset(instance_size); |
| cls.set_num_native_fields(field_count); |
| cls.set_is_finalized(); |
| cls.set_is_type_finalized(); |
| library.AddClass(cls); |
| return cls.raw(); |
| } else { |
| return Class::null(); |
| } |
| } |
| |
| |
| RawClass* Class::NewStringClass(intptr_t class_id) { |
| intptr_t instance_size; |
| if (class_id == kOneByteStringCid) { |
| instance_size = OneByteString::InstanceSize(); |
| } else if (class_id == kTwoByteStringCid) { |
| instance_size = TwoByteString::InstanceSize(); |
| } else if (class_id == kExternalOneByteStringCid) { |
| instance_size = ExternalOneByteString::InstanceSize(); |
| } else { |
| ASSERT(class_id == kExternalTwoByteStringCid); |
| instance_size = ExternalTwoByteString::InstanceSize(); |
| } |
| Class& result = Class::Handle(New<String>(class_id)); |
| result.set_instance_size(instance_size); |
| result.set_next_field_offset(instance_size); |
| result.set_is_prefinalized(); |
| return result.raw(); |
| } |
| |
| |
| RawClass* Class::NewTypedDataClass(intptr_t class_id) { |
| ASSERT(RawObject::IsTypedDataClassId(class_id)); |
| intptr_t instance_size = TypedData::InstanceSize(); |
| Class& result = Class::Handle(New<TypedData>(class_id)); |
| result.set_instance_size(instance_size); |
| result.set_next_field_offset(instance_size); |
| result.set_is_prefinalized(); |
| return result.raw(); |
| } |
| |
| |
| RawClass* Class::NewTypedDataViewClass(intptr_t class_id) { |
| ASSERT(RawObject::IsTypedDataViewClassId(class_id)); |
| Class& result = Class::Handle(New<Instance>(class_id)); |
| result.set_instance_size(0); |
| result.set_next_field_offset(0); |
| return result.raw(); |
| } |
| |
| |
| RawClass* Class::NewExternalTypedDataClass(intptr_t class_id) { |
| ASSERT(RawObject::IsExternalTypedDataClassId(class_id)); |
| intptr_t instance_size = ExternalTypedData::InstanceSize(); |
| Class& result = Class::Handle(New<ExternalTypedData>(class_id)); |
| result.set_instance_size(instance_size); |
| result.set_next_field_offset(instance_size); |
| result.set_is_prefinalized(); |
| return result.raw(); |
| } |
| |
| |
| void Class::set_name(const String& value) const { |
| ASSERT(value.IsSymbol()); |
| StorePointer(&raw_ptr()->name_, value.raw()); |
| } |
| |
| |
| void Class::set_script(const Script& value) const { |
| StorePointer(&raw_ptr()->script_, value.raw()); |
| } |
| |
| |
| void Class::set_token_pos(intptr_t token_pos) const { |
| ASSERT(token_pos >= 0); |
| raw_ptr()->token_pos_ = token_pos; |
| } |
| |
| |
| void Class::set_is_implemented() const { |
| set_state_bits(ImplementedBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_abstract() const { |
| set_state_bits(AbstractBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_type_finalized() const { |
| set_state_bits(TypeFinalizedBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_patch() const { |
| set_state_bits(PatchBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_synthesized_class() const { |
| set_state_bits(SynthesizedClassBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_const() const { |
| set_state_bits(ConstBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_mixin_typedef() const { |
| set_state_bits(MixinTypedefBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_mixin_type_applied() const { |
| set_state_bits(MixinTypeAppliedBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_finalized() const { |
| ASSERT(!is_finalized()); |
| set_state_bits(ClassFinalizedBits::update(RawClass::kFinalized, |
| raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_prefinalized() const { |
| ASSERT(!is_finalized()); |
| set_state_bits(ClassFinalizedBits::update(RawClass::kPreFinalized, |
| raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_is_marked_for_parsing() const { |
| set_state_bits(MarkedForParsingBit::update(true, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::reset_is_marked_for_parsing() const { |
| set_state_bits(MarkedForParsingBit::update(false, raw_ptr()->state_bits_)); |
| } |
| |
| |
| void Class::set_interfaces(const Array& value) const { |
| // Verification and resolving of interfaces occurs in finalizer. |
| ASSERT(!value.IsNull()); |
| StorePointer(&raw_ptr()->interfaces_, value.raw()); |
| } |
| |
| |
| void Class::set_mixin(const Type& value) const { |
| // Resolution and application of mixin type occurs in finalizer. |
| ASSERT(!value.IsNull()); |
| StorePointer(&raw_ptr()->mixin_, value.raw()); |
| } |
| |
| |
| bool Class::IsMixinApplication() const { |
| return mixin() != Type::null(); |
| } |
| |
| |
| void Class::set_patch_class(const Class& cls) const { |
| ASSERT(patch_class() == Class::null()); |
| StorePointer(&raw_ptr()->patch_class_, cls.raw()); |
| } |
| |
| |
| void Class::AddDirectSubclass(const Class& subclass) const { |
| ASSERT(!subclass.IsNull()); |
| ASSERT(subclass.SuperClass() == raw()); |
| // Do not keep track of the direct subclasses of class Object. |
| ASSERT(!IsObjectClass()); |
| GrowableObjectArray& direct_subclasses = |
| GrowableObjectArray::Handle(raw_ptr()->direct_subclasses_); |
| if (direct_subclasses.IsNull()) { |
| direct_subclasses = GrowableObjectArray::New(4, Heap::kOld); |
| StorePointer(&raw_ptr()->direct_subclasses_, direct_subclasses.raw()); |
| } |
| #if defined(DEBUG) |
| // Verify that the same class is not added twice. |
| for (intptr_t i = 0; i < direct_subclasses.Length(); i++) { |
| ASSERT(direct_subclasses.At(i) != subclass.raw()); |
| } |
| #endif |
| direct_subclasses.Add(subclass); |
| } |
| |
| |
| RawArray* Class::constants() const { |
| return raw_ptr()->constants_; |
| } |
| |
| void Class::set_constants(const Array& value) const { |
| ASSERT(!value.IsNull()); |
| StorePointer(&raw_ptr()->constants_, value.raw()); |
| } |
| |
| |
| RawArray* Class::canonical_types() const { |
| return raw_ptr()->canonical_types_; |
| } |
| |
| void Class::set_canonical_types(const Array& value) const { |
| ASSERT(!value.IsNull()); |
| StorePointer(&raw_ptr()->canonical_types_, value.raw()); |
| } |
| |
| |
| void Class::set_allocation_stub(const Code& value) const { |
| ASSERT(!value.IsNull()); |
| ASSERT(raw_ptr()->allocation_stub_ == Code::null()); |
| StorePointer(&raw_ptr()->allocation_stub_, value.raw()); |
| } |
| |
| |
| bool Class::IsFunctionClass() const { |
| return raw() == Type::Handle(Type::Function()).type_class(); |
| } |
| |
| |
| bool Class::IsCanonicalSignatureClass() const { |
| const Function& function = Function::Handle(signature_function()); |
| return (!function.IsNull() && (function.signature_class() == raw())); |
| } |
| |
| |
| // If test_kind == kIsSubtypeOf, checks if type S is a subtype of type T. |
| // If test_kind == kIsMoreSpecificThan, checks if S is more specific than T. |
| // Type S is specified by this class parameterized with 'type_arguments', and |
| // type T by class 'other' parameterized with 'other_type_arguments'. |
| // This class and class 'other' do not need to be finalized, however, they must |
| // be resolved as well as their interfaces. |
| bool Class::TypeTest( |
| TypeTestKind test_kind, |
| const AbstractTypeArguments& type_arguments, |
| const Class& other, |
| const AbstractTypeArguments& other_type_arguments, |
| Error* bound_error) const { |
| ASSERT(!IsVoidClass()); |
| // Check for DynamicType. |
| // Each occurrence of DynamicType in type T is interpreted as the dynamic |
| // type, a supertype of all types. |
| if (other.IsDynamicClass()) { |
| return true; |
| } |
| // In the case of a subtype test, each occurrence of DynamicType in type S is |
| // interpreted as the bottom type, a subtype of all types. |
| // However, DynamicType is not more specific than any type. |
| if (IsDynamicClass()) { |
| return test_kind == kIsSubtypeOf; |
| } |
| // Check for NullType, which is only a subtype of ObjectType, of DynamicType, |
| // or of itself, and which is more specific than any type. |
| if (IsNullClass()) { |
| // We already checked for other.IsDynamicClass() above. |
| return (test_kind == kIsMoreSpecificThan) || |
| other.IsObjectClass() || other.IsNullClass(); |
| } |
| // Check for ObjectType. Any type that is not NullType or DynamicType (already |
| // checked above), is more specific than ObjectType. |
| if (other.IsObjectClass()) { |
| return true; |
| } |
| // Check for reflexivity. |
| if (raw() == other.raw()) { |
| const intptr_t len = NumTypeArguments(); |
| if (len == 0) { |
| return true; |
| } |
| // Since we do not truncate the type argument vector of a subclass (see |
| // below), we only check a prefix of the proper length. |
| // Check for covariance. |
| if (other_type_arguments.IsNull() || other_type_arguments.IsRaw(len)) { |
| return true; |
| } |
| if (type_arguments.IsNull() || type_arguments.IsRaw(len)) { |
| // Other type can't be more specific than this one because for that |
| // it would have to have all dynamic type arguments which is checked |
| // above. |
| return test_kind == kIsSubtypeOf; |
| } |
| return type_arguments.TypeTest(test_kind, |
| other_type_arguments, |
| len, |
| bound_error); |
| } |
| const bool other_is_function_class = other.IsFunctionClass(); |
| if (other.IsSignatureClass() || other_is_function_class) { |
| const Function& other_fun = Function::Handle(other.signature_function()); |
| if (IsSignatureClass()) { |
| if (other_is_function_class) { |
| return true; |
| } |
| // Check for two function types. |
| const Function& fun = Function::Handle(signature_function()); |
| return fun.TypeTest(test_kind, |
| type_arguments, |
| other_fun, |
| other_type_arguments, |
| bound_error); |
| } |
| // Check if type S has a call() method of function type T. |
| Function& function = |
| Function::Handle(LookupDynamicFunction(Symbols::Call())); |
| if (function.IsNull()) { |
| // Walk up the super_class chain. |
| Class& cls = Class::Handle(SuperClass()); |
| while (!cls.IsNull() && function.IsNull()) { |
| function = cls.LookupDynamicFunction(Symbols::Call()); |
| cls = cls.SuperClass(); |
| } |
| } |
| if (!function.IsNull()) { |
| if (other_is_function_class || |
| function.TypeTest(test_kind, |
| type_arguments, |
| other_fun, |
| other_type_arguments, |
| bound_error)) { |
| return true; |
| } |
| } |
| } |
| // Check for 'direct super type' specified in the implements clause |
| // and check for transitivity at the same time. |
| Array& interfaces = Array::Handle(this->interfaces()); |
| AbstractType& interface = AbstractType::Handle(); |
| Class& interface_class = Class::Handle(); |
| AbstractTypeArguments& interface_args = AbstractTypeArguments::Handle(); |
| Error& args_bound_error = Error::Handle(); |
| for (intptr_t i = 0; i < interfaces.Length(); i++) { |
| interface ^= interfaces.At(i); |
| if (!interface.IsFinalized()) { |
| // We may be checking bounds at finalization time. Skipping this |
| // unfinalized interface will postpone bound checking to run time. |
| continue; |
| } |
| interface_class = interface.type_class(); |
| interface_args = interface.arguments(); |
| if (!interface_args.IsNull() && !interface_args.IsInstantiated()) { |
| // This type class implements an interface that is parameterized with |
| // generic type(s), e.g. it implements List<T>. |
| // The uninstantiated type T must be instantiated using the type |
| // parameters of this type before performing the type test. |
| // The type arguments of this type that are referred to by the type |
| // parameters of the interface are at the end of the type vector, |
| // after the type arguments of the super type of this type. |
| // The index of the type parameters is adjusted upon finalization. |
| args_bound_error = Error::null(); |
| interface_args = interface_args.InstantiateFrom(type_arguments, |
| &args_bound_error); |
| if (!args_bound_error.IsNull()) { |
| // Return the first bound error to the caller if it requests it. |
| if ((bound_error != NULL) && bound_error->IsNull()) { |
| *bound_error = args_bound_error.raw(); |
| } |
| continue; // Another interface may work better. |
| } |
| } |
| if (interface_class.TypeTest(test_kind, |
| interface_args, |
| other, |
| other_type_arguments, |
| bound_error)) { |
| return true; |
| } |
| } |
| const Class& super_class = Class::Handle(SuperClass()); |
| if (super_class.IsNull()) { |
| return false; |
| } |
| // Instead of truncating the type argument vector to the length of the super |
| // type argument vector, we make sure that the code works with a vector that |
| // is longer than necessary. |
| return super_class.TypeTest(test_kind, |
| type_arguments, |
| other, |
| other_type_arguments, |
| bound_error); |
| } |
| |
| |
| bool Class::IsTopLevel() const { |
| return Name() == Symbols::TopLevel().raw(); |
| } |
| |
| |
| RawFunction* Class::LookupDynamicFunction(const String& name) const { |
| return LookupFunction(name, kInstance); |
| } |
| |
| |
| RawFunction* Class::LookupDynamicFunctionAllowPrivate( |
| const String& name) const { |
| return LookupFunctionAllowPrivate(name, kInstance); |
| } |
| |
| |
| RawFunction* Class::LookupStaticFunction(const String& name) const { |
| return LookupFunction(name, kStatic); |
| } |
| |
| |
| RawFunction* Class::LookupStaticFunctionAllowPrivate(const String& name) const { |
| return LookupFunctionAllowPrivate(name, kStatic); |
| } |
| |
| |
| RawFunction* Class::LookupConstructor(const String& name) const { |
| return LookupFunction(name, kConstructor); |
| } |
| |
| |
| RawFunction* Class::LookupConstructorAllowPrivate(const String& name) const { |
| return LookupFunctionAllowPrivate(name, kConstructor); |
| } |
| |
| |
| RawFunction* Class::LookupFactory(const String& name) const { |
| return LookupFunction(name, kFactory); |
| } |
| |
| |
| RawFunction* Class::LookupFunction(const String& name) const { |
| return LookupFunction(name, kAny); |
| } |
| |
| |
| RawFunction* Class::LookupFunctionAllowPrivate(const String& name) const { |
| return LookupFunctionAllowPrivate(name, kAny); |
| } |
| |
| |
| // Returns true if 'prefix' and 'accessor_name' match 'name'. |
| static bool MatchesAccessorName(const String& name, |
| const char* prefix, |
| intptr_t prefix_length, |
| const String& accessor_name) { |
| intptr_t name_len = name.Length(); |
| intptr_t accessor_name_len = accessor_name.Length(); |
| |
| if (name_len != (accessor_name_len + prefix_length)) { |
| return false; |
| } |
| for (intptr_t i = 0; i < prefix_length; i++) { |
| if (name.CharAt(i) != prefix[i]) { |
| return false; |
| } |
| } |
| for (intptr_t i = 0, j = prefix_length; i < accessor_name_len; i++, j++) { |
| if (name.CharAt(j) != accessor_name.CharAt(i)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| |
| RawFunction* Class::CheckFunctionType(const Function& func, intptr_t type) { |
| if (type == kInstance) { |
| if (func.IsDynamicFunction()) { |
| return func.raw(); |
| } |
| } else if (type == kStatic) { |
| if (func.IsStaticFunction()) { |
| return func.raw(); |
| } |
| } else if (type == kConstructor) { |
| if (func.IsConstructor()) { |
| ASSERT(!func.is_static()); |
| return func.raw(); |
| } |
| } else if (type == kFactory) { |
| if (func.IsFactory()) { |
| ASSERT(func.is_static()); |
| return func.raw(); |
| } |
| } else if (type == kAny) { |
| return func.raw(); |
| } |
| return Function::null(); |
| } |
| |
| |
| RawFunction* Class::LookupFunction(const String& name, intptr_t type) const { |
| Isolate* isolate = Isolate::Current(); |
| if (EnsureIsFinalized(isolate) != Error::null()) { |
| return Function::null(); |
| } |
| ReusableHandleScope reused_handles(isolate); |
| Array& funcs = reused_handles.ArrayHandle(); |
| funcs ^= functions(); |
| ASSERT(!funcs.IsNull()); |
| Function& function = reused_handles.FunctionHandle(); |
| const intptr_t len = funcs.Length(); |
| if (name.IsSymbol()) { |
| // Quick Symbol compare. |
| NoGCScope no_gc; |
| for (intptr_t i = 0; i < len; i++) { |
| function ^= funcs.At(i); |
| if (function.name() == name.raw()) { |
| return CheckFunctionType(function, type); |
| } |
| } |
| } else { |
| String& function_name = reused_handles.StringHandle(); |
| for (intptr_t i = 0; i < len; i++) { |
| function ^= funcs.At(i); |
| function_name ^= function.name(); |
| if (function_name.Equals(name)) { |
| return CheckFunctionType(function, type); |
| } |
| } |
| } |
| // No function found. |
| return Function::null(); |
| } |
| |
| |
| RawFunction* Class::LookupFunctionAllowPrivate(const String& name, |
| intptr_t type) const { |
| Isolate* isolate = Isolate::Current(); |
|