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