Version 2.13.0-71.0.dev
Merge commit '7b68624218c2eb84af346790b579947062c9b012' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 7d4584e..0a38cf0 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -42,7 +42,6 @@
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/services/available_declarations.dart';
-import 'package:analyzer/src/util/glob.dart';
/// Implementations of [AbstractAnalysisServer] implement a server that listens
/// on a [CommunicationChannel] for analysis messages and process them.
@@ -109,24 +108,12 @@
/// The set of the files that are currently priority.
final Set<String> priorityFiles = <String>{};
- final List<String> analyzableFilePatterns = <String>[
- '**/*.${AnalysisEngine.SUFFIX_DART}',
- '**/${AnalysisEngine.ANALYSIS_OPTIONS_YAML_FILE}',
- '**/${AnalysisEngine.FIX_DATA_FILE}',
- '**/${AnalysisEngine.PUBSPEC_YAML_FILE}',
- '**/${AnalysisEngine.ANDROID_MANIFEST_FILE}'
- ];
-
/// The [ResourceProvider] using which paths are converted into [Resource]s.
final OverlayResourceProvider resourceProvider;
/// The next modification stamp for a changed file in the [resourceProvider].
int overlayModificationStamp = 0;
- /// A list of the globs used to determine which files should be analyzed. The
- /// list is lazily created and should be accessed using [analyzedFilesGlobs].
- List<Glob> _analyzedFilesGlobs;
-
AbstractAnalysisServer(
this.options,
this.sdkManager,
@@ -183,31 +170,11 @@
byteStore,
analysisPerformanceLogger,
analysisDriverScheduler,
- analyzedFilesGlobs,
instrumentationService,
);
searchEngine = SearchEngineImpl(driverMap.values);
}
- /// Return a list of the globs used to determine which files should be
- /// analyzed.
- List<Glob> get analyzedFilesGlobs {
- if (_analyzedFilesGlobs == null) {
- _analyzedFilesGlobs = <Glob>[];
- for (var pattern in analyzableFilePatterns) {
- try {
- _analyzedFilesGlobs
- .add(Glob(resourceProvider.pathContext.separator, pattern));
- } catch (exception, stackTrace) {
- AnalysisEngine.instance.instrumentationService.logException(
- CaughtException.withMessage(
- 'Invalid glob pattern: "$pattern"', exception, stackTrace));
- }
- }
- }
- return _analyzedFilesGlobs;
- }
-
/// The list of current analysis sessions in all contexts.
List<AnalysisSession> get currentSessions {
return driverMap.values.map((driver) => driver.currentSession).toList();
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 0de59b9..9ff6e06 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -26,7 +26,6 @@
import 'package:analyzer/src/pubspec/pubspec_validator.dart';
import 'package:analyzer/src/task/options.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
-import 'package:analyzer/src/util/glob.dart';
import 'package:analyzer/src/workspace/bazel.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
import 'package:analyzer_plugin/utilities/analyzer_converter.dart';
@@ -177,9 +176,6 @@
@override
List<String> includedPaths = <String>[];
- /// A list of the globs used to determine which files should be analyzed.
- final List<Glob> analyzedFilesGlobs;
-
/// The instrumentation service used to report instrumentation data.
final InstrumentationService _instrumentationService;
@@ -206,7 +202,6 @@
this._byteStore,
this._performanceLog,
this._scheduler,
- this.analyzedFilesGlobs,
this._instrumentationService,
) {
pathContext = resourceProvider.pathContext;
@@ -658,19 +653,6 @@
bazelSubscriptions[folder] = _BazelWorkspaceSubscription(subscription);
}
}
-
- /// Create and return a source representing the given [file] within the given
- /// [driver].
- static Source createSourceInContext(AnalysisDriver driver, File file) {
- // TODO(brianwilkerson) Optimize this, by allowing support for source
- // factories to restore URI's from a file path rather than a source.
- var source = file.createSource();
- if (driver == null) {
- return source;
- }
- var uri = driver.sourceFactory.restoreUri(source);
- return file.createSource(uri);
- }
}
/// A watcher with subscription used to detect changes to some file.
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index d7efddf..46b867d 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -508,13 +508,23 @@
}
String _getSdkPath(ArgResults args) {
- if (args[DART_SDK] != null) {
- return args[DART_SDK];
- } else if (args[DART_SDK_ALIAS] != null) {
- return args[DART_SDK_ALIAS];
- } else {
- return getSdkPath();
+ String sdkPath;
+
+ void tryCandidateArgument(String argumentName) {
+ var argumentValue = args[argumentName];
+ if (sdkPath == null && argumentValue is String) {
+ sdkPath = argumentValue;
+ }
}
+
+ tryCandidateArgument(DART_SDK);
+ tryCandidateArgument(DART_SDK_ALIAS);
+ sdkPath ??= getSdkPath();
+
+ var pathContext = PhysicalResourceProvider.INSTANCE.pathContext;
+ return pathContext.normalize(
+ pathContext.absolute(sdkPath),
+ );
}
/// Print information about how to use the server.
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index ddf271f..6cf3730 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -22,6 +22,7 @@
defineReflectiveTests(ErrorResolverTest);
defineReflectiveTests(PrefixedNamespaceTest);
defineReflectiveTests(StrictModeTest);
+ defineReflectiveTests(StrictModeWithoutNullSafetyTest);
defineReflectiveTests(TypePropagationTest);
});
}
@@ -278,10 +279,60 @@
/// The class `StrictModeTest` contains tests to ensure that the correct errors
/// and warnings are reported when the analysis engine is run in strict mode.
@reflectiveTest
-class StrictModeTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
- // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
- // test cases.
+class StrictModeTest extends PubPackageResolutionTest with StrictModeTestCases {
+ test_conditional_isNot() async {
+ await assertNoErrorsInCode(r'''
+int f(num n) {
+ return (n is! int) ? 0 : n & 0x0F;
+}
+''');
+ }
+
+ test_conditional_or_is() async {
+ await assertNoErrorsInCode(r'''
+int f(num n) {
+ return (n is! int || n < 0) ? 0 : n & 0x0F;
+}
+''');
+ }
+
+ test_if_isNot() async {
+ await assertNoErrorsInCode(r'''
+int f(num n) {
+ if (n is! int) {
+ return 0;
+ } else {
+ return n & 0x0F;
+ }
+}
+''');
+ }
+
+ test_if_isNot_abrupt() async {
+ await assertNoErrorsInCode(r'''
+int f(num n) {
+ if (n is! int) {
+ return 0;
+ }
+ return n & 0x0F;
+}
+''');
+ }
+
+ test_if_or_is() async {
+ await assertNoErrorsInCode(r'''
+int f(num n) {
+ if (n is! int || n < 0) {
+ return 0;
+ } else {
+ return n & 0x0F;
+ }
+}
+''');
+ }
+}
+
+mixin StrictModeTestCases on PubPackageResolutionTest {
test_assert_is() async {
await assertErrorsInCode(r'''
int f(num n) {
@@ -306,48 +357,27 @@
}''');
}
- test_conditional_isNot() async {
- await assertErrorsInCode(r'''
-int f(num n) {
- return (n is! int) ? 0 : n & 0x0F;
-}''', [
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 44, 1),
- ]);
- }
-
- test_conditional_or_is() async {
- await assertErrorsInCode(r'''
-int f(num n) {
- return (n is! int || n < 0) ? 0 : n & 0x0F;
-}''', [
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 53, 1),
- ]);
- }
-
test_for() async {
- await assertErrorsInCode(r'''
-int f(List<int> list) {
- num sum = 0;
- for (num i = 0; i < list.length; i++) {
+ await assertNoErrorsInCode(r'''
+void f(List<int> list) {
+ num sum = 0; // ignore: unused_local_variable
+ for (int i = 0; i < list.length; i++) {
sum += list[i];
}
-}''', [
- error(HintCode.MISSING_RETURN, 4, 1),
- error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
- ]);
+}
+''');
}
test_forEach() async {
await assertErrorsInCode(r'''
-int f(List<int> list) {
- num sum = 0;
+void f(List<int> list) {
+ num sum = 0; // ignore: unused_local_variable
for (num n in list) {
sum += n & 0x0F;
}
-}''', [
- error(HintCode.MISSING_RETURN, 4, 1),
- error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 76, 1),
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 110, 1),
]);
}
@@ -371,6 +401,42 @@
}''');
}
+ test_localVar() async {
+ await assertErrorsInCode(r'''
+int f() {
+ num n = 1234;
+ return n & 0x0F;
+}''', [
+ error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 37, 1),
+ ]);
+ }
+}
+
+/// The class `StrictModeTest` contains tests to ensure that the correct errors
+/// and warnings are reported when the analysis engine is run in strict mode.
+@reflectiveTest
+class StrictModeWithoutNullSafetyTest extends PubPackageResolutionTest
+ with StrictModeTestCases, WithoutNullSafetyMixin {
+ test_conditional_isNot() async {
+ await assertErrorsInCode(r'''
+int f(num n) {
+ return (n is! int) ? 0 : n & 0x0F;
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 44, 1),
+ ]);
+ }
+
+ test_conditional_or_is() async {
+ await assertErrorsInCode(r'''
+int f(num n) {
+ return (n is! int || n < 0) ? 0 : n & 0x0F;
+}
+''', [
+ error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 53, 1),
+ ]);
+ }
+
test_if_isNot() async {
await assertErrorsInCode(r'''
int f(num n) {
@@ -379,7 +445,8 @@
} else {
return n & 0x0F;
}
-}''', [
+}
+''', [
error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 72, 1),
]);
}
@@ -391,7 +458,8 @@
return 0;
}
return n & 0x0F;
-}''', [
+}
+''', [
error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 63, 1),
]);
}
@@ -404,27 +472,15 @@
} else {
return n & 0x0F;
}
-}''', [
+}
+''', [
error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 81, 1),
]);
}
-
- test_localVar() async {
- await assertErrorsInCode(r'''
-int f() {
- num n = 1234;
- return n & 0x0F;
-}''', [
- error(CompileTimeErrorCode.UNDEFINED_OPERATOR, 37, 1),
- ]);
- }
}
@reflectiveTest
-class TypePropagationTest extends PubPackageResolutionTest
- with WithoutNullSafetyMixin {
- // TODO(https://github.com/dart-lang/sdk/issues/44666): Use null safety in
- // test cases.
+class TypePropagationTest extends PubPackageResolutionTest {
test_assignment_null() async {
String code = r'''
main() {
@@ -515,22 +571,23 @@
}
class C {
- void f() {
- Base x = null;
+ void f(Base x) {
+ x = Base();
if (x is Derived) {
print(x.y); // BAD
}
- x = null;
+ x = Base();
}
}
-void g() {
- Base x = null;
+void g(Base x) {
+ x = Base();
if (x is Derived) {
print(x.y); // GOOD
}
- x = null;
-}''');
+ x = Base();
+}
+''');
}
test_objectAccessInference_disabled_for_library_prefix() async {
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index e9319da..60d4e16 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -15,9 +15,6 @@
defineReflectiveSuite(() {
defineReflectiveTests(MethodInvocationResolutionTest);
defineReflectiveTests(MethodInvocationResolutionWithNullSafetyTest);
- defineReflectiveTests(
- MethodInvocationResolutionWithNonFunctionTypeAliasesTest,
- );
});
}
@@ -2447,66 +2444,9 @@
}
}
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class MethodInvocationResolutionWithNonFunctionTypeAliasesTest
- extends PubPackageResolutionTest {
- test_hasReceiver_typeAlias_staticMethod() async {
- await assertNoErrorsInCode(r'''
-class A {
- static void foo(int _) {}
-}
-
-typedef B = A;
-
-void f() {
- B.foo(0);
-}
-''');
-
- assertMethodInvocation(
- findNode.methodInvocation('foo(0)'),
- findElement.method('foo'),
- 'void Function(int)',
- );
-
- assertTypeAliasRef(
- findNode.simple('B.foo'),
- findElement.typeAlias('B'),
- );
- }
-
- test_hasReceiver_typeAlias_staticMethod_generic() async {
- await assertNoErrorsInCode(r'''
-class A<T> {
- static void foo(int _) {}
-}
-
-typedef B<T> = A<T>;
-
-void f() {
- B.foo(0);
-}
-''');
-
- assertMethodInvocation(
- findNode.methodInvocation('foo(0)'),
- findElement.method('foo'),
- 'void Function(int)',
- );
-
- assertTypeAliasRef(
- findNode.simple('B.foo'),
- findElement.typeAlias('B'),
- );
- }
-}
-
@reflectiveTest
class MethodInvocationResolutionWithNullSafetyTest
- extends PubPackageResolutionTest
- with WithNullSafetyMixin, MethodInvocationResolutionTestCases {
+ extends PubPackageResolutionTest with MethodInvocationResolutionTestCases {
test_hasReceiver_deferredImportPrefix_loadLibrary_optIn_fromOptOut() async {
newFile('$testPackageLibPath/a.dart', content: r'''
class A {}
@@ -2764,6 +2704,56 @@
);
}
+ test_hasReceiver_typeAlias_staticMethod() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ static void foo(int _) {}
+}
+
+typedef B = A;
+
+void f() {
+ B.foo(0);
+}
+''');
+
+ assertMethodInvocation(
+ findNode.methodInvocation('foo(0)'),
+ findElement.method('foo'),
+ 'void Function(int)',
+ );
+
+ assertTypeAliasRef(
+ findNode.simple('B.foo'),
+ findElement.typeAlias('B'),
+ );
+ }
+
+ test_hasReceiver_typeAlias_staticMethod_generic() async {
+ await assertNoErrorsInCode(r'''
+class A<T> {
+ static void foo(int _) {}
+}
+
+typedef B<T> = A<T>;
+
+void f() {
+ B.foo(0);
+}
+''');
+
+ assertMethodInvocation(
+ findNode.methodInvocation('foo(0)'),
+ findElement.method('foo'),
+ 'void Function(int)',
+ );
+
+ assertTypeAliasRef(
+ findNode.simple('B.foo'),
+ findElement.typeAlias('B'),
+ );
+ }
+
test_hasReceiver_typeParameter_promotedToNonNullable() async {
await assertNoErrorsInCode('''
void f<T>(T? t) {
diff --git a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
index 4776e89..17d8a68 100644
--- a/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_initialized_with_non_constant_value_test.dart
@@ -10,9 +10,6 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConstInitializedWithNonConstantValueTest);
- defineReflectiveTests(
- ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest,
- );
});
}
@@ -105,13 +102,7 @@
1),
]);
}
-}
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one above it.
-@reflectiveTest
-class ConstInitializedWithNonConstantValueWithNonFunctionTypeAliasesTest
- extends PubPackageResolutionTest {
test_typeLiteral_interfaceType() async {
await assertNoErrorsInCode(r'''
const a = int;
diff --git a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
index da6d102..af8e6e3 100644
--- a/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/nullable_type_in_on_clause_test.dart
@@ -13,8 +13,6 @@
});
}
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one above it.
@reflectiveTest
class NullableTypeInOnClauseTest extends PubPackageResolutionTest {
test_nonNullable() async {
diff --git a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
index 831cfaa..68ba41b 100644
--- a/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_alias_cannot_reference_itself_test.dart
@@ -10,9 +10,6 @@
main() {
defineReflectiveSuite(() {
defineReflectiveTests(TypeAliasCannotReferenceItselfTest);
- defineReflectiveTests(
- TypeAliasCannotReferenceItselfWithNonFunctionTypeAliasesTest,
- );
});
}
@@ -91,6 +88,56 @@
''');
}
+ test_nonFunction_aliasedType_cycleOf2() async {
+ await assertErrorsInCode('''
+typedef T1 = T2;
+typedef T2 = T1;
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 16),
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 17, 16),
+ ]);
+ }
+
+ test_nonFunction_aliasedType_directly_functionWithIt() async {
+ await assertErrorsInCode('''
+typedef T = void Function(T);
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 29),
+ ]);
+ }
+
+ test_nonFunction_aliasedType_directly_it_none() async {
+ await assertErrorsInCode('''
+typedef T = T;
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 14),
+ ]);
+ }
+
+ test_nonFunction_aliasedType_directly_it_question() async {
+ await assertErrorsInCode('''
+typedef T = T?;
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 15),
+ ]);
+ }
+
+ test_nonFunction_aliasedType_directly_ListOfIt() async {
+ await assertErrorsInCode('''
+typedef T = List<T>;
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 20),
+ ]);
+ }
+
+ test_nonFunction_typeParameterBounds() async {
+ await assertErrorsInCode('''
+typedef T<X extends T<Never>> = List<X>;
+''', [
+ error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 40),
+ ]);
+ }
+
test_parameterType_named() async {
await assertErrorsInCode('''
typedef A({A a});
@@ -161,59 +208,3 @@
]);
}
}
-
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class TypeAliasCannotReferenceItselfWithNonFunctionTypeAliasesTest
- extends PubPackageResolutionTest {
- test_nonFunction_aliasedType_cycleOf2() async {
- await assertErrorsInCode('''
-typedef T1 = T2;
-typedef T2 = T1;
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 16),
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 17, 16),
- ]);
- }
-
- test_nonFunction_aliasedType_directly_functionWithIt() async {
- await assertErrorsInCode('''
-typedef T = void Function(T);
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 29),
- ]);
- }
-
- test_nonFunction_aliasedType_directly_it_none() async {
- await assertErrorsInCode('''
-typedef T = T;
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 14),
- ]);
- }
-
- test_nonFunction_aliasedType_directly_it_question() async {
- await assertErrorsInCode('''
-typedef T = T?;
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 15),
- ]);
- }
-
- test_nonFunction_aliasedType_directly_ListOfIt() async {
- await assertErrorsInCode('''
-typedef T = List<T>;
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 20),
- ]);
- }
-
- test_nonFunction_typeParameterBounds() async {
- await assertErrorsInCode('''
-typedef T<X extends T<Never>> = List<X>;
-''', [
- error(CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF, 0, 40),
- ]);
- }
-}
diff --git a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
index b33e321..b9850fb 100644
--- a/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/type_argument_not_matching_bounds_test.dart
@@ -13,9 +13,6 @@
defineReflectiveTests(
TypeArgumentNotMatchingBoundsWithNullSafetyTest,
);
- defineReflectiveTests(
- TypeArgumentNotMatchingBoundsWithNonFunctionTypeAliasesTest,
- );
});
}
@@ -418,44 +415,10 @@
}
}
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
-@reflectiveTest
-class TypeArgumentNotMatchingBoundsWithNonFunctionTypeAliasesTest
- extends PubPackageResolutionTest
- with TypeArgumentNotMatchingBoundsTestCases {
- test_nonFunctionTypeAlias_interfaceType_parameter() async {
- await assertErrorsInCode(r'''
-class A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<String> a) {}
-''', [
- error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6),
- ]);
- }
-
- test_nonFunctionTypeAlias_interfaceType_parameter_regularBounded() async {
- await assertNoErrorsInCode(r'''
-class A {}
-class B extends A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<B> a) {}
-''');
- }
-
- test_nonFunctionTypeAlias_interfaceType_parameter_superBounded() async {
- await assertNoErrorsInCode(r'''
-class A {}
-typedef X<T extends A> = Map<int, T>;
-void f(X<Never> a) {}
-''');
- }
-}
-
@reflectiveTest
class TypeArgumentNotMatchingBoundsWithNullSafetyTest
extends PubPackageResolutionTest
- with TypeArgumentNotMatchingBoundsTestCases, WithNullSafetyMixin {
+ with TypeArgumentNotMatchingBoundsTestCases {
test_extends_optIn_fromOptOut_Null() async {
newFile('$testPackageLibPath/a.dart', content: r'''
class A<X extends int> {}
@@ -488,6 +451,33 @@
''');
}
+ test_nonFunctionTypeAlias_interfaceType_parameter() async {
+ await assertErrorsInCode(r'''
+class A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<String> a) {}
+''', [
+ error(CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS, 58, 6),
+ ]);
+ }
+
+ test_nonFunctionTypeAlias_interfaceType_parameter_regularBounded() async {
+ await assertNoErrorsInCode(r'''
+class A {}
+class B extends A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<B> a) {}
+''');
+ }
+
+ test_nonFunctionTypeAlias_interfaceType_parameter_superBounded() async {
+ await assertNoErrorsInCode(r'''
+class A {}
+typedef X<T extends A> = Map<int, T>;
+void f(X<Never> a) {}
+''');
+ }
+
test_notRegularBounded_notSuperBounded_invariant() async {
await assertErrorsInCode(r'''
typedef A<X> = X Function(X);
diff --git a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
index de7dc23..c3ca7ec 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_element_test.dart
@@ -11,7 +11,6 @@
defineReflectiveSuite(() {
defineReflectiveTests(UnusedElementTest);
defineReflectiveTests(UnusedElementWithNullSafetyTest);
- defineReflectiveTests(UnusedElementWithNonFunctionTypeAliasesTest);
});
}
@@ -1578,11 +1577,20 @@
}
}
-/// TODO(https://github.com/dart-lang/sdk/issues/44666): Combine this class
-/// with the one it extends.
@reflectiveTest
-class UnusedElementWithNonFunctionTypeAliasesTest
- extends PubPackageResolutionTest {
+class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest {
+ test_optionalParameter_isUsed_overrideRequiredNamed() async {
+ await assertNoErrorsInCode(r'''
+class A {
+ void _m({required int a}) {}
+}
+class B implements A {
+ void _m({int a = 0}) {}
+}
+f() => A()._m(a: 0);
+''');
+ }
+
test_typeAlias_interfaceType_isUsed_typeName_isExpression() async {
await assertNoErrorsInCode(r'''
typedef _A = List<int>;
@@ -1619,19 +1627,3 @@
]);
}
}
-
-@reflectiveTest
-class UnusedElementWithNullSafetyTest extends PubPackageResolutionTest
- with WithNullSafetyMixin {
- test_optionalParameter_isUsed_overrideRequiredNamed() async {
- await assertNoErrorsInCode(r'''
-class A {
- void _m({required int a}) {}
-}
-class B implements A {
- void _m({int a = 0}) {}
-}
-f() => A()._m(a: 0);
-''');
- }
-}
diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml
index 155491d..2a4517c 100644
--- a/pkg/dev_compiler/pubspec.yaml
+++ b/pkg/dev_compiler/pubspec.yaml
@@ -28,8 +28,6 @@
path: ../vm
dev_dependencies:
- analyzer:
- path: ../analyzer
expect:
path: ../expect
js:
@@ -38,7 +36,6 @@
path: ../modular_test
package_config: any
pedantic: ^1.8.0
- pub_semver: any
sourcemap_testing:
path: ../sourcemap_testing
stack_trace: any
diff --git a/pkg/dev_compiler/tool/check_nnbd_sdk.dart b/pkg/dev_compiler/tool/check_nnbd_sdk.dart
deleted file mode 100644
index e4b9028..0000000
--- a/pkg/dev_compiler/tool/check_nnbd_sdk.dart
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2019, 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.
-
-// @dart = 2.9
-
-/// Command-line tool that runs dartanalyzer on a sdk under the perspective of
-/// one tool.
-// TODO(sigmund): generalize this to support other tools, not just ddc.
-
-import 'dart:io';
-import 'package:args/args.dart';
-import 'package:front_end/src/fasta/resolve_input_uri.dart';
-
-import 'patch_sdk.dart' as patch;
-
-void main(List<String> argv) {
- if (Platform.isWindows) {
- print('Golden file does not support Windows. Skipping.');
- return;
- }
- var args = _parser.parse(argv);
- if (args['help'] as bool) {
- print('Apply patch file to the SDK and report analysis errors from the '
- 'resulting libraries.\n\n'
- 'Usage: ${Platform.script.pathSegments.last} [options...]\n\n'
- '${_parser.usage}');
- return;
- }
- var baseDir = args['out'] as String;
- if (baseDir == null) {
- var tmp = Directory.systemTemp.createTempSync('check_sdk-');
- baseDir = tmp.path;
- }
- var baseUri = resolveInputUri(baseDir.endsWith('/') ? baseDir : '$baseDir/');
- var sdkDir = baseUri.resolve('sdk/').toFilePath();
- print('Generating a patched sdk at ${baseUri.path}');
-
- var librariesJson = args['libraries'] != null
- ? resolveInputUri(args['libraries'] as String)
- : Platform.script.resolve('../../../sdk/lib/libraries.json');
- var target = args['target'] as String;
- patch.main([
- '--libraries',
- librariesJson.toFilePath(),
- '--target',
- target,
- '--out',
- sdkDir,
- '--merge-parts',
- '--nnbd',
- ]);
-
- var isWeb = false;
- var isNative = false;
- switch (target) {
- case 'dartdevc':
- case 'dart2js':
- isWeb = true;
- break;
- case 'flutter':
- case 'vm':
- isNative = true;
- break;
- }
-
- var core = '''
-import 'dart:async';
-import 'dart:collection';
-import 'dart:convert';
-import 'dart:core';
-import 'dart:developer';
-import 'dart:math';
-import 'dart:typed_data';
-''';
-
- var web = !isWeb
- ? ''
- : '''
-import 'dart:js';
-import 'dart:js_util';
-import 'dart:indexed_db';
-import 'dart:html';
-import 'dart:html_common';
-import 'dart:svg';
-import 'dart:web_audio';
-import 'dart:web_gl';
-import 'dart:web_sql';
-''';
-
- var native = !isNative
- ? '''
-import 'dart:io';
-import 'dart:isolate';
-'''
- : '''
-import 'dart:ffi';
-import 'dart:io';
-import 'dart:isolate';
-import 'dart:mirrors';
-''';
-
- var emptyProgramUri = baseUri.resolve('empty_program.dart');
- File.fromUri(emptyProgramUri).writeAsStringSync('''
-$core
-$web
-$native
-
-main() {}
-''');
-
- print('Running dartanalyzer');
- var dart = resolveInputUri(Platform.resolvedExecutable);
- var analyzerSnapshot =
- dart.resolve('snapshots/dartanalyzer.dart.snapshot').toFilePath();
- var result = Process.runSync(dart.toFilePath(), [
- // The NNBD dart binaries / snapshots require this flag to be enabled at
- // VM level.
- analyzerSnapshot,
- '--dart-sdk=${sdkDir}',
- '--format',
- 'machine',
- '--sdk-warnings',
- '--no-hints',
- emptyProgramUri.toFilePath()
- ]);
-
- stdout.write(result.stdout);
- var errors = result.stderr as String;
-
- // Trim temporary directory paths and sort errors.
- errors = errors.replaceAll(sdkDir, '');
- if (!(args['keep-lines'] as bool)) {
- // Golden files change frequenty if line numbers are recorded.
- // We remove them by default but provide an option to show them if
- // they can be helpful.
- errors = errors.replaceAll(RegExp(r'\|[0-9]*\|[0-9]*\|[0-9]*\|'), '|');
- }
- var errorList = errors.isEmpty ? <String>[] : errors.trim().split('\n');
- var count = errorList.length;
- print('$count analyzer errors.');
- errorList.sort();
- errors = errorList.join('\n') + '\n';
- var errorFile = baseUri.resolve('errors.txt');
- print('Errors emitted to ${errorFile.path}');
- File.fromUri(errorFile).writeAsStringSync(errors, flush: true);
-
- // Check against golden file.
- var goldenFile =
- Platform.script.resolve('${target}_nnbd_sdk_error_golden.txt');
- var golden = File.fromUri(goldenFile).readAsStringSync();
- if (errors != golden) {
- if (args['update-golden'] as bool) {
- // Update the golden file.
- File.fromUri(goldenFile).writeAsStringSync(errors, flush: true);
- print('Golden file updated.');
- exit(0);
- } else {
- // Fail.
- print('Golden file does not match.');
- print('\nTo update the golden file, run:'
- '\n ${Platform.executable} ${Platform.script} '
- '${argv.join(' ')} --update-golden');
-
- // Compare the two sorted lists to show what errors changed. Note, we
- // don't use `diff` as an external tool because it is not available on
- // windows bots.
- var toAdd = <String>[];
- var toRemove = <String>[];
- var goldenList = golden.trim().split('\n');
- var i = 0, j = 0;
- for (; i < errorList.length && j < goldenList.length;) {
- var compare = errorList[i].compareTo(goldenList[j]);
- if (compare == 0) {
- i++;
- j++;
- } else if (compare < 0) {
- toAdd.add(errorList[i]);
- i++;
- } else {
- toRemove.add(goldenList[j]);
- j++;
- }
- }
- for (; i < errorList.length; i++) {
- toAdd.add(errorList[i]);
- }
- for (; j < goldenList.length; j++) {
- toRemove.add(goldenList[j]);
- }
- print('\nNew errors:');
- print(toAdd.join('\n'));
- print('\nErrors that can be removed from the golden file:');
- print(toRemove.join('\n'));
- exit(1);
- }
- }
- exit(0);
-}
-
-final _parser = ArgParser()
- ..addOption('libraries',
- help: 'Path to the nnbd libraries.json (defaults to the one under '
- 'sdk/lib/libraries.json.')
- ..addOption('out',
- help: 'Path to an output folder (defaults to a new tmp folder).')
- ..addOption('target',
- help: 'The target tool. '
- 'This name matches one of the possible targets in libraries.json '
- 'and it is used to pick which patch files will be applied.',
- allowed: ['dartdevc', 'dart2js', 'dart2js_server', 'vm', 'flutter'],
- defaultsTo: 'dartdevc')
- ..addFlag('update-golden', help: 'Update the golden file.', defaultsTo: false)
- ..addFlag('keep-lines',
- help: 'Show line numbers on errors.', defaultsTo: false)
- ..addFlag('help', abbr: 'h', help: 'Display this message.');
diff --git a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 98ddc8c..0000000
--- a/pkg/dev_compiler/tool/dart2js_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '&': JSNumber.& (num Function(num)), int.& (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '<<': JSNumber.<< (num Function(num)), int.<< (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '>>': JSNumber.>> (num Function(num)), int.>> (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '>>>': JSNumber.>>> (num Function(num)), int.>>> (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '\|': JSNumber.\| (num Function(num)), int.\| (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|INCONSISTENT_INHERITANCE|lib/_internal/js_runtime/lib/interceptors.dart|Superinterfaces don't have a valid override for '^': JSNumber.^ (num Function(num)), int.^ (int Function(int)).
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
-ERROR|COMPILE_TIME_ERROR|UNDEFINED_OPERATOR|lib/_internal/js_runtime/lib/interceptors.dart|The operator '&' isn't defined for the type 'JSInt'.
diff --git a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 3adfa76..0000000
--- a/pkg/dev_compiler/tool/dartdevc_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|COMPILE_TIME_ERROR|CONST_CONSTRUCTOR_THROWS_EXCEPTION|lib/core/core.dart|Const constructors can't throw exceptions.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
-ERROR|SYNTACTIC_ERROR|CONST_FACTORY|lib/core/core.dart|Only redirecting factory constructors can be declared to be 'const'.
diff --git a/pkg/dev_compiler/tool/kernel_sdk.dart b/pkg/dev_compiler/tool/kernel_sdk.dart
deleted file mode 100755
index 64ace35..0000000
--- a/pkg/dev_compiler/tool/kernel_sdk.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2017, 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.
-
-// @dart = 2.9
-
-import 'dart:async';
-import 'dart:convert' show json;
-import 'dart:io';
-import 'package:args/args.dart' show ArgParser;
-import 'package:build_integration/file_system/multi_root.dart';
-import 'package:dev_compiler/src/compiler/module_builder.dart';
-import 'package:dev_compiler/src/compiler/shared_command.dart'
- show SharedCompilerOptions;
-import 'package:dev_compiler/src/kernel/target.dart';
-import 'package:dev_compiler/src/kernel/command.dart';
-import 'package:dev_compiler/src/kernel/compiler.dart';
-import 'package:front_end/src/api_unstable/ddc.dart'
- show
- CompilerOptions,
- DiagnosticMessage,
- Severity,
- StandardFileSystem,
- kernelForModule,
- printDiagnosticMessage;
-import 'package:kernel/kernel.dart';
-import 'package:kernel/target/targets.dart';
-import 'package:path/path.dart' as p;
-
-Future main(List<String> args) async {
- var ddcPath = p.dirname(p.dirname(p.fromUri(Platform.script)));
-
- // Parse flags.
- var parser = ArgParser()
- ..addOption('output')
- ..addOption('libraries',
- defaultsTo: p.join(ddcPath, '../../sdk/lib/libraries.json'))
- ..addOption('packages', defaultsTo: p.join(ddcPath, '../../.packages'));
- var parserOptions = parser.parse(args);
-
- var outputPath = parserOptions['output'] as String;
- if (outputPath == null) {
- var sdkRoot = p.absolute(p.dirname(p.dirname(ddcPath)));
- var buildDir = p.join(sdkRoot, Platform.isMacOS ? 'xcodebuild' : 'out');
- var genDir = p.join(buildDir, 'ReleaseX64', 'gen', 'utils', 'dartdevc');
- outputPath = p.join(genDir, 'kernel', 'ddc_sdk.dill');
- }
-
- var librarySpecPath = parserOptions['libraries'] as String;
- var packagesPath = parserOptions['packages'] as String;
-
- var target = DevCompilerTarget(TargetFlags());
- void onDiagnostic(DiagnosticMessage message) {
- printDiagnosticMessage(message, print);
- if (message.severity == Severity.error ||
- message.severity == Severity.internalProblem) {
- exitCode = 1;
- }
- }
-
- var customScheme = 'org-dartlang-sdk';
- var fileSystem = MultiRootFileSystem(
- customScheme, [Uri.base], StandardFileSystem.instance);
- var sdkRoot = Uri.parse('$customScheme:/');
- var packagesFileUri = sdkRoot
- .resolve(p.relative(Uri.file(packagesPath).path, from: Uri.base.path));
- if (packagesFileUri.scheme != customScheme) {
- throw 'packagesPath has to be under ${Uri.base}';
- }
- var librariesSpecificationUri = sdkRoot
- .resolve(p.relative(Uri.file(librarySpecPath).path, from: Uri.base.path));
- if (librariesSpecificationUri.scheme != customScheme) {
- throw 'librarySpecPath has to be under ${Uri.base}';
- }
-
- var options = CompilerOptions()
- ..compileSdk = true
- ..fileSystem = fileSystem
- ..sdkRoot = sdkRoot
- ..packagesFileUri = packagesFileUri
- ..librariesSpecificationUri = librariesSpecificationUri
- ..target = target
- ..onDiagnostic = onDiagnostic
- ..environmentDefines = {};
-
- var inputs = target.extraRequiredLibraries.map(Uri.parse).toList();
-
- var compilerResult = await kernelForModule(inputs, options);
- var component = compilerResult.component;
-
- var outputDir = p.dirname(outputPath);
- await Directory(outputDir).create(recursive: true);
- await writeComponentToBinary(component, outputPath);
- File(librarySpecPath)
- .copySync(p.join(p.dirname(outputDir), p.basename(librarySpecPath)));
-
- var jsModule = ProgramCompiler(
- component,
- compilerResult.classHierarchy,
- SharedCompilerOptions(moduleName: 'dart_sdk'),
- const {},
- const {}).emitModule(component);
- var moduleFormats = {
- 'amd': ModuleFormat.amd,
- 'common': ModuleFormat.common,
- 'es6': ModuleFormat.es6,
- 'legacy': ModuleFormat.ddc,
- };
-
- for (var name in moduleFormats.keys) {
- var format = moduleFormats[name];
- var jsDir = p.join(outputDir, name);
- var jsPath = p.join(jsDir, 'dart_sdk.js');
- var mapPath = '$jsPath.map';
- await Directory(jsDir).create();
- var jsCode = jsProgramToCode(jsModule, format,
- jsUrl: jsPath,
- mapUrl: mapPath,
- buildSourceMap: true,
- customScheme: customScheme,
- component: component);
- await File(jsPath).writeAsString(jsCode.code);
- await File(mapPath).writeAsString(json.encode(jsCode.sourceMap));
- }
-}
diff --git a/pkg/dev_compiler/tool/patch_sdk.dart b/pkg/dev_compiler/tool/patch_sdk.dart
deleted file mode 100755
index f1cd943..0000000
--- a/pkg/dev_compiler/tool/patch_sdk.dart
+++ /dev/null
@@ -1,619 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2015, 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.
-
-// @dart = 2.9
-
-/// Command line tool to merge the SDK libraries and our patch files.
-/// This is currently designed as an offline tool, but we could automate it.
-
-import 'dart:io';
-
-import 'package:_fe_analyzer_shared/src/util/relativize.dart';
-import 'package:analyzer/dart/analysis/features.dart';
-import 'package:analyzer/dart/analysis/results.dart';
-import 'package:analyzer/dart/analysis/utilities.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:args/args.dart';
-import 'package:front_end/src/base/libraries_specification.dart';
-import 'package:front_end/src/fasta/resolve_input_uri.dart';
-import 'package:pub_semver/pub_semver.dart';
-
-void main(List<String> argv) {
- var args = _parser.parse(argv);
- if (args['libraries'] == null || args['out'] == null) {
- var self = relativizeUri(Uri.base, Platform.script, isWindows);
- var librariesJson = relativizeUri(Uri.base,
- Platform.script.resolve('../../../sdk/lib/libraries.json'), isWindows);
- print('Usage: $self [other options]'
- ' --libraries <libraries.json> --out <output-dir>');
- print('For example:');
- print('\$ $self --nnbd --libraries $librariesJson --out patched-sdk-dir');
- exit(1);
- }
-
- var useNnbd = args['nnbd'] as bool;
- var target = args['target'] as String;
- var jsonUri = resolveInputUri(args['libraries'] as String);
- var libRoot = jsonUri.resolve('./');
- var outPath = args['out'] as String;
- var outDir = resolveInputUri(outPath.endsWith('/') ? outPath : '$outPath/');
- var outLibRoot = outDir.resolve('lib/');
-
- var inputVersion = Uri.file(Platform.executable).resolve('../version');
- var outVersion = outDir.resolve('version');
-
- var specification = LibrariesSpecification.parse(
- jsonUri, File.fromUri(jsonUri).readAsStringSync())
- .specificationFor(target);
-
- // Copy libraries.dart and version
- _writeSync(outVersion, File.fromUri(inputVersion).readAsStringSync());
-
- // Enumerate sdk libraries and apply patches
- for (var library in specification.allLibraries) {
- var libraryFile = File.fromUri(library.uri);
- var libraryOut =
- outLibRoot.resolve(relativizeLibraryUri(libRoot, library.uri, useNnbd));
- if (libraryFile.existsSync()) {
- var outUris = <Uri>[libraryOut];
- var libraryContents = libraryFile.readAsStringSync();
- var contents = <String>[libraryContents];
-
- for (var part
- in _parseString(libraryContents, useNnbd: useNnbd).unit.directives) {
- if (part is PartDirective) {
- var partPath = part.uri.stringValue;
- outUris.add(libraryOut.resolve(partPath));
- contents.add(
- File.fromUri(library.uri.resolve(partPath)).readAsStringSync());
- }
- }
-
- if (args['merge-parts'] as bool && outUris.length > 1) {
- outUris.length = 1;
- contents = [
- contents
- .join('\n')
- .replaceAll(RegExp('^part [^\n]*\$', multiLine: true), '')
- ];
- }
-
- var buffer = StringBuffer();
- for (var patchUri in library.patches) {
- // Note: VM targets enumerate more than one patch file, they are
- // currently written so that the first file is a valid patch file and
- // all other files can be appended at the end.
- buffer.write(File.fromUri(patchUri).readAsStringSync());
- }
- var patchContents = '$buffer';
-
- if (patchContents.isNotEmpty) {
- contents = _patchLibrary(contents, patchContents, useNnbd: useNnbd);
- }
-
- if (contents != null) {
- for (var i = 0; i < outUris.length; i++) {
- _writeSync(outUris[i], contents[i]);
- }
- } else {
- exitCode = 2;
- }
- }
- }
-
- var outLibrariesDart =
- outLibRoot.resolve('_internal/sdk_library_metadata/lib/libraries.dart');
- _writeSync(outLibrariesDart,
- _generateLibrariesDart(libRoot, specification, useNnbd));
-
- var experimentsPath = '_internal/allowed_experiments.json';
- _writeSync(
- outLibRoot.resolve(experimentsPath),
- File.fromUri(libRoot.resolve(experimentsPath)).readAsStringSync(),
- );
-}
-
-/// Writes a file, creating the directory if needed.
-void _writeSync(Uri fileUri, String contents) {
- var outDir = Directory.fromUri(fileUri.resolve('.'));
- if (!outDir.existsSync()) outDir.createSync(recursive: true);
-
- File.fromUri(fileUri).writeAsStringSync(contents);
-}
-
-final _parser = ArgParser()
- ..addFlag('nnbd',
- help: 'Whether to enable the nnbd feature.', defaultsTo: false)
- ..addFlag('merge-parts',
- help: 'Whether to merge part files. '
- 'Technically this is not necessary, but dartanalyzer '
- 'produces less warnings when enabling this flag.',
- defaultsTo: false)
- ..addOption('libraries',
- help: 'Path to a libraries.json specification file (required). '
- 'All libary URIs within libraries.json are expected to be somewhere '
- 'under the directory containing the libraries.json file. Reaching '
- 'out above such directory is generally not supported. Today it is '
- 'only allowed for the nnbd sdk to reuse libraries of the non-nnbd '
- 'sdk, in which case the path starts with "../../sdk/lib/".')
- ..addOption('out', help: 'Path to an output folder (required).')
- ..addOption('target',
- help: 'The target tool. '
- 'This name matches one of the possible targets in libraries.json '
- 'and it is used to pick which patch files will be applied.',
- allowed: ['dartdevc', 'dart2js', 'dart2js_server', 'vm', 'flutter']);
-
-/// Merges dart:* library code with code from *_patch.dart file.
-///
-/// Takes a list of the library's parts contents, with the main library contents
-/// first in the list, and the contents of the patch file.
-///
-/// The result will have `@patch` implementations merged into the correct place
-/// (e.g. the class or top-level function declaration) and all other
-/// declarations introduced by the patch will be placed into the main library
-/// file.
-///
-/// This is purely a syntactic transformation. Unlike dart2js patch files, there
-/// is no semantic meaning given to the *_patch files, and they do not magically
-/// get their own library scope, etc.
-///
-/// Editorializing: the dart2js approach requires a Dart front end such as
-/// package:analyzer to semantically model a feature beyond what is specified
-/// in the Dart language. Since this feature is only for the convenience of
-/// writing the dart:* libraries, and not a tool given to Dart developers, it
-/// seems like a non-ideal situation. Instead we keep the preprocessing simple.
-List<String> _patchLibrary(List<String> partsContents, String patchContents,
- {bool useNnbd = false}) {
- var results = <StringEditBuffer>[];
-
- // Parse the patch first. We'll need to extract bits of this as we go through
- // the other files.
- var patchFinder = PatchFinder.parseAndVisit(patchContents, useNnbd: useNnbd);
-
- // Merge `external` declarations with the corresponding `@patch` code.
- var failed = false;
- for (var partContent in partsContents) {
- var partEdits = StringEditBuffer(partContent);
- var partUnit = _parseString(partContent, useNnbd: useNnbd).unit;
- var patcher = PatchApplier(partEdits, patchFinder);
- partUnit.accept(patcher);
- if (!failed) failed = patcher.patchWasMissing;
- results.add(partEdits);
- }
- if (failed) return null;
- return List<String>.from(results.map((e) => e.toString()));
-}
-
-/// Merge `@patch` declarations into `external` declarations.
-class PatchApplier extends GeneralizingAstVisitor<void> {
- final StringEditBuffer edits;
- final PatchFinder patch;
-
- bool _isLibrary = true; // until proven otherwise.
- bool patchWasMissing = false;
-
- PatchApplier(this.edits, this.patch);
-
- @override
- void visitCompilationUnit(CompilationUnit node) {
- super.visitCompilationUnit(node);
- if (_isLibrary) _mergeUnpatched(node);
- }
-
- void _merge(AstNode node, int pos) {
- var code = patch.contents.substring(node.offset, node.end);
- edits.insert(pos, '\n' + code);
- }
-
- /// Merges directives and declarations that are not `@patch` into the library.
- void _mergeUnpatched(CompilationUnit unit) {
- // Merge imports from the patch
- // TODO(jmesserly): remove duplicate imports
-
- // To patch a library, we must have a library directive
- var libDir = unit.directives.first as LibraryDirective;
- var importPos = unit.directives
- .lastWhere((d) => d is ImportDirective, orElse: () => libDir)
- .end;
- for (var d in patch.unit.directives.whereType<ImportDirective>()) {
- _merge(d, importPos);
- }
-
- var partPos = unit.directives.last.end;
- for (var d in patch.unit.directives.whereType<PartDirective>()) {
- _merge(d, partPos);
- }
-
- // Merge declarations from the patch
- var declPos = edits.original.length;
- for (var d in patch.mergeDeclarations) {
- _merge(d, declPos);
- }
- }
-
- @override
- void visitPartOfDirective(PartOfDirective node) {
- _isLibrary = false;
- }
-
- @override
- void visitFunctionDeclaration(FunctionDeclaration node) {
- _maybePatch(node);
- }
-
- /// Merge patches and extensions into the class
- @override
- void visitClassDeclaration(ClassDeclaration node) {
- node.members.forEach(_maybePatch);
-
- var mergeMembers = patch.mergeMembers[_qualifiedName(node)];
- if (mergeMembers == null) return;
-
- // Merge members from the patch
- var pos = node.members.last.end;
- for (var member in mergeMembers) {
- var code = patch.contents.substring(member.offset, member.end);
- edits.insert(pos, '\n\n ' + code);
- }
- }
-
- void _maybePatch(Declaration node) {
- if (node is FieldDeclaration) return;
-
- var externalKeyword = (node as dynamic).externalKeyword as Token;
- if (externalKeyword == null) return;
-
- var name = _qualifiedName(node);
- var patchNode = patch.patches[name];
- if (patchNode == null) {
- // *.fromEnvironment are left unpatched by dart2js and are handled via
- // codegen.
- if (name != 'bool.fromEnvironment' &&
- name != 'int.fromEnvironment' &&
- name != 'String.fromEnvironment') {
- print('warning: patch not found for $name: $node');
- // TODO(sigmund): delete this fail logic? Rather than emit an empty
- // file, it's more useful to emit a file with missing patches.
- // patchWasMissing = true;
- }
- return;
- }
-
- var patchMeta = patchNode.metadata.lastWhere(_isPatchAnnotation);
- var start = patchMeta.endToken.next.offset;
- var code = patch.contents.substring(start, patchNode.end);
-
- // Const factory constructors can't be legally parsed from the patch file,
- // so we need to omit the "const" there, but still preserve it.
- if (node is ConstructorDeclaration &&
- node.constKeyword != null &&
- patchNode is ConstructorDeclaration &&
- patchNode.constKeyword == null) {
- code = 'const $code';
- }
-
- // For some node like static fields, the node's offset doesn't include
- // the external keyword. Also starting from the keyword lets us preserve
- // documentation comments.
- edits.replace(externalKeyword.offset, node.end, code);
- }
-}
-
-class PatchFinder extends GeneralizingAstVisitor<void> {
- final String contents;
- final CompilationUnit unit;
-
- final patches = <String, Declaration>{};
- final mergeMembers = <String, List<ClassMember>>{};
- final mergeDeclarations = <CompilationUnitMember>[];
-
- PatchFinder.parseAndVisit(String contents, {bool useNnbd})
- : contents = contents,
- unit = _parseString(contents, useNnbd: useNnbd).unit {
- visitCompilationUnit(unit);
- }
-
- @override
- void visitCompilationUnitMember(CompilationUnitMember node) {
- mergeDeclarations.add(node);
- }
-
- @override
- void visitClassDeclaration(ClassDeclaration node) {
- if (_isPatch(node)) {
- var members = <ClassMember>[];
- for (var member in node.members) {
- if (_isPatch(member)) {
- patches[_qualifiedName(member)] = member;
- } else {
- members.add(member);
- }
- }
- if (members.isNotEmpty) {
- mergeMembers[_qualifiedName(node)] = members;
- }
- } else {
- mergeDeclarations.add(node);
- }
- }
-
- @override
- void visitFunctionDeclaration(FunctionDeclaration node) {
- if (_isPatch(node)) {
- patches[_qualifiedName(node)] = node;
- } else {
- mergeDeclarations.add(node);
- }
- }
-
- @override
- void visitFunctionBody(node) {} // skip method bodies
-}
-
-String _qualifiedName(Declaration node) {
- var result = '';
-
- var parent = node.parent;
- if (parent is ClassDeclaration) {
- result = '${parent.name.name}.';
- }
-
- var name = (node as dynamic).name as SimpleIdentifier;
- if (name != null) result += name.name;
-
- // Make sure setters and getters don't collide.
- if (node is FunctionDeclaration && node.isSetter ||
- node is MethodDeclaration && node.isSetter) {
- result += '=';
- }
-
- return result;
-}
-
-bool _isPatch(AnnotatedNode node) => node.metadata.any(_isPatchAnnotation);
-
-bool _isPatchAnnotation(Annotation m) =>
- m.name.name == 'patch' && m.constructorName == null && m.arguments == null;
-
-/// Editable string buffer.
-///
-/// Applies a series of edits (insertions, removals, replacements) using
-/// original location information, and composes them into the edited string.
-///
-/// For example, starting with a parsed AST with original source locations,
-/// this type allows edits to be made without regards to other edits.
-class StringEditBuffer {
- final String original;
- final _edits = <_StringEdit>[];
-
- /// Creates a new transaction.
- StringEditBuffer(this.original);
-
- bool get hasEdits => _edits.isNotEmpty;
-
- /// Edit the original text, replacing text on the range [begin] and
- /// exclusive [end] with the [replacement] string.
- void replace(int begin, int end, String replacement) {
- _edits.add(_StringEdit(begin, end, replacement));
- }
-
- /// Insert [string] at [offset].
- /// Equivalent to `replace(offset, offset, string)`.
- void insert(int offset, String string) => replace(offset, offset, string);
-
- /// Remove text from the range [begin] to exclusive [end].
- /// Equivalent to `replace(begin, end, '')`.
- void remove(int begin, int end) => replace(begin, end, '');
-
- /// Applies all pending [edit]s and returns a new string.
- ///
- /// This method is non-destructive: it does not discard existing edits or
- /// change the [original] string. Further edits can be added and this method
- /// can be called again.
- ///
- /// Throws [UnsupportedError] if the edits were overlapping. If no edits were
- /// made, the original string will be returned.
- @override
- String toString() {
- var sb = StringBuffer();
- if (_edits.isEmpty) return original;
-
- // Sort edits by start location.
- _edits.sort();
-
- var consumed = 0;
- for (var edit in _edits) {
- if (consumed > edit.begin) {
- sb = StringBuffer();
- sb.write('overlapping edits. Insert at offset ');
- sb.write(edit.begin);
- sb.write(' but have consumed ');
- sb.write(consumed);
- sb.write(' input characters. List of edits:');
- for (var e in _edits) {
- sb.write('\n ');
- sb.write(e);
- }
- throw UnsupportedError(sb.toString());
- }
-
- // Add characters from the original string between this edit and the last
- // one, if any.
- var betweenEdits = original.substring(consumed, edit.begin);
- sb.write(betweenEdits);
- sb.write(edit.replace);
- consumed = edit.end;
- }
-
- // Add any text from the end of the original string that was not replaced.
- sb.write(original.substring(consumed));
- return sb.toString();
- }
-}
-
-class _StringEdit implements Comparable<_StringEdit> {
- final int begin;
- final int end;
- final String replace;
-
- _StringEdit(this.begin, this.end, this.replace);
-
- int get length => end - begin;
-
- @override
- String toString() => '(Edit @ $begin,$end: "$replace")';
-
- @override
- int compareTo(_StringEdit other) {
- var diff = begin - other.begin;
- if (diff != 0) return diff;
- return end - other.end;
- }
-}
-
-ParseStringResult _parseString(String source, {bool useNnbd}) {
- var features = FeatureSet.fromEnableFlags2(
- sdkLanguageVersion: Version.parse('2.10.0'),
- flags: [if (useNnbd) 'non-nullable', 'triple-shift'],
- );
- return parseString(content: source, featureSet: features);
-}
-
-/// Use the data from a libraries.json specification to generate the contents
-/// of `sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart`, which is
-/// needed by dartdevc-legacy and dartanalyzer.
-String _generateLibrariesDart(
- Uri libBaseUri, TargetLibrariesSpecification specification, bool usdNnbd) {
- var contents = StringBuffer();
- contents.write(_LIBRARIES_DART_PREFIX);
- for (var library in specification.allLibraries) {
- var path = relativizeLibraryUri(libBaseUri, library.uri, usdNnbd);
- contents.write(' "${library.name}": \n'
- ' const LibraryInfo("$path",\n'
- ' categories: "Client,Server"),\n');
- }
- contents.write(_LIBRARIES_DART_SUFFIX);
- return '$contents';
-}
-
-String relativizeLibraryUri(Uri libRoot, Uri uri, bool useNnbd) {
- var relativePath = relativizeUri(libRoot, uri, isWindows);
- // During the nnbd-migration we may have paths that reach out into the
- // non-nnbd directory.
- if (relativePath.startsWith('..')) {
- if (!useNnbd || !relativePath.startsWith('../../sdk/lib/')) {
- print("error: can't handle libraries that live out of the sdk folder"
- ': $relativePath');
- exit(1);
- }
- relativePath = relativePath.replaceFirst('../../sdk/lib/', '');
- }
- return relativePath;
-}
-
-final _LIBRARIES_DART_PREFIX = r'''
-library libraries;
-
-const int DART2JS_PLATFORM = 1;
-const int VM_PLATFORM = 2;
-
-enum Category { client, server, embedded }
-
-Category parseCategory(String name) {
- switch (name) {
- case "Client":
- return Category.client;
- case "Server":
- return Category.server;
- case "Embedded":
- return Category.embedded;
- }
- return null;
-}
-
-const Map<String, LibraryInfo> libraries = const {
-''';
-
-final _LIBRARIES_DART_SUFFIX = r'''
-};
-
-class LibraryInfo {
- final String path;
- final String _categories;
- final String dart2jsPath;
- final String dart2jsPatchPath;
- final bool documented;
- final int platforms;
- final bool implementation;
- final Maturity maturity;
-
- const LibraryInfo(this.path,
- {String categories: "",
- this.dart2jsPath,
- this.dart2jsPatchPath,
- this.implementation: false,
- this.documented: true,
- this.maturity: Maturity.UNSPECIFIED,
- this.platforms: DART2JS_PLATFORM | VM_PLATFORM})
- : _categories = categories;
-
- bool get isDart2jsLibrary => (platforms & DART2JS_PLATFORM) != 0;
- bool get isVmLibrary => (platforms & VM_PLATFORM) != 0;
- List<Category> get categories {
- // `"".split(,)` returns [""] not [], so we handle that case separately.
- if (_categories == "") return const <Category>[];
- return _categories.split(",").map(parseCategory).toList();
- }
-
- bool get isInternal => categories.isEmpty;
- String get categoriesString => _categories;
-}
-
-class Maturity {
- final int level;
- final String name;
- final String description;
-
- const Maturity(this.level, this.name, this.description);
-
- String toString() => "$name: $level\n$description\n";
-
- static const Maturity DEPRECATED = const Maturity(0, "Deprecated",
- "This library will be remove before next major release.");
-
- static const Maturity EXPERIMENTAL = const Maturity(
- 1,
- "Experimental",
- "This library is experimental and will likely change or be removed\n"
- "in future versions.");
-
- static const Maturity UNSTABLE = const Maturity(
- 2,
- "Unstable",
- "This library is in still changing and have not yet endured\n"
- "sufficient real-world testing.\n"
- "Backwards-compatibility is NOT guaranteed.");
-
- static const Maturity WEB_STABLE = const Maturity(
- 3,
- "Web Stable",
- "This library is tracking the DOM evolution as defined by WC3.\n"
- "Backwards-compatibility is NOT guaranteed.");
-
- static const Maturity STABLE = const Maturity(
- 4,
- "Stable",
- "The library is stable. API backwards-compatibility is guaranteed.\n"
- "However implementation details might change.");
-
- static const Maturity LOCKED = const Maturity(5, "Locked",
- "This library will not change except when serious bugs are encountered.");
-
- static const Maturity UNSPECIFIED = const Maturity(-1, "Unspecified",
- "The maturity for this library has not been specified.");
-}
-''';
diff --git a/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt b/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt
deleted file mode 100644
index 45beed5..0000000
--- a/pkg/dev_compiler/tool/vm_nnbd_sdk_error_golden.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-ERROR|STATIC_WARNING|NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER|lib/core/core.dart|3894|7|16|Missing concrete implementations of 'getter Error._stackTrace' and 'setter Error._stackTrace'.
-ERROR|STATIC_WARNING|NON_ABSTRACT_CLASS_INHERITS_ABSTRACT_MEMBER|lib/core/core.dart|3902|7|18|Missing concrete implementations of 'getter Error._stackTrace' and 'setter Error._stackTrace'.
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index af4a2a7..e22f89c 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -224,6 +224,19 @@
return ShiftOperationHelper(Token::kSHR, value, amount);
}
+DEFINE_NATIVE_ENTRY(Integer_ushrFromInteger, 0, 2) {
+ const Integer& amount =
+ Integer::CheckedHandle(zone, arguments->NativeArgAt(0));
+ GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1));
+ ASSERT(CheckInteger(amount));
+ ASSERT(CheckInteger(value));
+ if (FLAG_trace_intrinsified_natives) {
+ OS::PrintErr("Integer_ushrFromInteger: %s >>> %s\n", value.ToCString(),
+ amount.ToCString());
+ }
+ return ShiftOperationHelper(Token::kUSHR, value, amount);
+}
+
DEFINE_NATIVE_ENTRY(Integer_shlFromInteger, 0, 2) {
const Integer& amount =
Integer::CheckedHandle(zone, arguments->NativeArgAt(0));
diff --git a/runtime/tests/vm/dart/regress45047_test.dart b/runtime/tests/vm/dart/regress45047_test.dart
new file mode 100644
index 0000000..4d2f083
--- /dev/null
+++ b/runtime/tests/vm/dart/regress45047_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.
+
+// Verify that socket connection gracefully closes if cancelled.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+void main() async {
+ final task = await Socket.startConnect('google.com', 80);
+ task.cancel();
+ try {
+ await task.socket;
+ } catch (e) {
+ Expect.isTrue(e is SocketException);
+ final socketException = e as SocketException;
+ Expect.isTrue(
+ socketException.message.startsWith('Connection attempt cancelled'));
+ }
+}
diff --git a/runtime/tests/vm/dart/unsigned_shift_right_test.dart b/runtime/tests/vm/dart/unsigned_shift_right_test.dart
new file mode 100644
index 0000000..41c18f9
--- /dev/null
+++ b/runtime/tests/vm/dart/unsigned_shift_right_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, 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.
+
+// SharedOptions=--enable-experiment=triple-shift
+// VMOptions=--deterministic --optimization_counter_threshold=50
+// VMOptions=--no-intrinsify
+
+// Test int.operator>>> in case of Smi range overflow and deoptimization.
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+void test1(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test2(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test3(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test4(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test5(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test6(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test7(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test8(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test9(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test10(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test11(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test12(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test13(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test14(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+void testCornerCases() {
+ // Make sure test methods are optimized.
+ for (int i = 0; i < 100; ++i) {
+ test1(1, 1, 0);
+ test2(1, 1, 0);
+ test3(1, 1, 0);
+ test4(1, 1, 0);
+ test5(1, 1, 0);
+ test6(1, 1, 0);
+ test7(1, 1, 0);
+ test8(1, 1, 0);
+ test9(1, 1, 0);
+ test10(1, 1, 0);
+ test11(1, 1, 0);
+ test12(1, 1, 0);
+ test13(1, 1, 0);
+ test14(1, 1, 0);
+ }
+ // Run tests, may trigger deoptimization.
+ for (int i = 0; i < 100; ++i) {
+ test1(0xffffffffffffffff, 1, 0x7fffffffffffffff);
+ test2(0xffffffffffffabcd, 1, 0x7fffffffffffd5e6);
+ test3(0xffffffffffffffff, 31, 0x1ffffffff);
+ test4(0xffffffffffffffff, 32, 0xffffffff);
+ test5(0xfedcba9876543210, 32, 0xfedcba98);
+ test6(0xffffffffffffffff, 34, 0x3fffffff);
+ test7(0xffffffffffffffff, 56, 0xff);
+ test8(0xfeffffffffffabcd, 56, 0xfe);
+ test9(0xffffffffffffffff, 63, 0x1);
+ test10(0xffffffffffffffff, 64, 0);
+ test11(0xffffffffffffffff, 70, 0);
+ test12(0x8000000000000000, 1, 0x4000000000000000);
+ test13(0x7fedcba987654321, 1, 0x3ff6e5d4c3b2a190);
+ test14(0x7fedcba987654321, 40, 0x7fedcb);
+ }
+}
+
+void testSingleOneBit() {
+ for (int i = 0; i <= 63; ++i) {
+ final int x = 1 << i;
+ for (int j = 0; j <= 127; ++j) {
+ Expect.equals((j > i) ? 0 : 1 << (i - j), x >>> j);
+ }
+ }
+}
+
+void testSingleZeroBit() {
+ for (int i = 0; i <= 63; ++i) {
+ final int x = ~(1 << i);
+ for (int j = 0; j <= 127; ++j) {
+ if (j >= 64) {
+ Expect.equals(0, x >>> j);
+ } else {
+ final int mask = (1 << (64 - j)) - 1;
+ if (j > i) {
+ Expect.equals(mask, x >>> j);
+ } else {
+ Expect.equals(mask & ~(1 << (i - j)), x >>> j);
+ }
+ }
+ }
+ }
+}
+
+main() {
+ testCornerCases();
+
+ for (int i = 0; i < 100; ++i) {
+ testSingleOneBit();
+ testSingleZeroBit();
+ }
+}
diff --git a/runtime/tests/vm/dart_2/regress45047_test.dart b/runtime/tests/vm/dart_2/regress45047_test.dart
new file mode 100644
index 0000000..4d2f083
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress45047_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2021, 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.
+
+// Verify that socket connection gracefully closes if cancelled.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+
+void main() async {
+ final task = await Socket.startConnect('google.com', 80);
+ task.cancel();
+ try {
+ await task.socket;
+ } catch (e) {
+ Expect.isTrue(e is SocketException);
+ final socketException = e as SocketException;
+ Expect.isTrue(
+ socketException.message.startsWith('Connection attempt cancelled'));
+ }
+}
diff --git a/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart b/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart
new file mode 100644
index 0000000..41c18f9
--- /dev/null
+++ b/runtime/tests/vm/dart_2/unsigned_shift_right_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2021, 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.
+
+// SharedOptions=--enable-experiment=triple-shift
+// VMOptions=--deterministic --optimization_counter_threshold=50
+// VMOptions=--no-intrinsify
+
+// Test int.operator>>> in case of Smi range overflow and deoptimization.
+
+import "package:expect/expect.dart";
+
+@pragma('vm:never-inline')
+void test1(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test2(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test3(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test4(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test5(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test6(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test7(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test8(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test9(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test10(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test11(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test12(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test13(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+@pragma('vm:never-inline')
+void test14(int a, int b, int expected) {
+ Expect.equals(expected, a >>> b);
+}
+
+void testCornerCases() {
+ // Make sure test methods are optimized.
+ for (int i = 0; i < 100; ++i) {
+ test1(1, 1, 0);
+ test2(1, 1, 0);
+ test3(1, 1, 0);
+ test4(1, 1, 0);
+ test5(1, 1, 0);
+ test6(1, 1, 0);
+ test7(1, 1, 0);
+ test8(1, 1, 0);
+ test9(1, 1, 0);
+ test10(1, 1, 0);
+ test11(1, 1, 0);
+ test12(1, 1, 0);
+ test13(1, 1, 0);
+ test14(1, 1, 0);
+ }
+ // Run tests, may trigger deoptimization.
+ for (int i = 0; i < 100; ++i) {
+ test1(0xffffffffffffffff, 1, 0x7fffffffffffffff);
+ test2(0xffffffffffffabcd, 1, 0x7fffffffffffd5e6);
+ test3(0xffffffffffffffff, 31, 0x1ffffffff);
+ test4(0xffffffffffffffff, 32, 0xffffffff);
+ test5(0xfedcba9876543210, 32, 0xfedcba98);
+ test6(0xffffffffffffffff, 34, 0x3fffffff);
+ test7(0xffffffffffffffff, 56, 0xff);
+ test8(0xfeffffffffffabcd, 56, 0xfe);
+ test9(0xffffffffffffffff, 63, 0x1);
+ test10(0xffffffffffffffff, 64, 0);
+ test11(0xffffffffffffffff, 70, 0);
+ test12(0x8000000000000000, 1, 0x4000000000000000);
+ test13(0x7fedcba987654321, 1, 0x3ff6e5d4c3b2a190);
+ test14(0x7fedcba987654321, 40, 0x7fedcb);
+ }
+}
+
+void testSingleOneBit() {
+ for (int i = 0; i <= 63; ++i) {
+ final int x = 1 << i;
+ for (int j = 0; j <= 127; ++j) {
+ Expect.equals((j > i) ? 0 : 1 << (i - j), x >>> j);
+ }
+ }
+}
+
+void testSingleZeroBit() {
+ for (int i = 0; i <= 63; ++i) {
+ final int x = ~(1 << i);
+ for (int j = 0; j <= 127; ++j) {
+ if (j >= 64) {
+ Expect.equals(0, x >>> j);
+ } else {
+ final int mask = (1 << (64 - j)) - 1;
+ if (j > i) {
+ Expect.equals(mask, x >>> j);
+ } else {
+ Expect.equals(mask & ~(1 << (i - j)), x >>> j);
+ }
+ }
+ }
+ }
+}
+
+main() {
+ testCornerCases();
+
+ for (int i = 0; i < 100; ++i) {
+ testSingleOneBit();
+ testSingleZeroBit();
+ }
+}
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index db34630..2470e89 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -51,6 +51,7 @@
V(Integer_parse, 1) \
V(Integer_shlFromInteger, 2) \
V(Integer_shrFromInteger, 2) \
+ V(Integer_ushrFromInteger, 2) \
V(Bool_fromEnvironment, 3) \
V(Bool_hasEnvironment, 2) \
V(CapabilityImpl_factory, 1) \
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 95ae6e3..6073695 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -436,6 +436,19 @@
if (!arguments.IsNull()) {
type_arg = arguments.TypeAt(i);
// The parsed type_arg may or may not be finalized.
+ if (type_arg.IsTypeRef()) {
+ // Dereferencing the TypeRef 'rotates' the cycle in the recursive
+ // type argument, so that the top level type arguments of the type
+ // do not start with a TypeRef, for better readability and possibly
+ // fewer later dereferences in various type traversal routines.
+ // This rotation is not required for correctness.
+ // The cycle containing TypeRefs always involves type arguments of
+ // the super class in the flatten argument vector, so it is safe to
+ // remove TypeRefs from type arguments corresponding to the type
+ // parameters of the type class.
+ // Such TypeRefs may appear after instantiation of types at runtime.
+ type_arg = TypeRef::Cast(type_arg).type();
+ }
}
full_arguments.SetTypeAt(offset + i, type_arg);
}
diff --git a/runtime/vm/compiler/aot/aot_call_specializer.cc b/runtime/vm/compiler/aot/aot_call_specializer.cc
index 4ea2220..2729e66 100644
--- a/runtime/vm/compiler/aot/aot_call_specializer.cc
+++ b/runtime/vm/compiler/aot/aot_call_specializer.cc
@@ -567,6 +567,8 @@
FALL_THROUGH;
case Token::kSHR:
FALL_THROUGH;
+ case Token::kUSHR:
+ FALL_THROUGH;
case Token::kBIT_OR:
FALL_THROUGH;
case Token::kBIT_XOR:
@@ -579,7 +581,8 @@
FALL_THROUGH;
case Token::kMUL: {
if (FlowGraphCompiler::SupportsUnboxedInt64()) {
- if (op_kind == Token::kSHR || op_kind == Token::kSHL) {
+ if (op_kind == Token::kSHL || op_kind == Token::kSHR ||
+ op_kind == Token::kUSHR) {
left_value = PrepareStaticOpInput(left_value, kMintCid, instr);
right_value = PrepareStaticOpInput(right_value, kMintCid, instr);
replacement = new (Z) ShiftInt64OpInstr(
@@ -844,6 +847,7 @@
}
case Token::kSHL:
case Token::kSHR:
+ case Token::kUSHR:
case Token::kBIT_OR:
case Token::kBIT_XOR:
case Token::kBIT_AND:
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index f7ebf06..21149ef 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -1530,6 +1530,18 @@
LslImmediate(dst, src, kSmiTagSize);
}
+ void SmiTagAndBranchIfOverflow(Register reg, Label* label) {
+ COMPILE_ASSERT(kSmiTag == 0);
+ adds(reg, reg, compiler::Operand(reg)); // SmiTag
+ // If the value doesn't fit in a smi, the tagging changes the sign,
+ // which causes the overflow flag to be set.
+ b(label, OVERFLOW);
+#if defined(DART_COMPRESSED_POINTERS)
+ cmp(reg, compiler::Operand(reg, SXTW, 0));
+ b(label, NOT_EQUAL);
+#endif // defined(DART_COMPRESSED_POINTERS)
+ }
+
// For ARM, the near argument is ignored.
void BranchIfNotSmi(Register reg,
Label* label,
diff --git a/runtime/vm/compiler/backend/evaluator.cc b/runtime/vm/compiler/backend/evaluator.cc
index 5364b28..11b007a 100644
--- a/runtime/vm/compiler/backend/evaluator.cc
+++ b/runtime/vm/compiler/backend/evaluator.cc
@@ -27,6 +27,8 @@
case Token::kSHL:
FALL_THROUGH;
case Token::kSHR:
+ FALL_THROUGH;
+ case Token::kUSHR:
if (right.AsInt64Value() >= 0) {
return left.ShiftOp(token_kind, right, Heap::kOld);
}
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 26198ca..69b2bd0 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -2073,8 +2073,9 @@
switch (smi_op->op_kind()) {
case Token::kMUL:
case Token::kSHR:
- // For kMUL we save untagging of the argument for kSHR
- // we save tagging of the result.
+ case Token::kUSHR:
+ // For kMUL we save untagging of the argument.
+ // For kSHR/kUSHR we save tagging of the result.
return true;
default:
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index f1f8f02..fb55f7d 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -2118,6 +2118,7 @@
case Token::kSHR:
return false;
+ case Token::kUSHR:
case Token::kSHL:
// Currently only shifts by in range constant are supported, see
// BinaryInt32OpInstr::IsSupported.
@@ -2142,6 +2143,7 @@
case Token::kSHR:
return !RangeUtils::IsPositive(right_range());
+ case Token::kUSHR:
case Token::kSHL:
return can_overflow() || !RangeUtils::IsPositive(right_range());
@@ -2344,8 +2346,9 @@
case Token::kTRUNCDIV:
if (representation != kTagged) break;
FALL_THROUGH;
- case Token::kSHR:
case Token::kSHL:
+ case Token::kSHR:
+ case Token::kUSHR:
if (auto const const_def = right->definition()->AsConstant()) {
right_range = new Range();
const_def->InferRange(nullptr, right_range);
@@ -2365,7 +2368,8 @@
op = new BinaryInt32OpInstr(op_kind, left, right, deopt_id);
break;
case kUnboxedUint32:
- if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+ if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+ (op_kind == Token::kUSHR)) {
if (speculative_mode == kNotSpeculative) {
op = new ShiftUint32OpInstr(op_kind, left, right, deopt_id,
right_range);
@@ -2378,7 +2382,8 @@
}
break;
case kUnboxedInt64:
- if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+ if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+ (op_kind == Token::kUSHR)) {
if (speculative_mode == kNotSpeculative) {
op = new ShiftInt64OpInstr(op_kind, left, right, deopt_id,
right_range);
@@ -2631,6 +2636,12 @@
}
break;
+ case Token::kUSHR:
+ if (rhs >= kBitsPerInt64) {
+ return flow_graph->TryCreateConstantReplacementFor(this,
+ Object::smi_zero());
+ }
+ FALL_THROUGH;
case Token::kSHR:
if (rhs == 0) {
return left()->definition();
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index 48ba834..8e19b15 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -7828,6 +7828,7 @@
case Token::kSHL:
case Token::kSHR:
+ case Token::kUSHR:
if (right->BindsToConstant() && right->BoundConstant().IsSmi()) {
const intptr_t value = Smi::Cast(right->BoundConstant()).Value();
return 0 <= value && value < kBitsPerWord;
@@ -7958,7 +7959,8 @@
Range* right_range = nullptr)
: BinaryIntegerOpInstr(op_kind, left, right, deopt_id),
shift_range_(right_range) {
- ASSERT((op_kind == Token::kSHR) || (op_kind == Token::kSHL));
+ ASSERT((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+ (op_kind == Token::kUSHR));
mark_truncating();
}
diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc
index 694c7f9..5d9b0e9 100644
--- a/runtime/vm/compiler/backend/il_arm.cc
+++ b/runtime/vm/compiler/backend/il_arm.cc
@@ -4070,6 +4070,56 @@
__ Asr(result, TMP, result);
__ SmiTag(result);
break;
+ case Token::kUSHR: {
+ ASSERT(result != left);
+ ASSERT(result != right);
+ __ CompareImmediate(right, compiler::target::ToRawSmi(kBitsPerInt64));
+ __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
+
+ compiler::Label done;
+ __ SmiUntag(result, right);
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s s xxxxxxxxxxxxx
+ // | | | | | |
+ // 63 32 31 30 kSmiBits-1 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Smi range if and only if
+ // the shift amount >= 64 - kSmiBits.
+ //
+ // If left operand is non-negative, the result always
+ // fits into Smi range.
+ //
+ __ CompareImmediate(result, 64 - compiler::target::kSmiBits);
+ // Shift amount >= 64 - kSmiBits > 32, but < 64.
+ // Result is guaranteed to fit into Smi range.
+ // Low (Smi) part of the left operand is shifted out.
+ // High part is filled with sign bits.
+ __ sub(result, result, compiler::Operand(32), GE);
+ __ Asr(TMP, left, compiler::Operand(31), GE);
+ __ Lsr(result, TMP, result, GE);
+ __ SmiTag(result, GE);
+ __ b(&done, GE);
+ // Shift amount < 64 - kSmiBits.
+ // If left is negative, then result will not fit into Smi range.
+ // Also deopt in case of negative shift amount.
+ __ tst(left, compiler::Operand(left));
+ __ b(slow_path->entry_label(), MI);
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ __ CompareImmediate(result, compiler::target::kSmiBits);
+ // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+ __ LoadImmediate(result, 0, GE);
+ // Left operand >= 0, shift amount < kSmiBits < 32.
+ __ SmiUntag(TMP, left, LT);
+ __ Lsr(result, TMP, result, LT);
+ __ SmiTag(result, LT);
+ __ Bind(&done);
+ break;
+ }
default:
UNREACHABLE();
}
@@ -4230,7 +4280,7 @@
} else if (op_kind() == Token::kMOD) {
num_temps = 2;
} else if (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR)) {
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
num_temps = 1;
}
LocationSummary* summary = new (zone)
@@ -4262,7 +4312,7 @@
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrSmiConstant(right()));
if (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR)) {
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
summary->set_temp(0, Location::RequiresRegister());
}
// We make use of 3-operand instructions by not requiring result register
@@ -4388,6 +4438,52 @@
__ SmiTag(result);
break;
}
+ case Token::kUSHR: {
+ const intptr_t value = compiler::target::SmiValue(constant);
+ ASSERT((value > 0) && (value < 64));
+ COMPILE_ASSERT(compiler::target::kSmiBits < 32);
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s s xxxxxxxxxxxxx
+ // | | | | | |
+ // 63 32 31 30 kSmiBits-1 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Smi range if and only if
+ // the shift amount >= 64 - kSmiBits.
+ //
+ // If left operand is non-negative, the result always
+ // fits into Smi range.
+ //
+ if (value < (64 - compiler::target::kSmiBits)) {
+ if (deopt != nullptr) {
+ __ CompareImmediate(left, 0);
+ __ b(deopt, LT);
+ } else {
+ // Operation cannot overflow only if left value is always
+ // non-negative.
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (value >= compiler::target::kSmiBits) {
+ __ LoadImmediate(result, 0);
+ } else {
+ __ Lsr(result, left, compiler::Operand(value + kSmiTagSize));
+ __ SmiTag(result);
+ }
+ } else {
+ // Shift amount > 32, and the result is guaranteed to fit into Smi.
+ // Low (Smi) part of the left operand is shifted out.
+ // High part is filled with sign bits.
+ __ Asr(result, left, compiler::Operand(31));
+ __ Lsr(result, result, compiler::Operand(value - 32));
+ __ SmiTag(result);
+ }
+ break;
+ }
default:
UNREACHABLE();
@@ -4516,6 +4612,71 @@
__ SmiTag(result);
break;
}
+ case Token::kUSHR: {
+ compiler::Label done;
+ __ SmiUntag(IP, right);
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s s xxxxxxxxxxxxx
+ // | | | | | |
+ // 63 32 31 30 kSmiBits-1 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Smi range if and only if
+ // the shift amount >= 64 - kSmiBits.
+ //
+ // If left operand is non-negative, the result always
+ // fits into Smi range.
+ //
+ if (!RangeUtils::OnlyLessThanOrEqualTo(
+ right_range(), 64 - compiler::target::kSmiBits - 1)) {
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+ kBitsPerInt64 - 1)) {
+ __ CompareImmediate(IP, kBitsPerInt64);
+ // If shift amount >= 64, then result is 0.
+ __ LoadImmediate(result, 0, GE);
+ __ b(&done, GE);
+ }
+ __ CompareImmediate(IP, 64 - compiler::target::kSmiBits);
+ // Shift amount >= 64 - kSmiBits > 32, but < 64.
+ // Result is guaranteed to fit into Smi range.
+ // Low (Smi) part of the left operand is shifted out.
+ // High part is filled with sign bits.
+ __ sub(IP, IP, compiler::Operand(32), GE);
+ __ Asr(result, left, compiler::Operand(31), GE);
+ __ Lsr(result, result, IP, GE);
+ __ SmiTag(result, GE);
+ __ b(&done, GE);
+ }
+ // Shift amount < 64 - kSmiBits.
+ // If left is negative, then result will not fit into Smi range.
+ // Also deopt in case of negative shift amount.
+ if (deopt != nullptr) {
+ __ tst(left, compiler::Operand(left));
+ __ tst(right, compiler::Operand(right), PL);
+ __ b(deopt, MI);
+ } else {
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+ compiler::target::kSmiBits - 1)) {
+ __ CompareImmediate(IP, compiler::target::kSmiBits);
+ // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+ __ LoadImmediate(result, 0, GE);
+ __ b(&done, GE);
+ }
+ // Left operand >= 0, shift amount < kSmiBits < 32.
+ const Register temp = locs()->temp(0).reg();
+ __ SmiUntag(temp, left);
+ __ Lsr(result, temp, IP);
+ __ SmiTag(result);
+ __ Bind(&done);
+ break;
+ }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -4568,7 +4729,7 @@
// Calculate number of temporaries.
intptr_t num_temps = 0;
if (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR)) {
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
num_temps = 1;
}
LocationSummary* summary = new (zone)
@@ -4576,7 +4737,7 @@
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrSmiConstant(right()));
if (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR)) {
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
summary->set_temp(0, Location::RequiresRegister());
}
// We make use of 3-operand instructions by not requiring result register
@@ -4678,6 +4839,45 @@
compiler::Operand(Utils::Minimum(value, kCountLimit)));
break;
}
+ case Token::kUSHR: {
+ ASSERT((value > 0) && (value < 64));
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s xxxxxxxxxxxxx
+ // | | | | |
+ // 63 32 31 30 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Int32 range if and only if
+ // the shift amount > 32.
+ //
+ if (value <= 32) {
+ if (deopt != nullptr) {
+ __ tst(left, compiler::Operand(left));
+ __ b(deopt, MI);
+ } else {
+ // Operation cannot overflow only if left value is always
+ // non-negative.
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (value == 32) {
+ __ LoadImmediate(result, 0);
+ } else {
+ __ Lsr(result, left, compiler::Operand(value));
+ }
+ } else {
+ // Shift amount > 32.
+ // Low (Int32) part of the left operand is shifted out.
+ // Shift high part which is filled with sign bits.
+ __ Asr(result, left, compiler::Operand(31));
+ __ Lsr(result, result, compiler::Operand(value - 32));
+ }
+ break;
+ }
default:
UNREACHABLE();
@@ -6832,6 +7032,22 @@
}
break;
}
+ case Token::kUSHR: {
+ ASSERT(shift < 64);
+ if (shift < 32) {
+ __ Lsl(out_lo, left_hi, compiler::Operand(32 - shift));
+ __ orr(out_lo, out_lo, compiler::Operand(left_lo, LSR, shift));
+ __ Lsr(out_hi, left_hi, compiler::Operand(shift));
+ } else {
+ if (shift == 32) {
+ __ mov(out_lo, compiler::Operand(left_hi));
+ } else {
+ __ Lsr(out_lo, left_hi, compiler::Operand(shift - 32));
+ }
+ __ mov(out_hi, compiler::Operand(0));
+ }
+ break;
+ }
case Token::kSHL: {
ASSERT(shift < 64);
if (shift < 32) {
@@ -6870,6 +7086,15 @@
__ mov(out_hi, compiler::Operand(left_hi, ASR, right));
break;
}
+ case Token::kUSHR: {
+ __ rsbs(IP, right, compiler::Operand(32));
+ __ sub(IP, right, compiler::Operand(32), MI);
+ __ mov(out_lo, compiler::Operand(left_hi, LSR, IP), MI);
+ __ mov(out_lo, compiler::Operand(left_lo, LSR, right), PL);
+ __ orr(out_lo, out_lo, compiler::Operand(left_hi, LSL, IP), PL);
+ __ mov(out_hi, compiler::Operand(left_hi, LSR, right));
+ break;
+ }
case Token::kSHL: {
__ rsbs(IP, right, compiler::Operand(32));
__ sub(IP, right, compiler::Operand(32), MI);
@@ -6896,6 +7121,7 @@
} else {
switch (op_kind) {
case Token::kSHR:
+ case Token::kUSHR:
__ Lsr(out, left, compiler::Operand(shift));
break;
case Token::kSHL:
@@ -6914,6 +7140,7 @@
Register right) {
switch (op_kind) {
case Token::kSHR:
+ case Token::kUSHR:
__ Lsr(out, left, right);
break;
case Token::kSHL:
@@ -6951,6 +7178,7 @@
compiler::Operand(compiler::target::kBitsPerWord - 1), GE);
__ mov(out_lo, compiler::Operand(out_hi), GE);
break;
+ case Token::kUSHR:
case Token::kSHL: {
__ LoadImmediate(out_lo, 0, GE);
__ LoadImmediate(out_hi, 0, GE);
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index cc4cc66..5f4a51c 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -3600,15 +3600,22 @@
__ b(slow_path->entry_label(), NE); // Overflow.
break;
case Token::kSHR:
+ case Token::kUSHR:
ASSERT(result != left);
ASSERT(result != right);
- __ CompareImmediate(right, static_cast<int64_t>(Smi::New(Smi::kBits)));
- __ b(slow_path->entry_label(), CS);
+ __ CompareImmediate(right, static_cast<int64_t>(Smi::New(kBitsPerInt64)));
+ __ b(slow_path->entry_label(), UNSIGNED_GREATER_EQUAL);
__ SmiUntag(result, right);
__ SmiUntag(TMP, left);
- __ asrv(result, TMP, result);
- __ SmiTag(result);
+ if (op_kind() == Token::kSHR) {
+ __ asrv(result, TMP, result);
+ __ SmiTag(result);
+ } else {
+ ASSERT(op_kind() == Token::kUSHR);
+ __ lsrv(result, TMP, result);
+ __ SmiTagAndBranchIfOverflow(result, slow_path->entry_label());
+ }
break;
default:
UNIMPLEMENTED();
@@ -3770,10 +3777,11 @@
LocationSummary* BinarySmiOpInstr::MakeLocationSummary(Zone* zone,
bool opt) const {
const intptr_t kNumInputs = 2;
- const intptr_t kNumTemps = (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR))
- ? 1
- : 0;
+ const intptr_t kNumTemps =
+ (((op_kind() == Token::kSHL) && can_overflow()) ||
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR))
+ ? 1
+ : 0;
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
if (op_kind() == Token::kTRUNCDIV) {
@@ -3796,7 +3804,7 @@
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrSmiConstant(right()));
if (((op_kind() == Token::kSHL) && can_overflow()) ||
- (op_kind() == Token::kSHR)) {
+ (op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
summary->set_temp(0, Location::RequiresRegister());
}
// We make use of 3-operand instructions by not requiring result register
@@ -3921,6 +3929,22 @@
__ SmiTag(result);
break;
}
+ case Token::kUSHR: {
+ // Lsr operation masks the count to 6 bits, but
+ // unsigned shifts by >= kBitsPerInt64 are eliminated by
+ // BinaryIntegerOpInstr::Canonicalize.
+ const intptr_t kCountLimit = 0x3F;
+ intptr_t value = Smi::Cast(constant).Value();
+ ASSERT((value >= 0) && (value <= kCountLimit));
+ __ SmiUntag(left);
+ __ LsrImmediate(result, left, value);
+ if (deopt != nullptr) {
+ __ SmiTagAndBranchIfOverflow(result, deopt);
+ } else {
+ __ SmiTag(result);
+ }
+ break;
+ }
default:
UNREACHABLE();
break;
@@ -3998,8 +4022,7 @@
case Token::kTRUNCDIV: {
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ CompareRegisters(right, ZR);
- __ b(deopt, EQ);
+ __ cbz(deopt, right);
}
const Register temp = TMP2;
__ SmiUntag(temp, left);
@@ -4022,8 +4045,7 @@
case Token::kMOD: {
if (RangeUtils::CanBeZero(right_range())) {
// Handle divide by zero in runtime.
- __ CompareRegisters(right, ZR);
- __ b(deopt, EQ);
+ __ cbz(deopt, right);
}
const Register temp = TMP2;
__ SmiUntag(temp, left);
@@ -4055,11 +4077,10 @@
}
case Token::kSHR: {
if (CanDeoptimize()) {
- __ CompareRegisters(right, ZR);
- __ b(deopt, LT);
+ __ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
}
__ SmiUntag(TMP, right);
- // sarl operation masks the count to 6 bits.
+ // asrv operation masks the count to 6 bits.
const intptr_t kCountLimit = 0x3F;
if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
__ LoadImmediate(TMP2, kCountLimit);
@@ -4072,6 +4093,32 @@
__ SmiTag(result);
break;
}
+ case Token::kUSHR: {
+ if (CanDeoptimize()) {
+ __ tbnz(deopt, right, compiler::target::kSmiBits + kSmiTagSize);
+ }
+ __ SmiUntag(TMP, right);
+ // lsrv operation masks the count to 6 bits.
+ const intptr_t kCountLimit = 0x3F;
+ COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
+ compiler::Label done;
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
+ __ LoadImmediate(TMP2, kCountLimit);
+ __ CompareRegisters(TMP, TMP2);
+ __ csel(result, ZR, result, GT);
+ __ b(&done, GT);
+ }
+ const Register temp = locs()->temp(0).reg();
+ __ SmiUntag(temp, left);
+ __ lsrv(result, temp, TMP);
+ if (deopt != nullptr) {
+ __ SmiTagAndBranchIfOverflow(result, deopt);
+ } else {
+ __ SmiTag(result);
+ }
+ __ Bind(&done);
+ break;
+ }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -6144,6 +6191,11 @@
Utils::Minimum<int64_t>(shift, kBitsPerWord - 1));
break;
}
+ case Token::kUSHR: {
+ ASSERT(shift < 64);
+ __ LsrImmediate(out, left, shift);
+ break;
+ }
case Token::kSHL: {
ASSERT(shift < 64);
__ LslImmediate(out, left, shift);
@@ -6164,6 +6216,10 @@
__ asrv(out, left, right);
break;
}
+ case Token::kUSHR: {
+ __ lsrv(out, left, right);
+ break;
+ }
case Token::kSHL: {
__ lslv(out, left, right);
break;
@@ -6185,6 +6241,7 @@
} else {
switch (op_kind) {
case Token::kSHR:
+ case Token::kUSHR:
__ LsrImmediate(out, left, shift, compiler::kFourBytes);
break;
case Token::kSHL:
@@ -6203,6 +6260,7 @@
Register right) {
switch (op_kind) {
case Token::kSHR:
+ case Token::kUSHR:
__ lsrvw(out, left, right);
break;
case Token::kSHL:
@@ -6235,6 +6293,7 @@
case Token::kSHR:
__ AsrImmediate(out, left, kBitsPerWord - 1);
break;
+ case Token::kUSHR:
case Token::kSHL:
__ mov(out, ZR);
break;
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index a78e47b..e192ce4 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -3280,7 +3280,7 @@
// Will be used for sign extension and division.
summary->set_temp(0, Location::RegisterLocation(EAX));
return summary;
- } else if (op_kind() == Token::kSHR) {
+ } else if ((op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
const intptr_t kNumTemps = 0;
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3412,6 +3412,52 @@
break;
}
+ case Token::kUSHR: {
+ ASSERT((value > 0) && (value < 64));
+ COMPILE_ASSERT(compiler::target::kSmiBits < 32);
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s s xxxxxxxxxxxxx
+ // | | | | | |
+ // 63 32 31 30 kSmiBits-1 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Smi range if and only if
+ // the shift amount >= 64 - kSmiBits.
+ //
+ // If left operand is non-negative, the result always
+ // fits into Smi range.
+ //
+ if (value < (64 - compiler::target::kSmiBits)) {
+ if (deopt != nullptr) {
+ __ testl(left, left);
+ __ j(LESS, deopt);
+ } else {
+ // Operation cannot overflow only if left value is always
+ // non-negative.
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (value >= compiler::target::kSmiBits) {
+ __ xorl(left, left);
+ } else {
+ __ shrl(left, compiler::Immediate(value + kSmiTagSize));
+ __ SmiTag(left);
+ }
+ } else {
+ // Shift amount > 32, and the result is guaranteed to fit into Smi.
+ // Low (Smi) part of the left operand is shifted out.
+ // High part is filled with sign bits.
+ __ sarl(left, compiler::Immediate(31));
+ __ shrl(left, compiler::Immediate(value - 32));
+ __ SmiTag(left);
+ }
+ break;
+ }
+
default:
UNREACHABLE();
break;
@@ -3534,6 +3580,82 @@
__ SmiTag(left);
break;
}
+ case Token::kUSHR: {
+ compiler::Label done;
+ __ SmiUntag(right);
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s s xxxxxxxxxxxxx
+ // | | | | | |
+ // 63 32 31 30 kSmiBits-1 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Smi range if and only if
+ // the shift amount >= 64 - kSmiBits.
+ //
+ // If left operand is non-negative, the result always
+ // fits into Smi range.
+ //
+ if (!RangeUtils::OnlyLessThanOrEqualTo(
+ right_range(), 64 - compiler::target::kSmiBits - 1)) {
+ __ cmpl(right, compiler::Immediate(64 - compiler::target::kSmiBits));
+ compiler::Label shift_less_34;
+ __ j(LESS, &shift_less_34, compiler::Assembler::kNearJump);
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+ kBitsPerInt64 - 1)) {
+ __ cmpl(right, compiler::Immediate(kBitsPerInt64));
+ compiler::Label shift_less_64;
+ __ j(LESS, &shift_less_64, compiler::Assembler::kNearJump);
+ // Shift amount >= 64. Result is 0.
+ __ xorl(left, left);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(&shift_less_64);
+ }
+ // Shift amount >= 64 - kSmiBits > 32, but < 64.
+ // Result is guaranteed to fit into Smi range.
+ // Low (Smi) part of the left operand is shifted out.
+ // High part is filled with sign bits.
+ ASSERT(right == ECX); // Count must be in ECX
+ __ subl(right, compiler::Immediate(32));
+ __ sarl(left, compiler::Immediate(31));
+ __ shrl(left, right);
+ __ SmiTag(left);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(&shift_less_34);
+ }
+ // Shift amount < 64 - kSmiBits.
+ // If left is negative, then result will not fit into Smi range.
+ // Also deopt in case of negative shift amount.
+ if (deopt != nullptr) {
+ __ testl(left, left);
+ __ j(LESS, deopt);
+ __ testl(right, right);
+ __ j(LESS, deopt);
+ } else {
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(),
+ compiler::target::kSmiBits - 1)) {
+ __ cmpl(right, compiler::Immediate(compiler::target::kSmiBits));
+ compiler::Label shift_less_30;
+ __ j(LESS, &shift_less_30, compiler::Assembler::kNearJump);
+ // Left operand >= 0, shift amount >= kSmiBits. Result is 0.
+ __ xorl(left, left);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(&shift_less_30);
+ }
+ // Left operand >= 0, shift amount < kSmiBits < 32.
+ ASSERT(right == ECX); // Count must be in ECX
+ __ SmiUntag(left);
+ __ shrl(left, right);
+ __ SmiTag(left);
+ __ Bind(&done);
+ break;
+ }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -3562,7 +3684,7 @@
} else if (op_kind() == Token::kMOD) {
UNREACHABLE();
return NULL;
- } else if (op_kind() == Token::kSHR) {
+ } else if ((op_kind() == Token::kSHR) || (op_kind() == Token::kUSHR)) {
const intptr_t kNumTemps = 0;
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3670,6 +3792,46 @@
break;
}
+ case Token::kUSHR: {
+ ASSERT((value > 0) && (value < 64));
+ // 64-bit representation of left operand value:
+ //
+ // ss...sssss s xxxxxxxxxxxxx
+ // | | | | |
+ // 63 32 31 30 0
+ //
+ // Where 's' is a sign bit.
+ //
+ // If left operand is negative (sign bit is set), then
+ // result will fit into Int32 range if and only if
+ // the shift amount > 32.
+ //
+ if (value <= 32) {
+ if (deopt != nullptr) {
+ __ testl(left, left);
+ __ j(LESS, deopt);
+ } else {
+ // Operation cannot overflow only if left value is always
+ // non-negative.
+ ASSERT(!can_overflow());
+ }
+ // At this point left operand is non-negative, so unsigned shift
+ // can't overflow.
+ if (value == 32) {
+ __ xorl(left, left);
+ } else {
+ __ shrl(left, compiler::Immediate(value));
+ }
+ } else {
+ // Shift amount > 32.
+ // Low (Int32) part of the left operand is shifted out.
+ // Shift high part which is filled with sign bits.
+ __ sarl(left, compiler::Immediate(31));
+ __ shrl(left, compiler::Immediate(value - 32));
+ }
+ break;
+ }
+
default:
UNREACHABLE();
break;
@@ -5850,6 +6012,20 @@
}
break;
}
+ case Token::kUSHR: {
+ ASSERT(shift < 64);
+ if (shift > 31) {
+ __ movl(left_lo, left_hi); // Shift by 32.
+ __ xorl(left_hi, left_hi); // Zero extend left hi.
+ if (shift > 32) {
+ __ shrl(left_lo, compiler::Immediate(shift - 32));
+ }
+ } else {
+ __ shrdl(left_lo, left_hi, compiler::Immediate(shift));
+ __ shrl(left_hi, compiler::Immediate(shift));
+ }
+ break;
+ }
case Token::kSHL: {
ASSERT(shift < 64);
if (shift > 31) {
@@ -5892,6 +6068,21 @@
__ sarl(left_lo, ECX); // Shift count: CL % 32.
break;
}
+ case Token::kUSHR: {
+ __ cmpl(ECX, compiler::Immediate(31));
+ __ j(ABOVE, &large_shift);
+
+ __ shrdl(left_lo, left_hi, ECX); // Shift count in CL.
+ __ shrl(left_hi, ECX); // Shift count in CL.
+ __ jmp(&done, compiler::Assembler::kNearJump);
+
+ __ Bind(&large_shift);
+ // No need to subtract 32 from CL, only 5 bits used by sarl.
+ __ movl(left_lo, left_hi); // Shift by 32.
+ __ xorl(left_hi, left_hi); // Zero extend left hi.
+ __ shrl(left_lo, ECX); // Shift count: CL % 32.
+ break;
+ }
case Token::kSHL: {
__ cmpl(ECX, compiler::Immediate(31));
__ j(ABOVE, &large_shift);
@@ -5922,7 +6113,8 @@
__ xorl(left, left);
} else {
switch (op_kind) {
- case Token::kSHR: {
+ case Token::kSHR:
+ case Token::kUSHR: {
__ shrl(left, compiler::Immediate(shift));
break;
}
@@ -5940,7 +6132,8 @@
Token::Kind op_kind,
Register left) {
switch (op_kind) {
- case Token::kSHR: {
+ case Token::kSHR:
+ case Token::kUSHR: {
__ shrl(left, ECX);
break;
}
@@ -5986,6 +6179,7 @@
__ sarl(out_hi, compiler::Immediate(31));
__ movl(out_lo, out_hi);
break;
+ case Token::kUSHR:
case Token::kSHL: {
__ xorl(out_lo, out_lo);
__ xorl(out_hi, out_hi);
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index bd87d93..2616a66 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -3563,7 +3563,8 @@
LocationSummary* CheckedSmiOpInstr::MakeLocationSummary(Zone* zone,
bool opt) const {
- bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR);
+ bool is_shift = (op_kind() == Token::kSHL) || (op_kind() == Token::kSHR) ||
+ (op_kind() == Token::kUSHR);
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = is_shift ? 1 : 0;
LocationSummary* summary = new (zone) LocationSummary(
@@ -3576,6 +3577,7 @@
case Token::kMUL:
case Token::kSHL:
case Token::kSHR:
+ case Token::kUSHR:
summary->set_out(0, Location::RequiresRegister());
break;
case Token::kBIT_OR:
@@ -3686,19 +3688,33 @@
__ movsxd(result, result);
#endif
break;
- case Token::kSHR: {
+ case Token::kSHR:
+ case Token::kUSHR: {
compiler::Label shift_count_ok;
ASSERT(result != right);
ASSERT(locs()->temp(0).reg() == RCX);
- __ cmpq(right, compiler::Immediate(Smi::RawValue(Smi::kBits)));
+ __ cmpq(right, compiler::Immediate(Smi::RawValue(kBitsPerInt64)));
__ j(ABOVE_EQUAL, slow_path->entry_label());
__ movq(RCX, right);
__ SmiUntag(RCX);
__ movq(result, left);
__ SmiUntag(result);
- __ sarq(result, RCX);
- __ SmiTag(result);
+ if (op_kind() == Token::kSHR) {
+ __ sarq(result, RCX);
+ __ SmiTag(result);
+ } else {
+ ASSERT(op_kind() == Token::kUSHR);
+ __ shrq(result, RCX);
+ __ SmiTag(result);
+ __ j(OVERFLOW, slow_path->entry_label());
+#if defined(DART_COMPRESSED_POINTERS)
+ const Register temp = locs()->temp(0).reg();
+ __ movsxd(temp, result);
+ __ cmpq(temp, result);
+ __ j(NOT_EQUAL, slow_path->entry_label());
+#endif // defined(DART_COMPRESSED_POINTERS)
+ }
break;
}
default:
@@ -3874,8 +3890,12 @@
ConstantInstr* right_constant = right()->definition()->AsConstant();
if ((right_constant != NULL) && (op_kind() != Token::kTRUNCDIV) &&
- (op_kind() != Token::kSHL) && (op_kind() != Token::kMUL) &&
- (op_kind() != Token::kMOD) && CanBeImmediate(right_constant->value())) {
+ (op_kind() != Token::kSHL) &&
+#if defined(DART_COMPRESSED_POINTERS)
+ (op_kind() != Token::kUSHR) &&
+#endif
+ (op_kind() != Token::kMUL) && (op_kind() != Token::kMOD) &&
+ CanBeImmediate(right_constant->value())) {
const intptr_t kNumTemps = 0;
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3915,7 +3935,11 @@
// Will be used for sign extension and division.
summary->set_temp(0, Location::RegisterLocation(RAX));
return summary;
- } else if (op_kind() == Token::kSHR) {
+ } else if ((op_kind() == Token::kSHR)
+#if !defined(DART_COMPRESSED_POINTERS)
+ || (op_kind() == Token::kUSHR)
+#endif // !defined(DART_COMPRESSED_POINTERS)
+ ) {
const intptr_t kNumTemps = 0;
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
@@ -3923,6 +3947,22 @@
summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
summary->set_out(0, Location::SameAsFirstInput());
return summary;
+#if defined(DART_COMPRESSED_POINTERS)
+ } else if (op_kind() == Token::kUSHR) {
+ const intptr_t kNumTemps = 1;
+ LocationSummary* summary = new (zone)
+ LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ if ((right_constant != nullptr) &&
+ CanBeImmediate(right_constant->value())) {
+ summary->set_in(1, Location::Constant(right_constant));
+ } else {
+ summary->set_in(1, LocationFixedRegisterOrSmiConstant(right(), RCX));
+ }
+ summary->set_out(0, Location::SameAsFirstInput());
+ summary->set_temp(0, Location::RequiresRegister());
+ return summary;
+#endif // defined(DART_COMPRESSED_POINTERS)
} else if (op_kind() == Token::kSHL) {
// Shift-by-1 overflow checking can use flags, otherwise we need a temp.
const bool shiftBy1 =
@@ -4082,6 +4122,28 @@
break;
}
+ case Token::kUSHR: {
+ // shrq operation masks the count to 6 bits, but
+ // unsigned shifts by >= kBitsPerInt64 are eliminated by
+ // BinaryIntegerOpInstr::Canonicalize.
+ const intptr_t kCountLimit = 0x3F;
+ const intptr_t value = Smi::Cast(constant).Value();
+ ASSERT((value >= 0) && (value <= kCountLimit));
+ __ SmiUntag(left);
+ __ shrq(left, compiler::Immediate(value));
+ __ SmiTag(left);
+ if (deopt != nullptr) {
+ __ j(OVERFLOW, deopt);
+#if defined(DART_COMPRESSED_POINTERS)
+ const Register temp = locs()->temp(0).reg();
+ __ movsxd(temp, left);
+ __ cmpq(temp, left);
+ __ j(NOT_EQUAL, deopt);
+#endif // defined(DART_COMPRESSED_POINTERS)
+ }
+ break;
+ }
+
default:
UNREACHABLE();
break;
@@ -4398,6 +4460,40 @@
__ SmiTag(left);
break;
}
+ case Token::kUSHR: {
+ if (deopt != nullptr) {
+ __ CompareImmediate(right, compiler::Immediate(0));
+ __ j(LESS, deopt);
+ }
+ __ SmiUntag(right);
+ // shrq operation masks the count to 6 bits.
+ const intptr_t kCountLimit = 0x3F;
+ COMPILE_ASSERT(kCountLimit + 1 == kBitsPerInt64);
+ compiler::Label done;
+ if (!RangeUtils::OnlyLessThanOrEqualTo(right_range(), kCountLimit)) {
+ __ CompareImmediate(right, compiler::Immediate(kCountLimit));
+ compiler::Label count_ok;
+ __ j(LESS_EQUAL, &count_ok, compiler::Assembler::kNearJump);
+ __ xorq(left, left);
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(&count_ok);
+ }
+ ASSERT(right == RCX); // Count must be in RCX
+ __ SmiUntag(left);
+ __ shrq(left, right);
+ __ SmiTag(left);
+ if (deopt != nullptr) {
+ __ j(OVERFLOW, deopt);
+#if defined(DART_COMPRESSED_POINTERS)
+ const Register temp = locs()->temp(0).reg();
+ __ movsxd(temp, left);
+ __ cmpq(temp, left);
+ __ j(NOT_EQUAL, deopt);
+#endif // defined(DART_COMPRESSED_POINTERS)
+ }
+ __ Bind(&done);
+ break;
+ }
case Token::kDIV: {
// Dispatches to 'Double./'.
// TODO(srdjan): Implement as conversion to double and double division.
@@ -6660,6 +6756,10 @@
__ sarq(left, compiler::Immediate(
Utils::Minimum<int64_t>(shift, kBitsPerWord - 1)));
break;
+ case Token::kUSHR:
+ ASSERT(shift < 64);
+ __ shrq(left, compiler::Immediate(shift));
+ break;
case Token::kSHL: {
ASSERT(shift < 64);
__ shlq(left, compiler::Immediate(shift));
@@ -6678,6 +6778,10 @@
__ sarq(left, RCX);
break;
}
+ case Token::kUSHR: {
+ __ shrq(left, RCX);
+ break;
+ }
case Token::kSHL: {
__ shlq(left, RCX);
break;
@@ -6697,7 +6801,8 @@
__ xorl(left, left);
} else {
switch (op_kind) {
- case Token::kSHR: {
+ case Token::kSHR:
+ case Token::kUSHR: {
__ shrl(left, compiler::Immediate(shift));
break;
}
@@ -6715,7 +6820,8 @@
Token::Kind op_kind,
Register left) {
switch (op_kind) {
- case Token::kSHR: {
+ case Token::kSHR:
+ case Token::kUSHR: {
__ shrl(left, RCX);
break;
}
@@ -6749,6 +6855,7 @@
case Token::kSHR:
__ sarq(out, compiler::Immediate(kBitsPerInt64 - 1));
break;
+ case Token::kUSHR:
case Token::kSHL:
__ xorq(out, out);
break;
diff --git a/runtime/vm/compiler/backend/range_analysis.cc b/runtime/vm/compiler/backend/range_analysis.cc
index febd184..5f0ebc3 100644
--- a/runtime/vm/compiler/backend/range_analysis.cc
+++ b/runtime/vm/compiler/backend/range_analysis.cc
@@ -1580,7 +1580,7 @@
// because we need the high bits.
if (def->IsShiftInt64Op() || def->IsSpeculativeShiftInt64Op()) {
ShiftIntegerOpInstr* op = def->AsShiftIntegerOp();
- if (op->op_kind() == Token::kSHR) {
+ if ((op->op_kind() == Token::kSHR) || (op->op_kind() == Token::kUSHR)) {
Definition* shift_input = op->left()->definition();
ASSERT(shift_input != NULL);
Range* range = shift_input->range();
@@ -2186,6 +2186,75 @@
left_max, left_max.ConstantValue() > 0 ? right_min : right_max);
}
+static void ConvertRangeToUnsigned(int64_t a,
+ int64_t b,
+ uint64_t* ua,
+ uint64_t* ub) {
+ ASSERT(a <= b);
+ if ((a < 0) && (b >= 0)) {
+ // Range contains -1 and 0 and wraps-around as unsigned.
+ *ua = 0;
+ *ub = kMaxUint64;
+ } else {
+ // Range is fully in the negative or non-negative part
+ // and doesn't wrap-around if interpreted as unsigned.
+ *ua = static_cast<uint64_t>(a);
+ *ub = static_cast<uint64_t>(b);
+ }
+}
+
+static void ConvertRangeToSigned(uint64_t a,
+ uint64_t b,
+ int64_t* sa,
+ int64_t* sb) {
+ ASSERT(a <= b);
+ if ((a <= static_cast<uint64_t>(kMaxInt64)) &&
+ (b >= static_cast<uint64_t>(kMinInt64))) {
+ // Range contains kMinInt64 and kMaxInt64 and wraps-around as signed.
+ *sa = kMinInt64;
+ *sb = kMaxInt64;
+ } else {
+ // Range is fully in the negative or non-negative part
+ // and doesn't wrap-around if interpreted as signed.
+ *sa = static_cast<int64_t>(a);
+ *sb = static_cast<int64_t>(b);
+ }
+}
+
+void Range::Ushr(const Range* left,
+ const Range* right,
+ RangeBoundary* result_min,
+ RangeBoundary* result_max) {
+ const int64_t left_max = Range::ConstantMax(left).ConstantValue();
+ const int64_t left_min = Range::ConstantMin(left).ConstantValue();
+ // A negative shift count always deoptimizes (and throws), so the minimum
+ // shift count is zero.
+ const int64_t right_max = Utils::Maximum(
+ Range::ConstantMax(right).ConstantValue(), static_cast<int64_t>(0));
+ const int64_t right_min = Utils::Maximum(
+ Range::ConstantMin(right).ConstantValue(), static_cast<int64_t>(0));
+
+ uint64_t unsigned_left_min, unsigned_left_max;
+ ConvertRangeToUnsigned(left_min, left_max, &unsigned_left_min,
+ &unsigned_left_max);
+
+ const uint64_t unsigned_result_min =
+ (right_max >= kBitsPerInt64)
+ ? 0
+ : unsigned_left_min >> static_cast<uint64_t>(right_max);
+ const uint64_t unsigned_result_max =
+ (right_min >= kBitsPerInt64)
+ ? 0
+ : unsigned_left_max >> static_cast<uint64_t>(right_min);
+
+ int64_t signed_result_min, signed_result_max;
+ ConvertRangeToSigned(unsigned_result_min, unsigned_result_max,
+ &signed_result_min, &signed_result_max);
+
+ *result_min = RangeBoundary(signed_result_min);
+ *result_max = RangeBoundary(signed_result_max);
+}
+
void Range::And(const Range* left_range,
const Range* right_range,
RangeBoundary* result_min,
@@ -2464,6 +2533,10 @@
Range::Shr(left_range, right_range, &min, &max);
break;
+ case Token::kUSHR:
+ Range::Ushr(left_range, right_range, &min, &max);
+ break;
+
case Token::kBIT_AND:
Range::And(left_range, right_range, &min, &max);
break;
@@ -2902,8 +2975,9 @@
void BinarySmiOpInstr::InferRange(RangeAnalysis* analysis, Range* range) {
const Range* right_smi_range = analysis->GetSmiRange(right());
// TODO(vegorov) completely remove this once GetSmiRange is eliminated.
- if (op_kind() == Token::kSHR || op_kind() == Token::kSHL ||
- op_kind() == Token::kMOD || op_kind() == Token::kTRUNCDIV) {
+ if (op_kind() == Token::kSHL || op_kind() == Token::kSHR ||
+ op_kind() == Token::kUSHR || op_kind() == Token::kMOD ||
+ op_kind() == Token::kTRUNCDIV) {
CacheRange(&right_range_, right_smi_range,
RangeBoundary::kRangeBoundarySmi);
}
diff --git a/runtime/vm/compiler/backend/range_analysis.h b/runtime/vm/compiler/backend/range_analysis.h
index 0f8cc42..9a8971e 100644
--- a/runtime/vm/compiler/backend/range_analysis.h
+++ b/runtime/vm/compiler/backend/range_analysis.h
@@ -476,6 +476,11 @@
RangeBoundary* min,
RangeBoundary* max);
+ static void Ushr(const Range* left_range,
+ const Range* right_range,
+ RangeBoundary* min,
+ RangeBoundary* max);
+
static void Shl(const Range* left_range,
const Range* right_range,
RangeBoundary* min,
diff --git a/runtime/vm/compiler/call_specializer.cc b/runtime/vm/compiler/call_specializer.cc
index f81fd02..4791955 100644
--- a/runtime/vm/compiler/call_specializer.cc
+++ b/runtime/vm/compiler/call_specializer.cc
@@ -571,10 +571,11 @@
return false;
}
break;
- case Token::kSHR:
case Token::kSHL:
+ case Token::kSHR:
+ case Token::kUSHR:
if (binary_feedback.OperandsAre(kSmiCid)) {
- // Left shift may overflow from smi into mint or big ints.
+ // Left shift may overflow from smi into mint.
// Don't generate smi code if the IC data is marked because
// of an overflow.
if (call->ic_data()->HasDeoptReason(ICData::kDeoptBinaryInt64Op)) {
@@ -638,7 +639,8 @@
ReplaceCall(call, double_bin_op);
} else if (operands_type == kMintCid) {
if (!FlowGraphCompiler::SupportsUnboxedInt64()) return false;
- if ((op_kind == Token::kSHR) || (op_kind == Token::kSHL)) {
+ if ((op_kind == Token::kSHL) || (op_kind == Token::kSHR) ||
+ (op_kind == Token::kUSHR)) {
SpeculativeShiftInt64OpInstr* shift_op = new (Z)
SpeculativeShiftInt64OpInstr(op_kind, new (Z) Value(left),
new (Z) Value(right), call->deopt_id());
diff --git a/runtime/vm/compiler/method_recognizer.cc b/runtime/vm/compiler/method_recognizer.cc
index 6a6bd41..873f9e0 100644
--- a/runtime/vm/compiler/method_recognizer.cc
+++ b/runtime/vm/compiler/method_recognizer.cc
@@ -315,6 +315,8 @@
return Token::kSHL;
} else if (name.ptr() == Symbols::RightShiftOperator().ptr()) {
return Token::kSHR;
+ } else if (name.ptr() == Symbols::UnsignedRightShiftOperator().ptr()) {
+ return Token::kUSHR;
} else if (name.ptr() == Symbols::Tilde().ptr()) {
return Token::kBIT_NOT;
} else if (name.ptr() == Symbols::UnaryMinus().ptr()) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index bb608ff..1d8c7ac 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -5952,6 +5952,21 @@
if (type.IsNull() || type.IsNullTypeRef()) {
return 0; // Do not cache hash, since it will still change.
}
+ if (type.IsTypeRef()) {
+ // Unwrapping the TypeRef here cannot lead to infinite recursion, because
+ // traversal during hash computation stops at the TypeRef. Indeed,
+ // unwrapping the TypeRef does not always remove it completely, but may
+ // only rotate the cycle. The same TypeRef can be encountered when calling
+ // type.Hash() below after traversing the whole cycle. The class id of the
+ // referenced type is used and the traversal stops.
+ // By dereferencing the TypeRef, we maximize the information reflected by
+ // the hash value. Two equal vectors may have some of their type arguments
+ // 'oriented' differently, i.e. pointing to identical (TypeRef containing)
+ // cyclic type graphs, but to two different nodes in the cycle, thereby
+ // breaking the hash computation earlier for one vector and yielding two
+ // different hash values for identical type graphs.
+ type = TypeRef::Cast(type).type();
+ }
result = CombineHashes(result, type.Hash());
}
result = FinalizeHash(result, kHashBits);
@@ -20515,6 +20530,10 @@
// Only include hashes of type arguments corresponding to type parameters.
// This prevents obtaining different hashes depending on the location of
// TypeRefs in the super class type argument vector.
+ // Note that TypeRefs can also appear as type arguments corresponding to
+ // type parameters, typically after an instantiation at runtime.
+ // These are dealt with in TypeArguments::HashForRange, which is also called
+ // to compute the hash of a full standalone TypeArguments.
const TypeArguments& type_args = TypeArguments::Handle(arguments());
const Class& cls = Class::Handle(type_class());
const intptr_t num_type_params = cls.NumTypeParameters();
@@ -21842,6 +21861,9 @@
return Integer::New(Utils::ShiftLeftWithTruncation(a, b), space);
case Token::kSHR:
return Integer::New(a >> Utils::Minimum<int64_t>(b, Mint::kBits), space);
+ case Token::kUSHR:
+ return Integer::New(
+ (b >= kBitsPerInt64) ? 0 : static_cast<uint64_t>(a) >> b, space);
default:
UNIMPLEMENTED();
return Integer::null();
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 631c7a8..1646c69 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -288,6 +288,7 @@
V(Uint8ClampedList, "Uint8ClampedList") \
V(Uint8List, "Uint8List") \
V(UnaryMinus, "unary-") \
+ V(UnsignedRightShiftOperator, ">>>") \
V(UnhandledException, "UnhandledException") \
V(UnlinkedCall, "UnlinkedCall") \
V(UnsafeCast, "unsafeCast") \
diff --git a/runtime/vm/token.cc b/runtime/vm/token.cc
index 3038486..5208384 100644
--- a/runtime/vm/token.cc
+++ b/runtime/vm/token.cc
@@ -55,6 +55,7 @@
case Token::kBIT_AND:
case Token::kSHL:
case Token::kSHR:
+ case Token::kUSHR:
return true;
default:
return false;
@@ -72,6 +73,7 @@
case Token::kBIT_AND:
case Token::kSHL:
case Token::kSHR:
+ case Token::kUSHR:
return true;
default:
return false;
diff --git a/runtime/vm/token.h b/runtime/vm/token.h
index 844f9b9..bae0743 100644
--- a/runtime/vm/token.h
+++ b/runtime/vm/token.h
@@ -14,7 +14,7 @@
//
// 14 multiplicative * / ~/ %
// 13 additive + -
-// 12 shift << >>
+// 12 shift << >> >>>
// 11 bitwise and &
// 10 bitwise xor ^
// 9 bitwise or |
@@ -24,7 +24,7 @@
// 5 logical or ||
// 4 null check ??
// 3 conditional ?
-// 2 assignment = *= /= ~/= %= += -= <<= >>= &= ^= |= ??=
+// 2 assignment = *= /= ~/= %= += -= <<= >>= >>>= &= ^= |= ??=
// 1 comma ,
// Token definitions.
@@ -57,6 +57,7 @@
TOK(kASSIGN_AND, "&=", 2, kNoAttribute) \
TOK(kASSIGN_SHL, "<<=", 2, kNoAttribute) \
TOK(kASSIGN_SHR, ">>=", 2, kNoAttribute) \
+ TOK(kASSIGN_USHR, ">>>=", 2, kNoAttribute) \
TOK(kASSIGN_ADD, "+=", 2, kNoAttribute) \
TOK(kASSIGN_SUB, "-=", 2, kNoAttribute) \
TOK(kASSIGN_MUL, "*=", 2, kNoAttribute) \
@@ -79,6 +80,7 @@
/* Shift operators. */ \
TOK(kSHL, "<<", 12, kNoAttribute) \
TOK(kSHR, ">>", 12, kNoAttribute) \
+ TOK(kUSHR, ">>>", 12, kNoAttribute) \
\
/* Additive operators. */ \
TOK(kADD, "+", 13, kNoAttribute) \
@@ -285,8 +287,8 @@
static bool CanBeOverloaded(Kind tok) {
ASSERT(tok < kNumTokens);
return IsRelationalOperator(tok) || (tok == kEQ) ||
- (tok >= kADD && tok <= kMOD) || // Arithmetic operations.
- (tok >= kBIT_OR && tok <= kSHR) || // Bit operations.
+ (tok >= kADD && tok <= kMOD) || // Arithmetic operations.
+ (tok >= kBIT_OR && tok <= kUSHR) || // Bit operations.
(tok == kINDEX) || (tok == kASSIGN_INDEX);
}
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index fb9e7a9..339d9a7 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -844,7 +844,10 @@
}
connecting.clear();
addressesSubscription.cancel();
- result.complete(socket);
+ if (!result.isCompleted) {
+ // Might be already completed via onCancel
+ result.complete(socket);
+ }
}, error: (e, st) {
connecting.remove(socket);
socket.close();
@@ -864,6 +867,7 @@
s.setHandlers();
s.setListening(read: false, write: false);
}
+ addressesSubscription.cancel();
connecting.clear();
if (!result.isCompleted) {
error ??= createError(
diff --git a/sdk/lib/_internal/vm/lib/integers.dart b/sdk/lib/_internal/vm/lib/integers.dart
index f53e439..aefb957 100644
--- a/sdk/lib/_internal/vm/lib/integers.dart
+++ b/sdk/lib/_internal/vm/lib/integers.dart
@@ -80,6 +80,8 @@
@pragma("vm:non-nullable-result-type")
int _shrFromInteger(int other) native "Integer_shrFromInteger";
@pragma("vm:non-nullable-result-type")
+ int _ushrFromInteger(int other) native "Integer_ushrFromInteger";
+ @pragma("vm:non-nullable-result-type")
int _shlFromInteger(int other) native "Integer_shlFromInteger";
@pragma("vm:recognized", "asm-intrinsic")
@pragma("vm:non-nullable-result-type")
@@ -106,8 +108,7 @@
@pragma("vm:never-inline")
int operator >>(int other) => other._shrFromInteger(this);
@pragma("vm:never-inline")
- int operator >>>(int other) =>
- throw UnimplementedError('int.>>> is not implemented yet');
+ int operator >>>(int other) => other._ushrFromInteger(this);
@pragma("vm:recognized", "asm-intrinsic")
@pragma("vm:non-nullable-result-type")
@pragma("vm:never-inline")
diff --git a/sdk/lib/_internal/vm/lib/integers_patch.dart b/sdk/lib/_internal/vm/lib/integers_patch.dart
index fa9cf80..8848974 100644
--- a/sdk/lib/_internal/vm/lib/integers_patch.dart
+++ b/sdk/lib/_internal/vm/lib/integers_patch.dart
@@ -15,8 +15,9 @@
int _bitAndFromInteger(int other);
int _bitOrFromInteger(int other);
int _bitXorFromInteger(int other);
- int _shrFromInteger(int other);
int _shlFromInteger(int other);
+ int _shrFromInteger(int other);
+ int _ushrFromInteger(int other);
static int? _tryParseSmi(String str, int first, int last) {
assert(first <= last);
diff --git a/tests/corelib/unsigned_shift_test.dart b/tests/corelib/unsigned_shift_test.dart
index 457bc7c..fd52016 100644
--- a/tests/corelib/unsigned_shift_test.dart
+++ b/tests/corelib/unsigned_shift_test.dart
@@ -55,25 +55,21 @@
}
}
-int testConstantShifts() {
+void testConstantShifts() {
const c = C();
// >>> is a constant operation on integers.
const c1 = 2 >>> 1;
const c2 = (1 >>> 0) >>> 0;
- // >>> is a potentially constant operation independent of type.
- // The type must still type-check.
- const c3 = false ? 1 : c >>> c;
+ const c3 = 1 >>> 65;
- // It's an error if it doesn't type-check.
- const c4 = true || c >>> c; //# 02: compile-time error
- const c5 = true || "string" >>> 1; //# 03: compile-time error
-
- // Or if the shift isn't on integers and it is evaluated.
- const c6 = c >>> c; //# 04: compile-time error
+ // >>> is a non-constant operation on other types.
+ const c4 = false ? 1 : c >>> c; //# 02: compile-time error
+ const c5 = true || c >>> c; //# 03: compile-time error
+ const c6 = true || "string" >>> 1; //# 04: compile-time error
+ const c7 = c >>> c; //# 05: compile-time error
// Or if shifting throws
- const c7 = 1 >>> -1; //# 05: compile-time error
- const c8 = 1 >>> 65; //# 06: compile-time error
+ const c8 = 1 >>> -1; //# 06: compile-time error
Expect.isNotNull(c1 + c2 + c3); // Avoid "unused variable" warnings.
}
@@ -85,12 +81,7 @@
var title = "0x${value.toRadixString(16)} >>> $shift$jsFlag";
if (shift < 0) {
// No platform allows shifting a negative.
- Expect.throws(() => value >>> shift, "$title: shift < 0");
- return;
- }
- if (!isJSBitOps && shift > 64) {
- // Native 64-bit integers do not allow shifts above 64.
- Expect.throws(() => value >>> shift, "$title: shift > 64");
+ Expect.throwsArgumentError(() => value >>> shift, "$title: shift < 0");
return;
}
var expected;
@@ -98,7 +89,9 @@
// TODO: Check that this is the desired behavior for JS >>>.
expected = value.toUnsigned(32) >> shift;
} else if (value < 0) {
- if (shift > 0) {
+ if (shift >= 64) {
+ expected = 0;
+ } else if (shift > 0) {
expected = (value >> shift).toUnsigned(64 - shift);
} else {
expected = value;
diff --git a/tests/corelib_2/unsigned_shift_test.dart b/tests/corelib_2/unsigned_shift_test.dart
index 457bc7c..fd52016 100644
--- a/tests/corelib_2/unsigned_shift_test.dart
+++ b/tests/corelib_2/unsigned_shift_test.dart
@@ -55,25 +55,21 @@
}
}
-int testConstantShifts() {
+void testConstantShifts() {
const c = C();
// >>> is a constant operation on integers.
const c1 = 2 >>> 1;
const c2 = (1 >>> 0) >>> 0;
- // >>> is a potentially constant operation independent of type.
- // The type must still type-check.
- const c3 = false ? 1 : c >>> c;
+ const c3 = 1 >>> 65;
- // It's an error if it doesn't type-check.
- const c4 = true || c >>> c; //# 02: compile-time error
- const c5 = true || "string" >>> 1; //# 03: compile-time error
-
- // Or if the shift isn't on integers and it is evaluated.
- const c6 = c >>> c; //# 04: compile-time error
+ // >>> is a non-constant operation on other types.
+ const c4 = false ? 1 : c >>> c; //# 02: compile-time error
+ const c5 = true || c >>> c; //# 03: compile-time error
+ const c6 = true || "string" >>> 1; //# 04: compile-time error
+ const c7 = c >>> c; //# 05: compile-time error
// Or if shifting throws
- const c7 = 1 >>> -1; //# 05: compile-time error
- const c8 = 1 >>> 65; //# 06: compile-time error
+ const c8 = 1 >>> -1; //# 06: compile-time error
Expect.isNotNull(c1 + c2 + c3); // Avoid "unused variable" warnings.
}
@@ -85,12 +81,7 @@
var title = "0x${value.toRadixString(16)} >>> $shift$jsFlag";
if (shift < 0) {
// No platform allows shifting a negative.
- Expect.throws(() => value >>> shift, "$title: shift < 0");
- return;
- }
- if (!isJSBitOps && shift > 64) {
- // Native 64-bit integers do not allow shifts above 64.
- Expect.throws(() => value >>> shift, "$title: shift > 64");
+ Expect.throwsArgumentError(() => value >>> shift, "$title: shift < 0");
return;
}
var expected;
@@ -98,7 +89,9 @@
// TODO: Check that this is the desired behavior for JS >>>.
expected = value.toUnsigned(32) >> shift;
} else if (value < 0) {
- if (shift > 0) {
+ if (shift >= 64) {
+ expected = 0;
+ } else if (shift > 0) {
expected = (value >> shift).toUnsigned(64 - shift);
} else {
expected = value;
diff --git a/tests/language/regress/regress45060_test.dart b/tests/language/regress/regress45060_test.dart
new file mode 100644
index 0000000..1681580
--- /dev/null
+++ b/tests/language/regress/regress45060_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, 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.
+
+import "package:expect/expect.dart";
+
+void main() {
+ var executor = new Executor();
+ new Command1().execute(executor);
+ executor.execute<Command1>(new Command1());
+}
+
+class Command1 extends CommandBase<Command1> {}
+
+abstract class Command {}
+
+abstract class CommandBase<TSelf extends CommandBase<TSelf>> extends Command {
+ void execute(Executor e) {
+ TSelf self = this as TSelf;
+ e.execute<TSelf>(self);
+ }
+}
+
+abstract class CommandHandler<T extends Command> {
+ void handle(T command);
+}
+
+class Executor {
+ void execute<T extends Command>(T action) {
+ testTypeEquality<CommandHandler<T>, CommandHandler<Command1>>();
+ }
+}
+
+void testTypeEquality<T1, T2>() {
+ Expect.equals(T1.hashCode, T2.hashCode);
+ Expect.equals(T1, T2);
+ Expect.identical(T1, T2);
+}
diff --git a/tests/language_2/regress/regress45060_test.dart b/tests/language_2/regress/regress45060_test.dart
new file mode 100644
index 0000000..1681580
--- /dev/null
+++ b/tests/language_2/regress/regress45060_test.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2021, 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.
+
+import "package:expect/expect.dart";
+
+void main() {
+ var executor = new Executor();
+ new Command1().execute(executor);
+ executor.execute<Command1>(new Command1());
+}
+
+class Command1 extends CommandBase<Command1> {}
+
+abstract class Command {}
+
+abstract class CommandBase<TSelf extends CommandBase<TSelf>> extends Command {
+ void execute(Executor e) {
+ TSelf self = this as TSelf;
+ e.execute<TSelf>(self);
+ }
+}
+
+abstract class CommandHandler<T extends Command> {
+ void handle(T command);
+}
+
+class Executor {
+ void execute<T extends Command>(T action) {
+ testTypeEquality<CommandHandler<T>, CommandHandler<Command1>>();
+ }
+}
+
+void testTypeEquality<T1, T2>() {
+ Expect.equals(T1.hashCode, T2.hashCode);
+ Expect.equals(T1, T2);
+ Expect.identical(T1, T2);
+}
diff --git a/tests/lib/html/js_util_test.dart b/tests/lib/html/js_util_test.dart
index f06e729..a3d5092 100644
--- a/tests/lib/html/js_util_test.dart
+++ b/tests/lib/html/js_util_test.dart
@@ -2,75 +2,19 @@
// 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.
-@JS()
-library js_native_test;
+// Tests the functionality of js_util with HTML objects.
-import 'dart:async';
+@JS()
+library js_util_test;
+
import 'dart:html';
-import 'dart:typed_data' show ByteBuffer, Int32List;
-import 'dart:indexed_db' show IdbFactory, KeyRange;
import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util;
import 'package:expect/minitest.dart';
-import 'package:async_helper/async_helper.dart';
-
-_injectJs() {
- final script = new ScriptElement();
- script.type = 'text/javascript';
- script.innerHtml = r"""
-var x = 42;
-
-var _x = 123;
-
-var myArray = ["value1"];
-
-function returnThis() {
- return this;
-}
-
-function getTypeOf(o) {
- return typeof(o);
-}
-
-function Foo(a) {
- this.a = a;
-}
-
-Foo.b = 38;
-
-Foo.prototype.bar = function() {
- return this.a;
-}
-Foo.prototype.toString = function() {
- return "I'm a Foo a=" + this.a;
-}
-
-var container = new Object();
-container.Foo = Foo;
-
-function checkMap(m, key, value) {
- if (m.hasOwnProperty(key))
- return m[key] == value;
- else
- return false;
-}
-
-var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
-var resolvedPromise = new Promise(resolve => resolve('resolved'));
-function getResolvedPromise() {
- return resolvedPromise;
-}
-
-""";
- document.body!.append(script);
-}
@JS()
-external bool checkMap(m, String, value);
-
-@JS('JSON.stringify')
-external String stringify(o);
+external void eval(String code);
@JS('Node')
external get JSNodeType;
@@ -86,302 +30,61 @@
@JS()
class Foo {
- external Foo(num a);
-
- external num get a;
- external num bar();
-}
-
-@JS('Foo')
-external get JSFooType;
-
-@JS()
-@anonymous
-class ExampleTypedLiteral {
- external factory ExampleTypedLiteral({a, b, JS$_c, JS$class});
-
- external get a;
- external get b;
- external get JS$_c;
- external set JS$_c(v);
- // Identical to JS$_c but only accessible within the library.
- external get _c;
- external get JS$class;
- external set JS$class(v);
-}
-
-@JS()
-abstract class Promise<T> {}
-
-@JS()
-external Promise get resolvedPromise;
-
-@JS()
-external Promise get rejectedPromise;
-
-@JS()
-external Promise getResolvedPromise();
-
-@JS("Object.prototype.hasOwnProperty")
-external get _hasOwnProperty;
-
-bool hasOwnProperty(o, String name) {
- return js_util.callMethod(_hasOwnProperty, 'call', [o, name]);
+ external Foo();
}
main() {
- _injectJs();
+ eval(r""" function Foo() {} """);
- group('js_util.jsify()', () {
- test('convert a List', () {
- final list = [1, 2, 3, 4, 5, 6, 7, 8];
- var array = js_util.jsify(list);
- expect(array is List, isTrue);
- expect(identical(array, list), isFalse);
- expect(array.length, equals(list.length));
- for (var i = 0; i < list.length; i++) {
- expect(array[i], equals(list[i]));
- }
- });
-
- test('convert an Iterable', () {
- final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
- var array = js_util.jsify(set);
- expect(array is List, isTrue);
- expect(array.length, equals(set.length));
- for (var i = 0; i < array.length; i++) {
- expect(set.contains(array[i]), isTrue);
- }
- });
-
- test('convert a Map', () {
- var map = {'a': 1, 'b': 2, 'c': 3};
- var jsMap = js_util.jsify(map);
- expect(jsMap is! List, isTrue);
- for (var key in map.keys) {
- expect(checkMap(jsMap, key, map[key]), isTrue);
- }
- });
-
- test('deep convert a complex object', () {
- dynamic object = {
- 'a': [
- 1,
- [2, 3]
- ],
- 'b': {'c': 3, 'd': new Foo(42)},
- 'e': null
- };
- var jsObject = js_util.jsify(object);
- expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
- expect(
- js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
- expect(
- js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
- expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'c'),
- equals(object['b']['c']));
- expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
- equals(object['b']['d']));
- expect(
- js_util.callMethod(
- js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
- 'bar', []),
- equals(42));
- expect(js_util.getProperty(jsObject, 'e'), isNull);
- });
-
- test('throws if object is not a Map or Iterable', () {
- expect(() => js_util.jsify('a'), throwsArgumentError);
- });
+ test('hasProperty', () {
+ var textElement = new Text('foo');
+ expect(js_util.hasProperty(textElement, 'data'), isTrue);
});
- group('js_util.newObject', () {
- test('create', () {
- expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
- });
-
- test('callMethod', () {
- var o = js_util.newObject();
- expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
- expect(stringify(o), equals('{}'));
- });
-
- test('properties', () {
- var o = js_util.newObject();
- expect(js_util.hasProperty(o, 'foo bar'), isFalse);
- expect(js_util.hasProperty(o, 'toString'), isTrue);
- expect(hasOwnProperty(o, 'toString'), isFalse);
- expect(hasOwnProperty(o, 'foo bar'), isFalse);
- js_util.setProperty(o, 'foo bar', 42);
- expect(hasOwnProperty(o, 'foo bar'), isTrue);
- expect(js_util.getProperty(o, 'foo bar'), equals(42));
- expect(js_util.hasProperty(o, 'foo bar'), isTrue);
- expect(stringify(o), equals('{"foo bar":42}'));
- });
+ test('getProperty', () {
+ var textElement = new Text('foo');
+ expect(js_util.getProperty(textElement, 'data'), equals('foo'));
});
- group('hasProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.hasProperty(f, 'a'), isTrue);
- expect(js_util.hasProperty(f, 'toString'), isTrue);
- js_util.setProperty(f, '__proto__', null);
- expect(js_util.hasProperty(f, 'toString'), isFalse);
- });
- test('typed literal', () {
- var l =
- new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: null, JS$class: true);
- expect(js_util.hasProperty(l, 'a'), isTrue);
- expect(js_util.hasProperty(l, 'b'), isTrue);
- expect(js_util.hasProperty(l, '_c'), isTrue);
- expect(l.JS$_c, isNull);
- expect(js_util.hasProperty(l, 'class'), isTrue);
- // JS$_c escapes to _c so the property JS$_c will not exist on the object.
- expect(js_util.hasProperty(l, r'JS$_c'), isFalse);
- expect(js_util.hasProperty(l, r'JS$class'), isFalse);
- expect(l.JS$class, isTrue);
-
- l = new ExampleTypedLiteral(a: null);
- expect(js_util.hasProperty(l, 'a'), isTrue);
- expect(js_util.hasProperty(l, 'b'), isFalse);
- expect(js_util.hasProperty(l, '_c'), isFalse);
- expect(js_util.hasProperty(l, 'class'), isFalse);
-
- l = new ExampleTypedLiteral(JS$_c: 74);
- expect(js_util.hasProperty(l, '_c'), isTrue);
- expect(l.JS$_c, equals(74));
- });
+ test('setProperty', () {
+ var textElement = new Text('foo');
+ js_util.setProperty(textElement, 'data', 'bar');
+ expect(textElement.text, equals('bar'));
});
- group('getProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.getProperty(f, 'a'), equals(42));
- expect(js_util.getProperty(f, 'toString') is Function, isTrue);
- js_util.setProperty(f, '__proto__', null);
- expect(js_util.getProperty(f, 'toString'), isNull);
- });
-
- test('typed literal', () {
- var l = new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: 7, JS$class: true);
- expect(js_util.getProperty(l, 'a'), equals('x'));
- expect(js_util.getProperty(l, 'b'), equals(42));
- expect(js_util.getProperty(l, '_c'), equals(7));
- expect(l.JS$_c, equals(7));
- expect(js_util.getProperty(l, 'class'), isTrue);
- expect(js_util.getProperty(l, r'JS$_c'), isNull);
- expect(js_util.getProperty(l, r'JS$class'), isNull);
- });
+ test('callMethod', () {
+ var canvas = new CanvasElement();
+ expect(
+ identical(canvas.getContext('2d'),
+ js_util.callMethod(canvas, 'getContext', ['2d'])),
+ isTrue);
});
- group('setProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.getProperty(f, 'a'), equals(42));
- js_util.setProperty(f, 'a', 100);
- expect(f.a, equals(100));
- expect(js_util.getProperty(f, 'a'), equals(100));
- });
+ test('instanceof', () {
+ var canvas = new Element.tag('canvas');
+ expect(js_util.instanceof(canvas, JSNodeType), isTrue);
+ expect(js_util.instanceof(canvas, JSTextType), isFalse);
+ expect(js_util.instanceof(canvas, JSElementType), isTrue);
+ expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
+ var div = new Element.tag('div');
+ expect(js_util.instanceof(div, JSNodeType), isTrue);
+ expect(js_util.instanceof(div, JSTextType), isFalse);
+ expect(js_util.instanceof(div, JSElementType), isTrue);
+ expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
- test('typed literal', () {
- var l = new ExampleTypedLiteral();
- js_util.setProperty(l, 'a', 'foo');
- expect(js_util.getProperty(l, 'a'), equals('foo'));
- expect(l.a, equals('foo'));
- js_util.setProperty(l, 'a', l);
- expect(identical(l.a, l), isTrue);
- var list = ['arr'];
- js_util.setProperty(l, 'a', list);
- expect(identical(l.a, list), isTrue);
- l.JS$class = 42;
- expect(l.JS$class, equals(42));
- js_util.setProperty(l, 'class', 100);
- expect(l.JS$class, equals(100));
- });
+ var text = new Text('foo');
+ expect(js_util.instanceof(text, JSNodeType), isTrue);
+ expect(js_util.instanceof(text, JSTextType), isTrue);
+ expect(js_util.instanceof(text, JSElementType), isFalse);
+
+ var f = new Foo();
+ expect(js_util.instanceof(f, JSNodeType), isFalse);
});
- group('callMethod', () {
- test('html object', () {
- var canvas = new CanvasElement();
- expect(
- identical(canvas.getContext('2d'),
- js_util.callMethod(canvas, 'getContext', ['2d'])),
- isTrue);
- });
-
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.callMethod(f, 'bar', []), equals(42));
- });
- });
-
- group('instanceof', () {
- test('html object', () {
- var canvas = new Element.tag('canvas');
- expect(js_util.instanceof(canvas, JSNodeType), isTrue);
- expect(js_util.instanceof(canvas, JSTextType), isFalse);
- expect(js_util.instanceof(canvas, JSElementType), isTrue);
- expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
- var div = new Element.tag('div');
- expect(js_util.instanceof(div, JSNodeType), isTrue);
- expect(js_util.instanceof(div, JSTextType), isFalse);
- expect(js_util.instanceof(div, JSElementType), isTrue);
- expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
-
- var text = new Text('foo');
- expect(js_util.instanceof(text, JSNodeType), isTrue);
- expect(js_util.instanceof(text, JSTextType), isTrue);
- expect(js_util.instanceof(text, JSElementType), isFalse);
- });
-
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.instanceof(f, JSFooType), isTrue);
- expect(js_util.instanceof(f, JSNodeType), isFalse);
- });
-
- test('typed literal', () {
- var l = new ExampleTypedLiteral();
- expect(js_util.instanceof(l, JSFooType), isFalse);
- });
- });
-
- group('callConstructor', () {
- test('html object', () {
- var textNode = js_util.callConstructor(JSTextType, ['foo']);
- expect(js_util.instanceof(textNode, JSTextType), isTrue);
- expect(textNode is Text, isTrue);
- expect(textNode.text, equals('foo'));
- });
-
- test('typed object', () {
- Foo f = js_util.callConstructor(JSFooType, [42]);
- expect(f.a, equals(42));
- });
- });
-
- Future<void> testResolvedPromise() async {
- final String result = await js_util.promiseToFuture(resolvedPromise);
- expect(result, equals('resolved'));
- }
-
- Future<void> testRejectedPromise() async {
- try {
- final String result = await promiseToFuture(rejectedPromise);
- fail('expected Future to throw an error');
- } catch (error) {
- expect(error, equals('rejected'));
- }
- }
-
- Future<void> testReturnRejectedPromise() async {
- final String result = await promiseToFuture(getResolvedPromise());
- expect(result, equals('resolved'));
- }
-
- asyncTest(() async {
- await testResolvedPromise();
- await testRejectedPromise();
- await testReturnRejectedPromise();
+ test('callConstructor', () {
+ var textNode = js_util.callConstructor(JSTextType, ['foo']);
+ expect(js_util.instanceof(textNode, JSTextType), isTrue);
+ expect(textNode is Text, isTrue);
+ expect(textNode.text, equals('foo'));
});
}
diff --git a/tests/lib/js/js_util/async_test.dart b/tests/lib/js/js_util/async_test.dart
new file mode 100644
index 0000000..9172fe7
--- /dev/null
+++ b/tests/lib/js/js_util/async_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2021, 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.
+
+@JS()
+library js_util_async_test;
+
+import 'dart:async';
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+import 'package:async_helper/async_helper.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+abstract class Promise<T> {}
+
+@JS()
+external Promise get resolvedPromise;
+
+@JS()
+external Promise get rejectedPromise;
+
+@JS()
+external Promise getResolvedPromise();
+
+main() {
+ eval(r"""
+ var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
+ var resolvedPromise = new Promise(resolve => resolve('resolved'));
+ function getResolvedPromise() {
+ return resolvedPromise;
+ }
+ """);
+
+ Future<void> testResolvedPromise() async {
+ final String result = await js_util.promiseToFuture(resolvedPromise);
+ expect(result, equals('resolved'));
+ }
+
+ Future<void> testRejectedPromise() async {
+ asyncExpectThrows<String>(() => js_util.promiseToFuture(rejectedPromise),
+ (String error) => error == 'rejected');
+ }
+
+ Future<void> testReturnResolvedPromise() async {
+ final String result = await js_util.promiseToFuture(getResolvedPromise());
+ expect(result, equals('resolved'));
+ }
+
+ asyncTest(() async {
+ await testResolvedPromise();
+ await testRejectedPromise();
+ await testReturnResolvedPromise();
+ });
+}
diff --git a/tests/lib/js/js_util/js_prefix_test.dart b/tests/lib/js/js_util/js_prefix_test.dart
new file mode 100644
index 0000000..47205cb
--- /dev/null
+++ b/tests/lib/js/js_util/js_prefix_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2021, 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.
+
+// Tests the functionality of the JS$ prefix for escaping keywords in JS names.
+// Currently only implemented in dart2js, expected to fail in ddc.
+
+@JS()
+library js_prefix_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+ external factory ExampleTypedLiteral({JS$_c, JS$class});
+
+ external get JS$_c;
+ external set JS$_c(v);
+ // Identical to JS$_c but only accessible within the library.
+ external get _c;
+ external get JS$class;
+ external set JS$class(v);
+}
+
+main() {
+ test('hasProperty', () {
+ var literal = new ExampleTypedLiteral(JS$_c: null, JS$class: true);
+ expect(js_util.hasProperty(literal, '_c'), isTrue);
+ expect(literal.JS$_c, isNull);
+ expect(js_util.hasProperty(literal, 'class'), isTrue);
+ // JS$_c escapes to _c so the property JS$_c will not exist on the object.
+ expect(js_util.hasProperty(literal, r'JS$_c'), isFalse);
+ expect(js_util.hasProperty(literal, r'JS$class'), isFalse);
+ expect(literal.JS$class, isTrue);
+
+ literal = new ExampleTypedLiteral();
+ expect(js_util.hasProperty(literal, '_c'), isFalse);
+ expect(js_util.hasProperty(literal, 'class'), isFalse);
+
+ literal = new ExampleTypedLiteral(JS$_c: 74);
+ expect(js_util.hasProperty(literal, '_c'), isTrue);
+ expect(literal.JS$_c, equals(74));
+ });
+
+ test('getProperty', () {
+ var literal = new ExampleTypedLiteral(JS$_c: 7, JS$class: true);
+ expect(js_util.getProperty(literal, '_c'), equals(7));
+ expect(literal.JS$_c, equals(7));
+ expect(js_util.getProperty(literal, 'class'), isTrue);
+ expect(js_util.getProperty(literal, r'JS$_c'), isNull);
+ expect(js_util.getProperty(literal, r'JS$class'), isNull);
+ });
+
+ test('setProperty', () {
+ var literal = new ExampleTypedLiteral();
+ literal.JS$class = 42;
+ expect(literal.JS$class, equals(42));
+ js_util.setProperty(literal, 'class', 100);
+ expect(literal.JS$class, equals(100));
+ });
+}
diff --git a/tests/lib/js/js_util/jsify_test.dart b/tests/lib/js/js_util/jsify_test.dart
new file mode 100644
index 0000000..5ed916d
--- /dev/null
+++ b/tests/lib/js/js_util/jsify_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, 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.
+
+// Tests the jsify functionality of the js_util library.
+
+@JS()
+library js_util_jsify_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external bool checkMap(m, String, value);
+
+@JS()
+class Foo {
+ external Foo(num a);
+
+ external num get a;
+ external num bar();
+}
+
+main() {
+ eval(r"""
+ function Foo(a) {
+ this.a = a;
+ }
+
+ Foo.prototype.bar = function() {
+ return this.a;
+ }
+
+ function checkMap(m, key, value) {
+ if (m.hasOwnProperty(key))
+ return m[key] == value;
+ else
+ return false;
+ }
+ """);
+
+ test('convert a List', () {
+ final list = [1, 2, 3, 4, 5, 6, 7, 8];
+ var array = js_util.jsify(list);
+ expect(array is List, isTrue);
+ expect(identical(array, list), isFalse);
+ expect(array.length, equals(list.length));
+ for (var i = 0; i < list.length; i++) {
+ expect(array[i], equals(list[i]));
+ }
+ });
+
+ test('convert an Iterable', () {
+ final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
+ var array = js_util.jsify(set);
+ expect(array is List, isTrue);
+ expect(array.length, equals(set.length));
+ for (var i = 0; i < array.length; i++) {
+ expect(set.contains(array[i]), isTrue);
+ }
+ });
+
+ test('convert a Map', () {
+ var map = {'a': 1, 'b': 2, 'c': 3};
+ var jsMap = js_util.jsify(map);
+ expect(jsMap is List, isFalse);
+ expect(jsMap is Map, isFalse);
+ for (var key in map.keys) {
+ expect(checkMap(jsMap, key, map[key]), isTrue);
+ }
+ });
+
+ test('deep convert a complex object', () {
+ dynamic object = {
+ 'a': [
+ 1,
+ [2, 3]
+ ],
+ 'b': {'c': 3, 'd': new Foo(42)},
+ 'e': null
+ };
+ var jsObject = js_util.jsify(object);
+ expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
+ expect(js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
+ expect(js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
+
+ var b = js_util.getProperty(jsObject, 'b');
+ expect(js_util.getProperty(b, 'c'), equals(object['b']['c']));
+ var d = js_util.getProperty(b, 'd');
+ expect(d, equals(object['b']['d']));
+ expect(js_util.getProperty(d, 'a'), equals(42));
+ expect(js_util.callMethod(d, 'bar', []), equals(42));
+
+ expect(js_util.getProperty(jsObject, 'e'), isNull);
+ });
+
+ test('throws if object is not a Map or Iterable', () {
+ expect(() => js_util.jsify('a'), throwsArgumentError);
+ });
+}
diff --git a/tests/lib/js/js_util/properties_test.dart b/tests/lib/js/js_util/properties_test.dart
new file mode 100644
index 0000000..986c431
--- /dev/null
+++ b/tests/lib/js/js_util/properties_test.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2021, 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.
+
+// Tests the functionality of object properties with the js_util library. For
+// js_util tests with HTML objects see tests/lib/html/js_util_test.dart.
+
+@JS()
+library js_util_properties_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external String jsFunction();
+
+@JS()
+external void eval(String code);
+
+@JS('JSON.stringify')
+external String stringify(o);
+
+@JS('ArrayBuffer')
+external get JSArrayBufferType;
+
+@JS()
+class Foo {
+ external Foo(num a);
+
+ external num get a;
+ external num bar();
+ external Object get objectProperty;
+}
+
+@JS('Foo')
+external get JSFooType;
+
+String dartFunction() {
+ return 'Dart Function';
+}
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+ external factory ExampleTypedLiteral({a, b});
+
+ external get a;
+ external get b;
+}
+
+String _getBarWithSideEffect() {
+ var x = 5;
+ expect(x, equals(5));
+ return 'bar';
+}
+
+main() {
+ eval(r"""
+ function Foo(a) {
+ this.a = a;
+ }
+
+ Foo.b = 38;
+
+ Foo.prototype.list = [2, 4, 6];
+
+ Foo.prototype.bar = function() {
+ return this.a;
+ }
+
+ Foo.prototype.toString = function() {
+ return "I'm a Foo a=" + this.a;
+ }
+
+ Foo.prototype.fnList = [Foo.prototype.bar, Foo.prototype.toString];
+
+ Foo.prototype.objectProperty = {
+ 'c': 1,
+ 'list': [10, 20, 30],
+ 'functionProperty': function() { return 'Function Property'; }
+ }
+
+ function jsFunction() {
+ return "JS Function";
+ }
+
+ Foo.prototype.nestedFunction = function() {
+ return function() {
+ return 'Nested Function';
+ };
+ }
+
+ Foo.prototype.getFirstEl = function(list) {
+ return list[0];
+ }
+
+ Foo.prototype.sumFn = function(a, b) {
+ return a + b;
+ }
+
+ Foo.prototype.getA = function(obj) {
+ return obj.a;
+ }
+
+ Foo.prototype.callFn = function(fn) {
+ return fn();
+ }
+ """);
+
+ group('newObject', () {
+ test('create', () {
+ expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
+ });
+
+ test('callMethod', () {
+ var o = js_util.newObject();
+ expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
+ expect(stringify(o), equals('{}'));
+ });
+
+ test('properties', () {
+ var o = js_util.newObject();
+ expect(js_util.hasProperty(o, 'foo bar'), isFalse);
+ expect(js_util.hasProperty(o, 'toString'), isTrue);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['toString']), isFalse);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isFalse);
+ js_util.setProperty(o, 'foo bar', 42);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isTrue);
+ expect(js_util.getProperty(o, 'foo bar'), equals(42));
+ expect(js_util.hasProperty(o, 'foo bar'), isTrue);
+ expect(stringify(o), equals('{"foo bar":42}'));
+ });
+
+ test('nested properties calls', () {
+ var o = js_util.newObject();
+ var f = new Foo(42);
+ js_util.setProperty(o, 'foo', f);
+ var foo = js_util.getProperty(o, 'foo');
+ expect(foo, equals(f));
+ expect(js_util.hasProperty(foo, 'a'), isTrue);
+ expect(js_util.getProperty(foo, 'a'), equals(42));
+ js_util.setProperty(foo, 'a', 24);
+ expect(js_util.getProperty(foo, 'a'), equals(24));
+ });
+ });
+
+ group('hasProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.hasProperty(f, 'a'), isTrue);
+ expect(js_util.hasProperty(f, 'bar'), isTrue);
+ expect(js_util.hasProperty(f, 'b'), isFalse);
+ expect(js_util.hasProperty(f, 'toString'), isTrue);
+ js_util.setProperty(f, '__proto__', null);
+ expect(js_util.hasProperty(f, 'toString'), isFalse);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+ expect(js_util.hasProperty(literal, 'a'), isTrue);
+ expect(js_util.hasProperty(literal, 'b'), isTrue);
+ expect(js_util.hasProperty(literal, 'anything'), isFalse);
+
+ literal = new ExampleTypedLiteral(a: null);
+ expect(js_util.hasProperty(literal, 'a'), isTrue);
+ expect(js_util.hasProperty(literal, 'b'), isFalse);
+ expect(js_util.hasProperty(literal, 'anything'), isFalse);
+ });
+
+ test('complex hasProperty calls', () {
+ var f = new Foo(42);
+ expect(js_util.hasProperty(f.objectProperty, 'c'), isTrue);
+ expect(js_util.hasProperty(f.objectProperty, 'nonexistent'), isFalse);
+
+ // Using a variable for the property name.
+ String propertyName = 'bar';
+ expect(js_util.hasProperty(f, propertyName), isTrue);
+ });
+ });
+
+ group('getProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.getProperty(f, 'a'), equals(42));
+ expect(js_util.getProperty(f, 'b'), isNull);
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ expect(js_util.getProperty(f, 'objectProperty') is Object, isTrue);
+ expect(js_util.getProperty(f, 'toString') is Function, isTrue);
+ js_util.setProperty(f, '__proto__', null);
+ expect(js_util.getProperty(f, 'toString'), isNull);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+ expect(js_util.getProperty(literal, 'a'), equals('x'));
+ expect(js_util.getProperty(literal, 'b'), equals(42));
+ expect(js_util.getProperty(literal, 'anything'), isNull);
+ });
+
+ test('complex getProperty calls', () {
+ var f = new Foo(42);
+
+ // Accessing a method property.
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals(42));
+
+ // Accessing list properties.
+ expect(js_util.getProperty(f, 'list')[0], equals(2));
+ expect(js_util.getProperty(f, 'fnList')[0] is Function, isTrue);
+ expect(
+ js_util.callMethod(
+ js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
+ equals(42));
+
+ // Accessing nested object properites.
+ var objectProperty = js_util.getProperty(f, 'objectProperty');
+ expect(js_util.getProperty(objectProperty, 'c'), equals(1));
+ expect(js_util.getProperty(objectProperty, 'list') is List, isTrue);
+ expect(js_util.getProperty(objectProperty, 'list')[1], equals(20));
+ expect(
+ js_util.getProperty(objectProperty, 'functionProperty') is Function,
+ isTrue);
+
+ // Using a variable for the property name.
+ String propertyName = 'a';
+ expect(js_util.getProperty(f, propertyName), equals(42));
+ String bar = _getBarWithSideEffect();
+ expect(js_util.getProperty(f, bar) is Function, isTrue);
+ expect(js_util.callMethod(f, bar, []), equals(42));
+ });
+ });
+
+ group('setProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.getProperty(f, 'a'), equals(42));
+ js_util.setProperty(f, 'a', 100);
+ expect(f.a, equals(100));
+ expect(js_util.getProperty(f, 'a'), equals(100));
+
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ js_util.setProperty(f, 'list', [8]);
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ expect(js_util.getProperty(f, 'list')[0], equals(8));
+
+ js_util.setProperty(f, 'newProperty', 'new');
+ expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral();
+ js_util.setProperty(literal, 'a', 'foo');
+ expect(js_util.getProperty(literal, 'a'), equals('foo'));
+ expect(literal.a, equals('foo'));
+ js_util.setProperty(literal, 'a', literal);
+ expect(identical(literal.a, literal), isTrue);
+ var list = ['arr'];
+ js_util.setProperty(literal, 'a', list);
+ expect(identical(literal.a, list), isTrue);
+ });
+
+ test('complex setProperty calls', () {
+ var f = new Foo(42);
+
+ // Set function property to a num.
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ js_util.setProperty(f, 'bar', 5);
+ expect(js_util.getProperty(f, 'bar') is Function, isFalse);
+ expect(js_util.getProperty(f, 'bar'), equals(5));
+
+ // Set property to a Dart function.
+ js_util.setProperty(f, 'bar', allowInterop(dartFunction));
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals('Dart Function'));
+ js_util.setProperty(f, 'bar', allowInterop(() {
+ return 'Inline';
+ }));
+ expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
+
+ // Set property to a JS function.
+ js_util.setProperty(f, 'bar', allowInterop(jsFunction));
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals('JS Function'));
+
+ // Set property with nested object properties.
+ js_util.setProperty(f.objectProperty, 'c', 'new val');
+ expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
+ js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
+ expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+
+ // Using a variable for the property name.
+ String propertyName = 'bar';
+ js_util.setProperty(f, propertyName, 'foo');
+ expect(js_util.getProperty(f, 'bar'), equals('foo'));
+ String bar = _getBarWithSideEffect();
+ js_util.setProperty(f, bar, 'baz');
+ expect(js_util.getProperty(f, bar), equals('baz'));
+ });
+ });
+
+ group('callMethod', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.callMethod(f, 'bar', []), equals(42));
+ });
+
+ test('complex callMethod calls', () {
+ var f = new Foo(42);
+
+ // Call a method that returns an unbound function.
+ expect(js_util.callMethod(f, 'nestedFunction', []) is Function, isTrue);
+ expect(js_util.callMethod(f, 'nestedFunction', [])(),
+ equals('Nested Function'));
+
+ // Call method on a nested function property.
+ expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
+ equals('Function Property'));
+
+ // Call method with different args.
+ expect(
+ js_util.callMethod(f, 'getFirstEl', [
+ [25, 50]
+ ]),
+ equals(25));
+ expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
+ expect(js_util.callMethod(f, 'getA', [f]), equals(42));
+ expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
+ equals("JS Function"));
+ expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
+ equals("Dart Function"));
+ expect(
+ js_util.callMethod(f, 'callFn', [
+ allowInterop(() {
+ return "inline";
+ })
+ ]),
+ equals("inline"));
+
+ // Using a variable for the method name.
+ String methodName = 'bar';
+ expect(js_util.callMethod(f, methodName, []), equals(42));
+ String bar = _getBarWithSideEffect();
+ expect(js_util.callMethod(f, bar, []), equals(42));
+ });
+ });
+
+ group('instanceof', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.instanceof(f, JSFooType), isTrue);
+ expect(js_util.instanceof(f, JSArrayBufferType), isFalse);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral();
+ expect(js_util.instanceof(literal, JSFooType), isFalse);
+ });
+ });
+
+ group('callConstructor', () {
+ test('typed object', () {
+ Foo f = js_util.callConstructor(JSFooType, [42]);
+ expect(f.a, equals(42));
+ });
+ });
+}
diff --git a/tests/lib/lib_dartdevc.status b/tests/lib/lib_dartdevc.status
index a8a3790..1fb6c51 100644
--- a/tests/lib/lib_dartdevc.status
+++ b/tests/lib/lib_dartdevc.status
@@ -37,6 +37,7 @@
html/custom_elements_test: Skip # Issue 29922
html/notification_permission_test: Skip # Issue 32002
isolate/*: SkipByDesign # No support for dart:isolate in dart4web (http://dartbug.com/30538)
+js/js_util/js_prefix_test: SkipByDesign # JS$ prefix not implemented on ddc.
mirrors/*: SkipByDesign # Mirrors not supported on web in Dart 2.0.
typed_data/int64_list_load_store_test: SkipByDesign # dartdevk/c does not support Int64List
typed_data/typed_data_hierarchy_int64_test: SkipByDesign # dartdevk/c does not support Int64List
diff --git a/tests/lib_2/html/js_util_test.dart b/tests/lib_2/html/js_util_test.dart
index d32df10..a3d5092 100644
--- a/tests/lib_2/html/js_util_test.dart
+++ b/tests/lib_2/html/js_util_test.dart
@@ -2,75 +2,19 @@
// 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.
-@JS()
-library js_native_test;
+// Tests the functionality of js_util with HTML objects.
-import 'dart:async';
+@JS()
+library js_util_test;
+
import 'dart:html';
-import 'dart:typed_data' show ByteBuffer, Int32List;
-import 'dart:indexed_db' show IdbFactory, KeyRange;
import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util;
import 'package:expect/minitest.dart';
-import 'package:async_helper/async_helper.dart';
-
-_injectJs() {
- final script = new ScriptElement();
- script.type = 'text/javascript';
- script.innerHtml = r"""
-var x = 42;
-
-var _x = 123;
-
-var myArray = ["value1"];
-
-function returnThis() {
- return this;
-}
-
-function getTypeOf(o) {
- return typeof(o);
-}
-
-function Foo(a) {
- this.a = a;
-}
-
-Foo.b = 38;
-
-Foo.prototype.bar = function() {
- return this.a;
-}
-Foo.prototype.toString = function() {
- return "I'm a Foo a=" + this.a;
-}
-
-var container = new Object();
-container.Foo = Foo;
-
-function checkMap(m, key, value) {
- if (m.hasOwnProperty(key))
- return m[key] == value;
- else
- return false;
-}
-
-var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
-var resolvedPromise = new Promise(resolve => resolve('resolved'));
-function getResolvedPromise() {
- return resolvedPromise;
-}
-
-""";
- document.body.append(script);
-}
@JS()
-external bool checkMap(m, String, value);
-
-@JS('JSON.stringify')
-external String stringify(o);
+external void eval(String code);
@JS('Node')
external get JSNodeType;
@@ -86,302 +30,61 @@
@JS()
class Foo {
- external Foo(num a);
-
- external num get a;
- external num bar();
-}
-
-@JS('Foo')
-external get JSFooType;
-
-@JS()
-@anonymous
-class ExampleTypedLiteral {
- external factory ExampleTypedLiteral({a, b, JS$_c, JS$class});
-
- external get a;
- external get b;
- external get JS$_c;
- external set JS$_c(v);
- // Identical to JS$_c but only accessible within the library.
- external get _c;
- external get JS$class;
- external set JS$class(v);
-}
-
-@JS()
-abstract class Promise<T> {}
-
-@JS()
-external Promise get resolvedPromise;
-
-@JS()
-external Promise get rejectedPromise;
-
-@JS()
-external Promise getResolvedPromise();
-
-@JS("Object.prototype.hasOwnProperty")
-external get _hasOwnProperty;
-
-bool hasOwnProperty(o, String name) {
- return js_util.callMethod(_hasOwnProperty, 'call', [o, name]);
+ external Foo();
}
main() {
- _injectJs();
+ eval(r""" function Foo() {} """);
- group('js_util.jsify()', () {
- test('convert a List', () {
- final list = [1, 2, 3, 4, 5, 6, 7, 8];
- var array = js_util.jsify(list);
- expect(array is List, isTrue);
- expect(identical(array, list), isFalse);
- expect(array.length, equals(list.length));
- for (var i = 0; i < list.length; i++) {
- expect(array[i], equals(list[i]));
- }
- });
-
- test('convert an Iterable', () {
- final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
- var array = js_util.jsify(set);
- expect(array is List, isTrue);
- expect(array.length, equals(set.length));
- for (var i = 0; i < array.length; i++) {
- expect(set.contains(array[i]), isTrue);
- }
- });
-
- test('convert a Map', () {
- var map = {'a': 1, 'b': 2, 'c': 3};
- var jsMap = js_util.jsify(map);
- expect(jsMap is! List, isTrue);
- for (var key in map.keys) {
- expect(checkMap(jsMap, key, map[key]), isTrue);
- }
- });
-
- test('deep convert a complex object', () {
- dynamic object = {
- 'a': [
- 1,
- [2, 3]
- ],
- 'b': {'c': 3, 'd': new Foo(42)},
- 'e': null
- };
- var jsObject = js_util.jsify(object);
- expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
- expect(
- js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
- expect(
- js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
- expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'c'),
- equals(object['b']['c']));
- expect(js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
- equals(object['b']['d']));
- expect(
- js_util.callMethod(
- js_util.getProperty(js_util.getProperty(jsObject, 'b'), 'd'),
- 'bar', []),
- equals(42));
- expect(js_util.getProperty(jsObject, 'e'), isNull);
- });
-
- test('throws if object is not a Map or Iterable', () {
- expect(() => js_util.jsify('a'), throwsArgumentError);
- });
+ test('hasProperty', () {
+ var textElement = new Text('foo');
+ expect(js_util.hasProperty(textElement, 'data'), isTrue);
});
- group('js_util.newObject', () {
- test('create', () {
- expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
- });
-
- test('callMethod', () {
- var o = js_util.newObject();
- expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
- expect(stringify(o), equals('{}'));
- });
-
- test('properties', () {
- var o = js_util.newObject();
- expect(js_util.hasProperty(o, 'foo bar'), isFalse);
- expect(js_util.hasProperty(o, 'toString'), isTrue);
- expect(hasOwnProperty(o, 'toString'), isFalse);
- expect(hasOwnProperty(o, 'foo bar'), isFalse);
- js_util.setProperty(o, 'foo bar', 42);
- expect(hasOwnProperty(o, 'foo bar'), isTrue);
- expect(js_util.getProperty(o, 'foo bar'), equals(42));
- expect(js_util.hasProperty(o, 'foo bar'), isTrue);
- expect(stringify(o), equals('{"foo bar":42}'));
- });
+ test('getProperty', () {
+ var textElement = new Text('foo');
+ expect(js_util.getProperty(textElement, 'data'), equals('foo'));
});
- group('hasProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.hasProperty(f, 'a'), isTrue);
- expect(js_util.hasProperty(f, 'toString'), isTrue);
- js_util.setProperty(f, '__proto__', null);
- expect(js_util.hasProperty(f, 'toString'), isFalse);
- });
- test('typed literal', () {
- var l =
- new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: null, JS$class: true);
- expect(js_util.hasProperty(l, 'a'), isTrue);
- expect(js_util.hasProperty(l, 'b'), isTrue);
- expect(js_util.hasProperty(l, '_c'), isTrue);
- expect(l.JS$_c, isNull);
- expect(js_util.hasProperty(l, 'class'), isTrue);
- // JS$_c escapes to _c so the property JS$_c will not exist on the object.
- expect(js_util.hasProperty(l, r'JS$_c'), isFalse);
- expect(js_util.hasProperty(l, r'JS$class'), isFalse);
- expect(l.JS$class, isTrue);
-
- l = new ExampleTypedLiteral(a: null);
- expect(js_util.hasProperty(l, 'a'), isTrue);
- expect(js_util.hasProperty(l, 'b'), isFalse);
- expect(js_util.hasProperty(l, '_c'), isFalse);
- expect(js_util.hasProperty(l, 'class'), isFalse);
-
- l = new ExampleTypedLiteral(JS$_c: 74);
- expect(js_util.hasProperty(l, '_c'), isTrue);
- expect(l.JS$_c, equals(74));
- });
+ test('setProperty', () {
+ var textElement = new Text('foo');
+ js_util.setProperty(textElement, 'data', 'bar');
+ expect(textElement.text, equals('bar'));
});
- group('getProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.getProperty(f, 'a'), equals(42));
- expect(js_util.getProperty(f, 'toString') is Function, isTrue);
- js_util.setProperty(f, '__proto__', null);
- expect(js_util.getProperty(f, 'toString'), isNull);
- });
-
- test('typed literal', () {
- var l = new ExampleTypedLiteral(a: 'x', b: 42, JS$_c: 7, JS$class: true);
- expect(js_util.getProperty(l, 'a'), equals('x'));
- expect(js_util.getProperty(l, 'b'), equals(42));
- expect(js_util.getProperty(l, '_c'), equals(7));
- expect(l.JS$_c, equals(7));
- expect(js_util.getProperty(l, 'class'), isTrue);
- expect(js_util.getProperty(l, r'JS$_c'), isNull);
- expect(js_util.getProperty(l, r'JS$class'), isNull);
- });
+ test('callMethod', () {
+ var canvas = new CanvasElement();
+ expect(
+ identical(canvas.getContext('2d'),
+ js_util.callMethod(canvas, 'getContext', ['2d'])),
+ isTrue);
});
- group('setProperty', () {
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.getProperty(f, 'a'), equals(42));
- js_util.setProperty(f, 'a', 100);
- expect(f.a, equals(100));
- expect(js_util.getProperty(f, 'a'), equals(100));
- });
+ test('instanceof', () {
+ var canvas = new Element.tag('canvas');
+ expect(js_util.instanceof(canvas, JSNodeType), isTrue);
+ expect(js_util.instanceof(canvas, JSTextType), isFalse);
+ expect(js_util.instanceof(canvas, JSElementType), isTrue);
+ expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
+ var div = new Element.tag('div');
+ expect(js_util.instanceof(div, JSNodeType), isTrue);
+ expect(js_util.instanceof(div, JSTextType), isFalse);
+ expect(js_util.instanceof(div, JSElementType), isTrue);
+ expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
- test('typed literal', () {
- var l = new ExampleTypedLiteral();
- js_util.setProperty(l, 'a', 'foo');
- expect(js_util.getProperty(l, 'a'), equals('foo'));
- expect(l.a, equals('foo'));
- js_util.setProperty(l, 'a', l);
- expect(identical(l.a, l), isTrue);
- var list = ['arr'];
- js_util.setProperty(l, 'a', list);
- expect(identical(l.a, list), isTrue);
- l.JS$class = 42;
- expect(l.JS$class, equals(42));
- js_util.setProperty(l, 'class', 100);
- expect(l.JS$class, equals(100));
- });
+ var text = new Text('foo');
+ expect(js_util.instanceof(text, JSNodeType), isTrue);
+ expect(js_util.instanceof(text, JSTextType), isTrue);
+ expect(js_util.instanceof(text, JSElementType), isFalse);
+
+ var f = new Foo();
+ expect(js_util.instanceof(f, JSNodeType), isFalse);
});
- group('callMethod', () {
- test('html object', () {
- var canvas = new CanvasElement();
- expect(
- identical(canvas.getContext('2d'),
- js_util.callMethod(canvas, 'getContext', ['2d'])),
- isTrue);
- });
-
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.callMethod(f, 'bar', []), equals(42));
- });
- });
-
- group('instanceof', () {
- test('html object', () {
- var canvas = new Element.tag('canvas');
- expect(js_util.instanceof(canvas, JSNodeType), isTrue);
- expect(js_util.instanceof(canvas, JSTextType), isFalse);
- expect(js_util.instanceof(canvas, JSElementType), isTrue);
- expect(js_util.instanceof(canvas, JSHtmlCanvasElementType), isTrue);
- var div = new Element.tag('div');
- expect(js_util.instanceof(div, JSNodeType), isTrue);
- expect(js_util.instanceof(div, JSTextType), isFalse);
- expect(js_util.instanceof(div, JSElementType), isTrue);
- expect(js_util.instanceof(div, JSHtmlCanvasElementType), isFalse);
-
- var text = new Text('foo');
- expect(js_util.instanceof(text, JSNodeType), isTrue);
- expect(js_util.instanceof(text, JSTextType), isTrue);
- expect(js_util.instanceof(text, JSElementType), isFalse);
- });
-
- test('typed object', () {
- var f = new Foo(42);
- expect(js_util.instanceof(f, JSFooType), isTrue);
- expect(js_util.instanceof(f, JSNodeType), isFalse);
- });
-
- test('typed literal', () {
- var l = new ExampleTypedLiteral();
- expect(js_util.instanceof(l, JSFooType), isFalse);
- });
- });
-
- group('callConstructor', () {
- test('html object', () {
- var textNode = js_util.callConstructor(JSTextType, ['foo']);
- expect(js_util.instanceof(textNode, JSTextType), isTrue);
- expect(textNode is Text, isTrue);
- expect(textNode.text, equals('foo'));
- });
-
- test('typed object', () {
- Foo f = js_util.callConstructor(JSFooType, [42]);
- expect(f.a, equals(42));
- });
- });
-
- Future<void> testResolvedPromise() async {
- final String result = await js_util.promiseToFuture(resolvedPromise);
- expect(result, equals('resolved'));
- }
-
- Future<void> testRejectedPromise() async {
- try {
- final String result = await promiseToFuture(rejectedPromise);
- fail('expected Future to throw an error');
- } catch (error) {
- expect(error, equals('rejected'));
- }
- }
-
- Future<void> testReturnRejectedPromise() async {
- final String result = await promiseToFuture(getResolvedPromise());
- expect(result, equals('resolved'));
- }
-
- asyncTest(() async {
- await testResolvedPromise();
- await testRejectedPromise();
- await testReturnRejectedPromise();
+ test('callConstructor', () {
+ var textNode = js_util.callConstructor(JSTextType, ['foo']);
+ expect(js_util.instanceof(textNode, JSTextType), isTrue);
+ expect(textNode is Text, isTrue);
+ expect(textNode.text, equals('foo'));
});
}
diff --git a/tests/lib_2/js/js_util/async_test.dart b/tests/lib_2/js/js_util/async_test.dart
new file mode 100644
index 0000000..9172fe7
--- /dev/null
+++ b/tests/lib_2/js/js_util/async_test.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2021, 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.
+
+@JS()
+library js_util_async_test;
+
+import 'dart:async';
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+import 'package:async_helper/async_helper.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+abstract class Promise<T> {}
+
+@JS()
+external Promise get resolvedPromise;
+
+@JS()
+external Promise get rejectedPromise;
+
+@JS()
+external Promise getResolvedPromise();
+
+main() {
+ eval(r"""
+ var rejectedPromise = new Promise((resolve, reject) => reject('rejected'));
+ var resolvedPromise = new Promise(resolve => resolve('resolved'));
+ function getResolvedPromise() {
+ return resolvedPromise;
+ }
+ """);
+
+ Future<void> testResolvedPromise() async {
+ final String result = await js_util.promiseToFuture(resolvedPromise);
+ expect(result, equals('resolved'));
+ }
+
+ Future<void> testRejectedPromise() async {
+ asyncExpectThrows<String>(() => js_util.promiseToFuture(rejectedPromise),
+ (String error) => error == 'rejected');
+ }
+
+ Future<void> testReturnResolvedPromise() async {
+ final String result = await js_util.promiseToFuture(getResolvedPromise());
+ expect(result, equals('resolved'));
+ }
+
+ asyncTest(() async {
+ await testResolvedPromise();
+ await testRejectedPromise();
+ await testReturnResolvedPromise();
+ });
+}
diff --git a/tests/lib_2/js/js_util/js_prefix_test.dart b/tests/lib_2/js/js_util/js_prefix_test.dart
new file mode 100644
index 0000000..47205cb
--- /dev/null
+++ b/tests/lib_2/js/js_util/js_prefix_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2021, 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.
+
+// Tests the functionality of the JS$ prefix for escaping keywords in JS names.
+// Currently only implemented in dart2js, expected to fail in ddc.
+
+@JS()
+library js_prefix_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+ external factory ExampleTypedLiteral({JS$_c, JS$class});
+
+ external get JS$_c;
+ external set JS$_c(v);
+ // Identical to JS$_c but only accessible within the library.
+ external get _c;
+ external get JS$class;
+ external set JS$class(v);
+}
+
+main() {
+ test('hasProperty', () {
+ var literal = new ExampleTypedLiteral(JS$_c: null, JS$class: true);
+ expect(js_util.hasProperty(literal, '_c'), isTrue);
+ expect(literal.JS$_c, isNull);
+ expect(js_util.hasProperty(literal, 'class'), isTrue);
+ // JS$_c escapes to _c so the property JS$_c will not exist on the object.
+ expect(js_util.hasProperty(literal, r'JS$_c'), isFalse);
+ expect(js_util.hasProperty(literal, r'JS$class'), isFalse);
+ expect(literal.JS$class, isTrue);
+
+ literal = new ExampleTypedLiteral();
+ expect(js_util.hasProperty(literal, '_c'), isFalse);
+ expect(js_util.hasProperty(literal, 'class'), isFalse);
+
+ literal = new ExampleTypedLiteral(JS$_c: 74);
+ expect(js_util.hasProperty(literal, '_c'), isTrue);
+ expect(literal.JS$_c, equals(74));
+ });
+
+ test('getProperty', () {
+ var literal = new ExampleTypedLiteral(JS$_c: 7, JS$class: true);
+ expect(js_util.getProperty(literal, '_c'), equals(7));
+ expect(literal.JS$_c, equals(7));
+ expect(js_util.getProperty(literal, 'class'), isTrue);
+ expect(js_util.getProperty(literal, r'JS$_c'), isNull);
+ expect(js_util.getProperty(literal, r'JS$class'), isNull);
+ });
+
+ test('setProperty', () {
+ var literal = new ExampleTypedLiteral();
+ literal.JS$class = 42;
+ expect(literal.JS$class, equals(42));
+ js_util.setProperty(literal, 'class', 100);
+ expect(literal.JS$class, equals(100));
+ });
+}
diff --git a/tests/lib_2/js/js_util/jsify_test.dart b/tests/lib_2/js/js_util/jsify_test.dart
new file mode 100644
index 0000000..5ed916d
--- /dev/null
+++ b/tests/lib_2/js/js_util/jsify_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2021, 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.
+
+// Tests the jsify functionality of the js_util library.
+
+@JS()
+library js_util_jsify_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external void eval(String code);
+
+@JS()
+external bool checkMap(m, String, value);
+
+@JS()
+class Foo {
+ external Foo(num a);
+
+ external num get a;
+ external num bar();
+}
+
+main() {
+ eval(r"""
+ function Foo(a) {
+ this.a = a;
+ }
+
+ Foo.prototype.bar = function() {
+ return this.a;
+ }
+
+ function checkMap(m, key, value) {
+ if (m.hasOwnProperty(key))
+ return m[key] == value;
+ else
+ return false;
+ }
+ """);
+
+ test('convert a List', () {
+ final list = [1, 2, 3, 4, 5, 6, 7, 8];
+ var array = js_util.jsify(list);
+ expect(array is List, isTrue);
+ expect(identical(array, list), isFalse);
+ expect(array.length, equals(list.length));
+ for (var i = 0; i < list.length; i++) {
+ expect(array[i], equals(list[i]));
+ }
+ });
+
+ test('convert an Iterable', () {
+ final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]);
+ var array = js_util.jsify(set);
+ expect(array is List, isTrue);
+ expect(array.length, equals(set.length));
+ for (var i = 0; i < array.length; i++) {
+ expect(set.contains(array[i]), isTrue);
+ }
+ });
+
+ test('convert a Map', () {
+ var map = {'a': 1, 'b': 2, 'c': 3};
+ var jsMap = js_util.jsify(map);
+ expect(jsMap is List, isFalse);
+ expect(jsMap is Map, isFalse);
+ for (var key in map.keys) {
+ expect(checkMap(jsMap, key, map[key]), isTrue);
+ }
+ });
+
+ test('deep convert a complex object', () {
+ dynamic object = {
+ 'a': [
+ 1,
+ [2, 3]
+ ],
+ 'b': {'c': 3, 'd': new Foo(42)},
+ 'e': null
+ };
+ var jsObject = js_util.jsify(object);
+ expect(js_util.getProperty(jsObject, 'a')[0], equals(object['a'][0]));
+ expect(js_util.getProperty(jsObject, 'a')[1][0], equals(object['a'][1][0]));
+ expect(js_util.getProperty(jsObject, 'a')[1][1], equals(object['a'][1][1]));
+
+ var b = js_util.getProperty(jsObject, 'b');
+ expect(js_util.getProperty(b, 'c'), equals(object['b']['c']));
+ var d = js_util.getProperty(b, 'd');
+ expect(d, equals(object['b']['d']));
+ expect(js_util.getProperty(d, 'a'), equals(42));
+ expect(js_util.callMethod(d, 'bar', []), equals(42));
+
+ expect(js_util.getProperty(jsObject, 'e'), isNull);
+ });
+
+ test('throws if object is not a Map or Iterable', () {
+ expect(() => js_util.jsify('a'), throwsArgumentError);
+ });
+}
diff --git a/tests/lib_2/js/js_util/properties_test.dart b/tests/lib_2/js/js_util/properties_test.dart
new file mode 100644
index 0000000..986c431
--- /dev/null
+++ b/tests/lib_2/js/js_util/properties_test.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2021, 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.
+
+// Tests the functionality of object properties with the js_util library. For
+// js_util tests with HTML objects see tests/lib/html/js_util_test.dart.
+
+@JS()
+library js_util_properties_test;
+
+import 'package:js/js.dart';
+import 'package:js/js_util.dart' as js_util;
+import 'package:expect/minitest.dart';
+
+@JS()
+external String jsFunction();
+
+@JS()
+external void eval(String code);
+
+@JS('JSON.stringify')
+external String stringify(o);
+
+@JS('ArrayBuffer')
+external get JSArrayBufferType;
+
+@JS()
+class Foo {
+ external Foo(num a);
+
+ external num get a;
+ external num bar();
+ external Object get objectProperty;
+}
+
+@JS('Foo')
+external get JSFooType;
+
+String dartFunction() {
+ return 'Dart Function';
+}
+
+@JS()
+@anonymous
+class ExampleTypedLiteral {
+ external factory ExampleTypedLiteral({a, b});
+
+ external get a;
+ external get b;
+}
+
+String _getBarWithSideEffect() {
+ var x = 5;
+ expect(x, equals(5));
+ return 'bar';
+}
+
+main() {
+ eval(r"""
+ function Foo(a) {
+ this.a = a;
+ }
+
+ Foo.b = 38;
+
+ Foo.prototype.list = [2, 4, 6];
+
+ Foo.prototype.bar = function() {
+ return this.a;
+ }
+
+ Foo.prototype.toString = function() {
+ return "I'm a Foo a=" + this.a;
+ }
+
+ Foo.prototype.fnList = [Foo.prototype.bar, Foo.prototype.toString];
+
+ Foo.prototype.objectProperty = {
+ 'c': 1,
+ 'list': [10, 20, 30],
+ 'functionProperty': function() { return 'Function Property'; }
+ }
+
+ function jsFunction() {
+ return "JS Function";
+ }
+
+ Foo.prototype.nestedFunction = function() {
+ return function() {
+ return 'Nested Function';
+ };
+ }
+
+ Foo.prototype.getFirstEl = function(list) {
+ return list[0];
+ }
+
+ Foo.prototype.sumFn = function(a, b) {
+ return a + b;
+ }
+
+ Foo.prototype.getA = function(obj) {
+ return obj.a;
+ }
+
+ Foo.prototype.callFn = function(fn) {
+ return fn();
+ }
+ """);
+
+ group('newObject', () {
+ test('create', () {
+ expect(identical(js_util.newObject(), js_util.newObject()), isFalse);
+ });
+
+ test('callMethod', () {
+ var o = js_util.newObject();
+ expect(js_util.callMethod(o, 'toString', []), equals('[object Object]'));
+ expect(stringify(o), equals('{}'));
+ });
+
+ test('properties', () {
+ var o = js_util.newObject();
+ expect(js_util.hasProperty(o, 'foo bar'), isFalse);
+ expect(js_util.hasProperty(o, 'toString'), isTrue);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['toString']), isFalse);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isFalse);
+ js_util.setProperty(o, 'foo bar', 42);
+ expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isTrue);
+ expect(js_util.getProperty(o, 'foo bar'), equals(42));
+ expect(js_util.hasProperty(o, 'foo bar'), isTrue);
+ expect(stringify(o), equals('{"foo bar":42}'));
+ });
+
+ test('nested properties calls', () {
+ var o = js_util.newObject();
+ var f = new Foo(42);
+ js_util.setProperty(o, 'foo', f);
+ var foo = js_util.getProperty(o, 'foo');
+ expect(foo, equals(f));
+ expect(js_util.hasProperty(foo, 'a'), isTrue);
+ expect(js_util.getProperty(foo, 'a'), equals(42));
+ js_util.setProperty(foo, 'a', 24);
+ expect(js_util.getProperty(foo, 'a'), equals(24));
+ });
+ });
+
+ group('hasProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.hasProperty(f, 'a'), isTrue);
+ expect(js_util.hasProperty(f, 'bar'), isTrue);
+ expect(js_util.hasProperty(f, 'b'), isFalse);
+ expect(js_util.hasProperty(f, 'toString'), isTrue);
+ js_util.setProperty(f, '__proto__', null);
+ expect(js_util.hasProperty(f, 'toString'), isFalse);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+ expect(js_util.hasProperty(literal, 'a'), isTrue);
+ expect(js_util.hasProperty(literal, 'b'), isTrue);
+ expect(js_util.hasProperty(literal, 'anything'), isFalse);
+
+ literal = new ExampleTypedLiteral(a: null);
+ expect(js_util.hasProperty(literal, 'a'), isTrue);
+ expect(js_util.hasProperty(literal, 'b'), isFalse);
+ expect(js_util.hasProperty(literal, 'anything'), isFalse);
+ });
+
+ test('complex hasProperty calls', () {
+ var f = new Foo(42);
+ expect(js_util.hasProperty(f.objectProperty, 'c'), isTrue);
+ expect(js_util.hasProperty(f.objectProperty, 'nonexistent'), isFalse);
+
+ // Using a variable for the property name.
+ String propertyName = 'bar';
+ expect(js_util.hasProperty(f, propertyName), isTrue);
+ });
+ });
+
+ group('getProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.getProperty(f, 'a'), equals(42));
+ expect(js_util.getProperty(f, 'b'), isNull);
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ expect(js_util.getProperty(f, 'objectProperty') is Object, isTrue);
+ expect(js_util.getProperty(f, 'toString') is Function, isTrue);
+ js_util.setProperty(f, '__proto__', null);
+ expect(js_util.getProperty(f, 'toString'), isNull);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral(a: 'x', b: 42);
+ expect(js_util.getProperty(literal, 'a'), equals('x'));
+ expect(js_util.getProperty(literal, 'b'), equals(42));
+ expect(js_util.getProperty(literal, 'anything'), isNull);
+ });
+
+ test('complex getProperty calls', () {
+ var f = new Foo(42);
+
+ // Accessing a method property.
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals(42));
+
+ // Accessing list properties.
+ expect(js_util.getProperty(f, 'list')[0], equals(2));
+ expect(js_util.getProperty(f, 'fnList')[0] is Function, isTrue);
+ expect(
+ js_util.callMethod(
+ js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]),
+ equals(42));
+
+ // Accessing nested object properites.
+ var objectProperty = js_util.getProperty(f, 'objectProperty');
+ expect(js_util.getProperty(objectProperty, 'c'), equals(1));
+ expect(js_util.getProperty(objectProperty, 'list') is List, isTrue);
+ expect(js_util.getProperty(objectProperty, 'list')[1], equals(20));
+ expect(
+ js_util.getProperty(objectProperty, 'functionProperty') is Function,
+ isTrue);
+
+ // Using a variable for the property name.
+ String propertyName = 'a';
+ expect(js_util.getProperty(f, propertyName), equals(42));
+ String bar = _getBarWithSideEffect();
+ expect(js_util.getProperty(f, bar) is Function, isTrue);
+ expect(js_util.callMethod(f, bar, []), equals(42));
+ });
+ });
+
+ group('setProperty', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.getProperty(f, 'a'), equals(42));
+ js_util.setProperty(f, 'a', 100);
+ expect(f.a, equals(100));
+ expect(js_util.getProperty(f, 'a'), equals(100));
+
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ js_util.setProperty(f, 'list', [8]);
+ expect(js_util.getProperty(f, 'list') is List, isTrue);
+ expect(js_util.getProperty(f, 'list')[0], equals(8));
+
+ js_util.setProperty(f, 'newProperty', 'new');
+ expect(js_util.getProperty(f, 'newProperty'), equals('new'));
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral();
+ js_util.setProperty(literal, 'a', 'foo');
+ expect(js_util.getProperty(literal, 'a'), equals('foo'));
+ expect(literal.a, equals('foo'));
+ js_util.setProperty(literal, 'a', literal);
+ expect(identical(literal.a, literal), isTrue);
+ var list = ['arr'];
+ js_util.setProperty(literal, 'a', list);
+ expect(identical(literal.a, list), isTrue);
+ });
+
+ test('complex setProperty calls', () {
+ var f = new Foo(42);
+
+ // Set function property to a num.
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ js_util.setProperty(f, 'bar', 5);
+ expect(js_util.getProperty(f, 'bar') is Function, isFalse);
+ expect(js_util.getProperty(f, 'bar'), equals(5));
+
+ // Set property to a Dart function.
+ js_util.setProperty(f, 'bar', allowInterop(dartFunction));
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals('Dart Function'));
+ js_util.setProperty(f, 'bar', allowInterop(() {
+ return 'Inline';
+ }));
+ expect(js_util.callMethod(f, 'bar', []), equals('Inline'));
+
+ // Set property to a JS function.
+ js_util.setProperty(f, 'bar', allowInterop(jsFunction));
+ expect(js_util.getProperty(f, 'bar') is Function, isTrue);
+ expect(js_util.callMethod(f, 'bar', []), equals('JS Function'));
+
+ // Set property with nested object properties.
+ js_util.setProperty(f.objectProperty, 'c', 'new val');
+ expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val'));
+ js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]);
+ expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2));
+
+ // Using a variable for the property name.
+ String propertyName = 'bar';
+ js_util.setProperty(f, propertyName, 'foo');
+ expect(js_util.getProperty(f, 'bar'), equals('foo'));
+ String bar = _getBarWithSideEffect();
+ js_util.setProperty(f, bar, 'baz');
+ expect(js_util.getProperty(f, bar), equals('baz'));
+ });
+ });
+
+ group('callMethod', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.callMethod(f, 'bar', []), equals(42));
+ });
+
+ test('complex callMethod calls', () {
+ var f = new Foo(42);
+
+ // Call a method that returns an unbound function.
+ expect(js_util.callMethod(f, 'nestedFunction', []) is Function, isTrue);
+ expect(js_util.callMethod(f, 'nestedFunction', [])(),
+ equals('Nested Function'));
+
+ // Call method on a nested function property.
+ expect(js_util.callMethod(f.objectProperty, 'functionProperty', []),
+ equals('Function Property'));
+
+ // Call method with different args.
+ expect(
+ js_util.callMethod(f, 'getFirstEl', [
+ [25, 50]
+ ]),
+ equals(25));
+ expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5));
+ expect(js_util.callMethod(f, 'getA', [f]), equals(42));
+ expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]),
+ equals("JS Function"));
+ expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]),
+ equals("Dart Function"));
+ expect(
+ js_util.callMethod(f, 'callFn', [
+ allowInterop(() {
+ return "inline";
+ })
+ ]),
+ equals("inline"));
+
+ // Using a variable for the method name.
+ String methodName = 'bar';
+ expect(js_util.callMethod(f, methodName, []), equals(42));
+ String bar = _getBarWithSideEffect();
+ expect(js_util.callMethod(f, bar, []), equals(42));
+ });
+ });
+
+ group('instanceof', () {
+ test('typed object', () {
+ var f = new Foo(42);
+ expect(js_util.instanceof(f, JSFooType), isTrue);
+ expect(js_util.instanceof(f, JSArrayBufferType), isFalse);
+ });
+
+ test('typed literal', () {
+ var literal = new ExampleTypedLiteral();
+ expect(js_util.instanceof(literal, JSFooType), isFalse);
+ });
+ });
+
+ group('callConstructor', () {
+ test('typed object', () {
+ Foo f = js_util.callConstructor(JSFooType, [42]);
+ expect(f.a, equals(42));
+ });
+ });
+}
diff --git a/tests/lib_2/lib_2_dartdevc.status b/tests/lib_2/lib_2_dartdevc.status
index d6a6dd7..1b058f5 100644
--- a/tests/lib_2/lib_2_dartdevc.status
+++ b/tests/lib_2/lib_2_dartdevc.status
@@ -37,4 +37,5 @@
html/custom_elements_test: Skip # Issue 29922
html/notification_permission_test: Skip # Issue 32002
isolate/*: SkipByDesign # No support for dart:isolate in dart4web (http://dartbug.com/30538)
+js/js_util/js_prefix_test: SkipByDesign # JS$ prefix not implemented on ddc.
mirrors/*: SkipByDesign # Mirrors not supported on web in Dart 2.0.
diff --git a/tools/VERSION b/tools/VERSION
index 2a1c088..c50233a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 70
+PRERELEASE 71
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index e1800a4..ae306a9 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2068,13 +2068,6 @@
]
},
{
- "name": "check dart nnbd sdk for static errors",
- "script": "out/ReleaseX64/dart-sdk/bin/dart",
- "arguments": [
- "pkg/dev_compiler/tool/check_nnbd_sdk.dart"
- ]
- },
- {
"name": "ddc weak modular tests",
"script": "out/ReleaseX64/dart-sdk/bin/dart",
"testRunner": true,
@@ -2621,15 +2614,6 @@
]
},
{
- "name": "check dart nnbd sdk for static errors",
- "script": "out/ReleaseX64/dart-sdk/bin/dart",
- "arguments": [
- "pkg/dev_compiler/tool/check_nnbd_sdk.dart",
- "--target",
- "dart2js"
- ]
- },
- {
"name": "dart2js nnbd weak d8 tests",
"arguments": [
"-ndart2js-hostasserts-weak-linux-x64-d8",
@@ -2953,22 +2937,6 @@
"-nanalyzer-asserts-${system}",
"co19_2"
]
- },
- {
- "name": "check nnbd sdk (ddc patch) for static errors",
- "script": "out/ReleaseX64/dart-sdk/bin/dart",
- "arguments": [
- "pkg/dev_compiler/tool/check_nnbd_sdk.dart"
- ]
- },
- {
- "name": "check nnbd sdk (dart2js patch) for static errors",
- "script": "out/ReleaseX64/dart-sdk/bin/dart",
- "arguments": [
- "pkg/dev_compiler/tool/check_nnbd_sdk.dart",
- "--target",
- "dart2js"
- ]
}
]
},