Version 2.17.0-41.0.dev

Merge commit '2a34453dd1afb29bcea6c65f9d883107d2232632' into 'dev'
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_check.dart b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
index 3cd03b5..57a911e 100644
--- a/pkg/analysis_server/test/services/completion/dart/completion_check.dart
+++ b/pkg/analysis_server/test/services/completion/dart/completion_check.dart
@@ -184,6 +184,11 @@
     element.isNotNull.kind.isField;
   }
 
+  void get isFunctionReference {
+    kind.isIdentifier;
+    element.isNotNull.kind.isFunction;
+  }
+
   void get isGetter {
     kind.isIdentifier;
     element.isNotNull.kind.isGetter;
@@ -380,6 +385,10 @@
     isEqualTo(ElementKind.FIELD);
   }
 
+  void get isFunction {
+    isEqualTo(ElementKind.FUNCTION);
+  }
+
   void get isGetter {
     isEqualTo(ElementKind.GETTER);
   }
diff --git a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
index 1e716a2..c463ed9 100644
--- a/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
+++ b/pkg/analysis_server/test/services/completion/dart/local_reference_contributor_test.dart
@@ -5102,6 +5102,28 @@
     assertSuggestLocalVariable('foo', 'int');
   }
 
+  Future<void>
+      test_namedArgument_instanceCreation_x_localFunction_void() async {
+    addTestSource('''
+class A {
+  A({required void Function() a});
+}
+
+class B {
+  void bar() {
+    void foo01() {}
+    A(a: foo0^);
+  }
+}
+''');
+    await computeSuggestions();
+    assertSuggestFunction(
+      'foo01',
+      'void',
+      kind: CompletionSuggestionKind.IDENTIFIER,
+    );
+  }
+
   Future<void> test_new_instance() async {
     addTestSource('import "dart:math"; class A {x() {new Random().^}}');
     await computeSuggestions();
diff --git a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
index 2110c49..a525245 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/completion/optype.dart
@@ -956,6 +956,8 @@
       Element? element;
       if (grandparent is ConstructorReferenceNode) {
         element = grandparent.staticElement;
+      } else if (grandparent is InstanceCreationExpression) {
+        element = grandparent.constructorName.staticElement;
       } else if (grandparent is MethodInvocation) {
         element = grandparent.methodName.staticElement;
       }
diff --git a/pkg/compiler/lib/src/js_emitter/native_emitter.dart b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
index a3137e7..857b67b 100644
--- a/pkg/compiler/lib/src/js_emitter/native_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/native_emitter.dart
@@ -94,6 +94,7 @@
 
     Class objectClass = null;
     Class jsInterceptorClass = null;
+    Class jsJavaScriptObjectClass = null;
 
     void walk(Class cls) {
       if (cls.element == _commonElements.objectClass) {
@@ -104,6 +105,11 @@
         jsInterceptorClass = cls;
         return;
       }
+      // Native classes may inherit either `Interceptor` e.g. `JSBool` or
+      // `JavaScriptObject` e.g. `dart:html` classes.
+      if (cls.element == _commonElements.jsJavaScriptObjectClass) {
+        jsJavaScriptObjectClass = cls;
+      }
       if (seen.contains(cls)) return;
       seen.add(cls);
       walk(cls.superclass);
@@ -215,6 +221,9 @@
     // by getNativeInterceptor and custom elements.
     if (_nativeCodegenEnqueuer.hasInstantiatedNativeClasses) {
       fillNativeInfo(jsInterceptorClass);
+      if (jsJavaScriptObjectClass != null) {
+        fillNativeInfo(jsJavaScriptObjectClass);
+      }
       for (Class cls in classes) {
         if (!cls.isNative || neededClasses.contains(cls)) {
           fillNativeInfo(cls);
diff --git a/pkg/compiler/test/jsinterop/internal_annotations_test.dart b/pkg/compiler/test/jsinterop/internal_annotations_test.dart
index 4299a38..62deced 100644
--- a/pkg/compiler/test/jsinterop/internal_annotations_test.dart
+++ b/pkg/compiler/test/jsinterop/internal_annotations_test.dart
@@ -184,7 +184,15 @@
             "Expected $name to be indirectly instantiated in `${mainSource}`:"
             "\n${world.classHierarchy.dump(cls)}");
       }
-      if (!isInstantiated && (name != 'Object' && name != 'Interceptor')) {
+      // Classes that are expected to be instantiated by default. `Object` and
+      // `Interceptor` are base types for non-native and native types, and
+      // `JavaScriptObject` is the base type for `dart:html` types.
+      var insantiatedBaseClasses = [
+        'Object',
+        'Interceptor',
+        'JavaScriptObject'
+      ];
+      if (!isInstantiated && !insantiatedBaseClasses.contains(name)) {
         Expect.isFalse(
             world.classHierarchy.isInstantiated(cls),
             "Expected $name to be uninstantiated in `${mainSource}`:"
diff --git a/pkg/compiler/test/jsinterop/world_test.dart b/pkg/compiler/test/jsinterop/world_test.dart
index 6c3253f..230873f 100644
--- a/pkg/compiler/test/jsinterop/world_test.dart
+++ b/pkg/compiler/test/jsinterop/world_test.dart
@@ -178,7 +178,15 @@
             "Expected $name to be indirectly instantiated in `${mainSource}`:"
             "\n${world.classHierarchy.dump(cls)}");
       }
-      if (!isInstantiated && (name != 'Object' && name != 'Interceptor')) {
+      // Classes that are expected to be instantiated by default. `Object` and
+      // `Interceptor` are base types for non-native and native types, and
+      // `JavaScriptObject` is the base type for `dart:html` types.
+      var insantiatedBaseClasses = [
+        'Object',
+        'Interceptor',
+        'JavaScriptObject'
+      ];
+      if (!isInstantiated && !insantiatedBaseClasses.contains(name)) {
         Expect.isFalse(
             world.classHierarchy.isInstantiated(cls),
             "Expected $name to be uninstantiated in `${mainSource}`:"
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index 2b74839..4be44f3 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -219,7 +219,7 @@
 
   static Value ValueOf(Pair kv) { return kv; }
 
-  static inline uword Hash(Key key) { return key->GetClassId(); }
+  static inline uword Hash(Key key) { return key->CanonicalizeHash(); }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
     return pair->ptr() == key->ptr();
diff --git a/runtime/vm/compiler/runtime_api.cc b/runtime/vm/compiler/runtime_api.cc
index bc2daf8..a0cca50 100644
--- a/runtime/vm/compiler/runtime_api.cc
+++ b/runtime/vm/compiler/runtime_api.cc
@@ -101,6 +101,11 @@
   if (obj.IsNull()) {
     return kNullIdentityHash;
   }
+  // TypeArguments should be handled before Instance as TypeArguments extends
+  // Instance and TypeArguments::CanonicalizeHash just returns 0.
+  if (obj.IsTypeArguments()) {
+    return TypeArguments::Cast(obj).Hash();
+  }
   if (obj.IsInstance()) {
     return Instance::Cast(obj).CanonicalizeHash();
   }
@@ -121,6 +126,10 @@
   return obj.GetClassId();
 }
 
+const char* ObjectToCString(const Object& obj) {
+  return obj.ToCString();
+}
+
 void SetToNull(Object* obj) {
   *obj = Object::null();
 }
diff --git a/runtime/vm/compiler/runtime_api.h b/runtime/vm/compiler/runtime_api.h
index 0559b8c..af57f4ef 100644
--- a/runtime/vm/compiler/runtime_api.h
+++ b/runtime/vm/compiler/runtime_api.h
@@ -181,6 +181,9 @@
 // or canonical hash.
 intptr_t ObjectHash(const Object& obj);
 
+// Prints the given object into a C string.
+const char* ObjectToCString(const Object& obj);
+
 // If the given object represents a Dart integer returns true and sets [value]
 // to the value of the integer.
 bool HasIntegerValue(const dart::Object& obj, int64_t* value);
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 6e885be..3e0ea0f 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -143,6 +143,8 @@
   P(marker_tasks, int, 2,                                                      \
     "The number of tasks to spawn during old gen GC marking (0 means "         \
     "perform all marking on main thread).")                                    \
+  P(hash_map_probes_limit, int, kMaxInt32,                                     \
+    "Limit number of probes while doing lookups in hash maps.")                \
   P(max_polymorphic_checks, int, 4,                                            \
     "Maximum number of polymorphic check, otherwise it is megamorphic.")       \
   P(max_equality_polymorphic_checks, int, 32,                                  \
diff --git a/runtime/vm/hash_map.h b/runtime/vm/hash_map.h
index aacbf87..a55f683 100644
--- a/runtime/vm/hash_map.h
+++ b/runtime/vm/hash_map.h
@@ -6,6 +6,7 @@
 #define RUNTIME_VM_HASH_MAP_H_
 
 #include "platform/utils.h"
+#include "vm/flags.h"
 #include "vm/growable_array.h"  // For Malloc, EmptyBase
 #include "vm/hash.h"
 #include "vm/zone.h"
@@ -132,6 +133,7 @@
   uint32_t mask = hash_table_size_ - 1;
   uint32_t hash_index = hash & mask;
   uint32_t start = hash_index;
+  intptr_t probes = 0;
   for (;;) {
     uint32_t pair_index = hash_table_[hash_index];
     if (pair_index == kEmpty) {
@@ -139,6 +141,7 @@
     }
     if (pair_index != kDeleted) {
       ASSERT(pair_index < pairs_size_);
+      RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
       if (KeyValueTrait::IsKeyEqual(pairs_[pair_index], key)) {
         return &pairs_[pair_index];
       }
@@ -233,6 +236,7 @@
   uint32_t mask = hash_table_size_ - 1;
   uint32_t hash_index = hash & mask;
   uint32_t start = hash_index;
+  intptr_t probes = 0;
   for (;;) {
     uint32_t pair_index = hash_table_[hash_index];
     if ((pair_index == kEmpty) || (pair_index == kDeleted)) {
@@ -241,6 +245,7 @@
       next_pair_index_++;
       break;
     }
+    RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
     ASSERT(pair_index < pairs_size_);
     hash_index = (hash_index + 1) & mask;
     // Hashtable must contain at least one empty marker.
@@ -273,6 +278,7 @@
   uint32_t mask = hash_table_size_ - 1;
   uint32_t hash_index = hash & mask;
   uint32_t start = hash_index;
+  intptr_t probes = 0;
   for (;;) {
     uint32_t pair_index = hash_table_[hash_index];
     if (pair_index == kEmpty) {
@@ -280,6 +286,7 @@
     }
     if (pair_index != kDeleted) {
       ASSERT(pair_index < pairs_size_);
+      RELEASE_ASSERT(++probes < FLAG_hash_map_probes_limit);
       if (KeyValueTrait::IsKeyEqual(pairs_[pair_index], key)) {
         hash_table_[hash_index] = kDeleted;
         pairs_[pair_index] = typename KeyValueTrait::Pair();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6320a45..99c91df 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5887,6 +5887,11 @@
     return memcmp(untag(), other.untag(), InstanceSize(Length())) == 0;
   }
 
+  uint32_t Hash() const {
+    NoSafepointScope no_safepoint;
+    return HashBytes(Data(), Length());
+  }
+
   void PrintToJSONObject(JSONObject* jsobj, bool ref) const;
 
  private:
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index b7f31f3..feba7b0 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -1020,7 +1020,7 @@
 
   static inline uword Hash(Key key) {
     ASSERT(!key->IsNull());
-    return Utils::WordHash(key->Length());
+    return Utils::WordHash(key->Hash());
   }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
@@ -1070,7 +1070,14 @@
 
   static inline uword Hash(Key key) {
     ASSERT(!key->IsNull());
-    return Utils::WordHash(key->Length());
+    ASSERT(Thread::Current()->no_safepoint_scope_depth() > 0);
+    const intptr_t len = key->Length();
+    uint32_t hash = Utils::WordHash(len);
+    for (intptr_t i = 0; i < len; ++i) {
+      hash =
+          CombineHashes(hash, Utils::WordHash(static_cast<uword>(key->At(i))));
+    }
+    return hash;
   }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
@@ -1128,6 +1135,9 @@
   };
 
   StackZone stack_zone(thread);
+  // ArrayKeyValueTrait::Hash is based on object addresses, so make
+  // sure GC doesn't happen and doesn't move objects.
+  NoSafepointScope no_safepoint;
   DedupListsVisitor visitor(thread->zone());
   WalkProgram(thread->zone(), thread->isolate_group(), &visitor);
 }
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index a7f3ca8..d41e511 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -415,19 +415,21 @@
 
     var interceptor = getInterceptor(object);
     if (identical(interceptor, JS_INTERCEPTOR_CONSTANT(Interceptor)) ||
+        identical(interceptor, JS_INTERCEPTOR_CONSTANT(JavaScriptObject)) ||
         object is UnknownJavaScriptObject) {
       // Try to do better.  If we do not find something better, fallthrough to
-      // Dart-type based name that leave the name as 'UnknownJavaScriptObject'
-      // or 'Interceptor' (or the minified versions thereof).
+      // Dart-type based name that leave the name as 'UnknownJavaScriptObject',
+      // 'Interceptor', or 'JavaScriptObject' (or their minified versions).
       //
       // When we get here via the UnknownJavaScriptObject test (for JavaScript
       // objects from outside the program), the object's constructor has a
       // better name that 'UnknownJavaScriptObject'.
       //
-      // When we get here the Interceptor test (for Native classes that are
-      // declared in the Dart program but have been 'folded' into Interceptor),
-      // the native class's constructor name is better than the generic
-      // 'Interceptor' (an abstract class).
+      // When we get here via either the Interceptor or JavaScriptObject test
+      // (for Native classes that are declared in the Dart program but have been
+      // 'folded' into one of those interceptors), the native class's
+      // constructor name is better than the generic 'Interceptor' or
+      // 'JavaScriptObject'.
 
       // Try the [constructorNameFallback]. This gets the constructor name for
       // any browser (used by [getNativeInterceptor]).
diff --git a/tests/web/internal/object_members_test.dart b/tests/web/internal/object_members_test.dart
new file mode 100644
index 0000000..02092eb
--- /dev/null
+++ b/tests/web/internal/object_members_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Make sure `Object` methods work as expected with `dart:html` and interop
+// types. The expectations here aren't guarantees that they should work a
+// particular way, but rather a way to monitor regressions/changes.
+
+@JS()
+library object_members_test;
+
+import 'package:js/js.dart';
+import 'package:expect/minitest.dart';
+
+import 'dart:html';
+import 'dart:_interceptors' show JSObject;
+
+@JS()
+external void eval(String code);
+
+@JS()
+class JSClass {
+  external JSClass();
+}
+
+void main() {
+  eval(r'''
+    function JSClass() {}
+  ''');
+
+  // `dart:html` type.
+  var div = document.createElement('div');
+  expect(div == div, true);
+  expect(div == DomPointReadOnly(), false);
+  // Ensure that we get a random hash for each new instance. It should be
+  // improbable for this to fail across many runs if the hash is
+  // non-deterministic.
+  var hashCode = div.hashCode;
+  var attempts = 0;
+  var maxAttempts = 1000;
+  while (div.hashCode == hashCode && attempts < maxAttempts) {
+    div = document.createElement('div');
+    attempts++;
+  }
+  expect(attempts > 0 && attempts != maxAttempts, isTrue);
+  expect(div.toString, isNotNull);
+  expect(div.toString(), 'div');
+  expect(div.noSuchMethod, isNotNull);
+  var noSuchMethodErrorThrown = true;
+  try {
+    (div as dynamic).triggerNoSuchMethod();
+    noSuchMethodErrorThrown = false;
+  } catch (_) {}
+  expect(noSuchMethodErrorThrown, isTrue);
+  expect(div.runtimeType, DivElement);
+
+  // `toString` for `dart:html` types that do not have an overridden `toString`
+  // should look up the type through the proto.
+  expect(window.navigator.toString(), "Instance of 'Navigator'");
+
+  // Interop type.
+  var js = JSClass();
+  expect(js == js, true);
+  expect(js == JSClass(), false);
+  // TODO(srujzs): Modify this once interop has random hash codes.
+  hashCode = js.hashCode;
+  expect(hashCode, 0);
+  expect(hashCode, js.hashCode);
+  expect(js.toString, isNotNull);
+  // Should forward to underlying `toString` call.
+  expect(js.toString(), '[object Object]');
+  expect(js.noSuchMethod, isNotNull);
+  noSuchMethodErrorThrown = true;
+  try {
+    (js as dynamic).triggerNoSuchMethod();
+    noSuchMethodErrorThrown = false;
+  } catch (_) {}
+  expect(noSuchMethodErrorThrown, isTrue);
+  expect(js.runtimeType, JSObject);
+}
diff --git a/tests/web/web.status b/tests/web/web.status
index 481ba3d..7bdbea2 100644
--- a/tests/web/web.status
+++ b/tests/web/web.status
@@ -27,12 +27,16 @@
 [ $compiler == dart2js && $runtime == chrome && $csp ]
 deferred/load_in_correct_order_test: SkipByDesign # Purposely uses `eval`
 
+[ $compiler == dart2js && $runtime == d8 ]
+internal/object_members_test: SkipByDesign # Browser test
+
 [ $compiler == dart2js && $runtime == ff && $system == windows ]
 consistent_index_error_string_test: Slow, Pass # Issue 25940
 
 [ $compiler == dart2js && $csp ]
 deferred_custom_loader_test: SkipByDesign # Issue 25683
 deferred_fail_and_retry_test: SkipByDesign # Uses eval to simulate failed loading.
+internal/object_members_test: SkipByDesign # Uses eval for interop
 
 [ $compiler == dart2js && !$host_checked ]
 dummy_compiler_test: Slow, Pass # Issue 32439. self-hosting doesn't work with CFE yet.
diff --git a/tests/web_2/internal/object_members_test.dart b/tests/web_2/internal/object_members_test.dart
new file mode 100644
index 0000000..02092eb
--- /dev/null
+++ b/tests/web_2/internal/object_members_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Make sure `Object` methods work as expected with `dart:html` and interop
+// types. The expectations here aren't guarantees that they should work a
+// particular way, but rather a way to monitor regressions/changes.
+
+@JS()
+library object_members_test;
+
+import 'package:js/js.dart';
+import 'package:expect/minitest.dart';
+
+import 'dart:html';
+import 'dart:_interceptors' show JSObject;
+
+@JS()
+external void eval(String code);
+
+@JS()
+class JSClass {
+  external JSClass();
+}
+
+void main() {
+  eval(r'''
+    function JSClass() {}
+  ''');
+
+  // `dart:html` type.
+  var div = document.createElement('div');
+  expect(div == div, true);
+  expect(div == DomPointReadOnly(), false);
+  // Ensure that we get a random hash for each new instance. It should be
+  // improbable for this to fail across many runs if the hash is
+  // non-deterministic.
+  var hashCode = div.hashCode;
+  var attempts = 0;
+  var maxAttempts = 1000;
+  while (div.hashCode == hashCode && attempts < maxAttempts) {
+    div = document.createElement('div');
+    attempts++;
+  }
+  expect(attempts > 0 && attempts != maxAttempts, isTrue);
+  expect(div.toString, isNotNull);
+  expect(div.toString(), 'div');
+  expect(div.noSuchMethod, isNotNull);
+  var noSuchMethodErrorThrown = true;
+  try {
+    (div as dynamic).triggerNoSuchMethod();
+    noSuchMethodErrorThrown = false;
+  } catch (_) {}
+  expect(noSuchMethodErrorThrown, isTrue);
+  expect(div.runtimeType, DivElement);
+
+  // `toString` for `dart:html` types that do not have an overridden `toString`
+  // should look up the type through the proto.
+  expect(window.navigator.toString(), "Instance of 'Navigator'");
+
+  // Interop type.
+  var js = JSClass();
+  expect(js == js, true);
+  expect(js == JSClass(), false);
+  // TODO(srujzs): Modify this once interop has random hash codes.
+  hashCode = js.hashCode;
+  expect(hashCode, 0);
+  expect(hashCode, js.hashCode);
+  expect(js.toString, isNotNull);
+  // Should forward to underlying `toString` call.
+  expect(js.toString(), '[object Object]');
+  expect(js.noSuchMethod, isNotNull);
+  noSuchMethodErrorThrown = true;
+  try {
+    (js as dynamic).triggerNoSuchMethod();
+    noSuchMethodErrorThrown = false;
+  } catch (_) {}
+  expect(noSuchMethodErrorThrown, isTrue);
+  expect(js.runtimeType, JSObject);
+}
diff --git a/tests/web_2/web_2.status b/tests/web_2/web_2.status
index 481ba3d..7bdbea2 100644
--- a/tests/web_2/web_2.status
+++ b/tests/web_2/web_2.status
@@ -27,12 +27,16 @@
 [ $compiler == dart2js && $runtime == chrome && $csp ]
 deferred/load_in_correct_order_test: SkipByDesign # Purposely uses `eval`
 
+[ $compiler == dart2js && $runtime == d8 ]
+internal/object_members_test: SkipByDesign # Browser test
+
 [ $compiler == dart2js && $runtime == ff && $system == windows ]
 consistent_index_error_string_test: Slow, Pass # Issue 25940
 
 [ $compiler == dart2js && $csp ]
 deferred_custom_loader_test: SkipByDesign # Issue 25683
 deferred_fail_and_retry_test: SkipByDesign # Uses eval to simulate failed loading.
+internal/object_members_test: SkipByDesign # Uses eval for interop
 
 [ $compiler == dart2js && !$host_checked ]
 dummy_compiler_test: Slow, Pass # Issue 32439. self-hosting doesn't work with CFE yet.
diff --git a/tools/VERSION b/tools/VERSION
index f398668..3f0e9b0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 17
 PATCH 0
-PRERELEASE 40
+PRERELEASE 41
 PRERELEASE_PATCH 0
\ No newline at end of file