Skip to content

Commit

Permalink
[vm] Migrate away from native 'name' syntax.
Browse files Browse the repository at this point in the history
As part of deprecating support for native extensions we are also
migrating away from legacy VM-specific `native 'name'` syntax
towards metadata based encoding which does not require any special
syntax.

This CL is a step 1 in migration:

- introduces support for `@pragma('vm:external-name', 'name')`
which serves as a direct replacement for `native 'name'`;
- all core libraries and tests are migrated to use the annotation;

Once this CL lands and rolls we will edit internal and external embedders
to eliminate uses of the native keyword (step 2) and finally remove
support for native keyword across our parsers (step 3).

TEST=ci

Bug: #28791
Change-Id: Id6dea878db82dd4fd81149243c425b5c5dc6df86
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212461
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
  • Loading branch information
mraleph authored and commit-bot@chromium.org committed Sep 8, 2021
1 parent fbc70d8 commit d8d7af1
Show file tree
Hide file tree
Showing 83 changed files with 1,869 additions and 1,262 deletions.
30 changes: 15 additions & 15 deletions pkg/front_end/testcases/general/invalid_operator.dart.weak.expect
Original file line number Diff line number Diff line change
Expand Up @@ -564,37 +564,37 @@ library;
// Try adding explicit types.
// operator ==<T>(a) => true;
// ^^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is one of the overridden members.
// bool operator ==(Object other) native "Object_equals";
// ^^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is one of the overridden members.
// external bool operator ==(Object other);
// ^^
//
// pkg/front_end/testcases/general/invalid_operator.dart:6:12: Error: The method 'Operators1.==' has fewer positional arguments than those of overridden method 'Object.=='.
// operator ==() => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:27:12: Error: The method 'Operators2.==' has more required arguments than those of overridden method 'Object.=='.
// operator ==(a, b) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:71:12: Error: The method 'Operators4.==' has fewer positional arguments than those of overridden method 'Object.=='.
// operator ==({a}) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:137:12: Error: Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
// operator ==<T>(a) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
import self as self;
import "dart:core" as core;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,37 +564,37 @@ library;
// Try adding explicit types.
// operator ==<T>(a) => true;
// ^^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is one of the overridden members.
// bool operator ==(Object other) native "Object_equals";
// ^^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is one of the overridden members.
// external bool operator ==(Object other);
// ^^
//
// pkg/front_end/testcases/general/invalid_operator.dart:6:12: Error: The method 'Operators1.==' has fewer positional arguments than those of overridden method 'Object.=='.
// operator ==() => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:27:12: Error: The method 'Operators2.==' has more required arguments than those of overridden method 'Object.=='.
// operator ==(a, b) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:71:12: Error: The method 'Operators4.==' has fewer positional arguments than those of overridden method 'Object.=='.
// operator ==({a}) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/general/invalid_operator.dart:137:12: Error: Declared type variables of 'Operators7.==' doesn't match those on overridden method 'Object.=='.
// operator ==<T>(a) => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
import self as self;
import "dart:core" as core;
Expand Down
6 changes: 3 additions & 3 deletions pkg/front_end/testcases/nnbd/issue42603.dart.strong.expect
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ library /*isNonNullableByDefault*/;
// pkg/front_end/testcases/nnbd/issue42603.dart:18:17: Error: The method 'E.==' has fewer positional arguments than those of overridden method 'Object.=='.
// bool operator ==() => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/nnbd/issue42603.dart:22:17: Error: The method 'F.==' has more required arguments than those of overridden method 'E.=='.
// bool operator ==(Object? other) => super == other;
Expand Down
6 changes: 3 additions & 3 deletions pkg/front_end/testcases/nnbd/issue42603.dart.weak.expect
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ library /*isNonNullableByDefault*/;
// pkg/front_end/testcases/nnbd/issue42603.dart:18:17: Error: The method 'E.==' has fewer positional arguments than those of overridden method 'Object.=='.
// bool operator ==() => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/nnbd/issue42603.dart:22:17: Error: The method 'F.==' has more required arguments than those of overridden method 'E.=='.
// bool operator ==(Object? other) => super == other;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ library /*isNonNullableByDefault*/;
// pkg/front_end/testcases/nnbd/issue42603.dart:18:17: Error: The method 'E.==' has fewer positional arguments than those of overridden method 'Object.=='.
// bool operator ==() => true;
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:26:17: Context: This is the overridden method ('==').
// bool operator ==(Object other) native "Object_equals";
// ^
// sdk/lib/_internal/vm/lib/object_patch.dart:29:26: Context: This is the overridden method ('==').
// external bool operator ==(Object other);
// ^
//
// pkg/front_end/testcases/nnbd/issue42603.dart:22:17: Error: The method 'F.==' has more required arguments than those of overridden method 'E.=='.
// bool operator ==(Object? other) => super == other;
Expand Down
51 changes: 31 additions & 20 deletions pkg/kernel/lib/external_name.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,56 @@
library kernel.external_name;

import 'ast.dart';
import 'core_types.dart';

/// Returns external (native) name of given [Member].
String? getExternalName(Member procedure) {
String? getExternalName(CoreTypes coreTypes, Member procedure) {
// Native procedures are marked as external and have an annotation,
// which looks like this:
//
// @pragma("vm:external-name", "<name-of-native>")
// external Object foo(arg0, ...);
//
// Previously the following encoding was used, which is still supported
// until all users are migrated away from it:
//
// import 'dart:_internal' as internal;
//
// @internal.ExternalName("<name-of-native>")
// external Object foo(arg0, ...);
//

if (!procedure.isExternal) {
return null;
}
for (final Expression annotation in procedure.annotations) {
final String? value = _getExternalNameValue(annotation);
final String? value = _getExternalNameValue(coreTypes, annotation);
if (value != null) {
return value;
}
}
return null;
}

/// Returns native extension URIs for given [library].
List<String> getNativeExtensionUris(Library library) {
final List<String> uris = <String>[];
for (Expression annotation in library.annotations) {
final String? value = _getExternalNameValue(annotation);
if (value != null) {
uris.add(value);
}
}
return uris;
}

String? _getExternalNameValue(Expression annotation) {
if (annotation is ConstructorInvocation) {
if (_isExternalName(annotation.target.enclosingClass)) {
return (annotation.arguments.positional.single as StringLiteral).value;
}
} else if (annotation is ConstantExpression) {
String? _getExternalNameValue(CoreTypes coreTypes, Expression annotation) {
if (annotation is ConstantExpression) {
final Constant constant = annotation.constant;
if (constant is InstanceConstant) {
if (_isExternalName(constant.classNode)) {
return (constant.fieldValues.values.single as StringConstant).value;
} else if (_isPragma(constant.classNode)) {
final String pragmaName =
(constant.fieldValues[coreTypes.pragmaName.getterReference]
as StringConstant)
.value;
final Constant? pragmaOptionsValue =
constant.fieldValues[coreTypes.pragmaOptions.getterReference];
final String? pragmaOptions = pragmaOptionsValue is StringConstant
? pragmaOptionsValue.value
: null;
if (pragmaName == _externalNamePragma && pragmaOptions != null) {
return pragmaOptions;
}
}
}
}
Expand All @@ -59,3 +64,9 @@ String? _getExternalNameValue(Expression annotation) {
bool _isExternalName(Class klass) =>
klass.name == 'ExternalName' &&
klass.enclosingLibrary.importUri.toString() == 'dart:_internal';

bool _isPragma(Class klass) =>
klass.name == 'pragma' &&
klass.enclosingLibrary.importUri.toString() == 'dart:core';

const String _externalNamePragma = 'vm:external-name';
7 changes: 5 additions & 2 deletions pkg/vm/lib/transformations/type_flow/signature_shaking.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/external_name.dart';
import 'package:kernel/type_environment.dart';

Expand Down Expand Up @@ -200,6 +201,7 @@ class _ParameterInfo {

class _Collect extends RecursiveVisitor {
final SignatureShaker shaker;
final CoreTypes coreTypes;

/// Parameters of the current function.
final Map<VariableDeclaration, _ParameterInfo> localParameters = {};
Expand All @@ -209,7 +211,8 @@ class _Collect extends RecursiveVisitor {
/// via [_ParameterInfo.useDependencies] and not marked as read immediately.
final Set<VariableGet> useDependencies = {};

_Collect(this.shaker);
_Collect(this.shaker)
: coreTypes = shaker.typeFlowAnalysis.environment.coreTypes;

void enterFunction(Member member) {
final _ProcedureInfo? info = shaker._infoForMember(member);
Expand All @@ -233,7 +236,7 @@ class _Collect extends RecursiveVisitor {
shaker.typeFlowAnalysis.nativeCodeOracle
.isMemberReferencedFromNativeCode(member) ||
shaker.typeFlowAnalysis.nativeCodeOracle.isRecognized(member) ||
getExternalName(member) != null ||
getExternalName(coreTypes, member) != null ||
member.name.text == '==') {
info.eligible = false;
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/vm/lib/transformations/type_flow/unboxing_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,5 @@ class UnboxingInfoManager {
_nativeCodeOracle.hasDisableUnboxedParameters(member);
}

bool _isNative(Member member) => getExternalName(member) != null;
bool _isNative(Member member) => getExternalName(_coreTypes, member) != null;
}
1 change: 1 addition & 0 deletions runtime/docs/pragmas.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ These pragmas are part of the VM's API and are safe for use in external code.
| `vm:never-inline` | [Never inline a function or method](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-never-be-inlined) |
| `vm:prefer-inline` | [Inline a function or method when possible](compiler/pragmas_recognized_by_compiler.md#requesting-a-function-be-inlined) |
| `vm:notify-debugger-on-exception` | Marks a function that catches exceptions, making the VM treat any caught exception as if they were uncaught. This can be used to notify an attached debugger during debugging, without pausing the app during regular execution. |
| `vm:external-name` | Allows to specify an external (native) name for an `external` function. This name is used to lookup native implementation via native resolver associated with the current library through embedding APIs. This is a replacement for legacy VM specific `native "name"` syntax. |

## Unsafe pragmas for general use

Expand Down
31 changes: 17 additions & 14 deletions runtime/vm/benchmark_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,23 @@ static Dart_NativeFunction bm_uda_lookup(Dart_Handle name,

BENCHMARK(UseDartApi) {
const int kNumIterations = 1000000;
const char* kScriptChars =
"import 'dart:nativewrappers';\n"
"class Class extends NativeFieldWrapperClass1 {\n"
" void init() native 'init';\n"
" int method(int param1, int param2) native 'method';\n"
"}\n"
"\n"
"void benchmark(int count) {\n"
" Class c = Class();\n"
" c.init();\n"
" for (int i = 0; i < count; i++) {\n"
" c.method(i,7);\n"
" }\n"
"}\n";
const char* kScriptChars = R"(
import 'dart:nativewrappers';
class Class extends NativeFieldWrapperClass1 {
@pragma("vm:external-name", "init")
external void init();
@pragma("vm:external-name", "method")
external int method(int param1, int param2);
}
void benchmark(int count) {
Class c = Class();
c.init();
for (int i = 0; i < count; i++) {
c.method(i,7);
}
})";

Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, bm_uda_lookup,
RESOLVED_USER_TEST_URI, false);
Expand Down
35 changes: 18 additions & 17 deletions runtime/vm/code_descriptors_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,24 @@ static Dart_NativeFunction native_resolver(Dart_Handle name,
}

TEST_CASE(StackMapGC) {
const char* kScriptChars =
"class A {"
" static void func(var i, var k) native 'NativeFunc';"
" static foo() {"
" var i;"
" var s1;"
" var k;"
" var s2;"
" var s3;"
" i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C';"
" func(i, k);"
" return i + k; }"
" static void moo() {"
" var i = A.foo();"
" if (i != 30) throw '$i != 30';"
" }\n"
"}\n";
const char* kScriptChars = R"(
class A {
@pragma("vm:external-name", "NativeFunc")
external static void func(var i, var k);
static foo() {
var i;
var s1;
var k;
var s2;
var s3;
i = 10; s1 = 'abcd'; k = 20; s2 = 'B'; s3 = 'C';
func(i, k);
return i + k; }
static void moo() {
var i = A.foo();
if (i != 30) throw '$i != 30';
}
})";
// First setup the script and compile the script.
TestCase::LoadTestScript(kScriptChars, native_resolver);
TransitionNativeToVM transition(thread);
Expand Down
Loading

0 comments on commit d8d7af1

Please sign in to comment.