-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamic property access on js object not working on release mode #59837
Comments
Thanks for the question @MarcoPeli This is actually an intentional choice. Both There are some references to these limitations in some of our docs (when discussing limitations of package-web and comparing When possible, we recommend to bring the logic back to a statically defined API, as that is generally better for performance and producing smaller compilation artifacts. So for example, if there is an API you expect, you can define an interop type for it and use it: extension type A._(JSObject _) implements JSObject {
external Target get target;
}
extension type Target._(JSObject _) implements JSObject {
external String get result;
}
...
main() {
...
str = (obj as A).target.result;
} If your goal is to read directly properties in JS that are not well specified ahead of time, then there is also import 'dart:js_interop_unsafe';
...
main() {
...
str = (((obj as JSObject)['target'] as JSObject)['result'] as JSString).toDart;
}
|
@sigmundch sigmundch, I understand your point. But the main problem is: why same code is working on dartpad? Why does it work on debug mode? Isn't that misleading? I worked under the assumption that code running on debug mode would also work on release mode - a natural assumption for all developers on the entire world :P If the code is not intended to work on release mode, make it break on debug mode as well, please. I really doubt the code breaking only on release mode is an intentional choice. |
Excellent question! I share your natural assumption too, btw. Sorry I didn't address this inconsistency in my first comment. I agree, debug mode should be stricter and disallow these dynamic calls too. Unfortunately, we are in a position where can't fix this until we remove support for the old Today we are in the uncomfortable state that we have to support both the old and the new mechanisms for a transition period. Since both the interop sit on top of JS values, those JS values can be passed and cast between the old and new interop representations. And, because the old mechanism supports dynamic calls, they may accidentally be available some scenarios. What scenarios make that happen depends on some characteristics of each compilers. Generally, our release compiler can see the whole program, and can decide which dynamic calls are supported based on the types it sees in the code. This is why you saw the error above and why it's hard to run into this in practice. That said, you can technically also produce a case where the release compiler allows the dynamic calls accidentally. It's a bit contrived, but it requires confusing the compiler so it doesn't know what it can be calling and so that it has the names elsewhere in the program that you happen to be calling too. For example: import 'dart:js_interop' hide JS;
import 'package:js/js.dart' show JS;
// Note the types here don't matter, we are
// adding these declarations so that the
// compiler believes it needs to support virtual
// and dynamic dispatch of the methods declared
// here.
@JS() class A { external int get target; }
@JS() class B { external int get result; }
@JS('x') external A get a;
@JS('y') external B get b;
// A strange way to confuse our compiler today
// is to conflate many values into a single
// function
@pragma('dart2js:never-inline')
dynamic confuse(dynamic x) => x;
main() {
confuse(a);
confuse(b);
final o = confuse({'target': {'result': 'ok'}}.jsify());
// Here the release compiler can't tell whether `o` is an interop type
// of the style of package:js or the new kind of interop.
// In the end, this happens to print 'ok'!!
print(o.target.result);
} This is unlikely in practice though, you'd clearly need to have reasons for the compiler to keep around In contrast, our debug compiler doesn't see the whole program. Instead, it makes all decisions in a modular fashion. Since some modules may call JS values dynamically, it supports all dynamic calls by default. Until we drop support for Since that's not available yet, one thing that many have opted to do is to enable the avoid_dynamic_calls lint rule to help highlight where these dynamic calls may be occurring in the codebase. This helps provide an early signal that something may not match between debug and release mode. |
Flutter 3.27.1 • channel stable • https://github.com/flutter/flutter.git
Framework - revision 17025dd882 - Engine - revision cb4b5fff73 - Tools - Dart 3.6.0 - DevTools 2.40.2
My use case is reading event.target.result from web package indexedDB. But we don't need to go that far to reproduce the error. Check the dartpad code running fine on debug mode: https://dartpad.dev/?id=25d7be1f405f153d24bac3d862adcd58
Now you need to run same code on release mode. Dartpad don't allow it but you can check here: https://marcopeli.github.io/temp/ opening the console to see the error bellow:
The text was updated successfully, but these errors were encountered: