Version 2.13.0-149.0.dev
Merge commit '7c29dd9583604083ecdae3f05517967aba7dc35e' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 48079d1..5c095eda 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2021-03-15T10:48:35.022974",
+ "generated": "2021-03-17T11:06:05.181429",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -139,6 +139,12 @@
"languageVersion": "2.10"
},
{
+ "name": "browser_launcher",
+ "rootUri": "../third_party/pkg/browser_launcher",
+ "packageUri": "lib/",
+ "languageVersion": "2.2"
+ },
+ {
"name": "build_integration",
"rootUri": "../pkg/build_integration",
"packageUri": "lib/",
@@ -788,6 +794,12 @@
"languageVersion": "2.0"
},
{
+ "name": "webkit_inspection_protocol",
+ "rootUri": "../third_party/pkg/webkit_inspection_protocol",
+ "packageUri": "lib/",
+ "languageVersion": "2.0"
+ },
+ {
"name": "yaml",
"rootUri": "../third_party/pkg/yaml",
"packageUri": "lib/",
diff --git a/.packages b/.packages
index 3587c4f..5fc05a8 100644
--- a/.packages
+++ b/.packages
@@ -20,6 +20,7 @@
bazel_worker:third_party/pkg/bazel_worker/lib
benchmark_harness:third_party/pkg/benchmark_harness/lib
boolean_selector:third_party/pkg/boolean_selector/lib
+browser_launcher:third_party/pkg/browser_launcher/lib
build_integration:pkg/build_integration/lib
charcode:third_party/pkg/charcode/lib
cli_util:third_party/pkg/cli_util/lib
@@ -118,6 +119,7 @@
wasm:pkg/wasm/lib
watcher:third_party/pkg/watcher/lib
webdriver:third_party/pkg/webdriver/lib
+webkit_inspection_protocol:third_party/pkg/webkit_inspection_protocol/lib
web_components:third_party/pkg/web_components/lib
web_socket_channel:third_party/pkg/web_socket_channel/lib
yaml:third_party/pkg/yaml/lib
diff --git a/DEPS b/DEPS
index 184913f..50bd12c 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
- "co19_rev": "88b4f0556b0889067efcac45f2b96bc630e5b96c",
+ "co19_rev": "8cf837f5abf0a30ed91ebf480df58e5c0480947e",
"co19_2_rev": "cf6eed0535e45413672bb5bb6e65df9f59846372",
# The internal benchmarks to use. See go/dart-benchmarks-internal
@@ -101,6 +101,7 @@
"dart_style_tag": "2cf810073e0cc7e7ea05d3de51830e6fa6d62924",
"chromedriver_tag": "83.0.4103.39",
+ "browser_launcher_rev": "74949f0c599095e6fac3cb4581ed4769167576c7",
"dartdoc_rev" : "174021f3d76c2a8500b54977c80f32c5bbc099f1",
"ffi_rev": "f3346299c55669cc0db48afae85b8110088bf8da",
"fixnum_rev": "16d3890c6dc82ca629659da1934e412292508bba",
@@ -168,6 +169,7 @@
"web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
"web_socket_channel_rev": "76931ea1b81ba71e8319330c35285d3e88566315",
"WebCore_rev": "fb11e887f77919450e497344da570d780e078bc8",
+ "webkit_inspection_protocol_rev": "6b15729292d030f2e5c5861022da4c5a4c11961c",
"yaml_rev": "b4c4411631bda556ce9a45af1ab0eecaf9f3ac53",
"zlib_rev": "bf44340d1b6be1af8950bbdf664fec0cf5a831cc",
"crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
@@ -313,6 +315,9 @@
Var("dart_root") + "/third_party/pkg/boolean_selector":
Var("dart_git") + "boolean_selector.git" +
"@" + Var("boolean_selector_rev"),
+ Var("dart_root") + "/third_party/pkg/browser_launcher":
+ Var("dart_git") + "browser_launcher.git" +
+ "@" + Var("browser_launcher_rev"),
Var("dart_root") + "/third_party/pkg/charcode":
Var("dart_git") + "charcode.git" + "@" + Var("charcode_rev"),
Var("dart_root") + "/third_party/pkg/cli_util":
@@ -461,6 +466,9 @@
Var("dart_root") + "/third_party/pkg/webdriver":
Var("dart_git") + "external/github.com/google/webdriver.dart.git" +
"@" + Var("webdriver_rev"),
+ Var("dart_root") + "/third_party/pkg/webkit_inspection_protocol":
+ Var("dart_git") + "external/github.com/google/webkit_inspection_protocol.dart.git" +
+ "@" + Var("webkit_inspection_protocol_rev"),
Var("dart_root") + "/third_party/pkg/web_socket_channel":
Var("dart_git") + "web_socket_channel.git" +
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
index cfa4242e..e610849 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/yaml_completion_generator.dart
@@ -7,6 +7,7 @@
import 'package:analysis_server/src/services/pub/pub_package_service.dart';
import 'package:analysis_server/src/utilities/extensions/yaml.dart';
import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/util/yaml.dart';
import 'package:yaml/yaml.dart';
/// A completion generator that can produce completion suggestions for files
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
index 58abf49..29e814f 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_override_set_parser.dart
@@ -6,8 +6,8 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_override_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_parser.dart';
-import 'package:analysis_server/src/utilities/extensions/yaml.dart';
import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/util/yaml.dart';
import 'package:yaml/yaml.dart';
/// A parser used to parse the content of a configuration file.
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index b0c122c..21e7d1d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -19,8 +19,8 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
-import 'package:analysis_server/src/utilities/extensions/yaml.dart';
import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/util/yaml.dart';
import 'package:meta/meta.dart';
import 'package:yaml/yaml.dart';
diff --git a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
index 6ce971a..1199a2f 100644
--- a/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
+++ b/pkg/analysis_server/lib/src/utilities/extensions/yaml.dart
@@ -4,30 +4,6 @@
import 'package:yaml/yaml.dart';
-extension YamlMapExtensions on YamlMap {
- /// Return the node representing the key that corresponds to the value
- /// represented by the [value] node.
- YamlNode keyAtValue(YamlNode value) {
- for (var entry in nodes.entries) {
- if (entry.value == value) {
- return entry.key;
- }
- }
- return null;
- }
-
- /// Return the value associated with the key whose value matches the given
- /// [key], or `null` if there is no matching key.
- YamlNode valueAt(String key) {
- for (var keyNode in nodes.keys) {
- if (keyNode is YamlScalar && keyNode.value == key) {
- return nodes[key];
- }
- }
- return null;
- }
-}
-
extension YamlNodeExtensions on YamlNode {
/// Return the child of this node that contains the given [offset], or `null`
/// if none of the children contains the offset.
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index fd9f767..9395186ea 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -476,6 +476,9 @@
FfiCode.NON_CONSTANT_TYPE_ARGUMENT,
FfiCode.NON_NATIVE_FUNCTION_TYPE_ARGUMENT_TO_POINTER,
FfiCode.NON_SIZED_TYPE_ARGUMENT,
+ FfiCode.PACKED_ANNOTATION,
+ FfiCode.PACKED_ANNOTATION_ALIGNMENT,
+ FfiCode.PACKED_NESTING_NON_PACKED,
FfiCode.SIZE_ANNOTATION_DIMENSIONS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS,
diff --git a/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart b/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
index 6193114..3e512c7 100644
--- a/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
+++ b/pkg/analyzer/lib/src/analysis_options/analysis_options_provider.dart
@@ -62,7 +62,7 @@
/// Return an empty options map if the file does not exist.
YamlMap getOptionsFromSource(Source source) {
YamlMap options = getOptionsFromString(_readAnalysisOptions(source));
- var node = getValue(options, AnalyzerOptions.include);
+ var node = options.valueAt(AnalyzerOptions.include);
var sourceFactory = this.sourceFactory;
if (sourceFactory != null && node is YamlScalar) {
var path = node.value;
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
index 6b341e8..986e5a0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
@@ -433,10 +433,10 @@
.getOptionsFromFile(optionsFile);
if (doc is YamlMap) {
- var analyzerOptions = getValue(doc, AnalyzerOptions.analyzer);
+ var analyzerOptions = doc.valueAt(AnalyzerOptions.analyzer);
if (analyzerOptions is YamlMap) {
var excludeOptions =
- getValue(analyzerOptions, AnalyzerOptions.exclude);
+ analyzerOptions.valueAt(AnalyzerOptions.exclude);
if (excludeOptions is YamlList) {
List<String>? excludeList = toStringList(excludeOptions);
if (excludeList != null) {
diff --git a/pkg/analyzer/lib/src/dart/error/ffi_code.dart b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
index 08f603f..dc8e106 100644
--- a/pkg/analyzer/lib/src/dart/error/ffi_code.dart
+++ b/pkg/analyzer/lib/src/dart/error/ffi_code.dart
@@ -209,6 +209,35 @@
/**
* No parameters.
*/
+ static const FfiCode PACKED_ANNOTATION = FfiCode(
+ name: 'PACKED_ANNOTATION',
+ message: "Structs must have at most one 'Packed' annotation.",
+ correction: "Try removing extra 'Packed' annotations.");
+
+ /**
+ * No parameters.
+ */
+ static const FfiCode PACKED_ANNOTATION_ALIGNMENT = FfiCode(
+ name: 'PACKED_ANNOTATION_ALIGNMENT',
+ message: "Only packing to 1, 2, 4, 8, and 16 bytes is supported.",
+ correction:
+ "Try changing the 'Packed' annotation alignment to 1, 2, 4, 8, or 16.");
+
+ /**
+ * Parameters:
+ * 0: the name of the outer struct
+ * 1: the name of the struct being nested
+ */
+ static const FfiCode PACKED_NESTING_NON_PACKED = FfiCode(
+ name: 'PACKED_NESTING_NON_PACKED',
+ message:
+ "Nesting the non-packed or less tightly packed struct '{0}' in a packed struct '{1}' is not supported.",
+ correction:
+ "Try packing the nested struct or packing the nested struct more tightly.");
+
+ /**
+ * No parameters.
+ */
static const FfiCode SIZE_ANNOTATION_DIMENSIONS = FfiCode(
name: 'SIZE_ANNOTATION_DIMENSIONS',
message:
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 92d84fd..7ab99dd 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -13079,6 +13079,20 @@
// [Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
// for more details.
//
+ // The following code produces this diagnostic because `s` can't be `null`.
+ //
+ // ```dart
+ // void f(Object? o) {
+ // var s = o as String;
+ // s[!?.!]length;
+ // }
+ // ```
+ //
+ // The reason `s` can't be null, despite the fact that `o` can be `null`, is
+ // because of the cast to `String`, which is a non-nullable type. If `o` ever
+ // has the value `null`, the cast will fail and the invocation of `length`
+ // will not happen.
+ //
// #### Common fixes
//
// Replace the null-aware operator with a non-null-aware equivalent; for
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index d65781b..bd826d4 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -51,12 +51,16 @@
/// `Struct`.
bool inStruct = false;
+ /// Subclass of `Struct` we are currently visiting, or `null`.
+ ClassDeclaration? struct;
+
/// Initialize a newly created verifier.
FfiVerifier(this.typeSystem, this._errorReporter);
@override
void visitClassDeclaration(ClassDeclaration node) {
inStruct = false;
+ struct = null;
// Only the Allocator, Opaque and Struct class may be extended.
var extendsClause = node.extendsClause;
if (extendsClause != null) {
@@ -66,10 +70,12 @@
final className = ffiClass.name;
if (className == _structClassName) {
inStruct = true;
+ struct = node;
if (node.declaredElement!.isEmptyStruct) {
_errorReporter
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [node.name]);
}
+ _validatePackedAnnotation(node.metadata);
} else if (className != _allocatorClassName &&
className != _opaqueClassName) {
_errorReporter.reportErrorForNode(
@@ -561,12 +567,20 @@
}
final arrayDimensions = declaredType.arrayDimensions;
_validateSizeOfAnnotation(fieldType, annotations, arrayDimensions);
+ final arrayElement = declaredType.arrayElementType;
+ if (arrayElement.isStructSubtype) {
+ final elementClass = (arrayElement as InterfaceType).element;
+ _validatePackingNesting(struct!.declaredElement!, elementClass,
+ errorNode: fieldType);
+ }
} else if (declaredType.isStructSubtype) {
final clazz = (declaredType as InterfaceType).element;
if (clazz.isEmptyStruct) {
_errorReporter
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
}
+ _validatePackingNesting(struct!.declaredElement!, clazz,
+ errorNode: fieldType);
} else {
_errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
fieldType, [fieldType.toSource()]);
@@ -664,6 +678,54 @@
}
}
+ /// Validate that the [annotations] include at most one packed annotation.
+ void _validatePackedAnnotation(NodeList<Annotation> annotations) {
+ final ffiPackedAnnotations =
+ annotations.where((annotation) => annotation.isPacked).toList();
+
+ if (ffiPackedAnnotations.isEmpty) {
+ return;
+ }
+
+ if (ffiPackedAnnotations.length > 1) {
+ final extraAnnotations = ffiPackedAnnotations.skip(1);
+ for (final annotation in extraAnnotations) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.PACKED_ANNOTATION, annotation);
+ }
+ }
+
+ // Check number of dimensions.
+ final annotation = ffiPackedAnnotations.first;
+ final value = annotation.elementAnnotation?.packedMemberAlignment;
+ if (![1, 2, 4, 8, 16].contains(value)) {
+ _errorReporter.reportErrorForNode(
+ FfiCode.PACKED_ANNOTATION_ALIGNMENT, annotation);
+ }
+ }
+
+ void _validatePackingNesting(ClassElement outer, ClassElement nested,
+ {required TypeAnnotation errorNode}) {
+ final outerPacking = outer.structPacking;
+ if (outerPacking == null) {
+ // No packing for outer class, so we're done.
+ return;
+ }
+ bool error = false;
+ final nestedPacking = nested.structPacking;
+ if (nestedPacking == null) {
+ // The outer struct packs, but the nested struct does not.
+ error = true;
+ } else if (outerPacking < nestedPacking) {
+ // The outer struct packs tighter than the nested struct.
+ error = true;
+ }
+ if (error) {
+ _errorReporter.reportErrorForNode(FfiCode.PACKED_NESTING_NON_PACKED,
+ errorNode, [nested.name, outer.name]);
+ }
+ }
+
void _validateRefIndexed(IndexExpression node) {
var targetType = node.realTarget.staticType;
if (!_isValidFfiNativeType(targetType,
@@ -774,6 +836,30 @@
none,
}
+extension on Annotation {
+ bool get isPacked {
+ final element = this.element;
+ return element is ConstructorElement &&
+ element.ffiClass != null &&
+ element.enclosingElement.name == 'Packed';
+ }
+}
+
+extension on ElementAnnotation {
+ bool get isPacked {
+ final element = this.element;
+ return element is ConstructorElement &&
+ element.ffiClass != null &&
+ element.enclosingElement.name == 'Packed';
+ }
+
+ int get packedMemberAlignment {
+ assert(isPacked);
+ final value = computeConstantValue();
+ return value!.getField('memberAlignment')!.toIntValue()!;
+ }
+}
+
extension on Element? {
/// Return `true` if this represents the extension `AllocatorAlloc`.
bool get isAllocatorExtension {
@@ -855,6 +941,17 @@
bool get isFfiClass {
return library.name == FfiVerifier._dartFfiLibraryName;
}
+
+ int? get structPacking {
+ final packedAnnotations =
+ metadata.where((annotation) => annotation.isPacked);
+
+ if (packedAnnotations.isEmpty) {
+ return null;
+ }
+
+ return packedAnnotations.first.packedMemberAlignment;
+ }
}
extension on ExtensionElement {
@@ -886,6 +983,16 @@
return dimensions;
}
+ DartType get arrayElementType {
+ DartType iterator = this;
+ while (iterator is InterfaceType &&
+ iterator.element.name == FfiVerifier._arrayClassName &&
+ iterator.element.isFfiClass) {
+ iterator = iterator.typeArguments.single;
+ }
+ return iterator;
+ }
+
bool get isPointer {
final self = this;
return self is InterfaceType && self.element.isPointer;
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 270cc84..ff6050c 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -93,7 +93,7 @@
return buffer.toString();
}
- /// If the given [keyword] is not `null`, append it to the given [builder],
+ /// If the given [keyword] is not `null`, append it to the given [buffer],
/// prefixing it with a space if [needsSpace] is `true`. Return `true` if
/// subsequent keywords need to be prefixed with a space.
bool _appendKeyword(StringBuffer buffer, bool needsSpace, Token? keyword) {
diff --git a/pkg/analyzer/lib/src/lint/config.dart b/pkg/analyzer/lib/src/lint/config.dart
index 4b0dd66..288bf06 100644
--- a/pkg/analyzer/lib/src/lint/config.dart
+++ b/pkg/analyzer/lib/src/lint/config.dart
@@ -9,7 +9,7 @@
/// Return `null` if [optionsMap] is `null` or does not have `linter` map.
LintConfig? parseConfig(YamlMap? optionsMap) {
if (optionsMap != null) {
- var options = getValue(optionsMap, 'linter');
+ var options = optionsMap.valueAt('linter');
// Quick check of basic contract.
if (options is YamlMap) {
return LintConfig.parseMap(options);
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart
index 3a4d2a3..0fb77cf 100644
--- a/pkg/analyzer/lib/src/lint/linter.dart
+++ b/pkg/analyzer/lib/src/lint/linter.dart
@@ -849,6 +849,7 @@
case CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL:
case CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL:
case CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT:
+ case CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT:
case CompileTimeErrorCode.NON_CONSTANT_MAP_KEY:
case CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE:
case CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT:
diff --git a/pkg/analyzer/lib/src/lint/options_rule_validator.dart b/pkg/analyzer/lib/src/lint/options_rule_validator.dart
index 71e8620..6f326b1 100644
--- a/pkg/analyzer/lib/src/lint/options_rule_validator.dart
+++ b/pkg/analyzer/lib/src/lint/options_rule_validator.dart
@@ -68,9 +68,9 @@
@override
List<AnalysisError> validate(ErrorReporter reporter, YamlMap options) {
List<AnalysisError> errors = <AnalysisError>[];
- var node = getValue(options, linter);
+ var node = options.valueAt(linter);
if (node is YamlMap) {
- var rules = getValue(node, rulesKey);
+ var rules = node.valueAt(rulesKey);
validateRules(rules, reporter);
}
return errors;
diff --git a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
index 140cbd2..c7846a4 100644
--- a/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
+++ b/pkg/analyzer/lib/src/pubspec/pubspec_validator.dart
@@ -248,8 +248,8 @@
if (dependency is YamlMap) {
var pathEntry = _asString(dependency[PATH_FIELD]);
if (pathEntry != null) {
- YamlNode pathKey() => getKey(dependency, PATH_FIELD)!;
- YamlNode pathValue() => getValue(dependency, PATH_FIELD)!;
+ YamlNode pathKey() => dependency.getKey(PATH_FIELD)!;
+ YamlNode pathValue() => dependency.valueAt(PATH_FIELD)!;
if (pathEntry.contains(r'\')) {
_reportErrorForNode(reporter, pathValue(),
@@ -280,7 +280,7 @@
var gitEntry = dependency[GIT_FIELD];
if (gitEntry != null && checkForPathAndGitDeps) {
- _reportErrorForNode(reporter, getKey(dependency, GIT_FIELD)!,
+ _reportErrorForNode(reporter, dependency.getKey(GIT_FIELD)!,
PubspecWarningCode.INVALID_DEPENDENCY, [GIT_FIELD]);
}
}
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 5ed06e0..61d2688 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -21,6 +21,7 @@
import 'package:analyzer/src/lint/registry.dart';
import 'package:analyzer/src/plugin/options.dart';
import 'package:analyzer/src/util/yaml.dart';
+import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:source_span/source_span.dart';
import 'package:yaml/yaml.dart';
@@ -57,7 +58,7 @@
errors.addAll(validationErrors);
}
- var node = getValue(options, AnalyzerOptions.include);
+ var node = options.valueAt(AnalyzerOptions.include);
if (node == null) {
return;
}
@@ -171,10 +172,14 @@
/// Supported `analyzer` language options.
static const List<String> languageOptions = [strictInference, strictRawTypes];
- // Supported 'analyzer' optional checks options.
+ /// Supported 'analyzer' optional checks options.
static const List<String> optionalChecksOptions = [
chromeOsManifestChecks,
];
+
+ /// Proposed values for a `true` or `false` option.
+ static String get trueOrFalseProposal =>
+ AnalyzerOptions.trueOrFalse.quotedAndCommaSeparatedWithAnd;
}
/// Validates `analyzer` options.
@@ -203,15 +208,11 @@
/// Validates `analyzer` language configuration options.
class EnabledExperimentsValidator extends OptionsValidator {
- ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.languageOptions);
- ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
-
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var analyzer = getValue(options, AnalyzerOptions.analyzer);
+ var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var experimentNames =
- getValue(analyzer, AnalyzerOptions.enableExperiment);
+ var experimentNames = analyzer.valueAt(AnalyzerOptions.enableExperiment);
if (experimentNames is YamlList) {
var flags =
experimentNames.nodes.map((node) => node.toString()).toList();
@@ -242,34 +243,39 @@
/// Builds error reports with value proposals.
class ErrorBuilder {
- String? proposal;
- late final AnalysisOptionsWarningCode code;
+ static AnalysisOptionsWarningCode get noProposalCode =>
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES;
+
+ static AnalysisOptionsWarningCode get pluralProposalCode =>
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
+
+ static AnalysisOptionsWarningCode get singularProposalCode =>
+ AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
+
+ final String proposal;
+
+ final AnalysisOptionsWarningCode code;
/// Create a builder for the given [supportedOptions].
- ErrorBuilder(List<String> supportedOptions) {
+ factory ErrorBuilder(List<String> supportedOptions) {
+ var proposal = supportedOptions.quotedAndCommaSeparatedWithAnd;
if (supportedOptions.isEmpty) {
- code = noProposalCode;
+ return ErrorBuilder._(proposal: proposal, code: noProposalCode);
} else if (supportedOptions.length == 1) {
- proposal = "'${supportedOptions.join()}'";
- code = singularProposalCode;
+ return ErrorBuilder._(proposal: proposal, code: singularProposalCode);
} else {
- proposal = StringUtilities.printListOfQuotedNames(supportedOptions);
- code = pluralProposalCode;
+ return ErrorBuilder._(proposal: proposal, code: pluralProposalCode);
}
}
- AnalysisOptionsWarningCode get noProposalCode =>
- AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES;
-
- AnalysisOptionsWarningCode get pluralProposalCode =>
- AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
-
- AnalysisOptionsWarningCode get singularProposalCode =>
- AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
+ ErrorBuilder._({
+ required this.proposal,
+ required this.code,
+ });
/// Report an unsupported [node] value, defined in the given [scopeName].
void reportError(ErrorReporter reporter, String scopeName, YamlNode node) {
- if (proposal != null) {
+ if (proposal.isNotEmpty) {
reporter.reportErrorForSpan(
code, node.span, [scopeName, node.value, proposal]);
} else {
@@ -313,9 +319,9 @@
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var analyzer = getValue(options, AnalyzerOptions.analyzer);
+ var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var filters = getValue(analyzer, AnalyzerOptions.errors);
+ var filters = analyzer.valueAt(AnalyzerOptions.errors);
if (filters is YamlMap) {
String? value;
filters.nodes.forEach((k, v) {
@@ -350,14 +356,13 @@
/// Validates `analyzer` language configuration options.
class LanguageOptionValidator extends OptionsValidator {
- ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.languageOptions);
- ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+ final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.languageOptions);
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var analyzer = getValue(options, AnalyzerOptions.analyzer);
+ var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var language = getValue(analyzer, AnalyzerOptions.language);
+ var language = analyzer.valueAt(AnalyzerOptions.language);
if (language is YamlMap) {
language.nodes.forEach((k, v) {
String? key, value;
@@ -373,7 +378,7 @@
AnalysisOptionsHintCode.SUPER_MIXINS_SETTING_DEPRECATED,
k.span);
} else if (!AnalyzerOptions.languageOptions.contains(key)) {
- builder.reportError(reporter, AnalyzerOptions.language, k);
+ _builder.reportError(reporter, AnalyzerOptions.language, k);
} else {
// If we have a valid key, go on and check the value.
validKey = true;
@@ -382,7 +387,10 @@
if (validKey && v is YamlScalar) {
value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
- trueOrFalseBuilder.reportError(reporter, key!, v);
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+ v.span,
+ [key, v.value, AnalyzerOptions.trueOrFalseProposal]);
}
}
});
@@ -409,18 +417,18 @@
/// Validates `analyzer` optional-checks value configuration options.
class OptionalChecksValueValidator extends OptionsValidator {
- ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.optionalChecksOptions);
- ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+ final ErrorBuilder _builder =
+ ErrorBuilder(AnalyzerOptions.optionalChecksOptions);
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var analyzer = getValue(options, AnalyzerOptions.analyzer);
+ var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var v = getValue(analyzer, AnalyzerOptions.optionalChecks);
+ var v = analyzer.valueAt(AnalyzerOptions.optionalChecks);
if (v is YamlScalar) {
var value = toLowerCase(v.value);
if (value != AnalyzerOptions.chromeOsManifestChecks) {
- builder.reportError(
+ _builder.reportError(
reporter, AnalyzerOptions.chromeOsManifestChecks, v);
}
} else if (v is YamlMap) {
@@ -429,12 +437,15 @@
if (k is YamlScalar) {
key = k.value?.toString();
if (key != AnalyzerOptions.chromeOsManifestChecks) {
- builder.reportError(
+ _builder.reportError(
reporter, AnalyzerOptions.chromeOsManifestChecks, k);
} else {
value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
- trueOrFalseBuilder.reportError(reporter, key!, v);
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+ v.span,
+ [key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
}
}
}
@@ -471,19 +482,22 @@
/// Validates `analyzer` strong-mode value configuration options.
class StrongModeOptionValueValidator extends OptionsValidator {
- ErrorBuilder builder = ErrorBuilder(AnalyzerOptions.strongModeOptions);
- ErrorBuilder trueOrFalseBuilder = TrueOrFalseValueErrorBuilder();
+ final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.strongModeOptions);
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var analyzer = getValue(options, AnalyzerOptions.analyzer);
+ var analyzer = options.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
- var v = getValue(analyzer, AnalyzerOptions.strong_mode);
+ var v = analyzer.valueAt(AnalyzerOptions.strong_mode);
if (v is YamlScalar) {
var value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
- trueOrFalseBuilder.reportError(
- reporter, AnalyzerOptions.strong_mode, v);
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNSUPPORTED_VALUE, v.span, [
+ AnalyzerOptions.strong_mode,
+ v.value,
+ AnalyzerOptions.trueOrFalseProposal
+ ]);
} else if (value == 'false') {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.SPEC_MODE_REMOVED, v.span);
@@ -498,7 +512,7 @@
if (k is YamlScalar) {
key = k.value?.toString();
if (!AnalyzerOptions.strongModeOptions.contains(key)) {
- builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
+ _builder.reportError(reporter, AnalyzerOptions.strong_mode, k);
} else if (key == AnalyzerOptions.declarationCasts) {
reporter.reportErrorForSpan(
AnalysisOptionsWarningCode.ANALYSIS_OPTION_DEPRECATED,
@@ -512,7 +526,10 @@
if (validKey && v is YamlScalar) {
value = toLowerCase(v.value);
if (!AnalyzerOptions.trueOrFalse.contains(value)) {
- trueOrFalseBuilder.reportError(reporter, key!, v);
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.UNSUPPORTED_VALUE,
+ v.span,
+ [key!, v.value, AnalyzerOptions.trueOrFalseProposal]);
}
}
});
@@ -533,25 +550,19 @@
class TopLevelOptionValidator extends OptionsValidator {
final String pluginName;
final List<String> supportedOptions;
- late final String _valueProposal;
- late final AnalysisOptionsWarningCode _warningCode;
+ final String _valueProposal;
+ final AnalysisOptionsWarningCode _warningCode;
- TopLevelOptionValidator(this.pluginName, this.supportedOptions) {
- assert(supportedOptions.isNotEmpty);
- if (supportedOptions.length > 1) {
- _valueProposal = StringUtilities.printListOfQuotedNames(supportedOptions);
- _warningCode =
- AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
- } else {
- _valueProposal = "'${supportedOptions.join()}'";
- _warningCode =
- AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE;
- }
- }
+ TopLevelOptionValidator(this.pluginName, this.supportedOptions)
+ : assert(supportedOptions.isNotEmpty),
+ _valueProposal = supportedOptions.quotedAndCommaSeparatedWithAnd,
+ _warningCode = supportedOptions.length == 1
+ ? AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE
+ : AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES;
@override
void validate(ErrorReporter reporter, YamlMap options) {
- var node = getValue(options, pluginName);
+ var node = options.valueAt(pluginName);
if (node is YamlMap) {
node.nodes.forEach((k, v) {
if (k is YamlScalar) {
@@ -566,15 +577,6 @@
}
}
-/// An error-builder that knows about `true` and `false` legal values.
-class TrueOrFalseValueErrorBuilder extends ErrorBuilder {
- TrueOrFalseValueErrorBuilder() : super(AnalyzerOptions.trueOrFalse);
-
- @override
- AnalysisOptionsWarningCode get pluralProposalCode =>
- AnalysisOptionsWarningCode.UNSUPPORTED_VALUE;
-}
-
class _OptionsProcessor {
/// Apply the options in the given [optionMap] to the given analysis
/// [options].
@@ -582,19 +584,18 @@
if (optionMap == null) {
return;
}
- var analyzer = getValue(optionMap, AnalyzerOptions.analyzer);
+ var analyzer = optionMap.valueAt(AnalyzerOptions.analyzer);
if (analyzer is YamlMap) {
// Process strong mode option.
- var strongMode = getValue(analyzer, AnalyzerOptions.strong_mode);
+ var strongMode = analyzer.valueAt(AnalyzerOptions.strong_mode);
_applyStrongOptions(options, strongMode);
// Process filters.
- var filters = getValue(analyzer, AnalyzerOptions.errors);
+ var filters = analyzer.valueAt(AnalyzerOptions.errors);
_applyProcessors(options, filters);
// Process enabled experiments.
- var experimentNames =
- getValue(analyzer, AnalyzerOptions.enableExperiment);
+ var experimentNames = analyzer.valueAt(AnalyzerOptions.enableExperiment);
if (experimentNames is YamlList) {
List<String> enabledExperiments = <String>[];
for (var element in experimentNames.nodes) {
@@ -610,19 +611,19 @@
}
// Process optional checks options.
- var optionalChecks = getValue(analyzer, AnalyzerOptions.optionalChecks);
+ var optionalChecks = analyzer.valueAt(AnalyzerOptions.optionalChecks);
_applyOptionalChecks(options, optionalChecks);
// Process language options.
- var language = getValue(analyzer, AnalyzerOptions.language);
+ var language = analyzer.valueAt(AnalyzerOptions.language);
_applyLanguageOptions(options, language);
// Process excludes.
- var excludes = getValue(analyzer, AnalyzerOptions.exclude);
+ var excludes = analyzer.valueAt(AnalyzerOptions.exclude);
_applyExcludes(options, excludes);
// Process plugins.
- var names = getValue(analyzer, AnalyzerOptions.plugins);
+ var names = analyzer.valueAt(AnalyzerOptions.plugins);
List<String> pluginNames = <String>[];
var pluginName = _toString(names);
if (pluginName != null) {
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index f43dcc2..a0ea939 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -677,6 +677,12 @@
class Struct extends NativeType {}
+class Packed {
+ final int memberAlignment;
+
+ const Packed(this.memberAlignment);
+}
+
abstract class DynamicLibrary {}
extension DynamicLibraryExtension on DynamicLibrary {
diff --git a/pkg/analyzer/lib/src/util/yaml.dart b/pkg/analyzer/lib/src/util/yaml.dart
index 98f91e8..642dcc1 100644
--- a/pkg/analyzer/lib/src/util/yaml.dart
+++ b/pkg/analyzer/lib/src/util/yaml.dart
@@ -8,28 +8,6 @@
import 'package:yaml/src/event.dart';
import 'package:yaml/yaml.dart';
-/// Given a [map], return the [YamlNode] associated with the given [key], or
-/// `null` if there is no matching key.
-YamlNode? getKey(YamlMap map, String key) {
- for (YamlNode k in map.nodes.keys) {
- if (k is YamlScalar && k.value == key) {
- return k;
- }
- }
- return null;
-}
-
-/// Given a [map], return the value associated with the key whose value matches
-/// the given [key], or `null` if there is no matching key.
-YamlNode? getValue(YamlMap map, String key) {
- for (var k in map.nodes.keys) {
- if (k is YamlScalar && k.value == key) {
- return map.nodes[k];
- }
- }
- return null;
-}
-
/// If all of the elements of [list] are strings, return a list of strings
/// containing the same elements. Otherwise, return `null`.
List<String>? toStringList(List? list) {
@@ -82,9 +60,9 @@
return YamlMap.internal(map, o1.span, CollectionStyle.BLOCK);
}
- if (isListOfString(o1) && isMapToBools(o2)) {
+ if (_isListOfString(o1) && _isMapToBools(o2)) {
o1 = listToMap(o1 as YamlList);
- } else if (isMapToBools(o1) && isListOfString(o2)) {
+ } else if (_isMapToBools(o1) && _isListOfString(o2)) {
o2 = listToMap(o2 as YamlList);
}
@@ -117,11 +95,8 @@
m1.nodes.forEach((k, v) {
merged[k] = v;
});
- m2.nodes.forEach((k, v) {
- // TODO(srawlins): The key should always be a [YamlNode], but is not (see
- // https://pub.dev/documentation/yaml/latest/yaml/YamlMap/nodes.html).
- // We could write an extension method which does this cast for us.
- var value = (k as YamlNode).value;
+ m2.nodeMap.forEach((k, v) {
+ var value = k.value;
var mergedKey =
merged.keys.firstWhere((key) => key.value == value, orElse: () => k)
as YamlScalar;
@@ -135,11 +110,49 @@
return YamlMap.internal(merged, m1.span, CollectionStyle.BLOCK);
}
- static bool isListOfString(Object? o) =>
+ static bool _isListOfString(Object? o) =>
o is YamlList &&
o.nodes.every((e) => e is YamlScalar && e.value is String);
- static bool isMapToBools(Object? o) =>
+ static bool _isMapToBools(Object? o) =>
o is YamlMap &&
o.nodes.values.every((v) => v is YamlScalar && v.value is bool);
}
+
+extension YamlMapExtensions on YamlMap {
+ /// Return the value associated with the key whose value matches the given
+ /// [key], or `null` if there is no matching key.
+ YamlNode? valueAt(String key) {
+ for (var keyNode in nodes.keys) {
+ if (keyNode is YamlScalar && keyNode.value == key) {
+ return nodes[keyNode];
+ }
+ }
+ return null;
+ }
+
+ /// Return the [YamlNode] representing the key that corresponds to the value
+ /// represented by the [value] node.
+ YamlNode? keyAtValue(YamlNode value) {
+ for (var entry in nodes.entries) {
+ if (entry.value == value) {
+ return entry.key;
+ }
+ }
+ return null;
+ }
+
+ /// Return the [YamlNode] associated with the given [key], or `null` if there
+ /// is no matching key.
+ YamlNode? getKey(String key) {
+ for (YamlNode k in nodes.keys) {
+ if (k is YamlScalar && k.value == key) {
+ return k;
+ }
+ }
+ return null;
+ }
+
+ /// Return [nodes] as a Map with [YamlNode] keys.
+ Map<YamlNode, YamlNode> get nodeMap => nodes.cast<YamlNode, YamlNode>();
+}
diff --git a/pkg/analyzer/lib/src/utilities/extensions/string.dart b/pkg/analyzer/lib/src/utilities/extensions/string.dart
index d583029..6651cd7 100644
--- a/pkg/analyzer/lib/src/utilities/extensions/string.dart
+++ b/pkg/analyzer/lib/src/utilities/extensions/string.dart
@@ -14,24 +14,46 @@
String get commaSeparatedWithOr => _commaSeparated('or');
/// Produce a comma-separated representation of this list, with the last
+ /// element preceded by 'and' when there are more than two elements in this
+ /// list, and a pair of single quotes surrounding each element.
+ String get quotedAndCommaSeparatedWithAnd =>
+ _commaSeparated('and', quoted: true);
+
+ /// Produce a comma-separated representation of this list, with the last
/// element preceded by the [conjunction] when there are more than two
/// elements in this list.
- String _commaSeparated(String conjunction) {
+ ///
+ /// Each element is surrounded by a pair of single quotes if [quoted] is true.
+ String _commaSeparated(String conjunction, {bool quoted = false}) {
if (length == 0) {
return '';
} else if (length == 1) {
- return this[0];
+ return quoted ? "'${this[0]}'" : this[0];
} else if (length == 2) {
- return '${this[0]} $conjunction ${this[1]}';
+ return quoted
+ ? "'${this[0]}' $conjunction '${this[1]}'"
+ : '${this[0]} $conjunction ${this[1]}';
} else {
var buffer = StringBuffer();
for (int i = 0; i < length - 1; i++) {
+ if (quoted) {
+ buffer.write("'");
+ }
buffer.write(this[i]);
+ if (quoted) {
+ buffer.write("'");
+ }
buffer.write(', ');
}
buffer.write(conjunction);
buffer.write(' ');
+ if (quoted) {
+ buffer.write("'");
+ }
buffer.write(this[length - 1]);
+ if (quoted) {
+ buffer.write("'");
+ }
return buffer.toString();
}
}
diff --git a/pkg/analyzer/test/source/analysis_options_provider_test.dart b/pkg/analyzer/test/source/analysis_options_provider_test.dart
index ac4ea13..64cc0a4 100644
--- a/pkg/analyzer/test/source/analysis_options_provider_test.dart
+++ b/pkg/analyzer/test/source/analysis_options_provider_test.dart
@@ -131,9 +131,9 @@
YamlMap options = _getOptions('/foo/bar');
expect(options, hasLength(1));
{
- var analyzer = getValue(options, 'analyzer') as YamlMap;
+ var analyzer = options.valueAt('analyzer') as YamlMap;
expect(analyzer, isNotNull);
- expect(getValue(analyzer, 'ignore'), unorderedEquals(['bar']));
+ expect(analyzer.valueAt('ignore'), unorderedEquals(['bar']));
}
}
@@ -152,9 +152,9 @@
YamlMap options = _getOptions('/foo/bar/baz');
expect(options, hasLength(1));
{
- var analyzer = getValue(options, 'analyzer') as YamlMap;
+ var analyzer = options.valueAt('analyzer') as YamlMap;
expect(analyzer, isNotNull);
- expect(getValue(analyzer, 'ignore'), unorderedEquals(['bar']));
+ expect(analyzer.valueAt('ignore'), unorderedEquals(['bar']));
}
}
@@ -184,10 +184,10 @@
YamlMap options = _getOptions('/');
expect(options, hasLength(2));
{
- var analyzer = getValue(options, 'analyzer') as YamlMap;
+ var analyzer = options.valueAt('analyzer') as YamlMap;
expect(analyzer, hasLength(1));
{
- var ignore = getValue(analyzer, 'ignore') as YamlList;
+ var ignore = analyzer.valueAt('ignore') as YamlList;
expect(ignore, hasLength(2));
expect(ignore[0], 'ignoreme.dart');
expect(ignore[1], 'sdk_ext/**');
@@ -219,10 +219,10 @@
YamlMap options = _getOptions('/');
expect(options, hasLength(1));
{
- var analyzer = getValue(options, 'analyzer') as YamlMap;
+ var analyzer = options.valueAt('analyzer') as YamlMap;
expect(analyzer, hasLength(1));
{
- var ignore = getValue(analyzer, 'ignore') as YamlList;
+ var ignore = analyzer.valueAt('ignore') as YamlList;
expect(ignore, hasLength(2));
expect(ignore[0], 'ignoreme.dart');
expect(ignore[1], 'sdk_ext/**');
diff --git a/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart b/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart
new file mode 100644
index 0000000..b5e9223
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_annotation_alignment_test.dart
@@ -0,0 +1,41 @@
+// 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:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(PackedAnnotationAlignment);
+ });
+}
+
+@reflectiveTest
+class PackedAnnotationAlignment extends PubPackageResolutionTest {
+ test_error() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(3)
+class C extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+''', [
+ error(FfiCode.PACKED_ANNOTATION_ALIGNMENT, 20, 10),
+ ]);
+ }
+
+ test_no_error() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class C extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart
new file mode 100644
index 0000000..5ebf701
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_annotation_test.dart
@@ -0,0 +1,52 @@
+// 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:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(PackedAnnotation);
+ });
+}
+
+@reflectiveTest
+class PackedAnnotation extends PubPackageResolutionTest {
+ test_error() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+@Packed(1)
+class C extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+''', [
+ error(FfiCode.PACKED_ANNOTATION, 31, 10),
+ ]);
+ }
+
+ test_no_error_1() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+class C extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+''');
+ }
+
+ test_no_error_2() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class C extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart b/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart
new file mode 100644
index 0000000..9751139
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/packed_nesting_non_packed_test.dart
@@ -0,0 +1,116 @@
+// 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:analyzer/src/dart/error/ffi_code.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(PackedAnnotationNestingNonPacked);
+ });
+}
+
+@reflectiveTest
+class PackedAnnotationNestingNonPacked extends PubPackageResolutionTest {
+ test_error_1() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class TestStruct1603 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1603Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ external TestStruct1603 nestedNotPacked;
+}
+''', [
+ error(FfiCode.PACKED_NESTING_NON_PACKED, 200, 14),
+ ]);
+ }
+
+ test_error_2() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1604Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ external TestStruct1604 nestedLooselyPacked;
+}
+''', [
+ error(FfiCode.PACKED_NESTING_NON_PACKED, 211, 14),
+ ]);
+ }
+
+ test_error_3() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+class TestStruct1603 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1605Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ @Array(2)
+ external Array<TestStruct1603> nestedNotPacked;
+}
+''', [
+ error(FfiCode.PACKED_NESTING_NON_PACKED, 212, 21),
+ ]);
+ }
+
+ test_error_4() async {
+ await assertErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(8)
+class TestStruct1604 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ @Array(2)
+ external Array<TestStruct1604> nestedLooselyPacked;
+}
+''', [
+ error(FfiCode.PACKED_NESTING_NON_PACKED, 223, 21),
+ ]);
+ }
+
+ test_no_error() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:ffi';
+
+@Packed(1)
+class TestStruct1604 extends Struct {
+ external Pointer<Uint8> notEmpty;
+}
+
+@Packed(1)
+class TestStruct1606Packed extends Struct {
+ external Pointer<Uint8> notEmpty;
+
+ @Array(2)
+ external Array<TestStruct1604> nestedLooselyPacked;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 3916b75..9d666bb 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -498,6 +498,9 @@
as override_on_non_overriding_method;
import 'override_on_non_overriding_setter_test.dart'
as override_on_non_overriding_setter;
+import 'packed_annotation_alignment_test.dart' as packed_annotation_alignment;
+import 'packed_annotation_test.dart' as packed_annotation;
+import 'packed_nesting_non_packed_test.dart' as packed_nesting_non_packed;
import 'part_of_different_library_test.dart' as part_of_different_library;
import 'part_of_non_part_test.dart' as part_of_non_part;
import 'prefix_collides_with_top_level_member_test.dart'
@@ -1000,6 +1003,9 @@
override_on_non_overriding_getter.main();
override_on_non_overriding_method.main();
override_on_non_overriding_setter.main();
+ packed_annotation.main();
+ packed_annotation_alignment.main();
+ packed_nesting_non_packed.main();
part_of_different_library.main();
part_of_non_part.main();
prefix_collides_with_top_level_member.main();
diff --git a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
index f7e2b7d..99cce62 100644
--- a/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
+++ b/pkg/analyzer/test/src/lint/linter/linter_context_impl_test.dart
@@ -311,6 +311,13 @@
expect(context.canBeConst(node), expectedResult);
}
+ void test_listLiteral_false_forElement() async {
+ await resolve('''
+f() => [for (var i = 0; i < 10; i++) i];
+''');
+ assertCanBeConst('[for', false);
+ }
+
void test_listLiteral_false_methodInvocation() async {
await resolve('''
f() => [g()];
@@ -354,6 +361,14 @@
assertCanBeConst('[', true);
}
+ void test_listLiteral_true_ifElement() async {
+ await resolve('''
+const a = true;
+f() => [if (a) 0 else 1];
+''');
+ assertCanBeConst('[if', true);
+ }
+
void test_listLiteral_true_integerLiteral() async {
await resolve('''
f() => [1, 2, 3];
@@ -361,6 +376,13 @@
assertCanBeConst('[', true);
}
+ void test_mapLiteral_false_forElement() async {
+ await resolve('''
+f() => {for (var i = 0; i < 10; i++) i: 0};
+''');
+ assertCanBeConst('{', false);
+ }
+
void test_mapLiteral_false_methodInvocation_key() async {
await resolve('''
f() => {g(): 0};
@@ -377,6 +399,14 @@
assertCanBeConst('{', false);
}
+ void test_mapLiteral_true_ifElement() async {
+ await resolve('''
+const a = true;
+f() => {if (a) 0: 0 else 1: 1};
+''');
+ assertCanBeConst('{', true);
+ }
+
void test_mapLiteral_true_integerLiteral() async {
await resolve('''
f() => {1: 2, 3: 4};
@@ -384,6 +414,13 @@
assertCanBeConst('{', true);
}
+ void test_setLiteral_false_forElement() async {
+ await resolve('''
+f() => {for (var i = 0; i < 10; i++) i};
+''');
+ assertCanBeConst('{for', false);
+ }
+
void test_setLiteral_false_methodInvocation() async {
await resolve('''
f() => {g()};
@@ -392,6 +429,14 @@
assertCanBeConst('{', false);
}
+ void test_setLiteral_true_ifElement() async {
+ await resolve('''
+const a = true;
+f() => {if (a) 0 else 1};
+''');
+ assertCanBeConst('{', true);
+ }
+
void test_setLiteral_true_integerLiteral() async {
await resolve('''
f() => {1, 2, 3};
diff --git a/pkg/analyzer/test/src/utilities/extensions/string_test.dart b/pkg/analyzer/test/src/utilities/extensions/string_test.dart
index 2d9d373..ccf3226 100644
--- a/pkg/analyzer/test/src/utilities/extensions/string_test.dart
+++ b/pkg/analyzer/test/src/utilities/extensions/string_test.dart
@@ -32,7 +32,7 @@
}
void test_commaSeparatedWithAnd_zero() {
- expect(<String>[].commaSeparatedWithOr, isEmpty);
+ expect(<String>[].commaSeparatedWithAnd, isEmpty);
}
void test_commaSeparatedWithOr_five() {
@@ -55,4 +55,21 @@
void test_commaSeparatedWithOr_zero() {
expect(<String>[].commaSeparatedWithOr, isEmpty);
}
+
+ void test_quotedAndCommaSeparatedWithAnd_one() {
+ expect(<String>['a'].quotedAndCommaSeparatedWithAnd, "'a'");
+ }
+
+ void test_quotedAndCommaSeparatedWithAnd_three() {
+ expect(<String>['a', 'b', 'c'].quotedAndCommaSeparatedWithAnd,
+ "'a', 'b', and 'c'");
+ }
+
+ void test_quotedAndCommaSeparatedWithAnd_two() {
+ expect(<String>['a', 'b'].quotedAndCommaSeparatedWithAnd, "'a' and 'b'");
+ }
+
+ void test_quotedAndCommaSeparatedWithAnd_zero() {
+ expect(<String>[].quotedAndCommaSeparatedWithAnd, isEmpty);
+ }
}
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 6778017..39761ec 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -5260,6 +5260,20 @@
[Understanding null safety](/null-safety/understanding-null-safety#smarter-null-aware-methods)
for more details.
+The following code produces this diagnostic because `s` can't be `null`.
+
+{% prettify dart tag=pre+code %}
+void f(Object? o) {
+ var s = o as String;
+ s[!?.!]length;
+}
+{% endprettify %}
+
+The reason `s` can't be null, despite the fact that `o` can be `null`, is
+because of the cast to `String`, which is a non-nullable type. If `o` ever
+has the value `null`, the cast will fail and the invocation of `length`
+will not happen.
+
#### Common fixes
Replace the null-aware operator with a non-null-aware equivalent; for
diff --git a/pkg/analyzer_cli/lib/src/driver.dart b/pkg/analyzer_cli/lib/src/driver.dart
index 357b5d7..5a2a4fd 100644
--- a/pkg/analyzer_cli/lib/src/driver.dart
+++ b/pkg/analyzer_cli/lib/src/driver.dart
@@ -48,8 +48,8 @@
/// Test this option map to see if it specifies lint rules.
bool containsLintRuleEntry(YamlMap options) {
- var linterNode = getValue(options, 'linter');
- return linterNode is YamlMap && getValue(linterNode, 'rules') != null;
+ var linterNode = options.valueAt('linter');
+ return linterNode is YamlMap && linterNode.valueAt('rules') != null;
}
class Driver implements CommandLineStarter {
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 2a65eb6..dedd502 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -80,13 +80,6 @@
return name.compareTo(other.name);
}
- void merge(OutputUnit that) {
- assert(this != that);
- // We don't currently support merging code into the main output unit.
- assert(!isMainOutput);
- this.imports.addAll(that.imports);
- }
-
@override
String toString() => "OutputUnit($name, $imports)";
}
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index e12068e..aebbf7b 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -921,10 +921,19 @@
@override
TypeInformation visitEqualsNull(ir.EqualsNull node) {
- // TODO(johnniwinther). This triggers the computation of the mask for the
- // receiver of the call to `==`, which doesn't happen in this case. Remove
- // this when the ssa builder recognized `== null` directly.
- _typeOfReceiver(node, node.expression);
+ visit(node.expression);
+ if (node.fileOffset < node.expression.fileOffset) {
+ // Hack to detect `null == o`.
+ // TODO(johnniwinther): Remove this after the new method invocation has
+ // landed stably. This is only included to make the transition a no-op.
+ KernelGlobalTypeInferenceElementData data = _memberData;
+ data.setReceiverTypeMask(node, _closedWorld.abstractValueDomain.nullType);
+ } else {
+ // TODO(johnniwinther). This triggers the computation of the mask for the
+ // receiver of the call to `==`, which doesn't happen in this case. Remove
+ // this when the ssa builder recognized `== null` directly.
+ _typeOfReceiver(node, node.expression);
+ }
_potentiallyAddNullCheck(node, node.expression);
return _types.boolType;
}
@@ -941,8 +950,8 @@
_checkIfExposesThis(
selector, _types.newTypedSelector(receiverType, mask));
}
- TypeInformation type = handleDynamicInvoke(
- CallType.access, node, selector, mask, receiverType, arguments);
+ TypeInformation type = handleDynamicInvoke(CallType.access, node, selector,
+ mask, receiverType, arguments, _getVariableDeclaration(receiver));
if (interfaceTarget != null) {
if (interfaceTarget is ir.Procedure &&
(interfaceTarget.kind == ir.ProcedureKind.Method ||
@@ -1018,6 +1027,17 @@
}
@override
+ TypeInformation visitInstanceGetterInvocation(
+ ir.InstanceGetterInvocation node) {
+ Selector selector = _elementMap.getSelector(node);
+ ir.Expression receiver = node.receiver;
+ TypeInformation receiverType = visit(receiver);
+ ArgumentsTypes arguments = analyzeArguments(node.arguments);
+ return _handleMethodInvocation(node, node.receiver, receiverType, selector,
+ arguments, node.interfaceTarget);
+ }
+
+ @override
TypeInformation visitDynamicInvocation(ir.DynamicInvocation node) {
Selector selector = _elementMap.getSelector(node);
ir.Expression receiver = node.receiver;
@@ -1063,13 +1083,18 @@
arguments, node.interfaceTarget);
}
+ ir.VariableDeclaration _getVariableDeclaration(ir.Expression node) {
+ return node is ir.VariableGet ? node.variable : null;
+ }
+
TypeInformation _handleDynamic(
CallType callType,
ir.Node node,
Selector selector,
AbstractValue mask,
TypeInformation receiverType,
- ArgumentsTypes arguments) {
+ ArgumentsTypes arguments,
+ ir.VariableDeclaration variable) {
assert(receiverType != null);
if (_types.selectorNeedsUpdate(receiverType, mask)) {
mask = receiverType == _types.dynamicType
@@ -1079,18 +1104,6 @@
_analyzedMember, callType, node, selector, mask);
}
- ir.VariableDeclaration variable;
- if (node is ir.MethodInvocation && node.receiver is ir.VariableGet) {
- ir.VariableGet get = node.receiver;
- variable = get.variable;
- } else if (node is ir.PropertyGet && node.receiver is ir.VariableGet) {
- ir.VariableGet get = node.receiver;
- variable = get.variable;
- } else if (node is ir.PropertySet && node.receiver is ir.VariableGet) {
- ir.VariableGet get = node.receiver;
- variable = get.variable;
- }
-
if (variable != null) {
Local local = _localsMap.getLocalVariable(variable);
if (!_capturedVariables.contains(local)) {
@@ -1107,10 +1120,14 @@
inLoop: inLoop, isConditional: false);
}
- TypeInformation handleDynamicGet(ir.Node node, Selector selector,
- AbstractValue mask, TypeInformation receiverType) {
+ TypeInformation handleDynamicGet(
+ ir.Node node,
+ Selector selector,
+ AbstractValue mask,
+ TypeInformation receiverType,
+ ir.VariableDeclaration variable) {
return _handleDynamic(
- CallType.access, node, selector, mask, receiverType, null);
+ CallType.access, node, selector, mask, receiverType, null, variable);
}
TypeInformation handleDynamicSet(
@@ -1118,10 +1135,11 @@
Selector selector,
AbstractValue mask,
TypeInformation receiverType,
- TypeInformation rhsType) {
+ TypeInformation rhsType,
+ ir.VariableDeclaration variable) {
ArgumentsTypes arguments = new ArgumentsTypes([rhsType], null);
- return _handleDynamic(
- CallType.access, node, selector, mask, receiverType, arguments);
+ return _handleDynamic(CallType.access, node, selector, mask, receiverType,
+ arguments, variable);
}
TypeInformation handleDynamicInvoke(
@@ -1130,9 +1148,10 @@
Selector selector,
AbstractValue mask,
TypeInformation receiverType,
- ArgumentsTypes arguments) {
+ ArgumentsTypes arguments,
+ ir.VariableDeclaration variable) {
return _handleDynamic(
- callType, node, selector, mask, receiverType, arguments);
+ callType, node, selector, mask, receiverType, arguments, variable);
}
@override
@@ -1178,18 +1197,19 @@
moveNextMask = _memberData.typeOfIteratorMoveNext(node);
iteratorType = handleDynamicInvoke(CallType.forIn, node, iteratorSelector,
- iteratorMask, expressionType, new ArgumentsTypes.empty());
+ iteratorMask, expressionType, new ArgumentsTypes.empty(), null);
}
handleDynamicInvoke(CallType.forIn, node, Selectors.moveNext, moveNextMask,
- iteratorType, new ArgumentsTypes.empty());
+ iteratorType, new ArgumentsTypes.empty(), null);
TypeInformation currentType = handleDynamicInvoke(
CallType.forIn,
node,
Selectors.current,
currentMask,
iteratorType,
- new ArgumentsTypes.empty());
+ new ArgumentsTypes.empty(),
+ null);
Local variable = _localsMap.getLocalVariable(node.variable);
DartType variableType = _localsMap.getLocalType(_elementMap, variable);
@@ -1598,7 +1618,8 @@
_checkIfExposesThis(
selector, _types.newTypedSelector(receiverType, mask));
}
- TypeInformation type = handleDynamicGet(node, selector, mask, receiverType);
+ TypeInformation type = handleDynamicGet(
+ node, selector, mask, receiverType, _getVariableDeclaration(receiver));
if (interfaceTarget != null) {
// Pull the type from kernel (instead of from the J-model) because the
// interface target might be abstract and therefore not part of the
@@ -1674,7 +1695,8 @@
_checkIfExposesThis(
selector, _types.newTypedSelector(receiverType, mask));
}
- handleDynamicSet(node, selector, mask, receiverType, rhsType);
+ handleDynamicSet(node, selector, mask, receiverType, rhsType,
+ _getVariableDeclaration(receiver));
return rhsType;
}
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 02ae17c..45dea65 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -651,10 +651,24 @@
// mimicks that ast inferrer which return `true` for [ast.Send] and
// non-const [ast.NewExpression].
if (initializer is ir.MethodInvocation ||
+ initializer is ir.InstanceInvocation ||
+ initializer is ir.InstanceGetterInvocation ||
+ initializer is ir.DynamicInvocation ||
+ initializer is ir.FunctionInvocation ||
+ initializer is ir.LocalFunctionInvocation ||
+ initializer is ir.EqualsNull ||
+ initializer is ir.EqualsCall ||
initializer is ir.PropertyGet ||
+ initializer is ir.InstanceGet ||
+ initializer is ir.DynamicGet ||
+ initializer is ir.InstanceTearOff ||
+ initializer is ir.FunctionTearOff ||
initializer is ir.PropertySet ||
+ initializer is ir.InstanceSet ||
+ initializer is ir.DynamicSet ||
initializer is ir.StaticInvocation ||
initializer is ir.StaticGet ||
+ initializer is ir.StaticTearOff ||
initializer is ir.StaticSet ||
initializer is ir.Let ||
initializer is ir.ConstructorInvocation && !initializer.isConst) {
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index 1d8b365..803aed8 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -178,7 +178,9 @@
List<String> namedArguments,
List<ir.DartType> typeArguments);
- void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+ // TODO(johnniwinther): Change [node] to `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
ir.DartType receiverType, ir.DartType argumentType);
// TODO(johnniwinther): Remove these when CFE provides constants.
@@ -595,6 +597,20 @@
}
@override
+ void handleEqualsNull(ir.EqualsNull node, ir.DartType expressionType) {
+ // TODO(johnniwinther): Remove this after the new method invocation has landed
+ // stably. This is only included to make the transition a no-op.
+ if (node.fileOffset < node.expression.fileOffset) {
+ // Hack to detect `null == o`.
+ expressionType = const ir.NullType();
+ }
+ ClassRelation relation = computeClassRelationFromType(expressionType);
+ registerDynamicInvocation(
+ expressionType, relation, ir.Name.equalsName, 1, const [], const []);
+ registerNullLiteral();
+ }
+
+ @override
void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
ir.Name name, ir.DartType resultType) {
ClassRelation relation = computeClassRelationFromType(receiverType);
@@ -660,8 +676,10 @@
return super.visitSwitchStatement(node);
}
+ // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+ // invocation encoding is no longer used.
@override
- void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+ void handleRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
ir.DartType receiverType, ir.DartType argumentType) {
registerRuntimeTypeUse(node, kind, receiverType, argumentType);
}
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index eecf083..f259a4f 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -418,8 +418,10 @@
_data._intLiterals.add(value);
}
+ // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+ // invocation encoding is no longer used.
@override
- void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+ void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
ir.DartType receiverType, ir.DartType argumentType) {
_data._runtimeTypeUses ??= [];
_data._runtimeTypeUses
@@ -1577,7 +1579,9 @@
class _RuntimeTypeUse {
static const String tag = '_RuntimeTypeUse';
- final ir.PropertyGet node;
+ // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ final ir.Expression node;
final RuntimeTypeUseKind kind;
final ir.DartType receiverType;
final ir.DartType argumentType;
diff --git a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
index b62aafc..2fe49b8 100644
--- a/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
+++ b/pkg/compiler/lib/src/ir/runtime_type_analysis.dart
@@ -29,7 +29,9 @@
final RuntimeTypeUseKind kind;
/// The property get for the left (or single) occurrence of `.runtimeType`.
- final ir.PropertyGet leftRuntimeTypeExpression;
+ // TODO(johnniwinther): Change this to `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ final ir.Expression leftRuntimeTypeExpression;
/// The receiver expression.
final ir.Expression receiver;
@@ -40,7 +42,9 @@
/// The property get for the right occurrence of `.runtimeType` when [kind]
/// is `RuntimeTypeUseKind.equals`.
- final ir.PropertyGet rightRuntimeTypeExpression;
+ // TODO(johnniwinther): Change this to `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ final ir.Expression rightRuntimeTypeExpression;
/// The argument expression if [kind] is `RuntimeTypeUseKind.equals`.
final ir.Expression argument;
@@ -77,37 +81,43 @@
///
/// [cache] is used to ensure that only one [RuntimeTypeUseData] object is
/// created per case, even for the `==` case.
+// TODO(johnniwinther): Change [cache] key and [node] to `InstanceGet` when the
+// old method invocation encoding is no longer used.
RuntimeTypeUseData computeRuntimeTypeUse(
- Map<ir.PropertyGet, RuntimeTypeUseData> cache, ir.PropertyGet node) {
+ Map<ir.Expression, RuntimeTypeUseData> cache, ir.Expression node) {
RuntimeTypeUseData receiverData = cache[node];
if (receiverData != null) return receiverData;
/// Returns `true` if [node] is of the form `e.runtimeType`.
bool isGetRuntimeType(ir.TreeNode node) {
- return node is ir.PropertyGet && node.name.text == Identifiers.runtimeType_;
- }
-
- /// Returns [node] if [node] is of the form `e.runtimeType` and `null`
- /// otherwise.
- ir.PropertyGet asGetRuntimeType(ir.TreeNode node) {
- return isGetRuntimeType(node) ? node : null;
+ return node is ir.PropertyGet &&
+ node.name.text == Identifiers.runtimeType_ ||
+ node is ir.InstanceGet && node.name.text == Identifiers.runtimeType_ ||
+ node is ir.DynamicGet && node.name.text == Identifiers.runtimeType_;
}
/// Returns `true` if [node] is of the form `e.toString()`.
bool isInvokeToString(ir.TreeNode node) {
- return node is ir.MethodInvocation && node.name.text == 'toString';
+ return node is ir.MethodInvocation && node.name.text == 'toString' ||
+ node is ir.InstanceInvocation && node.name.text == 'toString';
}
assert(isGetRuntimeType(node));
+ // TODO(johnniwinther): Replace this with `node.receiver` when the old method
+ // invocation encoding is no longer used.
+ _RuntimeTypeAccess runtimeTypeAccess = _getRuntimeTypeAccess(node);
+ assert(runtimeTypeAccess != null);
+ // TODO(johnniwinther): Change [receiverGet] and [argumentGet] to
+ // `InstanceGet` when the old method invocation encoding is no longer used.
// TODO(johnniwinther): Special-case `this.runtimeType`.
- ir.PropertyGet receiverGet;
+ ir.Expression receiverGet;
ir.Expression receiver;
- ir.PropertyGet argumentGet;
+ ir.Expression argumentGet;
ir.Expression argument;
RuntimeTypeUseKind kind;
- if (node.receiver is ir.VariableGet &&
+ if (runtimeTypeAccess.receiver is ir.VariableGet &&
node.parent is ir.ConditionalExpression &&
node.parent.parent is ir.Let) {
NullAwareExpression nullAware = getNullAwareExpression(node.parent.parent);
@@ -138,15 +148,16 @@
receiver = nullAware.receiver;
receiverGet = node;
}
- } else if (nullAware.parent is ir.MethodInvocation) {
- ir.MethodInvocation methodInvocation = nullAware.parent;
- if (methodInvocation.receiver == nullAware.let &&
- methodInvocation.name.text == '==') {
+ } else if (_isObjectMethodInvocation(nullAware.parent)) {
+ _EqualsInvocation equalsInvocation =
+ _getEqualsInvocation(nullAware.parent);
+ if (equalsInvocation != null &&
+ equalsInvocation.left == nullAware.let) {
// Detected
//
// e0?.runtimeType == other
- ir.PropertyGet otherGetRuntimeType =
- asGetRuntimeType(methodInvocation.arguments.positional.first);
+ _RuntimeTypeAccess otherGetRuntimeType =
+ _getRuntimeTypeAccess(equalsInvocation.right);
if (otherGetRuntimeType != null) {
// Detected
//
@@ -161,11 +172,11 @@
receiver = nullAware.receiver;
receiverGet = node;
argument = otherGetRuntimeType.receiver;
- argumentGet = methodInvocation.arguments.positional.first;
+ argumentGet = otherGetRuntimeType.node;
}
- NullAwareExpression otherNullAware = getNullAwareExpression(
- methodInvocation.arguments.positional.first);
+ NullAwareExpression otherNullAware =
+ getNullAwareExpression(equalsInvocation.right);
if (otherNullAware != null &&
isGetRuntimeType(otherNullAware.expression)) {
// Detected
@@ -200,16 +211,17 @@
receiverGet = node;
}
} else if (nullAware.parent is ir.Arguments &&
- nullAware.parent.parent is ir.MethodInvocation) {
- ir.MethodInvocation methodInvocation = nullAware.parent.parent;
- if (methodInvocation.name.text == '==' &&
- methodInvocation.arguments.positional.first == nullAware.let) {
+ _isObjectMethodInvocation(nullAware.parent.parent)) {
+ _EqualsInvocation equalsInvocation =
+ _getEqualsInvocation(nullAware.parent.parent);
+ if (equalsInvocation != null &&
+ equalsInvocation.right == nullAware.let) {
// [nullAware] is the right hand side of ==.
- ir.PropertyGet otherGetRuntimeType =
- asGetRuntimeType(methodInvocation.receiver);
+ _RuntimeTypeAccess otherGetRuntimeType =
+ _getRuntimeTypeAccess(equalsInvocation.left);
NullAwareExpression otherNullAware =
- getNullAwareExpression(methodInvocation.receiver);
+ getNullAwareExpression(equalsInvocation.left);
if (otherGetRuntimeType != null) {
// Detected
@@ -223,7 +235,7 @@
// ^
kind = RuntimeTypeUseKind.equals;
receiver = otherGetRuntimeType.receiver;
- receiverGet = otherGetRuntimeType;
+ receiverGet = otherGetRuntimeType.node;
argument = nullAware.receiver;
argumentGet = node;
}
@@ -285,19 +297,18 @@
// let #t1 = e.runtimeType in #t1 == null ? null : #t1.toString()
// ^
kind = RuntimeTypeUseKind.string;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
}
- } else if (node.parent is ir.MethodInvocation) {
- ir.MethodInvocation methodInvocation = node.parent;
- if (methodInvocation.name.text == '==' &&
- methodInvocation.receiver == node) {
+ } else if (_isObjectMethodInvocation(node.parent)) {
+ _EqualsInvocation equalsInvocation = _getEqualsInvocation(node.parent);
+ if (equalsInvocation != null && equalsInvocation.left == node) {
// [node] is the left hand side of ==.
- ir.PropertyGet otherGetRuntimeType =
- asGetRuntimeType(methodInvocation.arguments.positional.first);
+ _RuntimeTypeAccess otherGetRuntimeType =
+ _getRuntimeTypeAccess(equalsInvocation.right);
NullAwareExpression nullAware =
- getNullAwareExpression(methodInvocation.arguments.positional.first);
+ getNullAwareExpression(equalsInvocation.right);
if (otherGetRuntimeType != null) {
// Detected
//
@@ -308,10 +319,10 @@
// e0.runtimeType.==(e1.runtimeType)
// ^
kind = RuntimeTypeUseKind.equals;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
argument = otherGetRuntimeType.receiver;
- argumentGet = otherGetRuntimeType;
+ argumentGet = otherGetRuntimeType.node;
} else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
// Detected
//
@@ -323,7 +334,7 @@
// ^
// let #t1 = e1 in #t1 == null ? null : #t1.runtimeType)
kind = RuntimeTypeUseKind.equals;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
argument = nullAware.receiver;
argumentGet = nullAware.expression;
@@ -334,19 +345,19 @@
// e.runtimeType.toString()
// ^
kind = RuntimeTypeUseKind.string;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
}
} else if (node.parent is ir.Arguments &&
- node.parent.parent is ir.MethodInvocation) {
- ir.MethodInvocation methodInvocation = node.parent.parent;
- if (methodInvocation.name.text == '==' &&
- methodInvocation.arguments.positional.first == node) {
+ _isObjectMethodInvocation(node.parent.parent)) {
+ _EqualsInvocation _equalsInvocation =
+ _getEqualsInvocation(node.parent.parent);
+ if (_equalsInvocation != null && _equalsInvocation.right == node) {
// [node] is the right hand side of ==.
- ir.PropertyGet otherGetRuntimeType =
- asGetRuntimeType(methodInvocation.receiver);
+ _RuntimeTypeAccess otherGetRuntimeType =
+ _getRuntimeTypeAccess(_equalsInvocation.left);
NullAwareExpression nullAware =
- getNullAwareExpression(methodInvocation.receiver);
+ getNullAwareExpression(_equalsInvocation.left);
if (otherGetRuntimeType != null) {
// Detected
@@ -359,8 +370,8 @@
// ^
kind = RuntimeTypeUseKind.equals;
receiver = otherGetRuntimeType.receiver;
- receiverGet = otherGetRuntimeType;
- argument = node.receiver;
+ receiverGet = otherGetRuntimeType.node;
+ argument = runtimeTypeAccess.receiver;
argumentGet = node;
} else if (nullAware != null && isGetRuntimeType(nullAware.expression)) {
// Detected
@@ -375,7 +386,7 @@
kind = RuntimeTypeUseKind.equals;
receiver = nullAware.receiver;
receiverGet = nullAware.expression;
- argument = node.receiver;
+ argument = runtimeTypeAccess.receiver;
argumentGet = node;
}
}
@@ -385,7 +396,7 @@
// '${e.runtimeType}'
// ^
kind = RuntimeTypeUseKind.string;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
}
@@ -395,7 +406,7 @@
// e.runtimeType
// ^
kind = RuntimeTypeUseKind.unknown;
- receiver = node.receiver;
+ receiver = runtimeTypeAccess.receiver;
receiverGet = node;
}
@@ -413,3 +424,50 @@
receiverGet != argumentGet, "Duplicate property get in $data for $node.");
return data;
}
+
+/// Returns `true` if [node] is a potential invocation of an Object method.
+bool _isObjectMethodInvocation(ir.TreeNode node) {
+ return node is ir.MethodInvocation ||
+ node is ir.InstanceInvocation ||
+ node is ir.EqualsCall;
+}
+
+/// Returns the [_RuntimeTypeAccess] corresponding to [node] if it is an access
+/// of `.runtimeType`, and `null` otherwise.
+_RuntimeTypeAccess _getRuntimeTypeAccess(ir.TreeNode node) {
+ if (node is ir.PropertyGet && node.name.text == 'runtimeType') {
+ return _RuntimeTypeAccess(node, node.receiver);
+ } else if (node is ir.InstanceGet && node.name.text == 'runtimeType') {
+ return _RuntimeTypeAccess(node, node.receiver);
+ } else if (node is ir.DynamicGet && node.name.text == 'runtimeType') {
+ return _RuntimeTypeAccess(node, node.receiver);
+ }
+ return null;
+}
+
+class _RuntimeTypeAccess {
+ final ir.Expression node;
+ final ir.Expression receiver;
+
+ _RuntimeTypeAccess(this.node, this.receiver);
+}
+
+/// Returns the [_EqualsInvocation] corresponding to [node] if it is a call to
+/// of `==`, and `null` otherwise.
+_EqualsInvocation _getEqualsInvocation(ir.TreeNode node) {
+ if (node is ir.MethodInvocation && node.name.text == '==') {
+ return _EqualsInvocation(
+ node, node.receiver, node.arguments.positional.single);
+ } else if (node is ir.EqualsCall) {
+ return _EqualsInvocation(node, node.left, node.right);
+ }
+ return null;
+}
+
+class _EqualsInvocation {
+ final ir.Expression node;
+ final ir.Expression left;
+ final ir.Expression right;
+
+ _EqualsInvocation(this.node, this.left, this.right);
+}
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 07e82b0..3004f39 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -1070,6 +1070,24 @@
}
@override
+ EvaluationComplexity visitInstanceGetterInvocation(
+ ir.InstanceGetterInvocation node) {
+ node.receiver = _handleExpression(node.receiver);
+ if (node.arguments.types.isNotEmpty) {
+ ir.TreeNode receiver = node.receiver;
+ assert(
+ !(receiver is ir.VariableGet &&
+ receiver.variable.parent is ir.LocalFunction),
+ "Unexpected local function invocation ${node} "
+ "(${node.runtimeType}).");
+ VariableUse usage = new VariableUse.instanceTypeArgument(node);
+ visitNodesInContext(node.arguments.types, usage);
+ }
+ visitArguments(node.arguments);
+ return const EvaluationComplexity.lazy();
+ }
+
+ @override
EvaluationComplexity visitDynamicInvocation(ir.DynamicInvocation node) {
node.receiver = _handleExpression(node.receiver);
if (node.arguments.types.isNotEmpty) {
@@ -1106,6 +1124,7 @@
@override
EvaluationComplexity visitLocalFunctionInvocation(
ir.LocalFunctionInvocation node) {
+ _markVariableAsUsed(node.variable, VariableUse.explicit);
if (node.arguments.types.isNotEmpty) {
VariableUse usage =
new VariableUse.localTypeArgument(node.localFunction, node);
diff --git a/pkg/compiler/lib/src/ir/static_type.dart b/pkg/compiler/lib/src/ir/static_type.dart
index 8dcffab..cb063b6 100644
--- a/pkg/compiler/lib/src/ir/static_type.dart
+++ b/pkg/compiler/lib/src/ir/static_type.dart
@@ -73,7 +73,9 @@
abstract class StaticTypeVisitor extends StaticTypeBase {
final StaticTypeCacheImpl _staticTypeCache;
Map<ir.Expression, TypeMap> typeMapsForTesting;
- Map<ir.PropertyGet, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
+ // TODO(johnniwinther): Change the key to `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ Map<ir.Expression, RuntimeTypeUseData> _pendingRuntimeTypeUseData = {};
final ir.ClassHierarchy hierarchy;
@@ -130,13 +132,19 @@
TypeMap _typeMapWhenTrue;
TypeMap _typeMapWhenFalse;
- /// Returns the local variable type promotions for when the boolean value of
- /// the most recent node is not taken into account.
- TypeMap get typeMap {
+ /// Joins [_typeMapWhenTrue] and [_typeMapWhenFalse] and stores the result
+ /// in [_typeMapBase].
+ void _flattenTypeMap() {
if (_typeMapBase == null) {
_typeMapBase = _typeMapWhenTrue.join(_typeMapWhenFalse);
_typeMapWhenTrue = _typeMapWhenFalse = null;
}
+ }
+
+ /// Returns the local variable type promotions for when the boolean value of
+ /// the most recent node is not taken into account.
+ TypeMap get typeMap {
+ _flattenTypeMap();
return _typeMapBase;
}
@@ -282,13 +290,22 @@
return const ir.DynamicType();
}
+ /// Replaces [original] with [replacement] in the AST and removes cached
+ /// expression type information for [original].
+ void _replaceExpression(ir.Expression original, ir.Expression replacement) {
+ original.replaceWith(replacement);
+ _staticTypeCache._expressionTypes.remove(original);
+ }
+
void handleDynamicGet(ir.Expression node, ir.DartType receiverType,
ir.Name name, ir.DartType resultType) {}
void handleInstanceGet(ir.Expression node, ir.DartType receiverType,
ir.Member interfaceTarget, ir.DartType resultType) {}
- void handleRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+ // TODO(johnniwinther): Change [node] to `InstanceGet` when the old method
+ // invocation encoding is no longer used.
+ void handleRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
ir.DartType receiverType, ir.DartType argumentType) {}
@override
@@ -343,13 +360,25 @@
resultType = _computeInstanceGetType(receiverType, interfaceTarget);
ir.InstanceGet instanceGet = ir.InstanceGet(
ir.InstanceAccessKind.Instance, node.receiver, node.name,
- interfaceTarget: interfaceTarget, resultType: resultType);
- node.replaceWith(instanceGet);
+ interfaceTarget: interfaceTarget, resultType: resultType)
+ ..fileOffset = node.fileOffset;
+ _replaceExpression(node, instanceGet);
handleInstanceGet(instanceGet, receiverType, interfaceTarget, resultType);
+ } else if (node.name == ir.Name.callName &&
+ (receiverType is ir.FunctionType ||
+ (receiverType is ir.InterfaceType &&
+ receiverType.classNode == typeEnvironment.functionClass))) {
+ ir.FunctionTearOff functionTearOff = ir.FunctionTearOff(node.receiver)
+ ..fileOffset = node.fileOffset;
+ _replaceExpression(node, functionTearOff);
+ handleDynamicGet(
+ functionTearOff, receiverType, ir.Name.callName, resultType);
+ resultType = receiverType;
} else {
handleDynamicGet(node, receiverType, node.name, resultType);
}
if (node.name.text == Identifiers.runtimeType_) {
+ // This handles `runtimeType` access on `Never`.
handleRuntimeTypeGet(receiverType, node);
}
return resultType;
@@ -358,9 +387,17 @@
@override
ir.DartType visitInstanceGet(ir.InstanceGet node) {
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType resultType = super.visitInstanceGet(node);
+ // We compute the function type instead of reading it of [node] since the
+ // receiver and argument types might have improved through inference of
+ // effectively final variable types and type promotion.
+ ir.DartType resultType =
+ _computeInstanceGetType(receiverType, node.interfaceTarget);
+ node.resultType = resultType;
+ receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
if (node.name.text == Identifiers.runtimeType_) {
+ // This handles `runtimeType` access on non-Never types, like in
+ // `(throw 'foo').runtimeType`.
handleRuntimeTypeGet(receiverType, node);
}
return resultType;
@@ -369,8 +406,14 @@
@override
ir.DartType visitInstanceTearOff(ir.InstanceTearOff node) {
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType resultType = super.visitInstanceTearOff(node);
- assert(node.name.text == Identifiers.runtimeType_,
+ // We compute the function type instead of reading it of [node] since the
+ // receiver and argument types might have improved through inference of
+ // effectively final variable types and type promotion.
+ ir.DartType resultType =
+ _computeInstanceGetType(receiverType, node.interfaceTarget);
+ node.resultType = resultType;
+ receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+ assert(node.name.text != Identifiers.runtimeType_,
"Unexpected .runtimeType instance tear-off.");
handleInstanceGet(node, receiverType, node.interfaceTarget, resultType);
return resultType;
@@ -379,9 +422,8 @@
@override
ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) {
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType resultType = super.visitFunctionTearOff(node);
- handleDynamicGet(node, receiverType, ir.Name.callName, resultType);
- return resultType;
+ handleDynamicGet(node, receiverType, ir.Name.callName, receiverType);
+ return receiverType;
}
void handleDynamicSet(ir.Expression node, ir.DartType receiverType,
@@ -452,7 +494,7 @@
@override
ir.DartType visitDynamicSet(ir.DynamicSet node) {
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType valueType = super.visitDynamicSet(node);
+ ir.DartType valueType = visitNode(node.value);
ir.Member interfaceTarget = _resolveDynamicSet(receiverType, node.name);
if (interfaceTarget != null) {
ir.DartType setterType =
@@ -470,7 +512,7 @@
ir.InstanceSet instanceSet = ir.InstanceSet(
ir.InstanceAccessKind.Instance, node.receiver, node.name, value,
interfaceTarget: interfaceTarget);
- node.replaceWith(instanceSet);
+ _replaceExpression(node, instanceSet);
receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
handleInstanceSet(node, receiverType, interfaceTarget, valueType);
} else {
@@ -482,7 +524,7 @@
@override
ir.DartType visitInstanceSet(ir.InstanceSet node) {
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType valueType = super.visitInstanceSet(node);
+ ir.DartType valueType = visitNode(node.value);
handleInstanceSet(node, receiverType, node.interfaceTarget, valueType);
return valueType;
}
@@ -522,74 +564,77 @@
return receiverType;
}
- /// Returns `true` if [member] can be called with the structure of
- /// [arguments].
- bool _isApplicable(ir.Arguments arguments, ir.Member member) {
- /// Returns `true` if [arguments] are applicable to the function type
- /// structure.
- bool isFunctionTypeApplicable(
- int typeParameterCount,
- int requiredParameterCount,
- int positionalParameterCount,
- Iterable<String> Function() getNamedParameters) {
- if (arguments.types.isNotEmpty &&
- arguments.types.length != typeParameterCount) {
- return false;
- }
- if (arguments.positional.length < requiredParameterCount) {
- return false;
- }
- if (arguments.positional.length > positionalParameterCount) {
- return false;
- }
- Iterable<String> namedParameters = getNamedParameters();
- if (arguments.named.length > namedParameters.length) {
- return false;
- }
- if (arguments.named.isNotEmpty) {
- for (ir.NamedExpression namedArguments in arguments.named) {
- if (!namedParameters.contains(namedArguments.name)) {
- return false;
- }
- }
- }
- return true;
- }
-
- /// Returns `true` if [arguments] are applicable to a value of the static
- /// [type].
- bool isTypeApplicable(ir.DartType type) {
- if (type is ir.DynamicType) return true;
- if (type == typeEnvironment.coreTypes.functionLegacyRawType ||
- type == typeEnvironment.coreTypes.functionNullableRawType ||
- type == typeEnvironment.coreTypes.functionNonNullableRawType)
- return true;
- if (type is ir.FunctionType) {
- return isFunctionTypeApplicable(
- type.typeParameters.length,
- type.requiredParameterCount,
- type.positionalParameters.length,
- () => type.namedParameters.map((p) => p.name).toSet());
- }
+ /// Returns `true` if [arguments] are applicable to the function type
+ /// structure.
+ bool _isApplicableToFunctionType(
+ ir.Arguments arguments,
+ int typeParameterCount,
+ int requiredParameterCount,
+ int positionalParameterCount,
+ Iterable<String> Function() getNamedParameters) {
+ if (arguments.types.isNotEmpty &&
+ arguments.types.length != typeParameterCount) {
return false;
}
+ if (arguments.positional.length < requiredParameterCount) {
+ return false;
+ }
+ if (arguments.positional.length > positionalParameterCount) {
+ return false;
+ }
+ Iterable<String> namedParameters = getNamedParameters();
+ if (arguments.named.length > namedParameters.length) {
+ return false;
+ }
+ if (arguments.named.isNotEmpty) {
+ for (ir.NamedExpression namedArguments in arguments.named) {
+ if (!namedParameters.contains(namedArguments.name)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ /// Returns `true` if [arguments] are applicable to a value of the static
+ /// [type].
+ bool _isApplicableToType(ir.Arguments arguments, ir.DartType type) {
+ if (type is ir.DynamicType) return true;
+ if (type == typeEnvironment.coreTypes.functionLegacyRawType ||
+ type == typeEnvironment.coreTypes.functionNullableRawType ||
+ type == typeEnvironment.coreTypes.functionNonNullableRawType)
+ return true;
+ if (type is ir.FunctionType) {
+ return _isApplicableToFunctionType(
+ arguments,
+ type.typeParameters.length,
+ type.requiredParameterCount,
+ type.positionalParameters.length,
+ () => type.namedParameters.map((p) => p.name).toSet());
+ }
+ return false;
+ }
+
+ /// Returns `true` if [member] can be called with the structure of
+ /// [arguments].
+ bool _isApplicableToMember(ir.Arguments arguments, ir.Member member) {
if (member is ir.Procedure) {
if (member.kind == ir.ProcedureKind.Setter ||
member.kind == ir.ProcedureKind.Factory) {
return false;
} else if (member.kind == ir.ProcedureKind.Getter) {
- return isTypeApplicable(member.getterType);
+ return _isApplicableToType(arguments, member.getterType);
} else if (member.kind == ir.ProcedureKind.Method ||
member.kind == ir.ProcedureKind.Operator) {
- return isFunctionTypeApplicable(
+ return _isApplicableToFunctionType(
+ arguments,
member.function.typeParameters.length,
member.function.requiredParameterCount,
member.function.positionalParameters.length,
() => member.function.namedParameters.map((p) => p.name).toSet());
}
} else if (member is ir.Field) {
- return isTypeApplicable(member.type);
+ return _isApplicableToType(arguments, member.type);
}
return false;
}
@@ -703,18 +748,41 @@
if (receiverType is ir.InterfaceType) {
ir.Member member =
hierarchy.getInterfaceMember(receiverType.classNode, name);
- if (_isApplicable(arguments, member)) {
+ if (_isApplicableToMember(arguments, member)) {
return member;
}
}
return null;
}
+ /// Computes the function type of the function invocation of type
+ /// [functionType] with the given [argument].
+ ir.DartType _computeFunctionInvocationType(
+ ir.DartType functionType, ir.Arguments arguments) {
+ if (functionType is ir.FunctionType) {
+ List<ir.DartType> typeArguments = arguments.types;
+ if (functionType.typeParameters.isNotEmpty && typeArguments.isEmpty) {
+ // If this was a dynamic call the invocation does not have the
+ // inferred default type arguments so we need to create them here
+ // to perform a valid substitution.
+ typeArguments =
+ functionType.typeParameters.map((t) => t.defaultType).toList();
+ }
+ return ir.Substitution.fromPairs(
+ functionType.typeParameters, typeArguments)
+ .substituteType(functionType.withoutTypeParameters);
+ }
+ return functionType;
+ }
+
/// Computes the function type of the instance invocation [node] on a receiver
/// of type [receiverType] on the [interfaceTarget] with the given
/// [argumentTypes].
- ir.DartType _computeInvocationFunctionType(ir.DartType receiverType,
- ir.Member interfaceTarget, ir.Arguments arguments) {
+ ir.DartType _computeInstanceInvocationType(
+ ir.DartType receiverType,
+ ir.Member interfaceTarget,
+ ir.Arguments arguments,
+ ArgumentTypes argumentTypes) {
ir.Class superclass = interfaceTarget.enclosingClass;
ir.Substitution receiverSubstitution = ir.Substitution.fromInterfaceType(
getTypeAsInstanceOf(receiverType, superclass));
@@ -737,27 +805,20 @@
ir.Substitution.fromPairs(functionType.typeParameters, typeArguments)
.substituteType(functionType.withoutTypeParameters);
}
+ if (isSpecialCasedBinaryOperator(interfaceTarget)) {
+ ir.DartType argumentType = argumentTypes.positional[0];
+ ir.DartType resultType = typeEnvironment
+ .getTypeOfSpecialCasedBinaryOperator(receiverType, argumentType);
+ return new ir.FunctionType(
+ <ir.DartType>[argumentType], resultType, currentLibrary.nonNullable);
+ }
return getterType;
}
- /// Computes the result type of the instance invocation [node] on a receiver
- /// of type [receiverType] on the [interfaceTarget] with the [functionType]
- /// computed from the [receiverType], [argumentTypes] and the
- /// [interfaceTarget].
- ir.DartType _computeInstanceInvocationReturnTypeFromFunctionType(
- ir.DartType receiverType,
- ir.Member interfaceTarget,
- ArgumentTypes argumentTypes,
- ir.DartType functionType) {
- if (isSpecialCasedBinaryOperator(interfaceTarget)) {
- ir.DartType argumentType = argumentTypes.positional[0];
- return typeEnvironment.getTypeOfSpecialCasedBinaryOperator(
- receiverType, argumentType);
- } else if (functionType is ir.FunctionType) {
- return functionType.returnType;
- } else {
- return const ir.DynamicType();
- }
+ ir.DartType _getFunctionReturnType(ir.DartType functionType) {
+ return functionType is ir.FunctionType
+ ? functionType.returnType
+ : const ir.DynamicType();
}
/// Computes the result type of the dynamic invocation [node] on a receiver of
@@ -858,8 +919,8 @@
receiverType, node.name, node.arguments);
ir.DartType returnType;
if (interfaceTarget != null) {
- ir.DartType functionType = _computeInvocationFunctionType(
- receiverType, interfaceTarget, node.arguments);
+ ir.DartType functionType = _computeInstanceInvocationType(
+ receiverType, interfaceTarget, node.arguments, argumentTypes);
if (node.interfaceTarget == null) {
// We change [node] from being a dynamic invocation to an instance
// invocation, so we need to add static type checks to the arguments to
@@ -868,8 +929,7 @@
_updateMethodInvocationTarget(node, argumentTypes, functionType);
node.interfaceTarget = interfaceTarget;
}
- returnType = _computeInstanceInvocationReturnTypeFromFunctionType(
- receiverType, interfaceTarget, argumentTypes, functionType);
+ returnType = _getFunctionReturnType(functionType);
} else {
returnType = _computeDynamicInvocationReturnType(node, receiverType);
}
@@ -914,6 +974,64 @@
}
@override
+ ir.DartType visitInstanceInvocation(ir.InstanceInvocation node) {
+ ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+ ir.DartType receiverType = visitNode(node.receiver);
+ ir.Member interfaceTarget = node.interfaceTarget;
+ // We compute the function type instead of reading it of [node] since the
+ // receiver and argument types might have improved through inference of
+ // effectively final variable types and type promotion.
+ ir.DartType functionType = _computeInstanceInvocationType(
+ receiverType, interfaceTarget, node.arguments, argumentTypes);
+ if (functionType != node.functionType) {
+ node.functionType = functionType;
+ // TODO(johnniwinther): To provide the static guarantee that arguments
+ // of a statically typed call have been checked against the parameter
+ // types we need to call [_updateMethodInvocationTarget]. This can create
+ // uses of type variables are not registered with the closure model so
+ // we skip it for now. Note that this invariant is not currently used
+ // in later phases since it wasn't provided for function invocations in
+ // the old method invocation encoding.
+ //_updateMethodInvocationTarget(node, argumentTypes, functionType);
+ }
+ ir.DartType returnType = _getFunctionReturnType(functionType);
+ receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+ handleInstanceInvocation(
+ node, receiverType, interfaceTarget, argumentTypes);
+ _staticTypeCache._expressionTypes[node] = returnType;
+ return returnType;
+ }
+
+ @override
+ ir.DartType visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+ ArgumentTypes argumentTypes = _visitArguments(node.arguments);
+ ir.DartType receiverType = visitNode(node.receiver);
+ ir.Member interfaceTarget = node.interfaceTarget;
+ // We compute the function type instead of reading it of [node] since the
+ // receiver and argument types might have improved through inference of
+ // effectively final variable types and type promotion.
+ ir.DartType functionType = _computeInstanceInvocationType(
+ receiverType, interfaceTarget, node.arguments, argumentTypes);
+ if (functionType != node.functionType) {
+ node.functionType = functionType;
+ // TODO(johnniwinther): To provide the static guarantee that arguments
+ // of a statically typed call have been checked against the parameter
+ // types we need to call [_updateMethodInvocationTarget]. This can create
+ // uses of type variables are not registered with the closure model so
+ // we skip it for now. Note that this invariant is not currently used
+ // in later phases since it wasn't provided for function invocations in
+ // the old method invocation encoding.
+ //_updateMethodInvocationTarget(node, argumentTypes, functionType);
+ }
+ ir.DartType returnType = _getFunctionReturnType(functionType);
+ receiverType = _narrowInstanceReceiver(node.interfaceTarget, receiverType);
+ handleInstanceInvocation(
+ node, receiverType, interfaceTarget, argumentTypes);
+ _staticTypeCache._expressionTypes[node] = returnType;
+ return returnType;
+ }
+
+ @override
ir.DartType visitDynamicInvocation(ir.DynamicInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType receiverType = visitNode(node.receiver);
@@ -921,31 +1039,67 @@
receiverType, node.name, node.arguments);
if (interfaceTarget != null) {
// We can turn the dynamic invocation into an instance invocation.
- ir.DartType functionType = _computeInvocationFunctionType(
- receiverType, interfaceTarget, node.arguments);
- ir.InstanceInvocation instanceInvocation = ir.InstanceInvocation(
- ir.InstanceAccessKind.Instance,
- node.receiver,
- node.name,
- node.arguments,
- interfaceTarget: interfaceTarget,
- functionType: functionType);
- node.replaceWith(instanceInvocation);
- _updateMethodInvocationTarget(
- instanceInvocation, argumentTypes, functionType);
- ir.DartType returnType =
- _computeInstanceInvocationReturnTypeFromFunctionType(
- receiverType, interfaceTarget, argumentTypes, functionType);
+ ir.DartType functionType = _computeInstanceInvocationType(
+ receiverType, interfaceTarget, node.arguments, argumentTypes);
+ ir.Expression replacement;
+ if (interfaceTarget is ir.Field ||
+ (interfaceTarget is ir.Procedure && interfaceTarget.isGetter)) {
+ // This should actually be a function invocation of an instance get but
+ // this doesn't work for invocation of js-interop properties. We
+ // therefore use [ir.MethodInvocation] instead.
+ // TODO(johnniwinther): Use [ir.InstanceGetterInvocation] instead.
+ replacement = ir.MethodInvocation(
+ node.receiver, node.name, node.arguments, interfaceTarget)
+ ..fileOffset = node.fileOffset;
+ } else {
+ replacement = ir.InstanceInvocation(ir.InstanceAccessKind.Instance,
+ node.receiver, node.name, node.arguments,
+ interfaceTarget: interfaceTarget, functionType: functionType)
+ ..fileOffset = node.fileOffset;
+ }
+ _replaceExpression(node, replacement);
+ _updateMethodInvocationTarget(replacement, argumentTypes, functionType);
+ ir.DartType resultType = _getFunctionReturnType(functionType);
receiverType = _narrowInstanceReceiver(interfaceTarget, receiverType);
handleInstanceInvocation(
- instanceInvocation, receiverType, interfaceTarget, argumentTypes);
- _staticTypeCache._expressionTypes[node] = returnType;
- return returnType;
+ replacement, receiverType, interfaceTarget, argumentTypes);
+ if (replacement is ir.MethodInvocation) {
+ _staticTypeCache._expressionTypes[replacement] = resultType;
+ }
+ return resultType;
+ } else if (node.name == ir.Name.callName &&
+ (receiverType is ir.FunctionType ||
+ (receiverType is ir.InterfaceType &&
+ receiverType.classNode == typeEnvironment.functionClass)) &&
+ _isApplicableToType(node.arguments, receiverType)) {
+ ir.DartType functionType =
+ _computeFunctionInvocationType(receiverType, node.arguments);
+ bool hasFunctionType = functionType is ir.FunctionType;
+ ir.FunctionInvocation replacement = ir.FunctionInvocation(
+ hasFunctionType
+ ? ir.FunctionAccessKind.FunctionType
+ : ir.FunctionAccessKind.Function,
+ node.receiver,
+ node.arguments,
+ functionType: hasFunctionType ? functionType : null)
+ ..fileOffset = node.fileOffset;
+ ir.DartType resultType = _getFunctionReturnType(functionType);
+ _replaceExpression(node, replacement);
+ _updateMethodInvocationTarget(replacement, argumentTypes, functionType);
+ handleFunctionInvocation(
+ replacement, receiverType, argumentTypes, resultType);
+ return resultType;
} else {
ir.DartType returnType =
_computeDynamicInvocationReturnType(node, receiverType);
_staticTypeCache._expressionTypes[node] = returnType;
handleDynamicInvocation(node, receiverType, argumentTypes, returnType);
+ if (operatorFromString(node.name.text) == null &&
+ receiverType is ir.DynamicType) {
+ // We might implicitly call a getter that returns a function.
+ handleFunctionInvocation(
+ node, const ir.DynamicType(), argumentTypes, returnType);
+ }
return returnType;
}
}
@@ -954,15 +1108,30 @@
ir.DartType visitEqualsCall(ir.EqualsCall node) {
ir.DartType leftType = visitNode(node.left);
ir.DartType rightType = visitNode(node.right);
+ // This is accessed to ensure that [typeMapWhenTrue] and [typeMapWhenFalse]
+ // are joined as the result of this node.
+ // This is related to dartbug.com/45053
+ _flattenTypeMap();
+ leftType = _narrowInstanceReceiver(node.interfaceTarget, leftType);
handleEqualsCall(
node.left, leftType, node.right, rightType, node.interfaceTarget);
return super.visitEqualsCall(node);
}
+ // TODO(johnniwinther): Remove this after the new method invocation has landed
+ // stably. This is only included to make the transition a no-op.
+ void handleEqualsNull(ir.EqualsNull node, ir.DartType expressionType) {}
+
@override
ir.DartType visitEqualsNull(ir.EqualsNull node) {
- visitNode(node.expression);
+ ir.DartType expressionType = visitNode(node.expression);
+ if (expressionType is ir.DynamicType) {
+ expressionType = currentLibrary.isNonNullableByDefault
+ ? typeEnvironment.objectNullableRawType
+ : typeEnvironment.objectLegacyRawType;
+ }
_registerEqualsNull(typeMap, node.expression);
+ handleEqualsNull(node, expressionType);
return super.visitEqualsNull(node);
}
@@ -970,8 +1139,18 @@
ir.DartType visitFunctionInvocation(ir.FunctionInvocation node) {
ArgumentTypes argumentTypes = _visitArguments(node.arguments);
ir.DartType receiverType = visitNode(node.receiver);
- ir.DartType returnType = super.visitFunctionInvocation(node);
- handleFunctionInvocation(node, receiverType, argumentTypes, returnType);
+ ir.DartType functionType =
+ _computeFunctionInvocationType(receiverType, node.arguments);
+ if (functionType is ir.FunctionType) {
+ // We might have improved the known function type through inference of
+ // effectively final variable types and type promotion.
+ node.functionType = functionType;
+ }
+ // We compute the return type instead of reading it of [node] since the
+ // receiver and argument types might have improved through inference of
+ // effectively final variable types and type promotion.
+ ir.DartType returnType = _getFunctionReturnType(functionType);
+ handleFunctionInvocation(node, functionType, argumentTypes, returnType);
return returnType;
}
@@ -1269,7 +1448,8 @@
@override
ir.DartType visitBlock(ir.Block node) {
- assert(_pendingRuntimeTypeUseData.isEmpty);
+ assert(_pendingRuntimeTypeUseData.isEmpty,
+ "Incomplete RuntimeTypeUseData: $_pendingRuntimeTypeUseData");
ir.DartType type;
for (ir.Statement statement in node.statements) {
if (!completes(visitNode(statement))) {
@@ -1770,6 +1950,12 @@
final List<ir.DartType> named;
ArgumentTypes(this.positional, this.named);
+
+ @override
+ String toString() {
+ return 'ArgumentTypes(position=[${positional.join(',')}],'
+ ' named=[${named.join(',')}])';
+ }
}
/// Type information collected for a single path for a local variable.
diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart
index 7330972..315ee23 100644
--- a/pkg/compiler/lib/src/ir/static_type_base.dart
+++ b/pkg/compiler/lib/src/ir/static_type_base.dart
@@ -262,6 +262,10 @@
node.getStaticType(staticTypeContext);
@override
+ ir.DartType visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) =>
+ node.getStaticType(staticTypeContext);
+
+ @override
ir.DartType visitFunctionTearOff(ir.FunctionTearOff node) =>
node.getStaticType(staticTypeContext);
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index b8c1318..eab6a08 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -118,18 +118,26 @@
ir.Expression body = node.body;
if (node.variable.name == null &&
node.variable.isFinal &&
- body is ir.ConditionalExpression &&
- body.condition is ir.MethodInvocation &&
- isNullLiteral(body.then)) {
- ir.MethodInvocation invocation = body.condition;
- ir.Expression receiver = invocation.receiver;
- if (invocation.name.text == '==' &&
- receiver is ir.VariableGet &&
- receiver.variable == node.variable &&
- isNullLiteral(invocation.arguments.positional.single)) {
- // We have
- // let #t1 = e0 in #t1 == null ? null : e1
- return new NullAwareExpression(node.variable, body.otherwise);
+ body is ir.ConditionalExpression) {
+ if (body.condition is ir.MethodInvocation && isNullLiteral(body.then)) {
+ ir.MethodInvocation invocation = body.condition;
+ ir.Expression receiver = invocation.receiver;
+ if (invocation.name.text == '==' &&
+ receiver is ir.VariableGet &&
+ receiver.variable == node.variable &&
+ isNullLiteral(invocation.arguments.positional.single)) {
+ // We have
+ // let #t1 = e0 in #t1 == null ? null : e1
+ return new NullAwareExpression(node.variable, body.otherwise);
+ }
+ } else if (body.condition is ir.EqualsNull) {
+ ir.EqualsNull equalsNull = body.condition;
+ ir.Expression receiver = equalsNull.expression;
+ if (receiver is ir.VariableGet && receiver.variable == node.variable) {
+ // We have
+ // let #t1 = e0 in #t1 == null ? null : e1
+ return new NullAwareExpression(node.variable, body.otherwise);
+ }
}
}
}
@@ -153,7 +161,16 @@
//
// (let _ = check(prefix) in prefix::field).property
if (node is ir.StaticGet || node is ir.ConstantExpression) {
- while (parent is ir.PropertyGet || parent is ir.MethodInvocation) {
+ while (parent is ir.PropertyGet ||
+ parent is ir.InstanceGet ||
+ parent is ir.DynamicGet ||
+ parent is ir.InstanceTearOff ||
+ parent is ir.FunctionTearOff ||
+ parent is ir.MethodInvocation ||
+ parent is ir.InstanceInvocation ||
+ parent is ir.InstanceGetterInvocation ||
+ parent is ir.DynamicInvocation ||
+ parent is ir.FunctionInvocation) {
parent = parent.parent;
}
}
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
index 4802555..d9d65cc 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_merger.dart
@@ -83,17 +83,20 @@
this.lazyInitializers.addAll(that.lazyInitializers);
this.nativeSupport.addAll(that.nativeSupport);
this.successors.remove(that);
+ this.predecessors.remove(that);
that.successors.forEach((fragment) {
+ if (fragment == this) return;
+ this.successors.add(fragment);
fragment.predecessors.remove(that);
fragment.predecessors.add(this);
});
- this.successors.addAll(that.successors);
- that.predecessors.remove(this);
that.predecessors.forEach((fragment) {
+ if (fragment == this) return;
+ this.predecessors.add(fragment);
fragment.successors.remove(that);
fragment.successors.add(this);
});
- this.predecessors.addAll(that.predecessors);
+ that.clearAll();
this.size += that.size;
return this;
}
@@ -102,6 +105,7 @@
Program program, Map<Fragment, FinalizedFragment> fragmentMap) {
FinalizedFragment finalizedFragment;
var seedFragment = fragments.first;
+ var seedOutputUnit = seedFragment.outputUnit;
// If we only have a single fragment, then wen just finalize it by itself.
// Otherwise, we finalize an entire group of fragments into a single
@@ -109,7 +113,7 @@
if (fragments.length == 1) {
finalizedFragment = FinalizedFragment(
seedFragment.outputFileName,
- seedFragment.outputUnit,
+ [seedOutputUnit],
seedFragment.libraries,
classPrototypes.first,
closurePrototypes.first,
@@ -122,21 +126,22 @@
staticNonFinalFields.first,
lazyInitializers.first,
nativeSupport.first,
- program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+ program.metadataTypesForOutputUnit(seedOutputUnit));
fragmentMap[seedFragment] = finalizedFragment;
} else {
+ List<OutputUnit> outputUnits = [seedOutputUnit];
List<Library> libraries = [];
for (var fragment in fragments) {
- if (seedFragment.outputUnit != fragment.outputUnit) {
- program.mergeOutputUnitMetadata(
- seedFragment.outputUnit, fragment.outputUnit);
- seedFragment.outputUnit.merge(fragment.outputUnit);
+ var fragmentOutputUnit = fragment.outputUnit;
+ if (seedOutputUnit != fragmentOutputUnit) {
+ program.mergeOutputUnitMetadata(seedOutputUnit, fragmentOutputUnit);
+ outputUnits.add(fragmentOutputUnit);
}
libraries.addAll(fragment.libraries);
}
finalizedFragment = FinalizedFragment(
seedFragment.outputFileName,
- seedFragment.outputUnit,
+ outputUnits,
libraries,
js.Block(classPrototypes),
js.Block(closurePrototypes),
@@ -149,7 +154,7 @@
js.Block(staticNonFinalFields),
js.Block(lazyInitializers),
js.Block(nativeSupport),
- program.metadataTypesForOutputUnit(seedFragment.outputUnit));
+ program.metadataTypesForOutputUnit(seedOutputUnit));
for (var fragment in fragments) {
fragmentMap[fragment] = finalizedFragment;
}
@@ -183,11 +188,31 @@
}
return "${outputUnitStrings.join('+')}";
}
+
+ /// Clears all [PreFragment] data structure and zeros out the size. Should be
+ /// used only after merging to GC internal data structures.
+ void clearAll() {
+ fragments.clear();
+ classPrototypes.clear();
+ closurePrototypes.clear();
+ inheritance.clear();
+ methodAliases.clear();
+ tearOffs.clear();
+ constants.clear();
+ typeRules.clear();
+ variances.clear();
+ staticNonFinalFields.clear();
+ lazyInitializers.clear();
+ nativeSupport.clear();
+ successors.clear();
+ predecessors.clear();
+ size = 0;
+ }
}
class FinalizedFragment {
final String outputFileName;
- final OutputUnit outputUnit;
+ final List<OutputUnit> outputUnits;
final List<Library> libraries;
final js.Statement classPrototypes;
final js.Statement closurePrototypes;
@@ -204,7 +229,7 @@
FinalizedFragment(
this.outputFileName,
- this.outputUnit,
+ this.outputUnits,
this.libraries,
this.classPrototypes,
this.closurePrototypes,
@@ -247,6 +272,12 @@
isEmptyStatement(lazyInitializers) &&
isEmptyStatement(nativeSupport);
}
+
+ // The 'main' [OutputUnit] for this [FinalizedFragment].
+ // TODO(joshualitt): Refactor this to more clearly disambiguate between
+ // [OutputUnits](units of deferred merging), fragments(units of emitted code),
+ // and files.
+ OutputUnit get canonicalOutputUnit => outputUnits.first;
}
class _Partition {
@@ -261,6 +292,7 @@
class FragmentMerger {
final CompilerOptions _options;
+ int totalSize = 0;
FragmentMerger(this._options);
@@ -284,16 +316,63 @@
return loadMap;
}
- // Attaches predecessors to each PreFragment. We only care about
- // direct predecessors.
- static void attachDependencies(Map<String, List<Fragment>> programLoadMap,
- Map<Fragment, PreFragment> fragmentMap) {
- programLoadMap.forEach((loadId, fragments) {
- for (int i = 0; i < fragments.length - 1; i++) {
- var fragment = fragmentMap[fragments[i]];
- var nextFragment = fragmentMap[fragments[i + 1]];
- fragment.successors.add(nextFragment);
- nextFragment.predecessors.add(fragment);
+ /// Given a list of OutputUnits sorted by their import entites,
+ /// returns a map of all the direct edges between output units.
+ Map<OutputUnit, Set<OutputUnit>> createDirectEdges(
+ List<OutputUnit> allOutputUnits) {
+ Map<OutputUnit, Set<OutputUnit>> backEdges = {};
+ for (int i = 0; i < allOutputUnits.length; i++) {
+ var a = allOutputUnits[i];
+ var aImports = a.imports;
+ for (int j = i + 1; j < allOutputUnits.length; j++) {
+ var b = allOutputUnits[j];
+ if (b.imports.containsAll(aImports)) {
+ backEdges[b] ??= {};
+
+ // Remove transitive edges from nodes that will reach 'b' from the
+ // edge we just added.
+ // Note: Because we add edges in order (starting from the smallest
+ // sets) we always add transitive edges before the last direct edge.
+ backEdges[b].removeWhere((c) => aImports.containsAll(c.imports));
+
+ // Create an edge to denote that 'b' must be loaded before 'a'.
+ backEdges[b].add(a);
+ }
+ }
+ }
+
+ Map<OutputUnit, Set<OutputUnit>> forwardEdges = {};
+ backEdges.forEach((b, edges) {
+ for (var a in edges) {
+ (forwardEdges[a] ??= {}).add(b);
+ }
+ });
+ return forwardEdges;
+ }
+
+ /// Attachs predecessors and successors to each PreFragment.
+ void attachDependencies(Map<Fragment, PreFragment> fragmentMap,
+ List<PreFragment> preDeferredFragments) {
+ // Create a map of OutputUnit to Fragment.
+ Map<OutputUnit, Fragment> outputUnitMap = {};
+ List<OutputUnit> allOutputUnits = [];
+ for (var preFragment in preDeferredFragments) {
+ var fragment = preFragment.fragments.single;
+ var outputUnit = fragment.outputUnit;
+ outputUnitMap[outputUnit] = fragment;
+ allOutputUnits.add(outputUnit);
+ totalSize += preFragment.size;
+ }
+ allOutputUnits.sort();
+
+ // Get a list of direct edges and then attach them to PreFragments.
+ var allEdges = createDirectEdges(allOutputUnits);
+ allEdges.forEach((outputUnit, edges) {
+ var predecessor = fragmentMap[outputUnitMap[outputUnit]];
+ for (var edge in edges) {
+ var successor = fragmentMap[outputUnitMap[edge]];
+ predecessor.successors.add(successor);
+ successor.predecessors.add(predecessor);
}
});
}
@@ -315,12 +394,6 @@
});
int desiredNumberOfFragment = _options.mergeFragmentsThreshold;
- // TODO(joshualitt): Precalculate totalSize when computing dependencies.
- int totalSize = 0;
- for (var preFragment in preDeferredFragments) {
- totalSize += preFragment.size;
- }
-
int idealFragmentSize = (totalSize / desiredNumberOfFragment).ceil();
List<_Partition> partitions = [];
void add(PreFragment next) {
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index e12027b..943f80e 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -98,6 +98,7 @@
final JClosedWorld _closedWorld;
final ConstantOrdering _constantOrdering;
final SourceInformationStrategy _sourceInformationStrategy;
+ final FragmentMerger fragmentMerger;
// The full code that is written to each hunk part-file.
final Map<OutputUnit, CodeOutput> emittedOutputBuffers = {};
@@ -128,7 +129,8 @@
this._sourceInformationStrategy,
RecipeEncoder rtiRecipeEncoder,
this._shouldGenerateSourceMap)
- : _constantOrdering = new ConstantOrdering(_closedWorld.sorter) {
+ : _constantOrdering = new ConstantOrdering(_closedWorld.sorter),
+ fragmentMerger = FragmentMerger(_options) {
this.constantEmitter = new ConstantEmitter(
_options,
_namer,
@@ -227,12 +229,11 @@
}
});
- // Attach dependencies to each PreFragment.
- FragmentMerger.attachDependencies(program.loadMap, preFragmentMap);
-
+ // If we are going to merge, then we attach dependencies to each PreFragment
+ // and merge.
if (shouldMergeFragments) {
preDeferredFragments = _task.measureSubtask('merge fragments', () {
- FragmentMerger fragmentMerger = FragmentMerger(_options);
+ fragmentMerger.attachDependencies(preFragmentMap, preDeferredFragments);
return fragmentMerger.mergeFragments(preDeferredFragments);
});
}
@@ -252,7 +253,7 @@
if (fragmentCode != null) {
deferredFragmentsCode[finalizedFragment] = fragmentCode;
} else {
- omittedOutputUnits.add(finalizedFragment.outputUnit);
+ omittedOutputUnits.addAll(finalizedFragment.outputUnits);
}
}
@@ -419,7 +420,9 @@
hunkPrefix, deferredExtension, OutputType.jsPart),
outputListeners);
- emittedOutputBuffers[fragment.outputUnit] = output;
+ // TODO(joshualitt): This breaks dump_info when we merge, but fixing it will
+ // require updating the schema.
+ emittedOutputBuffers[fragment.canonicalOutputUnit] = output;
// The [code] contains the function that must be invoked when the deferred
// hunk is loaded.
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index 7a42127..5849d77 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -1249,12 +1249,30 @@
if (node is ir.PropertyGet) {
return getGetterSelector(node.name);
}
+ if (node is ir.InstanceGet) {
+ return getGetterSelector(node.name);
+ }
+ if (node is ir.InstanceTearOff) {
+ return getGetterSelector(node.name);
+ }
+ if (node is ir.DynamicGet) {
+ return getGetterSelector(node.name);
+ }
+ if (node is ir.FunctionTearOff) {
+ return getGetterSelector(ir.Name.callName);
+ }
if (node is ir.SuperPropertyGet) {
return getGetterSelector(node.name);
}
if (node is ir.PropertySet) {
return getSetterSelector(node.name);
}
+ if (node is ir.InstanceSet) {
+ return getSetterSelector(node.name);
+ }
+ if (node is ir.DynamicSet) {
+ return getSetterSelector(node.name);
+ }
if (node is ir.SuperPropertySet) {
return getSetterSelector(node.name);
}
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 60301f2..a0ce422 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -86,7 +86,7 @@
bool get supportsExplicitGetterCalls => false;
@override
- bool get supportsNewMethodInvocationEncoding => false;
+ bool get supportsNewMethodInvocationEncoding => true;
@override
List<String> get extraRequiredLibraries => _requiredLibraries[name];
diff --git a/pkg/compiler/lib/src/kernel/kernel_impact.dart b/pkg/compiler/lib/src/kernel/kernel_impact.dart
index 9a40f68..bd6095b 100644
--- a/pkg/compiler/lib/src/kernel/kernel_impact.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_impact.dart
@@ -665,8 +665,10 @@
const <DartType>[]));
}
+ // TODO(johnniwinther): Change [node] `InstanceGet` when the old method
+ // invocation encoding is no longer used.
@override
- void registerRuntimeTypeUse(ir.PropertyGet node, RuntimeTypeUseKind kind,
+ void registerRuntimeTypeUse(ir.Expression node, RuntimeTypeUseKind kind,
ir.DartType receiverType, ir.DartType argumentType) {
DartType receiverDartType = elementMap.getDartType(receiverType);
DartType argumentDartType =
diff --git a/pkg/compiler/lib/src/serialization/node_indexer.dart b/pkg/compiler/lib/src/serialization/node_indexer.dart
index 6846687..663e593 100644
--- a/pkg/compiler/lib/src/serialization/node_indexer.dart
+++ b/pkg/compiler/lib/src/serialization/node_indexer.dart
@@ -166,6 +166,12 @@
}
@override
+ void visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+ registerNode(node);
+ super.visitInstanceGetterInvocation(node);
+ }
+
+ @override
void visitDynamicInvocation(ir.DynamicInvocation node) {
registerNode(node);
super.visitDynamicInvocation(node);
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 2831f5c..864f6a8 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -5060,6 +5060,9 @@
if (node is ir.MethodInvocation) {
invoke.isInvariant = node.isInvariant;
invoke.isBoundsSafe = node.isBoundsSafe;
+ } else if (node is ir.InstanceInvocation) {
+ invoke.isInvariant = node.isInvariant;
+ invoke.isBoundsSafe = node.isBoundsSafe;
}
push(invoke);
}
@@ -5257,6 +5260,11 @@
}
@override
+ void visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+ _handleMethodInvocation(node, node.receiver, node.arguments);
+ }
+
+ @override
void visitDynamicInvocation(ir.DynamicInvocation node) {
_handleMethodInvocation(node, node.receiver, node.arguments);
}
@@ -5268,8 +5276,27 @@
@override
void visitLocalFunctionInvocation(ir.LocalFunctionInvocation node) {
- _handleMethodInvocation(
- node, ir.VariableGet(node.variable), node.arguments);
+ Local local = _localsMap.getLocalVariable(node.variable);
+ stack.add(localsHandler.readLocal(local,
+ sourceInformation: _sourceInformationBuilder.buildGet(node)));
+ HInstruction receiverInstruction = pop();
+ Selector selector = _elementMap.getSelector(node);
+ List<DartType> typeArguments = [];
+ selector =
+ _fillDynamicTypeArguments(selector, node.arguments, typeArguments);
+ _pushDynamicInvocation(
+ node,
+ StaticType(_elementMap.getDartType(node.variable.type),
+ computeClassRelationFromType(node.variable.type)),
+ _typeInferenceMap.receiverTypeOfInvocation(node, _abstractValueDomain),
+ selector,
+ [
+ receiverInstruction,
+ ..._visitArgumentsForDynamicTarget(
+ selector, node.arguments, typeArguments)
+ ],
+ typeArguments,
+ _sourceInformationBuilder.buildCall(node, node));
}
@override
@@ -5293,8 +5320,28 @@
void visitEqualsNull(ir.EqualsNull node) {
node.expression.accept(this);
HInstruction receiverInstruction = pop();
- return _handleEquals(node, node.expression, receiverInstruction,
- graph.addConstantNull(closedWorld));
+
+ // Hack to detect `null == o`.
+ // TODO(johnniwinther): Remove this after the new method invocation has
+ // landed stably. This is only included to make the transition a no-op.
+ if (node.fileOffset < node.expression.fileOffset) {
+ _pushDynamicInvocation(
+ node,
+ new StaticType(
+ _elementMap.commonElements.nullType, ClassRelation.subtype),
+ _typeInferenceMap.receiverTypeOfInvocation(
+ node, _abstractValueDomain),
+ Selectors.equals,
+ <HInstruction>[
+ graph.addConstantNull(closedWorld),
+ receiverInstruction
+ ],
+ const <DartType>[],
+ _sourceInformationBuilder.buildCall(node.expression, node));
+ } else {
+ _handleEquals(node, node.expression, receiverInstruction,
+ graph.addConstantNull(closedWorld));
+ }
}
@override
@@ -7366,6 +7413,16 @@
}
@override
+ visitInstanceGetterInvocation(ir.InstanceGetterInvocation node) {
+ registerRegularNode();
+ registerReductiveNode();
+ registerCall();
+ visit(node.receiver);
+ skipReductiveNodes(() => visit(node.name));
+ _processArguments(node.arguments, null);
+ }
+
+ @override
visitDynamicInvocation(ir.DynamicInvocation node) {
registerRegularNode();
registerReductiveNode();
diff --git a/pkg/compiler/test/codegen/constant_folding_test.dart b/pkg/compiler/test/codegen/constant_folding_test.dart
index 06d09ad..a3471c3 100644
--- a/pkg/compiler/test/codegen/constant_folding_test.dart
+++ b/pkg/compiler/test/codegen/constant_folding_test.dart
@@ -65,16 +65,16 @@
NEGATIVE_NUMBER_FOLDING, 'main', new RegExp(r"print\(1\)"));
await compile(NULL_EQUALS_FOLDING, entry: 'foo', check: (String generated) {
RegExp regexp = new RegExp(r'a == null');
- Expect.isTrue(regexp.hasMatch(generated));
+ Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
regexp = new RegExp(r'null == b');
- Expect.isTrue(regexp.hasMatch(generated));
+ Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
regexp = new RegExp(r'4 === c');
- Expect.isTrue(regexp.hasMatch(generated));
+ Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
regexp = new RegExp('"foo" === d');
- Expect.isTrue(regexp.hasMatch(generated));
+ Expect.isTrue(regexp.hasMatch(generated), 'No match found for ${regexp}');
});
await compileAndMatch(LIST_LENGTH_FOLDING, 'foo', new RegExp(r"return 3"));
await compileAndMatch(LIST_INDEX_FOLDING, 'foo', new RegExp(r"return 1"));
diff --git a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
index d50cc23..58108e9 100644
--- a/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/deferred_overlapping/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [1{lib1, lib2}], usedBy: [2, 3], needs: []},
- f2: {units: [2{lib1}], usedBy: [], needs: [1]},
- f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ f1: {units: [1{lib1, lib2}], usedBy: [], needs: []},
+ f2: {units: [2{lib1}], usedBy: [], needs: []},
+ f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [1{lib1, lib2}], usedBy: [], needs: [2, 3]},
+ f2: {units: [2{lib1}], usedBy: [1], needs: []},
+ f3: {units: [3{lib2}], usedBy: [1], needs: []}],
steps=[
lib1=(f1, f2),
lib2=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
index d65299a..db1d7a6 100644
--- a/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/dont_inline_deferred_constants/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*spec|three-frag.library:
+/*spec.library:
output_units=[
- f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
- f2: {units: [1{lib1}], usedBy: [], needs: [1]},
- f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ f1: {units: [2{lib1, lib2}], usedBy: [], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: []},
+ f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*three-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}], usedBy: [], needs: [2, 3]},
+ f2: {units: [1{lib1}], usedBy: [1], needs: []},
+ f3: {units: [3{lib2}], usedBy: [1], needs: []}],
steps=[
lib1=(f1, f2),
lib2=(f1, f3)]
@@ -14,8 +24,8 @@
/*two-frag.library:
output_units=[
- f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [2], needs: []},
- f2: {units: [1{lib1}], usedBy: [], needs: [1]}],
+ f1: {units: [2{lib1, lib2}, 3{lib2}], usedBy: [], needs: [2]},
+ f2: {units: [1{lib1}], usedBy: [1], needs: []}],
steps=[
lib1=(f1, f2),
lib2=(f1)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
index ce14794..4642e97 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation1/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
- f2: {units: [1{b}], usedBy: [], needs: [1]},
- f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ f1: {units: [2{b, c}], usedBy: [], needs: []},
+ f2: {units: [1{b}], usedBy: [], needs: []},
+ f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}], usedBy: [], needs: [2, 3]},
+ f2: {units: [1{b}], usedBy: [1], needs: []},
+ f3: {units: [3{c}], usedBy: [1], needs: []}],
steps=[
b=(f1, f2),
c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
index 0aba5e2..4f5bd68 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation2/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
- f2: {units: [2{b}], usedBy: [], needs: [1]},
- f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ f1: {units: [1{b, c}], usedBy: [], needs: []},
+ f2: {units: [2{b}], usedBy: [], needs: []},
+ f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}], usedBy: [], needs: [2, 3]},
+ f2: {units: [2{b}], usedBy: [1], needs: []},
+ f3: {units: [3{c}], usedBy: [1], needs: []}],
steps=[
b=(f1, f2),
c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
index 1629521..f972fc5 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation4/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [2{b, c}], usedBy: [2, 3], needs: []},
- f2: {units: [1{b}], usedBy: [], needs: [1]},
- f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ f1: {units: [2{b, c}], usedBy: [], needs: []},
+ f2: {units: [1{b}], usedBy: [], needs: []},
+ f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [2{b, c}], usedBy: [], needs: [2, 3]},
+ f2: {units: [1{b}], usedBy: [1], needs: []},
+ f3: {units: [3{c}], usedBy: [1], needs: []}],
steps=[
b=(f1, f2),
c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
index 768282f..f9dd67d 100644
--- a/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/instantiation5/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [1{b, c}], usedBy: [2, 3], needs: []},
- f2: {units: [2{b}], usedBy: [], needs: [1]},
- f3: {units: [3{c}], usedBy: [], needs: [1]}],
+ f1: {units: [1{b, c}], usedBy: [], needs: []},
+ f2: {units: [2{b}], usedBy: [], needs: []},
+ f3: {units: [3{c}], usedBy: [], needs: []}],
+ steps=[
+ b=(f1, f2),
+ c=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [1{b, c}], usedBy: [], needs: [2, 3]},
+ f2: {units: [2{b}], usedBy: [1], needs: []},
+ f3: {units: [3{c}], usedBy: [1], needs: []}],
steps=[
b=(f1, f2),
c=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
index 120b83c..4cd9f15 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/lib.dart
@@ -4,25 +4,18 @@
// @dart = 2.7
-/*spec|three-frag.class: Foo:
+/*class: Foo:
class_unit=1{libB},
type_unit=3{libA, libB, libC}
*/
-/*two-frag.class: Foo:
- class_unit=1{libB, libA},
- type_unit=3{libA, libB, libC}
-*/
class Foo {
- /*spec|three-frag.member: Foo.x:member_unit=1{libB}*/
- /*two-frag.member: Foo.x:member_unit=1{libB, libA}*/
+ /*member: Foo.x:member_unit=1{libB}*/
int x;
- /*spec|three-frag.member: Foo.:member_unit=1{libB}*/
- /*two-frag.member: Foo.:member_unit=1{libB, libA}*/
+ /*member: Foo.:member_unit=1{libB}*/
Foo() {
x = DateTime.now().millisecond;
}
- /*spec|three-frag.member: Foo.method:member_unit=1{libB}*/
- /*two-frag.member: Foo.method:member_unit=1{libB, libA}*/
+ /*member: Foo.method:member_unit=1{libB}*/
int method() => x;
}
@@ -31,8 +24,7 @@
return o is Foo;
}
-/*spec|three-frag.member: callFooMethod:member_unit=1{libB}*/
-/*two-frag.member: callFooMethod:member_unit=1{libB, libA}*/
+/*member: callFooMethod:member_unit=1{libB}*/
int callFooMethod() {
return Foo().method();
}
@@ -64,14 +56,10 @@
/*member: Coo.:member_unit=2{libC}*/
class Coo<T> {}
-/*spec|three-frag.class: Doo:
+/*class: Doo:
class_unit=2{libC},
type_unit=5{libB, libC}
*/
-/*two-frag.class: Doo:
- class_unit=2{libC},
- type_unit=5{libB, libC, libA}
-*/
/*member: Doo.:member_unit=2{libC}*/
class Doo<T> extends Coo<T> with Boo<T> {}
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
index 7c98f96..20c2423 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/libb.dart
@@ -6,18 +6,14 @@
import 'lib.dart' as lib;
-/*spec|three-frag.member: callFooMethod:member_unit=1{libB}*/
-/*two-frag.member: callFooMethod:member_unit=1{libB, libA}*/
+/*member: callFooMethod:member_unit=1{libB}*/
int callFooMethod() => lib.callFooMethod();
-/*spec|three-frag.member: isFoo:member_unit=1{libB}*/
-/*two-frag.member: isFoo:member_unit=1{libB, libA}*/
+/*member: isFoo:member_unit=1{libB}*/
bool isFoo(o) => lib.isFoo(o);
-/*spec|three-frag.member: isFunFunFoo:member_unit=1{libB}*/
-/*two-frag.member: isFunFunFoo:member_unit=1{libB, libA}*/
+/*member: isFunFunFoo:member_unit=1{libB}*/
bool isFunFunFoo(o) => lib.isFunFunFoo(o);
-/*spec|three-frag.member: isDooFunFunFoo:member_unit=1{libB}*/
-/*two-frag.member: isDooFunFunFoo:member_unit=1{libB, libA}*/
+/*member: isDooFunFunFoo:member_unit=1{libB}*/
bool isDooFunFunFoo(o) => o is lib.Doo<lib.FunFunFoo>;
diff --git a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
index 6e80197..15e8022 100644
--- a/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/lazy_types/main.dart
@@ -4,12 +4,12 @@
/*spec.library:
output_units=[
- f1: {units: [3{libA, libB, libC}], usedBy: [2, 4], needs: []},
- f2: {units: [4{libA, libC}], usedBy: [3, 6], needs: [1, 4]},
- f3: {units: [6{libA}], usedBy: [], needs: [2]},
- f4: {units: [5{libB, libC}], usedBy: [5, 2], needs: [1]},
- f5: {units: [1{libB}], usedBy: [], needs: [4]},
- f6: {units: [2{libC}], usedBy: [], needs: [2]}],
+ f1: {units: [3{libA, libB, libC}], usedBy: [], needs: []},
+ f2: {units: [4{libA, libC}], usedBy: [], needs: []},
+ f3: {units: [6{libA}], usedBy: [], needs: []},
+ f4: {units: [5{libB, libC}], usedBy: [], needs: []},
+ f5: {units: [1{libB}], usedBy: [], needs: []},
+ f6: {units: [2{libC}], usedBy: [], needs: []}],
steps=[
libA=(f1, f2, f3),
libB=(f1, f4, f5),
@@ -18,9 +18,9 @@
/*two-frag.library:
output_units=[
- f1: {units: [3{libA, libB, libC}], usedBy: [2], needs: []},
- f2: {units: [5{libB, libC, libA}, 4{libA, libC}, 2{libC}], usedBy: [3], needs: [1]},
- f3: {units: [1{libB, libA}, 6{libA}], usedBy: [], needs: [2]}],
+ f1: {units: [3{libA, libB, libC}], usedBy: [], needs: [2]},
+ f2: {units: [5{libB, libC}, 4{libA, libC}, 2{libC}], usedBy: [1], needs: [3]},
+ f3: {units: [1{libB}, 6{libA}], usedBy: [2], needs: []}],
steps=[
libA=(f1, f2, f3),
libB=(f1, f2, f3),
@@ -29,10 +29,10 @@
/*three-frag.library:
output_units=[
- f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}], usedBy: [3, 2, 4], needs: []},
- f2: {units: [6{libA}], usedBy: [], needs: [1]},
- f3: {units: [1{libB}], usedBy: [], needs: [1]},
- f4: {units: [2{libC}], usedBy: [], needs: [1]}],
+ f1: {units: [3{libA, libB, libC}, 5{libB, libC}, 4{libA, libC}], usedBy: [], needs: [4, 3, 2]},
+ f2: {units: [6{libA}], usedBy: [1], needs: []},
+ f3: {units: [1{libB}], usedBy: [1], needs: []},
+ f4: {units: [2{libC}], usedBy: [1], needs: []}],
steps=[
libA=(f1, f2),
libB=(f1, f3),
@@ -45,7 +45,7 @@
import 'libb.dart' deferred as libB;
import 'libc.dart' deferred as libC;
-/*spec|three-frag.member: foo:
+/*member: foo:
constants=[
FunctionConstant(callFooMethod)=1{libB},
FunctionConstant(createB2)=2{libC},
@@ -62,23 +62,6 @@
FunctionConstant(isMega)=6{libA}],
member_unit=main{}
*/
-/*two-frag.member: foo:
- constants=[
- FunctionConstant(callFooMethod)=1{libB, libA},
- FunctionConstant(createB2)=2{libC},
- FunctionConstant(createC3)=2{libC},
- FunctionConstant(createD3)=2{libC},
- FunctionConstant(createDooFunFunFoo)=2{libC},
- FunctionConstant(isDooFunFunFoo)=1{libB, libA},
- FunctionConstant(isFoo)=1{libB, libA},
- FunctionConstant(isFoo)=2{libC},
- FunctionConstant(isFoo)=6{libA},
- FunctionConstant(isFunFunFoo)=1{libB, libA},
- FunctionConstant(isFunFunFoo)=2{libC},
- FunctionConstant(isFunFunFoo)=6{libA},
- FunctionConstant(isMega)=6{libA}],
- member_unit=main{}
-*/
void foo() async {
await libA.loadLibrary();
await libB.loadLibrary();
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
index 64e287e..7f86fc1 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/libB.dart
@@ -35,13 +35,11 @@
f_010_11(Set<String> u, int b) => v(u, '01011', b);
@pragma('dart2js:noInline')
-/*spec|three-frag.member: f_011_01:member_unit=8{b1, b3, b4}*/
-/*two-frag.member: f_011_01:member_unit=8{b1, b3, b4, b2, b5}*/
+/*member: f_011_01:member_unit=8{b1, b3, b4}*/
f_011_01(Set<String> u, int b) => v(u, '01101', b);
@pragma('dart2js:noInline')
-/*spec|two-frag.member: f_011_11:member_unit=9{b1, b2, b3, b4}*/
-/*three-frag.member: f_011_11:member_unit=9{b1, b2, b3, b4, b5}*/
+/*member: f_011_11:member_unit=9{b1, b2, b3, b4}*/
f_011_11(Set<String> u, int b) => v(u, '01111', b);
@pragma('dart2js:noInline')
@@ -105,8 +103,7 @@
f_110_10(Set<String> u, int b) => v(u, '11010', b);
@pragma('dart2js:noInline')
-/*spec.member: f_111_10:member_unit=24{b2, b3, b4, b5}*/
-/*two-frag|three-frag.member: f_111_10:member_unit=24{b2, b3, b4, b5, b1}*/
+/*member: f_111_10:member_unit=24{b2, b3, b4, b5}*/
f_111_10(Set<String> u, int b) => v(u, '11110', b);
@pragma('dart2js:noInline')
@@ -114,8 +111,7 @@
f_001_00(Set<String> u, int b) => v(u, '00100', b);
@pragma('dart2js:noInline')
-/*spec|two-frag.member: f_011_00:member_unit=26{b3, b4}*/
-/*three-frag.member: f_011_00:member_unit=26{b3, b4, b2, b5, b1}*/
+/*member: f_011_00:member_unit=26{b3, b4}*/
f_011_00(Set<String> u, int b) => v(u, '01100', b);
@pragma('dart2js:noInline')
diff --git a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
index 6f86ab9..6cd9224 100644
--- a/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/many_parts/main.dart
@@ -4,37 +4,37 @@
/*spec.library:
output_units=[
- f10: {units: [7{b1, b2, b4}], usedBy: [11, 29], needs: [9, 8]},
- f11: {units: [5{b1, b2, b3}], usedBy: [12, 21, 26], needs: [10, 8]},
- f12: {units: [10{b1, b5}], usedBy: [13, 31], needs: [11, 21]},
- f13: {units: [6{b1, b4}], usedBy: [14, 30], needs: [12, 22]},
- f14: {units: [4{b1, b3}], usedBy: [15, 28], needs: [13, 23]},
- f15: {units: [3{b1, b2}], usedBy: [16, 24], needs: [14, 23]},
- f16: {units: [2{b1}], usedBy: [], needs: [15]},
- f17: {units: [24{b2, b3, b4, b5}], usedBy: [3, 2], needs: [1]},
- f18: {units: [23{b2, b4, b5}], usedBy: [19, 20], needs: [5, 25]},
- f19: {units: [22{b2, b3, b5}], usedBy: [20, 6], needs: [18, 25]},
- f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2, 17], needs: []},
- f20: {units: [20{b2, b3, b4}], usedBy: [9, 7, 6], needs: [19, 18]},
- f21: {units: [21{b2, b5}], usedBy: [22, 12], needs: [11, 26]},
- f22: {units: [19{b2, b4}], usedBy: [23, 13], needs: [21, 27]},
- f23: {units: [18{b2, b3}], usedBy: [15, 14], needs: [22, 27]},
- f24: {units: [17{b2}], usedBy: [], needs: [15]},
- f25: {units: [28{b3, b4, b5}], usedBy: [19, 18], needs: [5, 4]},
- f26: {units: [27{b3, b5}], usedBy: [27, 21], needs: [11, 29]},
- f27: {units: [26{b3, b4}], usedBy: [23, 22], needs: [26, 29]},
- f28: {units: [25{b3}], usedBy: [], needs: [14]},
- f29: {units: [30{b4, b5}], usedBy: [27, 26], needs: [10, 9]},
- f2: {units: [16{b1, b3, b4, b5}], usedBy: [3, 4], needs: [1, 17]},
- f30: {units: [29{b4}], usedBy: [], needs: [13]},
- f31: {units: [31{b5}], usedBy: [], needs: [12]},
- f3: {units: [15{b1, b2, b4, b5}], usedBy: [4, 5], needs: [2, 17]},
- f4: {units: [13{b1, b2, b3, b5}], usedBy: [5, 25], needs: [3, 2]},
- f5: {units: [9{b1, b2, b3, b4}], usedBy: [6, 18, 25], needs: [4, 3]},
- f6: {units: [14{b1, b4, b5}], usedBy: [7, 8], needs: [5, 20, 19]},
- f7: {units: [12{b1, b3, b5}], usedBy: [8, 9], needs: [6, 20]},
- f8: {units: [8{b1, b3, b4}], usedBy: [9, 11, 10], needs: [7, 6]},
- f9: {units: [11{b1, b2, b5}], usedBy: [10, 29], needs: [8, 20, 7]}],
+ f10: {units: [7{b1, b2, b4}], usedBy: [], needs: []},
+ f11: {units: [5{b1, b2, b3}], usedBy: [], needs: []},
+ f12: {units: [10{b1, b5}], usedBy: [], needs: []},
+ f13: {units: [6{b1, b4}], usedBy: [], needs: []},
+ f14: {units: [4{b1, b3}], usedBy: [], needs: []},
+ f15: {units: [3{b1, b2}], usedBy: [], needs: []},
+ f16: {units: [2{b1}], usedBy: [], needs: []},
+ f17: {units: [24{b2, b3, b4, b5}], usedBy: [], needs: []},
+ f18: {units: [23{b2, b4, b5}], usedBy: [], needs: []},
+ f19: {units: [22{b2, b3, b5}], usedBy: [], needs: []},
+ f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: []},
+ f20: {units: [20{b2, b3, b4}], usedBy: [], needs: []},
+ f21: {units: [21{b2, b5}], usedBy: [], needs: []},
+ f22: {units: [19{b2, b4}], usedBy: [], needs: []},
+ f23: {units: [18{b2, b3}], usedBy: [], needs: []},
+ f24: {units: [17{b2}], usedBy: [], needs: []},
+ f25: {units: [28{b3, b4, b5}], usedBy: [], needs: []},
+ f26: {units: [27{b3, b5}], usedBy: [], needs: []},
+ f27: {units: [26{b3, b4}], usedBy: [], needs: []},
+ f28: {units: [25{b3}], usedBy: [], needs: []},
+ f29: {units: [30{b4, b5}], usedBy: [], needs: []},
+ f2: {units: [16{b1, b3, b4, b5}], usedBy: [], needs: []},
+ f30: {units: [29{b4}], usedBy: [], needs: []},
+ f31: {units: [31{b5}], usedBy: [], needs: []},
+ f3: {units: [15{b1, b2, b4, b5}], usedBy: [], needs: []},
+ f4: {units: [13{b1, b2, b3, b5}], usedBy: [], needs: []},
+ f5: {units: [9{b1, b2, b3, b4}], usedBy: [], needs: []},
+ f6: {units: [14{b1, b4, b5}], usedBy: [], needs: []},
+ f7: {units: [12{b1, b3, b5}], usedBy: [], needs: []},
+ f8: {units: [8{b1, b3, b4}], usedBy: [], needs: []},
+ f9: {units: [11{b1, b2, b5}], usedBy: [], needs: []}],
steps=[
b1=(f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16),
b2=(f1, f17, f3, f4, f5, f18, f19, f20, f9, f10, f11, f21, f22, f23, f15, f24),
@@ -45,10 +45,10 @@
/*three-frag.library:
output_units=[
- f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2], needs: []},
- f2: {units: [24{b2, b3, b4, b5, b1}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}], usedBy: [3], needs: [1]},
- f3: {units: [9{b1, b2, b3, b4, b5}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}], usedBy: [4], needs: [2]},
- f4: {units: [26{b3, b4, b2, b5, b1}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [], needs: [3]}],
+ f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: [3, 2]},
+ f2: {units: [24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}], usedBy: [1], needs: [3]},
+ f3: {units: [9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}, 8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}], usedBy: [1, 2], needs: [4]},
+ f4: {units: [26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [3], needs: []}],
steps=[
b1=(f1, f2, f3, f4),
b2=(f1, f2, f3, f4),
@@ -59,9 +59,9 @@
/*two-frag.library:
output_units=[
- f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [2], needs: []},
- f2: {units: [24{b2, b3, b4, b5, b1}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}], usedBy: [3], needs: [1]},
- f3: {units: [8{b1, b3, b4, b2, b5}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [], needs: [2]}],
+ f1: {units: [1{b1, b2, b3, b4, b5}], usedBy: [], needs: [2]},
+ f2: {units: [24{b2, b3, b4, b5}, 16{b1, b3, b4, b5}, 15{b1, b2, b4, b5}, 13{b1, b2, b3, b5}, 9{b1, b2, b3, b4}, 28{b3, b4, b5}, 23{b2, b4, b5}, 22{b2, b3, b5}, 20{b2, b3, b4}, 14{b1, b4, b5}, 12{b1, b3, b5}], usedBy: [1], needs: [3]},
+ f3: {units: [8{b1, b3, b4}, 11{b1, b2, b5}, 7{b1, b2, b4}, 5{b1, b2, b3}, 30{b4, b5}, 27{b3, b5}, 26{b3, b4}, 21{b2, b5}, 19{b2, b4}, 18{b2, b3}, 10{b1, b5}, 6{b1, b4}, 4{b1, b3}, 3{b1, b2}, 31{b5}, 29{b4}, 25{b3}, 17{b2}, 2{b1}], usedBy: [2], needs: []}],
steps=[
b1=(f1, f2, f3),
b2=(f1, f2, f3),
diff --git a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
index 9c8dd2f..a4f5f5a 100644
--- a/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/static_separate/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [2{lib1, lib2}], usedBy: [2, 3], needs: []},
- f2: {units: [1{lib1}], usedBy: [], needs: [1]},
- f3: {units: [3{lib2}], usedBy: [], needs: [1]}],
+ f1: {units: [2{lib1, lib2}], usedBy: [], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: []},
+ f3: {units: [3{lib2}], usedBy: [], needs: []}],
+ steps=[
+ lib1=(f1, f2),
+ lib2=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [2{lib1, lib2}], usedBy: [], needs: [2, 3]},
+ f2: {units: [1{lib1}], usedBy: [1], needs: []},
+ f3: {units: [3{lib2}], usedBy: [1], needs: []}],
steps=[
lib1=(f1, f2),
lib2=(f1, f3)]
diff --git a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
index 42fed69..13f5f03 100644
--- a/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
+++ b/pkg/compiler/test/deferred_loading/data/type_arguments/main.dart
@@ -2,11 +2,21 @@
// 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.
-/*library:
+/*spec.library:
output_units=[
- f1: {units: [3{lib1, lib3}], usedBy: [2, 3], needs: []},
- f2: {units: [1{lib1}], usedBy: [], needs: [1]},
- f3: {units: [2{lib3}], usedBy: [], needs: [1]}],
+ f1: {units: [3{lib1, lib3}], usedBy: [], needs: []},
+ f2: {units: [1{lib1}], usedBy: [], needs: []},
+ f3: {units: [2{lib3}], usedBy: [], needs: []}],
+ steps=[
+ lib1=(f1, f2),
+ lib3=(f1, f3)]
+*/
+
+/*two-frag|three-frag.library:
+ output_units=[
+ f1: {units: [3{lib1, lib3}], usedBy: [], needs: [2, 3]},
+ f2: {units: [1{lib1}], usedBy: [1], needs: []},
+ f3: {units: [2{lib3}], usedBy: [1], needs: []}],
steps=[
lib1=(f1, f2),
lib3=(f1, f3)]
diff --git a/pkg/compiler/test/inference/inference_test_helper.dart b/pkg/compiler/test/inference/inference_test_helper.dart
index 701d33d..b51b0e6 100644
--- a/pkg/compiler/test/inference/inference_test_helper.dart
+++ b/pkg/compiler/test/inference/inference_test_helper.dart
@@ -146,11 +146,23 @@
node is ir.FunctionDeclaration) {
ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo(node);
return getMemberValue(info.callMethod);
- } else if (node is ir.MethodInvocation) {
+ } else if (node is ir.MethodInvocation ||
+ node is ir.InstanceInvocation ||
+ node is ir.InstanceGetterInvocation ||
+ node is ir.DynamicInvocation ||
+ node is ir.FunctionInvocation ||
+ node is ir.EqualsNull ||
+ node is ir.EqualsCall) {
return getTypeMaskValue(result.typeOfReceiver(node));
- } else if (node is ir.PropertyGet) {
+ } else if (node is ir.PropertyGet ||
+ node is ir.InstanceGet ||
+ node is ir.DynamicGet ||
+ node is ir.InstanceTearOff ||
+ node is ir.FunctionTearOff) {
return getTypeMaskValue(result.typeOfReceiver(node));
- } else if (node is ir.PropertySet) {
+ } else if (node is ir.PropertySet ||
+ node is ir.InstanceSet ||
+ node is ir.DynamicSet) {
return getTypeMaskValue(result.typeOfReceiver(node));
} else if (node is ir.ForInStatement) {
if (id.kind == IdKind.iterator) {
diff --git a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
index e2a2196..51e385e 100644
--- a/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
+++ b/pkg/compiler/test/kernel/data/list_generate_2.dart.expect
@@ -13,7 +13,7 @@
final _in::JSArray<core::int*> _list = _in::JSArray::allocateGrowable<core::int*>(_length);
for (core::int i = 0; i.{core::num::<}(_length); i = i.{core::num::+}(1)) {
core::int* i = i;
- _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1));
+ _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::+}(1){(core::num*) →* core::int*});
}
} =>_list);
}
diff --git a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect b/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
index 05df05d..9a50e1e 100644
--- a/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
+++ b/pkg/compiler/test/kernel/data/list_generate_3.dart.expect
@@ -34,9 +34,9 @@
return i;
}, growable: self::someGrowable);
static field core::List<core::int*>* list5 = core::List::generate<core::int*>(10, (core::int* i) → core::int* {
- if(i.{core::int::isEven})
- return i.{core::num::+}(1);
- return i.{core::num::-}(1);
+ if(i.{core::int::isEven}{core::bool*})
+ return i.{core::num::+}(1){(core::num*) →* core::int*};
+ return i.{core::num::-}(1){(core::num*) →* core::int*};
});
static field core::List<core::int*>* list6 = core::List::generate<core::int*>(10, #C1);
static field core::List<core::int*>* list7 = core::List::generate<core::int*>(10, self::bar);
diff --git a/pkg/compiler/test/static_type/data/effectively_final_access.dart b/pkg/compiler/test/static_type/data/effectively_final_access.dart
new file mode 100644
index 0000000..352b77c
--- /dev/null
+++ b/pkg/compiler/test/static_type/data/effectively_final_access.dart
@@ -0,0 +1,56 @@
+// 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.
+
+// @dart = 2.7
+
+main() {
+ effectivelyFinalFunctionTyped();
+ effectivelyFinalGenericFunctionTyped(null);
+ effectivelyFinalDynamicallyTyped();
+ effectivelyFinalPartiallyTyped();
+}
+
+effectivelyFinalFunctionTyped() {
+ Function f = (int i) => /*spec.int*/ i;
+ /*spec.int Function(int)*/ f
+ . /*spec.invoke: [int Function(int)]->int*/ call(0);
+ (/*spec.int Function(int)*/ f.call)
+ /*spec.invoke: [int Function(int)]->int*/ (0);
+}
+
+effectivelyFinalGenericFunctionTyped(T Function<T>(T) g) {
+ Function f = /*spec.T Function<T extends Object>(T)*/ g;
+ /*spec.T Function<T extends Object>(T)*/ f
+ . /*spec.invoke: [T Function<T extends Object>(T)]->int*/ call<int>(0);
+ (/*spec.T Function<T extends Object>(T)*/ f.call)
+ .
+ /*spec.invoke: [T Function<T extends Object>(T)]->int*/
+ call<int>(0);
+}
+
+effectivelyFinalDynamicallyTyped() {
+ dynamic list = <int>[0];
+ /*spec.List<int>*/ list.first /*spec.invoke: [int]->int*/ + 0;
+ /*spec.List<int>*/ list /*spec.[List<int>]->int*/
+ [0] /*spec.invoke: [int]->int*/ +
+ 0;
+ (/*spec.List<int>*/ list.contains)
+ /*spec.invoke: [bool Function(Object)]->bool*/ (0);
+ /*spec.List<int>*/ list
+ /*spec.update: [List<int>]->void*/ /*spec.[List<int>]->int*/
+ [0] /*spec.invoke: [int]->int*/ ++;
+}
+
+effectivelyFinalPartiallyTyped() {
+ List list = <int>[0];
+ /*spec.List<int>*/ list.first /*spec.invoke: [int]->int*/ + 0;
+ /*spec.List<int>*/ list /*spec.[List<int>]->int*/
+ [0] /*spec.invoke: [int]->int*/ +
+ 0;
+ (/*spec.List<int>*/ list.contains)
+ /*spec.invoke: [bool Function(Object)]->bool*/ (0);
+ /*spec.List<int>*/ list
+ /*spec.update: [List<int>]->void*/ /*spec.[List<int>]->int*/
+ [0] /*spec.invoke: [int]->int*/ ++;
+}
diff --git a/pkg/compiler/test/static_type/static_type_test.dart b/pkg/compiler/test/static_type/static_type_test.dart
index 220b990..fca7c8a 100644
--- a/pkg/compiler/test/static_type/static_type_test.dart
+++ b/pkg/compiler/test/static_type/static_type_test.dart
@@ -89,6 +89,29 @@
} else if (node is ir.MethodInvocation) {
return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
'${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.InstanceInvocation) {
+ return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.InstanceGetterInvocation) {
+ return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.DynamicInvocation) {
+ return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.FunctionInvocation) {
+ return '[${typeToText(node.receiver.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.LocalFunctionInvocation) {
+ return '[${typeToText(node.variable.type)}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.EqualsCall) {
+ return '[${typeToText(node.left.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
+ } else if (node is ir.EqualsNull) {
+ // TODO(johnniwinther): Remove this after the new method invocation has
+ // landed stably. This is only included to make the transition a no-op.
+ return '[${typeToText(node.expression.accept(staticTypeCache))}]->'
+ '${typeToText(node.accept(staticTypeCache))}';
}
return null;
}
diff --git a/pkg/compiler/test/static_type/type_promotion_data/equals.dart b/pkg/compiler/test/static_type/type_promotion_data/equals.dart
index cd6ec3c..2438e4d 100644
--- a/pkg/compiler/test/static_type/type_promotion_data/equals.dart
+++ b/pkg/compiler/test/static_type/type_promotion_data/equals.dart
@@ -53,7 +53,7 @@
if (
/*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
e1 is List !=
- /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Set<dynamic>,Map<dynamic,dynamic>,Iterable<dynamic>]}*/
+ /*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
e2 is List) {
return
/*{e1:[{true:Iterable<dynamic>,false:Set<dynamic>,Map<dynamic,dynamic>}|Iterable<dynamic>,Set<dynamic>,Map<dynamic,dynamic>]}*/
diff --git a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
index d4ef9bf..4d44607 100644
--- a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
+++ b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
@@ -16,9 +16,6 @@
import 'package:kernel/target/targets.dart' show Target;
-import '../api_prototype/compiler_options.dart'
- show CompilerOptions, parseExperimentalFlags;
-
import '../api_prototype/experimental_flags.dart' show ExperimentalFlag;
import '../api_prototype/file_system.dart' show FileSystem;
diff --git a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
index c0e9971..86e27d1 100644
--- a/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/named_type_builder.dart
@@ -6,8 +6,6 @@
library fasta.named_type_builder;
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
import 'package:kernel/ast.dart';
import '../fasta_codes.dart'
@@ -30,9 +28,6 @@
import '../identifiers.dart' show Identifier, QualifiedName, flattenName;
-import '../messages.dart'
- show noLength, templateSupertypeIsIllegal, templateSupertypeIsTypeVariable;
-
import '../problems.dart' show unhandled;
import '../scope.dart';
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index ae2dc15..ecc024e 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -8,8 +8,6 @@
import 'package:front_end/src/fasta/kernel/kernel_api.dart';
import 'package:kernel/ast.dart';
-import 'package:kernel/type_algebra.dart';
-
import '../kernel/class_hierarchy_builder.dart';
import '../kernel/forest.dart';
import '../kernel/internal_ast.dart';
diff --git a/pkg/front_end/lib/src/fasta/compiler_context.dart b/pkg/front_end/lib/src/fasta/compiler_context.dart
index 913a4e1..67cfc5a 100644
--- a/pkg/front_end/lib/src/fasta/compiler_context.dart
+++ b/pkg/front_end/lib/src/fasta/compiler_context.dart
@@ -24,9 +24,6 @@
import 'command_line_reporting.dart' as command_line_reporting;
-import 'fasta_codes.dart'
- show LocatedMessage, Message, messageInternalProblemMissingContext;
-
final Object compilerContextKey = new Object();
/// Shared context used throughout the compiler.
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 75da2ac..82ba08c 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -109,14 +109,6 @@
import 'util/textual_outline.dart' show textualOutline;
-import 'fasta_codes.dart'
- show
- DiagnosticMessageFromJson,
- templateInitializeFromDillNotSelfContained,
- templateInitializeFromDillNotSelfContainedNoDump,
- templateInitializeFromDillUnknownProblem,
- templateInitializeFromDillUnknownProblemNoDump;
-
import 'hybrid_file_system.dart' show HybridFileSystem;
import 'kernel/kernel_builder.dart' show ClassHierarchyBuilder;
@@ -127,8 +119,6 @@
import 'library_graph.dart' show LibraryGraph;
-import 'messages.dart' show Message;
-
import 'source/source_library_builder.dart' show SourceLibraryBuilder;
import 'ticker.dart' show Ticker;
diff --git a/pkg/front_end/lib/src/fasta/kernel/collections.dart b/pkg/front_end/lib/src/fasta/kernel/collections.dart
index 9b23f9f..e2fb85b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/collections.dart
@@ -12,14 +12,6 @@
import 'package:kernel/type_environment.dart' show StaticTypeContext;
-import 'package:kernel/visitor.dart'
- show
- ExpressionVisitor,
- ExpressionVisitor1,
- Transformer,
- TreeVisitor,
- Visitor;
-
import '../messages.dart'
show noLength, templateExpectedAfterButGot, templateExpectedButGot;
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 cc156ed..d2e276e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -3413,6 +3413,18 @@
exprEvaluator.env.addVariableValue(node, value);
return const ProceedStatus();
}
+
+ @override
+ ExecutionStatus visitWhileStatement(WhileStatement node) {
+ Constant condition = evaluate(node.condition);
+ while (condition is BoolConstant && condition.value) {
+ final ExecutionStatus status = node.body.accept(this);
+ if (status is! ProceedStatus) return status;
+ condition = evaluate(node.condition);
+ }
+ if (condition is AbortConstant) return new AbortStatus(condition);
+ return const ProceedStatus();
+ }
}
class ConstantCoverage {
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 4e59fb4..6f89031 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -67,18 +67,6 @@
import 'kernel_api.dart' show NameSystem, printNodeOn, printQualifiedNameOn;
-import 'kernel_ast_api.dart'
- show
- Arguments,
- DartType,
- DynamicType,
- Expression,
- Initializer,
- Member,
- Name,
- Procedure,
- VariableDeclaration;
-
import 'kernel_builder.dart' show LoadLibraryBuilder;
import 'internal_ast.dart';
diff --git a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
index bfaef2c..3cafb52 100644
--- a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
@@ -5,7 +5,6 @@
// @dart = 2.9
import 'package:kernel/ast.dart';
-import 'package:kernel/visitor.dart';
import '../type_inference/type_schema.dart';
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
index ac5f466..d017229 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_collections.dart
@@ -15,8 +15,6 @@
import 'package:kernel/type_environment.dart'
show SubtypeCheckMode, TypeEnvironment;
-import 'package:kernel/visitor.dart' show Transformer;
-
import 'collections.dart'
show
ControlFlowElement,
diff --git a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
index 7195f01..253d4ce 100644
--- a/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/transform_set_literals.dart
@@ -14,8 +14,6 @@
import 'package:kernel/type_algebra.dart' show Substitution;
-import 'package:kernel/visitor.dart' show Transformer;
-
import '../source/source_loader.dart' show SourceLoader;
import 'redirecting_factory_body.dart' show RedirectingFactoryBody;
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index bb2c869..8f660b6 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -8,8 +8,6 @@
import 'dart:convert' show jsonEncode;
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
import 'package:_fe_analyzer_shared/src/util/resolve_relative_uri.dart'
diff --git a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
index 83721d7..3548229 100644
--- a/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
+++ b/pkg/front_end/lib/src/fasta/source/stack_listener_impl.dart
@@ -6,8 +6,6 @@
library fasta.stack_listener_impl;
-import 'package:_fe_analyzer_shared/src/messages/codes.dart' show Message;
-
import 'package:_fe_analyzer_shared/src/parser/parser.dart' show Parser;
import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart';
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
index 569d058..7e7f6cd 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_constraint_gatherer.dart
@@ -12,8 +12,6 @@
import 'package:kernel/type_environment.dart';
-import 'type_schema.dart' show UnknownType;
-
import '../names.dart' show callName;
import 'type_schema.dart';
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 78e4c89..fe251d2 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -41,12 +41,6 @@
import '../fasta_codes.dart';
-import '../kernel/internal_ast.dart'
- show
- VariableDeclarationImpl,
- getExplicitTypeArguments,
- getExtensionTypeParameterCount;
-
import '../kernel/inference_visitor.dart';
import '../kernel/invalid_type.dart';
@@ -2948,7 +2942,7 @@
VariableDeclaration variable = receiver.variable;
TreeNode parent = variable.parent;
if (parent is FunctionDeclaration) {
- assert(inferredFunctionType != unknownFunction,
+ assert(!identical(inferredFunctionType, unknownFunction),
"Unknown function type for local function invocation.");
expression = new LocalFunctionInvocation(variable, arguments,
functionType: inferredFunctionType)
diff --git a/pkg/front_end/lib/src/fasta/uri_translator.dart b/pkg/front_end/lib/src/fasta/uri_translator.dart
index 55222b2..cf294d2 100644
--- a/pkg/front_end/lib/src/fasta/uri_translator.dart
+++ b/pkg/front_end/lib/src/fasta/uri_translator.dart
@@ -6,8 +6,6 @@
library fasta.uri_translator;
-import 'package:_fe_analyzer_shared/src/messages/severity.dart' show Severity;
-
import 'package:package_config/package_config.dart';
import '../base/libraries_specification.dart' show TargetLibrariesSpecification;
diff --git a/pkg/front_end/test/lint_test.status b/pkg/front_end/test/lint_test.status
index 691adfa..4b2a34a 100644
--- a/pkg/front_end/test/lint_test.status
+++ b/pkg/front_end/test/lint_test.status
@@ -15,7 +15,6 @@
front_end/lib/src/api_prototype/incremental_kernel_generator/Exports: Fail
front_end/lib/src/api_prototype/language_version/Exports: Fail
front_end/lib/src/api_prototype/terminal_color_support/Exports: Fail
-front_end/lib/src/api_unstable/bazel_worker/ImportsTwice: Fail
front_end/lib/src/fasta/fasta_codes/Exports: Fail
front_end/lib/src/fasta/incremental_compiler/ImportsTwice: Fail
front_end/lib/src/fasta/kernel/body_builder/ImportsTwice: Fail
@@ -37,7 +36,6 @@
front_end/lib/src/fasta/source/scope_listener/Exports: Fail
front_end/lib/src/fasta/source/source_class_builder/ImportsTwice: Fail
front_end/lib/src/fasta/source/value_kinds/ImportsTwice: Fail
-front_end/lib/src/fasta/type_inference/type_constraint_gatherer/ImportsTwice: Fail
front_end/lib/src/fasta/type_inference/type_inferrer/ImportsTwice: Fail
front_end/lib/src/testing/id_testing_helper/Exports: Fail
kernel/lib/ast/ImportsTwice: Fail
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart
new file mode 100644
index 0000000..cceb2b2
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart
@@ -0,0 +1,36 @@
+// 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 while statements for const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+int fn(int a) {
+ int b = a;
+ int i = 0;
+ while (i < 2) {
+ b += a;
+ i++;
+ }
+ return b;
+}
+
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn1(int a) {
+ int b = a;
+ while (true) {
+ b *= 3;
+ if (b > 10) return b;
+ }
+}
+
+void main() {
+ Expect.equals(var1, 6);
+ Expect.equals(var2, 9);
+ Expect.equals(var3, 18);
+ Expect.equals(var4, 27);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.expect
@@ -0,0 +1,41 @@
+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::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+ core::int b = a;
+ core::int i = 0;
+ while (i.{core::num::<}(2)) {
+ b = b.{core::num::+}(a);
+ i = i.{core::num::+}(1);
+ }
+ return b;
+}
+static method fn1(core::int a) → core::int {
+ core::int b = a;
+ while (true) {
+ b = b.{core::num::*}(3);
+ if(b.{core::num::>}(10))
+ return b;
+ }
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 6);
+ exp::Expect::equals(#C2, 9);
+ exp::Expect::equals(#C3, 18);
+ exp::Expect::equals(#C4, 27);
+}
+
+constants {
+ #C1 = 6
+ #C2 = 9
+ #C3 = 18
+ #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.strong.transformed.expect
@@ -0,0 +1,41 @@
+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::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+ core::int b = a;
+ core::int i = 0;
+ while (i.{core::num::<}(2)) {
+ b = b.{core::num::+}(a);
+ i = i.{core::num::+}(1);
+ }
+ return b;
+}
+static method fn1(core::int a) → core::int {
+ core::int b = a;
+ while (true) {
+ b = b.{core::num::*}(3);
+ if(b.{core::num::>}(10))
+ return b;
+ }
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 6);
+ exp::Expect::equals(#C2, 9);
+ exp::Expect::equals(#C3, 18);
+ exp::Expect::equals(#C4, 27);
+}
+
+constants {
+ #C1 = 6
+ #C2 = 9
+ #C3 = 18
+ #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect
new file mode 100644
index 0000000..2f001f7
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline.expect
@@ -0,0 +1,9 @@
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+int fn(int a) {}
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn1(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..d2a9f6b
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.textual_outline_modelled.expect
@@ -0,0 +1,9 @@
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+const var2 = fn(3);
+const var3 = fn1(2);
+const var4 = fn1(3);
+int fn(int a) {}
+int fn1(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.expect
@@ -0,0 +1,41 @@
+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::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+ core::int b = a;
+ core::int i = 0;
+ while (i.{core::num::<}(2)) {
+ b = b.{core::num::+}(a);
+ i = i.{core::num::+}(1);
+ }
+ return b;
+}
+static method fn1(core::int a) → core::int {
+ core::int b = a;
+ while (true) {
+ b = b.{core::num::*}(3);
+ if(b.{core::num::>}(10))
+ return b;
+ }
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 6);
+ exp::Expect::equals(#C2, 9);
+ exp::Expect::equals(#C3, 18);
+ exp::Expect::equals(#C4, 27);
+}
+
+constants {
+ #C1 = 6
+ #C2 = 9
+ #C3 = 18
+ #C4 = 27
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect
new file mode 100644
index 0000000..005f16a
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::fn(2);
+static const field core::int var2 = self::fn(3);
+static const field core::int var3 = self::fn1(2);
+static const field core::int var4 = self::fn1(3);
+static method fn(core::int a) → core::int
+ ;
+static method fn1(core::int a) → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect
new file mode 100644
index 0000000..9f4a00e
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_while_statements.dart.weak.transformed.expect
@@ -0,0 +1,41 @@
+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::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C4;
+static method fn(core::int a) → core::int {
+ core::int b = a;
+ core::int i = 0;
+ while (i.{core::num::<}(2)) {
+ b = b.{core::num::+}(a);
+ i = i.{core::num::+}(1);
+ }
+ return b;
+}
+static method fn1(core::int a) → core::int {
+ core::int b = a;
+ while (true) {
+ b = b.{core::num::*}(3);
+ if(b.{core::num::>}(10))
+ return b;
+ }
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 6);
+ exp::Expect::equals(#C2, 9);
+ exp::Expect::equals(#C3, 18);
+ exp::Expect::equals(#C4, 27);
+}
+
+constants {
+ #C1 = 6
+ #C2 = 9
+ #C3 = 18
+ #C4 = 27
+}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
index c9e4703..1d7d6ef 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.expect
@@ -3,5 +3,5 @@
import "dart:core" as core;
static method main() → dynamic {
- core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+ core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2){(core::num) → core::int});
}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
index a118ecb..c99286d 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.strong.transformed.expect
@@ -8,7 +8,7 @@
final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
core::int i = i;
- _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+ _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2){(core::num) → core::int});
}
} =>_list;
}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
index c9e4703..1d7d6ef 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.expect
@@ -3,5 +3,5 @@
import "dart:core" as core;
static method main() → dynamic {
- core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2));
+ core::List::generate<core::int>(10, (core::int i) → core::int => i.{core::num::*}(2){(core::num) → core::int});
}
diff --git a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
index a118ecb..c99286d 100644
--- a/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/dart2js/list_generate.dart.weak.transformed.expect
@@ -8,7 +8,7 @@
final _in::JSArray<core::int> _list = _in::JSArray::allocateGrowable<core::int>(10);
for (core::int* i = 0; i.{core::num::<}(10); i = i.{core::num::+}(1)) {
core::int i = i;
- _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2));
+ _list.{_in::JSArray::[]=}{Invariant,BoundsSafe}(i, i.{core::num::*}(2){(core::num) → core::int});
}
} =>_list;
}
diff --git a/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
new file mode 100644
index 0000000..d30310a
--- /dev/null
+++ b/runtime/observatory/tests/service/break_on_function_child_isolate_test.dart
@@ -0,0 +1,84 @@
+// 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.
+// VMOptions=--verbose_debug --enable-isolate-groups --experimental-enable-isolate-groups-jit
+import 'dart:async';
+import 'dart:isolate' as dart_isolate;
+
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+import 'service_test_common.dart';
+import 'test_helper.dart';
+import 'dart:developer';
+
+const int LINE_A = 18;
+const int LINE_B = 25;
+const int LINE_C = 29;
+
+foo(args) { // LINE_A
+ final dart_isolate.SendPort sendPort = args[0] as dart_isolate.SendPort;
+ sendPort.send('reply from foo');
+}
+
+testMain() async {
+ final rpResponse = dart_isolate.ReceivePort();
+ debugger(); // LINE_B
+ await dart_isolate.Isolate.spawn(foo, [rpResponse.sendPort]);
+ await rpResponse.first;
+ rpResponse.close();
+ debugger(); // LINE_C
+}
+
+final completerAtFoo = Completer();
+
+final tests = <IsolateTest>[
+ hasPausedAtStart,
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_B + 1),
+ (Isolate isolate) async {
+ // Set up a listener to wait for child isolate launch and breakpoint events.
+ final stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ var childIsolate;
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) async {
+ switch (event.kind) {
+ case ServiceEvent.kPauseStart:
+ childIsolate = event.isolate!;
+ await childIsolate.reload();
+
+ Library rootLib = await childIsolate.rootLibrary.load() as Library;
+ final foo = rootLib.functions.singleWhere((f) => f.name == 'foo');
+ final bpt = await childIsolate.addBreakpointAtEntry(foo);
+
+ expect(bpt is Breakpoint, isTrue);
+ childIsolate.resume();
+ break;
+ case ServiceEvent.kPauseBreakpoint:
+ if (childIsolate == event.isolate) {
+ ServiceMap stack = await childIsolate.getStack();
+ Frame top = stack['frames'][0];
+ Script script = await top.location!.script.load() as Script;
+ expect(script.tokenToLine(top.location!.tokenPos), equals(LINE_A));
+
+ childIsolate.resume();
+ subscription.cancel();
+ completerAtFoo.complete();
+ }
+ break;
+ }
+ });
+ },
+ resumeIsolate,
+ (Isolate isolate) async {
+ await completerAtFoo.future;
+ },
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_C + 1),
+ resumeIsolate,
+];
+
+main(args) async {
+ runIsolateTests(args, tests,
+ testeeConcurrent: testMain, pause_on_start: true);
+}
diff --git a/runtime/observatory/tests/service/capture_stdio_test.dart b/runtime/observatory/tests/service/capture_stdio_test.dart
index b02e476..fa4afc7 100644
--- a/runtime/observatory/tests/service/capture_stdio_test.dart
+++ b/runtime/observatory/tests/service/capture_stdio_test.dart
@@ -28,6 +28,7 @@
var stdoutSub;
stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
expect(event.bytesAsString, equals('stdout'));
stdoutSub.cancel().then((_) {
@@ -44,6 +45,7 @@
int eventNumber = 1;
stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
if (eventNumber == 1) {
expect(event.bytesAsString, equals('print'));
@@ -66,6 +68,7 @@
var stderrSub;
stderrSub = await isolate.vm.listenEventStream(VM.kStderrStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
expect(event.bytesAsString, equals('stderr'));
stderrSub.cancel().then((_) {
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 16e2d93..600d2df 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -58,6 +58,7 @@
break_on_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/observatory_2/tests/service_2/capture_stdio_test.dart b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
index dc81e5a..ccbdbfd 100644
--- a/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
+++ b/runtime/observatory_2/tests/service_2/capture_stdio_test.dart
@@ -28,6 +28,7 @@
var stdoutSub;
stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
expect(event.bytesAsString, equals('stdout'));
stdoutSub.cancel().then((_) {
@@ -44,6 +45,7 @@
int eventNumber = 1;
stdoutSub = await isolate.vm.listenEventStream(VM.kStdoutStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
if (eventNumber == 1) {
expect(event.bytesAsString, equals('print'));
@@ -66,6 +68,7 @@
var stderrSub;
stderrSub = await isolate.vm.listenEventStream(VM.kStderrStream,
(ServiceEvent event) {
+ expect(event.isolate != null, isTrue);
expect(event.kind, equals('WriteEvent'));
expect(event.bytesAsString, equals('stderr'));
stderrSub.cancel().then((_) {
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index f352aa6..a04fc9c 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -58,6 +58,7 @@
break_on_activation_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_async_function_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_default_constructor_test: SkipByDesign # Debugger is disabled in AOT mode.
+break_on_function_child_isolate_test: SkipByDesign # Debugger is disabled in AOT mode.
break_on_function_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_async_break_test: SkipByDesign # Debugger is disabled in AOT mode.
breakpoint_in_package_parts_class_file_uri_test: SkipByDesign # Debugger is disabled in AOT mode.
diff --git a/runtime/tests/vm/dart/regress_45306_test.dart b/runtime/tests/vm/dart/regress_45306_test.dart
new file mode 100644
index 0000000..2b5e3c0
--- /dev/null
+++ b/runtime/tests/vm/dart/regress_45306_test.dart
@@ -0,0 +1,33 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45306.
+// Verifies that ScopeBuilder doesn't crash on an async closure inside
+// instance field initializer.
+
+class X {
+ late final Y y = Y(
+ () async {},
+ );
+
+ final double? a;
+ final double? b;
+ final String? c;
+
+ X({
+ this.a,
+ this.b,
+ this.c,
+ });
+}
+
+typedef Callback = Future<void> Function();
+
+class Y {
+ Y(Callback? f);
+}
+
+void main() {
+ X();
+}
diff --git a/runtime/tests/vm/dart_2/regress_45306_test.dart b/runtime/tests/vm/dart_2/regress_45306_test.dart
new file mode 100644
index 0000000..36af440
--- /dev/null
+++ b/runtime/tests/vm/dart_2/regress_45306_test.dart
@@ -0,0 +1,33 @@
+// 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/45306.
+// Verifies that ScopeBuilder doesn't crash on an async closure inside
+// instance field initializer.
+
+class X {
+ final Y y = Y(
+ () async {},
+ );
+
+ final double a;
+ final double b;
+ final String c;
+
+ X({
+ this.a,
+ this.b,
+ this.c,
+ });
+}
+
+typedef Callback = Future<void> Function();
+
+class Y {
+ Y(Callback f);
+}
+
+void main() {
+ X();
+}
diff --git a/runtime/vm/compiler/backend/il_ia32.cc b/runtime/vm/compiler/backend/il_ia32.cc
index c2aae32..2b8fd43 100644
--- a/runtime/vm/compiler/backend/il_ia32.cc
+++ b/runtime/vm/compiler/backend/il_ia32.cc
@@ -636,20 +636,23 @@
}
}
-static void EmitBranchOnCondition(FlowGraphCompiler* compiler,
- Condition true_condition,
- BranchLabels labels) {
+static void EmitBranchOnCondition(
+ FlowGraphCompiler* compiler,
+ Condition true_condition,
+ BranchLabels labels,
+ compiler::Assembler::JumpDistance jump_distance =
+ compiler::Assembler::kFarJump) {
if (labels.fall_through == labels.false_label) {
// If the next block is the false successor, fall through to it.
- __ j(true_condition, labels.true_label);
+ __ j(true_condition, labels.true_label, jump_distance);
} else {
// If the next block is not the false successor, branch to it.
Condition false_condition = InvertCondition(true_condition);
- __ j(false_condition, labels.false_label);
+ __ j(false_condition, labels.false_label, jump_distance);
// Fall through or jump to the true successor.
if (labels.fall_through != labels.true_label) {
- __ jmp(labels.true_label);
+ __ jmp(labels.true_label, jump_distance);
}
}
}
@@ -815,7 +818,8 @@
BranchLabels labels = {&is_true, &is_false, &is_false};
Condition true_condition = EmitComparisonCode(compiler, labels);
if (true_condition != kInvalidCondition) {
- EmitBranchOnCondition(compiler, true_condition, labels);
+ EmitBranchOnCondition(compiler, true_condition, labels,
+ compiler::Assembler::kNearJump);
}
Register result = locs()->out(0).reg();
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index b0a0802..1bef8cf 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -789,20 +789,23 @@
}
}
-static void EmitBranchOnCondition(FlowGraphCompiler* compiler,
- Condition true_condition,
- BranchLabels labels) {
+static void EmitBranchOnCondition(
+ FlowGraphCompiler* compiler,
+ Condition true_condition,
+ BranchLabels labels,
+ compiler::Assembler::JumpDistance jump_distance =
+ compiler::Assembler::kFarJump) {
if (labels.fall_through == labels.false_label) {
// If the next block is the false successor, fall through to it.
- __ j(true_condition, labels.true_label);
+ __ j(true_condition, labels.true_label, jump_distance);
} else {
// If the next block is not the false successor, branch to it.
Condition false_condition = InvertCondition(true_condition);
- __ j(false_condition, labels.false_label);
+ __ j(false_condition, labels.false_label, jump_distance);
// Fall through or jump to the true successor.
if (labels.fall_through != labels.true_label) {
- __ jmp(labels.true_label);
+ __ jmp(labels.true_label, jump_distance);
}
}
}
@@ -936,29 +939,20 @@
Condition true_condition = EmitComparisonCode(compiler, labels);
Register result = locs()->out(0).reg();
- if (is_true.IsLinked() || is_false.IsLinked()) {
- if (true_condition != kInvalidCondition) {
- EmitBranchOnCondition(compiler, true_condition, labels);
- }
- compiler::Label done;
- __ Bind(&is_false);
- __ LoadObject(result, Bool::False());
- __ jmp(&done, compiler::Assembler::kNearJump);
- __ Bind(&is_true);
- __ LoadObject(result, Bool::True());
- __ Bind(&done);
- } else {
- // If EmitComparisonCode did not use the labels and just returned
- // a condition we can avoid the branch and use conditional loads.
- ASSERT(true_condition != kInvalidCondition);
- __ setcc(InvertCondition(true_condition), ByteRegisterOf(result));
- __ movzxb(result, result);
- __ movq(result,
- compiler::Address(THR, result, TIMES_8,
- compiler::target::Thread::bool_true_offset()));
- ASSERT(compiler::target::Thread::bool_true_offset() + 8 ==
- compiler::target::Thread::bool_false_offset());
+ if (true_condition != kInvalidCondition) {
+ EmitBranchOnCondition(compiler, true_condition, labels,
+ compiler::Assembler::kNearJump);
}
+ // Note: We use branches instead of setcc or cmov even when the branch labels
+ // are otherwise unused, as this runs faster for the x86 processors tested on
+ // our benchmarking server.
+ compiler::Label done;
+ __ Bind(&is_false);
+ __ LoadObject(result, Bool::False());
+ __ jmp(&done, compiler::Assembler::kNearJump);
+ __ Bind(&is_true);
+ __ LoadObject(result, Bool::True());
+ __ Bind(&done);
}
void ComparisonInstr::EmitBranchCode(FlowGraphCompiler* compiler,
@@ -3892,11 +3886,12 @@
EMIT_SMI_CHECK;
Condition true_condition = EmitComparisonCode(compiler, labels);
ASSERT(true_condition != kInvalidCondition);
- EmitBranchOnCondition(compiler, true_condition, labels);
+ EmitBranchOnCondition(compiler, true_condition, labels,
+ compiler::Assembler::kNearJump);
Register result = locs()->out(0).reg();
__ Bind(false_label);
__ LoadObject(result, Bool::False());
- __ jmp(&done);
+ __ jmp(&done, compiler::Assembler::kNearJump);
__ Bind(true_label);
__ LoadObject(result, Bool::True());
__ Bind(&done);
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 12d1c1e..673bc1f 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -546,15 +546,6 @@
}
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
- // The :sync_op and :async_op continuations are called multiple times. So we
- // don't want the parameters from the first invocation to get stored in the
- // context and reused on later invocations with different parameters.
- if (function_node_helper.async_marker_ == FunctionNodeHelper::kSyncYielding) {
- for (intptr_t i = 0; i < function.NumParameters(); i++) {
- parsed_function_->ParameterVariable(i)->set_is_forced_stack();
- }
- }
-
// Read (but don't visit) the positional and named parameters, because they've
// already been added to the scope.
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
@@ -1537,7 +1528,11 @@
if (helper.IsCovariant()) {
variable->set_is_explicit_covariant_parameter();
}
- if (variable->name().ptr() == Symbols::IteratorParameter().ptr()) {
+
+ // The :sync_op and :async_op continuations are called multiple times. So we
+ // don't want the parameters from the first invocation to get stored in the
+ // context and reused on later invocations with different parameters.
+ if (current_function_async_marker_ == FunctionNodeHelper::kSyncYielding) {
variable->set_is_forced_stack();
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 712bdb0..1a9d77d 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6269,7 +6269,8 @@
"Dart_ServiceSendDataEvent expects argument 'bytes_length' to be >= "
"0.");
}
- Service::SendEmbedderEvent(stream_id, event_kind, bytes, bytes_length);
+ Service::SendEmbedderEvent(Isolate::Current(), // May be NULL
+ stream_id, event_kind, bytes, bytes_length);
#endif
return nullptr;
}
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 2b5db6a..643e324 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -57,12 +57,14 @@
#ifndef PRODUCT
// Create an unresolved breakpoint in given token range and script.
-BreakpointLocation::BreakpointLocation(const Script& script,
+BreakpointLocation::BreakpointLocation(Debugger* debugger,
+ const Script& script,
TokenPosition token_pos,
TokenPosition end_token_pos,
intptr_t requested_line_number,
intptr_t requested_column_number)
- : script_(script.ptr()),
+ : debugger_(debugger),
+ script_(script.ptr()),
url_(script.url()),
token_pos_(token_pos),
end_token_pos_(end_token_pos),
@@ -77,10 +79,12 @@
}
// Create a latent breakpoint at given url and line number.
-BreakpointLocation::BreakpointLocation(const String& url,
+BreakpointLocation::BreakpointLocation(Debugger* debugger,
+ const String& url,
intptr_t requested_line_number,
intptr_t requested_column_number)
- : script_(Script::null()),
+ : debugger_(debugger),
+ script_(Script::null()),
url_(url.ptr()),
token_pos_(TokenPosition::kNoSource),
end_token_pos_(TokenPosition::kNoSource),
@@ -323,7 +327,7 @@
bpt->set_next(breakpoints());
set_breakpoints(bpt);
- dbg->SyncBreakpointLocation(this);
+ dbg->group_debugger()->SyncBreakpointLocation(this);
dbg->SendBreakpointEvent(ServiceEvent::kBreakpointAdded, bpt);
}
@@ -441,6 +445,11 @@
}
return false;
}
+ return group_debugger()->HasCodeBreakpointInFunction(func);
+}
+
+bool GroupDebugger::HasCodeBreakpointInFunction(const Function& func) {
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
if (func.ptr() == cbpt->function()) {
@@ -451,7 +460,8 @@
return false;
}
-bool Debugger::HasBreakpoint(const Code& code) {
+bool GroupDebugger::HasBreakpointInCode(const Code& code) {
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
if (code.ptr() == cbpt->code_) {
@@ -1446,7 +1456,6 @@
pc_(pc),
line_number_(-1),
is_enabled_(false),
- bpt_location_(NULL),
next_(NULL),
breakpoint_kind_(kind),
saved_value_(Code::null()) {
@@ -1463,7 +1472,6 @@
#ifdef DEBUG
code_ = Code::null();
pc_ = 0ul;
- bpt_location_ = NULL;
next_ = NULL;
breakpoint_kind_ = UntaggedPcDescriptors::kOther;
#endif
@@ -1506,12 +1514,57 @@
ASSERT(!is_enabled_);
}
+bool CodeBreakpoint::HasBreakpointLocation(
+ BreakpointLocation* breakpoint_location) {
+ for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+ if (breakpoint_locations_[i] == breakpoint_location) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CodeBreakpoint::FindAndDeleteBreakpointLocation(
+ BreakpointLocation* breakpoint_location) {
+ for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+ if (breakpoint_locations_[i] == breakpoint_location) {
+ breakpoint_locations_.EraseAt(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+BreakpointLocation* CodeBreakpoint::FindBreakpointForDebugger(
+ Debugger* debugger) {
+ for (intptr_t i = 0; i < breakpoint_locations_.length(); i++) {
+ if (breakpoint_locations_[i]->debugger() == debugger) {
+ return breakpoint_locations_[i];
+ }
+ }
+ return nullptr;
+}
+
+GroupDebugger::GroupDebugger(IsolateGroup* isolate_group)
+ : isolate_group_(isolate_group),
+ code_breakpoints_lock_(new SafepointRwLock()),
+ code_breakpoints_(nullptr),
+ needs_breakpoint_cleanup_(false) {}
+
+GroupDebugger::~GroupDebugger() {
+ while (code_breakpoints_ != nullptr) {
+ CodeBreakpoint* cbpt = code_breakpoints_;
+ code_breakpoints_ = code_breakpoints_->next();
+ ASSERT(!cbpt->IsEnabled());
+ delete cbpt;
+ }
+}
+
Debugger::Debugger(Isolate* isolate)
: isolate_(isolate),
next_id_(1),
latent_locations_(NULL),
breakpoint_locations_(NULL),
- code_breakpoints_(NULL),
resume_action_(kContinue),
resume_frame_index_(-1),
post_deopt_frame_index_(-1),
@@ -1526,7 +1579,6 @@
async_stepping_fp_(0),
top_frame_awaiter_(Object::null()),
skip_next_step_(false),
- needs_breakpoint_cleanup_(false),
synthetic_async_breakpoint_(NULL),
exc_pause_info_(kNoPauseOnExceptions) {}
@@ -1534,7 +1586,6 @@
ASSERT(!IsPaused());
ASSERT(latent_locations_ == NULL);
ASSERT(breakpoint_locations_ == NULL);
- ASSERT(code_breakpoints_ == NULL);
ASSERT(stack_trace_ == NULL);
ASSERT(async_causal_stack_trace_ == NULL);
ASSERT(synthetic_async_breakpoint_ == NULL);
@@ -1546,22 +1597,18 @@
if (Isolate::IsSystemIsolate(isolate_)) {
return;
}
- while (breakpoint_locations_ != NULL) {
+ while (breakpoint_locations_ != nullptr) {
BreakpointLocation* loc = breakpoint_locations_;
+ group_debugger()->DisableCodeBreakpointsFor(loc);
breakpoint_locations_ = breakpoint_locations_->next();
delete loc;
}
- while (latent_locations_ != NULL) {
+ while (latent_locations_ != nullptr) {
BreakpointLocation* loc = latent_locations_;
+ group_debugger()->DisableCodeBreakpointsFor(loc);
latent_locations_ = latent_locations_->next();
delete loc;
}
- while (code_breakpoints_ != NULL) {
- CodeBreakpoint* cbpt = code_breakpoints_;
- code_breakpoints_ = code_breakpoints_->next();
- cbpt->Disable();
- delete cbpt;
- }
if (NeedsIsolateEvents()) {
ServiceEvent event(isolate_, ServiceEvent::kIsolateExit);
InvokeEventHandler(&event);
@@ -2439,8 +2486,8 @@
return TokenPosition::kNoSource;
}
-void Debugger::MakeCodeBreakpointAt(const Function& func,
- BreakpointLocation* loc) {
+void GroupDebugger::MakeCodeBreakpointAt(const Function& func,
+ BreakpointLocation* loc) {
ASSERT(loc->token_pos_.IsReal());
ASSERT((loc != NULL) && loc->IsResolved());
ASSERT(!func.HasOptimizedCode());
@@ -2464,19 +2511,31 @@
if (lowest_pc_offset != kUwordMax) {
uword lowest_pc = code.PayloadStart() + lowest_pc_offset;
CodeBreakpoint* code_bpt = GetCodeBreakpoint(lowest_pc);
- if (code_bpt == NULL) {
- // No code breakpoint for this code exists; create one.
- code_bpt =
- new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
- if (FLAG_verbose_debug) {
- OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
- " offset %#" Px "\n",
- loc->token_pos_.ToCString(), lowest_pc,
- lowest_pc - code.PayloadStart());
+ if (code_bpt == nullptr) {
+ SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ code_bpt = GetCodeBreakpoint(lowest_pc);
+ if (code_bpt == nullptr) {
+ // No code breakpoint for this code exists; create one.
+ code_bpt =
+ new CodeBreakpoint(code, loc->token_pos_, lowest_pc, lowest_kind);
+ if (FLAG_verbose_debug) {
+ OS::PrintErr("Setting code breakpoint at pos %s pc %#" Px
+ " offset %#" Px "\n",
+ loc->token_pos_.ToCString(), lowest_pc,
+ lowest_pc - code.PayloadStart());
+ }
+ RegisterCodeBreakpoint(code_bpt);
+ } else {
+ if (FLAG_verbose_debug) {
+ OS::PrintErr(
+ "Adding location to existing code breakpoint at pos %s pc %#" Px
+ " offset %#" Px "\n",
+ loc->token_pos_.ToCString(), lowest_pc,
+ lowest_pc - code.PayloadStart());
+ }
}
- RegisterCodeBreakpoint(code_bpt);
}
- code_bpt->set_bpt_location(loc);
+ code_bpt->AddBreakpointLocation(loc);
if (loc->AnyEnabled()) {
code_bpt->Enable();
}
@@ -2734,7 +2793,7 @@
requested_column);
}
if (loc == NULL) {
- loc = new BreakpointLocation(script, breakpoint_pos, breakpoint_pos,
+ loc = new BreakpointLocation(this, script, breakpoint_pos, breakpoint_pos,
requested_line, requested_column);
RegisterBreakpointLocation(loc);
}
@@ -2751,7 +2810,7 @@
for (intptr_t i = 0; i < num_functions; i++) {
func ^= functions.At(i);
ASSERT(func.HasCode());
- MakeCodeBreakpointAt(func, loc);
+ group_debugger()->MakeCodeBreakpointAt(func, loc);
}
if (FLAG_verbose_debug) {
intptr_t line_number = -1;
@@ -2842,7 +2901,7 @@
BreakpointLocation* loc =
GetBreakpointLocation(script, token_pos, -1, requested_column);
if (loc == NULL) {
- loc = new BreakpointLocation(script, token_pos, last_token_pos,
+ loc = new BreakpointLocation(this, script, token_pos, last_token_pos,
requested_line, requested_column);
RegisterBreakpointLocation(loc);
}
@@ -2851,12 +2910,13 @@
// Synchronize the enabled/disabled state of all code breakpoints
// associated with the breakpoint location loc.
-void Debugger::SyncBreakpointLocation(BreakpointLocation* loc) {
+void GroupDebugger::SyncBreakpointLocation(BreakpointLocation* loc) {
bool any_enabled = loc->AnyEnabled();
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
- if (loc == cbpt->bpt_location()) {
+ if (cbpt->HasBreakpointLocation(loc)) {
if (any_enabled) {
cbpt->Enable();
} else {
@@ -2867,6 +2927,17 @@
}
}
+void GroupDebugger::DisableCodeBreakpointsFor(BreakpointLocation* location) {
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ CodeBreakpoint* cbpt = code_breakpoints_;
+ while (cbpt != nullptr) {
+ if (cbpt->HasBreakpointLocation(location)) {
+ cbpt->Disable();
+ }
+ cbpt = cbpt->next();
+ }
+}
+
Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
bool single_shot) {
ASSERT(!target_function.IsNull());
@@ -3044,6 +3115,14 @@
return loc;
}
+void GroupDebugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
+ CodeBreakpoint* cbpt = code_breakpoints_;
+ while (cbpt != nullptr) {
+ cbpt->VisitObjectPointers(visitor);
+ cbpt = cbpt->next();
+ }
+}
+
// static
void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
ASSERT(visitor != NULL);
@@ -3057,11 +3136,6 @@
loc->VisitObjectPointers(visitor);
loc = loc->next();
}
- CodeBreakpoint* cbpt = code_breakpoints_;
- while (cbpt != NULL) {
- cbpt->VisitObjectPointers(visitor);
- cbpt = cbpt->next();
- }
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&top_frame_awaiter_));
}
@@ -3102,10 +3176,14 @@
}
}
+ group_debugger()->Pause();
+ pause_event_ = nullptr;
+}
+
+void GroupDebugger::Pause() {
if (needs_breakpoint_cleanup_) {
RemoveUnlinkedCodeBreakpoints();
}
- pause_event_ = NULL;
}
void Debugger::EnterSingleStepMode() {
@@ -3664,7 +3742,7 @@
// If there is an active breakpoint at this pc, then we should have
// already bailed out of this function in the skip_next_step_ test
// above.
- ASSERT(!HasActiveBreakpoint(frame->pc()));
+ ASSERT(!group_debugger()->HasActiveBreakpoint(frame->pc()));
if (FLAG_verbose_debug) {
OS::PrintErr(">>> single step break at %s:%" Pd ":%" Pd
@@ -3701,17 +3779,19 @@
ASSERT(stack_trace->Length() > 0);
ActivationFrame* top_frame = stack_trace->FrameAt(0);
ASSERT(top_frame != nullptr);
- CodeBreakpoint* cbpt = GetCodeBreakpoint(top_frame->pc());
- ASSERT(cbpt != nullptr);
-
if (!Library::Handle(top_frame->Library()).IsDebuggable()) {
return Error::null();
}
- BreakpointLocation* bpt_location = cbpt->bpt_location_;
+ CodeBreakpoint* cbpt = nullptr;
+ BreakpointLocation* bpt_location =
+ group_debugger()->GetBreakpointLocationFor(this, top_frame->pc(), &cbpt);
if (bpt_location == nullptr) {
+ // There might be no breakpoint locations for this isolate/debugger.
return Error::null();
}
+ ASSERT(cbpt != nullptr);
+
Breakpoint* bpt_hit = bpt_location->FindHitBreakpoint(top_frame);
if (bpt_hit == nullptr) {
return Error::null();
@@ -3967,7 +4047,7 @@
bpt = bpt->next();
}
}
- MakeCodeBreakpointAt(func, loc);
+ group_debugger()->MakeCodeBreakpointAt(func, loc);
}
}
}
@@ -4039,9 +4119,9 @@
if (existing_loc == NULL) {
// Create and register a new source breakpoint for the
// latent breakpoint.
- BreakpointLocation* unresolved_loc =
- new BreakpointLocation(script, first_token_pos, last_token_pos,
- line_number, column_number);
+ BreakpointLocation* unresolved_loc = new BreakpointLocation(
+ this, script, first_token_pos, last_token_pos, line_number,
+ column_number);
RegisterBreakpointLocation(unresolved_loc);
// Move breakpoints over.
@@ -4058,7 +4138,7 @@
}
bpt = bpt->next();
}
- SyncBreakpointLocation(unresolved_loc);
+ group_debugger()->SyncBreakpointLocation(unresolved_loc);
}
delete matched_loc;
// Break out of the iteration over loaded libraries. If the
@@ -4093,12 +4173,12 @@
// TODO(hausner): Could potentially make this faster by checking
// whether the call target at pc is a debugger stub.
-bool Debugger::HasActiveBreakpoint(uword pc) {
+bool GroupDebugger::HasActiveBreakpoint(uword pc) {
CodeBreakpoint* cbpt = GetCodeBreakpoint(pc);
- return (cbpt != NULL) && (cbpt->IsEnabled());
+ return (cbpt != nullptr) && (cbpt->IsEnabled());
}
-CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) {
+CodeBreakpoint* GroupDebugger::GetCodeBreakpoint(uword breakpoint_address) {
CodeBreakpoint* cbpt = code_breakpoints_;
while (cbpt != NULL) {
if (cbpt->pc() == breakpoint_address) {
@@ -4106,10 +4186,33 @@
}
cbpt = cbpt->next();
}
- return NULL;
+ return nullptr;
}
-CodePtr Debugger::GetPatchedStubAddress(uword breakpoint_address) {
+BreakpointLocation* GroupDebugger::GetBreakpointLocationFor(
+ Debugger* debugger,
+ uword breakpoint_address,
+ CodeBreakpoint** pcbpt) {
+ ASSERT(pcbpt != nullptr);
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ *pcbpt = code_breakpoints_;
+ while (*pcbpt != nullptr) {
+ if ((*pcbpt)->pc() == breakpoint_address) {
+ return (*pcbpt)->FindBreakpointForDebugger(debugger);
+ }
+ *pcbpt = (*pcbpt)->next();
+ }
+ return nullptr;
+}
+
+void GroupDebugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
+ ASSERT(cbpt->next() == NULL);
+ DEBUG_ASSERT(code_breakpoints_lock()->IsCurrentThreadWriter());
+ cbpt->set_next(code_breakpoints_);
+ code_breakpoints_ = cbpt;
+}
+
+CodePtr GroupDebugger::GetPatchedStubAddress(uword breakpoint_address) {
CodeBreakpoint* cbpt = GetCodeBreakpoint(breakpoint_address);
if (cbpt != NULL) {
return cbpt->OrigStubAddress();
@@ -4174,7 +4277,7 @@
// Remove references from code breakpoints to this breakpoint
// location and disable them.
// Latent breakpoint locations won't have code breakpoints.
- UnlinkCodeBreakpoints(curr_loc);
+ group_debugger()->UnlinkCodeBreakpoints(curr_loc);
}
BreakpointLocation* next_loc = curr_loc->next();
delete curr_loc;
@@ -4200,14 +4303,16 @@
// They will later be deleted when control returns from the pause event
// callback. Also, disable the breakpoint so it no longer fires if it
// should be hit before it gets deleted.
-void Debugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
- ASSERT(bpt_location != NULL);
+void GroupDebugger::UnlinkCodeBreakpoints(BreakpointLocation* bpt_location) {
+ ASSERT(bpt_location != nullptr);
+ SafepointReadRwLocker sl(Thread::Current(), code_breakpoints_lock());
CodeBreakpoint* curr_bpt = code_breakpoints_;
- while (curr_bpt != NULL) {
- if (curr_bpt->bpt_location() == bpt_location) {
- curr_bpt->Disable();
- curr_bpt->set_bpt_location(NULL);
- needs_breakpoint_cleanup_ = true;
+ while (curr_bpt != nullptr) {
+ if (curr_bpt->FindAndDeleteBreakpointLocation(bpt_location)) {
+ if (curr_bpt->HasNoBreakpointLocations()) {
+ curr_bpt->Disable();
+ needs_breakpoint_cleanup_ = true;
+ }
}
curr_bpt = curr_bpt->next();
}
@@ -4215,19 +4320,19 @@
// Remove and delete unlinked code breakpoints, i.e. breakpoints that
// are not associated with a breakpoint location.
-void Debugger::RemoveUnlinkedCodeBreakpoints() {
- CodeBreakpoint* prev_bpt = NULL;
+void GroupDebugger::RemoveUnlinkedCodeBreakpoints() {
+ SafepointWriteRwLocker sl(Thread::Current(), code_breakpoints_lock());
+ CodeBreakpoint* prev_bpt = nullptr;
CodeBreakpoint* curr_bpt = code_breakpoints_;
- while (curr_bpt != NULL) {
- if (curr_bpt->bpt_location() == NULL) {
- if (prev_bpt == NULL) {
+ while (curr_bpt != nullptr) {
+ if (curr_bpt->HasNoBreakpointLocations()) {
+ if (prev_bpt == nullptr) {
code_breakpoints_ = code_breakpoints_->next();
} else {
prev_bpt->set_next(curr_bpt->next());
}
CodeBreakpoint* temp_bpt = curr_bpt;
curr_bpt = curr_bpt->next();
- temp_bpt->Disable();
delete temp_bpt;
} else {
prev_bpt = curr_bpt;
@@ -4317,7 +4422,7 @@
loc = loc->next();
}
// No breakpoint for this location requested. Allocate new one.
- loc = new BreakpointLocation(url, line, column);
+ loc = new BreakpointLocation(this, url, line, column);
loc->set_next(latent_locations_);
latent_locations_ = loc;
return loc;
@@ -4329,12 +4434,6 @@
breakpoint_locations_ = loc;
}
-void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* cbpt) {
- ASSERT(cbpt->next() == NULL);
- cbpt->set_next(code_breakpoints_);
- code_breakpoints_ = cbpt;
-}
-
#endif // !PRODUCT
} // namespace dart
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 6378fb1..c833e5d 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -5,6 +5,8 @@
#ifndef RUNTIME_VM_DEBUGGER_H_
#define RUNTIME_VM_DEBUGGER_H_
+#include <memory>
+
#include "include/dart_tools_api.h"
#include "vm/kernel_isolate.h"
@@ -16,6 +18,8 @@
#include "vm/stack_frame.h"
#include "vm/stack_trace.h"
+#if !defined(PRODUCT)
+
DECLARE_FLAG(bool, verbose_debug);
// 'Trace Debugger' TD_Print.
@@ -120,13 +124,15 @@
class BreakpointLocation {
public:
// Create a new unresolved breakpoint.
- BreakpointLocation(const Script& script,
+ BreakpointLocation(Debugger* debugger,
+ const Script& script,
TokenPosition token_pos,
TokenPosition end_token_pos,
intptr_t requested_line_number,
intptr_t requested_column_number);
// Create a new latent breakpoint.
- BreakpointLocation(const String& url,
+ BreakpointLocation(Debugger* debugger,
+ const String& url,
intptr_t requested_line_number,
intptr_t requested_column_number);
@@ -154,6 +160,8 @@
bool IsResolved() const { return code_token_pos_.IsReal(); }
bool IsLatent() const { return !token_pos_.IsReal(); }
+ Debugger* debugger() { return debugger_; }
+
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
@@ -170,6 +178,7 @@
// Finds the breakpoint we hit at |location|.
Breakpoint* FindHitBreakpoint(ActivationFrame* top_frame);
+ Debugger* debugger_;
ScriptPtr script_;
StringPtr url_;
TokenPosition token_pos_;
@@ -184,12 +193,16 @@
TokenPosition code_token_pos_;
friend class Debugger;
+ friend class GroupDebugger;
DISALLOW_COPY_AND_ASSIGN(BreakpointLocation);
};
// CodeBreakpoint represents a location in compiled code.
// There may be more than one CodeBreakpoint for one BreakpointLocation,
// e.g. when a function gets compiled as a regular function and as a closure.
+// There may be more than one BreakpointLocation associated with CodeBreakpoint,
+// one for for every isolate in a group that sets a breakpoint at particular
+// code location represented by the CodeBreakpoint.
class CodeBreakpoint {
public:
CodeBreakpoint(const Code& code,
@@ -201,6 +214,11 @@
FunctionPtr function() const;
uword pc() const { return pc_; }
TokenPosition token_pos() const { return token_pos_; }
+ bool HasBreakpointLocation(BreakpointLocation* breakpoint_location);
+ bool FindAndDeleteBreakpointLocation(BreakpointLocation* breakpoint_location);
+ bool HasNoBreakpointLocations() {
+ return breakpoint_locations_.length() == 0;
+ }
ScriptPtr SourceCode();
StringPtr SourceUrl();
@@ -215,8 +233,10 @@
private:
void VisitObjectPointers(ObjectPointerVisitor* visitor);
- BreakpointLocation* bpt_location() const { return bpt_location_; }
- void set_bpt_location(BreakpointLocation* value) { bpt_location_ = value; }
+ BreakpointLocation* FindBreakpointForDebugger(Debugger* debugger);
+ void AddBreakpointLocation(BreakpointLocation* value) {
+ breakpoint_locations_.Add(value);
+ }
void set_next(CodeBreakpoint* value) { next_ = value; }
CodeBreakpoint* next() const { return this->next_; }
@@ -230,13 +250,16 @@
intptr_t line_number_;
bool is_enabled_;
- BreakpointLocation* bpt_location_;
+ // Breakpoint locations from different debuggers/isolates that
+ // point to this code breakpoint.
+ MallocGrowableArray<BreakpointLocation*> breakpoint_locations_;
CodeBreakpoint* next_;
UntaggedPcDescriptors::Kind breakpoint_kind_;
CodePtr saved_value_;
friend class Debugger;
+ friend class GroupDebugger;
DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint);
};
@@ -473,6 +496,52 @@
kInvalidExceptionPauseInfo
} Dart_ExceptionPauseInfo;
+class GroupDebugger {
+ public:
+ explicit GroupDebugger(IsolateGroup* isolate_group);
+ ~GroupDebugger();
+
+ void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
+ // Returns [nullptr] if no breakpoint exists for the given address.
+ CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
+ BreakpointLocation* GetBreakpointLocationFor(Debugger* debugger,
+ uword breakpoint_address,
+ CodeBreakpoint** pcbpt);
+ CodePtr GetPatchedStubAddress(uword breakpoint_address);
+
+ void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
+
+ // Returns true if the call at address pc is patched to point to
+ // a debugger stub.
+ bool HasActiveBreakpoint(uword pc);
+ bool HasCodeBreakpointInFunction(const Function& func);
+ bool HasCodeBreakpointInCode(const Code& code);
+
+ bool HasBreakpointInFunction(const Function& func);
+ bool HasBreakpointInCode(const Code& code);
+
+ void SyncBreakpointLocation(BreakpointLocation* loc);
+ void DisableCodeBreakpointsFor(BreakpointLocation* loc);
+
+ void Pause();
+
+ void VisitObjectPointers(ObjectPointerVisitor* visitor);
+
+ private:
+ IsolateGroup* isolate_group_;
+
+ std::unique_ptr<SafepointRwLock> code_breakpoints_lock_;
+ CodeBreakpoint* code_breakpoints_;
+
+ SafepointRwLock* code_breakpoints_lock() {
+ return code_breakpoints_lock_.get();
+ }
+ void RemoveUnlinkedCodeBreakpoints();
+ void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
+
+ bool needs_breakpoint_cleanup_;
+};
+
class Debugger {
public:
enum ResumeAction {
@@ -560,11 +629,6 @@
// This may be called from different threads, therefore do not use the,
// debugger's zone.
bool HasBreakpoint(const Function& func, Zone* zone);
- bool HasBreakpoint(const Code& code);
-
- // Returns true if the call at address pc is patched to point to
- // a debugger stub.
- bool HasActiveBreakpoint(uword pc);
// Returns a stack trace with frames corresponding to invisible functions
// omitted. CurrentStackTrace always returns a new trace on the current stack.
@@ -595,14 +659,11 @@
// Dart.
void PauseDeveloper(const String& msg);
- CodePtr GetPatchedStubAddress(uword breakpoint_address);
-
void PrintBreakpointsToJSONArray(JSONArray* jsarr) const;
void PrintSettingsToJSONObject(JSONObject* jsobj) const;
static bool IsDebuggable(const Function& func);
- // IsolateGroupDebugger::
static bool IsDebugging(Thread* thread, const Function& func);
intptr_t limitBreakpointId() { return next_id_; }
@@ -621,17 +682,14 @@
void SendBreakpointEvent(ServiceEvent::EventKind kind, Breakpoint* bpt);
- // IsolateGroupDebugger::
void FindCompiledFunctions(const Script& script,
TokenPosition start_pos,
TokenPosition end_pos,
GrowableObjectArray* code_function_list);
- // IsolateGroupDebugger::
bool FindBestFit(const Script& script,
TokenPosition token_pos,
TokenPosition last_token_pos,
Function* best_fit);
- // IsolateGroupDebugger::
void DeoptimizeWorld();
void NotifySingleStepping(bool value) const;
BreakpointLocation* SetCodeBreakpoints(const Script& script,
@@ -649,24 +707,17 @@
const Function& function);
bool RemoveBreakpointFromTheList(intptr_t bp_id, BreakpointLocation** list);
Breakpoint* GetBreakpointByIdInTheList(intptr_t id, BreakpointLocation* list);
- void RemoveUnlinkedCodeBreakpoints();
- void UnlinkCodeBreakpoints(BreakpointLocation* bpt_location);
BreakpointLocation* GetLatentBreakpoint(const String& url,
intptr_t line,
intptr_t column);
void RegisterBreakpointLocation(BreakpointLocation* bpt);
- void RegisterCodeBreakpoint(CodeBreakpoint* bpt);
BreakpointLocation* GetBreakpointLocation(
const Script& script,
TokenPosition token_pos,
intptr_t requested_line,
intptr_t requested_column,
TokenPosition code_token_pos = TokenPosition::kNoSource);
- void MakeCodeBreakpointAt(const Function& func, BreakpointLocation* bpt);
- // Returns NULL if no breakpoint exists for the given address.
- CodeBreakpoint* GetCodeBreakpoint(uword breakpoint_address);
- void SyncBreakpointLocation(BreakpointLocation* loc);
void PrintBreakpointsListToJSONArray(BreakpointLocation* sbpt,
JSONArray* jsarr) const;
@@ -702,6 +753,8 @@
void SetAsyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
void SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace);
+ GroupDebugger* group_debugger() { return isolate_->group()->debugger(); }
+
Isolate* isolate_;
// ID number generator.
@@ -709,7 +762,6 @@
BreakpointLocation* latent_locations_;
BreakpointLocation* breakpoint_locations_;
- CodeBreakpoint* code_breakpoints_;
// Tells debugger what to do when resuming execution after a breakpoint.
ResumeAction resume_action_;
@@ -751,8 +803,6 @@
// breakpoint.
bool skip_next_step_;
- bool needs_breakpoint_cleanup_;
-
// We keep this breakpoint alive until after the debugger does the step over
// async continuation machinery so that we can report that we've stopped
// at the breakpoint.
@@ -787,4 +837,6 @@
} // namespace dart
+#endif // !defined(PRODUCT)
+
#endif // RUNTIME_VM_DEBUGGER_H_
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 1cb8896..495d026 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -386,7 +386,12 @@
boxed_field_list_(GrowableObjectArray::null()),
program_lock_(new SafepointRwLock()),
active_mutators_monitor_(new Monitor()),
- max_active_mutators_(Scavenger::MaxMutatorThreadCount()) {
+ max_active_mutators_(Scavenger::MaxMutatorThreadCount())
+#if !defined(PRODUCT)
+ ,
+ debugger_(new GroupDebugger(this))
+#endif
+{
FlagsCopyFrom(api_flags);
const bool is_vm_isolate = Dart::VmIsolateNameEquals(source_->name);
if (!is_vm_isolate) {
@@ -425,6 +430,11 @@
}
delete[] obfuscation_map_;
}
+
+#if !defined(PRODUCT)
+ delete debugger_;
+ debugger_ = nullptr;
+#endif
}
void IsolateGroup::RegisterIsolate(Isolate* isolate) {
@@ -2528,7 +2538,7 @@
}
// Cache these two fields, since they are no longer available after the
- // `delete this` further down.
+ // `delete isolate` further down.
IsolateGroup* isolate_group = isolate->isolate_group_;
Dart_IsolateCleanupCallback cleanup = isolate->on_cleanup_callback();
auto callback_data = isolate->init_callback_data_;
@@ -2811,6 +2821,12 @@
NOT_IN_PRECOMPILED(background_compiler()->VisitPointers(visitor));
+#if !defined(PRODUCT)
+ if (debugger() != nullptr) {
+ debugger()->VisitObjectPointers(visitor);
+ }
+#endif
+
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// Visit objects that are being used for isolate reload.
if (program_reload_context() != nullptr) {
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index d146eac..90f557a 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -51,6 +51,7 @@
class Debugger;
class DeoptContext;
class ExternalTypedData;
+class GroupDebugger;
class HandleScope;
class HandleVisitor;
class Heap;
@@ -344,6 +345,10 @@
#endif
}
+#if !defined(PRODUCT)
+ GroupDebugger* debugger() const { return debugger_; }
+#endif
+
IdleTimeHandler* idle_time_handler() { return &idle_time_handler_; }
// Returns true if this is the first isolate registered.
@@ -938,6 +943,8 @@
intptr_t active_mutators_ = 0;
intptr_t waiting_mutators_ = 0;
intptr_t max_active_mutators_ = 0;
+
+ NOT_IN_PRODUCT(GroupDebugger* debugger_ = nullptr);
};
// When an isolate sends-and-exits this class represent things that it passed
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e1e0d82..37da0f5 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -16333,17 +16333,7 @@
#if defined(PRODUCT)
return false;
#else
- // TODO(dartbug.com/36097): We might need to adjust this once we start adding
- // debugging support to --enable-isolate-groups.
- auto isolate_group = Thread::Current()->isolate_group();
-
- bool has_breakpoint = false;
- isolate_group->ForEachIsolate([&](Isolate* isolate) {
- if (isolate->debugger()->HasBreakpoint(*this)) {
- has_breakpoint = true;
- }
- });
- return has_breakpoint;
+ return IsolateGroup::Current()->debugger()->HasBreakpointInCode(*this);
#endif
}
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 5b562ac..95d3875 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -1146,7 +1146,8 @@
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != NULL);
Code& orig_stub = Code::Handle(zone);
- orig_stub = isolate->debugger()->GetPatchedStubAddress(caller_frame->pc());
+ orig_stub =
+ isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
const Error& error =
Error::Handle(zone, isolate->debugger()->PauseBreakpoint());
ThrowIfError(error);
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index e56f4eae..ee7a7ec 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4309,11 +4309,12 @@
Service::HandleEvent(&event);
}
-void Service::SendEmbedderEvent(const char* stream_id,
+void Service::SendEmbedderEvent(Isolate* isolate,
+ const char* stream_id,
const char* event_kind,
const uint8_t* bytes,
intptr_t bytes_len) {
- ServiceEvent event(ServiceEvent::kEmbedder);
+ ServiceEvent event(isolate, ServiceEvent::kEmbedder);
event.set_embedder_kind(event_kind);
event.set_embedder_stream_id(stream_id);
event.set_bytes(bytes, bytes_len);
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index 99c7e20..00a3f32 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -125,7 +125,8 @@
static void SendEchoEvent(Isolate* isolate, const char* text);
static void SendInspectEvent(Isolate* isolate, const Object& inspectee);
- static void SendEmbedderEvent(const char* stream_id,
+ static void SendEmbedderEvent(Isolate* isolate,
+ const char* stream_id,
const char* event_kind,
const uint8_t* bytes,
intptr_t bytes_len);
diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status
index 0f04e9d..96a4ef6 100644
--- a/tests/co19/co19-co19.status
+++ b/tests/co19/co19-co19.status
@@ -20,6 +20,7 @@
LanguageFeatures/nnbd/local_variable_read_A01_t03: Skip # Triple shift is not implemented yet
LanguageFeatures/nnbd/local_variable_read_A02_t03: Skip # Triple shift is not implemented yet
LanguageFeatures/nnbd/local_variable_read_A03_t03: Skip # Triple shift is not implemented yet
+LibTest/ffi/Array/PointerArray_A01_t01: Skip # https://github.com/dart-lang/co19/issues/1018
LibTest/io/RawDatagramSocket/*: Skip # https://github.com/dart-lang/co19/issues/195
[ $compiler == dart2analyzer ]
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 1e1adac..07256d4 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -3,19 +3,19 @@
# BSD-style license that can be found in the LICENSE file.
[ $compiler == dart2js ]
-Language/Expressions/Constants/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/integer_size_t04: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/literal_number_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t06: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Constants/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/integer_size_t04: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/literal_number_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
-Language/Expressions/Numbers/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t06: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t09: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Numbers/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
Language/Expressions/Object_Identity/object_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/42222#issuecomment-640431711
-Language/Expressions/Shift/integer_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Shift/integer_t02: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Shift/integer_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Shift/integer_t02: SkipByDesign # uses integer literal not representable as JavaScript numbert
Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign
Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # Non-JS-interop external members are not supported
Language/Libraries_and_Scripts/Scripts/main_optional_parameters_t01: SkipByDesign # https://github.com/dart-lang/co19/issues/952
diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status
index 7f9fab3..ebc0be6 100644
--- a/tests/co19/co19-dartdevc.status
+++ b/tests/co19/co19-dartdevc.status
@@ -52,21 +52,21 @@
Language/Classes/Instance_Variables/definition_t02: Skip # Times out
Language/Classes/Instance_Variables/definition_t04: Skip # Times out
Language/Classes/Setters/instance_setter_t01: Skip # Times out
-Language/Expressions/Constants/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/integer_size_t04: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/literal_number_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Constants/math_operators_t06: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Constants/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/integer_size_t04: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/literal_number_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Constants/math_operators_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
Language/Expressions/Function_Invocation/async_generator_invokation_t08: Skip # Times out
Language/Expressions/Function_Invocation/async_generator_invokation_t10: Skip # Times out
Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
-Language/Expressions/Numbers/integer_size_t03: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t06: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Numbers/syntax_t09: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Numbers/integer_size_t03: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/static_type_of_int_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript numbert
Language/Expressions/Object_Identity/object_t02: SkipByDesign # https://github.com/dart-lang/sdk/issues/42222#issuecomment-640431711
-Language/Expressions/Shift/integer_t01: SkipByDesign # big integer cannot be represented in JavaScript
-Language/Expressions/Shift/integer_t02: SkipByDesign # big integer cannot be represented in JavaScript
+Language/Expressions/Shift/integer_t01: SkipByDesign # uses integer literal not representable as JavaScript numbert
+Language/Expressions/Shift/integer_t02: SkipByDesign # uses integer literal not representable as JavaScript numbert
Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign # dart:isolate not supported.
Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # External variables are not supported
Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index d54175c..80fa459 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -42,4 +42,5 @@
LibTest/ffi/*: SkipByDesign # dart:ffi is not supported on sim architectures
[ $compiler == fasta || $runtime == dart_precompiled || $runtime == vm ]
+Language/Expressions/Numbers/syntax_t34: SkipByDesign # expects integer literal error that only occurs in JS
LibTest/html/*: SkipByDesign # dart:html not supported on VM.
diff --git a/tests/co19_2/co19_2-dart2js.status b/tests/co19_2/co19_2-dart2js.status
index 0d620e8..63158f2 100644
--- a/tests/co19_2/co19_2-dart2js.status
+++ b/tests/co19_2/co19_2-dart2js.status
@@ -4,6 +4,8 @@
[ $compiler == dart2js ]
Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign
Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # Non-JS-interop external members are not supported
Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # Non-JS-interop external members are not supported
diff --git a/tests/co19_2/co19_2-dartdevc.status b/tests/co19_2/co19_2-dartdevc.status
index 7497344..4ce31b8 100644
--- a/tests/co19_2/co19_2-dartdevc.status
+++ b/tests/co19_2/co19_2-dartdevc.status
@@ -55,6 +55,8 @@
Language/Expressions/Function_Invocation/async_generator_invokation_t08: Skip # Times out
Language/Expressions/Function_Invocation/async_generator_invokation_t10: Skip # Times out
Language/Expressions/Null/instance_of_class_null_t01: Skip # dart:mirrors not supported https://github.com/dart-lang/co19/issues/522.
+Language/Expressions/Numbers/syntax_t06: SkipByDesign # uses integer literal not representable as JavaScript number
+Language/Expressions/Numbers/syntax_t09: SkipByDesign # uses integer literal not representable as JavaScript number
Language/Expressions/Spawning_an_Isolate/new_isolate_t01: SkipByDesign # dart:isolate not supported.
Language/Functions/External_Functions/not_connected_to_a_body_t01: SkipByDesign # External variables are not supported
Language/Libraries_and_Scripts/Scripts/top_level_syntax_t01: SkipByDesign # External variables are not supported
diff --git a/tests/co19_2/co19_2-runtime.status b/tests/co19_2/co19_2-runtime.status
index a09c881..c9cfe7d 100644
--- a/tests/co19_2/co19_2-runtime.status
+++ b/tests/co19_2/co19_2-runtime.status
@@ -33,4 +33,5 @@
LibTest/collection/ListMixin/ListMixin_class_A01_t06: SkipSlow # Very slow on sim* architectures.
[ $compiler == fasta || $runtime == dart_precompiled || $runtime == vm ]
+Language/Expressions/Numbers/syntax_t34: SkipByDesign # expects integer literal error that only occurs in JS
LibTest/html/*: SkipByDesign # dart:html not supported on VM.
diff --git a/tests/language/const_functions/const_functions_while_statements_test.dart b/tests/language/const_functions/const_functions_while_statements_test.dart
new file mode 100644
index 0000000..59ea40c
--- /dev/null
+++ b/tests/language/const_functions/const_functions_while_statements_test.dart
@@ -0,0 +1,46 @@
+// 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 while statements for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = fn(2);
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var2 = fn(3);
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn(int a) {
+ int b = a;
+ int i = 0;
+ while (i < 2) {
+ b += a;
+ i++;
+ }
+ return b;
+}
+
+const var3 = fn1(2);
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var4 = fn1(3);
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn1(int a) {
+ int b = a;
+ while (true) {
+ b *= 3;
+ if (b > 10) return b;
+ }
+}
+
+void main() {
+ Expect.equals(var1, 6);
+ Expect.equals(var2, 9);
+ Expect.equals(var3, 18);
+ Expect.equals(var4, 27);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 5a93430..2965fb3 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 148
+PRERELEASE 149
PRERELEASE_PATCH 0
\ No newline at end of file