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.21k stars 1.57k forks source link

[js_interop] Field with late initializer does not initialize correctly #56135

Open pattobrien opened 3 months ago

pattobrien commented 3 months ago

Description

Given the below snippet:

Future<void> main() async {
  final foo = Foo();
  print('value1: ${foo.value1}');
  print('value2: ${foo.value2}');
}

class Foo {
  Foo() {
    value2 = 'foo';
  }

  late final String value1 = 'foo';
  late final String value2;
}

And compiled to JS using the following command:

webdev build --no-release

One would assume that value1 and value2 below would print the same thing (i.e. "foo"). Instead, the output is the following:

value1: Instance of 'JavaScriptObject'
value2: foo

Impact

I spent a few hours debugging some issues revolving around a late-initialized field instance, but rather than still being able to access the field, my program was instead throwing a "noSuchMethod" exception when trying to access a method on that object (a more severe problem than the one stated above).

I was able to resolve the issue by initializing the field in the constructor body, but unfortunately was not able to reproduce the exact "noSuchMethod" behavior in a shareable repo. I'm hoping someone more knowledgeable about late initialization could debug further.

Additional Information

Dart SDK version: 3.5.0-277.0.dev (dev) (Tue Jun 18 13:02:52 2024 -0700) on "macos_arm64"

dart-github-bot commented 3 months ago

Summary: The issue is that a late-initialized field in a Dart class does not initialize correctly when compiled to JavaScript using webdev build. The field value1 prints as a JavaScriptObject instead of the expected string "foo", while value2 initializes correctly. This behavior leads to unexpected errors when accessing methods on the object.

sigmundch commented 3 months ago

@pattobrien thanks for reaching out and filing this issue!

I've tried to reproduce it locally, but have not been successful in seeing what you saw. Your description seems to indicate that you are using the development-mode in webdev (--no-release), which internally uses our development compiler (DDC). I tried the sample above with multiple versions of the compiler, with and without webdev, in dev and release mode, and in all cases I see the desired output (foo printed twice). Any chance I'm missing a step or doing it incorrectly?

In your title you mention js-interop, any chance this only happens in combination with something else related to js-interop?