Version 2.13.0-119.0.dev
Merge commit '0ac716d098791ff9a8893f3ffce44a08439e90f2' into 'dev'
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
index ef38ab3..dcaffb5 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_ne_null.dart
@@ -4,6 +4,9 @@
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
+import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -16,6 +19,13 @@
@override
Future<void> compute(ChangeBuilder builder) async {
+ if (unit.featureSet.isEnabled(Feature.non_nullable)) {
+ var node = this.node;
+ if (node is Expression &&
+ node.staticType?.nullabilitySuffix == NullabilitySuffix.none) {
+ return;
+ }
+ }
var problemMessage = diagnostic.problemMessage;
await builder.addDartFileEdit(file, (builder) {
builder.addSimpleInsertion(
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
index fe07091..0d52849 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_ne_null_test.dart
@@ -7,6 +7,7 @@
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import '../../../../abstract_context.dart';
import 'fix_processor.dart';
void main() {
@@ -17,13 +18,11 @@
}
@reflectiveTest
-class AddNeNullMultiTest extends FixProcessorTest {
+class AddNeNullMultiTest extends FixProcessorTest with WithNullSafetyMixin {
@override
FixKind get kind => DartFixKind.ADD_NE_NULL_MULTI;
- // todo (pq): add null-safe aware tests
- // see: https://dart-review.googlesource.com/c/sdk/+/188681
- Future<void> test_nonBoolCondition_all() async {
+ Future<void> test_nonBoolCondition_all_nonNullable() async {
await resolveTestCode('''
f(String p, String q) {
if (p) {
@@ -34,8 +33,22 @@
}
}
''');
+ await assertNoFixAllFix(CompileTimeErrorCode.NON_BOOL_CONDITION);
+ }
+
+ Future<void> test_nonBoolCondition_all_nullable() async {
+ await resolveTestCode('''
+f(String? p, String? q) {
+ if (p) {
+ print(p);
+ }
+ if (q) {
+ print(q);
+ }
+}
+''');
await assertHasFixAllFix(CompileTimeErrorCode.NON_BOOL_CONDITION, '''
-f(String p, String q) {
+f(String? p, String? q) {
if (p != null) {
print(p);
}
@@ -48,11 +61,11 @@
}
@reflectiveTest
-class AddNeNullTest extends FixProcessorTest {
+class AddNeNullTest extends FixProcessorTest with WithNullSafetyMixin {
@override
FixKind get kind => DartFixKind.ADD_NE_NULL;
- Future<void> test_nonBoolCondition() async {
+ Future<void> test_nonBoolCondition_nonNullable() async {
await resolveTestCode('''
f(String p) {
if (p) {
@@ -60,8 +73,19 @@
}
}
''');
+ await assertNoFix();
+ }
+
+ Future<void> test_nonBoolCondition_nullable() async {
+ await resolveTestCode('''
+f(String? p) {
+ if (p) {
+ print(p);
+ }
+}
+''');
await assertHasFix('''
-f(String p) {
+f(String? p) {
if (p != null) {
print(p);
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
index 67e4ffe..7ca8451 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_processor.dart
@@ -279,6 +279,11 @@
await _assertNoFix(error);
}
+ Future<void> assertNoFixAllFix(ErrorCode errorCode) async {
+ var error = await _findErrorToFixOfType(errorCode);
+ await _assertNoFixAllFix(error);
+ }
+
List<LinkedEditSuggestion> expectedSuggestions(
LinkedEditSuggestionKind kind, List<String> values) {
return values.map((value) {
@@ -383,6 +388,19 @@
}
}
+ Future<void> _assertNoFixAllFix(AnalysisError error) async {
+ if (!kind.canBeAppliedTogether()) {
+ fail('Expected to find and return fix-all FixKind for $kind, '
+ 'but kind.canBeAppliedTogether is ${kind.canBeAppliedTogether}');
+ }
+ var fixes = await _computeFixes(error);
+ for (var fix in fixes) {
+ if (fix.kind == kind && fix.isFixAllFix()) {
+ fail('Unexpected fix $kind in\n${fixes.join('\n')}');
+ }
+ }
+ }
+
/// Computes fixes for the given [error] in [testUnit].
Future<List<Fix>> _computeFixes(AnalysisError error) async {
var analysisContext = contextFor(testFile);
diff --git a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
index 0193a85..6c8865c 100644
--- a/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/micro/analysis_context.dart
@@ -7,6 +7,7 @@
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/uri_converter.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/context/context.dart';
import 'package:analyzer/src/dart/analysis/context_root.dart';
@@ -178,6 +179,11 @@
}
@override
+ Future<LibraryElement> getLibraryByUri(String uriStr) async {
+ return analysisContext.fileResolver.getLibraryByUri(uriStr: uriStr);
+ }
+
+ @override
Future<ResolvedLibraryResult> getResolvedLibrary(String path) async {
var resolvedUnit = await getResolvedUnit(path);
return ResolvedLibraryResultImpl(
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index 583f799..14c858c 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -5,6 +5,7 @@
import 'dart:typed_data';
import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
@@ -256,6 +257,39 @@
});
}
+ LibraryElement getLibraryByUri({
+ required String uriStr,
+ OperationPerformanceImpl? performance,
+ }) {
+ performance ??= OperationPerformanceImpl('<default>');
+
+ var uri = Uri.parse(uriStr);
+ var path = sourceFactory.forUri2(uri)?.fullName;
+
+ if (path == null) {
+ throw ArgumentError('$uri cannot be resolved to a file.');
+ }
+
+ var fileContext = getFileContext(
+ path: path,
+ performance: performance,
+ );
+ var file = fileContext.file;
+
+ if (file.partOfLibrary != null) {
+ throw ArgumentError('$uri is not a library.');
+ }
+
+ performance.run('libraryContext', (performance) {
+ libraryContext!.load2(
+ targetLibrary: file,
+ performance: performance,
+ );
+ });
+
+ return libraryContext!.elementFactory.libraryOfUri2(uriStr);
+ }
+
String getLibraryLinkedSignature({
required String path,
required OperationPerformanceImpl performance,
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 8709a1f..2b84c88 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -438,6 +438,42 @@
expect(fileResolver.testView!.resolvedFiles, <Object>[]);
}
+ test_getLibraryByUri() {
+ newFile('/workspace/dart/my/lib/a.dart', content: r'''
+class A {}
+''');
+
+ var element = fileResolver.getLibraryByUri(
+ uriStr: 'package:dart.my/a.dart',
+ );
+ expect(element.definingCompilationUnit.types, hasLength(1));
+ }
+
+ test_getLibraryByUri_notExistingFile() {
+ var element = fileResolver.getLibraryByUri(
+ uriStr: 'package:dart.my/a.dart',
+ );
+ expect(element.definingCompilationUnit.types, isEmpty);
+ }
+
+ test_getLibraryByUri_partOf() {
+ newFile('/workspace/dart/my/lib/a.dart', content: r'''
+part of 'b.dart';
+''');
+
+ expect(() {
+ fileResolver.getLibraryByUri(
+ uriStr: 'package:dart.my/a.dart',
+ );
+ }, throwsArgumentError);
+ }
+
+ test_getLibraryByUri_unresolvedUri() {
+ expect(() {
+ fileResolver.getLibraryByUri(uriStr: 'my:unresolved');
+ }, throwsArgumentError);
+ }
+
test_hint() async {
await assertErrorsInCode(r'''
import 'dart:math';
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index a78e3bfc4..2a65eb6 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -865,6 +865,7 @@
Map<ImportEntity, int> importMap = {};
var entities = _deferredImportDescriptions.keys.toList();
entities.sort(_compareImportEntities);
+ entities = entities.reversed.toList();
for (var key in entities) {
importMap[key] = id++;
}
diff --git a/pkg/compiler/test/deferred/load_mapping_test.dart b/pkg/compiler/test/deferred/load_mapping_test.dart
index c158dc4..b6a6821 100644
--- a/pkg/compiler/test/deferred/load_mapping_test.dart
+++ b/pkg/compiler/test/deferred/load_mapping_test.dart
@@ -50,7 +50,9 @@
// result we expect to have an 6 output files:
// * one for code that is only use by an individual deferred import, and
// * an extra one for the intersection of lib1 and lib2.
- var expected = ['10000', '01000', '00100', '00010', '00001', '01100'];
+ var expected = ['10000', '01000', '00100', '00010', '00001', '00110'];
+ expected.sort();
+ actual.sort();
Expect.listEquals(expected, actual);
}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart
new file mode 100644
index 0000000..91a0c15
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib1.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_100_0.dart' deferred as b1;
+
+entryLib1() async {
+ await b1.loadLibrary();
+ b1.g_100_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart
new file mode 100644
index 0000000..37a0a7c
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib2.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_010_0.dart' deferred as b2;
+
+entryLib2() async {
+ await b2.loadLibrary();
+ b2.g_010_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart
new file mode 100644
index 0000000..597682a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib3.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_001_0.dart' deferred as b3;
+
+entryLib3() async {
+ await b3.loadLibrary();
+ b3.g_001_0();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart
new file mode 100644
index 0000000..41a32f6
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib4.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_000_1.dart' deferred as b4;
+
+entryLib4() async {
+ await b4.loadLibrary();
+ b4.g_000_1();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart
new file mode 100644
index 0000000..5731c13
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/libImport.dart
@@ -0,0 +1,31 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+void v(Set<String> u, String name, int bit) {
+ Expect.isTrue(u.add(name));
+ Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+f_100_0(Set<String> u, int b) => v(u, '1000', b);
+@pragma('dart2js:noInline')
+f_110_0(Set<String> u, int b) => v(u, '1100', b);
+@pragma('dart2js:noInline')
+f_101_1(Set<String> u, int b) => v(u, '1011', b);
+@pragma('dart2js:noInline')
+f_111_1(Set<String> u, int b) => v(u, '1111', b);
+@pragma('dart2js:noInline')
+f_010_0(Set<String> u, int b) => v(u, '0100', b);
+@pragma('dart2js:noInline')
+f_010_1(Set<String> u, int b) => v(u, '0101', b);
+@pragma('dart2js:noInline')
+f_011_1(Set<String> u, int b) => v(u, '0111', b);
+@pragma('dart2js:noInline')
+f_001_0(Set<String> u, int b) => v(u, '0010', b);
+@pragma('dart2js:noInline')
+f_000_1(Set<String> u, int b) => v(u, '0001', b);
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart
new file mode 100644
index 0000000..37a1c2e
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_000_1.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_000_1() {
+ Set<String> uniques = {};
+
+ // f_***_1;
+ f_000_1(uniques, 3);
+ f_010_1(uniques, 3);
+ f_011_1(uniques, 3);
+ f_101_1(uniques, 3);
+ f_111_1(uniques, 3);
+ Expect.equals(5, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart
new file mode 100644
index 0000000..3534e43
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_001_0.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_001_0() {
+ Set<String> uniques = {};
+
+ // f_**1_*;
+ f_001_0(uniques, 2);
+ f_011_1(uniques, 2);
+ f_101_1(uniques, 2);
+ f_111_1(uniques, 2);
+ Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart
new file mode 100644
index 0000000..d9ef060
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_010_0.dart
@@ -0,0 +1,22 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_010_0() {
+ Set<String> uniques = {};
+
+ // f_*1*_*;
+ f_010_0(uniques, 1);
+ f_010_1(uniques, 1);
+ f_011_1(uniques, 1);
+ f_110_0(uniques, 1);
+ f_111_1(uniques, 1);
+ Expect.equals(5, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart
new file mode 100644
index 0000000..6d2c07f
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/lib_100_0.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_100_0() {
+ Set<String> uniques = {};
+
+ // f_1**_*;
+ f_100_0(uniques, 0);
+ f_101_1(uniques, 0);
+ f_110_0(uniques, 0);
+ f_111_1(uniques, 0);
+ Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart
new file mode 100644
index 0000000..fb21cef
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/less_simple/main.dart
@@ -0,0 +1,17 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+import 'lib4.dart';
+
+main() {
+ entryLib1();
+ entryLib2();
+ entryLib3();
+ entryLib4();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart
new file mode 100644
index 0000000..c664b0c
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib1.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_100.dart' deferred as b1;
+
+entryLib1() async {
+ await b1.loadLibrary();
+ b1.g_100();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart
new file mode 100644
index 0000000..6741292
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib2.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_010.dart' deferred as b2;
+
+entryLib2() async {
+ await b2.loadLibrary();
+ b2.g_010();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart
new file mode 100644
index 0000000..2133ef7
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib3.dart
@@ -0,0 +1,12 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib_001.dart' deferred as b3;
+
+entryLib3() async {
+ await b3.loadLibrary();
+ b3.g_001();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart
new file mode 100644
index 0000000..8d1058b
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/libImport.dart
@@ -0,0 +1,25 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+void v(Set<String> u, String name, int bit) {
+ Expect.isTrue(u.add(name));
+ Expect.equals(name[bit], '1');
+}
+
+@pragma('dart2js:noInline')
+f_100(Set<String> u, int b) => v(u, '100', b);
+@pragma('dart2js:noInline')
+f_101(Set<String> u, int b) => v(u, '101', b);
+@pragma('dart2js:noInline')
+f_111(Set<String> u, int b) => v(u, '111', b);
+@pragma('dart2js:noInline')
+f_010(Set<String> u, int b) => v(u, '010', b);
+@pragma('dart2js:noInline')
+f_011(Set<String> u, int b) => v(u, '011', b);
+@pragma('dart2js:noInline')
+f_001(Set<String> u, int b) => v(u, '001', b);
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart
new file mode 100644
index 0000000..16c561a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_001.dart
@@ -0,0 +1,21 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_001() {
+ Set<String> uniques = {};
+
+ // f_**1;
+ f_001(uniques, 2);
+ f_011(uniques, 2);
+ f_101(uniques, 2);
+ f_111(uniques, 2);
+ Expect.equals(4, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart
new file mode 100644
index 0000000..3ac623a
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_010.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_010() {
+ Set<String> uniques = {};
+
+ // f_*1*;
+ f_010(uniques, 1);
+ f_011(uniques, 1);
+ f_111(uniques, 1);
+ Expect.equals(3, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart
new file mode 100644
index 0000000..631b02b
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/lib_100.dart
@@ -0,0 +1,20 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import "package:expect/expect.dart";
+
+import 'libImport.dart';
+
+@pragma('dart2js:noInline')
+g_100() {
+ Set<String> uniques = {};
+
+ // f_1**;
+ f_100(uniques, 0);
+ f_101(uniques, 0);
+ f_111(uniques, 0);
+ Expect.equals(3, uniques.length);
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart
new file mode 100644
index 0000000..ef1a426
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/golden/simple/main.dart
@@ -0,0 +1,15 @@
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+import 'lib1.dart';
+import 'lib2.dart';
+import 'lib3.dart';
+
+main() {
+ entryLib1();
+ entryLib2();
+ entryLib3();
+}
diff --git a/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart b/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart
new file mode 100644
index 0000000..eda8526
--- /dev/null
+++ b/pkg/compiler/test/tool/graph_isomorphizer/graph_isomorphizer_test.dart
@@ -0,0 +1,130 @@
+// 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.
+
+import 'dart:io';
+
+import 'package:expect/expect.dart';
+import 'package:compiler/compiler_new.dart';
+import '../../helpers/memory_compiler.dart';
+import '../../../tool/graph_isomorphizer.dart';
+
+/// Only generate goldens from the root sdk directory.
+const String goldenDirectory =
+ 'pkg/compiler/test/tool/graph_isomorphizer/golden';
+
+/// A map of folder name to graph file lines. When adding new tests, its
+/// probably best to get the ordering of these lines from the compiler.
+const Map<String, List<String>> testCases = {
+ 'simple': ['100', '010', '001', '101', '011', '111'],
+ 'less_simple': [
+ '1000',
+ '0100',
+ '0010',
+ '0001',
+ '1100',
+ '0101',
+ '1011',
+ '0111',
+ '1111'
+ ]
+};
+
+void unorderedListEquals(List<String> expected, List<String> actual) {
+ var sortedExpected = expected.toList();
+ var sortedActual = actual.toList();
+ sortedExpected.sort();
+ sortedActual.sort();
+ Expect.listEquals(sortedExpected, sortedActual);
+}
+
+void verifyGeneratedFile(
+ String filename, StringBuffer contents, Map<String, String> expectations) {
+ Expect.stringEquals(contents.toString(), expectations[filename]);
+}
+
+GraphIsomorphizer generateFiles(List<String> graphFileLines,
+ {String outDirectory: '.'}) {
+ Map<int, List<List<int>>> names = {};
+ int maxBit = namesFromGraphFileLines(graphFileLines, names);
+ var graphIsomorphizer =
+ GraphIsomorphizer(names, maxBit, outDirectory: outDirectory);
+ graphIsomorphizer.generateFiles();
+ return graphIsomorphizer;
+}
+
+/// Tests isomorphicity of the code generated by the GraphIsomorphizer.
+void verifyGraphFileLines(
+ List<String> graphFileLines, Map<String, String> expectations) async {
+ // Generate files.
+ var graphIsomorphizer = generateFiles(graphFileLines);
+
+ // Verify generated files.
+ verifyGeneratedFile(graphIsomorphizer.mainFilename,
+ graphIsomorphizer.mainBuffer, expectations);
+ verifyGeneratedFile(graphIsomorphizer.rootImportFilename,
+ graphIsomorphizer.rootImportBuffer, expectations);
+ graphIsomorphizer.entryLibBuffers.forEach((filename, contents) {
+ verifyGeneratedFile(filename, contents, expectations);
+ });
+ graphIsomorphizer.mixerLibBuffers.forEach((filename, contents) {
+ verifyGeneratedFile(filename, contents, expectations);
+ });
+
+ // Compile generated code and dump deferred graph shape.
+ var collector = new OutputCollector();
+ await runCompiler(
+ memorySourceFiles: expectations,
+ options: ['--dump-deferred-graph=deferred_graph.txt'],
+ outputProvider: collector);
+ var actual =
+ collector.getOutput("deferred_graph.txt", OutputType.debug).split('\n');
+
+ // Confirm new graph is isomorphic.
+ unorderedListEquals(graphFileLines, actual);
+}
+
+void generateGoldens() {
+ testCases.forEach((name, test) {
+ var graphIsomorphizer =
+ generateFiles(test, outDirectory: goldenDirectory + '/' + name);
+ graphIsomorphizer.writeFiles();
+ });
+}
+
+String getFilename(String path) {
+ var lastSlash = path.lastIndexOf('/');
+ return path.substring(lastSlash + 1, path.length);
+}
+
+void verifyGoldens() {
+ testCases.forEach((name, test) {
+ Map<String, String> expectations = {};
+ var golden = Directory(goldenDirectory + '/' + name);
+ var files = golden.listSync();
+ for (var file in files) {
+ assert(file is File);
+ var contents = (file as File).readAsStringSync();
+ var filename = getFilename(file.path);
+ expectations[filename] = contents;
+ }
+ verifyGraphFileLines(test, expectations);
+ });
+}
+
+void main(List<String> args) {
+ bool generate = false;
+ for (var arg in args) {
+ if (arg == '-g') {
+ generate = true;
+ }
+ }
+
+ if (generate) {
+ generateGoldens();
+ } else {
+ verifyGoldens();
+ }
+}
diff --git a/pkg/compiler/tool/graph_isomorphizer.dart b/pkg/compiler/tool/graph_isomorphizer.dart
new file mode 100644
index 0000000..dca1cf1
--- /dev/null
+++ b/pkg/compiler/tool/graph_isomorphizer.dart
@@ -0,0 +1,355 @@
+// 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.
+
+/// This tool builds a program with a deferred graph isomorphic to the provided
+/// graph, or generates permutations of bits and the associated files to
+/// generate complex deferred graphs.
+
+/// For example, if 5 bits are permuted, we end up with files like:
+/// lib1.dart
+/// lib2.dart
+/// lib3.dart
+/// lib4.dart
+/// lib5.dart
+/// libB.dart
+/// lib_000_01.dart
+/// lib_000_10.dart
+/// lib_001_00.dart
+/// lib_010_00.dart
+/// lib_100_00.dart
+/// main.dart
+///
+/// Where
+/// main.dart contains main().
+/// libX.dart contains the actual deferred import of the file with the bit at
+/// the X position starting from the left, ie lib1 imports lib_100_00, lib2
+/// imports lib_010_00, etc.
+/// libImport.dart is the 'top of the diamond' which contains all of the code.
+/// lib_XXX_XX.dart invokes all of the functions in libImport which have a
+/// 1 bit at that position, ie lib_100_00 invokes all code in libImport with
+/// a first bit of 1, f100_00, f110_00, etc.
+///
+/// Note: There are restrictions to what we can generate. Specifically, certain
+/// OutputUnits can never be empty, namely we will always generate one file for
+/// each entryLib, and because of our dependency on expect, we will always have
+/// a file representing the intersection of all import entities. So, for example
+/// with three bits, each of 100, 010, 001 and 111 must be present in the graph
+/// file, but 110, 101, and 011 are optional.
+
+import 'dart:io';
+import 'dart:math';
+
+typedef NameFunc = String Function(List<int>, int);
+
+/// A simple constant pass through function for bit strings that don't need
+/// special printing, ie stops.
+String passThroughNameFunc(List<int> l, int i) => l[i].toString();
+
+/// Generates all permutations of bits recursively.
+void generatePermutedNames(
+ Map<int, List<List<int>>> names, int maxBit, List<int> bits, int bit) {
+ if (bit == maxBit) {
+ for (int i = 0; i < bits.length; i++) {
+ if (bits[i] == 1) {
+ names.putIfAbsent(i, () => []);
+ names[i].add(List.from(bits));
+ }
+ }
+ return;
+ }
+ generatePermutedNames(names, maxBit, bits, bit + 1);
+ bits[bit] = 1;
+ generatePermutedNames(names, maxBit, bits, bit + 1);
+ bits[bit] = 0;
+}
+
+/// A helper function to generate names from lists of strings of bits.
+int namesFromGraphFileLines(
+ List<String> lines, Map<int, List<List<int>>> names) {
+ int maxBit = 0;
+ for (var line in lines) {
+ List<int> name = [];
+ // Each line should have the same length.
+ assert(maxBit == 0 || maxBit == line.length);
+ maxBit = max(maxBit, line.length);
+ for (int i = 0; i < line.length; i++) {
+ var bit = line[i];
+ if (bit == '1') {
+ name.add(1);
+ (names[i] ??= []).add(name);
+ } else {
+ name.add(0);
+ }
+ }
+ }
+ return maxBit;
+}
+
+/// Parses names from a graph file dumped from dart2js and returns the max bit.
+int namesFromGraphFile(String graphFile, Map<int, List<List<int>>> names) {
+ var lines = File(graphFile).readAsLinesSync();
+ return namesFromGraphFileLines(lines, names);
+}
+
+class ImportData {
+ String import;
+ String entryPoint;
+
+ ImportData(this.import, this.entryPoint);
+}
+
+class GraphIsomorphizer {
+ /// The output directory, only relevant if files are written out.
+ final String outDirectory;
+
+ /// Various buffers for the files the GraphIsomorphizer generates.
+ StringBuffer rootImportBuffer = StringBuffer();
+ StringBuffer mainBuffer = StringBuffer();
+ Map<String, StringBuffer> mixerLibBuffers = {};
+ Map<String, StringBuffer> entryLibBuffers = {};
+
+ /// A map of bit positions to lists of bit lists.
+ final Map<int, List<List<int>>> names;
+
+ /// We will permute bits up until the maximum bit.
+ int maxBit = 0;
+
+ /// The 'top of the diamond' import file containing all code.
+ final String rootImportFilename = 'libImport.dart';
+
+ /// The main filename.
+ final String mainFilename = 'main.dart';
+
+ /// A bool to omit the comment block.
+ final bool skipCopyright;
+
+ GraphIsomorphizer(this.names, this.maxBit,
+ {this.outDirectory: '.', this.skipCopyright: false});
+
+ void noInlineDecorator(StringBuffer out) {
+ out.write("@pragma('dart2js:noInline')\n");
+ }
+
+ void importExpect(StringBuffer out) {
+ out.write('import "package:expect/expect.dart";\n\n');
+ }
+
+ /// Generates the header for a file.
+ void generateHeader(StringBuffer out) {
+ if (!skipCopyright) {
+ out.write("""
+// 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.
+
+// This file was autogenerated by the pkg/compiler/tool/graph_isomorphizer.dart.
+
+""");
+ }
+ }
+
+ void generateRootImport(StringBuffer out) {
+ generateHeader(out);
+ importExpect(out);
+
+ // We verify that each function in libImport is invoked only once from each
+ // mixerLib and that only the correct functions are called, ie for lib_001,
+ // only functions with XX1 are invoked.
+ out.write('void v(Set<String> u, String name, int bit) {\n' +
+ ' Expect.isTrue(u.add(name));\n' +
+ " Expect.equals(name[bit], '1');\n" +
+ '}\n\n');
+
+ Set<String> uniques = {};
+
+ // Sort the names to ensure they are in a canonical order.
+ var nameKeys = names.keys.toList();
+ nameKeys.sort();
+ for (var name in nameKeys) {
+ var bitsList = names[name];
+ for (var bits in bitsList) {
+ var name = generateBitString(bits);
+ if (uniques.add(name)) {
+ noInlineDecorator(out);
+ var stringBits = generateBitString(bits, withStops: false);
+ out.write(
+ "f$name(Set<String> u, int b) => v(u, '$stringBits', b);\n");
+ }
+ }
+ }
+ }
+
+ /// Generates a mixerLib which will be loaded as a deferred library from an entryLib.
+ void generateMixerLib(
+ String name, StringBuffer out, String import, List<int> bits, int bit) {
+ generateHeader(out);
+ importExpect(out);
+ out.write("import '$import';\n\n");
+ noInlineDecorator(out);
+ out.write('g$name() {\n'
+ ' Set<String> uniques = {};\n\n'
+ ' // f${generateCommentName(bits, bit)};\n');
+ int count = 0;
+
+ // Collect the names so we can sort them and put them in a canonical order.
+ List<String> namesBits = [];
+ names[bit].forEach((nameBits) {
+ var nameString = generateBitString(nameBits);
+ namesBits.add(nameString);
+ count++;
+ });
+
+ namesBits.sort();
+ for (var name in namesBits) {
+ out.write(' f$name(uniques, $bit);\n');
+ }
+
+ // We expect 'count' unique strings added to be added to 'uniques'.
+ out.write(" Expect.equals($count, uniques.length);\n"
+ '}\n');
+ }
+
+ /// Generates a string of bits, with optional parameters to control how the
+ /// bits print.
+ String generateBitString(List<int> bits,
+ {NameFunc f: passThroughNameFunc, bool withStops: true}) {
+ int stop = 0;
+ StringBuffer sb = StringBuffer();
+ for (int i = 0; i < bits.length; i++) {
+ if (stop++ % 3 == 0 && withStops) {
+ sb.write('_');
+ }
+ sb.write(f(bits, i));
+ }
+ return sb.toString();
+ }
+
+ /// Generates a pretty bit string for use in comments.
+ String generateCommentName(List<int> bits, int fixBit) {
+ return generateBitString(bits,
+ f: (List<int> bits, int bit) => bit == fixBit ? '1' : '*');
+ }
+
+ /// Generates an entryLib file.
+ void generateEntryLib(StringBuffer out, String mainName, String funcName,
+ String import, int bit) {
+ generateHeader(out);
+ var name = 'b$bit';
+ out.write("import '$import' deferred as $name;\n\n"
+ '$mainName async {\n'
+ ' await $name.loadLibrary();\n'
+ ' $name.g$funcName();\n'
+ '}\n');
+ }
+
+ /// Generates entry and mixer libs for the supplied names.
+ List<ImportData> generateEntryAndMixerLibs() {
+ // Generates each lib_XXX.dart and the associated entryLib file.
+ List<ImportData> importData = [];
+ for (int i = 1; i <= maxBit; i++) {
+ // Generate the bit list representing this library. ie a list of all
+ // 0s with a single 1 bit flipped.
+ int oneBit = i - 1;
+ List<int> bits = [];
+ for (int j = 0; j < maxBit; j++) bits.add(j == oneBit ? 1 : 0);
+
+ // Generate the mixerLib for this entryLib.
+ var name = generateBitString(bits);
+ var mixerLibBuffer = StringBuffer();
+ var mixerLibName = "lib$name.dart";
+ generateMixerLib(name, mixerLibBuffer, rootImportFilename, bits, oneBit);
+ mixerLibBuffers[mixerLibName] = mixerLibBuffer;
+
+ // Generate the entryLib for this mixerLib.
+ var entryLibName = 'lib$i.dart';
+ var entryFuncName = 'entryLib$i()';
+ var entryLibBuffer = StringBuffer();
+ generateEntryLib(entryLibBuffer, entryFuncName, name, mixerLibName, i);
+ entryLibBuffers[entryLibName] = entryLibBuffer;
+
+ // Stash the entry point in entryLib for later reference in the main file.
+ importData.add(ImportData(entryLibName, entryFuncName));
+ }
+ return importData;
+ }
+
+ /// Generates the main file.
+ void generateMain(StringBuffer out, List<ImportData> importData) {
+ generateHeader(mainBuffer);
+ for (var data in importData) {
+ out.write("import '${data.import}';\n");
+ }
+ out.write('\n'
+ 'main() {\n');
+ for (var data in importData) {
+ out.write(' ${data.entryPoint};\n');
+ }
+ out.write('}\n');
+ }
+
+ /// Generates all files into buffers.
+ void generateFiles() {
+ generateRootImport(rootImportBuffer);
+ var importData = generateEntryAndMixerLibs();
+ generateMain(mainBuffer, importData);
+ }
+
+ /// Helper to dump contents to file.
+ void writeToFile(String filename, StringBuffer contents) {
+ var file = File(this.outDirectory + '/' + filename);
+ file.createSync(recursive: true);
+ var sink = file.openWrite();
+ sink.write(contents);
+ sink.close();
+ }
+
+ /// Writes all buffers to files.
+ void writeFiles() {
+ mixerLibBuffers.forEach(writeToFile);
+ entryLibBuffers.forEach(writeToFile);
+ writeToFile(rootImportFilename, rootImportBuffer);
+ writeToFile(mainFilename, mainBuffer);
+ }
+
+ /// Generate and write files.
+ void run() {
+ generateFiles();
+ writeFiles();
+ }
+}
+
+/// Creates a GraphIsomorphizer based on the provided args.
+GraphIsomorphizer createGraphIsomorphizer(List<String> args) {
+ int maxBit = 0;
+ String graphFile = '';
+ String outDirectory = '.';
+
+ for (var arg in args) {
+ if (arg.startsWith('--max-bit')) {
+ maxBit = int.parse(arg.substring('--max-bit='.length));
+ }
+ if (arg.startsWith('--graph-file')) {
+ graphFile = arg.substring('--graph-file='.length);
+ }
+ if (arg.startsWith('--out-dir')) {
+ outDirectory = arg.substring('--out-dir='.length);
+ }
+ }
+
+ // If we don't have a graphFile, then we generate all permutations of bits up
+ // to maxBit.
+ Map<int, List<List<int>>> names = {};
+ if (graphFile.isEmpty) {
+ List<int> bits = List.filled(maxBit, 0);
+ generatePermutedNames(names, maxBit, bits, 0);
+ } else {
+ maxBit = namesFromGraphFile(graphFile, names);
+ }
+ return GraphIsomorphizer(names, maxBit, outDirectory: outDirectory);
+}
+
+void main(List<String> args) {
+ var graphIsomorphizer = createGraphIsomorphizer(args);
+ graphIsomorphizer.run();
+}
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 5b0237b..7c9aad9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -991,7 +991,12 @@
Constant execute(Statement statement) {
StatementConstantEvaluator statementEvaluator =
new StatementConstantEvaluator(this);
- return statement.accept(statementEvaluator);
+ ExecutionStatus status = statement.accept(statementEvaluator);
+ if (status is ReturnStatus) {
+ return status.value;
+ }
+ return createInvalidExpressionConstant(statement,
+ 'No valid constant returned from the execution of $statement.');
}
/// Create an error-constant indicating that an error has been detected during
@@ -2377,15 +2382,21 @@
// TODO(kustermann): The heuristic of allowing all [VariableGet]s on [Let]
// variables might allow more than it should.
final VariableDeclaration variable = node.variable;
- if (variable.parent is Let || _isFormalParameter(variable)) {
- return env.lookupVariable(node.variable) ??
- createErrorConstant(
- node,
- templateConstEvalNonConstantVariableGet
- .withArguments(variable.name));
- }
- if (variable.isConst) {
- return _evaluateSubexpression(variable.initializer);
+ if (enableConstFunctions) {
+ return env.lookupVariable(variable) ??
+ createInvalidExpressionConstant(
+ node, 'Variable get of an unknown value.');
+ } else {
+ if (variable.parent is Let || _isFormalParameter(variable)) {
+ return env.lookupVariable(node.variable) ??
+ createErrorConstant(
+ node,
+ templateConstEvalNonConstantVariableGet
+ .withArguments(variable.name));
+ }
+ if (variable.isConst) {
+ return _evaluateSubexpression(variable.initializer);
+ }
}
return createInvalidExpressionConstant(
node, 'Variable get of a non-const variable.');
@@ -3189,7 +3200,7 @@
}
}
-class StatementConstantEvaluator extends StatementVisitor<Constant> {
+class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
ConstantEvaluator exprEvaluator;
StatementConstantEvaluator(this.exprEvaluator) {
@@ -3202,12 +3213,31 @@
Constant evaluate(Expression expr) => expr.accept(exprEvaluator);
@override
- Constant defaultStatement(Statement node) => throw new UnsupportedError(
- 'Statement constant evaluation does not support ${node.runtimeType}.');
+ ExecutionStatus defaultStatement(Statement node) {
+ throw new UnsupportedError(
+ 'Statement constant evaluation does not support ${node.runtimeType}.');
+ }
@override
- Constant visitReturnStatement(ReturnStatement node) =>
- evaluate(node.expression);
+ ExecutionStatus visitBlock(Block node) {
+ for (Statement statement in node.statements) {
+ final ExecutionStatus status = statement.accept(this);
+ if (status is! ProceedStatus) return status;
+ }
+ return const ProceedStatus();
+ }
+
+ @override
+ ExecutionStatus visitReturnStatement(ReturnStatement node) =>
+ new ReturnStatus(evaluate(node.expression));
+
+ @override
+ ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
+ Constant value = evaluate(node.initializer);
+ if (value is AbortConstant) return new ReturnStatus(value);
+ exprEvaluator.env.updateVariableValue(node, value);
+ return const ProceedStatus();
+ }
}
class ConstantCoverage {
@@ -3345,6 +3375,22 @@
EvaluationReference(this.value);
}
+/// Represents a status for statement execution.
+abstract class ExecutionStatus {
+ const ExecutionStatus();
+}
+
+/// Status that the statement completed execution successfully.
+class ProceedStatus extends ExecutionStatus {
+ const ProceedStatus();
+}
+
+/// Status that the statement returned a valid [Constant] value.
+class ReturnStatus extends ExecutionStatus {
+ final Constant value;
+ ReturnStatus(this.value);
+}
+
/// An intermediate result that is used within the [ConstantEvaluator].
class IntermediateValue implements Constant {
dynamic value;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
new file mode 100644
index 0000000..691f46f
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart
@@ -0,0 +1,49 @@
+// 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 creating new local variables within const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+int function1(int a, int b) {
+ var x = 1 + a + b;
+ return x;
+}
+
+const var2 = function2();
+String function2() {
+ dynamic x = "string";
+ return x;
+}
+
+const var3 = function3();
+int function3() {
+ var first = 2;
+ var second = 2 + first;
+ return 2 + second;
+}
+
+const var4 = function4();
+int function4() {
+ var first = 2;
+ var second = 0;
+ return first + second;
+}
+
+const var5 = function5();
+int function5() {
+ const constant = -2;
+ return constant;
+}
+
+void main() {
+ Expect.equals(var1, 4);
+ Expect.equals(var2, "string");
+ Expect.equals(var3, 6);
+ Expect.equals(var4, 2);
+ Expect.equals(var5, -2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
new file mode 100644
index 0000000..0be3638
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.expect
@@ -0,0 +1,48 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+ core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+ return x;
+}
+static method function2() → core::String {
+ dynamic x = "string";
+ return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+ core::int first = 2;
+ core::int second = 2.{core::num::+}(first);
+ return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+ core::int first = 2;
+ core::int second = 0;
+ return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+ return #C5;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 4);
+ exp::Expect::equals(#C2, "string");
+ exp::Expect::equals(#C3, 6);
+ exp::Expect::equals(#C4, 2);
+ exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants {
+ #C1 = 4
+ #C2 = "string"
+ #C3 = 6
+ #C4 = 2
+ #C5 = -2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
new file mode 100644
index 0000000..cf33701
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.strong.transformed.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+ core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+ return x;
+}
+static method function2() → core::String {
+ dynamic x = "string";
+ return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+ core::int first = 2;
+ core::int second = 2.{core::num::+}(first);
+ return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+ core::int first = 2;
+ core::int second = 0;
+ return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+ return #C5;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 4);
+ exp::Expect::equals(#C2, "string");
+ exp::Expect::equals(#C3, 6);
+ exp::Expect::equals(#C4, 2);
+ exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants {
+ #C1 = 4
+ #C2 = "string"
+ #C3 = 6
+ #C4 = 2
+ #C5 = -2
+}
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:48:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 20, effectively constant: 1
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
new file mode 100644
index 0000000..723b134
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+int function1(int a, int b) {}
+const var2 = function2();
+String function2() {}
+const var3 = function3();
+int function3() {}
+const var4 = function4();
+int function4() {}
+const var5 = function5();
+int function5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d1c0ddb
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.textual_outline_modelled.expect
@@ -0,0 +1,13 @@
+import "package:expect/expect.dart";
+
+String function2() {}
+const var1 = function1(1, 2);
+const var2 = function2();
+const var3 = function3();
+const var4 = function4();
+const var5 = function5();
+int function1(int a, int b) {}
+int function3() {}
+int function4() {}
+int function5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
new file mode 100644
index 0000000..0be3638
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.expect
@@ -0,0 +1,48 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+ core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+ return x;
+}
+static method function2() → core::String {
+ dynamic x = "string";
+ return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+ core::int first = 2;
+ core::int second = 2.{core::num::+}(first);
+ return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+ core::int first = 2;
+ core::int second = 0;
+ return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+ return #C5;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 4);
+ exp::Expect::equals(#C2, "string");
+ exp::Expect::equals(#C3, 6);
+ exp::Expect::equals(#C4, 2);
+ exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants {
+ #C1 = 4
+ #C2 = "string"
+ #C3 = 6
+ #C4 = 2
+ #C5 = -2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
new file mode 100644
index 0000000..5941219
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.outline.expect
@@ -0,0 +1,23 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::function1(1, 2);
+static const field core::String var2 = self::function2();
+static const field core::int var3 = self::function3();
+static const field core::int var4 = self::function4();
+static const field core::int var5 = self::function5();
+static method function1(core::int a, core::int b) → core::int
+ ;
+static method function2() → core::String
+ ;
+static method function3() → core::int
+ ;
+static method function4() → core::int
+ ;
+static method function5() → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
new file mode 100644
index 0000000..cf33701
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_declarations.dart.weak.transformed.expect
@@ -0,0 +1,52 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::String var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static const field core::int var5 = #C5;
+static method function1(core::int a, core::int b) → core::int {
+ core::int x = 1.{core::num::+}(a).{core::num::+}(b);
+ return x;
+}
+static method function2() → core::String {
+ dynamic x = "string";
+ return x as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
+}
+static method function3() → core::int {
+ core::int first = 2;
+ core::int second = 2.{core::num::+}(first);
+ return 2.{core::num::+}(second);
+}
+static method function4() → core::int {
+ core::int first = 2;
+ core::int second = 0;
+ return first.{core::num::+}(second);
+}
+static method function5() → core::int {
+ return #C5;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 4);
+ exp::Expect::equals(#C2, "string");
+ exp::Expect::equals(#C3, 6);
+ exp::Expect::equals(#C4, 2);
+ exp::Expect::equals(#C5, 2.{core::int::unary-}());
+}
+
+constants {
+ #C1 = 4
+ #C2 = "string"
+ #C3 = 6
+ #C4 = 2
+ #C5 = -2
+}
+
+Extra constant evaluation status:
+Evaluated: MethodInvocation @ org-dartlang-testcase:///const_functions_variable_declarations.dart:48:23 -> IntConstant(-2)
+Extra constant evaluation: evaluated: 20, effectively constant: 1
diff --git a/tests/language/const_functions/const_functions_variable_declarations_test.dart b/tests/language/const_functions/const_functions_variable_declarations_test.dart
new file mode 100644
index 0000000..1bd577f
--- /dev/null
+++ b/tests/language/const_functions/const_functions_variable_declarations_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.
+
+// Tests creating new local variables within const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = function1(1, 2);
+// ^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function1(int a, int b) {
+ var x = 1 + a + b;
+ return x;
+}
+
+const var2 = function2();
+// ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+String function2() {
+ dynamic x = "string";
+ return x;
+}
+
+const var3 = function3();
+// ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function3() {
+ var first = 2;
+ var second = 2 + first;
+ return 2 + second;
+}
+
+const var4 = function4();
+// ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function4() {
+ var first = 2;
+ var second = 0;
+ return first + second;
+}
+
+const var5 = function5();
+// ^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int function5() {
+ const constant = -2;
+ return constant;
+}
+
+void main() {
+ Expect.equals(var1, 4);
+ Expect.equals(var2, "string");
+ Expect.equals(var3, 6);
+ Expect.equals(var4, 2);
+ Expect.equals(var5, -2);
+}
diff --git a/tools/VERSION b/tools/VERSION
index cffe45d2..5f9ce2d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 118
+PRERELEASE 119
PRERELEASE_PATCH 0
\ No newline at end of file