Version 2.13.0-163.0.dev
Merge commit 'e8d7058f165cc49a9fc542b90587f6c548d4b358' into 'dev'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 871a73d..13057f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,8 +24,12 @@
#### Linter
-Updated the Linter to `1.1.0`, which includes:
+Updated the Linter to `1.2.1`, which includes:
+- improvements to `iterable_contains_unrelated_type` to better support `List`
+ content checks.
+- fixes to `camel_case_types` and `prefer_mixin` to support non-function
+ type aliases.
- fixed `prefer_mixin` to properly make exceptions for `dart.collection`
legacy mixins.
- new lint: `use_build_context_synchronously` (experimental).
@@ -36,6 +40,34 @@
- new lint: `use_named_constants`.
- deprecation of `avoid_as`.
+### Pub
+
+* `dart pub publish` now respects `.pubignore` files with gitignore-style rules.
+ `.gitignore` files in the repo are still respected if they are not
+ overridden by a `.pubignore` in the same directory.
+
+ pub no longer queries git for listing the files. This implies:
+ * Checked in files will now be ignored if they are included by a `.gitignore`
+ rule.
+ * Global ignores are no longer taken into account.
+ * Even packages that are not in git source control will have their
+ `.gitignore` files respected.
+
+* New flag `dart pub deps --json` gives a machine parsable overview of the
+ current dependencies.
+* New command: `dart pub cache clean`. Will delete everything in your current
+ pub cache.
+* Commands related to a single package now takes a `--directory` option to
+ operate on a package in the given directory instead of the working directory.
+* git dependencies with a relative repo url would previously be interpreted
+ relative to the current package, even for transitive dependencies. This now
+ fails instead.
+
+* Pub now uses a Dart library to read and write tar files.
+ This should fix several issues we had with incompatibilities between different
+ system `tar`s.
+* `PUB_HOSTED_URL` can now include a trailing slash.
+
## 2.12.2 - 2021-03-17
This is a patch release that fixes crashes reported by Flutter 2 users (issue
diff --git a/DEPS b/DEPS
index dc039c0..70d86d7 100644
--- a/DEPS
+++ b/DEPS
@@ -119,7 +119,7 @@
"intl_tag": "0.17.0-nullsafety",
"jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
"json_rpc_2_rev": "b8dfe403fd8528fd14399dee3a6527b55802dd4d",
- "linter_tag": "1.1.0",
+ "linter_tag": "1.2.1",
"logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
"markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
"markdown_rev": "9c4beaac96d8f008078e00b027915f81b665d2de",
@@ -136,7 +136,7 @@
"pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
"process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
"protobuf_rev": "0d03fd588df69e9863e2a2efc0059dee8f18d5b2",
- "pub_rev": "0e657414a472e74ca5dd76ae0db50cc060251dec",
+ "pub_rev": "255a3091fc278b04be74d246a3bec8743ef4d0b7",
"pub_semver_rev": "f50d80ef10c4b2fa5f4c8878036a4d9342c0cc82",
"resource_rev": "6b79867d0becf5395e5819a75720963b8298e9a7",
"root_certificates_rev": "7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8",
@@ -162,7 +162,7 @@
"test_reflective_loader_rev": "54e930a11c372683792e22bddad79197728c91ce",
"test_rev": "e673623f45d75ccec750d35271b0b4d1423e9fac",
"typed_data_tag": "f94fc57b8e8c0e4fe4ff6cfd8290b94af52d3719",
- "usage_rev": "6c64d9e7b6b3758d06d030efcb5afe20bfc04dde",
+ "usage_rev": "5b7317ba89166f3cf1af98cb280a4cc8e78f25f9",
"vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
"watcher_rev": "3924194385fb215cef483193ed2879a618a3d69c",
"webdriver_rev": "ff5ccb1522edf4bed578ead4d65e0cbc1f2c4f02",
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
index d8b48dc..ea06634 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/feature_computer.dart
@@ -669,7 +669,16 @@
if (range.endEnd(node.functionDefinition, node).contains(offset)) {
var parent = node.parent;
if (parent is MethodDeclaration) {
- return BodyInferenceContext.of(parent.body).contextType;
+ var bodyContext = BodyInferenceContext.of(parent.body);
+ // TODO(scheglov) https://github.com/dart-lang/sdk/issues/45429
+ if (bodyContext == null) {
+ throw StateError('''
+Expected body context.
+Method: $parent
+Class: ${parent.parent}
+''');
+ }
+ return bodyContext.contextType;
} else if (parent is FunctionExpression) {
var grandparent = parent.parent;
if (grandparent is FunctionDeclaration) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index 9bd876d..c2e5b28 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -302,8 +302,13 @@
(r) => r.path == elementPath,
orElse: () {
var elementStr = element.getDisplayString(withNullability: true);
- throw ArgumentError('Element (${element.runtimeType}) $elementStr is '
- 'not defined in this library.');
+ var buffer = StringBuffer();
+ buffer.write('Element (${element.runtimeType}) $elementStr');
+ buffer.writeln(' is not defined in this library.');
+ // TODO(scheglov) https://github.com/dart-lang/sdk/issues/45430
+ buffer.writeln('elementPath: $elementPath');
+ buffer.writeln('unitPaths: ${units!.map((e) => e.path).toList()}');
+ throw ArgumentError('$buffer');
},
);
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 845aa80..921b235 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -323,8 +323,8 @@
if (analyzer is YamlMap) {
var filters = analyzer.valueAt(AnalyzerOptions.errors);
if (filters is YamlMap) {
- String? value;
filters.nodes.forEach((k, v) {
+ String? value;
if (k is YamlScalar) {
value = toUpperCase(k.value);
if (!errorCodes.contains(value) && !lintCodes.contains(value)) {
@@ -347,8 +347,18 @@
legalValueString
]);
}
+ } else {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ v.span,
+ [AnalyzerOptions.enableExperiment]);
}
});
+ } else if (filters != null) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ filters.span,
+ [AnalyzerOptions.enableExperiment]);
}
}
}
@@ -450,6 +460,11 @@
}
}
});
+ } else if (v != null) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ v.span,
+ [AnalyzerOptions.enableExperiment]);
}
}
}
@@ -533,6 +548,11 @@
}
}
});
+ } else if (v != null) {
+ reporter.reportErrorForSpan(
+ AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+ v.span,
+ [AnalyzerOptions.enableExperiment]);
}
}
}
diff --git a/pkg/analyzer/test/generated/error_parser_test.dart b/pkg/analyzer/test/generated/error_parser_test.dart
index 4889978..c7b7232 100644
--- a/pkg/analyzer/test/generated/error_parser_test.dart
+++ b/pkg/analyzer/test/generated/error_parser_test.dart
@@ -1886,8 +1886,8 @@
expectedEndOffset: 14 /* parsing ends at synthetic ')' */);
FormalParameterList list = parser.parseFormalParameterList();
expectNotNullIfNoErrors(list);
- listener.errors
- .contains(expectedError(ParserErrorCode.EXPECTED_TOKEN, 14, 1));
+ listener
+ .assertErrors([expectedError(ScannerErrorCode.EXPECTED_TOKEN, 14, 1)]);
}
void test_missingConstFinalVarOrType_static() {
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index c021511..c7b5907 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -261,6 +261,24 @@
''', [AnalysisOptionsWarningCode.UNRECOGNIZED_ERROR_CODE]);
}
+ test_analyzer_errors_notAMap() {
+ validate('''
+analyzer:
+ errors:
+ - invalid_annotation
+ - unused_import
+ ''', [AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT]);
+ }
+
+ test_analyzer_errors_valueNotAScalar() {
+ validate('''
+analyzer:
+ errors:
+ invalid_annotation: ignore
+ unused_import: [1, 2, 3]
+ ''', [AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT]);
+ }
+
test_analyzer_language_bad_format_list() {
validate('''
analyzer:
@@ -297,14 +315,14 @@
analyzer:
errors:
fantastic_test_rule: ignore
- ''', []);
+''', []);
}
test_analyzer_strong_mode_deprecated() {
validate('''
analyzer:
strong-mode: true
- ''', [AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED]);
+''', [AnalysisOptionsHintCode.STRONG_MODE_SETTING_DEPRECATED]);
}
test_analyzer_strong_mode_deprecated_key() {
@@ -327,7 +345,15 @@
validate('''
analyzer:
strong-mode: false
- ''', [AnalysisOptionsWarningCode.SPEC_MODE_REMOVED]);
+''', [AnalysisOptionsWarningCode.SPEC_MODE_REMOVED]);
+ }
+
+ test_analyzer_strong_mode_notAMap() {
+ validate('''
+analyzer:
+ strong-mode:
+ - implicit_casts
+''', [AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT]);
}
test_analyzer_strong_mode_unsupported_key() {
@@ -351,21 +377,21 @@
analyzer:
exclude:
- test/_data/p4/lib/lib1.dart
- ''', []);
+''', []);
}
test_analyzer_supported_strong_mode_supported_bad_value() {
validate('''
analyzer:
strong-mode: w00t
- ''', [AnalysisOptionsWarningCode.UNSUPPORTED_VALUE]);
+''', [AnalysisOptionsWarningCode.UNSUPPORTED_VALUE]);
}
test_analyzer_unsupported_option() {
validate('''
analyzer:
not_supported: true
- ''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES]);
+''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUES]);
}
test_chromeos_manifest_checks() {
@@ -384,6 +410,14 @@
''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITH_LEGAL_VALUE]);
}
+ test_chromeos_manifest_checks_notAMap() {
+ validate('''
+analyzer:
+ optional-checks:
+ - chrome-os-manifest-checks
+''', [AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT]);
+ }
+
test_linter_supported_rules() {
Registry.ruleRegistry.register(TestRule());
validate('''
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 87e47b5..ba133a6 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -164,7 +164,7 @@
return canBeNegativeZero(input.left) && canBePositiveZero(input.right);
}
if (input is HPhi) {
- if (input.inputs.any((phiInput) => phiInput.block.id > input.block.id)) {
+ if (input.inputs.any((phiInput) => phiInput.block.id >= input.block.id)) {
return true; // Assume back-edge may be negative zero.
}
return input.inputs.any(canBeNegativeZero);
diff --git a/pkg/compiler/lib/src/ssa/variable_allocator.dart b/pkg/compiler/lib/src/ssa/variable_allocator.dart
index c279758..a544500 100644
--- a/pkg/compiler/lib/src/ssa/variable_allocator.dart
+++ b/pkg/compiler/lib/src/ssa/variable_allocator.dart
@@ -168,14 +168,22 @@
int instructionId = 0;
/// The liveIns of basic blocks.
- final Map<HBasicBlock, LiveEnvironment> liveInstructions;
+ final Map<HBasicBlock, LiveEnvironment> liveInstructions = {};
/// The live intervals of instructions.
- final Map<HInstruction, LiveInterval> liveIntervals;
+ final Map<HInstruction, LiveInterval> liveIntervals = {};
- SsaLiveIntervalBuilder(this.generateAtUseSite, this.controlFlowOperators)
- : liveInstructions = new Map<HBasicBlock, LiveEnvironment>(),
- liveIntervals = new Map<HInstruction, LiveInterval>();
+ /// Controlling conditions for control-flow operators. Control-flow operators,
+ /// e.g. `c ? a : b`, have a condition input `c` from the HIf node
+ /// at the top of the control flow diamond as well as the HPhi inputs for `a`
+ /// and `b` at the bottom of the diamond.
+ final Map<HInstruction, HInstruction> _phiToCondition = {};
+
+ SsaLiveIntervalBuilder(this.generateAtUseSite, this.controlFlowOperators) {
+ for (HIf ifNode in controlFlowOperators) {
+ _phiToCondition[ifNode.joinBlock.phis.first] = ifNode.condition;
+ }
+ }
@override
void visitGraph(HGraph graph) {
@@ -187,6 +195,12 @@
void markInputsAsLiveInEnvironment(
HInstruction instruction, LiveEnvironment environment) {
+ if (instruction is HPhi) {
+ HInstruction condition = _phiToCondition[instruction];
+ if (condition != null) {
+ markAsLiveInEnvironment(condition, environment);
+ }
+ }
for (int i = 0, len = instruction.inputs.length; i < len; i++) {
markAsLiveInEnvironment(instruction.inputs[i], environment);
}
@@ -257,21 +271,7 @@
@override
void visitBasicBlock(HBasicBlock block) {
- LiveEnvironment environment =
- new LiveEnvironment(liveIntervals, instructionId);
-
- // If the control flow instruction in this block will actually be
- // inlined in the codegen in the join block, we need to make
- // whatever is used by that control flow instruction as live in
- // the join block.
- if (controlFlowOperators.contains(block.last)) {
- HIf ifInstruction = block.last;
- HBasicBlock joinBlock = ifInstruction.joinBlock;
- if (generateAtUseSite.contains(joinBlock.phis.first)) {
- markInputsAsLiveInEnvironment(
- ifInstruction, liveInstructions[joinBlock]);
- }
- }
+ LiveEnvironment environment = LiveEnvironment(liveIntervals, instructionId);
// Add to the environment the liveIn of its successor, as well as
// the inputs of the phis of the successor that flow from this block.
diff --git a/pkg/dartdev/lib/src/analysis_server.dart b/pkg/dartdev/lib/src/analysis_server.dart
index 4102f12..2908f51 100644
--- a/pkg/dartdev/lib/src/analysis_server.dart
+++ b/pkg/dartdev/lib/src/analysis_server.dart
@@ -251,6 +251,10 @@
String get correction => json['correction'] as String;
+ int get endColumn => json['location']['endColumn'] as int;
+
+ int get endLine => json['location']['endLine'] as int;
+
String get file => json['location']['file'] as String;
int get startLine => json['location']['startLine'] as int;
@@ -307,11 +311,19 @@
int get column => json['location']['startColumn'] as int;
+ int get endColumn => json['location']['endColumn'] as int;
+
+ int get endLine => json['location']['endLine'] as int;
+
String get filePath => json['location']['file'] as String;
+ int get length => json['location']['length'] as int;
+
int get line => json['location']['startLine'] as int;
String get message => json['message'] as String;
+
+ int get offset => json['location']['offset'] as int;
}
class FileAnalysisErrors {
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index 4bdcd62..95e8a37 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
+import 'dart:convert';
import 'dart:io' as io;
import 'package:cli_util/cli_logging.dart';
@@ -47,12 +48,13 @@
'format',
valueHelp: 'value',
help: 'Specifies the format to display errors.',
- allowed: ['default', 'machine'],
+ allowed: ['default', 'json', 'machine'],
allowedHelp: {
'default':
'The default output format. This format is intended to be user '
'consumable.\nThe format is not specified and can change '
'between releases.',
+ 'json': 'A machine readable output in a JSON format.',
'machine': 'A machine readable output. The format is:\n\n'
'SEVERITY|TYPE|ERROR_CODE|FILE_PATH|LINE|COLUMN|LENGTH|ERROR_MESSAGE\n\n'
'Note that the pipe character is escaped with backslashes for '
@@ -93,6 +95,7 @@
final List<AnalysisError> errors = <AnalysisError>[];
final machineFormat = argResults['format'] == 'machine';
+ final jsonFormat = argResults['format'] == 'json';
var progress = machineFormat
? null
@@ -138,6 +141,8 @@
if (machineFormat) {
emitMachineFormat(log, errors);
+ } else if (jsonFormat) {
+ emitJsonFormat(log, errors);
} else {
emitDefaultFormat(log, errors,
relativeToDir: relativeToDir, verbose: verbose);
@@ -234,6 +239,67 @@
log.stdout('$errorCount ${pluralize('issue', errorCount)} found.');
}
+ @visibleForTesting
+ static void emitJsonFormat(Logger log, List<AnalysisError> errors) {
+ Map<String, dynamic> location(
+ String filePath, Map<String, dynamic> range) =>
+ {
+ 'file': filePath,
+ 'range': range,
+ };
+
+ Map<String, dynamic> position(int offset, int line, int column) => {
+ 'offset': offset,
+ 'line': line,
+ 'column': column,
+ };
+
+ Map<String, dynamic> range(
+ Map<String, dynamic> start, Map<String, dynamic> end) =>
+ {
+ 'start': start,
+ 'end': end,
+ };
+
+ var diagnostics = <Map<String, dynamic>>[];
+ for (final AnalysisError error in errors) {
+ var contextMessages = [];
+ for (var contextMessage in error.contextMessages) {
+ var startOffset = contextMessage.offset;
+ contextMessages.add({
+ 'location': location(
+ contextMessage.filePath,
+ range(
+ position(
+ startOffset, contextMessage.line, contextMessage.column),
+ position(startOffset + contextMessage.length,
+ contextMessage.endLine, contextMessage.endColumn))),
+ 'message': contextMessage.message,
+ });
+ }
+ var startOffset = error.offset;
+ diagnostics.add({
+ 'code': error.code,
+ 'severity': error.severity,
+ 'type': error.type,
+ 'location': location(
+ error.file,
+ range(
+ position(startOffset, error.startLine, error.startColumn),
+ position(startOffset + error.length, error.endLine,
+ error.endColumn))),
+ 'problemMessage': error.message,
+ if (error.correction != null) 'correctionMessage': error.correction,
+ if (contextMessages.isNotEmpty) 'contextMessages': contextMessages,
+ if (error.url != null) 'documentation': error.url,
+ });
+ }
+ log.stdout(json.encode({
+ 'version': 1,
+ 'diagnostics': diagnostics,
+ }));
+ }
+
/// Return a relative path if it is a shorter reference than the given dir.
static String _relativePath(String givenPath, io.Directory fromDir) {
String fromPath = fromDir?.absolute?.resolveSymbolicLinksSync();
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 8a23601..223962f 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -234,6 +234,8 @@
'type': 'TODO',
'code': 'dead_code',
'location': {
+ 'endLine': 16,
+ 'endColumn': 12,
'file': 'lib/test.dart',
'offset': 362,
'length': 72,
@@ -243,6 +245,37 @@
'message': 'Foo bar baz.',
'hasFix': false,
};
+ final fullDiagnosticJson = {
+ 'severity': 'ERROR',
+ 'type': 'COMPILE_TIME_ERROR',
+ 'location': {
+ 'file': 'lib/test.dart',
+ 'offset': 19,
+ 'length': 1,
+ 'startLine': 2,
+ 'startColumn': 9
+ },
+ 'message':
+ "Local variable 's' can't be referenced before it is declared.",
+ 'correction':
+ "Try moving the declaration to before the first use, or renaming the local variable so that it doesn't hide a name from an enclosing scope.",
+ 'code': 'referenced_before_declaration',
+ 'url':
+ 'https:://dart.dev/tools/diagnostic-messages#referenced_before_declaration',
+ 'contextMessages': [
+ {
+ 'message': "The declaration of 's' is on line 3.",
+ 'location': {
+ 'file': 'lib/test.dart',
+ 'offset': 29,
+ 'length': 1,
+ 'startLine': 3,
+ 'startColumn': 7
+ }
+ }
+ ],
+ 'hasFix': false
+ };
test('default', () {
final logger = TestLogger(false);
@@ -258,6 +291,49 @@
expect(stdout, contains('dead_code'));
});
+ group('json', () {
+ test('short', () {
+ final logger = TestLogger(false);
+ final errors = [AnalysisError(sampleInfoJson)];
+
+ AnalyzeCommand.emitJsonFormat(logger, errors);
+
+ expect(logger.stderrBuffer, isEmpty);
+ final stdout = logger.stdoutBuffer.toString().trim();
+ expect(
+ stdout,
+ '{"version":1,"diagnostics":[{"code":"dead_code","severity":"INFO",'
+ '"type":"TODO","location":{"file":"lib/test.dart","range":{'
+ '"start":{"offset":362,"line":15,"column":4},"end":{"offset":434,'
+ '"line":16,"column":12}}},"problemMessage":"Foo bar baz."}]}');
+ });
+ test('full', () {
+ final logger = TestLogger(false);
+ final errors = [AnalysisError(fullDiagnosticJson)];
+
+ AnalyzeCommand.emitJsonFormat(logger, errors);
+
+ expect(logger.stderrBuffer, isEmpty);
+ final stdout = logger.stdoutBuffer.toString().trim();
+ expect(
+ stdout,
+ '{"version":1,"diagnostics":[{'
+ '"code":"referenced_before_declaration","severity":"ERROR",'
+ '"type":"COMPILE_TIME_ERROR","location":{"file":"lib/test.dart",'
+ '"range":{"start":{"offset":19,"line":2,"column":9},"end":{'
+ '"offset":20,"line":null,"column":null}}},"problemMessage":'
+ '"Local variable \'s\' can\'t be referenced before it is declared.",'
+ '"correctionMessage":"Try moving the declaration to before the'
+ ' first use, or renaming the local variable so that it doesn\'t hide'
+ ' a name from an enclosing scope.","contextMessages":[{"location":{'
+ '"file":"lib/test.dart","range":{"start":{"offset":29,"line":3,'
+ '"column":7},"end":{"offset":30,"line":null,"column":null}}},'
+ '"message":"The declaration of \'s\' is on line 3."}],'
+ '"documentation":'
+ '"https:://dart.dev/tools/diagnostic-messages#referenced_before_declaration"}]}');
+ });
+ });
+
test('machine', () {
final logger = TestLogger(false);
final errors = [AnalysisError(sampleInfoJson)];
diff --git a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
index c2abb01..bd23544 100644
--- a/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_retaining_path_rpc_test.dart
@@ -10,9 +10,9 @@
class _TestClass {
_TestClass();
// Make sure these fields are not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
dynamic x;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
dynamic y;
}
@@ -22,6 +22,7 @@
dynamic target4 = new _TestClass();
dynamic target5 = new _TestClass();
dynamic target6 = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
Expando<_TestClass> expando = Expando<_TestClass>();
@pragma("vm:entry-point") // Prevent obfuscation
dynamic globalObject = new _TestClass();
@@ -40,45 +41,45 @@
globalMap2[target5] = 'value';
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getGlobalObject() => globalObject;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget1() {
var tmp = target1;
target1 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget2() {
var tmp = target2;
target2 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget3() {
var tmp = target3;
target3 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget4() {
var tmp = target4;
target4 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget5() {
var tmp = target5;
target5 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeExpandoTarget() {
var tmp = target6;
target6 = null;
@@ -87,7 +88,7 @@
return tmp2;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getTrue() => true;
invoke(Isolate isolate, String selector) async {
@@ -216,7 +217,6 @@
expect(result['elements'][1]['parentMapKey']['class']['name'],
equals('_TestClass'));
expect(result['elements'][2]['parentListIndex'], isNotNull);
- expect(result['elements'][3]['value']['class']['name'], 'Expando');
expect(result['elements'][4]['value']['name'], 'expando');
},
diff --git a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
index 535c882..4273caf 100644
--- a/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_user_level_retaining_path_rpc_test.dart
@@ -13,13 +13,16 @@
var y;
}
+@pragma("vm:entry-point") // Prevent obfuscation
class _TestConst {
const _TestConst();
}
_TopLevelClosure() {}
+@pragma("vm:entry-point") // Prevent obfuscation
var x;
+@pragma("vm:entry-point") // Prevent obfuscation
var fn;
void warmup() {
@@ -27,10 +30,10 @@
fn = _TopLevelClosure;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getX() => x;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getFn() => fn;
invoke(Isolate isolate, String selector) async {
diff --git a/runtime/observatory/tests/service/inbound_references_test.dart b/runtime/observatory/tests/service/inbound_references_test.dart
index 548aa7e..9e781c0 100644
--- a/runtime/observatory/tests/service/inbound_references_test.dart
+++ b/runtime/observatory/tests/service/inbound_references_test.dart
@@ -8,14 +8,16 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
class Node {
// Make sure this field is not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var edge;
}
class Edge {}
+@pragma("vm:entry-point") // Prevent obfuscation
var n, e, array;
void script() {
diff --git a/runtime/observatory/tests/service/object_graph_identity_hash_test.dart b/runtime/observatory/tests/service/object_graph_identity_hash_test.dart
index eb7c109..66a029f 100644
--- a/runtime/observatory/tests/service/object_graph_identity_hash_test.dart
+++ b/runtime/observatory/tests/service/object_graph_identity_hash_test.dart
@@ -8,40 +8,42 @@
import 'service_test_common.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
class Foo {}
+@pragma("vm:entry-point") // Prevent obfuscation
class Bar {}
class Container1 {
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Foo foo = Foo();
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Bar bar = Bar();
}
class Container2 {
Container2(this.foo);
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Foo foo;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Bar bar = Bar();
}
class Container3 {
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
int number = 42;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
double doub = 3.14;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
String foo = 'foobar';
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
bool bar = false;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
late Map baz;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
late List list;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
late List unmodifiableList;
Container3() {
@@ -53,11 +55,11 @@
}
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
late Container1 c1;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
late Container2 c2;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
late Container3 c3;
void script() {
diff --git a/runtime/observatory/tests/service/reachable_size_test.dart b/runtime/observatory/tests/service/reachable_size_test.dart
index 8cf9f9e..db0a3ee 100644
--- a/runtime/observatory/tests/service/reachable_size_test.dart
+++ b/runtime/observatory/tests/service/reachable_size_test.dart
@@ -10,13 +10,15 @@
class Pair {
// Make sure these fields are not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
dynamic x;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
dynamic y;
}
+@pragma("vm:entry-point") // Prevent obfuscation
dynamic p1;
+@pragma("vm:entry-point") // Prevent obfuscation
dynamic p2;
buildGraph() {
diff --git a/runtime/observatory/tests/service/string_escaping_test.dart b/runtime/observatory/tests/service/string_escaping_test.dart
index d306186..866178c6 100644
--- a/runtime/observatory/tests/service/string_escaping_test.dart
+++ b/runtime/observatory/tests/service/string_escaping_test.dart
@@ -9,20 +9,35 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
var ascii;
+@pragma("vm:entry-point") // Prevent obfuscation
var latin1;
+@pragma("vm:entry-point") // Prevent obfuscation
var unicode;
+@pragma("vm:entry-point") // Prevent obfuscation
var hebrew;
+@pragma("vm:entry-point") // Prevent obfuscation
var singleQuotes;
+@pragma("vm:entry-point") // Prevent obfuscation
var doubleQuotes;
+@pragma("vm:entry-point") // Prevent obfuscation
var newLines;
+@pragma("vm:entry-point") // Prevent obfuscation
var tabs;
+@pragma("vm:entry-point") // Prevent obfuscation
var suggrogatePairs;
+@pragma("vm:entry-point") // Prevent obfuscation
var nullInTheMiddle;
+@pragma("vm:entry-point") // Prevent obfuscation
var escapedUnicodeEscape;
+@pragma("vm:entry-point") // Prevent obfuscation
var longStringEven;
+@pragma("vm:entry-point") // Prevent obfuscation
var longStringOdd;
+@pragma("vm:entry-point") // Prevent obfuscation
var malformedWithLeadSurrogate;
+@pragma("vm:entry-point") // Prevent obfuscation
var malformedWithTrailSurrogate;
void script() {
diff --git a/runtime/observatory/tests/service/typed_data_test.dart b/runtime/observatory/tests/service/typed_data_test.dart
index 9c4ba3b..d37a79f 100644
--- a/runtime/observatory/tests/service/typed_data_test.dart
+++ b/runtime/observatory/tests/service/typed_data_test.dart
@@ -9,25 +9,42 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
var int8List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int16List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint8List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint16List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint8ClampedList;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32x4;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32x4;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64x2;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32x4List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32x4List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64x2List;
void script() {
diff --git a/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart
index 43f02e5..56ccd2e 100644
--- a/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_retaining_path_rpc_test.dart
@@ -10,9 +10,9 @@
class _TestClass {
_TestClass();
// Make sure these fields are not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var x;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var y;
}
@@ -22,6 +22,7 @@
var target4 = new _TestClass();
var target5 = new _TestClass();
var target6 = new _TestClass();
+@pragma("vm:entry-point") // Prevent obfuscation
Expando<_TestClass> expando = Expando<_TestClass>();
@pragma("vm:entry-point") // Prevent obfuscation
var globalObject = new _TestClass();
@@ -40,45 +41,45 @@
globalMap2[target5] = 'value';
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getGlobalObject() => globalObject;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget1() {
var tmp = target1;
target1 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget2() {
var tmp = target2;
target2 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget3() {
var tmp = target3;
target3 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget4() {
var tmp = target4;
target4 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeTarget5() {
var tmp = target5;
target5 = null;
return tmp;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
takeExpandoTarget() {
var tmp = target6;
target6 = null;
@@ -87,7 +88,7 @@
return tmp2;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getTrue() => true;
invoke(Isolate isolate, String selector) async {
@@ -216,7 +217,6 @@
expect(result['elements'][1]['parentMapKey']['class']['name'],
equals('_TestClass'));
expect(result['elements'][2]['parentListIndex'], isNotNull);
- expect(result['elements'][3]['value']['class']['name'], 'Expando');
expect(result['elements'][4]['value']['name'], 'expando');
},
diff --git a/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart
index 45c8941..e88a74c 100644
--- a/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_user_level_retaining_path_rpc_test.dart
@@ -13,13 +13,16 @@
var y;
}
+@pragma("vm:entry-point") // Prevent obfuscation
class _TestConst {
const _TestConst();
}
_TopLevelClosure() {}
+@pragma("vm:entry-point") // Prevent obfuscation
var x;
+@pragma("vm:entry-point") // Prevent obfuscation
var fn;
void warmup() {
@@ -27,10 +30,10 @@
fn = _TopLevelClosure;
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getX() => x;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
getFn() => fn;
invoke(Isolate isolate, String selector) async {
diff --git a/runtime/observatory_2/tests/service_2/inbound_references_test.dart b/runtime/observatory_2/tests/service_2/inbound_references_test.dart
index 6053042..40601e2 100644
--- a/runtime/observatory_2/tests/service_2/inbound_references_test.dart
+++ b/runtime/observatory_2/tests/service_2/inbound_references_test.dart
@@ -8,14 +8,16 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
class Node {
// Make sure this field is not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var edge;
}
class Edge {}
+@pragma("vm:entry-point") // Prevent obfuscation
var n, e, array;
void script() {
diff --git a/runtime/observatory_2/tests/service_2/object_graph_identity_hash_test.dart b/runtime/observatory_2/tests/service_2/object_graph_identity_hash_test.dart
index ed5c71b..3ab0ae1 100644
--- a/runtime/observatory_2/tests/service_2/object_graph_identity_hash_test.dart
+++ b/runtime/observatory_2/tests/service_2/object_graph_identity_hash_test.dart
@@ -8,40 +8,42 @@
import 'service_test_common.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
class Foo {}
+@pragma("vm:entry-point") // Prevent obfuscation
class Bar {}
class Container1 {
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Foo foo = Foo();
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Bar bar = Bar();
}
class Container2 {
Container2(this.foo);
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Foo foo;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Bar bar = Bar();
}
class Container3 {
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
int number = 42;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
double doub = 3.14;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
String foo = 'foobar';
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
bool bar = false;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
Map baz;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
List list;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
List unmodifiableList;
Container3() {
@@ -53,11 +55,11 @@
}
}
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
Container1 c1;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
Container2 c2;
-@pragma("vm:entry-point")
+@pragma("vm:entry-point") // Prevent obfuscation
Container3 c3;
void script() {
diff --git a/runtime/observatory_2/tests/service_2/reachable_size_test.dart b/runtime/observatory_2/tests/service_2/reachable_size_test.dart
index f92962d..5073e9c 100644
--- a/runtime/observatory_2/tests/service_2/reachable_size_test.dart
+++ b/runtime/observatory_2/tests/service_2/reachable_size_test.dart
@@ -10,13 +10,15 @@
class Pair {
// Make sure these fields are not removed by the tree shaker.
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var x;
- @pragma("vm:entry-point")
+ @pragma("vm:entry-point") // Prevent obfuscation
var y;
}
+@pragma("vm:entry-point") // Prevent obfuscation
var p1;
+@pragma("vm:entry-point") // Prevent obfuscation
var p2;
buildGraph() {
diff --git a/runtime/observatory_2/tests/service_2/string_escaping_test.dart b/runtime/observatory_2/tests/service_2/string_escaping_test.dart
index 6a9c829..4d48a3f 100644
--- a/runtime/observatory_2/tests/service_2/string_escaping_test.dart
+++ b/runtime/observatory_2/tests/service_2/string_escaping_test.dart
@@ -9,20 +9,35 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
var ascii;
+@pragma("vm:entry-point") // Prevent obfuscation
var latin1;
+@pragma("vm:entry-point") // Prevent obfuscation
var unicode;
+@pragma("vm:entry-point") // Prevent obfuscation
var hebrew;
+@pragma("vm:entry-point") // Prevent obfuscation
var singleQuotes;
+@pragma("vm:entry-point") // Prevent obfuscation
var doubleQuotes;
+@pragma("vm:entry-point") // Prevent obfuscation
var newLines;
+@pragma("vm:entry-point") // Prevent obfuscation
var tabs;
+@pragma("vm:entry-point") // Prevent obfuscation
var suggrogatePairs;
+@pragma("vm:entry-point") // Prevent obfuscation
var nullInTheMiddle;
+@pragma("vm:entry-point") // Prevent obfuscation
var escapedUnicodeEscape;
+@pragma("vm:entry-point") // Prevent obfuscation
var longStringEven;
+@pragma("vm:entry-point") // Prevent obfuscation
var longStringOdd;
+@pragma("vm:entry-point") // Prevent obfuscation
var malformedWithLeadSurrogate;
+@pragma("vm:entry-point") // Prevent obfuscation
var malformedWithTrailSurrogate;
void script() {
diff --git a/runtime/observatory_2/tests/service_2/typed_data_test.dart b/runtime/observatory_2/tests/service_2/typed_data_test.dart
index 3de01a8..e6ae724 100644
--- a/runtime/observatory_2/tests/service_2/typed_data_test.dart
+++ b/runtime/observatory_2/tests/service_2/typed_data_test.dart
@@ -9,25 +9,42 @@
import 'package:test/test.dart';
import 'test_helper.dart';
+@pragma("vm:entry-point") // Prevent obfuscation
var int8List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int16List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint8List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint16List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var uint8ClampedList;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64List;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32x4;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32x4;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64x2;
+@pragma("vm:entry-point") // Prevent obfuscation
var int32x4List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float32x4List;
+@pragma("vm:entry-point") // Prevent obfuscation
var float64x2List;
void script() {
diff --git a/runtime/tests/vm/dart/deopt/assert_subtype_test.dart b/runtime/tests/vm/dart/deopt/assert_subtype_test.dart
new file mode 100644
index 0000000..964aa7d
--- /dev/null
+++ b/runtime/tests/vm/dart/deopt/assert_subtype_test.dart
@@ -0,0 +1,18 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=SubtypeCheck
+
+main() {
+ for (int i = 0; i < 20; ++i) {
+ if (foo()<int>() != 42) throw 'a';
+ if (foo()<double>() != 42) throw 'a';
+ }
+}
+
+@pragma('vm:never-inline')
+dynamic foo() {
+ int bar<T extends num>() => 42;
+ return bar;
+}
diff --git a/runtime/tests/vm/dart/deopt/instantiate_type_arguments_test.dart b/runtime/tests/vm/dart/deopt/instantiate_type_arguments_test.dart
new file mode 100644
index 0000000..4426064
--- /dev/null
+++ b/runtime/tests/vm/dart/deopt/instantiate_type_arguments_test.dart
@@ -0,0 +1,18 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=InstantiateTypeArguments
+
+main() {
+ final a = A<int>();
+ for (int i = 0; i < 20; ++i) {
+ final m = a.foo<double>();
+ if (m is! Map<int, double>) throw 'a';
+ }
+}
+
+class A<T> {
+ @pragma('vm:never-inline')
+ Map foo<H>() => <T, H>{};
+}
diff --git a/runtime/tests/vm/dart/deopt/instantiate_type_test.dart b/runtime/tests/vm/dart/deopt/instantiate_type_test.dart
new file mode 100644
index 0000000..2ae579e
--- /dev/null
+++ b/runtime/tests/vm/dart/deopt/instantiate_type_test.dart
@@ -0,0 +1,26 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=InstantiateType
+
+main() {
+ final a = A<int>();
+ for (int i = 0; i < 20; ++i) {
+ final l = a.foo<double>();
+ if (l[0] != int) throw 'a';
+ if (l[1] != double) throw 'a';
+ }
+}
+
+class A<T> {
+ @pragma('vm:never-inline')
+ List<Type> foo<H>() {
+ final l = <Type>[];
+ for (int i = 0; i < 10; ++i) {
+ l.add(T);
+ l.add(H);
+ }
+ return l;
+ }
+}
diff --git a/runtime/tests/vm/dart_2/deopt/assert_subtype_test.dart b/runtime/tests/vm/dart_2/deopt/assert_subtype_test.dart
new file mode 100644
index 0000000..964aa7d
--- /dev/null
+++ b/runtime/tests/vm/dart_2/deopt/assert_subtype_test.dart
@@ -0,0 +1,18 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=SubtypeCheck
+
+main() {
+ for (int i = 0; i < 20; ++i) {
+ if (foo()<int>() != 42) throw 'a';
+ if (foo()<double>() != 42) throw 'a';
+ }
+}
+
+@pragma('vm:never-inline')
+dynamic foo() {
+ int bar<T extends num>() => 42;
+ return bar;
+}
diff --git a/runtime/tests/vm/dart_2/deopt/instantiate_type_arguments_test.dart b/runtime/tests/vm/dart_2/deopt/instantiate_type_arguments_test.dart
new file mode 100644
index 0000000..4426064
--- /dev/null
+++ b/runtime/tests/vm/dart_2/deopt/instantiate_type_arguments_test.dart
@@ -0,0 +1,18 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=InstantiateTypeArguments
+
+main() {
+ final a = A<int>();
+ for (int i = 0; i < 20; ++i) {
+ final m = a.foo<double>();
+ if (m is! Map<int, double>) throw 'a';
+ }
+}
+
+class A<T> {
+ @pragma('vm:never-inline')
+ Map foo<H>() => <T, H>{};
+}
diff --git a/runtime/tests/vm/dart_2/deopt/instantiate_type_test.dart b/runtime/tests/vm/dart_2/deopt/instantiate_type_test.dart
new file mode 100644
index 0000000..2ae579e
--- /dev/null
+++ b/runtime/tests/vm/dart_2/deopt/instantiate_type_test.dart
@@ -0,0 +1,26 @@
+// 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=--disable-dart-dev --deoptimize-on-runtime-call-every=1 --deterministic --optimization-counter-threshold=1 --deoptimize-on-runtime-call-name-filter=InstantiateType
+
+main() {
+ final a = A<int>();
+ for (int i = 0; i < 20; ++i) {
+ final l = a.foo<double>();
+ if (l[0] != int) throw 'a';
+ if (l[1] != double) throw 'a';
+ }
+}
+
+class A<T> {
+ @pragma('vm:never-inline')
+ List<Type> foo<H>() {
+ final l = <Type>[];
+ for (int i = 0; i < 10; ++i) {
+ l.add(T);
+ l.add(H);
+ }
+ return l;
+ }
+}
diff --git a/runtime/vm/compiler/backend/block_builder.h b/runtime/vm/compiler/backend/block_builder.h
index ef51d8d..9c4dc68 100644
--- a/runtime/vm/compiler/backend/block_builder.h
+++ b/runtime/vm/compiler/backend/block_builder.h
@@ -25,7 +25,7 @@
entry_(entry),
current_(entry),
dummy_env_(
- new Environment(0, 0, flow_graph->parsed_function(), nullptr)) {
+ new Environment(0, 0, 0, flow_graph->parsed_function(), nullptr)) {
// Some graph transformations use environments from block entries.
entry->SetEnvironment(dummy_env_);
}
@@ -50,6 +50,7 @@
template <typename T>
T* AddInstruction(T* instr) {
if (instr->ComputeCanDeoptimize() ||
+ instr->ComputeCanDeoptimizeAfterCall() ||
instr->CanBecomeDeoptimizationTarget()) {
// All instructions that can deoptimize must have an environment attached
// to them.
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 639a7cd..2f14395 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -1304,24 +1304,9 @@
void FlowGraph::AttachEnvironment(Instruction* instr,
GrowableArray<Definition*>* env) {
- Environment* deopt_env =
- Environment::From(zone(), *env, num_direct_parameters_, parsed_function_);
- if (instr->IsClosureCall() || instr->IsLoadField()) {
- // Trim extra inputs of ClosureCall and LoadField instructions from
- // the environment. Inputs of those instructions are not pushed onto
- // the stack at the point where deoptimization can occur.
- // Note that in case of LoadField there can be two possible situations,
- // the code here handles LoadField to LoadField lazy deoptimization in
- // which we are transitioning from position after the call to initialization
- // stub in optimized code to a similar position after the call to
- // initialization stub in unoptimized code. There is another variant
- // (LoadField deoptimizing into a position after a getter call) which is
- // handled in a different way (see
- // CallSpecializer::InlineImplicitInstanceGetter).
- deopt_env =
- deopt_env->DeepCopy(zone(), deopt_env->Length() - instr->InputCount() +
- instr->ArgumentCount());
- }
+ auto deopt_env = Environment::From(zone(), *env, num_direct_parameters_,
+ instr->NumberOfInputsConsumedBeforeCall(),
+ parsed_function_);
instr->SetEnvironment(deopt_env);
for (Environment::DeepIterator it(deopt_env); !it.Done(); it.Advance()) {
Value* use = it.CurrentValue();
@@ -2308,6 +2293,7 @@
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
Instruction* current = it.Current();
if (!current->ComputeCanDeoptimize() &&
+ !current->ComputeCanDeoptimizeAfterCall() &&
(!current->MayThrow() || !current->GetBlock()->InsideTryBlock())) {
// Instructions that can throw need an environment for optimized
// try-catch.
diff --git a/runtime/vm/compiler/backend/flow_graph_checker.cc b/runtime/vm/compiler/backend/flow_graph_checker.cc
index 0943943..162fdd3 100644
--- a/runtime/vm/compiler/backend/flow_graph_checker.cc
+++ b/runtime/vm/compiler/backend/flow_graph_checker.cc
@@ -118,8 +118,11 @@
// correspond directly with the arguments.
const intptr_t env_count = env->Length();
const intptr_t arg_count = call->ArgumentCount();
- ASSERT(arg_count <= env_count);
- const intptr_t env_base = env_count - arg_count;
+ // Some calls (e.g. closure calls) have more inputs than actual arguments.
+ // Those extra inputs will be consumed from the stack before the call.
+ const intptr_t after_args_input_count = call->env()->LazyDeoptPruneCount();
+ ASSERT((arg_count + after_args_input_count) <= env_count);
+ const intptr_t env_base = env_count - arg_count - after_args_input_count;
for (intptr_t i = 0; i < arg_count; i++) {
if (call->HasPushArguments()) {
ASSERT(call->ArgumentAt(i) == env->ValueAt(env_base + i)
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index ba7725c..0a08391 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -910,6 +910,9 @@
if (env == nullptr) {
env = pending_deoptimization_env_;
}
+ if (env != nullptr) {
+ env = env->GetLazyDeoptEnv(zone());
+ }
CompilerDeoptInfo* info =
new (zone()) CompilerDeoptInfo(deopt_id, ICData::kDeoptAtCall,
0, // No flags.
@@ -1108,7 +1111,8 @@
return nullptr;
}
- Environment* slow_path_env = env->DeepCopy(zone());
+ Environment* slow_path_env =
+ env->DeepCopy(zone(), env->Length() - env->LazyDeoptPruneCount());
// 1. Iterate the registers in the order they will be spilled to compute
// the slots they will be spilled to.
intptr_t next_slot = StackSize() + slow_path_env->CountArgsPushed();
@@ -2864,8 +2868,7 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs,
- bool was_licm_hoisted) {
+ LocationSummary* locs) {
ASSERT(!source.token_pos.IsClassifying());
ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
@@ -2896,8 +2899,7 @@
}
}
- GenerateTTSCall(source, deopt_id, type_reg, dst_type, dst_name, locs,
- was_licm_hoisted);
+ GenerateTTSCall(source, deopt_id, type_reg, dst_type, dst_name, locs);
__ Bind(&done);
}
@@ -2909,8 +2911,7 @@
Register reg_with_type,
const AbstractType& dst_type,
const String& dst_name,
- LocationSummary* locs,
- bool was_licm_hoisted) {
+ LocationSummary* locs) {
ASSERT(!dst_name.IsNull());
// We use 2 consecutive entries in the pool for the subtype cache and the
// destination name. The second entry, namely [dst_name] seems to be unused,
@@ -2938,21 +2939,7 @@
GenerateIndirectTTSCall(assembler(), reg_with_type, sub_type_cache_index);
}
- // Lazy deopt to after the call should not have the inputs to AssertAssignable
- // because those are poped before doing the call.
- auto pruned_env = pending_deoptimization_env_;
- if (pruned_env != nullptr) {
- // If the AssertAssignable was licm hoisted, a lazy-deopt will not continue
- // after the TTS call inside the assert assignable. Rather the lazy-deopt
- // will continue at the instruction it was hoisted above (e.g. continue at a
- // Goto branch) and will later on re-do the AssertAssignable (again).
- if (!was_licm_hoisted) {
- pruned_env = pruned_env->DeepCopy(
- zone(), pruned_env->Length() - AssertAssignableInstr::kNumInputs);
- }
- }
- EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs,
- pruned_env);
+ EmitCallsiteMetadata(source, deopt_id, UntaggedPcDescriptors::kOther, locs);
}
// Optimize assignable type check by adding inlined tests for:
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index b77578b..e05c568 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -591,8 +591,7 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs,
- bool was_licm_hoisted);
+ LocationSummary* locs);
#if !defined(TARGET_ARCH_IA32)
void GenerateCallerChecksForAssertAssignable(CompileType* receiver_type,
@@ -604,8 +603,7 @@
Register reg_with_type,
const AbstractType& dst_type,
const String& dst_name,
- LocationSummary* locs,
- bool was_licm_hoisted);
+ LocationSummary* locs);
static void GenerateIndirectTTSCall(compiler::Assembler* assembler,
Register reg_with_type,
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
index c4d084f..f3acbb0 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc
@@ -143,7 +143,7 @@
// For any outer environment the deopt id is that of the call instruction
// which is recorded in the outer environment.
builder->AddReturnAddress(current->function(),
- DeoptId::ToDeoptAfter(current->deopt_id()),
+ DeoptId::ToDeoptAfter(current->GetDeoptId()),
slot_ix++);
// The values of outgoing arguments can be changed from the inlined call so
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
index d7bf55f..0f32cf2 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc
@@ -137,7 +137,7 @@
// For any outer environment the deopt id is that of the call instruction
// which is recorded in the outer environment.
builder->AddReturnAddress(current->function(),
- DeoptId::ToDeoptAfter(current->deopt_id()),
+ DeoptId::ToDeoptAfter(current->GetDeoptId()),
slot_ix++);
// The values of outgoing arguments can be changed from the inlined call so
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
index 025ac3b..5984b26 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc
@@ -113,7 +113,7 @@
// For any outer environment the deopt id is that of the call instruction
// which is recorded in the outer environment.
builder->AddReturnAddress(current->function(),
- DeoptId::ToDeoptAfter(current->deopt_id()),
+ DeoptId::ToDeoptAfter(current->GetDeoptId()),
slot_ix++);
// The values of outgoing arguments can be changed from the inlined call so
@@ -265,8 +265,7 @@
const InstructionSource& source,
intptr_t deopt_id,
const String& dst_name,
- LocationSummary* locs,
- bool was_licm_hoisted) {
+ LocationSummary* locs) {
ASSERT(!source.token_pos.IsClassifying());
ASSERT(CheckAssertAssignableTypeTestingABILocations(*locs));
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
index d5d9020..e0b9329 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc
@@ -138,7 +138,7 @@
// For any outer environment the deopt id is that of the call instruction
// which is recorded in the outer environment.
builder->AddReturnAddress(current->function(),
- DeoptId::ToDeoptAfter(current->deopt_id()),
+ DeoptId::ToDeoptAfter(current->GetDeoptId()),
slot_ix++);
// The values of outgoing arguments can be changed from the inlined call so
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 4310548..9dc20b5 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -1580,11 +1580,15 @@
}
void Instruction::RepairPushArgsInEnvironment() const {
+ // Some calls (e.g. closure calls) have more inputs than actual arguments.
+ // Those extra inputs will be consumed from the stack before the call.
+ const intptr_t after_args_input_count = env()->LazyDeoptPruneCount();
PushArgumentsArray* push_arguments = GetPushArguments();
ASSERT(push_arguments != nullptr);
const intptr_t arg_count = ArgumentCount();
- ASSERT(arg_count <= env()->Length());
- const intptr_t env_base = env()->Length() - arg_count;
+ ASSERT((arg_count + after_args_input_count) <= env()->Length());
+ const intptr_t env_base =
+ env()->Length() - arg_count - after_args_input_count;
for (intptr_t i = 0; i < arg_count; ++i) {
env()->ValueAt(env_base + i)->BindToEnvironment(push_arguments->At(i));
}
@@ -4429,9 +4433,6 @@
UNREACHABLE();
}
- // Instruction inputs are popped from the stack at this point,
- // so deoptimization environment has to be adjusted.
- // This adjustment is done in FlowGraph::AttachEnvironment.
compiler->GenerateStubCall(source(), stub,
/*kind=*/UntaggedPcDescriptors::kOther, locs(),
deopt_id());
@@ -5379,7 +5380,7 @@
void AssertAssignableInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateAssertAssignable(value()->Type(), source(), deopt_id(),
- dst_name(), locs(), licm_hoisted());
+ dst_name(), locs());
ASSERT(locs()->in(kInstancePos).reg() == locs()->out(0).reg());
}
@@ -5406,7 +5407,7 @@
void AssertSubtypeInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
compiler->GenerateStubCall(source(), StubCode::AssertSubtype(),
- UntaggedPcDescriptors::kOther, locs());
+ UntaggedPcDescriptors::kOther, locs(), deopt_id());
}
LocationSummary* InstantiateTypeInstr::MakeLocationSummary(Zone* zone,
@@ -5707,9 +5708,11 @@
Environment* Environment::From(Zone* zone,
const GrowableArray<Definition*>& definitions,
intptr_t fixed_parameter_count,
+ intptr_t lazy_deopt_pruning_count,
const ParsedFunction& parsed_function) {
- Environment* env = new (zone) Environment(
- definitions.length(), fixed_parameter_count, parsed_function, NULL);
+ Environment* env =
+ new (zone) Environment(definitions.length(), fixed_parameter_count,
+ lazy_deopt_pruning_count, parsed_function, NULL);
for (intptr_t i = 0; i < definitions.length(); ++i) {
env->values_.Add(new (zone) Value(definitions[i]));
}
@@ -5722,10 +5725,10 @@
Environment* Environment::DeepCopy(Zone* zone, intptr_t length) const {
ASSERT(length <= values_.length());
- Environment* copy =
- new (zone) Environment(length, fixed_parameter_count_, parsed_function_,
- (outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
- copy->deopt_id_ = this->deopt_id_;
+ Environment* copy = new (zone) Environment(
+ length, fixed_parameter_count_, LazyDeoptPruneCount(), parsed_function_,
+ (outer_ == NULL) ? NULL : outer_->DeepCopy(zone));
+ copy->SetDeoptId(DeoptIdBits::decode(bitfield_));
if (locations_ != NULL) {
Location* new_locations = zone->Alloc<Location>(length);
copy->set_locations(new_locations);
@@ -5762,7 +5765,9 @@
it.CurrentValue()->RemoveFromUseList();
}
- Environment* copy = DeepCopy(zone, values_.length() - argc);
+ Environment* copy =
+ DeepCopy(zone, values_.length() - argc - LazyDeoptPruneCount());
+ copy->SetLazyDeoptPruneCount(0);
for (intptr_t i = 0; i < argc; i++) {
copy->values_.Add(new (zone) Value(dead));
}
@@ -5784,11 +5789,13 @@
ASSERT(this != NULL);
ASSERT(instr->env()->outer() == NULL);
intptr_t argument_count = instr->env()->fixed_parameter_count();
- Environment* copy = DeepCopy(zone, values_.length() - argument_count);
- copy->deopt_id_ = outer_deopt_id;
- instr->env()->outer_ = copy;
+ Environment* outer =
+ DeepCopy(zone, values_.length() - argument_count - LazyDeoptPruneCount());
+ outer->SetDeoptId(outer_deopt_id);
+ outer->SetLazyDeoptPruneCount(0);
+ instr->env()->outer_ = outer;
intptr_t use_index = instr->env()->Length(); // Start index after inner.
- for (Environment::DeepIterator it(copy); !it.Done(); it.Advance()) {
+ for (Environment::DeepIterator it(outer); !it.Done(); it.Advance()) {
Value* value = it.CurrentValue();
value->set_instruction(instr);
value->set_use_index(use_index++);
diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h
index b9a9ff2..4d88ca8 100644
--- a/runtime/vm/compiler/backend/il.h
+++ b/runtime/vm/compiler/backend/il.h
@@ -502,7 +502,7 @@
M(BoxInt32, _) \
M(UnboxInt32, kNoGC) \
M(BoxUint8, kNoGC) \
- M(IntConverter, _) \
+ M(IntConverter, kNoGC) \
M(BitCast, kNoGC) \
M(Deoptimize, kNoGC) \
M(SimdOp, kNoGC)
@@ -775,10 +775,6 @@
explicit Instruction(const InstructionSource& source,
intptr_t deopt_id = DeoptId::kNone)
: deopt_id_(deopt_id),
- previous_(NULL),
- next_(NULL),
- env_(NULL),
- locs_(NULL),
inlining_id_(source.inlining_id) {}
explicit Instruction(intptr_t deopt_id = DeoptId::kNone)
@@ -791,7 +787,8 @@
virtual intptr_t statistics_tag() const { return tag(); }
intptr_t deopt_id() const {
- ASSERT(ComputeCanDeoptimize() || CanBecomeDeoptimizationTarget() ||
+ ASSERT(ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall() ||
+ CanBecomeDeoptimizationTarget() ||
CompilerState::Current().is_aot());
return GetDeoptId();
}
@@ -851,9 +848,19 @@
// the type or the range of input operands during compilation.
virtual bool ComputeCanDeoptimize() const = 0;
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
+ // TODO(dartbug.com/45213): Incrementally migrate IR instructions from using
+ // [ComputeCanDeoptimze] to either [ComputeCanDeoptimizeAfterCall] if they
+ // can only lazy deoptimize.
+ return false;
+ }
+
// Once we removed the deopt environment, we assume that this
// instruction can't deoptimize.
- bool CanDeoptimize() const { return env() != NULL && ComputeCanDeoptimize(); }
+ bool CanDeoptimize() const {
+ return env() != nullptr &&
+ (ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall());
+ }
// Visiting support.
virtual void Accept(FlowGraphVisitor* visitor) = 0;
@@ -972,6 +979,8 @@
void RemoveEnvironment();
void ReplaceInEnvironment(Definition* current, Definition* replacement);
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const { return 0; }
+
// Different compiler passes can assign pass specific ids to the instruction.
// Only one id can be stored at a time.
intptr_t GetPassSpecificId(CompilerPass::Id pass) const {
@@ -1089,8 +1098,8 @@
virtual void InheritDeoptTarget(Zone* zone, Instruction* other);
bool NeedsEnvironment() const {
- return ComputeCanDeoptimize() || CanBecomeDeoptimizationTarget() ||
- MayThrow();
+ return ComputeCanDeoptimize() || ComputeCanDeoptimizeAfterCall() ||
+ CanBecomeDeoptimizationTarget() || MayThrow();
}
virtual bool CanBecomeDeoptimizationTarget() const { return false; }
@@ -1179,12 +1188,12 @@
"Pass Id does not fit into the bit field");
};
- intptr_t deopt_id_;
+ intptr_t deopt_id_ = DeoptId::kNone;
intptr_t pass_specific_id_ = PassSpecificId::kNoId;
- Instruction* previous_;
- Instruction* next_;
- Environment* env_;
- LocationSummary* locs_;
+ Instruction* previous_ = nullptr;
+ Instruction* next_ = nullptr;
+ Environment* env_ = nullptr;
+ LocationSummary* locs_ = nullptr;
intptr_t inlining_id_;
DISALLOW_COPY_AND_ASSIGN(Instruction);
@@ -3628,9 +3637,13 @@
virtual TokenPosition token_pos() const { return token_pos_; }
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return InputCount();
+ }
virtual bool CanBecomeDeoptimizationTarget() const { return true; }
@@ -3704,9 +3717,21 @@
virtual TokenPosition token_pos() const { return token_pos_; }
const String& dst_name() const { return dst_name_; }
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+#if !defined(TARGET_ARCH_IA32)
+ return InputCount();
+#else
+ // The ia32 implementation calls the stub by pushing the input registers
+ // in the same order onto the stack thereby making the deopt-env correct.
+ // (Due to lack of registers we cannot use all-argument calling convention
+ // as in other architectures.)
+ return 0;
+#endif
+ }
virtual bool CanBecomeDeoptimizationTarget() const {
// AssertAssignable instructions that are specialized by the optimizer
@@ -3718,9 +3743,6 @@
virtual bool AttributesEqual(Instruction* other) const { return true; }
- void set_licm_hoisted(bool value) { licm_hoisted_ = value; }
- bool licm_hoisted() const { return licm_hoisted_; }
-
virtual Value* RedefinedValue() const;
PRINT_OPERANDS_TO_SUPPORT
@@ -3729,7 +3751,6 @@
const TokenPosition token_pos_;
const String& dst_name_;
const Kind kind_;
- bool licm_hoisted_ = false;
DISALLOW_COPY_AND_ASSIGN(AssertAssignableInstr);
};
@@ -3749,9 +3770,13 @@
virtual TokenPosition token_pos() const { return token_pos_; }
Value* value() const { return inputs_[0]; }
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return InputCount();
+ }
virtual Definition* Canonicalize(FlowGraph* flow_graph);
@@ -3875,6 +3900,13 @@
virtual intptr_t InputCount() const { return inputs_->length(); }
virtual Value* InputAt(intptr_t i) const { return inputs_->At(i); }
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
+ return !CompilerState::Current().is_aot();
+ }
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return kExtraInputs;
+ }
intptr_t FirstArgIndex() const { return type_args_len_ > 0 ? 1 : 0; }
Value* Receiver() const { return this->ArgumentValueAt(FirstArgIndex()); }
@@ -3958,10 +3990,6 @@
// TODO(kmillikin): implement exact call counts for closure calls.
virtual intptr_t CallCount() const { return 1; }
- virtual bool ComputeCanDeoptimize() const {
- return !CompilerState::Current().is_aot();
- }
-
virtual bool HasUnknownSideEffects() const { return true; }
Code::EntryKind entry_kind() const { return entry_kind_; }
@@ -4034,10 +4062,6 @@
virtual CompileType ComputeType() const;
- virtual bool ComputeCanDeoptimize() const {
- return !CompilerState::Current().is_aot();
- }
-
virtual bool CanBecomeDeoptimizationTarget() const {
// Instance calls that are specialized by the optimizer need a
// deoptimization descriptor before the call.
@@ -4331,8 +4355,6 @@
virtual CompileType ComputeType() const;
- virtual bool ComputeCanDeoptimize() const { return false; }
-
virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual bool CanBecomeDeoptimizationTarget() const { return false; }
@@ -6546,9 +6568,13 @@
DECLARE_INSTRUCTION(LoadField)
virtual CompileType ComputeType() const;
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return calls_initializer() && !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return InputCount();
+ }
virtual bool HasUnknownSideEffects() const {
return calls_initializer() && !throw_exception_on_initialization();
@@ -6624,9 +6650,13 @@
const AbstractType& type() const { return type_; }
virtual TokenPosition token_pos() const { return token_pos_; }
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return InputCount();
+ }
virtual bool HasUnknownSideEffects() const { return false; }
@@ -6669,9 +6699,13 @@
const Function& function() const { return function_; }
virtual TokenPosition token_pos() const { return token_pos_; }
- virtual bool ComputeCanDeoptimize() const {
+ virtual bool ComputeCanDeoptimize() const { return false; }
+ virtual bool ComputeCanDeoptimizeAfterCall() const {
return !CompilerState::Current().is_aot();
}
+ virtual intptr_t NumberOfInputsConsumedBeforeCall() const {
+ return InputCount();
+ }
virtual bool HasUnknownSideEffects() const { return false; }
@@ -9413,6 +9447,7 @@
static Environment* From(Zone* zone,
const GrowableArray<Definition*>& definitions,
intptr_t fixed_parameter_count,
+ intptr_t lazy_deopt_pruning_count,
const ParsedFunction& parsed_function);
void set_locations(Location* locations) {
@@ -9423,9 +9458,19 @@
// Get deopt_id associated with this environment.
// Note that only outer environments have deopt id associated with
// them (set by DeepCopyToOuter).
- intptr_t deopt_id() const {
- ASSERT(deopt_id_ != DeoptId::kNone);
- return deopt_id_;
+ intptr_t GetDeoptId() const {
+ ASSERT(DeoptIdBits::decode(bitfield_) != DeoptId::kNone);
+ return DeoptIdBits::decode(bitfield_);
+ }
+
+ intptr_t LazyDeoptPruneCount() const {
+ return LazyDeoptPruningBits::decode(bitfield_);
+ }
+
+ Environment* GetLazyDeoptEnv(Zone* zone) {
+ const intptr_t num_args_to_prune = LazyDeoptPruneCount();
+ if (num_args_to_prune == 0) return this;
+ return DeepCopy(zone, Length() - num_args_to_prune);
}
Environment* outer() const { return outer_; }
@@ -9499,21 +9544,39 @@
friend class compiler::BlockBuilder; // For Environment constructor.
friend class FlowGraphDeserializer; // For constructor and deopt_id_.
+ class LazyDeoptPruningBits : public BitField<uintptr_t, uintptr_t, 0, 8> {};
+ class DeoptIdBits
+ : public BitField<uintptr_t,
+ intptr_t,
+ LazyDeoptPruningBits::kNextBit,
+ kBitsPerWord - LazyDeoptPruningBits::kNextBit,
+ /*sign_extend=*/true> {};
+
Environment(intptr_t length,
intptr_t fixed_parameter_count,
+ intptr_t lazy_deopt_pruning_count,
const ParsedFunction& parsed_function,
Environment* outer)
: values_(length),
fixed_parameter_count_(fixed_parameter_count),
+ bitfield_(DeoptIdBits::encode(DeoptId::kNone) |
+ LazyDeoptPruningBits::encode(lazy_deopt_pruning_count)),
parsed_function_(parsed_function),
outer_(outer) {}
+ void SetDeoptId(intptr_t deopt_id) {
+ bitfield_ = DeoptIdBits::update(deopt_id, bitfield_);
+ }
+ void SetLazyDeoptPruneCount(intptr_t value) {
+ bitfield_ = LazyDeoptPruningBits::update(value, bitfield_);
+ }
+
GrowableArray<Value*> values_;
Location* locations_ = nullptr;
const intptr_t fixed_parameter_count_;
// Deoptimization id associated with this environment. Only set for
// outer environments.
- intptr_t deopt_id_ = DeoptId::kNone;
+ uintptr_t bitfield_;
const ParsedFunction& parsed_function_;
Environment* outer_;
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 04ad892..fb3d09c 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1351,8 +1351,6 @@
USE(check);
} else if (auto check = current->AsTestCids()) {
check->set_licm_hoisted(true);
- } else if (auto check = current->AsAssertAssignable()) {
- check->set_licm_hoisted(true);
}
if (FLAG_trace_optimization) {
THR_Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
diff --git a/runtime/vm/compiler/frontend/flow_graph_builder.cc b/runtime/vm/compiler/frontend/flow_graph_builder.cc
index 5034b6d..c2ed762 100644
--- a/runtime/vm/compiler/frontend/flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/flow_graph_builder.cc
@@ -50,6 +50,10 @@
// Attach the outer environment on each instruction in the callee graph.
ASSERT(call_->env() != NULL);
ASSERT(call_->deopt_id() != DeoptId::kNone);
+
+ auto zone = callee_graph->zone();
+ auto env = call_->env();
+
const intptr_t outer_deopt_id = call_->deopt_id();
// Scale the edge weights by the call count for the inlined function.
double scale_factor = 1.0;
@@ -65,18 +69,16 @@
block->AsTargetEntry()->adjust_edge_weight(scale_factor);
}
Instruction* instr = block;
- if (block->env() != NULL) {
- call_->env()->DeepCopyToOuter(callee_graph->zone(), block,
- outer_deopt_id);
+ if (block->env() != nullptr) {
+ env->DeepCopyToOuter(zone, block, outer_deopt_id);
}
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
instr = it.Current();
// TODO(zerny): Avoid creating unnecessary environments. Note that some
// optimizations need deoptimization info for non-deoptable instructions,
// eg, LICM on GOTOs.
- if (instr->env() != NULL) {
- call_->env()->DeepCopyToOuter(callee_graph->zone(), instr,
- outer_deopt_id);
+ if (instr->env() != nullptr) {
+ env->DeepCopyToOuter(zone, instr, outer_deopt_id);
}
}
if (instr->IsGoto()) {
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 604900f..92778d3 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -3070,6 +3070,25 @@
});
}
+static void DeoptimizeLastDartFrameIfOptimized() {
+ auto thread = Thread::Current();
+ // Have to grab program_lock before stopping everybody else.
+ SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
+
+ auto isolate = thread->isolate();
+ auto isolate_group = thread->isolate_group();
+ isolate_group->RunWithStoppedMutators([&]() {
+ auto mutator_thread = isolate->mutator_thread();
+ DartFrameIterator iterator(mutator_thread,
+ StackFrameIterator::kNoCrossThreadIteration);
+ StackFrame* frame = iterator.NextFrame();
+ const auto& optimized_code = Code::Handle(frame->LookupDartCode());
+ if (optimized_code.is_optimized() && !optimized_code.is_force_optimized()) {
+ DeoptimizeAt(mutator_thread, optimized_code, frame);
+ }
+ });
+}
+
#if !defined(DART_PRECOMPILED_RUNTIME)
static const intptr_t kNumberOfSavedCpuRegisters = kNumberOfCpuRegisters;
static const intptr_t kNumberOfSavedFpuRegisters = kNumberOfFpuRegisters;
@@ -3278,13 +3297,15 @@
return;
}
if (FLAG_deoptimize_on_runtime_call_name_filter != nullptr &&
- strstr(runtime_call_name, FLAG_deoptimize_on_runtime_call_name_filter) ==
- 0) {
+ (strlen(runtime_call_name) !=
+ strlen(FLAG_deoptimize_on_runtime_call_name_filter) ||
+ strstr(runtime_call_name, FLAG_deoptimize_on_runtime_call_name_filter) ==
+ 0)) {
return;
}
const uint32_t count = thread->IncrementAndGetRuntimeCallCount();
if ((count % FLAG_deoptimize_on_runtime_call_every) == 0) {
- DeoptimizeFunctionsOnStack();
+ DeoptimizeLastDartFrameIfOptimized();
}
}
diff --git a/tests/web/regress/45413_test.dart b/tests/web/regress/45413_test.dart
new file mode 100644
index 0000000..b24c72d
--- /dev/null
+++ b/tests/web/regress/45413_test.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Regression test for dart2js stack overflow.
+
+import 'package:expect/expect.dart';
+
+void main() {
+ int x = 0;
+ do {
+ x++;
+ } while (x % 10 == 7 ? false : true);
+ Expect.equals(7, x);
+}
diff --git a/tests/web_2/regress/183227419_test.dart b/tests/web_2/regress/183227419_test.dart
new file mode 100644
index 0000000..2d94ec8
--- /dev/null
+++ b/tests/web_2/regress/183227419_test.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.10
+
+// Regression test for variable allocator live range bug.
+//
+// Pre-fix, this program would crash because t2 was reused before it was dead.
+//
+// algo$1: function(n) {
+// var t1 = this.ax,
+// t2 = t1 == null;
+// if ((t2 ? null : t1.a) == null)
+// return;
+// t2 = n + 1; // the old value of t2 is used below!
+// (t2 ? null : t1.a).parameters.addAll$2(0, t2, t2);
+// }
+//
+// Several things were necessary to tickle the bug:
+//
+// [A] The repeated test that gets shared from the two null-aware `ax?.a`
+// expressions.
+// [B] A null-aware `parameters?.` that is optimized away because `parameters`
+// is always non-null.
+// [C] A repeated expression after the optimized null-aware access.
+
+class Thing {
+ @pragma('dart2js:noInline')
+ void addAll(x, y) {}
+}
+
+class A {
+ Thing parameters = Thing();
+}
+
+class AX {
+ A a;
+ AX(this.a);
+}
+
+class Host {
+ AX ax;
+ Host(this.ax);
+
+ algo(int n) {
+ if (ax?.a == null) return;
+ ax?.a.parameters?.addAll(n + 1, n + 1);
+ }
+}
+
+main() {
+ Host(null).algo(1);
+ Host(AX(A())).algo(2);
+ Host(AX(null)).algo(3);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 57ef5d9..4db5b67 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 13
PATCH 0
-PRERELEASE 162
+PRERELEASE 163
PRERELEASE_PATCH 0
\ No newline at end of file