Refined scope calculation in expression compiler

- make sure nested blocks scopes are visited
- make sure variable definitions do not leak beyond block scopes
- properly collect scopes for loops, if statements, constructors
- add calculation of fileEndOffsets for blocks
- save block file offsets to dill
  - update binary format version
  - change kernel readers and writers to read and write block offsets
  - change vm readers to read and block offsets for new version
- add missing fileOffsets and fileEndOffsets on functions for
  late fields
- add missing fileOffsets and fileEndOffsets on functions for
  extensions
- add errors on failures to find scope
- find libraries for private fields correctly
- add more expression compilation tests
- add test to verify fileOffsets and fileEndOffsets are set for
  SDK summary (will add full dill tests later)

Closes: https://github.com/dart-lang/sdk/issues/40278
Related: https://github.com/dart-lang/sdk/issues/34942

Change-Id: I5bc1bb645543045b689d8d61069ee77dc4ee9025
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167541
Commit-Queue: Anna Gringauze <annagrin@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 52b7f91..98448cc 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -4608,6 +4608,58 @@
     message: r"""This is the type variable whose bound isn't conformed to.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            string)> templateIncrementalCompilerIllegalParameter = const Template<
+        Message Function(String string)>(
+    messageTemplate:
+        r"""Illegal parameter name '#string' found during expression compilation.""",
+    withArguments: _withArgumentsIncrementalCompilerIllegalParameter);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)>
+    codeIncrementalCompilerIllegalParameter =
+    const Code<Message Function(String string)>(
+  "IncrementalCompilerIllegalParameter",
+  templateIncrementalCompilerIllegalParameter,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncrementalCompilerIllegalParameter(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeIncrementalCompilerIllegalParameter,
+      message:
+          """Illegal parameter name '${string}' found during expression compilation.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string)>
+    templateIncrementalCompilerIllegalTypeParameter =
+    const Template<Message Function(String string)>(
+        messageTemplate:
+            r"""Illegal type parameter name '#string' found during expression compilation.""",
+        withArguments: _withArgumentsIncrementalCompilerIllegalTypeParameter);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)>
+    codeIncrementalCompilerIllegalTypeParameter =
+    const Code<Message Function(String string)>(
+  "IncrementalCompilerIllegalTypeParameter",
+  templateIncrementalCompilerIllegalTypeParameter,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsIncrementalCompilerIllegalTypeParameter(String string) {
+  if (string.isEmpty) throw 'No string provided';
+  return new Message(codeIncrementalCompilerIllegalTypeParameter,
+      message:
+          """Illegal type parameter name '${string}' found during expression compilation.""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(Uri uri_)> templateInferredPackageUri =
     const Template<Message Function(Uri uri_)>(
         messageTemplate: r"""Interpreting this as package URI, '#uri'.""",
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_async.dart b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_async.dart
index f89ba6d..4a615c8 100644
--- a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_async.dart
+++ b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_async.dart
@@ -9,8 +9,7 @@
   /*1:main*/ test();
 }
 
-// TODO(34942): Step 2 should point to the body block.
 @pragma('dart2js:noInline')
-test /*2:test*/ () async {
+test() async /*2:test*/ {
   /*4:test*/ throw '>ExceptionMarker<';
 }
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
index 8d1ae16..01f5d88 100644
--- a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
+++ b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_awaited_async.dart
@@ -9,15 +9,13 @@
   /*1:main*/ test1();
 }
 
-// TODO(34942): Step 3 should point to the body block.
 @pragma('dart2js:noInline')
-test1 /*3:test1*/ () async {
+test1() async /*3:test1*/ {
   // This call is on the stack when the error is thrown.
   await /*5:test1*/ test2();
 }
 
-// TODO(34942): Step 7 should point to the body block.
 @pragma('dart2js:noInline')
-test2 /*7:test2*/ () async {
+test2() async /*7:test2*/ {
   /*9:test2*/ throw '>ExceptionMarker<';
 }
diff --git a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
index 23b08e9..9475ce8 100644
--- a/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
+++ b/pkg/compiler/test/sourcemaps/stacktrace/sync_throw_in_top_level_method_from_async.dart
@@ -8,9 +8,8 @@
   /*1:main*/ test1();
 }
 
-// TODO(34942): Step 2 should point to the body block.
 @pragma('dart2js:noInline')
-test1 /*2:test1*/ () async {
+test1() async /*2:test1*/ {
   /*9:test1*/ test2();
 }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index 3af0ea9..9d8b566 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -23,38 +23,52 @@
         Block,
         Class,
         Component,
+        Constructor,
         DartType,
         Field,
         FunctionNode,
         Library,
+        Member,
         Node,
         Procedure,
         PropertyGet,
         PropertySet,
+        RedirectingFactoryConstructor,
+        TreeNode,
         TypeParameter,
         VariableDeclaration,
         Visitor;
 
+DiagnosticMessage _createInternalError(Uri uri, int line, int col, String msg) {
+  return Message(Code<String>('Expression Compiler Internal error', null),
+          message: msg)
+      .withLocation(uri, 0, 0)
+      .withFormatting(
+          'Internal error: $msg', line, col, Severity.internalProblem, []);
+}
+
 /// Dart scope
 ///
 /// Provides information about symbols available inside a dart scope.
 class DartScope {
   final Library library;
   final Class cls;
-  final Procedure procedure;
+  final Member member;
+  final bool isStatic;
   final Map<String, DartType> definitions;
   final List<TypeParameter> typeParameters;
 
-  DartScope(this.library, this.cls, this.procedure, this.definitions,
-      this.typeParameters);
+  DartScope(this.library, this.cls, this.member, this.definitions,
+      this.typeParameters)
+      : isStatic = member is Procedure ? member.isStatic : false;
 
   @override
   String toString() {
     return '''DartScope {
       Library: ${library.importUri},
       Class: ${cls?.name},
-      Procedure: $procedure,
-      isStatic: ${procedure.isStatic},
+      Procedure: $member,
+      isStatic: $isStatic,
       Scope: $definitions,
       typeParameters: $typeParameters
     }
@@ -62,34 +76,49 @@
   }
 }
 
-/// DartScopeBuilder finds dart scope information in
-/// [component] on a given 1-based [line]:
-/// library, class, locals, formals, and any other
-/// avaiable symbols at that location.
-/// TODO(annagrin): Refine scope detection
-/// See [issue 40278](https://github.com/dart-lang/sdk/issues/40278)
+/// DartScopeBuilder finds dart scope information for a location.
+///
+/// Find all definitions in scope at a given 1-based [line] and [column]:
+///
+/// - library
+/// - class
+/// - locals
+/// - formals
+/// - captured variables (for closures)
 class DartScopeBuilder extends Visitor<void> {
   final Component _component;
-  Library _library;
-  Class _cls;
-  Procedure _procedure;
-  final List<FunctionNode> _functions = [];
   final int _line;
   final int _column;
+
+  Library _library;
+  Class _cls;
+  Member _member;
   int _offset;
+
+  DiagnosticMessageHandler onDiagnostic;
+
+  final List<FunctionNode> _functions = [];
   final Map<String, DartType> _definitions = {};
   final List<TypeParameter> _typeParameters = [];
 
-  DartScopeBuilder(this._component, this._line, this._column);
+  DartScopeBuilder._(this._component, this._line, this._column);
+
+  static DartScope findScope(Component component, Library library, int line,
+      int column, DiagnosticMessageHandler onDiagnostic) {
+    var builder = DartScopeBuilder._(component, line, column)
+      ..onDiagnostic = onDiagnostic;
+    library.accept(builder);
+    return builder.build();
+  }
 
   DartScope build() {
-    if (_library == null || _procedure == null) return null;
+    if (_offset == null || _library == null || _member == null) return null;
 
-    return DartScope(_library, _cls, _procedure, _definitions, _typeParameters);
+    return DartScope(_library, _cls, _member, _definitions, _typeParameters);
   }
 
   @override
-  void defaultNode(Node node) {
+  void defaultTreeNode(Node node) {
     node.visitChildren(this);
   }
 
@@ -98,7 +127,10 @@
     _library = library;
     _offset = _component.getOffset(_library.fileUri, _line, _column);
 
-    super.visitLibrary(library);
+    // Exit early if the evaluation offset is not found.
+    // Note: the complete scope is not found in this case,
+    // so the expression compiler will report an error.
+    if (_offset >= 0) super.visitLibrary(library);
   }
 
   @override
@@ -112,61 +144,115 @@
   }
 
   @override
-  void visitProcedure(Procedure p) {
-    if (_scopeContainsOffset(p.fileOffset, p.fileEndOffset, _offset)) {
-      _procedure = p;
+  void defaultMember(Member m) {
+    if (_scopeContainsOffset(m.fileOffset, m.fileEndOffset, _offset)) {
+      _member = m;
 
-      super.visitProcedure(p);
+      super.defaultMember(m);
     }
   }
 
   @override
   void visitFunctionNode(FunctionNode fun) {
     if (_scopeContainsOffset(fun.fileOffset, fun.fileEndOffset, _offset)) {
-      _collectDefinitions(fun);
+      _functions.add(fun);
       _typeParameters.addAll(fun.typeParameters);
 
       super.visitFunctionNode(fun);
     }
   }
 
-  void _collectDefinitions(FunctionNode fun) {
-    _functions.add(fun);
+  @override
+  void visitVariableDeclaration(VariableDeclaration decl) {
+    // collect locals and formals
+    _definitions[decl.name] = decl.type;
+    super.visitVariableDeclaration(decl);
+  }
 
-    // add formals
-    for (var formal in fun.namedParameters) {
-      _definitions[formal.name] = formal.type;
-    }
-
-    for (var formal in fun.positionalParameters) {
-      _definitions[formal.name] = formal.type;
-    }
-
-    // add locals
-    var body = fun.body;
-    if (body is VariableDeclaration) {
-      // local
-      _definitions[body.name] = body.type;
-    }
-    if (body is Block) {
-      for (var stmt in body.statements) {
-        if (stmt is VariableDeclaration) {
-          // local
-          _definitions[stmt.name] = stmt.type;
-        }
-      }
+  @override
+  void visitBlock(Block block) {
+    var fileEndOffset = FileEndOffsetCalculator.calculateEndOffset(block);
+    if (_scopeContainsOffset(block.fileOffset, fileEndOffset, _offset)) {
+      super.visitBlock(block);
     }
   }
 
-  static bool _scopeContainsOffset(int startOffset, int endOffset, int offset) {
-    if (offset < 0) return false;
-    if (startOffset < 0) return false;
-    if (endOffset < 0) return false;
-
+  bool _scopeContainsOffset(int startOffset, int endOffset, int offset) {
+    if (offset < 0 || startOffset < 0 || endOffset < 0) {
+      return false;
+    }
     return startOffset <= offset && offset <= endOffset;
   }
 }
 
+/// File end offset calculator.
+///
+/// Helps calculate file end offsets for nodes with internal scope
+/// that do not have .fileEndOffset field.
+///
+/// For example - [Block]
+class FileEndOffsetCalculator extends Visitor<int> {
+  static const int noOffset = -1;
+
+  final int _startOffset;
+  final TreeNode _root;
+
+  int _endOffset = noOffset;
+
+  /// Create calculator for a scoping node with no .fileEndOffset.
+  ///
+  /// [_root] is the parent of the scoping node.
+  /// [_startOffset] is the start offset of the scoping node.
+  FileEndOffsetCalculator._(this._root, this._startOffset);
+
+  /// Calculate file end offset for a scoping node.
+  ///
+  /// This calculator finds the first node in the ancestor chain that
+  /// can give such information for a given [node], i.e. satisfies one
+  /// of the following conditions:
+  ///
+  /// - a node with with a greater start offset that is a child of the
+  ///   closest ancestor. The start offset of this child is used as a
+  ///   file end offset of the [node].
+  ///
+  /// - the closest ancestor with .fileEndOffset information. The file
+  ///   end offset of the ancestor is used as the file end offset of
+  ///   the [node.]
+  ///
+  /// If none found, return [noOffset].
+  static int calculateEndOffset(TreeNode node) {
+    for (var n = node.parent; n != null; n = n.parent) {
+      var calculator = FileEndOffsetCalculator._(n, node.fileOffset);
+      var offset = n.accept(calculator);
+      if (offset != noOffset) return offset;
+    }
+    return noOffset;
+  }
+
+  @override
+  int defaultTreeNode(TreeNode node) {
+    if (node == _root) {
+      node.visitChildren(this);
+      if (_endOffset != noOffset) return _endOffset;
+      return _endOffsetForNode(node);
+    }
+    if (_endOffset == noOffset && node.fileOffset > _startOffset) {
+      _endOffset = node.fileOffset;
+    }
+    return _endOffset;
+  }
+
+  static int _endOffsetForNode(TreeNode node) {
+    if (node is Class) return node.fileEndOffset;
+    if (node is Constructor) return node.fileEndOffset;
+    if (node is Procedure) return node.fileEndOffset;
+    if (node is Field) return node.fileEndOffset;
+    if (node is RedirectingFactoryConstructor) return node.fileEndOffset;
+    if (node is FunctionNode) return node.fileEndOffset;
+    return noOffset;
+  }
+}
+
 class PrivateFieldsVisitor extends Visitor<void> {
   final Map<String, String> privateFields = {};
 
@@ -178,28 +264,32 @@
   @override
   void visitFieldReference(Field node) {
     if (node.name.isPrivate) {
-      privateFields[node.name.text] = node.name.library.importUri.toString();
+      var library = node.enclosingLibrary?.importUri;
+      privateFields[node.name.text] = library?.toString();
     }
   }
 
   @override
   void visitField(Field node) {
     if (node.name.isPrivate) {
-      privateFields[node.name.text] = node.name.library.importUri.toString();
+      var library = node.enclosingLibrary?.importUri;
+      privateFields[node.name.text] = library?.toString();
     }
   }
 
   @override
   void visitPropertyGet(PropertyGet node) {
     if (node.name.isPrivate) {
-      privateFields[node.name.text] = node.name.library.importUri.toString();
+      var library = node.interfaceTarget?.enclosingLibrary?.importUri;
+      privateFields[node.name.text] = library?.toString();
     }
   }
 
   @override
   void visitPropertySet(PropertySet node) {
     if (node.name.isPrivate) {
-      privateFields[node.name.text] = node.name.library.importUri.toString();
+      var library = node.interfaceTarget?.enclosingLibrary?.importUri;
+      privateFields[node.name.text] = library?.toString();
     }
   }
 }
@@ -260,83 +350,89 @@
       Map<String, String> jsScope,
       String moduleName,
       String expression) async {
-    // 1. find dart scope where debugger is paused
+    try {
+      // 1. find dart scope where debugger is paused
 
-    _log('Compiling expression in $moduleName:\n$expression');
+      _log('Compiling expression in $moduleName:\n$expression');
 
-    var dartScope = await _findScopeAt(Uri.parse(libraryUri), line, column);
-    if (dartScope == null) {
-      _log('Scope not found at $libraryUri:$line:$column');
+      var dartScope = await _findScopeAt(Uri.parse(libraryUri), line, column);
+      if (dartScope == null) {
+        _log('Scope not found at $libraryUri:$line:$column');
+        return null;
+      }
+
+      // 2. perform necessary variable substitutions
+
+      // TODO(annagrin): we only substitute for the same name or a value
+      // currently, need to extend to cases where js variable names are
+      // different from dart.
+      // See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
+
+      // remove undefined js variables (this allows us to get a reference error
+      // from chrome on evaluation)
+      dartScope.definitions
+          .removeWhere((variable, type) => !jsScope.containsKey(variable));
+
+      // map from values from the stack when available (this allows to evaluate
+      // captured variables optimized away in chrome)
+      var localJsScope =
+          dartScope.definitions.keys.map((variable) => jsScope[variable]);
+
+      _log('Performed scope substitutions for expression');
+
+      // 3. compile dart expression to JS
+
+      var jsExpression = await _compileExpression(
+          dartScope, jsModules, moduleName, expression);
+
+      if (jsExpression == null) {
+        _log('Failed to compile expression in $moduleName:\n$expression');
+        return null;
+      }
+
+      // some adjustments to get proper binding to 'this',
+      // making closure variables available, and catching errors
+
+      // TODO(annagrin): make compiler produce correct expression:
+      // See [issue 40277](https://github.com/dart-lang/sdk/issues/40277)
+      // - evaluate to an expression in function and class context
+      // - allow setting values
+      // See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
+      // - bind to proper 'this'
+      // - map to correct js names for dart symbols
+
+      // 4. create call the expression
+
+      if (dartScope.cls != null && !dartScope.isStatic) {
+        // bind to correct 'this' instead of 'globalThis'
+        jsExpression = '$jsExpression.bind(this)';
+      }
+
+      // 5. wrap in a try/catch to catch errors
+
+      var args = localJsScope.join(',\n    ');
+      jsExpression = jsExpression.split('\n').join('\n  ');
+      var callExpression = '\ntry {'
+          '\n  ($jsExpression('
+          '\n    $args'
+          '\n  ))'
+          '\n} catch (error) {'
+          '\n  error.name + ": " + error.message;'
+          '\n}';
+
+      _log('Compiled expression \n$expression to $callExpression');
+      return callExpression;
+    } catch (e, s) {
+      onDiagnostic(
+          _createInternalError(Uri.parse(libraryUri), line, column, '$e:$s'));
       return null;
     }
-
-    // 2. perform necessary variable substitutions
-
-    // TODO(annagrin): we only substitute for the same name or a value currently,
-    // need to extend to cases where js variable names are different from dart
-    // See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
-
-    // remove undefined js variables (this allows us to get a reference error
-    // from chrome on evaluation)
-    dartScope.definitions
-        .removeWhere((variable, type) => !jsScope.containsKey(variable));
-
-    // map from values from the stack when available (this allows to evaluate
-    // captured variables optimized away in chrome)
-    var localJsScope =
-        dartScope.definitions.keys.map((variable) => jsScope[variable]);
-
-    _log('Performed scope substitutions for expression');
-
-    // 3. compile dart expression to JS
-
-    var jsExpression =
-        await _compileExpression(dartScope, jsModules, moduleName, expression);
-
-    if (jsExpression == null) {
-      _log('Failed to compile expression in $moduleName:\n$expression');
-      return null;
-    }
-
-    // some adjustments to get proper binding to 'this',
-    // making closure variables available, and catching errors
-
-    // TODO(annagrin): make compiler produce correct expression:
-    // See [issue 40277](https://github.com/dart-lang/sdk/issues/40277)
-    // - evaluate to an expression in function and class context
-    // - allow setting values
-    // See [issue 40273](https://github.com/dart-lang/sdk/issues/40273)
-    // - bind to proper 'this'
-    // - map to correct js names for dart symbols
-
-    // 4. create call the expression
-
-    if (dartScope.cls != null && !dartScope.procedure.isStatic) {
-      // bind to correct 'this' instead of 'globalThis'
-      jsExpression = '$jsExpression.bind(this)';
-    }
-
-    // 5. wrap in a try/catch to catch errors
-
-    var args = localJsScope.join(', ');
-    var callExpression = '''
-try {
-($jsExpression(
-$args
-))
-} catch (error) {
-error.name + ": " + error.message;
-}''';
-
-    _log(
-        'Compiled expression in $moduleName:\n$expression to \n$callExpression');
-    return callExpression;
   }
 
   Future<DartScope> _findScopeAt(Uri libraryUri, int line, int column) async {
     if (line < 0) {
       onDiagnostic(_createInternalError(
-          libraryUri, line, column, 'invalid source location: $line, $column'));
+          libraryUri, line, column, 'Invalid source location'));
       return null;
     }
 
@@ -347,9 +443,8 @@
       return null;
     }
 
-    var builder = DartScopeBuilder(_component, line, column);
-    library.accept(builder);
-    var scope = builder.build();
+    var scope = DartScopeBuilder.findScope(
+        _component, library, line, column, onDiagnostic);
     if (scope == null) {
       onDiagnostic(_createInternalError(
           libraryUri, line, column, 'Dart scope not found for location'));
@@ -375,40 +470,6 @@
     });
   }
 
-  /// Creates a stament to require a module to bring it back to scope
-  /// example:
-  /// let dart = require('dart_sdk').dart;
-  js_ast.Statement _createRequireModuleStatement(
-      String moduleName, String moduleVariable, String fieldName) {
-    var variableName = moduleVariable.replaceFirst('.dart', '');
-    var rhs = js_ast.PropertyAccess.field(
-        js_ast.Call(js_ast.Identifier('require'),
-            [js_ast.LiteralExpression('\'$moduleName\'')]),
-        '$fieldName');
-
-    return rhs.toVariableDeclaration(js_ast.Identifier('$variableName'));
-  }
-
-  js_ast.Statement _createPrivateField(String field, String library) {
-    var libraryName = library.replaceFirst('.dart', '');
-    var rhs = js_ast.Call(
-        js_ast.PropertyAccess.field(js_ast.Identifier('dart'), 'privateName'), [
-      js_ast.LiteralExpression(libraryName),
-      js_ast.LiteralExpression('"$field"')
-    ]);
-
-    // example:
-    // let _f = dart.privateName(main, "_f");
-    return rhs.toVariableDeclaration(js_ast.Identifier('$field'));
-  }
-
-  DiagnosticMessage _createInternalError(
-      Uri uri, int line, int col, String msg) {
-    return Message(Code<String>('Internal error', null), message: msg)
-        .withLocation(uri, 0, 0)
-        .withFormatting('', line, col, Severity.internalProblem, []);
-  }
-
   /// Return a JS function that returns the evaluated results when called.
   ///
   /// [scope] current dart scope information
@@ -431,7 +492,7 @@
         debugProcedureName,
         scope.library.importUri,
         scope.cls?.name,
-        scope.procedure.isStatic);
+        scope.isStatic);
 
     _log('Compiled expression to kernel');
 
@@ -452,9 +513,29 @@
 
     _log('Generated JavaScript for expression');
 
-    // apply temporary workarounds for what ideally
-    // needs to be done in the compiler
+    var jsFunModified = _addSymbolDefinitions(procedure, jsFun, scope, modules);
 
+    _log('Added symbol definitions to JavaScript');
+
+    // print JS ast to string for evaluation
+
+    var context = js_ast.SimpleJavaScriptPrintingContext();
+    var opts =
+        js_ast.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
+
+    jsFunModified.accept(js_ast.Printer(opts, context));
+    _log('Performed JavaScript adjustments for expression');
+
+    return context.getText();
+  }
+
+  /// Add symbol definitions for all symbols in compiled expression
+  ///
+  /// TODO: this is a temporary workaround to make JavaScript produced
+  /// by the ProgramCompiler self-contained.
+  /// Issue: https://github.com/dart-lang/sdk/issues/41480
+  js_ast.Fun _addSymbolDefinitions(Procedure procedure, js_ast.Fun jsFun,
+      DartScope scope, Map<String, String> modules) {
     // get private fields accessed by the evaluated expression
     var fieldsCollector = PrivateFieldsVisitor();
     procedure.accept(fieldsCollector);
@@ -467,20 +548,22 @@
       var module = modules[variable];
       for (var field in privateFields.keys) {
         var library = privateFields[field];
-        var libraryVariable =
-            library.replaceAll('.dart', '').replaceAll('/', '__');
-        if (libraryVariable.endsWith(variable)) {
-          if (currentLibraries[field] != null) {
-            onDiagnostic(_createInternalError(
-                scope.library.importUri,
-                0,
-                0,
-                'ExpressionCompiler: $field defined in more than one library: '
-                '${currentLibraries[field]}, $variable'));
-            return null;
+        if (library != null) {
+          var libraryVariable =
+              library.replaceAll('.dart', '').replaceAll('/', '__');
+          if (libraryVariable.endsWith(variable)) {
+            if (currentLibraries[field] != null) {
+              onDiagnostic(_createInternalError(
+                  scope.library.importUri,
+                  0,
+                  0,
+                  'ExpressionCompiler: $field defined in more than one library: '
+                  '${currentLibraries[field]}, $variable'));
+              return null;
+            }
+            currentLibraries[field] = variable;
+            currentModules[variable] = module;
           }
-          currentLibraries[field] = variable;
-          currentModules[variable] = module;
         }
       }
     }
@@ -497,17 +580,36 @@
       ...jsFun.body.statements
     ]);
 
-    var jsFunModified = js_ast.Fun(jsFun.params, body);
+    return js_ast.Fun(jsFun.params, body);
+  }
 
-    // print JS ast to string for evaluation
+  /// Creates a library symbol definition
+  ///
+  /// example:
+  /// let dart = require('dart_sdk').dart;
+  js_ast.Statement _createRequireModuleStatement(
+      String moduleName, String moduleVariable, String fieldName) {
+    var variableName = moduleVariable.replaceFirst('.dart', '');
+    var rhs = js_ast.PropertyAccess.field(
+        js_ast.Call(js_ast.Identifier('require'),
+            [js_ast.LiteralExpression('\'$moduleName\'')]),
+        '$fieldName');
 
-    var context = js_ast.SimpleJavaScriptPrintingContext();
-    var opts =
-        js_ast.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
+    return rhs.toVariableDeclaration(js_ast.Identifier('$variableName'));
+  }
 
-    jsFunModified.accept(js_ast.Printer(opts, context));
-    _log('Performed JavaScript adjustments for expression');
+  /// Creates a private symbol definition
+  ///
+  /// example:
+  /// let _f = dart.privateName(main, "_f");
+  js_ast.Statement _createPrivateField(String field, String library) {
+    var libraryName = library.replaceFirst('.dart', '');
+    var rhs = js_ast.Call(
+        js_ast.PropertyAccess.field(js_ast.Identifier('dart'), 'privateName'), [
+      js_ast.LiteralExpression(libraryName),
+      js_ast.LiteralExpression('"$field"')
+    ]);
 
-    return context.getText();
+    return rhs.toVariableDeclaration(js_ast.Identifier('$field'));
   }
 }
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
index 5636b5f..f1b9e63 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
@@ -222,13 +222,14 @@
         scope: scope,
         expression: expression);
 
-    if (expectedError != null) {
-      expect(result.isSuccess, isFalse);
-      expect(_normalize(result.result), matches(expectedError));
-    } else if (expectedResult != null) {
-      expect(result.isSuccess, isTrue);
-      expect(_normalize(result.result), _matches(expectedResult));
-    }
+    var success = expectedError == null;
+    var message = success ? expectedResult : expectedError;
+
+    expect(
+        result,
+        const TypeMatcher<TestCompilationResult>()
+            .having((r) => _normalize(r.result), 'result', _matches(message))
+            .having((r) => r.isSuccess, 'isSuccess', success));
   }
 
   String _normalize(String text) {
@@ -236,11 +237,8 @@
   }
 
   Matcher _matches(String text) {
-    var indent = text.indexOf(RegExp('[^ ]'));
-    var unindented =
-        text.split('\n').map((line) => line.substring(indent)).join('\n');
-
-    return matches(RegExp(RegExp.escape(unindented), multiLine: true));
+    var unindented = RegExp.escape(text).replaceAll(RegExp('[ ]+'), '[ ]*');
+    return matches(RegExp(unindented, multiLine: true));
   }
 
   int _getEvaluationLine(String source) {
@@ -290,7 +288,14 @@
           expectedError: "Error: Getter not found: 'typo'");
     });
 
-    test('local', () async {
+    test('local (trimmed scope)', () async {
+      // Test that current expression evaluation works in extension methods.
+      //
+      // Note: the actual scope is {#this, ret}, but #this is effectively
+      // removed in the expression compilator because it does not exist
+      // in JavaScript code.
+      // See (full scope) tests for what will the evaluation will look like
+      // when the mapping from dart symbols to JavaScipt symbols is added.
       await driver.check(
           scope: <String, String>{'ret': '1234'},
           expression: 'ret',
@@ -298,16 +303,110 @@
           (function(ret) {
             return ret;
           }(
-          1234
+            1234
           ))
           ''');
     });
 
-    test('this', () async {
+    test('local (full scope)', () async {
+      // Test evalution in extension methods in the future when the mapping
+      // from kernel symbols to dartdevc symbols is added.
+      //
+      // Note: this currently fails due to
+      // - incremental compiler not allowing #this as a parameter name
       await driver.check(
-          scope: <String, String>{'ret': '1234'},
+          scope: <String, String>{'ret': '1234', '#this': 'this'},
+          expression: 'ret',
+          expectedError:
+              "Illegal parameter name '#this' found during expression compilation.");
+    });
+
+    test('this (full scope)', () async {
+      // Test evalution in extension methods in the future when the mapping
+      // from kernel symbols to dartdevc symbols is added.
+      //
+      // Note: this currently fails due to
+      // - incremental compiler not allowing #this as a parameter name
+      // - incremental compiler not mapping 'this' from user input to '#this'
+      await driver.check(
+          scope: <String, String>{'ret': '1234', '#this': 'this'},
           expression: 'this',
-          expectedError: "Expected identifier, but got 'this'.");
+          expectedError:
+              "Illegal parameter name '#this' found during expression compilation.");
+    });
+  });
+
+  group('Expression compiler tests in static function:', () {
+    const source = '''
+      int foo(int x, {int y}) {
+        int z = 0;
+        /* evaluation placeholder */
+        return x + y + z;
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('compilation error', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
+          expression: 'typo',
+          expectedError: "Getter not found: \'typo\'");
+    });
+
+    test('local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
+          expression: 'x',
+          expectedResult: '''
+          (function(x, y, z) {
+            return x;
+          }(
+            1,
+            2,
+            3
+          ))
+          ''');
+    });
+
+    test('formal', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
+          expression: 'y',
+          expectedResult: '''
+          (function(x, y, z) {
+            return y;
+          }(
+            1,
+            2,
+            3
+          ))
+          ''');
+    });
+
+    test('named formal', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'y': '2', 'z': '3'},
+          expression: 'z',
+          expectedResult: '''
+          (function(x, y, z) {
+            return z;
+          }(
+            1,
+            2,
+            3
+          ))
+          ''');
     });
   });
 
@@ -369,7 +468,7 @@
           (function(x) {
             return x;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -382,7 +481,7 @@
           (function(x) {
             return this;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -395,7 +494,7 @@
           (function(x) {
             return dart.notNull(x) + 1;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -408,7 +507,7 @@
           (function(x) {
             return dart.notNull(x) + dart.notNull(foo.C.staticField);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -423,7 +522,7 @@
             let _staticField = dart.privateName(foo, "_staticField");
             return dart.notNull(x) + dart.notNull(foo.C._staticField);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -436,7 +535,7 @@
           (function(x) {
             return dart.notNull(x) + dart.notNull(this.field);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -451,7 +550,7 @@
             let _field = dart.privateName(foo, "_field");
             return dart.notNull(x) + dart.notNull(this[_field]);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -464,7 +563,7 @@
           (function(x) {
             return dart.notNull(x) + dart.notNull(foo.global);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -477,7 +576,7 @@
           (function(x) {
             return this.methodFieldAccess(2);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -490,7 +589,7 @@
           (function(x) {
             return this.asyncMethod(2);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -500,12 +599,12 @@
           scope: <String, String>{'x': '1'},
           expression: '"1234".parseInt()',
           expectedResult: '''
-        (function(x) {
-          return foo['NumberParsing|parseInt']("1234");
-        }.bind(this)(
-        1
-        ))
-        ''');
+          (function(x) {
+            return foo['NumberParsing|parseInt']("1234");
+          }.bind(this)(
+            1
+          ))
+          ''');
     });
 
     test('private field modification', () async {
@@ -513,14 +612,14 @@
           scope: <String, String>{'x': '1'},
           expression: '_field = 2',
           expectedResult: '''
-        (function(x) {
-          let foo = require('foo.dart').foo;
-          let _field = dart.privateName(foo, "_field");
-          return this[_field] = 2;
-        }.bind(this)(
-        1
-        ))
-        ''');
+          (function(x) {
+            let foo = require('foo.dart').foo;
+            let _field = dart.privateName(foo, "_field");
+            return this[_field] = 2;
+          }.bind(this)(
+            1
+          ))
+          ''');
     });
 
     test('field modification', () async {
@@ -531,7 +630,7 @@
           (function(x) {
             return this.field = 2;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -546,7 +645,7 @@
             let _staticField = dart.privateName(foo, "_staticField");
             return foo.C._staticField = 2;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -559,7 +658,7 @@
           (function(x) {
             return foo.C.staticField = 2;
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -622,7 +721,7 @@
           (function(x) {
             return dart.notNull(x) + dart.notNull(foo.C.staticField);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -637,7 +736,7 @@
             let _staticField = dart.privateName(foo, "_staticField");
             return dart.notNull(x) + dart.notNull(foo.C._staticField);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -650,7 +749,7 @@
           (function(x) {
             return dart.notNull(x) + dart.notNull(this.field);
           }.bind(this)(
-          1
+            1
           ))
           ''');
     });
@@ -852,7 +951,8 @@
           (function(x, c) {
             return x;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -865,7 +965,8 @@
           (function(x, c) {
             return c;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -878,7 +979,8 @@
             (function(x, c) {
               return new foo.C.new(1, 3);
             }(
-            1, null
+              1,
+              null
             ))
             ''');
     });
@@ -893,7 +995,8 @@
             let _field = dart.privateName(foo, "_field");
             return new foo.C.new(1, 3)[_field];
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -906,7 +1009,8 @@
           (function(x, c) {
             return foo.C.staticField;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -926,7 +1030,8 @@
           (function(x, c) {
             return c.field;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -941,7 +1046,8 @@
             let _field = dart.privateName(foo, "_field");
             return c[_field];
           }(
-          1, null
+              1,
+              null
           ))
           ''');
     });
@@ -954,7 +1060,8 @@
           (function(x, c) {
             return c.methodFieldAccess(2);
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -967,7 +1074,8 @@
           (function(x, c) {
             return c.asyncMethod(2);
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -977,12 +1085,13 @@
           scope: <String, String>{'x': '1', 'c': 'null'},
           expression: '"1234".parseInt()',
           expectedResult: '''
-        (function(x, c) {
-          return foo['NumberParsing|parseInt']("1234");
-        }(
-        1, null
-        ))
-        ''');
+          (function(x, c) {
+            return foo['NumberParsing|parseInt']("1234");
+          }(
+            1,
+            null
+          ))
+          ''');
     });
 
     test('private field modification', () async {
@@ -995,7 +1104,8 @@
             let _field = dart.privateName(foo, "_field");
             return c[_field] = 2;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -1008,7 +1118,8 @@
           (function(x, c) {
             return c.field = 2;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -1028,7 +1139,8 @@
           (function(x, c) {
             return foo.C.staticField = 2;
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -1041,7 +1153,8 @@
           (function(x, c) {
             return core.print(x);
           }(
-          1, null
+            1,
+            null
           ))
           ''');
     });
@@ -1093,7 +1206,10 @@
           (function(x, c, y, z) {
             return dart.str(x) + "+" + dart.str(y) + "+" + dart.str(z);
           }(
-          1, null, 3, 0
+            1,
+            null,
+            3,
+            0
           ))
           ''');
     });
@@ -1106,7 +1222,10 @@
           (function(x, c, y, z) {
             return dart.str(y) + "+" + dart.str(z);
           }(
-          1, null, 3, 0
+            1,
+            null,
+            3,
+            0
           ))
           ''');
     });
@@ -1168,7 +1287,7 @@
           (function(p) {
             return foo.bar(p);
           }(
-          1
+            1
           ))
           ''');
     });
@@ -1195,7 +1314,7 @@
           (function(p) {
             return C0 || CT.C0;
           }(
-          1
+            1
           ))
           ''');
     });
@@ -1211,8 +1330,8 @@
           ))
           ''');
     },
-        skip:
-            'Cannot compile constants optimized away by the frontend'); // https://github.com/dart-lang/sdk/issues/41999
+        skip: 'Cannot compile constants optimized away by the frontend. '
+            'Issue: https://github.com/dart-lang/sdk/issues/41999');
 
     test('evaluate factory constructor call', () async {
       await driver.check(
@@ -1222,7 +1341,7 @@
           (function(p) {
             return new foo.ValueKey.new("t");
           }(
-          1
+            1
           ))
           ''');
     });
@@ -1235,9 +1354,476 @@
           (function(p) {
             return C0 || CT.C0;
           }(
-          1
+            1
           ))
           ''');
     });
   });
+
+  group('Expression compiler tests in constructor:', () {
+    const source = '''
+      extension NumberParsing on String {
+        int parseInt() {
+          return int.parse(this);
+        }
+      }
+
+      int global = 42;
+
+      class C {
+        C(int this.field, int this._field) {
+          int x = 1;
+          /* evaluation placeholder */
+          print(this.field);
+        }
+
+        static int staticField = 0;
+        static int _staticField = 1;
+
+        int _field;
+        int field;
+
+        int methodFieldAccess(int t) {
+          return t + _field + _staticField;
+        }
+
+        Future<int> asyncMethod(int t) async {
+          return t;
+        }
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('compilation error', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'typo',
+          expectedError: "The getter 'typo' isn't defined for the class 'C'");
+    });
+
+    test('local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x',
+          expectedResult: '''
+          (function(x) {
+            return x;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('this', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'this',
+          expectedResult: '''
+          (function(x) {
+            return this;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('expression using locals', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + 1',
+          expectedResult: '''
+          (function(x) {
+            return dart.notNull(x) + 1;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('expression using static fields', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + staticField',
+          expectedResult: '''
+          (function(x) {
+            return dart.notNull(x) + dart.notNull(foo.C.staticField);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('expression using private static fields', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + _staticField',
+          expectedResult: '''
+          (function(x) {
+            let foo = require('foo.dart').foo;
+            let _staticField = dart.privateName(foo, "_staticField");
+            return dart.notNull(x) + dart.notNull(foo.C._staticField);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('expression using fields', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + field',
+          expectedResult: '''
+          (function(x) {
+            return dart.notNull(x) + dart.notNull(this.field);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('expression using private fields', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + _field',
+          expectedResult: '''
+          (function(x) {
+            let foo = require('foo.dart').foo;
+            let _field = dart.privateName(foo, "_field");
+            return dart.notNull(x) + dart.notNull(this[_field]);
+          }.bind(this)(
+              1
+          ))
+          ''');
+    });
+
+    test('expression using globals', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'x + global',
+          expectedResult: '''
+          (function(x) {
+            return dart.notNull(x) + dart.notNull(foo.global);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('method call', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'methodFieldAccess(2)',
+          expectedResult: '''
+          (function(x) {
+            return this.methodFieldAccess(2);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('async method call', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'asyncMethod(2)',
+          expectedResult: '''
+          (function(x) {
+            return this.asyncMethod(2);
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('extension method call', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: '"1234".parseInt()',
+          expectedResult: '''
+        (function(x) {
+          return foo['NumberParsing|parseInt']("1234");
+        }.bind(this)(
+          1
+        ))
+        ''');
+    });
+
+    test('private field modification', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: '_field = 2',
+          expectedResult: '''
+        (function(x) {
+          let foo = require('foo.dart').foo;
+          let _field = dart.privateName(foo, "_field");
+          return this[_field] = 2;
+        }.bind(this)(
+          1
+        ))
+        ''');
+    });
+
+    test('field modification', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'field = 2',
+          expectedResult: '''
+          (function(x) {
+            return this.field = 2;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('private static field modification', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: '_staticField = 2',
+          expectedResult: '''
+          (function(x) {
+            let foo = require('foo.dart').foo;
+            let _staticField = dart.privateName(foo, "_staticField");
+            return foo.C._staticField = 2;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+
+    test('static field modification', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1'},
+          expression: 'staticField = 2',
+          expectedResult: '''
+          (function(x) {
+            return foo.C.staticField = 2;
+          }.bind(this)(
+            1
+          ))
+          ''');
+    });
+  });
+
+  group('Expression compiler tests in loops:', () {
+    const source = r'''
+      int globalFunction() {
+        int x = 15;
+        var c = C(1, 2);
+
+        for(int i = 0; i < 10; i++) {
+          /* evaluation placeholder */
+          print('$i+$x');
+        };
+        return 0;
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('expression using local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'i': '0'},
+          expression: 'x',
+          expectedResult: '''
+          (function(x, c, i) {
+            return x;
+          }(
+            1,
+            null,
+            0
+          ))
+          ''');
+    });
+
+    test('expression using loop variable', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'i': '0'},
+          expression: 'i',
+          expectedResult: '''
+          (function(x, c, i) {
+            return i;
+          }(
+            1,
+            null,
+            0
+          ))
+          ''');
+    });
+  });
+
+  group('Expression compiler tests in conditional (then):', () {
+    const source = r'''
+      int globalFunction() {
+        int x = 1;
+        var c = C(1, 2);
+
+        if (x == 14) {
+          int y = 3;
+          /* evaluation placeholder */
+          print('$y+$x');
+        } else {
+          int z = 3;
+          print('$z+$x');
+        }
+        return 0;
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('expression using local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'y': '3'},
+          expression: 'y',
+          expectedResult: '''
+          (function(x, c, y) {
+            return y;
+          }(
+            1,
+            null,
+            3
+          ))
+          ''');
+    });
+
+    test('expression using local out of scope', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'y': '3'},
+          expression: 'z',
+          expectedError: "Error: Getter not found: 'z'");
+    });
+  });
+
+  group('Expression compiler tests in conditional (else):', () {
+    const source = r'''
+      int globalFunction() {
+        int x = 1;
+        var c = C(1, 2);
+
+        if (x == 14) {
+          int y = 3;
+          print('$y+$x');
+        } else {
+          int z = 3;
+          /* evaluation placeholder */
+          print('$z+$x');
+        }
+        return 0;
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('expression using local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'z': '3'},
+          expression: 'z',
+          expectedResult: '''
+          (function(x, c, z) {
+            return z;
+          }(
+            1,
+            null,
+            3
+          ))
+          ''');
+    });
+
+    test('expression using local out of scope', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null', 'z': '3'},
+          expression: 'y',
+          expectedError: "Error: Getter not found: 'y'");
+    });
+  });
+
+  group('Expression compiler tests after conditionals:', () {
+    const source = r'''
+      int globalFunction() {
+        int x = 1;
+        var c = C(1, 2);
+
+        if (x == 14) {
+          int y = 3;
+          print('$y+$x');
+        } else {
+          int z = 3;
+          print('$z+$x');
+        }
+        /* evaluation placeholder */
+        return 0;
+      }
+
+      main() => 0;
+      ''';
+
+    TestDriver driver;
+    setUp(() {
+      driver = TestDriver(options, source);
+    });
+
+    tearDown(() {
+      driver.delete();
+    });
+
+    test('expression using local', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null'},
+          expression: 'x',
+          expectedResult: '''
+          (function(x, c) {
+            return x;
+          }(
+            1,
+            null
+          ))
+          ''');
+    });
+
+    test('expression using local out of scope', () async {
+      await driver.check(
+          scope: <String, String>{'x': '1', 'c': 'null'},
+          expression: 'z',
+          expectedError: "Error: Getter not found: 'z'");
+    });
+  });
 }
diff --git a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
index 65167c7..1735621 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_worker_test.dart
@@ -115,6 +115,14 @@
 
   B b = new B();
 }
+
+extension NumberParsing on String {
+  int parseInt() {
+    var ret = int.parse(this);
+    // line 17
+    return ret;
+  }
+}
 ''');
 
     var testLibrary = root.resolve('lib/test_library.dart');
@@ -317,6 +325,40 @@
           ]));
     });
 
+    test(
+        'can load dependencies and compile expressions in main (extension method)',
+        () async {
+      requestController.add({
+        'command': 'UpdateDeps',
+        'inputs': inputs,
+      });
+
+      requestController.add({
+        'command': 'CompileExpression',
+        'expression': 'ret',
+        'line': 17,
+        'column': 1,
+        'jsModules': {},
+        'jsScope': {'ret': 'ret'},
+        'libraryUri': config.mainModule.libraryUri,
+        'moduleName': config.mainModule.moduleName,
+      });
+
+      expect(
+          responseController.stream,
+          emitsInOrder([
+            equals({
+              'succeeded': true,
+            }),
+            equals({
+              'succeeded': true,
+              'errors': isEmpty,
+              'warnings': isEmpty,
+              'compiledProcedure': contains('return ret;'),
+            })
+          ]));
+    });
+
     test('can load dependencies and compile transitive expressions in main',
         () async {
       requestController.add({
diff --git a/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart b/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart
new file mode 100644
index 0000000..0ee38ea
--- /dev/null
+++ b/pkg/dev_compiler/test/expression_compiler/scope_offset_test.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'package:dev_compiler/src/kernel/expression_compiler.dart';
+import 'package:front_end/src/api_prototype/standard_file_system.dart';
+import 'package:front_end/src/api_unstable/ddc.dart';
+import 'package:front_end/src/compute_platform_binaries_location.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/binary/ast_from_binary.dart';
+import 'package:kernel/src/printer.dart';
+import 'package:test/test.dart';
+
+/// Verbose mode for debugging
+bool get verbose => false;
+
+Uri get sdkRoot => computePlatformBinariesLocation();
+Uri get sdkSummaryPath => sdkRoot.resolve('ddc_sdk.dill');
+Uri get librariesPath => sdkRoot.resolve('lib/libraries.json');
+
+void main(List<String> args) {
+  test('Offsets are present on scoping nodes in SDK', () async {
+    var entity = StandardFileSystem.instance.entityForUri(sdkSummaryPath);
+    var bytes = await entity.readAsBytes();
+
+    var component = Component();
+    BinaryBuilderWithMetadata(bytes, disableLazyReading: true)
+        .readComponent(component, checkCanonicalNames: true, createView: true);
+
+    for (var lib in component.libraries) {
+      ScopeOffsetValidator.validate(lib);
+    }
+  });
+}
+
+class ScopeOffsetValidator extends Visitor<void> {
+  int classCount = 0;
+  int memberCount = 0;
+  int blockCount = 0;
+
+  ScopeOffsetValidator._();
+
+  static void validate(Library library) {
+    var validator = ScopeOffsetValidator._();
+    validator.visitLibrary(library);
+    expect(validator.classCount + validator.memberCount, greaterThan(0),
+        reason: 'Validation was not empty');
+    expect(validator.blockCount, equals(0),
+        reason: 'SDK dill only contains outlines');
+  }
+
+  @override
+  void defaultTreeNode(Node node) {
+    node.visitChildren(this);
+  }
+
+  @override
+  void visitClass(Class cls) {
+    classCount++;
+    expect(
+        cls,
+        const TypeMatcher<Class>()
+            .having((c) => c.fileOffset, '${cls.name} : fileOffset',
+                isNot(equals(-1)))
+            .having((c) => c.fileEndOffset, '${cls.name} : fileEndOffset',
+                isNot(equals(-1))));
+
+    super.visitClass(cls);
+  }
+
+  @override
+  void defaultMember(Member member) {
+    // exclude code that does not correspond to a dart source
+    // location we can set a breakpoint on.
+    var noBreakPointPossible = (member is Constructor)
+        ? member.isSynthetic
+        : (member is Procedure)
+            ? member.isNoSuchMethodForwarder ||
+                member.isAbstract ||
+                member.isForwardingStub
+            : (member is Field)
+                ? member.name.name.contains(redirectingName)
+                : false;
+
+    if (!noBreakPointPossible) {
+      memberCount++;
+      expect(
+          member,
+          const TypeMatcher<Member>()
+              .having(
+                  (c) => c.fileOffset,
+                  '${member.enclosingClass}.${member.name} : fileOffset',
+                  isNot(equals(-1)))
+              .having(
+                  (c) => c.fileEndOffset,
+                  '${member.enclosingClass}.${member.name} : fileEndOffset',
+                  isNot(equals(-1))));
+
+      super.defaultMember(member);
+    }
+  }
+
+  @override
+  void visitFunctionNode(FunctionNode fun) {
+    expect(
+        fun,
+        const TypeMatcher<FunctionNode>()
+            .having(
+                (c) => c.fileOffset,
+                '${fun.parent.toText(astTextStrategyForTesting)} : fileOffset',
+                isNot(equals(-1)))
+            .having(
+                (c) => c.fileEndOffset,
+                '${fun.parent.toText(astTextStrategyForTesting)} : fileEndOffset',
+                isNot(equals(-1))));
+
+    super.visitFunctionNode(fun);
+  }
+
+  @override
+  void visitBlock(Block block) {
+    blockCount++;
+    expect(
+        block,
+        const TypeMatcher<FunctionNode>().having(
+            (c) => c.fileOffset,
+            '${block.toText(astTextStrategyForTesting)} : fileOffset',
+            isNot(equals(-1))));
+
+    var fileEndOffset = FileEndOffsetCalculator.calculateEndOffset(block);
+    expect(fileEndOffset, isNot(equals(-1)),
+        reason: '${block.toText(astTextStrategyForTesting)} : fileOffset');
+
+    super.visitBlock(block);
+  }
+}
diff --git a/pkg/front_end/lib/src/api_unstable/ddc.dart b/pkg/front_end/lib/src/api_unstable/ddc.dart
index 3c0befd..18a00ef 100644
--- a/pkg/front_end/lib/src/api_unstable/ddc.dart
+++ b/pkg/front_end/lib/src/api_unstable/ddc.dart
@@ -67,7 +67,7 @@
 export '../fasta/incremental_compiler.dart' show IncrementalCompiler;
 
 export '../fasta/kernel/redirecting_factory_body.dart'
-    show RedirectingFactoryBody, isRedirectingFactoryField;
+    show RedirectingFactoryBody, isRedirectingFactoryField, redirectingName;
 
 export '../fasta/type_inference/type_schema_environment.dart'
     show TypeSchemaEnvironment;
diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
index b9b46e8..5584775 100644
--- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart
@@ -737,6 +737,7 @@
 abstract class AbstractLateFieldEncoding implements FieldEncoding {
   final String name;
   final int fileOffset;
+  final int fileEndOffset;
   DartType _type;
   Field _field;
   Field _lateIsSetField;
@@ -772,6 +773,7 @@
       bool isCovariant,
       late_lowering.IsSetStrategy isSetStrategy)
       : fileOffset = charOffset,
+        fileEndOffset = charEndOffset,
         _isSetStrategy = isSetStrategy,
         _forceIncludeIsSetField =
             isSetStrategy == late_lowering.IsSetStrategy.forceUseIsSetField {
@@ -796,9 +798,15 @@
         break;
     }
     _lateGetter = new Procedure(
-        null, ProcedureKind.Getter, new FunctionNode(null),
-        fileUri: fileUri, reference: getterReferenceFrom?.reference)
+        null,
+        ProcedureKind.Getter,
+        new FunctionNode(null)
+          ..fileOffset = charOffset
+          ..fileEndOffset = charEndOffset,
+        fileUri: fileUri,
+        reference: getterReferenceFrom?.reference)
       ..fileOffset = charOffset
+      ..fileEndOffset = charEndOffset
       ..isNonNullableByDefault = true;
     _lateSetter = _createSetter(name, fileUri, charOffset, setterReferenceFrom,
         isCovariant: isCovariant);
@@ -913,10 +921,13 @@
         null,
         ProcedureKind.Setter,
         new FunctionNode(null,
-            positionalParameters: [parameter], returnType: const VoidType()),
+            positionalParameters: [parameter], returnType: const VoidType())
+          ..fileOffset = charOffset
+          ..fileEndOffset = fileEndOffset,
         fileUri: fileUri,
         reference: referenceFrom?.reference)
       ..fileOffset = charOffset
+      ..fileEndOffset = fileEndOffset
       ..isNonNullableByDefault = true;
   }
 
@@ -1496,7 +1507,9 @@
           null,
           ProcedureKind.Setter,
           new FunctionNode(null,
-              positionalParameters: [parameter], returnType: const VoidType()),
+              positionalParameters: [parameter], returnType: const VoidType())
+            ..fileOffset = charOffset
+            ..fileEndOffset = charEndOffset,
           fileUri: fileUri,
           reference: setterReference?.reference)
         ..fileOffset = charOffset
diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
index 6ae4f6d..654aef2 100644
--- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart
@@ -527,24 +527,29 @@
           ..fileOffset = fileOffset)
       ..fileOffset = fileOffset;
 
-    FunctionExpression closure = new FunctionExpression(new FunctionNode(
-        closureBody,
-        typeParameters: closureTypeParameters,
-        positionalParameters: closurePositionalParameters,
-        namedParameters: closureNamedParameters,
-        requiredParameterCount: _procedure.function.requiredParameterCount - 1,
-        returnType: closureReturnType))
+    FunctionExpression closure = new FunctionExpression(
+        new FunctionNode(closureBody,
+            typeParameters: closureTypeParameters,
+            positionalParameters: closurePositionalParameters,
+            namedParameters: closureNamedParameters,
+            requiredParameterCount:
+                _procedure.function.requiredParameterCount - 1,
+            returnType: closureReturnType)
+          ..fileOffset = fileOffset
+          ..fileEndOffset = fileEndOffset)
       ..fileOffset = fileOffset;
 
     _extensionTearOff
       ..name = new Name(
           '${extensionBuilder.name}|get#${name}', libraryBuilder.library)
-      ..function = new FunctionNode(
+      ..function = (new FunctionNode(
           new ReturnStatement(closure)..fileOffset = fileOffset,
           typeParameters: tearOffTypeParameters,
           positionalParameters: [extensionThis],
           requiredParameterCount: 1,
           returnType: closure.function.computeFunctionType(library.nonNullable))
+        ..fileOffset = fileOffset
+        ..fileEndOffset = fileEndOffset)
       ..fileUri = fileUri
       ..fileOffset = fileOffset
       ..fileEndOffset = fileEndOffset;
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index 5044b26..cb94983 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -7,6 +7,8 @@
 import 'dart:async' show Future;
 
 import 'package:front_end/src/api_prototype/experimental_flags.dart';
+import 'package:front_end/src/api_prototype/front_end.dart';
+import 'package:front_end/src/fasta/fasta_codes.dart';
 import 'package:kernel/binary/ast_from_binary.dart'
     show
         BinaryBuilderWithMetadata,
@@ -1668,10 +1670,27 @@
       userCode.loader.seenMessages.clear();
 
       for (TypeParameter typeParam in typeDefinitions) {
-        if (!isLegalIdentifier(typeParam.name)) return null;
+        if (!isLegalIdentifier(typeParam.name)) {
+          userCode.loader.addProblem(
+              templateIncrementalCompilerIllegalTypeParameter
+                  .withArguments('$typeParam'),
+              typeParam.fileOffset,
+              0,
+              libraryUri);
+          return null;
+        }
       }
       for (String name in definitions.keys) {
-        if (!isLegalIdentifier(name)) return null;
+        if (!isLegalIdentifier(name)) {
+          userCode.loader.addProblem(
+              templateIncrementalCompilerIllegalParameter.withArguments(name),
+              // TODO: pass variable declarations instead of
+              // parameter names for proper location detection.
+              -1,
+              -1,
+              libraryUri);
+          return null;
+        }
       }
 
       SourceLibraryBuilder debugLibrary = new SourceLibraryBuilder(
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 1cb6060..969f40c 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -394,6 +394,10 @@
 IncorrectTypeArgumentQualifiedInferredWarning/example: Fail
 IncorrectTypeArgumentQualifiedWarning/example: Fail
 IncorrectTypeArgumentWarning/example: Fail
+IncrementalCompilerIllegalParameter/analyzerCode: Fail
+IncrementalCompilerIllegalParameter/example: Fail
+IncrementalCompilerIllegalTypeParameter/analyzerCode: Fail
+IncrementalCompilerIllegalTypeParameter/example: Fail
 InitializerForStaticField/example: Fail
 InitializerOutsideConstructor/example: Fail
 InputFileNotFound/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 905e30c..14e312f 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1892,6 +1892,12 @@
   template: "Unsupported nullability value '#string' on type '#type'."
   severity: INTERNAL_PROBLEM
 
+IncrementalCompilerIllegalParameter:
+  template: "Illegal parameter name '#string' found during expression compilation."
+  
+IncrementalCompilerIllegalTypeParameter:
+  template: "Illegal type parameter name '#string' found during expression compilation."
+
 LocalDefinitionHidesExport:
   template: "Local definition of '#name' hides export from '#uri'."
   severity: IGNORED
diff --git a/pkg/front_end/testcases/expression/invalid_type_variable.expression.yaml.expect b/pkg/front_end/testcases/expression/invalid_type_variable.expression.yaml.expect
index ba1f145..792e96a 100644
--- a/pkg/front_end/testcases/expression/invalid_type_variable.expression.yaml.expect
+++ b/pkg/front_end/testcases/expression/invalid_type_variable.expression.yaml.expect
@@ -1,3 +1,4 @@
 Errors: {
+  pkg/front_end/testcases/expression/main.dart: Error: Illegal type parameter name 'TypeParameter(a#b)' found during expression compilation.
 }
 <no procedure>
diff --git a/pkg/front_end/testcases/expression/invalid_variable.expression.yaml.expect b/pkg/front_end/testcases/expression/invalid_variable.expression.yaml.expect
index ba1f145..a093fa5 100644
--- a/pkg/front_end/testcases/expression/invalid_variable.expression.yaml.expect
+++ b/pkg/front_end/testcases/expression/invalid_variable.expression.yaml.expect
@@ -1,3 +1,4 @@
 Errors: {
+  pkg/front_end/testcases/expression/main.dart: Error: Illegal parameter name 'a#b' found during expression compilation.
 }
 <no procedure>
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index aade49b..de1281f 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -143,7 +143,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 46;
+  UInt32 formatVersion = 47;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -1034,6 +1034,7 @@
 
 type Block extends Statement {
   Byte tag = 62;
+  FileOffset fileOffset;
   List<Statement> statements;
 }
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 01e245c..75e552b 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -2124,9 +2124,10 @@
 
   Block readBlock() {
     int stackHeight = variableStack.length;
+    var offset = readOffset();
     var body = readStatementList();
     variableStack.length = stackHeight;
-    return new Block(body);
+    return new Block(body)..fileOffset = offset;
   }
 
   AssertBlock readAssertBlock() {
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 2dbc82f..f6543c0 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -1807,6 +1807,7 @@
     _variableIndexer ??= new VariableIndexer();
     _variableIndexer.pushScope();
     writeByte(Tag.Block);
+    writeOffset(node.fileOffset);
     writeNodeList(node.statements);
     _variableIndexer.popScope();
   }
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 2bbc968..4fdf50b 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -146,7 +146,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 46;
+  static const int BinaryFormatVersion = 47;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/clone.dart b/pkg/kernel/lib/clone.dart
index 507d6a1..602ec1c7 100644
--- a/pkg/kernel/lib/clone.dart
+++ b/pkg/kernel/lib/clone.dart
@@ -330,7 +330,8 @@
   }
 
   visitBlock(Block node) {
-    return new Block(node.statements.map(clone).toList());
+    return new Block(node.statements.map(clone).toList())
+      ..fileOffset = _cloneFileOffset(node.fileOffset);
   }
 
   visitAssertBlock(AssertBlock node) {
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 4df9ad9..10d5611 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -4014,6 +4014,7 @@
   Fragment instructions;
 
   instructions += EnterScope(offset);
+  ReadPosition();
   intptr_t list_length = ReadListLength();  // read number of statements.
   for (intptr_t i = 0; i < list_length; ++i) {
     if (instructions.is_open()) {
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index 51ddf29..ed382e7 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -583,6 +583,7 @@
       CalculateExpressionFingerprint();  // read expression.
       return;
     case kBlock:
+      ReadPosition();
       CalculateStatementListFingerprint();
       return;
     case kEmptyStatement:
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index ff40a8b..fa4f92a 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2510,6 +2510,7 @@
       SkipExpression();  // read expression.
       return;
     case kBlock:
+      ReadPosition();
       SkipStatementList();
       return;
     case kEmptyStatement:
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 56e1dda..6e65b8d 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -993,7 +993,7 @@
       intptr_t offset = helper_.ReaderOffset() - 1;  // -1 to include tag byte.
 
       EnterScope(offset);
-
+      helper_.ReadPosition();  // read block start offset.
       intptr_t list_length =
           helper_.ReadListLength();  // read number of statements.
       for (intptr_t i = 0; i < list_length; ++i) {
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 1b53aaa..49898fe 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 46;
-static const uint32_t kMaxSupportedKernelFormatVersion = 46;
+static const uint32_t kMinSupportedKernelFormatVersion = 47;
+static const uint32_t kMaxSupportedKernelFormatVersion = 47;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \