Chromium Code Reviews| Index: runtime/vm/object.cc |
| diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
| index 7780ecd88ecbdad2e891540f5fa6c6db7bec48ea..ad7b37ae34434626c45ade692961e4d8114c090a 100644 |
| --- a/runtime/vm/object.cc |
| +++ b/runtime/vm/object.cc |
| @@ -27,10 +27,12 @@ |
| #include "vm/hash_table.h" |
| #include "vm/heap.h" |
| #include "vm/intrinsifier.h" |
| +#include "vm/isolate_reload.h" |
| #include "vm/object_store.h" |
| #include "vm/parser.h" |
| #include "vm/precompiler.h" |
| #include "vm/profiler.h" |
| +#include "vm/resolver.h" |
| #include "vm/reusable_handles.h" |
| #include "vm/runtime_entry.h" |
| #include "vm/scopes.h" |
| @@ -61,9 +63,13 @@ DEFINE_FLAG(bool, use_exp_cache, true, "Use library exported name cache"); |
| DEFINE_FLAG(bool, ignore_patch_signature_mismatch, false, |
| "Ignore patch file member signature mismatch."); |
| +DEFINE_FLAG(bool, remove_script_timestamps_for_test, false, |
| + "Remove script timestamps to allow for deterministic testing."); |
| + |
| DECLARE_FLAG(bool, show_invisible_frames); |
| DECLARE_FLAG(bool, trace_deoptimization); |
| DECLARE_FLAG(bool, trace_deoptimization_verbose); |
| +DECLARE_FLAG(bool, trace_reload); |
| DECLARE_FLAG(bool, write_protect_code); |
| DECLARE_FLAG(bool, support_externalizable_strings); |
| @@ -2773,13 +2779,18 @@ void Class::RegisterCHACode(const Code& code) { |
| void Class::DisableCHAOptimizedCode(const Class& subclass) { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| CHACodeArray a(*this); |
| - if (FLAG_trace_deoptimization && a.HasCodes()) { |
| + if (FLAG_trace_deoptimization && a.HasCodes() && !subclass.IsNull()) { |
| THR_Print("Adding subclass %s\n", subclass.ToCString()); |
| } |
| a.DisableCode(); |
| } |
| +void Class::DisableAllCHAOptimizedCode() { |
| + DisableCHAOptimizedCode(Class::Handle()); |
| +} |
| + |
| + |
| bool Class::TraceAllocation(Isolate* isolate) const { |
| ClassTable* class_table = isolate->class_table(); |
| return class_table->TraceAllocationFor(id()); |
| @@ -3124,7 +3135,7 @@ void Class::AddFields(const GrowableArray<const Field*>& new_fields) const { |
| template <class FakeInstance> |
| -RawClass* Class::New(intptr_t index) { |
| +RawClass* Class::NewCommon(intptr_t index) { |
| ASSERT(Object::class_class() != Class::null()); |
| Class& result = Class::Handle(); |
| { |
| @@ -3147,18 +3158,28 @@ RawClass* Class::New(intptr_t index) { |
| result.set_num_native_fields(0); |
| result.set_token_pos(TokenPosition::kNoSource); |
| result.InitEmptyFields(); |
| + return result.raw(); |
| +} |
| + |
| + |
| +template <class FakeInstance> |
| +RawClass* Class::New(intptr_t index) { |
| + Class& result = Class::Handle(NewCommon<FakeInstance>(index)); |
| Isolate::Current()->RegisterClass(result); |
| return result.raw(); |
| } |
| -RawClass* Class::New(const String& name, |
| +RawClass* Class::New(const Library& lib, |
| + const String& name, |
| const Script& script, |
| TokenPosition token_pos) { |
| - Class& result = Class::Handle(New<Instance>(kIllegalCid)); |
| + Class& result = Class::Handle(NewCommon<Instance>(kIllegalCid)); |
| + result.set_library(lib); |
| result.set_name(name); |
| result.set_script(script); |
| result.set_token_pos(token_pos); |
| + Isolate::Current()->RegisterClass(result); |
| return result.raw(); |
| } |
| @@ -3168,7 +3189,7 @@ RawClass* Class::NewNativeWrapper(const Library& library, |
| int field_count) { |
| Class& cls = Class::Handle(library.LookupClass(name)); |
| if (cls.IsNull()) { |
| - cls = New(name, Script::Handle(), TokenPosition::kNoSource); |
| + cls = New(library, name, Script::Handle(), TokenPosition::kNoSource); |
| cls.SetFields(Object::empty_array()); |
| cls.SetFunctions(Object::empty_array()); |
| // Set super class to Object. |
| @@ -5471,6 +5492,19 @@ void Function::ClearCode() const { |
| } |
| +void Function::EnsureHasCompiledUnoptimizedCode() const { |
| + Thread* thread = Thread::Current(); |
| + Zone* zone = thread->zone(); |
| + ASSERT(thread->IsMutatorThread()); |
| + |
| + const Error& error = Error::Handle(zone, |
| + Compiler::EnsureUnoptimizedCode(thread, *this)); |
| + if (!error.IsNull()) { |
| + Exceptions::PropagateError(error); |
| + } |
| +} |
| + |
| + |
| void Function::SwitchToUnoptimizedCode() const { |
| ASSERT(HasOptimizedCode()); |
| Thread* thread = Thread::Current(); |
| @@ -5497,6 +5531,34 @@ void Function::SwitchToUnoptimizedCode() const { |
| } |
| +void Function::SwitchToLazyCompiledUnoptimizedCode() const { |
| + if (!HasOptimizedCode()) { |
| + return; |
| + } |
| + |
| + Thread* thread = Thread::Current(); |
| + Zone* zone = thread->zone(); |
| + ASSERT(thread->IsMutatorThread()); |
| + |
| + const Code& current_code = Code::Handle(zone, CurrentCode()); |
| + TIR_Print("Disabling optimized code for %s\n", ToCString()); |
| + current_code.DisableDartCode(); |
| + |
| + const Code& unopt_code = Code::Handle(zone, unoptimized_code()); |
| + if (unopt_code.IsNull()) { |
| + // Set the lazy compile code. |
| + TIR_Print("Switched to lazy compile stub for %s\n", ToCString()); |
| + SetInstructions(Code::Handle(StubCode::LazyCompile_entry()->code())); |
| + return; |
| + } |
| + |
| + TIR_Print("Switched to unoptimized code for %s\n", ToCString()); |
| + |
| + AttachCode(unopt_code); |
| + unopt_code.Enable(); |
| +} |
| + |
| + |
| void Function::set_unoptimized_code(const Code& value) const { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| ASSERT(value.IsNull() || !value.is_optimized()); |
| @@ -7061,6 +7123,8 @@ RawClass* Function::origin() const { |
| RawScript* Function::script() const { |
| + // NOTE(turnidge): If you update this function, you probably want to |
| + // update Class::PatchFieldsAndFunctions() at the same time. |
| if (token_pos() == TokenPosition::kMinSource) { |
| // Testing for position 0 is an optimization that relies on temporary |
| // eval functions having token position 0. |
| @@ -7538,6 +7602,8 @@ RawClass* Field::Origin() const { |
| RawScript* Field::Script() const { |
| + // NOTE(turnidge): If you update this function, you probably want to |
| + // update Class::PatchFieldsAndFunctions() at the same time. |
| const Field& field = Field::Handle(Original()); |
| ASSERT(field.IsOriginal()); |
| const Object& obj = Object::Handle(field.raw_ptr()->owner_); |
| @@ -7569,16 +7635,14 @@ RawField* Field::New() { |
| } |
| -RawField* Field::New(const String& name, |
| - bool is_static, |
| - bool is_final, |
| - bool is_const, |
| - bool is_reflectable, |
| - const Class& owner, |
| - const AbstractType& type, |
| - TokenPosition token_pos) { |
| - ASSERT(!owner.IsNull()); |
| - const Field& result = Field::Handle(Field::New()); |
| +void Field::InitializeNew(const Field& result, |
| + const String& name, |
| + bool is_static, |
| + bool is_final, |
| + bool is_const, |
| + bool is_reflectable, |
| + const Object& owner, |
| + TokenPosition token_pos) { |
| result.set_name(name); |
| result.set_is_static(is_static); |
| if (!is_static) { |
| @@ -7589,19 +7653,47 @@ RawField* Field::New(const String& name, |
| result.set_is_reflectable(is_reflectable); |
| result.set_is_double_initialized(false); |
| result.set_owner(owner); |
| - result.SetFieldType(type); |
| result.set_token_pos(token_pos); |
| result.set_has_initializer(false); |
| result.set_is_unboxing_candidate(true); |
| - result.set_guarded_cid(FLAG_use_field_guards ? kIllegalCid : kDynamicCid); |
| - result.set_is_nullable(FLAG_use_field_guards ? false : true); |
| + Isolate* isolate = Isolate::Current(); |
| + |
| + // Use field guards if they are enabled and the isolate has never reloaded. |
| + // TODO(johnmccutchan): The reload case assumes the worst case (everything is |
| + // dynamic and possibly null). Attempt to relax this later. |
| + const bool use_field_guards = |
| + FLAG_use_field_guards && !isolate->HasAttemptedReload(); |
| + result.set_guarded_cid(use_field_guards ? kIllegalCid : kDynamicCid); |
| + result.set_is_nullable(use_field_guards ? false : true); |
| result.set_guarded_list_length_in_object_offset(Field::kUnknownLengthOffset); |
| // Presently, we only attempt to remember the list length for final fields. |
| - if (is_final && FLAG_use_field_guards) { |
| + if (is_final && use_field_guards) { |
| result.set_guarded_list_length(Field::kUnknownFixedLength); |
| } else { |
| result.set_guarded_list_length(Field::kNoFixedLength); |
| } |
| +} |
| + |
| + |
| +RawField* Field::New(const String& name, |
| + bool is_static, |
| + bool is_final, |
| + bool is_const, |
| + bool is_reflectable, |
| + const Class& owner, |
| + const AbstractType& type, |
| + TokenPosition token_pos) { |
| + ASSERT(!owner.IsNull()); |
| + const Field& result = Field::Handle(Field::New()); |
| + InitializeNew(result, |
| + name, |
| + is_static, |
| + is_final, |
| + is_const, |
| + is_reflectable, |
| + owner, |
| + token_pos); |
| + result.SetFieldType(type); |
| return result.raw(); |
| } |
| @@ -7613,25 +7705,14 @@ RawField* Field::NewTopLevel(const String& name, |
| TokenPosition token_pos) { |
| ASSERT(!owner.IsNull()); |
| const Field& result = Field::Handle(Field::New()); |
| - result.set_name(name); |
| - result.set_is_static(true); |
| - result.set_is_final(is_final); |
| - result.set_is_const(is_const); |
| - result.set_is_reflectable(true); |
| - result.set_is_double_initialized(false); |
| - result.set_owner(owner); |
| - result.set_token_pos(token_pos); |
| - result.set_has_initializer(false); |
| - result.set_is_unboxing_candidate(true); |
| - result.set_guarded_cid(FLAG_use_field_guards ? kIllegalCid : kDynamicCid); |
| - result.set_is_nullable(FLAG_use_field_guards ? false : true); |
| - result.set_guarded_list_length_in_object_offset(Field::kUnknownLengthOffset); |
| - // Presently, we only attempt to remember the list length for final fields. |
| - if (is_final && FLAG_use_field_guards) { |
| - result.set_guarded_list_length(Field::kUnknownFixedLength); |
| - } else { |
| - result.set_guarded_list_length(Field::kNoFixedLength); |
| - } |
| + InitializeNew(result, |
| + name, |
| + true, /* is_static */ |
| + is_final, |
| + is_const, |
| + true, /* is_reflectable */ |
| + owner, |
| + token_pos); |
| return result.raw(); |
| } |
| @@ -8100,6 +8181,18 @@ void Field::RecordStore(const Object& value) const { |
| } |
| +void Field::ForceDynamicGuardedCidAndLength() const { |
| + // Assume nothing about this field. |
| + set_is_unboxing_candidate(false); |
| + set_guarded_cid(kDynamicCid); |
| + set_is_nullable(true); |
| + set_guarded_list_length(Field::kNoFixedLength); |
| + set_guarded_list_length_in_object_offset(Field::kUnknownLengthOffset); |
| + // Drop any code that relied on the above assumptions. |
| + DeoptimizeDependentCode(); |
| +} |
| + |
| + |
| void LiteralToken::set_literal(const String& literal) const { |
| StorePointer(&raw_ptr()->literal_, literal.raw()); |
| } |
| @@ -8927,6 +9020,11 @@ void Script::set_kind(RawScript::Kind value) const { |
| } |
| +void Script::set_load_timestamp(int64_t value) const { |
| + StoreNonPointer(&raw_ptr()->load_timestamp_, value); |
| +} |
| + |
| + |
| void Script::set_tokens(const TokenStream& value) const { |
| StorePointer(&raw_ptr()->tokens_, value.raw()); |
| } |
| @@ -9180,13 +9278,16 @@ RawScript* Script::New(const String& url, |
| result.set_url(String::Handle(zone, Symbols::New(thread, url))); |
| result.set_source(source); |
| result.set_kind(kind); |
| + result.set_load_timestamp(FLAG_remove_script_timestamps_for_test |
| + ? 0 : OS::GetCurrentTimeMillis()); |
| result.SetLocationOffset(0, 0); |
| return result.raw(); |
| } |
| const char* Script::ToCString() const { |
| - return "Script"; |
| + const String& name = String::Handle(url()); |
| + return OS::SCreate(Thread::Current()->zone(), "Script(%s)", name.ToCString()); |
| } |
| @@ -12676,9 +12777,20 @@ intptr_t ICData::TestEntryLength() const { |
| } |
| +intptr_t ICData::Length() const { |
| + return (Smi::Value(ic_data()->ptr()->length_) / TestEntryLength()); |
| +} |
| + |
| + |
| intptr_t ICData::NumberOfChecks() const { |
| - // Do not count the sentinel; |
| - return (Smi::Value(ic_data()->ptr()->length_) / TestEntryLength()) - 1; |
| + const intptr_t length = Length(); |
| + for (intptr_t i = 0; i < length; i++) { |
| + if (IsSentinelAt(i)) { |
| + return i; |
| + } |
| + } |
|
Florian Schneider
2016/05/18 09:36:56
This approach does not work with background compil
|
| + UNREACHABLE(); |
| + return -1; |
| } |
| @@ -12715,6 +12827,7 @@ bool ICData::HasCheck(const GrowableArray<intptr_t>& cids) const { |
| GetClassIdsAt(i, &class_ids); |
| bool matches = true; |
| for (intptr_t k = 0; k < class_ids.length(); k++) { |
| + ASSERT(class_ids[k] != kIllegalCid); |
| if (class_ids[k] != cids[k]) { |
| matches = false; |
| break; |
| @@ -12729,6 +12842,142 @@ bool ICData::HasCheck(const GrowableArray<intptr_t>& cids) const { |
| #endif // DEBUG |
| +void ICData::WriteSentinelAt(intptr_t index) const { |
| + const intptr_t len = Length(); |
| + ASSERT(index >= 0); |
| + ASSERT(index < len); |
| + Array& data = Array::Handle(ic_data()); |
| + const intptr_t start = index * TestEntryLength(); |
| + const intptr_t end = start + TestEntryLength(); |
| + for (intptr_t i = start; i < end; i++) { |
| + data.SetAt(i, smi_illegal_cid()); |
| + } |
| +} |
| + |
| + |
| +void ICData::ClearCountAt(intptr_t index) const { |
| + const intptr_t len = NumberOfChecks(); |
| + ASSERT(index >= 0); |
| + ASSERT(index < len); |
| + SetCountAt(index, 0); |
| +} |
| + |
| + |
| +void ICData::ClearWithSentinel() const { |
| + if (IsImmutable()) { |
| + return; |
| + } |
| + // Write the sentinel value into all entries except the first one. |
| + const intptr_t len = Length(); |
| + if (len == 0) { |
| + return; |
| + } |
| + // The final entry is always the sentinel. |
| + ASSERT(IsSentinelAt(len - 1)); |
| + for (intptr_t i = len - 1; i > 0; i--) { |
| + WriteSentinelAt(i); |
| + } |
| + if (NumArgsTested() != 2) { |
| + // Not the smi fast path case, write sentinel to first one and exit. |
| + WriteSentinelAt(0); |
| + return; |
| + } |
| + if (IsSentinelAt(0)) { |
| + return; |
| + } |
| + Zone* zone = Thread::Current()->zone(); |
| + const String& name = String::Handle(target_name()); |
| + const Class& smi_class = Class::Handle(Smi::Class()); |
| + const Function& smi_op_target = |
| + Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name)); |
| + GrowableArray<intptr_t> class_ids(2); |
| + Function& target = Function::Handle(); |
| + GetCheckAt(0, &class_ids, &target); |
| + if ((target.raw() == smi_op_target.raw()) && |
| + (class_ids[0] == kSmiCid) && (class_ids[1] == kSmiCid)) { |
| + // The smi fast path case, preserve the initial entry but reset the count. |
| + ClearCountAt(0); |
| + return; |
| + } |
| + WriteSentinelAt(0); |
| +} |
| + |
| + |
| +void ICData::ClearAndSetStaticTarget(const Function& func) const { |
| + if (IsImmutable()) { |
| + return; |
| + } |
| + const intptr_t len = Length(); |
| + if (len == 0) { |
| + return; |
| + } |
| + // The final entry is always the sentinel. |
| + ASSERT(IsSentinelAt(len - 1)); |
| + if (NumArgsTested() == 0) { |
| + // No type feedback is being collected. |
| + const Array& data = Array::Handle(ic_data()); |
| + // Static calls with no argument checks hold only one target and the |
| + // sentinel value. |
| + ASSERT(len == 2); |
| + // Static calls with no argument checks only need two words. |
| + ASSERT(TestEntryLength() == 2); |
| + // Set the target. |
| + data.SetAt(0, func); |
| + // Set count to 0 as this is called during compilation, before the |
| + // call has been executed. |
| + const Smi& value = Smi::Handle(Smi::New(0)); |
| + data.SetAt(1, value); |
| + } else { |
| + // Type feedback on arguments is being collected. |
| + const Array& data = Array::Handle(ic_data()); |
| + |
| + // Fill all but the first entry with the sentinel. |
| + for (intptr_t i = len - 1; i > 0; i--) { |
| + WriteSentinelAt(i); |
| + } |
| + // Rewrite the dummy entry. |
| + const Smi& object_cid = Smi::Handle(Smi::New(kObjectCid)); |
| + for (intptr_t i = 0; i < NumArgsTested(); i++) { |
| + data.SetAt(i, object_cid); |
| + } |
| + data.SetAt(NumArgsTested(), func); |
| + const Smi& value = Smi::Handle(Smi::New(0)); |
| + data.SetAt(NumArgsTested() + 1, value); |
| + } |
| +} |
| + |
| + |
| +// Add an initial Smi/Smi check with count 0. |
| +bool ICData::AddSmiSmiCheckForFastSmiStubs() const { |
| + bool is_smi_two_args_op = false; |
| + |
| + ASSERT(NumArgsTested() == 2); |
| + const String& name = String::Handle(target_name()); |
| + const Class& smi_class = Class::Handle(Smi::Class()); |
| + Zone* zone = Thread::Current()->zone(); |
| + const Function& smi_op_target = |
| + Function::Handle(Resolver::ResolveDynamicAnyArgs(zone, smi_class, name)); |
| + if (NumberOfChecks() == 0) { |
| + GrowableArray<intptr_t> class_ids(2); |
| + class_ids.Add(kSmiCid); |
| + class_ids.Add(kSmiCid); |
| + AddCheck(class_ids, smi_op_target); |
| + // 'AddCheck' sets the initial count to 1. |
| + SetCountAt(0, 0); |
| + is_smi_two_args_op = true; |
| + } else if (NumberOfChecks() == 1) { |
| + GrowableArray<intptr_t> class_ids(2); |
| + Function& target = Function::Handle(); |
| + GetCheckAt(0, &class_ids, &target); |
| + if ((target.raw() == smi_op_target.raw()) && |
| + (class_ids[0] == kSmiCid) && (class_ids[1] == kSmiCid)) { |
| + is_smi_two_args_op = true; |
| + } |
| + } |
| + return is_smi_two_args_op; |
| +} |
| + |
| + |
| // Used for unoptimized static calls when no class-ids are checked. |
| void ICData::AddTarget(const Function& target) const { |
| ASSERT(!target.IsNull()); |
| @@ -12798,10 +13047,10 @@ void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids, |
| return; |
| } |
| } |
| - const intptr_t new_len = data.Length() + TestEntryLength(); |
| - data = Array::Grow(data, new_len, Heap::kOld); |
| - WriteSentinel(data, TestEntryLength()); |
| - intptr_t data_pos = old_num * TestEntryLength(); |
| + intptr_t index = -1; |
| + data = FindFreeIndex(&index); |
| + ASSERT(!data.IsNull()); |
| + intptr_t data_pos = index * TestEntryLength(); |
| Smi& value = Smi::Handle(); |
| for (intptr_t i = 0; i < class_ids.length(); i++) { |
| // kIllegalCid is used as terminating value, do not add it. |
| @@ -12819,6 +13068,57 @@ void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids, |
| } |
| +RawArray* ICData::FindFreeIndex(intptr_t* index) const { |
| + // The final entry is always the sentinel value, don't consider it |
| + // when searching. |
| + const intptr_t len = Length() - 1; |
| + Array& data = Array::Handle(ic_data()); |
| + *index = len; |
| + for (intptr_t i = 0; i < len; i++) { |
| + if (IsSentinelAt(i)) { |
| + *index = i; |
| + break; |
| + } |
| + } |
| + if (*index < len) { |
| + // We've found a free slot. |
| + return data.raw(); |
| + } |
| + // Append case. |
| + ASSERT(*index == len); |
| + ASSERT(*index >= 0); |
| + // Grow array. |
| + const intptr_t new_len = data.Length() + TestEntryLength(); |
| + data = Array::Grow(data, new_len, Heap::kOld); |
| + WriteSentinel(data, TestEntryLength()); |
| + return data.raw(); |
| +} |
| + |
| + |
| +void ICData::DebugDump() const { |
| + const Function& owner = Function::Handle(Owner()); |
| + THR_Print("ICData::DebugDump\n"); |
| + THR_Print("Owner = %s [deopt=%" Pd "]\n", owner.ToCString(), deopt_id()); |
| + THR_Print("NumArgsTested = %" Pd "\n", NumArgsTested()); |
| + THR_Print("Length = %" Pd "\n", Length()); |
| + THR_Print("NumberOfChecks = %" Pd "\n", NumberOfChecks()); |
| + |
| + GrowableArray<intptr_t> class_ids; |
| + for (intptr_t i = 0; i < NumberOfChecks(); i++) { |
| + THR_Print("Check[%" Pd "]:", i); |
| + GetClassIdsAt(i, &class_ids); |
| + for (intptr_t c = 0; c < class_ids.length(); c++) { |
| + THR_Print(" %" Pd "", class_ids[c]); |
| + } |
| + THR_Print("--- %" Pd " hits\n", GetCountAt(i)); |
| + } |
| +} |
| + |
| + |
| +void ICData::ValidateSentinelLocations() const { |
| +} |
| + |
| + |
| void ICData::AddReceiverCheck(intptr_t receiver_class_id, |
| const Function& target, |
| intptr_t count) const { |
| @@ -12831,12 +13131,9 @@ void ICData::AddReceiverCheck(intptr_t receiver_class_id, |
| ASSERT(NumArgsTested() == 1); // Otherwise use 'AddCheck'. |
| ASSERT(receiver_class_id != kIllegalCid); |
| - const intptr_t old_num = NumberOfChecks(); |
| - Array& data = Array::Handle(ic_data()); |
| - const intptr_t new_len = data.Length() + TestEntryLength(); |
| - data = Array::Grow(data, new_len, Heap::kOld); |
| - WriteSentinel(data, TestEntryLength()); |
| - intptr_t data_pos = old_num * TestEntryLength(); |
| + intptr_t index = -1; |
| + Array& data = Array::Handle(FindFreeIndex(&index)); |
| + intptr_t data_pos = index * TestEntryLength(); |
| if ((receiver_class_id == kSmiCid) && (data_pos > 0)) { |
| ASSERT(GetReceiverClassIdAt(0) != kSmiCid); |
| // Move class occupying position 0 to the data_pos. |
| @@ -12881,10 +13178,26 @@ void ICData::GetCheckAt(intptr_t index, |
| } |
| +bool ICData::IsSentinelAt(intptr_t index) const { |
| + ASSERT(index < Length()); |
| + const Array& data = Array::Handle(ic_data()); |
| + const intptr_t entry_length = TestEntryLength(); |
| + intptr_t data_pos = index * TestEntryLength(); |
| + for (intptr_t i = 0; i < entry_length; i++) { |
| + if (data.At(data_pos++) != smi_illegal_cid().raw()) { |
| + return false; |
| + } |
| + } |
| + // The entry at |index| was filled with the value kIllegalCid. |
| + return true; |
| +} |
| + |
| + |
| void ICData::GetClassIdsAt(intptr_t index, |
| GrowableArray<intptr_t>* class_ids) const { |
| - ASSERT(index < NumberOfChecks()); |
| + ASSERT(index < Length()); |
| ASSERT(class_ids != NULL); |
| + ASSERT(!IsSentinelAt(index)); |
| class_ids->Clear(); |
| const Array& data = Array::Handle(ic_data()); |
| intptr_t data_pos = index * TestEntryLength(); |
| @@ -12923,7 +13236,8 @@ intptr_t ICData::GetClassIdAt(intptr_t index, intptr_t arg_nr) const { |
| intptr_t ICData::GetReceiverClassIdAt(intptr_t index) const { |
| - ASSERT(index < NumberOfChecks()); |
| + ASSERT(index < Length()); |
| + ASSERT(!IsSentinelAt(index)); |
| const intptr_t data_pos = index * TestEntryLength(); |
| NoSafepointScope no_safepoint; |
| RawArray* raw_data = ic_data(); |
| @@ -13127,10 +13441,9 @@ RawICData* ICData::AsUnaryClassChecksSortedByCount() const { |
| // Room for all entries and the sentinel. |
| const intptr_t data_len = |
| result.TestEntryLength() * (aggregate.length() + 1); |
| + // Allocate the array but do not assign it to result until we have populated |
| + // it with the aggregate data and the terminating sentinel. |
| const Array& data = Array::Handle(Array::New(data_len, Heap::kOld)); |
| - result.set_ic_data_array(data); |
| - ASSERT(result.NumberOfChecks() == aggregate.length()); |
| - |
| intptr_t pos = 0; |
| for (intptr_t i = 0; i < aggregate.length(); i++) { |
| data.SetAt(pos + 0, Smi::Handle(Smi::New(aggregate[i].cid))); |
| @@ -13142,6 +13455,7 @@ RawICData* ICData::AsUnaryClassChecksSortedByCount() const { |
| } |
| WriteSentinel(data, result.TestEntryLength()); |
| result.set_ic_data_array(data); |
| + ASSERT(result.NumberOfChecks() == aggregate.length()); |
| return result.raw(); |
| } |
| @@ -13288,6 +13602,22 @@ RawICData* ICData::NewDescriptor(Zone* zone, |
| } |
| +bool ICData::IsImmutable() const { |
| + const Array& data = Array::Handle(ic_data()); |
| + return data.IsImmutable(); |
| +} |
| + |
| + |
| +void ICData::ResetData() const { |
| + // Number of array elements in one test entry. |
| + intptr_t len = TestEntryLength(); |
| + // IC data array must be null terminated (sentinel entry). |
| + const Array& ic_data = Array::Handle(Array::New(len, Heap::kOld)); |
| + set_ic_data_array(ic_data); |
| + WriteSentinel(ic_data, len); |
| +} |
| + |
| + |
| RawICData* ICData::New() { |
| ICData& result = ICData::Handle(); |
| { |
| @@ -15611,6 +15941,13 @@ bool AbstractType::HasResolvedTypeClass() const { |
| } |
| +classid_t AbstractType::type_class_id() const { |
| + // AbstractType is an abstract class. |
| + UNREACHABLE(); |
| + return kIllegalCid; |
| +} |
| + |
| + |
| RawClass* AbstractType::type_class() const { |
| // AbstractType is an abstract class. |
| UNREACHABLE(); |
| @@ -16476,19 +16813,18 @@ void Type::SetIsResolved() const { |
| bool Type::HasResolvedTypeClass() const { |
| - return (raw_ptr()->type_class_->GetClassId() == kClassCid); |
| + return !raw_ptr()->type_class_id_->IsHeapObject(); |
| } |
| -RawClass* Type::type_class() const { |
| -#ifdef DEBUG |
| +classid_t Type::type_class_id() const { |
| ASSERT(HasResolvedTypeClass()); |
| - Class& type_class = Class::Handle(); |
| - type_class ^= raw_ptr()->type_class_; |
| - return type_class.raw(); |
| -#else |
| - return reinterpret_cast<RawClass*>(raw_ptr()->type_class_); |
| -#endif |
| + return Smi::Value(reinterpret_cast<RawSmi*>(raw_ptr()->type_class_id_)); |
| +} |
| + |
| + |
| +RawClass* Type::type_class() const { |
| + return Isolate::Current()->class_table()->At(type_class_id()); |
| } |
| @@ -16496,13 +16832,13 @@ RawUnresolvedClass* Type::unresolved_class() const { |
| #ifdef DEBUG |
| ASSERT(!HasResolvedTypeClass()); |
| UnresolvedClass& unresolved_class = UnresolvedClass::Handle(); |
| - unresolved_class ^= raw_ptr()->type_class_; |
| + unresolved_class ^= raw_ptr()->type_class_id_; |
| ASSERT(!unresolved_class.IsNull()); |
| return unresolved_class.raw(); |
| #else |
| - ASSERT(!Object::Handle(raw_ptr()->type_class_).IsNull()); |
| - ASSERT(Object::Handle(raw_ptr()->type_class_).IsUnresolvedClass()); |
| - return reinterpret_cast<RawUnresolvedClass*>(raw_ptr()->type_class_); |
| + ASSERT(!Object::Handle(raw_ptr()->type_class_id_).IsNull()); |
| + ASSERT(Object::Handle(raw_ptr()->type_class_id_).IsUnresolvedClass()); |
| + return reinterpret_cast<RawUnresolvedClass*>(raw_ptr()->type_class_id_); |
| #endif |
| } |
| @@ -17068,9 +17404,16 @@ intptr_t Type::Hash() const { |
| } |
| -void Type::set_type_class(const Object& value) const { |
| - ASSERT(!value.IsNull() && (value.IsClass() || value.IsUnresolvedClass())); |
| - StorePointer(&raw_ptr()->type_class_, value.raw()); |
| +void Type::set_type_class(const Class& value) const { |
| + ASSERT(!value.IsNull()); |
| + StorePointer(&raw_ptr()->type_class_id_, |
| + reinterpret_cast<RawObject*>(Smi::New(value.id()))); |
| +} |
| + |
| + |
| +void Type::set_unresolved_class(const Object& value) const { |
| + ASSERT(!value.IsNull() && value.IsUnresolvedClass()); |
| + StorePointer(&raw_ptr()->type_class_id_, value.raw()); |
| } |
| @@ -17093,7 +17436,11 @@ RawType* Type::New(const Object& clazz, |
| TokenPosition token_pos, |
| Heap::Space space) { |
| const Type& result = Type::Handle(Type::New(space)); |
| - result.set_type_class(clazz); |
| + if (clazz.IsClass()) { |
| + result.set_type_class(Class::Cast(clazz)); |
| + } else { |
| + result.set_unresolved_class(clazz); |
| + } |
| result.set_arguments(arguments); |
| result.set_token_pos(token_pos); |
| result.StoreNonPointer(&result.raw_ptr()->type_state_, RawType::kAllocated); |
| @@ -17341,7 +17688,25 @@ bool TypeParameter::IsEquivalent(const Instance& other, TrailPtr trail) const { |
| void TypeParameter::set_parameterized_class(const Class& value) const { |
| // Set value may be null. |
| - StorePointer(&raw_ptr()->parameterized_class_, value.raw()); |
| + classid_t cid = kIllegalCid; |
| + if (!value.IsNull()) { |
| + cid = value.id(); |
| + } |
| + StoreNonPointer(&raw_ptr()->parameterized_class_id_, cid); |
| +} |
| + |
| + |
| +classid_t TypeParameter::parameterized_class_id() const { |
| + return raw_ptr()->parameterized_class_id_; |
| +} |
| + |
| + |
| +RawClass* TypeParameter::parameterized_class() const { |
| + classid_t cid = parameterized_class_id(); |
| + if (cid == kIllegalCid) { |
| + return Class::null(); |
| + } |
| + return Isolate::Current()->class_table()->At(cid); |
| } |