-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
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:
- Fixed some consistency issues with Function.toJS across all compilers. Specifically, calling Function.toJS on the same function gives you a new JS function (see issue Semantics of
dart:js_interopsFunction.toJSextensionΒ #55515), the maximum number of arguments that are passed to the JS function is determined by the static type of the Dart function, and extra arguments are dropped when passed to the JS function in all compilers (see JS interop makes non-breaking JS changes break Dart codeΒ #48186).
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 π.