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