Version 2.12.0-205.0.dev

Merge commit 'b1b0a6ce14df5f430f23331a276418eb2cd430ca' into 'dev'
diff --git a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
index 6af9fa0..8c47904 100644
--- a/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/expression_compiler.dart
@@ -163,8 +163,14 @@
 
   @override
   void visitVariableDeclaration(VariableDeclaration decl) {
-    // collect locals and formals
-    _definitions[decl.name] = decl.type;
+    // Collect locals and formals appearing before current breakpoint.
+    // Note that we include variables with no offset because the offset
+    // is not set in many cases in generated code, so omitting them would
+    // make expression evaluation fail in too many cases.
+    // Issue: https://github.com/dart-lang/sdk/issues/43966
+    if (decl.fileOffset < 0 || decl.fileOffset < _offset) {
+      _definitions[decl.name] = decl.type;
+    }
     super.visitVariableDeclaration(decl);
   }
 
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 d6983f9..108bc1e 100644
--- a/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
+++ b/pkg/dev_compiler/test/expression_compiler/expression_compiler_test.dart
@@ -292,6 +292,129 @@
   group('Unsound null safety:', () {
     var options = SetupCompilerOptions(false);
 
+    group('Expression compiler scope collection tests', () {
+      var source = '''
+        ${options.dartLangComment}
+
+        class C {
+          C(int this.field);
+
+          int methodFieldAccess(int x) {
+            var inScope = 1;
+            {
+              var innerInScope = global + staticField + field;
+              /* evaluation placeholder */
+              print(innerInScope);
+              var innerNotInScope = 2;
+            }
+            var notInScope = 3;
+          }
+
+          static int staticField = 0;
+          int field;
+        }
+
+        int global = 42;
+        main() => 0;
+        ''';
+
+      TestDriver driver;
+
+      setUp(() {
+        driver = TestDriver(options, source);
+      });
+
+      tearDown(() {
+        driver.delete();
+      });
+
+      test('local in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'inScope',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return inScope;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('local in inner scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'innerInScope',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return innerInScope;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('global in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'global',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return foo.global;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('static field in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'staticField',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return foo.C.staticField;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('field in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'field',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return this.field;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('local not in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'notInScope',
+            expectedError:
+                "Error: The getter 'notInScope' isn't defined for the class 'C'.");
+      });
+
+      test('local not in inner scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'innerNotInScope',
+            expectedError:
+                "Error: The getter 'innerNotInScope' isn't defined for the class 'C'.");
+      });
+    });
+
     group('Expression compiler tests in extension method:', () {
       var source = '''
         ${options.dartLangComment}
@@ -2024,6 +2147,129 @@
   group('Sound null safety:', () {
     var options = SetupCompilerOptions(true);
 
+    group('Expression compiler scope collection tests', () {
+      var source = '''
+        ${options.dartLangComment}
+
+        class C {
+          C(int this.field);
+
+          int methodFieldAccess(int x) {
+            var inScope = 1;
+            {
+              var innerInScope = global + staticField + field;
+              /* evaluation placeholder */
+              print(innerInScope);
+              var innerNotInScope = 2;
+            }
+            var notInScope = 3;
+          }
+
+          static int staticField = 0;
+          int field;
+        }
+
+        int global = 42;
+        main() => 0;
+        ''';
+
+      TestDriver driver;
+
+      setUp(() {
+        driver = TestDriver(options, source);
+      });
+
+      tearDown(() {
+        driver.delete();
+      });
+
+      test('local in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'inScope',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return inScope;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('local in inner scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'innerInScope',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return innerInScope;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('global in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'global',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return foo.global;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('static field in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'staticField',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return foo.C.staticField;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('field in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'field',
+            expectedResult: '''
+            (function(inScope, innerInScope) {
+              return this.field;
+            }.bind(this)(
+              1,
+              0
+            ))
+            ''');
+      });
+
+      test('local not in scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'notInScope',
+            expectedError:
+                "Error: The getter 'notInScope' isn't defined for the class 'C'.");
+      });
+
+      test('local not in inner scope', () async {
+        await driver.check(
+            scope: <String, String>{'inScope': '1', 'innerInScope': '0'},
+            expression: 'innerNotInScope',
+            expectedError:
+                "Error: The getter 'innerNotInScope' isn't defined for the class 'C'.");
+      });
+    });
+
     group('Expression compiler tests in extension method:', () {
       var source = '''
         ${options.dartLangComment}
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 68357f8..a8af750 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
@@ -119,7 +119,7 @@
 extension NumberParsing on String {
   int parseInt() {
     var ret = int.parse(this);
-    // line 17
+    // line 18
     return ret;
   }
 }
@@ -336,7 +336,7 @@
       requestController.add({
         'command': 'CompileExpression',
         'expression': 'ret',
-        'line': 17,
+        'line': 18,
         'column': 1,
         'jsModules': {},
         'jsScope': {'ret': 'ret'},
diff --git a/tools/VERSION b/tools/VERSION
index 5346579d..7b3ba46 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 204
+PRERELEASE 205
 PRERELEASE_PATCH 0
\ No newline at end of file