From b23bc03d6d451be005b0c0ffd8a26301794e477c Mon Sep 17 00:00:00 2001 From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 05:33:47 +0000 Subject: [PATCH] Reverts "Drop deprecated hash_code functions (#54000)" (flutter/engine#54002) Reverts: flutter/engine#54000 Initiated by: chingjun Reason for reverting: Broke Google Testing. See b/352191023 for more details. Original PR Author: kevmoo Reviewed By: {jonahwilliams} This change reverts the following previous change: Fixes https://github.com/flutter/flutter/issues/151679 --- .../ci/licenses_golden/licenses_flutter | 4 + engine/src/flutter/lib/ui/dart_ui.gni | 1 + engine/src/flutter/lib/ui/experiments/ui.dart | 1 + engine/src/flutter/lib/ui/hash_codes.dart | 151 ++++++++++++++++++ engine/src/flutter/lib/ui/ui.dart | 1 + .../flutter/lib/web_ui/lib/hash_codes.dart | 135 ++++++++++++++++ engine/src/flutter/lib/web_ui/lib/ui.dart | 1 + .../web_ui/test/engine/hash_codes_test.dart | 119 ++++++++++++++ 8 files changed, 413 insertions(+) create mode 100644 engine/src/flutter/lib/ui/hash_codes.dart create mode 100644 engine/src/flutter/lib/web_ui/lib/hash_codes.dart create mode 100644 engine/src/flutter/lib/web_ui/test/engine/hash_codes_test.dart diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index f197935de7..7da558aa2a 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -42703,6 +42703,7 @@ ORIGIN: ../../../flutter/lib/ui/experiments/setup_hooks.dart + ../../../flutter/ ORIGIN: ../../../flutter/lib/ui/experiments/ui.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/floating_point.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/geometry.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/ui/hash_codes.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/hooks.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/io_manager.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/ui/io_manager.h + ../../../flutter/LICENSE @@ -42865,6 +42866,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/canvas.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/channel_buffers.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/compositing.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/geometry.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/hash_codes.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/initialization.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/key.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/lerp.dart + ../../../flutter/LICENSE @@ -45581,6 +45583,7 @@ FILE: ../../../flutter/lib/ui/experiments/setup_hooks.dart FILE: ../../../flutter/lib/ui/experiments/ui.dart FILE: ../../../flutter/lib/ui/floating_point.h FILE: ../../../flutter/lib/ui/geometry.dart +FILE: ../../../flutter/lib/ui/hash_codes.dart FILE: ../../../flutter/lib/ui/hooks.dart FILE: ../../../flutter/lib/ui/io_manager.cc FILE: ../../../flutter/lib/ui/io_manager.h @@ -45744,6 +45747,7 @@ FILE: ../../../flutter/lib/web_ui/lib/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/channel_buffers.dart FILE: ../../../flutter/lib/web_ui/lib/compositing.dart FILE: ../../../flutter/lib/web_ui/lib/geometry.dart +FILE: ../../../flutter/lib/web_ui/lib/hash_codes.dart FILE: ../../../flutter/lib/web_ui/lib/initialization.dart FILE: ../../../flutter/lib/web_ui/lib/key.dart FILE: ../../../flutter/lib/web_ui/lib/lerp.dart diff --git a/engine/src/flutter/lib/ui/dart_ui.gni b/engine/src/flutter/lib/ui/dart_ui.gni index 70f80d59fb..173035f25a 100644 --- a/engine/src/flutter/lib/ui/dart_ui.gni +++ b/engine/src/flutter/lib/ui/dart_ui.gni @@ -7,6 +7,7 @@ dart_ui_files = [ "//flutter/lib/ui/channel_buffers.dart", "//flutter/lib/ui/compositing.dart", "//flutter/lib/ui/geometry.dart", + "//flutter/lib/ui/hash_codes.dart", "//flutter/lib/ui/hooks.dart", "//flutter/lib/ui/isolate_name_server.dart", "//flutter/lib/ui/key.dart", diff --git a/engine/src/flutter/lib/ui/experiments/ui.dart b/engine/src/flutter/lib/ui/experiments/ui.dart index f614b25a25..beaf3e143f 100644 --- a/engine/src/flutter/lib/ui/experiments/ui.dart +++ b/engine/src/flutter/lib/ui/experiments/ui.dart @@ -32,6 +32,7 @@ part '../annotations.dart'; part '../channel_buffers.dart'; part '../compositing.dart'; part '../geometry.dart'; +part '../hash_codes.dart'; part '../hooks.dart'; part '../isolate_name_server.dart'; part '../key.dart'; diff --git a/engine/src/flutter/lib/ui/hash_codes.dart b/engine/src/flutter/lib/ui/hash_codes.dart new file mode 100644 index 0000000000..0d9ecec95f --- /dev/null +++ b/engine/src/flutter/lib/ui/hash_codes.dart @@ -0,0 +1,151 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +part of dart.ui; + +// Examples can assume: +// int foo = 0; +// int bar = 0; +// List quux = []; +// List? thud; +// int baz = 0; + +class _HashEnd { const _HashEnd(); } +const _HashEnd _hashEnd = _HashEnd(); + +// ignore: avoid_classes_with_only_static_members +/// Jenkins hash function, optimized for small integers. +// +// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart. +class _Jenkins { + static int combine(int hash, Object? o) { + assert(o is! Iterable); + hash = 0x1fffffff & (hash + o.hashCode); + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// ## Deprecation +/// +/// This function has been replaced by [Object.hash], so that it can be used +/// outside of Flutter as well. The new function is a drop-in replacement. +/// +/// The [hashList] function has also been replaced, [Object.hashAll] is the new +/// function. The example above therefore is better written as: +/// +/// ```dart +/// int get hashCode => Object.hash(foo, bar, Object.hashAll(quux), baz); +/// ``` +/// +/// If a parameter is nullable, then it needs special handling, +/// because [Object.hashAll]'s argument is not nullable: +/// +/// ```dart +/// int get hashCode => Object.hash(foo, bar, thud == null ? null : Object.hashAll(thud!), baz); +/// ``` +@Deprecated( + 'Use Object.hash() instead. ' + 'This feature was deprecated in v3.1.0-0.0.pre.897' +) +int hashValues( + Object? arg01, Object? arg02, [ Object? arg03 = _hashEnd, + Object? arg04 = _hashEnd, Object? arg05 = _hashEnd, Object? arg06 = _hashEnd, + Object? arg07 = _hashEnd, Object? arg08 = _hashEnd, Object? arg09 = _hashEnd, + Object? arg10 = _hashEnd, Object? arg11 = _hashEnd, Object? arg12 = _hashEnd, + Object? arg13 = _hashEnd, Object? arg14 = _hashEnd, Object? arg15 = _hashEnd, + Object? arg16 = _hashEnd, Object? arg17 = _hashEnd, Object? arg18 = _hashEnd, + Object? arg19 = _hashEnd, Object? arg20 = _hashEnd ]) { + int result = 0; + result = _Jenkins.combine(result, arg01); + result = _Jenkins.combine(result, arg02); + if (!identical(arg03, _hashEnd)) { + result = _Jenkins.combine(result, arg03); + if (!identical(arg04, _hashEnd)) { + result = _Jenkins.combine(result, arg04); + if (!identical(arg05, _hashEnd)) { + result = _Jenkins.combine(result, arg05); + if (!identical(arg06, _hashEnd)) { + result = _Jenkins.combine(result, arg06); + if (!identical(arg07, _hashEnd)) { + result = _Jenkins.combine(result, arg07); + if (!identical(arg08, _hashEnd)) { + result = _Jenkins.combine(result, arg08); + if (!identical(arg09, _hashEnd)) { + result = _Jenkins.combine(result, arg09); + if (!identical(arg10, _hashEnd)) { + result = _Jenkins.combine(result, arg10); + if (!identical(arg11, _hashEnd)) { + result = _Jenkins.combine(result, arg11); + if (!identical(arg12, _hashEnd)) { + result = _Jenkins.combine(result, arg12); + if (!identical(arg13, _hashEnd)) { + result = _Jenkins.combine(result, arg13); + if (!identical(arg14, _hashEnd)) { + result = _Jenkins.combine(result, arg14); + if (!identical(arg15, _hashEnd)) { + result = _Jenkins.combine(result, arg15); + if (!identical(arg16, _hashEnd)) { + result = _Jenkins.combine(result, arg16); + if (!identical(arg17, _hashEnd)) { + result = _Jenkins.combine(result, arg17); + if (!identical(arg18, _hashEnd)) { + result = _Jenkins.combine(result, arg18); + if (!identical(arg19, _hashEnd)) { + result = _Jenkins.combine(result, arg19); + if (!identical(arg20, _hashEnd)) { + result = _Jenkins.combine(result, arg20); + // I can see my house from here! + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return _Jenkins.finish(result); +} + +/// Combine the [Object.hashCode] values of an arbitrary number of objects from +/// an [Iterable] into one value. This function will return the same value if +/// given null as if given an empty list. +/// +/// ## Deprecation +/// +/// This function has been replaced by [Object.hashAll], so that it can be used +/// outside of Flutter as well. The new function is a drop-in replacement, except +/// that the argument must not be null. +/// +/// There is also a new function, [Object.hashAllUnordered], which is similar +/// but returns the same hash code regardless of the order of the elements in +/// the provided iterable. +@Deprecated( + 'Use Object.hashAll() or Object.hashAllUnordered() instead. ' + 'This feature was deprecated in v3.1.0-0.0.pre.897' +) +int hashList(Iterable? arguments) { + int result = 0; + if (arguments != null) { + for (final Object? argument in arguments) { + result = _Jenkins.combine(result, argument); + } + } + return _Jenkins.finish(result); +} diff --git a/engine/src/flutter/lib/ui/ui.dart b/engine/src/flutter/lib/ui/ui.dart index 411d36005b..86d4e5dc2e 100644 --- a/engine/src/flutter/lib/ui/ui.dart +++ b/engine/src/flutter/lib/ui/ui.dart @@ -32,6 +32,7 @@ part 'annotations.dart'; part 'channel_buffers.dart'; part 'compositing.dart'; part 'geometry.dart'; +part 'hash_codes.dart'; part 'hooks.dart'; part 'isolate_name_server.dart'; part 'key.dart'; diff --git a/engine/src/flutter/lib/web_ui/lib/hash_codes.dart b/engine/src/flutter/lib/web_ui/lib/hash_codes.dart new file mode 100644 index 0000000000..18e18ad27b --- /dev/null +++ b/engine/src/flutter/lib/web_ui/lib/hash_codes.dart @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of ui; + +class _HashEnd { const _HashEnd(); } +const _HashEnd _hashEnd = _HashEnd(); + +// ignore: avoid_classes_with_only_static_members +/// Jenkins hash function, optimized for small integers. +// +// Borrowed from the dart sdk: sdk/lib/math/jenkins_smi_hash.dart. +class _Jenkins { + static int combine(int hash, Object? o) { + assert(o is! Iterable); + hash = 0x1fffffff & (hash + o.hashCode); + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// ## Deprecation +/// +/// This function has been replaced by [Object.hash], so that it can be used +/// outside of Flutter as well. The new function is a drop-in replacement. +/// +/// The [hashList] function has also been replaced, [Object.hashAll] is the new +/// function. The example above therefore is better written as: +/// +/// ```dart +/// int get hashCode => Object.hash(foo, bar, Object.hashAll(quux), baz); +/// ``` +/// +/// If a parameter is nullable, then it needs special handling, +/// because [Object.hashAll]'s argument is not nullable: +/// +/// ```dart +/// int get hashCode => Object.hash(foo, bar, thud == null ? null : Object.hashAll(thud!), baz); +/// ``` +@Deprecated( + 'Use Object.hash() instead. ' + 'This feature was deprecated in v3.1.0-0.0.pre.897' +) +int hashValues( + Object? arg01, Object? arg02, [ Object? arg03 = _hashEnd, + Object? arg04 = _hashEnd, Object? arg05 = _hashEnd, Object? arg06 = _hashEnd, + Object? arg07 = _hashEnd, Object? arg08 = _hashEnd, Object? arg09 = _hashEnd, + Object? arg10 = _hashEnd, Object? arg11 = _hashEnd, Object? arg12 = _hashEnd, + Object? arg13 = _hashEnd, Object? arg14 = _hashEnd, Object? arg15 = _hashEnd, + Object? arg16 = _hashEnd, Object? arg17 = _hashEnd, Object? arg18 = _hashEnd, + Object? arg19 = _hashEnd, Object? arg20 = _hashEnd ]) { + int result = 0; + result = _Jenkins.combine(result, arg01); + result = _Jenkins.combine(result, arg02); + if (!identical(arg03, _hashEnd)) { + result = _Jenkins.combine(result, arg03); + if (!identical(arg04, _hashEnd)) { + result = _Jenkins.combine(result, arg04); + if (!identical(arg05, _hashEnd)) { + result = _Jenkins.combine(result, arg05); + if (!identical(arg06, _hashEnd)) { + result = _Jenkins.combine(result, arg06); + if (!identical(arg07, _hashEnd)) { + result = _Jenkins.combine(result, arg07); + if (!identical(arg08, _hashEnd)) { + result = _Jenkins.combine(result, arg08); + if (!identical(arg09, _hashEnd)) { + result = _Jenkins.combine(result, arg09); + if (!identical(arg10, _hashEnd)) { + result = _Jenkins.combine(result, arg10); + if (!identical(arg11, _hashEnd)) { + result = _Jenkins.combine(result, arg11); + if (!identical(arg12, _hashEnd)) { + result = _Jenkins.combine(result, arg12); + if (!identical(arg13, _hashEnd)) { + result = _Jenkins.combine(result, arg13); + if (!identical(arg14, _hashEnd)) { + result = _Jenkins.combine(result, arg14); + if (!identical(arg15, _hashEnd)) { + result = _Jenkins.combine(result, arg15); + if (!identical(arg16, _hashEnd)) { + result = _Jenkins.combine(result, arg16); + if (!identical(arg17, _hashEnd)) { + result = _Jenkins.combine(result, arg17); + if (!identical(arg18, _hashEnd)) { + result = _Jenkins.combine(result, arg18); + if (!identical(arg19, _hashEnd)) { + result = _Jenkins.combine(result, arg19); + if (!identical(arg20, _hashEnd)) { + result = _Jenkins.combine(result, arg20); + // I can see my house from here! + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return _Jenkins.finish(result); +} + +/// Combine the [Object.hashCode] values of an arbitrary number of objects from +/// an [Iterable] into one value. This function will return the same value if +/// given null as if given an empty list. +@Deprecated( + 'Use Object.hashAll() or Object.hashAllUnordered() instead. ' + 'This feature was deprecated in v3.1.0-0.0.pre.897' +) +int hashList(Iterable? arguments) { + int result = 0; + if (arguments != null) { + for (final Object? argument in arguments) { + result = _Jenkins.combine(result, argument); + } + } + return _Jenkins.finish(result); +} diff --git a/engine/src/flutter/lib/web_ui/lib/ui.dart b/engine/src/flutter/lib/web_ui/lib/ui.dart index 5dc40b227e..80a06c1c1e 100644 --- a/engine/src/flutter/lib/web_ui/lib/ui.dart +++ b/engine/src/flutter/lib/web_ui/lib/ui.dart @@ -21,6 +21,7 @@ part 'canvas.dart'; part 'channel_buffers.dart'; part 'compositing.dart'; part 'geometry.dart'; +part 'hash_codes.dart'; part 'initialization.dart'; part 'key.dart'; part 'lerp.dart'; diff --git a/engine/src/flutter/lib/web_ui/test/engine/hash_codes_test.dart b/engine/src/flutter/lib/web_ui/test/engine/hash_codes_test.dart new file mode 100644 index 0000000000..7e34ed20db --- /dev/null +++ b/engine/src/flutter/lib/web_ui/test/engine/hash_codes_test.dart @@ -0,0 +1,119 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:test/bootstrap/browser.dart'; +import 'package:test/test.dart'; +import 'package:ui/ui.dart'; + +// The biggest integer value that can be represented in JavaScript is 1 << 53. +// However, the 1 << 53 expression cannot be used in JavaScript because that +// would apply the bitwise shift to a "number" (i.e. float64), which is +// meaningless. Instead, a decimal literal is used here. +const int _kBiggestExactJavaScriptInt = 9007199254740992; + +void main() { + internalBootstrapBrowserTest(() => testMain); +} + +void testMain() { + test('hashValues and hashList can hash lots of huge values effectively', () { + final int hashValueFromArgs = hashValues( + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + ); + + // Hash the same values via a list + final int hashValueFromList = hashList([ + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + ]); + + // Hash a slightly smaller number to verify that the hash code is different. + final int slightlyDifferentHashValueFromArgs = hashValues( + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt - 1, + ); + + final int slightlyDifferentHashValueFromList = hashList([ + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt, + _kBiggestExactJavaScriptInt - 1, + ]); + + expect(hashValueFromArgs, equals(hashValueFromList)); + expect(slightlyDifferentHashValueFromArgs, equals(slightlyDifferentHashValueFromList)); + expect(hashValueFromArgs, isNot(equals(slightlyDifferentHashValueFromArgs))); + }); +}