diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc index 2b29a342579..78b19fbdce2 100644 --- a/runtime/vm/code_generator.cc +++ b/runtime/vm/code_generator.cc @@ -448,7 +448,9 @@ DEFINE_RUNTIME_ENTRY(ResolvePatchInstanceCall, 1) { DartFrame* caller_frame = iterator.NextFrame(); String& function_name = String::Handle(); if ((!receiver.IsNull() && code.IsNull()) || !FLAG_inline_cache) { - // Let megamorphic lookup handle noSuchMethod. + // We did not find a method; it means either that we need to invoke + // noSuchMethod or that we have encountered a situation with implicit + // closures. All these cases are handled by the megamorphic lookup stub. CodePatcher::PatchInstanceCallAt( caller_frame->pc(), StubCode::MegamorphicLookupEntryPoint()); if (FLAG_trace_ic) { @@ -528,15 +530,24 @@ DEFINE_RUNTIME_ENTRY(ResolvePatchInstanceCall, 1) { } -static RawFunction* LookupDynamicFunction(Class* cls, const String& name) { +static RawFunction* LookupDynamicFunction(const Class& in_cls, + const String& name) { + Class& cls = Class::Handle(); + // For lookups treat null as an instance of class Object. + if (in_cls.IsNullClass()) { + cls = Isolate::Current()->object_store()->object_class(); + } else { + cls = in_cls.raw(); + } + Function& function = Function::Handle(); - while (!cls->IsNull()) { + while (!cls.IsNull()) { // Check if function exists. - function = cls->LookupDynamicFunction(name); + function = cls.LookupDynamicFunction(name); if (!function.IsNull()) { break; } - *cls = cls->SuperClass(); + cls = cls.SuperClass(); } return function.raw(); } @@ -554,11 +565,6 @@ DEFINE_RUNTIME_ENTRY(ResolveImplicitClosureFunction, 2) { kResolveImplicitClosureFunctionRuntimeEntry.argument_count()); const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); const String& original_func_name = String::CheckedHandle(arguments.At(1)); - if (receiver.IsNull()) { - // No implicit closure functions on null. - GrowableArray args; - Exceptions::ThrowByType(Exceptions::kNullPointer, args); - } const String& getter_prefix = String::Handle(String::New("get:")); Closure& closure = Closure::Handle(); if (!original_func_name.StartsWith(getter_prefix)) { @@ -574,7 +580,7 @@ DEFINE_RUNTIME_ENTRY(ResolveImplicitClosureFunction, 2) { func_name = String::SubString(original_func_name, getter_prefix.Length()); func_name = String::NewSymbol(func_name); const Function& function = - Function::Handle(LookupDynamicFunction(&receiver_class, func_name)); + Function::Handle(LookupDynamicFunction(receiver_class, func_name)); if (function.IsNull()) { // There is no function of the same name so can't be the case where // we are trying to create an implicit closure of an instance function. diff --git a/tests/language/language.status b/tests/language/language.status index 5c0fb9ac56b..f887d584cd5 100644 --- a/tests/language/language.status +++ b/tests/language/language.status @@ -87,6 +87,7 @@ InterfaceFactory3NegativeTest: Fail # 5387405 GenericParameterizedExtendsTest: Skip # Bug 5392297 ConstObjectsAreImmutableTest: Fail # Bug 5202940 SuperNegativeTest: Fail # Now that super calls are automatically injected this test doesn't make sense +NullToStringTest: Fail # Bug 5421978 # Crashes in dartc. FunctionTypeAliasTest: Crash # Bug 4519208. diff --git a/tests/language/src/NullToStringTest.dart b/tests/language/src/NullToStringTest.dart new file mode 100644 index 00000000000..19eef3ac416 --- /dev/null +++ b/tests/language/src/NullToStringTest.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2011, 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. +// Test correct handling of NULL object in invocation and implicit closures. + +main() { + var nullObj = null; + var x = nullObj.toString(); + Expect.isTrue(x is String); + var y = nullObj.toString; + Expect.isNotNull(y); +}