Skip to content

Conversation

@cbracken
Copy link
Member

MethodCall.arguments is of type dynamic, which allows for Iterables. Its
hashCode getter was implemented with hashValue() which asserts that its
input is not an Iterable. Instead XOR the method name hashCode with the
identityHashCode of the arguments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes hashCode inconsistent with equals. For lists and map arguments, you'd basically only have consistency with const expressions. As an example,

final c1 = new MethodCall('foo', {'bar':'baz'});
final c2 = new MethodCall('foo', {'bar':'baz'});
assert(c1 == c2);
assert(c1.hashCode() != c2.hashCode());

I think we should change hashValues to work with any type instead. Mostly because I'm sure I've made the same mistake in a number of other places.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was already broken before, since hashValues intentionally doesn't do anything with lists and doesn't do anything deep with maps.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hashValues intentionally asserts with Iterables because you almost never want to compare the actual object of a list. It definitely shouldn't automatically be deep because that's very expensive and expensive things should look expensive.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real solution here is to not use "dynamic". Every time we use "dynamic" we end up with bugs because it basically turns off the analyzer. Do we have to make the arguments dynamic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also do we need MethodCall to be comparable in this deep way?

Copy link
Contributor

@mravn-google mravn-google Sep 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your time, Ian.

This was already broken before [...]

Yes, you're right of course. My original code expected hashValues to go deep.

hashValues intentionally asserts with Iterables because you almost never want to
compare the actual object of a list. It definitely shouldn't automatically be deep
because that's very expensive and expensive things should look expensive.

Expecting equality and hash code to be deep is a remnant from my Java days where the Collections framework does it that way. I repeatedly find myself missing that behavior of Dart lists and maps.

The real solution here is to not use "dynamic". Every time we use "dynamic" we
end up with bugs because it basically turns off the analyzer. Do we have to make
the arguments dynamic?

The argument of a MethodCall could have been made List<dynamic> or Map<String, dynamic>, bringing overhead in code and at runtime to situations where we just want to pass a bool/int/String value. And I guess it would only move the problem with dynamic to the list entries or map values? In the end, the argument is dynamic for the same reason hashValues is parameterized with Object: We don't have a type that captures precisely the valid method call arguments (trees of nested lists and maps with bool/int/String leaves), nor a type that captures the non-Iterables accepted by hashValues.

Also do we need MethodCall to be comparable in this deep way?

I originally gave MethodCall a custom equals (and therefore hashCode) to facilitate writing unit tests. I don't have a production code use case, but one could conceivably have a Map<MethodCall, dynamic> cache in a situation where calculating the result is expensive. Whether we want to support that use case is an open question. If not, we could (remove the custom equality and hash and fix up the unit tests, or) accept a hash code with more collisions:

int get hashCode => method.hashCode;

If we do want to support MethodCalls as map keys, we could instead do

int get hashCode => hashValues(method, _deepHash(argument));

I'd vote for adding deep equality and hash code functions to dart:ui, possibly naming them treeEquals and treeHashCode or similar to reflect that they do recursive descent through lists and maps, and that they would fail on cyclic values. Then they look expensive, and we can use them for things like MethodCall arguments where the tree can reasonably be expected to be small.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd rather just remove these ==/hashCode methods and change the unit tests to manually verify the arguments are what they expect. It should have pretty minimal impact on the tests (and if it's a lot we can always write a custom matcher), and would avoid anyone relying on comparing method calls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your call :-) @cbracken Do you want to change this PR to do as Ian suggests? Otherwise, I'll be happy to do it in another PR and then I think we should close this one.

@Hixie
Copy link
Contributor

Hixie commented Nov 2, 2017

@cbracken are you still working on this?

@cbracken
Copy link
Member Author

cbracken commented Nov 2, 2017

@Hixie thanks for the ping. Fired this PR off in a few mins before running out the door one night so I wouldn't forget about it then promptly did just that. I'll blow away the equals/hashCodes and patch up the tests.

@cbracken cbracken force-pushed the message-codec-hash branch 3 times, most recently from 98a4c72 to 1f37936 Compare November 3, 2017 01:23
@cbracken
Copy link
Member Author

cbracken commented Nov 3, 2017

Still WIP, but quick reworked the existing code into a matcher that effectively keeps the same deep matching behaviour for convenience when testing but kills off hashCode/== on MethodCall itself given we don't have any production use for it at the moment. Will try to get this cleaned up tomorrow.

@cbracken
Copy link
Member Author

cbracken commented Nov 3, 2017

PTAL

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructor first

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@Hixie
Copy link
Contributor

Hixie commented Nov 3, 2017

LGTM

Copy link
Contributor

@mravn-google mravn-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change and should be announced.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constructor before fields (I guess?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prettier indentation with trailing comma here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional to make the parens symmetric with the opening line.

Since MethodCall equality checks are limited to test scenarios, this
patch replaces them with an equivalent test matcher. At present
MethodCalls are always used in scenarios where indentity-based
equality/hashing is appropriate.

This change avoids an assertion failure when MethodCall args are
Iterable (possible since args are of type dyanmic), and hashValue() from
dart:ui asserts that its input is not an Iterable.

The alternative of implementing support for deep equality in dart:ui was
rejected on the basis that if we're to encourage performant code,
expensive checks should be obviously-expensive to the author.
@cbracken
Copy link
Member Author

cbracken commented Nov 3, 2017

@cbracken cbracken merged commit 4e2e697 into flutter:master Nov 6, 2017
@cbracken cbracken deleted the message-codec-hash branch November 6, 2017 17:53
DaveShuckerow pushed a commit to DaveShuckerow/flutter that referenced this pull request May 14, 2018
Since MethodCall equality checks are limited to test scenarios, this
patch replaces them with an equivalent test matcher. At present
MethodCalls are always used in scenarios where indentity-based
equality/hashing is appropriate.

This change avoids an assertion failure when MethodCall args are
Iterable (possible since args are of type dyanmic), and hashValue() from
dart:ui asserts that its input is not an Iterable.

The alternative of implementing support for deep equality in dart:ui was
rejected on the basis that if we're to encourage performant code,
expensive checks should be obviously-expensive to the author.
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 3, 2019
engine-flutter-autoroll added a commit that referenced this pull request Oct 4, 2019
[email protected]:flutter/engine.git/compare/7d67e275ff82...8aa4732

git log 7d67e27..8aa4732 --no-merges --oneline
2019-10-03 [email protected] Fix Metal builds. (flutter/engine#12777)
2019-10-03 [email protected] Revert "Manage resource and onscreen contexts using separate IOSGLContext objects (#12277)" (flutter/engine#12773)
2019-10-03 [email protected] Roll src/third_party/dart afac6a3714..07a63a17a4 (6 commits)
2019-10-03 [email protected] roll buildroot to 01e9235 (flutter/engine#12771)
2019-10-03 [email protected] Create a package-able incremental compiler (flutter/engine#12681)
2019-10-03 [email protected] add windows embedding test (flutter/engine#12423)
2019-10-03 [email protected] Roll fuchsia/sdk/core/mac-amd64 from g-PD1... to wYLiQ... (flutter/engine#12770)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected] on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
Inconnu08 pushed a commit to Inconnu08/flutter that referenced this pull request Nov 26, 2019
[email protected]:flutter/engine.git/compare/7d67e275ff82...8aa4732

git log 7d67e27..8aa4732 --no-merges --oneline
2019-10-03 [email protected] Fix Metal builds. (flutter/engine#12777)
2019-10-03 [email protected] Revert "Manage resource and onscreen contexts using separate IOSGLContext objects (flutter#12277)" (flutter/engine#12773)
2019-10-03 [email protected] Roll src/third_party/dart afac6a3714..07a63a17a4 (6 commits)
2019-10-03 [email protected] roll buildroot to 01e9235 (flutter/engine#12771)
2019-10-03 [email protected] Create a package-able incremental compiler (flutter/engine#12681)
2019-10-03 [email protected] add windows embedding test (flutter/engine#12423)
2019-10-03 [email protected] Roll fuchsia/sdk/core/mac-amd64 from g-PD1... to wYLiQ... (flutter/engine#12770)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected] on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants