Skip to content

Regression in allowInterop idempotence in DDC in Dart >=3.3.0 <3.5.0Β #56897

@greglittlefield-wf

Description

@greglittlefield-wf

I noticed that a regression of #38311 was introduced in Dart 3.3.0, and was fixed in Dart 3.5.0.

The broken behavior is the same as described in that previous issue, and also is what's advertised in allowInterop's doc comment:

Calling this method repeatedly on a function will return the same result.

Using same reduced test case from that issue...

function() {}
final once = allowInterop(function);
final twice = allowInterop(once);
print(identical(once, twice)); // prints false

...I get the following results in these arbitrary Dart SDK versions that I tested:

  • prints true (good behavior)
    • 2.19.6, 3.0.0, 3.2.0, 3.2.6, 3.5.0, 3.5.3, 3.6.0-334.0.dev
  • prints false (bad behavior)
    • 3.3.0, 3.4.4

Even though the issue was fixed, it wasn't clear to me what exactly fixed the issue, whether it was fixed on purpose, or whether test coverage was added for this case.

And it looks like when the original issue was fixed (in this commit), no tests were added, so I'm worried that this issue might not have test coverage and could regress again πŸ˜•.

I noticed a potentially related changelog entry in the 3.5.0 release notes:

However, the linked issue that was resolved and the commit fixing it seemed more related to .toJS than allowInterop.


If it's helpful, I can also provide a little more context on how I'm currently relying on this behavior.

Right now, I have some code utilizing package:js interop and passing arbitrary values to JS code.

Some of those values are meant for consumption by JS code, and others (including Dart objects, and notably Dart functions) are meant only for consumption by Dart code reading them back from JS at a later point, and are fine to be treated as opaque to JS.

Values that have already been wrapped in allowInterop are assumed to be meant for consumption by JS code, and need to be passed through as-is, whereas Dart functions need to be wrapped in another object so they don't throw when passed over to JS. Without this, the assert in dart:js_util's setProperty fails with the message: Dart function requires allowInterop to be passed to JavaScript..

The utility that conditionally wraps functions lives here, and uses the idempotent behavior of allowInterop to test whether a function has already been wrapped in allowInterop.

I anticipate that this dynamic, conditional wrapping of functions may no longer be possible after switching to the latest Dart JS interop, and we'll need to refactor the code that prepares those values. But, for the time being, we rely on that behavior.

If there are any less-hacky ways of testing whether a function can be called from JS, I'm all ears πŸ˜„.

Metadata

Metadata

Assignees

Labels

area-web-jsIssues related to JavaScript support for Dart Web, including DDC, dart2js, and JS interop.type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)web-dev-compilerweb-js-interopIssues that impact all js interop

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions