Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #include "vm/object.h" | |
| 6 | |
| 7 #include "vm/isolate_reload.h" | |
| 8 #include "vm/log.h" | |
| 9 #include "vm/resolver.h" | |
| 10 #include "vm/symbols.h" | |
| 11 | |
| 12 namespace dart { | |
| 13 | |
| 14 #ifndef PRODUCT | |
| 15 | |
| 16 DECLARE_FLAG(bool, trace_reload); | |
| 17 DECLARE_FLAG(bool, two_args_smi_icd); | |
| 18 | |
| 19 #define IRC (Isolate::Current()->reload_context()) | |
| 20 | |
| 21 class ObjectReloadUtils : public AllStatic { | |
| 22 static void DumpLibraryDictionary(const Library& lib) { | |
| 23 DictionaryIterator it(lib); | |
| 24 Object& entry = Object::Handle(); | |
| 25 String& name = String::Handle(); | |
| 26 TIR_Print("Dumping dictionary for %s\n", lib.ToCString()); | |
| 27 while (it.HasNext()) { | |
| 28 entry = it.GetNext(); | |
| 29 name = entry.DictionaryName(); | |
| 30 TIR_Print("%s -> %s\n", name.ToCString(), entry.ToCString()); | |
| 31 } | |
| 32 } | |
| 33 }; | |
| 34 | |
| 35 | |
| 36 void Function::Reparent(const Class& new_cls) const { | |
| 37 set_owner(new_cls); | |
| 38 } | |
| 39 | |
| 40 | |
| 41 void Function::ZeroEdgeCounters() const { | |
| 42 const Array& saved_ic_data = Array::Handle(ic_data_array()); | |
| 43 if (saved_ic_data.IsNull()) { | |
| 44 return; | |
| 45 } | |
| 46 const intptr_t saved_ic_datalength = saved_ic_data.Length(); | |
| 47 ASSERT(saved_ic_datalength > 0); | |
| 48 const Array& edge_counters_array = | |
| 49 Array::Handle(Array::RawCast(saved_ic_data.At(0))); | |
| 50 ASSERT(!edge_counters_array.IsNull()); | |
| 51 // Fill edge counters array with zeros. | |
| 52 const Smi& zero = Smi::Handle(Smi::New(0)); | |
| 53 for (intptr_t i = 0; i < edge_counters_array.Length(); i++) { | |
| 54 edge_counters_array.SetAt(i, zero); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 | |
| 59 static void ClearICs(const Function& function, const Code& code) { | |
| 60 if (function.ic_data_array() == Array::null()) { | |
| 61 return; // Already reset in an earlier round. | |
| 62 } | |
| 63 | |
| 64 Thread* thread = Thread::Current(); | |
| 65 Zone* zone = thread->zone(); | |
| 66 | |
| 67 ZoneGrowableArray<const ICData*>* ic_data_array = | |
| 68 new(zone) ZoneGrowableArray<const ICData*>(); | |
| 69 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */); | |
| 70 if (ic_data_array->length() == 0) { | |
| 71 return; | |
| 72 } | |
| 73 const PcDescriptors& descriptors = | |
| 74 PcDescriptors::Handle(code.pc_descriptors()); | |
| 75 PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall | | |
| 76 RawPcDescriptors::kUnoptStaticCall); | |
| 77 while (iter.MoveNext()) { | |
| 78 const ICData* ic_data = (*ic_data_array)[iter.DeoptId()]; | |
| 79 if (ic_data == NULL) { | |
| 80 continue; | |
| 81 } | |
| 82 bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall; | |
| 83 ic_data->Reset(is_static_call); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 | |
| 88 void Function::FillICDataWithSentinels(const Code& code) const { | |
| 89 ASSERT(code.raw() == CurrentCode()); | |
| 90 ClearICs(*this, code); | |
| 91 } | |
| 92 | |
| 93 | |
| 94 void Class::CopyStaticFieldValues(const Class& old_cls) const { | |
| 95 // We only update values for non-enum classes. | |
| 96 const bool update_values = !is_enum_class(); | |
| 97 | |
| 98 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); | |
| 99 ASSERT(reload_context != NULL); | |
| 100 | |
| 101 const Array& old_field_list = Array::Handle(old_cls.fields()); | |
| 102 Field& old_field = Field::Handle(); | |
| 103 String& old_name = String::Handle(); | |
| 104 | |
| 105 const Array& field_list = Array::Handle(fields()); | |
| 106 Field& field = Field::Handle(); | |
| 107 String& name = String::Handle(); | |
| 108 | |
| 109 Instance& value = Instance::Handle(); | |
| 110 for (intptr_t i = 0; i < field_list.Length(); i++) { | |
| 111 field = Field::RawCast(field_list.At(i)); | |
| 112 name = field.name(); | |
| 113 if (field.is_static()) { | |
| 114 // Find the corresponding old field, if it exists, and migrate | |
| 115 // over the field value. | |
| 116 for (intptr_t j = 0; j < old_field_list.Length(); j++) { | |
| 117 old_field = Field::RawCast(old_field_list.At(j)); | |
| 118 old_name = old_field.name(); | |
| 119 if (name.Equals(old_name)) { | |
| 120 if (update_values) { | |
| 121 value = old_field.StaticValue(); | |
| 122 field.SetStaticValue(value); | |
| 123 } | |
| 124 reload_context->AddStaticFieldMapping(old_field, field); | |
| 125 } | |
| 126 } | |
| 127 } | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 | |
| 132 void Class::CopyCanonicalConstants(const Class& old_cls) const { | |
| 133 if (is_enum_class()) { | |
| 134 return; | |
| 135 } | |
| 136 #if defined(DEBUG) | |
| 137 { | |
| 138 // Class has no canonical constants allocated. | |
| 139 const Array& my_constants = Array::Handle(constants()); | |
| 140 ASSERT(my_constants.Length() == 0); | |
| 141 } | |
| 142 #endif // defined(DEBUG). | |
| 143 // Copy old constants into new class. | |
| 144 const Array& old_constants = Array::Handle(old_cls.constants()); | |
| 145 if (old_constants.IsNull() || old_constants.Length() == 0) { | |
| 146 return; | |
| 147 } | |
| 148 TIR_Print("Copied %" Pd " canonical constants for class `%s`\n", | |
| 149 old_constants.Length(), | |
| 150 ToCString()); | |
| 151 set_constants(old_constants); | |
| 152 } | |
| 153 | |
| 154 | |
| 155 void Class::CopyCanonicalTypes(const Class& old_cls) const { | |
| 156 const Object& old_canonical_types = Object::Handle(old_cls.canonical_types()); | |
| 157 if (old_canonical_types.IsNull()) { | |
| 158 return; | |
| 159 } | |
| 160 set_canonical_types(old_canonical_types); | |
| 161 } | |
| 162 | |
| 163 | |
| 164 static intptr_t IndexOfEnum(const Array& enum_names, const String& name) { | |
| 165 ASSERT(!enum_names.IsNull()); | |
| 166 ASSERT(!name.IsNull()); | |
| 167 String& enum_name = String::Handle(); | |
| 168 for (intptr_t i = 0; i < enum_names.Length(); i++) { | |
| 169 enum_name = String::RawCast(enum_names.At(i)); | |
| 170 ASSERT(!enum_name.IsNull()); | |
| 171 if (enum_name.Equals(name)) { | |
| 172 return i; | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 return -1; | |
| 177 } | |
| 178 | |
| 179 | |
| 180 static void UpdateEnumIndex(const Instance& enum_value, | |
| 181 const Field& enum_index_field, | |
| 182 const intptr_t index) { | |
| 183 enum_value.SetField(enum_index_field, Smi::Handle(Smi::New(index))); | |
| 184 } | |
| 185 | |
| 186 | |
| 187 // TODO(johnmccutchan): The code in the class finalizer canonicalizes all | |
| 188 // instances and the values array. We probably should do the same thing. | |
| 189 void Class::ReplaceEnum(const Class& old_enum) const { | |
| 190 // We only do this for finalized enum classes. | |
| 191 ASSERT(is_enum_class()); | |
| 192 ASSERT(old_enum.is_enum_class()); | |
| 193 ASSERT(is_finalized()); | |
| 194 ASSERT(old_enum.is_finalized()); | |
| 195 | |
| 196 Thread* thread = Thread::Current(); | |
| 197 IsolateReloadContext* reload_context = Isolate::Current()->reload_context(); | |
| 198 ASSERT(reload_context != NULL); | |
| 199 | |
| 200 TIR_Print("ReplaceEnum `%s` (%" Pd " and %" Pd ")\n", | |
| 201 ToCString(), id(), old_enum.id()); | |
| 202 | |
| 203 // Grab '_enum_names' from |old_enum|. | |
| 204 const Field& old_enum_names_field = Field::Handle( | |
| 205 old_enum.LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | |
| 206 ASSERT(!old_enum_names_field.IsNull()); | |
| 207 const Array& old_enum_names = | |
| 208 Array::Handle(Array::RawCast(old_enum_names_field.StaticValue())); | |
| 209 ASSERT(!old_enum_names.IsNull()); | |
| 210 | |
| 211 // Grab 'values' from |old_enum|. | |
| 212 const Field& old_enum_values_field = Field::Handle( | |
| 213 old_enum.LookupStaticField(Symbols::Values())); | |
| 214 ASSERT(!old_enum_values_field.IsNull()); | |
| 215 const Array& old_enum_values = | |
| 216 Array::Handle(Array::RawCast(old_enum_values_field.StaticValue())); | |
| 217 ASSERT(!old_enum_values.IsNull()); | |
| 218 | |
| 219 // Grab _enum_names from |this|. | |
| 220 const Field& enum_names_field = Field::Handle( | |
| 221 LookupStaticFieldAllowPrivate(Symbols::_EnumNames())); | |
| 222 ASSERT(!enum_names_field.IsNull()); | |
| 223 Array& enum_names = | |
| 224 Array::Handle(Array::RawCast(enum_names_field.StaticValue())); | |
| 225 ASSERT(!enum_names.IsNull()); | |
| 226 | |
| 227 // Grab values from |this|. | |
| 228 const Field& enum_values_field = Field::Handle( | |
| 229 LookupStaticField(Symbols::Values())); | |
| 230 ASSERT(!enum_values_field.IsNull()); | |
| 231 Array& enum_values = | |
| 232 Array::Handle(Array::RawCast(enum_values_field.StaticValue())); | |
| 233 ASSERT(!enum_values.IsNull()); | |
| 234 | |
| 235 // Grab the |index| field. | |
| 236 const Field& index_field = | |
| 237 Field::Handle(old_enum.LookupInstanceField(Symbols::Index())); | |
| 238 ASSERT(!index_field.IsNull()); | |
| 239 | |
| 240 // Build list of enum from |old_enum| that aren't present in |this|. | |
| 241 // This array holds pairs: (name, value). | |
| 242 const GrowableObjectArray& to_add = | |
| 243 GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld)); | |
| 244 const String& enum_class_name = String::Handle(UserVisibleName()); | |
| 245 String& enum_name = String::Handle(); | |
| 246 String& enum_field_name = String::Handle(); | |
| 247 Object& enum_value = Object::Handle(); | |
| 248 Field& enum_field = Field::Handle(); | |
| 249 | |
| 250 TIR_Print("New version of enum has %" Pd " elements\n", | |
| 251 enum_values.Length()); | |
| 252 TIR_Print("Old version of enum had %" Pd " elements\n", | |
| 253 old_enum_values.Length()); | |
| 254 | |
| 255 for (intptr_t i = 0; i < old_enum_names.Length(); i++) { | |
| 256 enum_name = String::RawCast(old_enum_names.At(i)); | |
| 257 const intptr_t index_in_new_cls = IndexOfEnum(enum_names, enum_name); | |
| 258 if (index_in_new_cls < 0) { | |
| 259 // Doesn't exist in new enum, add. | |
| 260 TIR_Print("Adding enum value `%s` to %s\n", | |
| 261 enum_name.ToCString(), | |
| 262 this->ToCString()); | |
| 263 enum_value = old_enum_values.At(i); | |
| 264 ASSERT(!enum_value.IsNull()); | |
| 265 to_add.Add(enum_name); | |
| 266 to_add.Add(enum_value); | |
| 267 } else { | |
| 268 // Exists in both the new and the old. | |
| 269 TIR_Print("Moving enum value `%s` to %" Pd "\n", | |
| 270 enum_name.ToCString(), | |
| 271 index_in_new_cls); | |
| 272 // Grab old value. | |
| 273 enum_value = old_enum_values.At(i); | |
| 274 // Update index to the be new index. | |
| 275 UpdateEnumIndex(Instance::Cast(enum_value), | |
| 276 index_field, | |
| 277 index_in_new_cls); | |
| 278 // Chop off the 'EnumClass.' | |
| 279 enum_field_name = String::SubString(enum_name, | |
| 280 enum_class_name.Length() + 1); | |
| 281 ASSERT(!enum_field_name.IsNull()); | |
| 282 // Grab the static field. | |
| 283 enum_field = LookupStaticField(enum_field_name); | |
| 284 ASSERT(!enum_field.IsNull()); | |
| 285 // Use old value with updated index. | |
| 286 enum_field.SetStaticValue(Instance::Cast(enum_value), true); | |
| 287 enum_values.SetAt(index_in_new_cls, enum_value); | |
| 288 enum_names.SetAt(index_in_new_cls, enum_name); | |
| 289 } | |
| 290 } | |
| 291 | |
| 292 if (to_add.Length() == 0) { | |
| 293 // Nothing to do. | |
| 294 TIR_Print("Found no missing enums in %s\n", ToCString()); | |
| 295 return; | |
| 296 } | |
| 297 | |
| 298 // Grow the values and enum_names arrays. | |
| 299 const intptr_t offset = enum_names.Length(); | |
| 300 const intptr_t num_to_add = to_add.Length() / 2; | |
| 301 ASSERT(offset == enum_values.Length()); | |
| 302 enum_names = Array::Grow(enum_names, | |
| 303 enum_names.Length() + num_to_add, | |
| 304 Heap::kOld); | |
| 305 enum_values = Array::Grow(enum_values, | |
| 306 enum_values.Length() + num_to_add, | |
| 307 Heap::kOld); | |
| 308 | |
| 309 // Install new names and values into the grown arrays. Also, update | |
| 310 // the index of the new enum values and add static fields for the new | |
| 311 // enum values. | |
| 312 Field& enum_value_field = Field::Handle(); | |
| 313 for (intptr_t i = 0; i < num_to_add; i++) { | |
| 314 const intptr_t target_index = offset + i; | |
| 315 enum_name = String::RawCast(to_add.At(i * 2)); | |
| 316 enum_value = to_add.At(i * 2 + 1); | |
| 317 | |
| 318 // Update the enum value's index into the new arrays. | |
| 319 TIR_Print("Updating index of %s in %s to %" Pd "\n", | |
| 320 enum_name.ToCString(), | |
| 321 ToCString(), | |
| 322 target_index); | |
| 323 UpdateEnumIndex(Instance::Cast(enum_value), index_field, target_index); | |
| 324 | |
| 325 enum_names.SetAt(target_index, enum_name); | |
| 326 enum_values.SetAt(target_index, enum_value); | |
| 327 | |
| 328 // Install new static field into class. | |
| 329 // Chop off the 'EnumClass.' | |
| 330 enum_field_name = String::SubString(enum_name, | |
| 331 enum_class_name.Length() + 1); | |
| 332 ASSERT(!enum_field_name.IsNull()); | |
| 333 enum_field_name = Symbols::New(thread, enum_field_name); | |
| 334 enum_value_field = Field::New(enum_field_name, | |
| 335 /* is_static = */ true, | |
| 336 /* is_final = */ true, | |
| 337 /* is_const = */ true, | |
| 338 /* is_reflectable = */ true, | |
| 339 *this, | |
| 340 Object::dynamic_type(), | |
| 341 token_pos()); | |
| 342 enum_value_field.set_has_initializer(false); | |
| 343 enum_value_field.SetStaticValue(Instance::Cast(enum_value), true); | |
| 344 enum_value_field.RecordStore(Instance::Cast(enum_value)); | |
| 345 AddField(enum_value_field); | |
| 346 } | |
| 347 | |
| 348 // Replace the arrays stored in the static fields. | |
| 349 enum_names_field.SetStaticValue(enum_names, true); | |
| 350 enum_values_field.SetStaticValue(enum_values, true); | |
| 351 } | |
| 352 | |
| 353 | |
| 354 void Class::PatchFieldsAndFunctions() const { | |
| 355 // Move all old functions and fields to a patch class so that they | |
| 356 // still refer to their original script. | |
| 357 const PatchClass& patch = | |
| 358 PatchClass::Handle(PatchClass::New(*this, Script::Handle(script()))); | |
| 359 ASSERT(!patch.IsNull()); | |
| 360 | |
| 361 const Array& funcs = Array::Handle(functions()); | |
| 362 Function& func = Function::Handle(); | |
| 363 Object& owner = Object::Handle(); | |
| 364 for (intptr_t i = 0; i < funcs.Length(); i++) { | |
| 365 func = Function::RawCast(funcs.At(i)); | |
| 366 if ((func.token_pos() == TokenPosition::kMinSource) || | |
| 367 func.IsClosureFunction()) { | |
| 368 // Eval functions do not need to have their script updated. | |
| 369 // | |
| 370 // Closure functions refer to the parent's script which we can | |
| 371 // rely on being updated for us, if necessary. | |
| 372 continue; | |
| 373 } | |
| 374 | |
| 375 // If the source for this function is already patched, leave it alone. | |
| 376 owner = func.RawOwner(); | |
| 377 ASSERT(!owner.IsNull()); | |
| 378 if (!owner.IsPatchClass()) { | |
| 379 ASSERT(owner.raw() == this->raw()); | |
| 380 func.set_owner(patch); | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 const Array& field_list = Array::Handle(fields()); | |
| 385 Field& field = Field::Handle(); | |
| 386 for (intptr_t i = 0; i < field_list.Length(); i++) { | |
| 387 field = Field::RawCast(field_list.At(i)); | |
| 388 owner = field.RawOwner(); | |
| 389 ASSERT(!owner.IsNull()); | |
| 390 if (!owner.IsPatchClass()) { | |
| 391 ASSERT(owner.raw() == this->raw()); | |
| 392 field.set_owner(patch); | |
| 393 } | |
| 394 field.ForceDynamicGuardedCidAndLength(); | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 | |
| 399 bool Class::CanReload(const Class& replacement) const { | |
| 400 #if defined(DEBUG) | |
| 401 { | |
| 402 ASSERT(IsolateReloadContext::IsSameClass(*this, replacement)); | |
| 403 } | |
| 404 #endif | |
| 405 | |
| 406 if (is_enum_class() && !replacement.is_enum_class()) { | |
| 407 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 408 "Enum class cannot be redefined to be a non-enum class: %s", | |
| 409 ToCString()))); | |
| 410 return false; | |
| 411 } | |
| 412 | |
| 413 if (!is_enum_class() && replacement.is_enum_class()) { | |
| 414 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 415 "Class cannot be redefined to be a enum class: %s", | |
| 416 ToCString()))); | |
| 417 return false; | |
| 418 } | |
| 419 | |
| 420 if (is_finalized()) { | |
| 421 const Error& error = | |
| 422 Error::Handle(replacement.EnsureIsFinalized(Thread::Current())); | |
| 423 if (!error.IsNull()) { | |
| 424 IRC->ReportError(error); | |
| 425 return false; | |
| 426 } | |
| 427 TIR_Print("Finalized replacement class for %s\n", ToCString()); | |
| 428 } | |
| 429 | |
| 430 if (is_finalized()) { | |
| 431 // Get the field maps for both classes. These field maps walk the class | |
| 432 // hierarchy. | |
| 433 const Array& fields = | |
| 434 Array::Handle(OffsetToFieldMap()); | |
| 435 const Array& replacement_fields = | |
| 436 Array::Handle(replacement.OffsetToFieldMap()); | |
| 437 | |
| 438 // Check that we have the same number of fields. | |
| 439 if (fields.Length() != replacement_fields.Length()) { | |
| 440 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 441 "Number of instance fields changed in %s", ToCString()))); | |
| 442 return false; | |
| 443 } | |
| 444 | |
| 445 // Verify that field names / offsets match across the entire hierarchy. | |
| 446 Field& field = Field::Handle(); | |
| 447 String& field_name = String::Handle(); | |
| 448 Field& replacement_field = Field::Handle(); | |
| 449 String& replacement_field_name = String::Handle(); | |
| 450 for (intptr_t i = 0; i < fields.Length(); i++) { | |
| 451 if (fields.At(i) == Field::null()) { | |
| 452 ASSERT(replacement_fields.At(i) == Field::null()); | |
| 453 continue; | |
| 454 } | |
| 455 field = Field::RawCast(fields.At(i)); | |
| 456 replacement_field = Field::RawCast(replacement_fields.At(i)); | |
| 457 field_name = field.name(); | |
| 458 replacement_field_name = replacement_field.name(); | |
| 459 if (!field_name.Equals(replacement_field_name)) { | |
| 460 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 461 "Name of instance field changed ('%s' vs '%s') in '%s'", | |
| 462 field_name.ToCString(), | |
| 463 replacement_field_name.ToCString(), | |
| 464 ToCString()))); | |
| 465 return false; | |
| 466 } | |
| 467 } | |
| 468 } else if (is_prefinalized()) { | |
| 469 if (!replacement.is_prefinalized()) { | |
| 470 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 471 "Original class ('%s') is prefinalized and replacement class ('%s')", | |
| 472 ToCString(), replacement.ToCString()))); | |
| 473 return false; | |
| 474 } | |
| 475 if (instance_size() != replacement.instance_size()) { | |
| 476 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 477 "Instance size mismatch between '%s' (%" Pd ") and replacement " | |
| 478 "'%s' ( %" Pd ")", | |
| 479 ToCString(), | |
| 480 instance_size(), | |
| 481 replacement.ToCString(), | |
| 482 replacement.instance_size()))); | |
| 483 return false; | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 // native field count check. | |
| 488 if (num_native_fields() != replacement.num_native_fields()) { | |
| 489 IRC->ReportError(String::Handle(String::NewFormatted( | |
| 490 "Number of native fields changed in %s", ToCString()))); | |
| 491 return false; | |
| 492 } | |
| 493 | |
| 494 // TODO(johnmccutchan) type parameter count check. | |
| 495 | |
| 496 TIR_Print("Class `%s` can be reloaded (%" Pd " and %" Pd ")\n", | |
| 497 ToCString(), | |
| 498 id(), | |
| 499 replacement.id()); | |
| 500 return true; | |
| 501 } | |
| 502 | |
| 503 | |
| 504 bool Library::CanReload(const Library& replacement) const { | |
| 505 return true; | |
| 506 } | |
| 507 | |
| 508 | |
| 509 static const Function* static_call_target = NULL; | |
| 510 | |
| 511 void ICData::Reset(bool is_static_call) const { | |
|
rmacnak
2016/05/11 19:56:18
ICData should have an 'is_static_call' bit.
Cutch
2016/05/12 15:50:14
Added TODO
| |
| 512 if (is_static_call) { | |
| 513 const Function& old_target = Function::Handle(GetTargetAt(0)); | |
| 514 if (old_target.IsNull()) { | |
| 515 FATAL("old_target is NULL.\n"); | |
| 516 } | |
| 517 static_call_target = &old_target; | |
| 518 if (!old_target.is_static()) { | |
| 519 // TODO(johnmccutchan): Improve this. | |
| 520 TIR_Print("Cannot rebind super-call to %s from %s\n", | |
| 521 old_target.ToCString(), | |
| 522 Object::Handle(Owner()).ToCString()); | |
| 523 return; | |
| 524 } | |
| 525 const String& selector = String::Handle(old_target.name()); | |
| 526 const Class& cls = Class::Handle(old_target.Owner()); | |
| 527 const Function& new_target = | |
| 528 Function::Handle(cls.LookupStaticFunction(selector)); | |
| 529 if (new_target.IsNull()) { | |
| 530 // TODO(johnmccutchan): Improve this. | |
| 531 TIR_Print("Cannot rebind static call to %s from %s\n", | |
| 532 old_target.ToCString(), | |
| 533 Object::Handle(Owner()).ToCString()); | |
| 534 return; | |
| 535 } | |
| 536 ClearAndSetStaticTarget(new_target); | |
| 537 } else { | |
| 538 ClearWithSentinel(); | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 #endif // !PRODUCT | |
| 543 | |
| 544 } // namespace dart. | |
| OLD | NEW |