Chromium Code Reviews| Index: runtime/vm/object.cc |
| =================================================================== |
| --- runtime/vm/object.cc (revision 24510) |
| +++ runtime/vm/object.cc (working copy) |
| @@ -1492,6 +1492,7 @@ |
| 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()->no_such_method_cache_, Object::empty_array().raw()); |
| } |
| @@ -1748,6 +1749,96 @@ |
| } |
| +RawFunction* Class::GetNoSuchMethodDispatcher(const String& target_name, |
| + const Array& args_desc) const { |
| + enum { |
| + kNameIndex = 0, |
| + kArgsDescIndex, |
| + kFunctionIndex, |
| + kEntrySize |
| + }; |
| + |
| + Function& dispatcher = Function::Handle(); |
| + Array& cache = Array::Handle(no_such_method_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. |
|
srdjan
2013/07/09 15:20:28
WHy do you terminate on name being null AND on cac
Florian Schneider
2013/07/10 09:06:00
When growing by more than 1 element, the cache arr
|
| + if (!name.Equals(target_name)) continue; |
| + desc ^= cache.At(i + kArgsDescIndex); |
| + if (desc.raw() != args_desc.raw()) continue; |
| + // Found match. |
| + dispatcher ^= cache.At(i + kFunctionIndex); |
| + ASSERT(dispatcher.IsFunction()); |
| + break; |
|
srdjan
2013/07/09 15:20:28
I believe this is simpler:
if (desc.raw() == args_
Florian Schneider
2013/07/10 09:06:00
Done.
|
| + } |
| + |
| + if (dispatcher.IsNull()) { |
| + if (i == cache.Length()) { |
| + // Allocate new larger cache. |
| + intptr_t new_len = cache.Length() == 0 ? kEntrySize : cache.Length() * 2; |
|
srdjan
2013/07/09 15:35:40
Is it necessary to grow that aggressively? I would
Florian Schneider
2013/07/10 09:06:00
I don't think it matters, For the common case of f
|
| + cache ^= Array::Grow(cache, new_len); |
| + set_no_such_method_cache(cache); |
| + } |
| + dispatcher ^= CreateNoSuchMethodDispatcher(target_name, args_desc); |
| + cache.SetAt(i + kNameIndex, target_name); |
| + cache.SetAt(i + kArgsDescIndex, args_desc); |
| + cache.SetAt(i + kFunctionIndex, dispatcher); |
| + } |
| + return dispatcher.raw(); |
| +} |
| + |
| + |
| +RawFunction* Class::CreateNoSuchMethodDispatcher(const String& target_name, |
| + const Array& args_desc) const { |
| + Function& invocation = Function::Handle( |
| + Function::New(String::Handle(Symbols::New(target_name)), |
| + RawFunction::kNoSuchMethodDispatcher, |
| + false, // Not static. |
| + false, // Not const. |
| + false, // Not abstract. |
| + false, // Not external. |
| + *this, |
| + 0)); // No token position. |
| + ArgumentsDescriptor desc(args_desc); |
| + const intptr_t num_parameters = desc.Count(); |
| + invocation.set_num_fixed_parameters(num_parameters); |
| + invocation.SetNumOptionalParameters(0, true); |
| + invocation.set_parameter_types(Array::Handle(Array::New(num_parameters, |
| + Heap::kOld))); |
| + invocation.set_parameter_names(Array::Handle(Array::New(num_parameters, |
| + Heap::kOld))); |
| + // Receiver. |
| + invocation.SetParameterTypeAt(0, Type::Handle(Type::DynamicType())); |
| + invocation.SetParameterNameAt(0, Symbols::This()); |
| + // Remaining parameters. |
| + for (intptr_t i = 1; i < num_parameters; 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))); |
| + } |
| + 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::no_such_method_cache() const { |
| + return raw_ptr()->no_such_method_cache_; |
| +} |
| + |
| + |
| +void Class::set_no_such_method_cache(const Array& cache) const { |
| + StorePointer(&raw_ptr()->no_such_method_cache_, cache.raw()); |
| +} |
| + |
| + |
| void Class::Finalize() const { |
| ASSERT(!is_finalized()); |
| // Prefinalized classes have a VM internal representation and no Dart fields. |
| @@ -3555,6 +3646,21 @@ |
| } |
| +RawArray* Function::saved_args_desc() const { |
| + ASSERT(kind() == RawFunction::kNoSuchMethodDispatcher); |
| + const Object& obj = Object::Handle(raw_ptr()->data_); |
| + ASSERT(obj.IsArray()); |
| + return Array::Cast(obj).raw(); |
| +} |
| + |
| + |
| +void Function::set_saved_args_desc(const Array& value) const { |
| + ASSERT(kind() == RawFunction::kNoSuchMethodDispatcher); |
| + ASSERT(raw_ptr()->data_ == Object::null()); |
| + set_data(value); |
| +} |
| + |
| + |
| RawFunction* Function::parent_function() const { |
| if (IsClosureFunction()) { |
| const Object& obj = Object::Handle(raw_ptr()->data_); |