-
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
JS Interop types have .hashCode == 0
#55823
Comments
@mkustermann does this look like a compiler or interop problem to you? |
@huanghui1998hhh If you share the repro you have (code + build instructions) we may be able to minimize it. |
@huanghui1998hhh Could you build your app with Otherwise I think we'll need to get a reproduction (be it minimal or not) under an open source license, so we can reproduce and debug it. (Side note: Looking at the code, it seems to have |
Sorry, my bad. |
Thanks for sharing your example. Good news is that we will have something to help with these issues in the future. @srujzs created a lint that will warn about casts that are inconsistent between dart2js and dart2wasm. The lint will be available with Dart 3.5 (now available on the dev channel, but hopefully soon will be released in the beta channel)
Good question. We accidentally do better in dart2js. We have the same behavior (hashcode == 0) for pure JS objects. However, anything in I see two options here:
We don't want to do (a) for all JS types, but only for some. For example, we need to exclude primitives and I'd like to also exclude JS object literals (where adding extra properties can be disruptive). On dart2js, we have the means to do some introspection (e.g. lookup an interceptor) to discern and take a specialized approach, but I don't believe that's practical in dart2wasm. TL;DR - I sense we may need to settle with (b). @mkustermann @srujzs - do you agree? |
The lint name is
I might be looking at the wrong code, but from debugging, it looks like we just directly compute a hash and store it on the instance and not use an expando. DDC is the same, except for arbitrary JS objects, DDC still computes a hash and stores it whereas dart2js always returns 0.
Do you mean compute a unique hash code for every JS object in dart2js regardless of whether it's a type intercepted in One idea might be to compute a unique hash code for an arbitrary JS instance and cache it in the
If we were to disallow it, it might not be discoverable until runtime in some cases e.g. upcasting a
We may not be able to lint in all cases, but it may be doable. At the very least, I should document this somewhere. |
Sorry, I didn't mean a Dart expando, but a property in JavaScript. When the extra property is added to a native object that was not not originally part of the prototype is also called an expando too. Sorry for the confusion 😕
Correct, modulo excluding primitives and object literals (since we don't want to add a property to those).
This happens today! For example, this program prints 0, but if you uncomment the import 'package:web/web.dart';
// import 'dart:html';
main() {
print(HTMLDivElement().hashCode);
} (I thought we were smarter and also removed
Do you mean caching it on both the JSWrapper and the JS object? At first I thought you meant only a property on the JS object (which makes 2 wrappers have a consistent value.). If we do only that, it may be too expensive: it would require that However, if we have second cache in the JSValue wrapper, then I can see this working more efficiently.
Yeah, I wouldn't change the hash of existing objects, that would be a bad regression
👍 At least we should be able to recognize when the type parameter of a Set or Map is an interop type and warn about it in those cases. |
Hah, I knew I was missing something.
Makes sense. We'd need to revisit this if we want similar hashing as dart2wasm for apps that migrate away from
Right, I was thinking store the hash code after the initial computation in a field in |
Is this an opt-in lint or always on? IMHO this should be always on. I'd even say that if we know such an
Currently any Dart object can be hashed and put into maps/sets because we have a) hash of a primitive value b) randomly distributed identity hash c) user defined IMHO it's not acceptable to silently break this behavior by making all JS objects get a 0 hash code. Then the data structures users expect to be O(1) suddenly become O(n) without their knowledge. In fact they become slower than using a simple list. So I'd argue the options are: a) Disallow hashing of JS objects (make b) We lazily attach an extra hash-code property on a JS object when needed (basically our identity hash code) c) We use an identity hash code on the Dart wrapper of a JS object with the downside that the same JS object with two different wrappers will get different hashes (this will lead to difference in behaviors between dart2js & dart2wasm - so may not be a good idea). Or do a) / b) / c) based on the type of the JS object. Maybe adding someone from language team for opinions (/cc @lrhn @leafpetersen ) |
I'm trying to get it into the recommended set of lints: dart-lang/lints#188. It's set up so that it's a lint on all platforms but ignores checks that involves interop types that can only be written in dart2js/DDC. I think (b) is the way forward, although I'm not sure if it has to be a property on the JS object. Users only pay the cost of the interop call if they're hashing, and it's a one-time cost per object. (a) would be too breaking in dart2js/DDC. We could do this only for dart2wasm, but then it's not discoverable and would be quite frustrating. I don't like (c) for the same reason I want to fix #55515. |
If we can't provide a non-aweful hash, I'd prefer to have If we can give some JS objects good hash codes (maybe |
/cc @rakudrama For how JS performance may be impacted if we lazily attach id hashes to JS objects. |
My preference is to do something that is backwards compatible and doesn't break existing code (even if we ignore the new I think it's feasible to do (b) and store the hash in JS. It would be good to verify this strategy doesn't cause issues on the JS side. Some libraries are sensitive to modifications to objects in a public way. We had issues like these in the past when we stored an expando for List RTIs in the past. Hopefully, using a symbol key for the expando could be enough to avoid problems. |
I encountered an issue where the following code works fine when running with
However, after building the web project with
To resolve the issue, I used the following code, which works correctly both during runtime and after building:
|
@mrtnetwork Would you be willing to file a new issue with a small standalone reproduction (complete example & build command)? (We have repurposed this bug for a discussion about hash codes of interop types) |
.hashCode == 0
Sorry, I used the wrong compile command. It seems that the issue is related to Flutter’s Dart compilation. |
When call this function here, it throw this error(only on WASM).
I'm trying to build a minimal reproduction, but i failed. At first i guess this problem is caused by extension on extension type, but seem not. My demo run well, so i can only provide the permalink.
The text was updated successfully, but these errors were encountered: