-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Hot-restart on web may preserve the state of Expando objects #45874
Comments
fyi - @sigmundch @nshahan @Markzipan I suspect this would be rather expensive to do as (I think) we store the expando value on the JS object directly. |
Just to add additional context; on FlutterFire we store references to the JS Firebase objects in Expando pretty much everywhere. The issue was discovered as callback subscriptions (e.g. |
Thanks for the report! Indeed @vsmenon, trying to clear things out with the current implementation is likely too expensive. We'd need to swap the implementation of expandos with a WeakMap instead. |
Ha, there is even a TODO from Jenny to do so -- not surprising :) |
Out of curiosity, why is the instance of Expando reused to being with? Shouldn't a hot restart create a new Expando instance? |
We don't actually track / clear all instances explicitly. Instead, we track "roots" like globals and class static variables and clear them. We rely on GC to clear the rest. That is much cheaper to do. The problem here is that the JSObject is an unaccounted for root. |
Good question @rrousselGit! @vsmenon I think what @rrousselGit is hinting here is that we are actually using a separate expando instance. That is. Yes, the underlying js-object is the same, and the old expando properties are leaked and kept in that object. However, two expando instances shouldn't collide with one another. If you had two expandos in a single program, they would have separate data as well. The code above declares the expando in a top level Dart variable, so the expectation would be that that new expando would be created after a hot restart. Looking more closely, I believe we do create a new expando, but that we reuse the same key to store it's data in JavaScript. The "key" is derived from a counter that is also in a static top-level variable. That top-level variable is getting reset as well after a hot restart. As a result, a brand new Expando instance after a hot restart is reusing the same key as the original program. We should be able to fix this by not resetting the key after hot restarts. Switching to WeakMaps would elide this problem because there wouldn't be any global state to distinguish expandos anymore. |
Expandos collide after a hot restart. This currently fails because the DDC implementation uses a static field (_keyCounter) to compute a unique name for each expando. After a hot-restart the counter gets reset and keys get reused accidentally. A hacky fix would be to ensure this field is not reset after restarts, but a more robust fix would be to use a weak map implementation like dart2js does. Change-Id: I991874aabf836be66cbd44de07dd38e681415ae7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/199221 Reviewed-by: Nicholas Shahan <nshahan@google.com>
@sigurdm I believe this was closed inadvertently, as the commit only adds tests instead of fixing the issue. |
@sigmundch - did github accidentally close this? ( Thanks for the catch @rrousselGit ) |
Not on this case :) - but it is confusing. There are two commits referenced above. For some reason the cross reference comment mentions one (the change that adds the test - 3a0c27b) but the message indicating that dart-bot closed this issue points to the other (the change with the fix - 460887d). Maybe it is a result of the fact that I used a "stacked" CL in gerrit, which landed 2 CLs together and pushed two commits at once? |
Here is a reproduction repository created specifically for this issue https://github.com/rrousselGit/hot-restart-expando-reproduction
Explanation
When an
Expando
has a JS object as a key, performing a hot-restart may not empty theExpando
Example:
Where
getJSObject
is a global JS function defined in theindex.html
as:With this code:
null
null
it will print42
, which is unexpected.The expected behavior is that after a hot-restart, the application will print
null
again. As otherwise, this means some state is preserved between hot-restartsThe text was updated successfully, but these errors were encountered: