dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.28k stars 1.58k forks source link

Expression evaluation on extension getters for nullable types throw exception #57040

Open FMorschel opened 2 weeks ago

FMorschel commented 2 weeks ago

Repro

extension on String? {
  bool get isNullOrEmpty {
    var str = this;
    return str == null || str.isEmpty;
  }
}

void main() {
  String? str;
  print(str.isNullOrEmpty);
}

Place a breakpoint on print and evaluate str.isNullOrEmpty you get:

Unhandled exception:
NoSuchMethodError: The getter 'isNullOrEmpty' was called on null.
Receiver: null
Tried calling: isNullOrEmpty
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1      Eval ()
#2      main (package:bug/a.dart:10:13)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

CC: @bkonyi

dart-github-bot commented 2 weeks ago

Summary: The issue is that evaluating an extension getter on a nullable type that is null throws a NoSuchMethodError instead of returning the expected value. This occurs when using a breakpoint in the debugger to evaluate the expression.

derekxu16 commented 1 week ago

I believe that this requires frontend changes to fix. I added this logging statement:

OS::PrintErr("%s\n", js->ToCString());

under this line:

https://github.com/dart-lang/sdk/blob/be7a40dd78655709cb512a9758437d90182c2f42/runtime/vm/service.cc#L3275

and these logging statements:

#if !defined(PRODUCT)
    for (intptr_t i = 0; i < eval_function.NumParameters(); ++i) {
      OS::PrintErr(
          "parameter_names[%ld]: %s\n", i,
          String::Handle(eval_function.ParameterNameAt(i)).ToCString());
      OS::PrintErr(
          "parameter_types[%ld]: %s\n", i,
          AbstractType::Handle(eval_function.ParameterTypeAt(i)).NameCString());
    }
    for (intptr_t i = 0; i < arguments.Length(); ++i) {
      OS::PrintErr("arguments[%ld]: address: %p, type: %s\n", i,
                   arguments.At(i).untag(),
                   Object::Handle(arguments.At(i)).JSONType());
    }
#endif  // !defined(PRODUCT)

under this line:

https://github.com/dart-lang/sdk/blob/be7a40dd78655709cb512a9758437d90182c2f42/runtime/vm/object.cc#L4954

and then followed the reproduction steps in the issue description, and the output was:

> str.isNullOrEmpty
{"jsonrpc":"2.0", "result":{"param_names":["str"],"param_types":["null"],"type_params_names":[],"type_params_bounds":[],"type_params_defaults":[],"libraryUri":"file:\/\/\/usr\/local\/google\/home\/derekx\/programming\/dart-sdk\/sdk\/local_test_files\/test_extension_methods.dart","method":"main","tokenPos":157,"scriptUri":"file:\/\/\/usr\/local\/google\/home\/derekx\/programming\/dart-sdk\/sdk\/local_test_files\/test_extension_methods.dart","isStatic":true
parameter_names[0]: str
parameter_types[0]: dynamic
arguments[0]: address: 0x7fcfe3e88080, type: null
Unhandled exception:
NoSuchMethodError: The getter 'isNullOrEmpty' was called on null.
Receiver: null
Tried calling: isNullOrEmpty
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
#1      Eval ()
#2      main (file:///usr/local/google/home/derekx/programming/dart-sdk/sdk/local_test_files/test_extension_methods.dart:10:13)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

So, it doesn't look like the VM is sending anything incorrect to the frontend. @johnniwinther or @jensjoha, can you please take a look at this?