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