Version 2.15.0-287.0.dev

Merge commit '67baa5848a2c70cde9e0e65e13f5878f01725473' into 'dev'
diff --git a/DEPS b/DEPS
index a514e15..5709b47 100644
--- a/DEPS
+++ b/DEPS
@@ -656,6 +656,15 @@
       ],
           "dep_type": "cipd",
   },
+  Var("dart_root") + "/benchmarks/NativeCall/native/out/": {
+      "packages": [
+          {
+              "package": "dart/benchmarks/nativecall",
+              "version": "w1JKzCIHSfDNIjqnioMUPq0moCXKwX67aUfhyrvw4E0C",
+          },
+      ],
+          "dep_type": "cipd",
+  },
   Var("dart_root") + "/third_party/browsers/chrome": {
       "packages": [
           {
diff --git a/benchmarks/NativeCall/dart/NativeCall.dart b/benchmarks/NativeCall/dart/NativeCall.dart
new file mode 100644
index 0000000..c453038
--- /dev/null
+++ b/benchmarks/NativeCall/dart/NativeCall.dart
@@ -0,0 +1,277 @@
+// 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.
+
+// These micro benchmarks track the speed of native calls.
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+
+import 'dlopen_helper.dart';
+
+// Number of benchmark iterations per function.
+const N = 1000;
+
+// The native library that holds all the native functions being called.
+final nativeFunctionsLib = dlopenPlatformSpecific('native_functions',
+    path: Platform.script.resolve('../native/out/').path);
+
+final getRootLibraryUrl = nativeFunctionsLib
+    .lookupFunction<Handle Function(), Object Function()>('GetRootLibraryUrl');
+
+final setNativeResolverForTest = nativeFunctionsLib.lookupFunction<
+    Void Function(Handle), void Function(Object)>('SetNativeResolverForTest');
+
+//
+// Benchmark fixtures.
+//
+
+abstract class NativeCallBenchmarkBase extends BenchmarkBase {
+  NativeCallBenchmarkBase(String name) : super(name);
+
+  void expectEquals(actual, expected) {
+    if (actual != expected) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+
+  void expectApprox(actual, expected) {
+    if (0.999 * expected > actual || actual > 1.001 * expected) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+
+  void expectIdentical(actual, expected) {
+    if (!identical(actual, expected)) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+}
+
+class Uint8x01 extends NativeCallBenchmarkBase {
+  Uint8x01() : super('NativeCall.Uint8x01');
+
+  @pragma('vm:external-name', 'Function1Uint8')
+  external static int f(int a);
+
+  @override
+  void run() {
+    int x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(17);
+    }
+    expectEquals(x, N * 17 + N * 42);
+  }
+}
+
+class Int64x20 extends NativeCallBenchmarkBase {
+  Int64x20() : super('NativeCall.Int64x20');
+
+  @pragma('vm:external-name', 'Function20Int64')
+  external static int f(
+      int a,
+      int b,
+      int c,
+      int d,
+      int e,
+      int f,
+      int g,
+      int h,
+      int i,
+      int j,
+      int k,
+      int l,
+      int m,
+      int n,
+      int o,
+      int p,
+      int q,
+      int r,
+      int s,
+      int t);
+
+  @override
+  void run() {
+    int x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
+    }
+    expectEquals(x, N * (N - 1) * 20 / 2);
+  }
+}
+
+class Doublex01 extends NativeCallBenchmarkBase {
+  Doublex01() : super('NativeCall.Doublex01');
+
+  @pragma('vm:external-name', 'Function1Double')
+  external static double f(double a);
+
+  @override
+  void run() {
+    double x = 0.0;
+    for (int i = 0; i < N; i++) {
+      x += f(17.0);
+    }
+    final double expected = N * (17.0 + 42.0);
+    expectApprox(x, expected);
+  }
+}
+
+class Doublex20 extends NativeCallBenchmarkBase {
+  Doublex20() : super('NativeCall.Doublex20');
+
+  @pragma('vm:external-name', 'Function20Double')
+  external static double f(
+      double a,
+      double b,
+      double c,
+      double d,
+      double e,
+      double f,
+      double g,
+      double h,
+      double i,
+      double j,
+      double k,
+      double l,
+      double m,
+      double n,
+      double o,
+      double p,
+      double q,
+      double r,
+      double s,
+      double t);
+
+  @override
+  void run() {
+    double x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
+          13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0);
+    }
+    final double expected = N *
+        (1.0 +
+            2.0 +
+            3.0 +
+            4.0 +
+            5.0 +
+            6.0 +
+            7.0 +
+            8.0 +
+            9.0 +
+            10.0 +
+            11.0 +
+            12.0 +
+            13.0 +
+            14.0 +
+            15.0 +
+            16.0 +
+            17.0 +
+            18.0 +
+            19.0 +
+            20.0);
+    expectApprox(x, expected);
+  }
+}
+
+class MyClass {
+  int a;
+  MyClass(this.a);
+}
+
+class Handlex01 extends NativeCallBenchmarkBase {
+  Handlex01() : super('NativeCall.Handlex01');
+
+  @pragma('vm:external-name', 'Function1Handle')
+  external static Object f(Object a);
+
+  @override
+  void run() {
+    final p1 = MyClass(123);
+    Object x = p1;
+    for (int i = 0; i < N; i++) {
+      x = f(x);
+    }
+    expectIdentical(x, p1);
+  }
+}
+
+class Handlex20 extends NativeCallBenchmarkBase {
+  Handlex20() : super('NativeCall.Handlex20');
+
+  @pragma('vm:external-name', 'Function20Handle')
+  external static Object f(
+      Object a,
+      Object b,
+      Object c,
+      Object d,
+      Object e,
+      Object f,
+      Object g,
+      Object h,
+      Object i,
+      Object j,
+      Object k,
+      Object l,
+      Object m,
+      Object n,
+      Object o,
+      Object p,
+      Object q,
+      Object r,
+      Object s,
+      Object t);
+
+  @override
+  void run() {
+    final p1 = MyClass(123);
+    final p2 = MyClass(2);
+    final p3 = MyClass(3);
+    final p4 = MyClass(4);
+    final p5 = MyClass(5);
+    final p6 = MyClass(6);
+    final p7 = MyClass(7);
+    final p8 = MyClass(8);
+    final p9 = MyClass(9);
+    final p10 = MyClass(10);
+    final p11 = MyClass(11);
+    final p12 = MyClass(12);
+    final p13 = MyClass(13);
+    final p14 = MyClass(14);
+    final p15 = MyClass(15);
+    final p16 = MyClass(16);
+    final p17 = MyClass(17);
+    final p18 = MyClass(18);
+    final p19 = MyClass(19);
+    final p20 = MyClass(20);
+    Object x = p1;
+    for (int i = 0; i < N; i++) {
+      x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
+          p16, p17, p18, p19, p20);
+    }
+    expectIdentical(x, p1);
+  }
+}
+
+//
+// Main driver.
+//
+
+void main() {
+  setNativeResolverForTest(getRootLibraryUrl());
+
+  final benchmarks = [
+    () => Uint8x01(),
+    () => Int64x20(),
+    () => Doublex01(),
+    () => Doublex20(),
+    () => Handlex01(),
+    () => Handlex20(),
+  ];
+  for (final benchmark in benchmarks) {
+    benchmark().report();
+  }
+}
diff --git a/benchmarks/NativeCall/dart/dlopen_helper.dart b/benchmarks/NativeCall/dart/dlopen_helper.dart
new file mode 100644
index 0000000..7e6129e
--- /dev/null
+++ b/benchmarks/NativeCall/dart/dlopen_helper.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+import 'dart:io';
+
+const arm = 'arm';
+const arm64 = 'arm64';
+const ia32 = 'ia32';
+const x64 = 'x64';
+
+// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m
+final _unames = {
+  'arm': arm,
+  'aarch64_be': arm64,
+  'aarch64': arm64,
+  'armv8b': arm64,
+  'armv8l': arm64,
+  'i386': ia32,
+  'i686': ia32,
+  'x86_64': x64,
+};
+
+String _checkRunningMode(String architecture) {
+  // Check if we're running in 32bit mode.
+  final int pointerSize = sizeOf<IntPtr>();
+  if (pointerSize == 4 && architecture == x64) return ia32;
+  if (pointerSize == 4 && architecture == arm64) return arm;
+
+  return architecture;
+}
+
+String _architecture() {
+  final String uname = Process.runSync('uname', ['-m']).stdout.trim();
+  final String? architecture = _unames[uname];
+  if (architecture == null) {
+    throw Exception('Unrecognized architecture: "$uname"');
+  }
+
+  // Check if we're running in 32bit mode.
+  return _checkRunningMode(architecture);
+}
+
+String _platformPath(String name, String path) {
+  if (Platform.isMacOS || Platform.isIOS) {
+    return '${path}mac/${_architecture()}/lib$name.dylib';
+  }
+
+  if (Platform.isWindows) {
+    return '${path}win/${_checkRunningMode(x64)}/$name.dll';
+  }
+
+  // Unknown platforms default to Unix implementation.
+  return '${path}linux/${_architecture()}/lib$name.so';
+}
+
+DynamicLibrary dlopenPlatformSpecific(String name, {String path = ''}) {
+  final String fullPath = _platformPath(name, path);
+  return DynamicLibrary.open(fullPath);
+}
diff --git a/benchmarks/NativeCall/dart2/NativeCall.dart b/benchmarks/NativeCall/dart2/NativeCall.dart
new file mode 100644
index 0000000..c2dbf13
--- /dev/null
+++ b/benchmarks/NativeCall/dart2/NativeCall.dart
@@ -0,0 +1,279 @@
+// 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.
+
+// These micro benchmarks track the speed of native calls.
+
+// @dart=2.9
+
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+
+import 'dlopen_helper.dart';
+
+// Number of benchmark iterations per function.
+const N = 1000;
+
+// The native library that holds all the native functions being called.
+final nativeFunctionsLib = dlopenPlatformSpecific('native_functions',
+    path: Platform.script.resolve('../native/out/').path);
+
+final getRootLibraryUrl = nativeFunctionsLib
+    .lookupFunction<Handle Function(), Object Function()>('GetRootLibraryUrl');
+
+final setNativeResolverForTest = nativeFunctionsLib.lookupFunction<
+    Void Function(Handle), void Function(Object)>('SetNativeResolverForTest');
+
+//
+// Benchmark fixtures.
+//
+
+abstract class NativeCallBenchmarkBase extends BenchmarkBase {
+  NativeCallBenchmarkBase(String name) : super(name);
+
+  void expectEquals(actual, expected) {
+    if (actual != expected) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+
+  void expectApprox(actual, expected) {
+    if (0.999 * expected > actual || actual > 1.001 * expected) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+
+  void expectIdentical(actual, expected) {
+    if (!identical(actual, expected)) {
+      throw Exception('$name: Unexpected result: $actual, expected $expected');
+    }
+  }
+}
+
+class Uint8x01 extends NativeCallBenchmarkBase {
+  Uint8x01() : super('NativeCall.Uint8x01');
+
+  @pragma('vm:external-name', 'Function1Uint8')
+  external static int f(int a);
+
+  @override
+  void run() {
+    int x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(17);
+    }
+    expectEquals(x, N * 17 + N * 42);
+  }
+}
+
+class Int64x20 extends NativeCallBenchmarkBase {
+  Int64x20() : super('NativeCall.Int64x20');
+
+  @pragma('vm:external-name', 'Function20Int64')
+  external static int f(
+      int a,
+      int b,
+      int c,
+      int d,
+      int e,
+      int f,
+      int g,
+      int h,
+      int i,
+      int j,
+      int k,
+      int l,
+      int m,
+      int n,
+      int o,
+      int p,
+      int q,
+      int r,
+      int s,
+      int t);
+
+  @override
+  void run() {
+    int x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
+    }
+    expectEquals(x, N * (N - 1) * 20 / 2);
+  }
+}
+
+class Doublex01 extends NativeCallBenchmarkBase {
+  Doublex01() : super('NativeCall.Doublex01');
+
+  @pragma('vm:external-name', 'Function1Double')
+  external static double f(double a);
+
+  @override
+  void run() {
+    double x = 0.0;
+    for (int i = 0; i < N; i++) {
+      x += f(17.0);
+    }
+    final double expected = N * (17.0 + 42.0);
+    expectApprox(x, expected);
+  }
+}
+
+class Doublex20 extends NativeCallBenchmarkBase {
+  Doublex20() : super('NativeCall.Doublex20');
+
+  @pragma('vm:external-name', 'Function20Double')
+  external static double f(
+      double a,
+      double b,
+      double c,
+      double d,
+      double e,
+      double f,
+      double g,
+      double h,
+      double i,
+      double j,
+      double k,
+      double l,
+      double m,
+      double n,
+      double o,
+      double p,
+      double q,
+      double r,
+      double s,
+      double t);
+
+  @override
+  void run() {
+    double x = 0;
+    for (int i = 0; i < N; i++) {
+      x += f(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
+          13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0);
+    }
+    final double expected = N *
+        (1.0 +
+            2.0 +
+            3.0 +
+            4.0 +
+            5.0 +
+            6.0 +
+            7.0 +
+            8.0 +
+            9.0 +
+            10.0 +
+            11.0 +
+            12.0 +
+            13.0 +
+            14.0 +
+            15.0 +
+            16.0 +
+            17.0 +
+            18.0 +
+            19.0 +
+            20.0);
+    expectApprox(x, expected);
+  }
+}
+
+class MyClass {
+  int a;
+  MyClass(this.a);
+}
+
+class Handlex01 extends NativeCallBenchmarkBase {
+  Handlex01() : super('NativeCall.Handlex01');
+
+  @pragma('vm:external-name', 'Function1Handle')
+  external static Object f(Object a);
+
+  @override
+  void run() {
+    final p1 = MyClass(123);
+    Object x = p1;
+    for (int i = 0; i < N; i++) {
+      x = f(x);
+    }
+    expectIdentical(x, p1);
+  }
+}
+
+class Handlex20 extends NativeCallBenchmarkBase {
+  Handlex20() : super('NativeCall.Handlex20');
+
+  @pragma('vm:external-name', 'Function20Handle')
+  external static Object f(
+      Object a,
+      Object b,
+      Object c,
+      Object d,
+      Object e,
+      Object f,
+      Object g,
+      Object h,
+      Object i,
+      Object j,
+      Object k,
+      Object l,
+      Object m,
+      Object n,
+      Object o,
+      Object p,
+      Object q,
+      Object r,
+      Object s,
+      Object t);
+
+  @override
+  void run() {
+    final p1 = MyClass(123);
+    final p2 = MyClass(2);
+    final p3 = MyClass(3);
+    final p4 = MyClass(4);
+    final p5 = MyClass(5);
+    final p6 = MyClass(6);
+    final p7 = MyClass(7);
+    final p8 = MyClass(8);
+    final p9 = MyClass(9);
+    final p10 = MyClass(10);
+    final p11 = MyClass(11);
+    final p12 = MyClass(12);
+    final p13 = MyClass(13);
+    final p14 = MyClass(14);
+    final p15 = MyClass(15);
+    final p16 = MyClass(16);
+    final p17 = MyClass(17);
+    final p18 = MyClass(18);
+    final p19 = MyClass(19);
+    final p20 = MyClass(20);
+    Object x = p1;
+    for (int i = 0; i < N; i++) {
+      x = f(x, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15,
+          p16, p17, p18, p19, p20);
+    }
+    expectIdentical(x, p1);
+  }
+}
+
+//
+// Main driver.
+//
+
+void main() {
+  setNativeResolverForTest(getRootLibraryUrl());
+
+  final benchmarks = [
+    () => Uint8x01(),
+    () => Int64x20(),
+    () => Doublex01(),
+    () => Doublex20(),
+    () => Handlex01(),
+    () => Handlex20(),
+  ];
+  for (final benchmark in benchmarks) {
+    benchmark().report();
+  }
+}
diff --git a/benchmarks/NativeCall/dart2/dlopen_helper.dart b/benchmarks/NativeCall/dart2/dlopen_helper.dart
new file mode 100644
index 0000000..e6e3bfa
--- /dev/null
+++ b/benchmarks/NativeCall/dart2/dlopen_helper.dart
@@ -0,0 +1,63 @@
+// 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.9
+
+import 'dart:ffi';
+import 'dart:io';
+
+const arm = 'arm';
+const arm64 = 'arm64';
+const ia32 = 'ia32';
+const x64 = 'x64';
+
+// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m
+final _unames = {
+  'arm': arm,
+  'aarch64_be': arm64,
+  'aarch64': arm64,
+  'armv8b': arm64,
+  'armv8l': arm64,
+  'i386': ia32,
+  'i686': ia32,
+  'x86_64': x64,
+};
+
+String _checkRunningMode(String architecture) {
+  // Check if we're running in 32bit mode.
+  final int pointerSize = sizeOf<IntPtr>();
+  if (pointerSize == 4 && architecture == x64) return ia32;
+  if (pointerSize == 4 && architecture == arm64) return arm;
+
+  return architecture;
+}
+
+String _architecture() {
+  final String uname = Process.runSync('uname', ['-m']).stdout.trim();
+  final String architecture = _unames[uname];
+  if (architecture == null) {
+    throw Exception('Unrecognized architecture: "$uname"');
+  }
+
+  // Check if we're running in 32bit mode.
+  return _checkRunningMode(architecture);
+}
+
+String _platformPath(String name, {String path = ''}) {
+  if (Platform.isMacOS || Platform.isIOS) {
+    return '${path}mac/${_architecture()}/lib$name.dylib';
+  }
+
+  if (Platform.isWindows) {
+    return '${path}win/${_checkRunningMode(x64)}/$name.dll';
+  }
+
+  // Unknown platforms default to Unix implementation.
+  return '${path}linux/${_architecture()}/lib$name.so';
+}
+
+DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
+  final String fullPath = _platformPath(name, path: path);
+  return DynamicLibrary.open(fullPath);
+}
diff --git a/benchmarks/NativeCall/native/.gitignore b/benchmarks/NativeCall/native/.gitignore
new file mode 100644
index 0000000..e778c6f
--- /dev/null
+++ b/benchmarks/NativeCall/native/.gitignore
@@ -0,0 +1,5 @@
+# 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.
+
+out/
diff --git a/benchmarks/NativeCall/native/Makefile b/benchmarks/NativeCall/native/Makefile
new file mode 100644
index 0000000..9a2eb44
--- /dev/null
+++ b/benchmarks/NativeCall/native/Makefile
@@ -0,0 +1,57 @@
+# 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.
+
+# TODO(37531): Remove this makefile and build with sdk instead when
+# benchmark runner gets support for that.
+
+CC=gcc
+CCARM=arm-linux-gnueabihf-gcc
+CCARM64=aarch64-linux-gnu-gcc
+CFLAGS=-Wall -g -O -fPIC -I../../../runtime/
+
+.PHONY: all clean
+
+all: out/linux/x64/libnative_functions.so out/linux/ia32/libnative_functions.so out/linux/arm64/libnative_functions.so out/linux/arm/libnative_functions.so
+
+cipd:
+	cipd create -name dart/benchmarks/nativecall -in out -install-mode copy
+
+clean:
+	rm -rf *.o *.so out
+
+out/linux/x64:
+	mkdir -p out/linux/x64
+
+out/linux/x64/native_functions.o: native_functions.c | out/linux/x64
+	$(CC) $(CFLAGS) -c -o $@ native_functions.c
+
+out/linux/x64/libnative_functions.so: out/linux/x64/native_functions.o
+	$(CC) $(CFLAGS) -s -shared -o $@ out/linux/x64/native_functions.o
+
+out/linux/ia32:
+	mkdir -p out/linux/ia32
+
+out/linux/ia32/native_functions.o: native_functions.c | out/linux/ia32
+	$(CC) $(CFLAGS) -m32 -c -o $@ native_functions.c
+
+out/linux/ia32/libnative_functions.so: out/linux/ia32/native_functions.o
+	$(CC) $(CFLAGS) -m32 -s -shared -o $@ out/linux/ia32/native_functions.o
+
+out/linux/arm64:
+	mkdir -p out/linux/arm64
+
+out/linux/arm64/native_functions.o: native_functions.c | out/linux/arm64
+	$(CCARM64) $(CFLAGS) -c -o $@ native_functions.c
+
+out/linux/arm64/libnative_functions.so: out/linux/arm64/native_functions.o
+	$(CCARM64) $(CFLAGS) -s -shared -o $@ out/linux/arm64/native_functions.o
+
+out/linux/arm:
+	mkdir -p out/linux/arm
+
+out/linux/arm/native_functions.o: native_functions.c | out/linux/arm
+	$(CCARM) $(CFLAGS) -c -o $@ native_functions.c
+
+out/linux/arm/libnative_functions.so: out/linux/arm/native_functions.o
+	$(CCARM) $(CFLAGS) -s -shared -o $@ out/linux/arm/native_functions.o
diff --git a/benchmarks/NativeCall/native/native_functions.c b/benchmarks/NativeCall/native/native_functions.c
new file mode 100644
index 0000000..c19251d
--- /dev/null
+++ b/benchmarks/NativeCall/native/native_functions.c
@@ -0,0 +1,119 @@
+// 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.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// TODO(dartbug.com/40579): This requires static linking to either link
+// dart.exe or dart_precompiled_runtime.exe on Windows.
+// The sample currently fails on Windows in AOT mode.
+#include "include/dart_api.h"
+
+#define ENSURE(X)                                                              \
+  if (!(X)) {                                                                  \
+    fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check failed: " #X);   \
+    exit(1);                                                                   \
+  }
+
+#define ENSURE_VALID(X) ENSURE(!Dart_IsError(X))
+
+//
+// Functions under test.
+//
+
+void Function1Uint8(Dart_NativeArguments args) {
+  int64_t arg = 0;
+  Dart_GetNativeIntegerArgument(args, /*index=*/0, &arg);
+  Dart_SetIntegerReturnValue(args, arg + 42);
+}
+
+void Function20Int64(Dart_NativeArguments args) {
+  int64_t arg = 0;
+  int64_t result = 0;
+  for (int i = 0; i < 20; i++) {
+    Dart_GetNativeIntegerArgument(args, /*index=*/i, &arg);
+    result += arg;
+  }
+  Dart_SetIntegerReturnValue(args, result);
+}
+
+void Function1Double(Dart_NativeArguments args) {
+  double arg = 0.0;
+  Dart_GetNativeDoubleArgument(args, /*index=*/0, &arg);
+  Dart_SetDoubleReturnValue(args, arg + 42.0);
+}
+
+void Function20Double(Dart_NativeArguments args) {
+  double arg = 0;
+  double result = 0;
+  for (int i = 0; i < 20; i++) {
+    Dart_GetNativeDoubleArgument(args, /*index=*/i, &arg);
+    result += arg;
+  }
+  Dart_SetDoubleReturnValue(args, result);
+}
+
+void Function1Handle(Dart_NativeArguments args) {
+  Dart_Handle arg = Dart_GetNativeArgument(args, /*index=*/0);
+  Dart_SetReturnValue(args, arg);
+}
+
+void Function20Handle(Dart_NativeArguments args) {
+  Dart_Handle arg = Dart_GetNativeArgument(args, /*index=*/0);
+  Dart_SetReturnValue(args, arg);
+}
+
+//
+// Test helpers.
+//
+
+DART_EXPORT Dart_Handle GetRootLibraryUrl() {
+  Dart_Handle root_lib = Dart_RootLibrary();
+  Dart_Handle lib_url = Dart_LibraryUrl(root_lib);
+  ENSURE_VALID(lib_url);
+  return lib_url;
+}
+
+Dart_NativeFunction NativeEntryResolver(Dart_Handle name,
+                                        int num_of_arguments,
+                                        bool* auto_setup_scope) {
+  ENSURE(Dart_IsString(name));
+
+  ENSURE(auto_setup_scope != NULL);
+  *auto_setup_scope = true;
+
+  const char* name_str = NULL;
+  ENSURE_VALID(Dart_StringToCString(name, &name_str));
+
+  if (strcmp(name_str, "Function1Uint8") == 0 && num_of_arguments == 1) {
+    return &Function1Uint8;
+  } else if (strcmp(name_str, "Function20Int64") == 0 &&
+             num_of_arguments == 20) {
+    return &Function20Int64;
+  } else if (strcmp(name_str, "Function1Double") == 0 &&
+             num_of_arguments == 1) {
+    return &Function1Double;
+  } else if (strcmp(name_str, "Function20Double") == 0 &&
+             num_of_arguments == 20) {
+    return &Function20Double;
+  } else if (strcmp(name_str, "Function1Handle") == 0 &&
+             num_of_arguments == 1) {
+    return &Function1Handle;
+  } else if (strcmp(name_str, "Function20Handle") == 0 &&
+             num_of_arguments == 20) {
+    return &Function20Handle;
+  }
+
+  // Unreachable in benchmark.
+  ENSURE(false);
+}
+
+DART_EXPORT void SetNativeResolverForTest(Dart_Handle url) {
+  Dart_Handle library = Dart_LookupLibrary(url);
+  ENSURE_VALID(library);
+  Dart_Handle result =
+      Dart_SetNativeResolver(library, &NativeEntryResolver, NULL);
+  ENSURE_VALID(result);
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index 56442ae..96389a0 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -239,6 +239,10 @@
 
   final AstNode node;
 
+  /// A map keyed by lock names whose value is a list of the ranges for which a
+  /// lock has already been acquired.
+  final Map<String, List<SourceRange>> _lockRanges = {};
+
   CorrectionProducerContext._({
     required this.resolvedResult,
     required this.workspace,
@@ -454,6 +458,26 @@
 
   CorrectionUtils get utils => _context.utils;
 
+  /// Return `true` if this is the first request to lock the given [range] for
+  /// the lock with the given [lockName], or false if a lock for that range has
+  /// already been acquired.
+  ///
+  /// This method is used to allow correction producers to guard against
+  /// repeating changes that have already been made. For example, if multiple
+  /// arguments in an argument list have diagnostics reported against them and
+  /// the fix will fix all of the arguments, then the fix should be applied for
+  /// the first diagnostic and not for the others. A correction producer can
+  /// ensure this behavior by attempting to acquire a lock prior to creating any
+  /// edits, and only create the edits if a lock could be acquired.
+  bool acquireLockOnRange(String lockName, SourceRange range) {
+    var ranges = _context._lockRanges.putIfAbsent(lockName, () => []);
+    if (ranges.contains(range)) {
+      return false;
+    }
+    ranges.add(range);
+    return true;
+  }
+
   /// Configure this producer based on the [context].
   void configure(CorrectionProducerContext context) {
     _context = context;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
index c71ea13..6ea735c 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/modify_parameters.dart
@@ -59,6 +59,9 @@
       // This should only happen if `validate` didn't check this case.
       return;
     }
+    if (!fix.acquireLockOnRange('ModifyParameters', range.node(argumentList))) {
+      return;
+    }
     var arguments = argumentList.arguments;
     var argumentCount = arguments.length;
     var templateContext = TemplateContext(invocation, fix.utils);
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 32ea11f..f0cabd7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -29,6 +29,7 @@
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/analysis/status.dart';
 import 'package:analyzer/src/dart/analysis/testing_data.dart';
+import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/diagnostic/diagnostic.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/exception/exception.dart';
@@ -42,6 +43,7 @@
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/summary2/ast_binary_flags.dart';
 import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/src/util/performance/operation_performance.dart';
 import 'package:meta/meta.dart';
 
 /// This class computes [AnalysisResult]s for Dart files.
@@ -1281,6 +1283,62 @@
     }
   }
 
+  ResolvedForCompletionResultImpl? resolveForCompletion({
+    required String path,
+    required int offset,
+    required OperationPerformanceImpl performance,
+  }) {
+    if (!_isAbsolutePath(path)) {
+      return null;
+    }
+
+    if (!_fsState.hasUri(path)) {
+      return null;
+    }
+
+    // Process pending changes.
+    while (_fileTracker.verifyChangedFilesIfNeeded()) {}
+
+    var file = _fsState.getFileForPath(path);
+
+    var library = file.isPart ? file.library : file;
+    if (library == null) {
+      return null;
+    }
+
+    libraryContext.load2(library);
+    var unitElement = libraryContext.computeUnitElement(library, file)
+        as CompilationUnitElementImpl;
+
+    var analyzer = LibraryAnalyzer(
+        analysisOptions as AnalysisOptionsImpl,
+        declaredVariables,
+        sourceFactory,
+        libraryContext.analysisContext,
+        libraryContext.elementFactory.libraryOfUri2(library.uriStr),
+        libraryContext.analysisSession.inheritanceManager,
+        library,
+        testingData: testingData);
+
+    var analysisResult = analyzer.analyzeForCompletion(
+      file: file,
+      offset: offset,
+      unitElement: unitElement,
+      performance: performance,
+    );
+
+    return ResolvedForCompletionResultImpl(
+      path: path,
+      uri: file.uri,
+      exists: file.exists,
+      content: file.content,
+      lineInfo: file.lineInfo,
+      parsedUnit: analysisResult.parsedUnit,
+      unitElement: unitElement,
+      resolvedNodes: analysisResult.resolvedNodes,
+    );
+  }
+
   void _addDeclaredVariablesToSignature(ApiSignature buffer) {
     var variableNames = declaredVariables.variableNames;
     buffer.addInt(variableNames.length);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 315b856..588e474 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -47,6 +47,7 @@
 import 'package:analyzer/src/services/lint.dart';
 import 'package:analyzer/src/summary/package_bundle_reader.dart';
 import 'package:analyzer/src/task/strong/checker.dart';
+import 'package:analyzer/src/util/performance/operation_performance.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 var timerLibraryAnalyzer = Stopwatch();
@@ -56,6 +57,16 @@
 var timerLibraryAnalyzerSplicer = Stopwatch();
 var timerLibraryAnalyzerVerify = Stopwatch();
 
+class AnalysisForCompletionResult {
+  final CompilationUnit parsedUnit;
+  final List<AstNode> resolvedNodes;
+
+  AnalysisForCompletionResult({
+    required this.parsedUnit,
+    required this.resolvedNodes,
+  });
+}
+
 /// Analyzer of a single library.
 class LibraryAnalyzer {
   /// A marker object used to prevent the initialization of
@@ -191,6 +202,84 @@
     return results;
   }
 
+  AnalysisForCompletionResult analyzeForCompletion({
+    required FileState file,
+    required int offset,
+    required CompilationUnitElementImpl unitElement,
+    required OperationPerformanceImpl performance,
+  }) {
+    var parsedUnit = performance.run('parse', (performance) {
+      return _parse(file);
+    });
+
+    var node = NodeLocator(offset).searchWithin(parsedUnit);
+
+    if (_hasEmptyCompletionContext(node)) {
+      return AnalysisForCompletionResult(
+        parsedUnit: parsedUnit,
+        resolvedNodes: [],
+      );
+    }
+
+    var errorListener = RecordingErrorListener();
+
+    return performance.run('resolve', (performance) {
+      // TODO(scheglov) We don't need to do this for the whole unit.
+      parsedUnit.accept(
+        ResolutionVisitor(
+          unitElement: unitElement,
+          errorListener: errorListener,
+          featureSet: _libraryElement.featureSet,
+          nameScope: _libraryElement.scope,
+          elementWalker: ElementWalker.forCompilationUnit(
+            unitElement,
+            libraryFilePath: _library.path,
+            unitFilePath: file.path,
+          ),
+        ),
+      );
+
+      // TODO(scheglov) We don't need to do this for the whole unit.
+      parsedUnit.accept(ScopeResolverVisitor(
+          _libraryElement, file.source, _typeProvider, errorListener,
+          nameScope: _libraryElement.scope));
+
+      FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
+          _typeSystem, _testingData != null, _libraryElement.featureSet);
+      _testingData?.recordFlowAnalysisDataForTesting(
+          file.uri, flowAnalysisHelper.dataForTesting!);
+
+      var resolverVisitor = ResolverVisitor(_inheritance, _libraryElement,
+          file.source, _typeProvider, errorListener,
+          featureSet: _libraryElement.featureSet,
+          flowAnalysisHelper: flowAnalysisHelper);
+
+      var nodeToResolve = node?.thisOrAncestorMatching((e) {
+        return e.parent is ClassDeclaration ||
+            e.parent is CompilationUnit ||
+            e.parent is ExtensionDeclaration ||
+            e.parent is MixinDeclaration;
+      });
+      if (nodeToResolve != null) {
+        var can = resolverVisitor.prepareForResolving(nodeToResolve);
+        if (can) {
+          nodeToResolve.accept(resolverVisitor);
+          return AnalysisForCompletionResult(
+            parsedUnit: parsedUnit,
+            resolvedNodes: [nodeToResolve],
+          );
+        }
+      }
+
+      var resolvedUnits = analyze();
+      var resolvedUnit = resolvedUnits[file]!;
+      return AnalysisForCompletionResult(
+        parsedUnit: resolvedUnit.unit,
+        resolvedNodes: [resolvedUnit.unit],
+      );
+    });
+  }
+
   void _checkForInconsistentLanguageVersionOverride(
     Map<FileState, CompilationUnit> units,
   ) {
@@ -813,6 +902,54 @@
       }
     }
   }
+
+  static bool _hasEmptyCompletionContext(AstNode? node) {
+    if (node is DoubleLiteral || node is IntegerLiteral) {
+      return true;
+    }
+
+    if (node is SimpleIdentifier) {
+      var parent = node.parent;
+
+      if (parent is ConstructorDeclaration && parent.name == node) {
+        return true;
+      }
+
+      if (parent is ConstructorFieldInitializer && parent.fieldName == node) {
+        return true;
+      }
+
+      // We have a contributor that looks at the type, but it is syntactic.
+      if (parent is FormalParameter && parent.identifier == node) {
+        return true;
+      }
+
+      if (parent is FunctionDeclaration && parent.name == node) {
+        return true;
+      }
+
+      if (parent is MethodDeclaration && parent.name == node) {
+        return true;
+      }
+
+      // The name of a NamedType does not provide any context.
+      // So, we don't need to resolve anything.
+      if (parent is NamedType) {
+        return true;
+      }
+
+      if (parent is TypeParameter && parent.name == node) {
+        return true;
+      }
+
+      // We have a contributor that looks at the type, but it is syntactic.
+      if (parent is VariableDeclaration && parent.name == node) {
+        return true;
+      }
+    }
+
+    return false;
+  }
 }
 
 /// Analysis result for single file.
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index 4856a55..cc7a673 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -167,6 +167,46 @@
   LineInfo get lineInfo => unit.lineInfo!;
 }
 
+class ResolvedForCompletionResultImpl {
+  final String path;
+  final Uri uri;
+  final bool exists;
+  final String content;
+  final LineInfo lineInfo;
+
+  /// The full parsed unit.
+  final CompilationUnit parsedUnit;
+
+  /// The full element for the unit.
+  final CompilationUnitElement unitElement;
+
+  /// Nodes from [parsedUnit] that were resolved to provide enough context
+  /// to perform completion. How much is enough depends on the location
+  /// where resolution for completion was requested, and our knowledge
+  /// how completion contributors work and what information they expect.
+  ///
+  /// This is usually a small subset of the whole unit - a method, a field.
+  /// It could be even empty if the location does not provide any context
+  /// information for any completion contributor, e.g. a type annotation.
+  /// But it could be the whole unit as well, if the location is not something
+  /// we have an optimization for.
+  ///
+  /// If this list is not empty, then the last node contains the requested
+  /// offset. Other nodes are provided mostly FYI.
+  final List<AstNode> resolvedNodes;
+
+  ResolvedForCompletionResultImpl({
+    required this.path,
+    required this.uri,
+    required this.exists,
+    required this.content,
+    required this.lineInfo,
+    required this.parsedUnit,
+    required this.unitElement,
+    required this.resolvedNodes,
+  });
+}
+
 class ResolvedLibraryResultImpl extends AnalysisResultImpl
     implements ResolvedLibraryResult {
   @override
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 5f87d47..4bc6cdc 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -800,8 +800,10 @@
 
     if (parent is CompilationUnit) {
       return node is ClassDeclaration ||
+          node is Directive ||
           node is ExtensionDeclaration ||
-          node is FunctionDeclaration;
+          node is FunctionDeclaration ||
+          node is TopLevelVariableDeclaration;
     }
 
     void forClassElement(ClassElement parentElement) {
@@ -813,6 +815,11 @@
       return true;
     }
 
+    if (parent is ExtensionDeclaration) {
+      enclosingExtension = parent.declaredElement!;
+      return true;
+    }
+
     if (parent is MixinDeclaration) {
       forClassElement(parent.declaredElement!);
       return true;
diff --git a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
new file mode 100644
index 0000000..e241371
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
@@ -0,0 +1,546 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/results.dart';
+import 'package:analyzer/src/util/performance/operation_performance.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(ResolveForCompletionTest);
+  });
+}
+
+@reflectiveTest
+class ResolveForCompletionTest extends PubPackageResolutionTest {
+  AnalysisDriver get testDriver {
+    return driverFor(testFilePathPlatform);
+  }
+
+  String get testFilePathPlatform => convertPath(testFilePath);
+
+  test_class__fieldDeclaration_type_namedType_name() async {
+    var result = _resolveTestCode(r'''
+class A {
+  var f1 = 0;
+  doub^ f2 = null;
+  var f3 = 1;
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class__fieldDeclaration_type_namedType_typeArgument_name() async {
+    var result = _resolveTestCode(r'''
+class A {
+  var f1 = 0;
+  List<doub^>? f2 = null;
+  var f3 = 1;
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class_extends_name() async {
+    var result = _resolveTestCode(r'''
+class A extends foo^ {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class_fieldDeclaration_initializer() async {
+    var result = _resolveTestCode(r'''
+class A {
+  var f1 = 0;
+  var f2 = foo^;
+  var f3 = 1;
+}
+''');
+
+    result.assertResolvedNodes([
+      'var f2 = foo;',
+    ]);
+  }
+
+  test_class_implements_name() async {
+    var result = _resolveTestCode(r'''
+class A implements foo^ {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class_methodDeclaration_body() async {
+    var result = _resolveTestCode(r'''
+class A {}
+
+class B {
+  void foo1() {}
+
+  void foo2() {
+    print(0);
+    bar^;
+    print(1);
+  }
+
+  void foo3() {}
+}
+''');
+
+    result.assertResolvedNodes([
+      'void foo2() {print(0); bar; print(1);}',
+    ]);
+  }
+
+  test_class_methodDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+class A {
+  void foo^() {
+    print(0);
+  }
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class_methodDeclaration_returnType_name() async {
+    var result = _resolveTestCode(r'''
+class A {
+  doub^ foo() {}
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_class_with_name() async {
+    var result = _resolveTestCode(r'''
+class A with foo^ {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_constructorDeclaration_body() async {
+    var result = _resolveTestCode(r'''
+class A {}
+
+class B {
+  void foo1() {}
+
+  B() {
+    print(0);
+    bar^;
+    print(1);
+  }
+
+  void foo2() {}
+}
+''');
+
+    result.assertResolvedNodes([
+      'B() {print(0); bar; print(1);}',
+    ]);
+  }
+
+  test_constructorDeclaration_fieldInitializer_name() async {
+    var result = _resolveTestCode(r'''
+class A {}
+
+class B {
+  var f;
+
+  void foo1() {}
+
+  B(int a) : bar^ = 0 {
+    print(0);
+  }
+
+  void foo2() {}
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_constructorDeclaration_fieldInitializer_value() async {
+    var result = _resolveTestCode(r'''
+class A {
+  var f;
+
+  A(int a) : f = a + bar^ {
+    print(0);
+  }
+}
+''');
+
+    // TODO(scheglov) Resolve only the initializer.
+    result.assertResolvedNodes([
+      'A(int a) : f = a + bar {print(0);}',
+    ]);
+  }
+
+  test_constructorDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+class A {
+  A.foo^() {
+    print(0);
+  }
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_doubleLiteral() async {
+    var result = _resolveTestCode(r'''
+var v = 1.2^;
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_extension_methodDeclaration_body() async {
+    var result = _resolveTestCode(r'''
+extension E on int {
+  void foo1() {}
+
+  void foo2() {
+    print(0);
+    bar^;
+    print(1);
+  }
+
+  void foo3() {}
+}
+''');
+
+    result.assertResolvedNodes([
+      'void foo2() {print(0); bar; print(1);}',
+    ]);
+  }
+
+  test_extension_methodDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+extension E on int {
+  void foo^() {
+    print(0);
+  }
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_extension_methodDeclaration_returnType_name() async {
+    var result = _resolveTestCode(r'''
+extension E on int {
+  doub^ foo() {}
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_extension_on_name() async {
+    var result = _resolveTestCode(r'''
+extension E on int^ {
+  void foo() {}
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_functionDeclaration_body() async {
+    var result = _resolveTestCode(r'''
+void foo1() {}
+
+void foo2() {
+  print(0);
+  bar^;
+  print(1);
+}
+
+void foo3() {}
+''');
+
+    result.assertResolvedNodes([
+      'void foo2() {print(0); bar; print(1);}',
+    ]);
+  }
+
+  test_functionDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+void foo^() {
+  print(0);
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_functionDeclaration_returnType_name() async {
+    var result = _resolveTestCode(r'''
+doub^ f() {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_importDirective_show_name() async {
+    var result = _resolveTestCode(r'''
+import 'dart:async';
+import 'dart:math' show ^;
+import 'dart:io';
+''');
+
+    result.assertResolvedNodes([
+      "import 'dart:math' show ;",
+    ]);
+  }
+
+  test_importDirective_uri() async {
+    var result = _resolveTestCode(r'''
+import 'dart:async';
+import 'dart:ma^'
+import 'dart:io';
+''');
+
+    result.assertResolvedNodes([
+      "import 'dart:ma';",
+    ]);
+  }
+
+  test_integerLiteral() async {
+    var result = _resolveTestCode(r'''
+var v = 0^;
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_localVariableDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+void f() {
+  var foo^
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_localVariableDeclaration_type_name() async {
+    var result = _resolveTestCode(r'''
+void f() {
+  doub^ a;
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_mixin_implements_name() async {
+    var result = _resolveTestCode(r'''
+mixin M implements foo^ {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_mixin_methodDeclaration_body() async {
+    var result = _resolveTestCode(r'''
+class A {}
+
+mixin M {
+  void foo1() {}
+
+  void foo2() {
+    print(0);
+    bar^;
+    print(1);
+  }
+
+  void foo3() {}
+}
+''');
+
+    result.assertResolvedNodes([
+      'void foo2() {print(0); bar; print(1);}',
+    ]);
+  }
+
+  test_mixin_methodDeclaration_name() async {
+    var result = _resolveTestCode(r'''
+mixin M {
+  void foo^() {
+    print(0);
+  }
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_mixin_methodDeclaration_returnType_name() async {
+    var result = _resolveTestCode(r'''
+mixin M {
+  doub^ foo() {}
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_mixin_on_name() async {
+    var result = _resolveTestCode(r'''
+mixin M on foo^ {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_processPendingChanges() async {
+    newFile(testFilePath, content: 'class A {}');
+
+    // Read the file.
+    testDriver.getFileSync(testFilePathPlatform);
+
+    // Should call `changeFile()`, and the driver must re-read the file.
+    var result = _resolveTestCode(r'''
+var v1 = 0;
+var v2 = v1.^;
+''');
+
+    result.assertResolvedNodes([
+      'var v2 = v1.;',
+    ]);
+  }
+
+  test_simpleFormalParameter_name() async {
+    var result = _resolveTestCode(r'''
+void f(doub^) {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_simpleFormalParameter_type_name() async {
+    var result = _resolveTestCode(r'''
+void f(doub^ a) {}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_topLevelVariable_initializer() async {
+    var result = _resolveTestCode(r'''
+var v1 = 0;
+var v2 = foo^;
+var v3 = 1;
+''');
+
+    result.assertResolvedNodes([
+      'var v2 = foo;',
+    ]);
+  }
+
+  test_topLevelVariable_name() async {
+    var result = _resolveTestCode(r'''
+var v1 = 0;
+var v2^
+var v3 = 0;
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_topLevelVariable_type_namedType_name() async {
+    var result = _resolveTestCode(r'''
+var v1 = 0;
+doub^ v2 = null;
+var v3 = 1;
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_topLevelVariable_type_namedType_typeArgument_name() async {
+    var result = _resolveTestCode(r'''
+var v1 = 0;
+List<doub^>? v2 = null;
+var v3 = 1;
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  test_typedef_name_nothing() async {
+    var result = _resolveTestCode(r'''
+typedef F^
+''');
+
+    _assertWholeUnitResolved(result);
+  }
+
+  test_typeParameter_name() async {
+    var result = _resolveTestCode(r'''
+void f<T^>() {
+  print(0);
+}
+''');
+
+    result.assertResolvedNodes([]);
+  }
+
+  int _newFileWithOffset(String path, String content) {
+    var offset = content.indexOf('^');
+    expect(offset, isNot(equals(-1)), reason: 'missing ^');
+
+    var nextOffset = content.indexOf('^', offset + 1);
+    expect(nextOffset, equals(-1), reason: 'too many ^');
+
+    var before = content.substring(0, offset);
+    var after = content.substring(offset + 1);
+    newFile(path, content: before + after);
+
+    return offset;
+  }
+
+  ResolvedForCompletionResultImpl _resolveTestCode(String content) {
+    var path = testFilePathPlatform;
+    var offset = _newFileWithOffset(path, content);
+    testDriver.changeFile(path);
+
+    var performance = OperationPerformanceImpl('<root>');
+    var result = testDriver.resolveForCompletion(
+      path: path,
+      offset: offset,
+      performance: performance,
+    );
+    return result!;
+  }
+
+  static void _assertWholeUnitResolved(
+    ResolvedForCompletionResultImpl result,
+  ) {
+    expect(result.resolvedNodes, [result.parsedUnit]);
+  }
+}
+
+extension ResolvedForCompletionResultImplExtension
+    on ResolvedForCompletionResultImpl {
+  void assertResolvedNodes(List<String> expected) {
+    var actual = resolvedNodes.map((e) => '$e').toList();
+    expect(actual, expected);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/test_all.dart b/pkg/analyzer/test/src/dart/analysis/test_all.dart
index fbbd8d5..2c5030d 100644
--- a/pkg/analyzer/test/src/dart/analysis/test_all.dart
+++ b/pkg/analyzer/test/src/dart/analysis/test_all.dart
@@ -24,6 +24,7 @@
 import 'index_test.dart' as index;
 import 'mutex_test.dart' as mutex;
 import 'referenced_names_test.dart' as referenced_names;
+import 'resolve_for_completion_test.dart' as resolve_for_completion;
 import 'results/test_all.dart' as results;
 import 'search_test.dart' as search;
 import 'session_helper_test.dart' as session_helper;
@@ -53,6 +54,7 @@
     index.main();
     mutex.main();
     referenced_names.main();
+    resolve_for_completion.main();
     results.main();
     search.main();
     session.main();
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index 27ab909..a98f355 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -129,20 +129,6 @@
   }
 }
 
-Representation FlowGraph::UnboxedFieldRepresentationOf(const Field& field) {
-  switch (field.UnboxedFieldCid()) {
-    case kDoubleCid:
-      return kUnboxedDouble;
-    case kFloat32x4Cid:
-      return kUnboxedFloat32x4;
-    case kFloat64x2Cid:
-      return kUnboxedFloat64x2;
-    default:
-      RELEASE_ASSERT(field.is_non_nullable_integer());
-      return kUnboxedInt64;
-  }
-}
-
 void FlowGraph::ReplaceCurrentInstruction(ForwardInstructionIterator* iterator,
                                           Instruction* current,
                                           Instruction* replacement) {
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index 5aeec58..fe59272 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -155,8 +155,6 @@
 
   static Representation ReturnRepresentationOf(const Function& function);
 
-  static Representation UnboxedFieldRepresentationOf(const Field& field);
-
   // The number of variables (or boxes) inside the functions frame - meaning
   // below the frame pointer.  This does not include the expression stack.
   intptr_t num_stack_locals() const {
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 3531dec..d8ce910 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -203,30 +203,6 @@
   ArchSpecificInitialization();
 }
 
-bool FlowGraphCompiler::IsUnboxedField(const Field& field) {
-  // The `field.is_non_nullable_integer()` is set in the kernel loader and can
-  // only be set if we consume a AOT kernel (annotated with inferred types).
-  ASSERT(!field.is_non_nullable_integer() || FLAG_precompiled_mode);
-  const bool valid_class =
-      ((SupportsUnboxedDoubles() && (field.guarded_cid() == kDoubleCid)) ||
-       (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat32x4Cid)) ||
-       (SupportsUnboxedSimd128() && (field.guarded_cid() == kFloat64x2Cid)) ||
-       field.is_non_nullable_integer());
-  return field.is_unboxing_candidate() && !field.is_nullable() && valid_class;
-}
-
-bool FlowGraphCompiler::IsPotentialUnboxedField(const Field& field) {
-  if (FLAG_precompiled_mode) {
-    // kernel_loader.cc:ReadInferredType sets the guarded cid for fields based
-    // on inferred types from TFA (if available). The guarded cid is therefore
-    // proven to be correct.
-    return IsUnboxedField(field);
-  }
-  return field.is_unboxing_candidate() &&
-         (FlowGraphCompiler::IsUnboxedField(field) ||
-          (field.guarded_cid() == kIllegalCid));
-}
-
 void FlowGraphCompiler::InitCompiler() {
   compressed_stackmaps_builder_ =
       new (zone()) CompressedStackMapsBuilder(zone());
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 25b699c..7669e78 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -465,9 +465,6 @@
   static bool SupportsHardwareDivision();
   static bool CanConvertInt64ToDouble();
 
-  static bool IsUnboxedField(const Field& field);
-  static bool IsPotentialUnboxedField(const Field& field);
-
   // Accessors.
   compiler::Assembler* assembler() const { return assembler_; }
   const ParsedFunction& parsed_function() const { return parsed_function_; }
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 30240ef..2ba0335 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -894,17 +894,17 @@
 
 bool LoadFieldInstr::IsUnboxedDartFieldLoad() const {
   return slot().representation() == kTagged && slot().IsDartField() &&
-         FlowGraphCompiler::IsUnboxedField(slot().field());
+         slot().IsUnboxed();
 }
 
 bool LoadFieldInstr::IsPotentialUnboxedDartFieldLoad() const {
   return slot().representation() == kTagged && slot().IsDartField() &&
-         FlowGraphCompiler::IsPotentialUnboxedField(slot().field());
+         slot().IsPotentialUnboxed();
 }
 
 Representation LoadFieldInstr::representation() const {
   if (IsUnboxedDartFieldLoad() && CompilerState::Current().is_optimizing()) {
-    return FlowGraph::UnboxedFieldRepresentationOf(slot().field());
+    return slot().UnboxedRepresentation();
   }
   return slot().representation();
 }
@@ -963,12 +963,12 @@
 
 bool StoreInstanceFieldInstr::IsUnboxedDartFieldStore() const {
   return slot().representation() == kTagged && slot().IsDartField() &&
-         FlowGraphCompiler::IsUnboxedField(slot().field());
+         slot().IsUnboxed();
 }
 
 bool StoreInstanceFieldInstr::IsPotentialUnboxedDartFieldStore() const {
   return slot().representation() == kTagged && slot().IsDartField() &&
-         FlowGraphCompiler::IsPotentialUnboxedField(slot().field());
+         slot().IsPotentialUnboxed();
 }
 
 Representation StoreInstanceFieldInstr::RequiredInputRepresentation(
@@ -979,7 +979,7 @@
     return kTagged;
   }
   if (IsUnboxedDartFieldStore() && CompilerState::Current().is_optimizing()) {
-    return FlowGraph::UnboxedFieldRepresentationOf(slot().field());
+    return slot().UnboxedRepresentation();
   }
   return slot().representation();
 }
@@ -6145,7 +6145,7 @@
 }
 
 bool Utf8ScanInstr::IsScanFlagsUnboxed() const {
-  return FlowGraphCompiler::IsUnboxedField(scan_flags_field_.field());
+  return scan_flags_field_.IsUnboxed();
 }
 
 InvokeMathCFunctionInstr::InvokeMathCFunctionInstr(
diff --git a/runtime/vm/compiler/backend/slot.cc b/runtime/vm/compiler/backend/slot.cc
index b4b9920..75afb9a 100644
--- a/runtime/vm/compiler/backend/slot.cc
+++ b/runtime/vm/compiler/backend/slot.cc
@@ -331,6 +331,58 @@
   return SlotCache::Instance(thread).Canonicalize(slot);
 }
 
+FieldGuardState::FieldGuardState(const Field& field)
+    : state_(GuardedCidBits::encode(field.guarded_cid()) |
+             IsNonNullableIntegerBit::encode(field.is_non_nullable_integer()) |
+             IsUnboxingCandidateBit::encode(field.is_unboxing_candidate()) |
+             IsNullableBit::encode(field.is_nullable())) {}
+
+bool FieldGuardState::IsUnboxed() const {
+  ASSERT(!is_non_nullable_integer() || FLAG_precompiled_mode);
+  const bool valid_class = ((FlowGraphCompiler::SupportsUnboxedDoubles() &&
+                             (guarded_cid() == kDoubleCid)) ||
+                            (FlowGraphCompiler::SupportsUnboxedSimd128() &&
+                             (guarded_cid() == kFloat32x4Cid)) ||
+                            (FlowGraphCompiler::SupportsUnboxedSimd128() &&
+                             (guarded_cid() == kFloat64x2Cid)) ||
+                            is_non_nullable_integer());
+  return is_unboxing_candidate() && !is_nullable() && valid_class;
+}
+
+bool FieldGuardState::IsPotentialUnboxed() const {
+  if (FLAG_precompiled_mode) {
+    // kernel_loader.cc:ReadInferredType sets the guarded cid for fields based
+    // on inferred types from TFA (if available). The guarded cid is therefore
+    // proven to be correct.
+    return IsUnboxed();
+  }
+
+  return is_unboxing_candidate() &&
+         (IsUnboxed() || (guarded_cid() == kIllegalCid));
+}
+
+bool Slot::IsUnboxed() const {
+  return field_guard_state().IsUnboxed();
+}
+
+bool Slot::IsPotentialUnboxed() const {
+  return field_guard_state().IsPotentialUnboxed();
+}
+
+Representation Slot::UnboxedRepresentation() const {
+  switch (field_guard_state().guarded_cid()) {
+    case kDoubleCid:
+      return kUnboxedDouble;
+    case kFloat32x4Cid:
+      return kUnboxedFloat32x4;
+    case kFloat64x2Cid:
+      return kUnboxedFloat64x2;
+    default:
+      RELEASE_ASSERT(field_guard_state().is_non_nullable_integer());
+      return kUnboxedInt64;
+  }
+}
+
 const Slot& Slot::Get(const Field& field,
                       const ParsedFunction* parsed_function) {
   Thread* thread = Thread::Current();
@@ -354,16 +406,18 @@
     is_nullable = false;
   }
 
+  FieldGuardState field_guard_state(field);
+
   bool used_guarded_state = false;
-  if (field.guarded_cid() != kIllegalCid &&
-      field.guarded_cid() != kDynamicCid) {
+  if (field_guard_state.guarded_cid() != kIllegalCid &&
+      field_guard_state.guarded_cid() != kDynamicCid) {
     // Use guarded state if it is more precise then what we already have.
     if (nullable_cid == kDynamicCid) {
-      nullable_cid = field.guarded_cid();
+      nullable_cid = field_guard_state.guarded_cid();
       used_guarded_state = true;
     }
 
-    if (is_nullable && !field.is_nullable()) {
+    if (is_nullable && !field_guard_state.is_nullable()) {
       is_nullable = false;
       used_guarded_state = true;
     }
@@ -377,10 +431,10 @@
     used_guarded_state = false;
   }
 
-  if (field.is_non_nullable_integer()) {
+  if (field_guard_state.is_non_nullable_integer()) {
     ASSERT(FLAG_precompiled_mode);
     is_nullable = false;
-    if (FlowGraphCompiler::IsUnboxedField(field)) {
+    if (field_guard_state.IsUnboxed()) {
       rep = kUnboxedInt64;
     }
   }
@@ -397,7 +451,7 @@
           IsSentinelVisibleBit::encode(field.is_late() && field.is_final() &&
                                        !field.has_initializer()),
       nullable_cid, compiler::target::Field::OffsetOf(field), &field, &type,
-      rep));
+      rep, field_guard_state));
 
   // If properties of this slot were based on the guarded state make sure
   // to add the field to the list of guarded fields. Note that during background
diff --git a/runtime/vm/compiler/backend/slot.h b/runtime/vm/compiler/backend/slot.h
index a31e9a9..88c1dd8 100644
--- a/runtime/vm/compiler/backend/slot.h
+++ b/runtime/vm/compiler/backend/slot.h
@@ -166,6 +166,35 @@
   NONNULLABLE_BOXED_NATIVE_SLOTS_LIST(V)                                       \
   UNBOXED_NATIVE_SLOTS_LIST(V)
 
+class FieldGuardState {
+ public:
+  FieldGuardState() : state_(0) {}
+  explicit FieldGuardState(const Field& field);
+
+  intptr_t guarded_cid() const { return GuardedCidBits::decode(state_); }
+  bool is_non_nullable_integer() const {
+    return IsNonNullableIntegerBit::decode(state_);
+  }
+  bool is_unboxing_candidate() const {
+    return IsUnboxingCandidateBit::decode(state_);
+  }
+  bool is_nullable() const { return IsNullableBit::decode(state_); }
+
+  bool IsUnboxed() const;
+  bool IsPotentialUnboxed() const;
+
+ private:
+  using GuardedCidBits = BitField<int32_t, ClassIdTagType, 0, 16>;
+  using IsNonNullableIntegerBit =
+      BitField<int32_t, bool, GuardedCidBits::kNextBit, 1>;
+  using IsUnboxingCandidateBit =
+      BitField<int32_t, bool, IsNonNullableIntegerBit::kNextBit, 1>;
+  using IsNullableBit =
+      BitField<int32_t, bool, IsUnboxingCandidateBit::kNextBit, 1>;
+
+  const int32_t state_;
+};
+
 // Slot is an abstraction that describes an readable (and possibly writeable)
 // location within an object.
 //
@@ -298,6 +327,10 @@
     return kind() == Kind::kCapturedVariable || kind() == Kind::kContext_parent;
   }
 
+  bool IsUnboxed() const;
+  bool IsPotentialUnboxed() const;
+  Representation UnboxedRepresentation() const;
+
  private:
   friend class FlowGraphDeserializer;  // For GetNativeSlot.
 
@@ -307,12 +340,14 @@
        intptr_t offset_in_bytes,
        const void* data,
        const AbstractType* static_type,
-       Representation representation)
+       Representation representation,
+       const FieldGuardState& field_guard_state = FieldGuardState())
       : kind_(kind),
         flags_(bits),
         cid_(cid),
         offset_in_bytes_(offset_in_bytes),
         representation_(representation),
+        field_guard_state_(field_guard_state),
         data_(data),
         static_type_(static_type) {}
 
@@ -323,7 +358,8 @@
              other.offset_in_bytes_,
              other.data_,
              other.static_type_,
-             other.representation_) {}
+             other.representation_,
+             other.field_guard_state_) {}
 
   using IsImmutableBit = BitField<int8_t, bool, 0, 1>;
   using IsNullableBit = BitField<int8_t, bool, IsImmutableBit::kNextBit, 1>;
@@ -342,6 +378,10 @@
   static AcqRelAtomic<Slot*> native_fields_;
   static const Slot& GetNativeSlot(Kind kind);
 
+  const FieldGuardState& field_guard_state() const {
+    return field_guard_state_;
+  }
+
   const Kind kind_;
   const int8_t flags_;        // is_immutable, is_nullable
   const ClassIdTagType cid_;  // Concrete cid of a value or kDynamicCid.
@@ -349,6 +389,8 @@
   const intptr_t offset_in_bytes_;
   const Representation representation_;
 
+  const FieldGuardState field_guard_state_;
+
   // Kind dependent data:
   //   - name as a Dart String object for local variables;
   //   - name as a C string for native slots;
diff --git a/runtime/vm/compiler/graph_intrinsifier.cc b/runtime/vm/compiler/graph_intrinsifier.cc
index 85ed2b5..a0da87b 100644
--- a/runtime/vm/compiler/graph_intrinsifier.cc
+++ b/runtime/vm/compiler/graph_intrinsifier.cc
@@ -1050,7 +1050,8 @@
   // [Intrinsifier::CanIntrinsifyFieldAccessor])
   auto zone = flow_graph->zone();
   const auto& function = flow_graph->function();
-  ASSERT(Intrinsifier::CanIntrinsifyFieldAccessor(function));
+  ASSERT(
+      Intrinsifier::CanIntrinsifyFieldAccessor(flow_graph->parsed_function()));
 
   auto& field = Field::Handle(zone, function.accessor_field());
   if (CompilerState::Current().should_clone_fields()) {
@@ -1072,12 +1073,10 @@
 
   // We only support cases where we do not have to create a box (whose
   // allocation could fail).
-  ASSERT(function.HasUnboxedReturnValue() ||
-         !FlowGraphCompiler::IsUnboxedField(field));
+  ASSERT(function.HasUnboxedReturnValue() || !slot.IsUnboxed());
 
   // We might need to unbox the field value before returning.
-  if (function.HasUnboxedReturnValue() &&
-      !FlowGraphCompiler::IsUnboxedField(field)) {
+  if (function.HasUnboxedReturnValue() && !slot.IsUnboxed()) {
     ASSERT(FLAG_precompiled_mode);
     field_value = builder.AddUnboxInstr(
         FlowGraph::ReturnRepresentationOf(flow_graph->function()),
@@ -1093,21 +1092,19 @@
   // [Intrinsifier::CanIntrinsifyFieldAccessor])
   auto zone = flow_graph->zone();
   const auto& function = flow_graph->function();
-  ASSERT(Intrinsifier::CanIntrinsifyFieldAccessor(function));
+  ASSERT(
+      Intrinsifier::CanIntrinsifyFieldAccessor(flow_graph->parsed_function()));
 
   auto& field = Field::Handle(zone, function.accessor_field());
   if (CompilerState::Current().should_clone_fields()) {
     field = field.CloneFromOriginal();
   }
   ASSERT(field.is_instance() && !field.is_final());
-  ASSERT(!function.HasUnboxedParameters() ||
-         FlowGraphCompiler::IsUnboxedField(field));
-
   const auto& slot = Slot::Get(field, &flow_graph->parsed_function());
+  ASSERT(!function.HasUnboxedParameters() || slot.IsUnboxed());
 
-  const auto barrier_mode = FlowGraphCompiler::IsUnboxedField(field)
-                                ? kNoStoreBarrier
-                                : kEmitStoreBarrier;
+  const auto barrier_mode =
+      slot.IsUnboxed() ? kNoStoreBarrier : kEmitStoreBarrier;
 
   flow_graph->CreateCommonConstants();
   GraphEntryInstr* graph_entry = flow_graph->graph_entry();
@@ -1118,14 +1115,13 @@
   auto value = builder.AddParameter(1, /*with_frame=*/false);
   VerifyParameterIsBoxed(&builder, 0);
 
-  if (!function.HasUnboxedParameters() &&
-      FlowGraphCompiler::IsUnboxedField(field)) {
+  if (!function.HasUnboxedParameters() && slot.IsUnboxed()) {
     // We do not support storing to possibly guarded fields in JIT in graph
     // intrinsics.
     ASSERT(FLAG_precompiled_mode);
-    value = builder.AddUnboxInstr(
-        FlowGraph::UnboxedFieldRepresentationOf(field), new Value(value),
-        /*is_checked=*/true);
+    value =
+        builder.AddUnboxInstr(slot.UnboxedRepresentation(), new Value(value),
+                              /*is_checked=*/true);
   }
 
   builder.AddInstruction(new (zone) StoreInstanceFieldInstr(
diff --git a/runtime/vm/compiler/intrinsifier.cc b/runtime/vm/compiler/intrinsifier.cc
index f343f73..4ba80d9f 100644
--- a/runtime/vm/compiler/intrinsifier.cc
+++ b/runtime/vm/compiler/intrinsifier.cc
@@ -25,7 +25,9 @@
 
 namespace compiler {
 
-bool Intrinsifier::CanIntrinsify(const Function& function) {
+bool Intrinsifier::CanIntrinsify(const ParsedFunction& parsed_function) {
+  const Function& function = parsed_function.function();
+
   if (FLAG_trace_intrinsifier) {
     THR_Print("CanIntrinsify %s ->", function.ToQualifiedCString());
   }
@@ -45,7 +47,8 @@
     }
     return false;
   }
-  if (!function.is_intrinsic() && !CanIntrinsifyFieldAccessor(function)) {
+  if (!function.is_intrinsic() &&
+      !CanIntrinsifyFieldAccessor(parsed_function)) {
     if (FLAG_trace_intrinsifier) {
       THR_Print("No, not intrinsic function.\n");
     }
@@ -73,7 +76,10 @@
   return true;
 }
 
-bool Intrinsifier::CanIntrinsifyFieldAccessor(const Function& function) {
+bool Intrinsifier::CanIntrinsifyFieldAccessor(
+    const ParsedFunction& parsed_function) {
+  const Function& function = parsed_function.function();
+
   const bool is_getter = function.IsImplicitGetterFunction();
   const bool is_setter = function.IsImplicitSetterFunction();
   if (!is_getter && !is_setter) return false;
@@ -97,11 +103,13 @@
   // We only graph intrinsify implicit instance getters/setter for now.
   if (!field.is_instance()) return false;
 
+  const auto& slot = Slot::Get(field, &parsed_function);
+
   if (is_getter) {
     // We don't support complex getter cases.
     if (field.is_late() || field.needs_load_guard()) return false;
 
-    if (FlowGraphCompiler::IsPotentialUnboxedField(field)) {
+    if (slot.IsPotentialUnboxed()) {
       if (function.HasUnboxedReturnValue()) {
         // In AOT mode: Unboxed fields contain the unboxed value and can be
         // returned in unboxed form.
@@ -136,7 +144,7 @@
     // avoid the need for boxing (which we cannot do in the intrinsic).
     if (function.HasUnboxedParameters()) {
       ASSERT(FLAG_precompiled_mode);
-      if (!FlowGraphCompiler::IsUnboxedField(field)) {
+      if (!slot.IsUnboxed()) {
         return false;
       }
     }
@@ -253,8 +261,7 @@
 // Returns true if fall-through code can be omitted.
 bool Intrinsifier::Intrinsify(const ParsedFunction& parsed_function,
                               FlowGraphCompiler* compiler) {
-  const Function& function = parsed_function.function();
-  if (!CanIntrinsify(function)) {
+  if (!CanIntrinsify(parsed_function)) {
     return false;
   }
 
@@ -262,6 +269,7 @@
     return compiler->intrinsic_slow_path_label()->IsUnused();
   }
 
+  const Function& function = parsed_function.function();
 #if !defined(HASH_IN_OBJECT_HEADER)
   // These two are more complicated on 32 bit platforms, where the
   // identity hash is not stored in the header of the object.  We
diff --git a/runtime/vm/compiler/intrinsifier.h b/runtime/vm/compiler/intrinsifier.h
index 835f73f..681b606 100644
--- a/runtime/vm/compiler/intrinsifier.h
+++ b/runtime/vm/compiler/intrinsifier.h
@@ -35,8 +35,8 @@
 
  private:
   friend class GraphIntrinsifier;  // For CanIntrinsifyFieldAccessor.
-  static bool CanIntrinsify(const Function& function);
-  static bool CanIntrinsifyFieldAccessor(const Function& function);
+  static bool CanIntrinsify(const ParsedFunction& parsed_function);
+  static bool CanIntrinsifyFieldAccessor(const ParsedFunction& parsed_function);
 };
 
 }  // namespace compiler
diff --git a/tools/VERSION b/tools/VERSION
index 8abe925..e9eadd2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 286
+PRERELEASE 287
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh
index 75f7375..d8ed384 100755
--- a/tools/bots/try_benchmarks.sh
+++ b/tools/bots/try_benchmarks.sh
@@ -184,6 +184,8 @@
     out/ReleaseIA32/run_vm_tests --dfe=out/ReleaseIA32/kernel-service.dart.snapshot --sound-null-safety UseDartApi
     out/ReleaseIA32/dart --profile-period=10000 --packages=.packages benchmarks/Example/dart2/Example.dart
     out/ReleaseIA32/dart --sound-null-safety --profile-period=10000 --packages=.packages benchmarks/Example/dart/Example.dart
+    out/ReleaseIA32/dart benchmarks/NativeCall/dart2/NativeCall.dart
+    out/ReleaseIA32/dart --sound-null-safety benchmarks/NativeCall/dart/NativeCall.dart
     out/ReleaseIA32/dart benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart
     out/ReleaseIA32/dart --sound-null-safety benchmarks/FfiBoringssl/dart/FfiBoringssl.dart
     out/ReleaseIA32/dart benchmarks/FfiCall/dart2/FfiCall.dart