[vm/kernel/bytecode] Generate bytecode for native methods
Change-Id: If47ef9ef4ff5ac3cb3f4f6737590370b647fc9ff
Reviewed-on: https://dart-review.googlesource.com/59180
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: RĂ©gis Crelier <regis@google.com>
diff --git a/pkg/kernel/lib/external_name.dart b/pkg/kernel/lib/external_name.dart
new file mode 100644
index 0000000..4da52a8
--- /dev/null
+++ b/pkg/kernel/lib/external_name.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2018, 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.
+
+library kernel.external_name;
+
+import 'ast.dart';
+
+/// Returns external (native) name of given [Member].
+String getExternalName(Member procedure) {
+ // Native procedures are marked as external and have an annotation,
+ // which looks like this:
+ //
+ // 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) {
+ if (annotation is ConstructorInvocation) {
+ if (_isExternalName(annotation.target.enclosingClass)) {
+ return (annotation.arguments.positional.single as StringLiteral).value;
+ }
+ } else if (annotation is ConstantExpression) {
+ final constant = annotation.constant;
+ if (constant is InstanceConstant) {
+ if (_isExternalName(constant.klass)) {
+ return (constant.fieldValues.values.single as StringConstant).value;
+ }
+ }
+ }
+ }
+ return null;
+}
+
+bool _isExternalName(Class klass) =>
+ klass.name == 'ExternalName' &&
+ klass.enclosingLibrary.importUri.toString() == 'dart:_internal';
diff --git a/pkg/kernel/lib/transformations/constants.dart b/pkg/kernel/lib/transformations/constants.dart
index de851c6..b8a3aa8 100644
--- a/pkg/kernel/lib/transformations/constants.dart
+++ b/pkg/kernel/lib/transformations/constants.dart
@@ -20,13 +20,13 @@
import 'dart:io' as io;
-import '../kernel.dart';
import '../ast.dart';
+import '../class_hierarchy.dart';
import '../core_types.dart';
+import '../external_name.dart' show getExternalName;
+import '../kernel.dart';
import '../type_algebra.dart';
import '../type_environment.dart';
-import '../class_hierarchy.dart';
-import 'treeshaker.dart' show findNativeName;
Component transformComponent(Component component, ConstantsBackend backend,
{bool keepFields: false,
@@ -940,7 +940,7 @@
visitStaticInvocation(StaticInvocation node) {
final Procedure target = node.target;
if (target.kind == ProcedureKind.Factory) {
- final String nativeName = findNativeName(target);
+ final String nativeName = getExternalName(target);
if (nativeName != null) {
final Constant constant = backend.buildConstantForNative(
nativeName,
diff --git a/pkg/kernel/lib/transformations/treeshaker.dart b/pkg/kernel/lib/transformations/treeshaker.dart
index f708bd9..2d50eda 100644
--- a/pkg/kernel/lib/transformations/treeshaker.dart
+++ b/pkg/kernel/lib/transformations/treeshaker.dart
@@ -1246,37 +1246,3 @@
/// Exception that is thrown to stop the tree shaking analysis when a use
/// of `dart:mirrors` is found.
class _UsingMirrorsException {}
-
-String findNativeName(Member procedure) {
- // Native procedures are marked as external and have an annotation,
- // which looks like this:
- //
- // import 'dart:_internal' as internal;
- //
- // @internal.ExternalName("<name-of-native>")
- // external Object foo(arg0, ...);
- //
- if (procedure.isExternal) {
- for (final Expression annotation in procedure.annotations) {
- if (annotation is ConstructorInvocation) {
- final Class klass = annotation.target.enclosingClass;
- if (klass.name == 'ExternalName' &&
- klass.enclosingLibrary.importUri.toString() == 'dart:_internal') {
- assert(annotation.arguments.positional.length == 1);
- return (annotation.arguments.positional[0] as StringLiteral).value;
- }
- } else if (annotation is ConstantExpression) {
- final constant = annotation.constant;
- if (constant is InstanceConstant) {
- final Class klass = constant.klass;
- if (klass.name == 'ExternalName' &&
- klass.enclosingLibrary.importUri.toString() == 'dart:_internal') {
- assert(constant.fieldValues.length == 1);
- return (constant.fieldValues.values.single as StringConstant).value;
- }
- }
- }
- }
- }
- return null;
-}
diff --git a/pkg/vm/lib/bytecode/assembler.dart b/pkg/vm/lib/bytecode/assembler.dart
index fdf239e..d5fa43b 100644
--- a/pkg/vm/lib/bytecode/assembler.dart
+++ b/pkg/vm/lib/bytecode/assembler.dart
@@ -246,8 +246,8 @@
emitWord(_encodeAD(Opcode.kPushPolymorphicInstanceCallByRange, ra, rd));
}
- void emitNativeCall(int ra, int rb, int rc) {
- emitWord(_encodeABC(Opcode.kNativeCall, ra, rb, rc));
+ void emitNativeCall(int rd) {
+ emitWord(_encodeD(Opcode.kNativeCall, rd));
}
void emitOneByteStringFromCharCode(int ra, int rx) {
diff --git a/pkg/vm/lib/bytecode/constant_pool.dart b/pkg/vm/lib/bytecode/constant_pool.dart
index 6de7663..b6b0ad1 100644
--- a/pkg/vm/lib/bytecode/constant_pool.dart
+++ b/pkg/vm/lib/bytecode/constant_pool.dart
@@ -153,6 +153,11 @@
Byte tag = 22;
}
+type ConstantNativeEntry extends ConstantPoolEntry {
+ Byte tag = 23;
+ StringReference nativeName;
+}
+
*/
enum ConstantTag {
@@ -179,6 +184,7 @@
kContextOffset,
kClosureFunction,
kEndClosureFunctionScope,
+ kNativeEntry,
}
abstract class ConstantPoolEntry {
@@ -243,6 +249,8 @@
return new ConstantClosureFunction.readFromBinary(source);
case ConstantTag.kEndClosureFunctionScope:
return new ConstantEndClosureFunctionScope.readFromBinary(source);
+ case ConstantTag.kNativeEntry:
+ return new ConstantNativeEntry.readFromBinary(source);
}
throw 'Unexpected constant tag $tag';
}
@@ -1006,6 +1014,33 @@
// [hashCode] and [operator ==].
}
+class ConstantNativeEntry extends ConstantPoolEntry {
+ final String nativeName;
+
+ ConstantNativeEntry(this.nativeName);
+
+ @override
+ ConstantTag get tag => ConstantTag.kNativeEntry;
+
+ @override
+ void writeValueToBinary(BinarySink sink) {
+ sink.writeStringReference(nativeName);
+ }
+
+ ConstantNativeEntry.readFromBinary(BinarySource source)
+ : nativeName = source.readStringReference();
+
+ @override
+ String toString() => 'NativeEntry $nativeName';
+
+ @override
+ int get hashCode => nativeName.hashCode;
+
+ @override
+ bool operator ==(other) =>
+ other is ConstantNativeEntry && this.nativeName == other.nativeName;
+}
+
class ConstantPool {
final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[];
final Map<ConstantPoolEntry, int> _canonicalizationCache =
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 53a6975..c2e6216 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -15,6 +15,9 @@
// parameters. This DBC instruction was removed at
// https://github.com/dart-lang/sdk/commit/cf1de7d46cd88e204380e8f96a993439be56b24c
//
+// 3. NativeCall instruction is modified to have 'D' format and take 1 argument:
+// D = index of NativeEntry constant pool entry
+//
enum Opcode {
kTrap,
@@ -302,7 +305,7 @@
Opcode.kPushPolymorphicInstanceCallByRange: const Format(
Encoding.kAD, const [Operand.imm, Operand.imm, Operand.none]),
Opcode.kNativeCall: const Format(
- Encoding.kABC, const [Operand.imm, Operand.imm, Operand.imm]),
+ Encoding.kD, const [Operand.lit, Operand.none, Operand.none]),
Opcode.kOneByteStringFromCharCode: const Format(
Encoding.kAX, const [Operand.reg, Operand.xeg, Operand.none]),
Opcode.kStringToCharCode: const Format(
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index d2d12b3..797bc67 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -8,6 +8,7 @@
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/clone.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
+import 'package:kernel/external_name.dart' show getExternalName;
import 'package:kernel/library_index.dart' show LibraryIndex;
import 'package:kernel/transformations/constants.dart'
show ConstantEvaluator, ConstantsBackend, EvaluationEnvironment;
@@ -103,7 +104,7 @@
@override
defaultMember(Member node) {
- if (node.isAbstract || node.isExternal) {
+ if (node.isAbstract) {
return;
}
try {
@@ -124,9 +125,17 @@
if (node is Constructor) {
_genConstructorInitializers(node);
}
- node.function?.body?.accept(this);
- // TODO(alexmarkov): figure out when 'return null' should be generated.
- _genPushNull();
+ if (node.isExternal) {
+ final String nativeName = getExternalName(node);
+ if (nativeName == null) {
+ return;
+ }
+ _genNativeCall(nativeName);
+ } else {
+ node.function?.body?.accept(this);
+ // TODO(alexmarkov): figure out when 'return null' should be generated.
+ _genPushNull();
+ }
_genReturnTOS();
end(node);
}
@@ -137,6 +146,27 @@
}
}
+ void _genNativeCall(String nativeName) {
+ final function = enclosingMember.function;
+ assert(function != null);
+
+ if (locals.hasTypeArgsVar) {
+ asm.emitPush(locals.typeArgsVarIndexInFrame);
+ }
+ if (locals.hasReceiver) {
+ asm.emitPush(locals.getVarIndexInFrame(locals.receiverVar));
+ }
+ for (var param in function.positionalParameters) {
+ asm.emitPush(locals.getVarIndexInFrame(param));
+ }
+ for (var param in function.namedParameters) {
+ asm.emitPush(locals.getVarIndexInFrame(param));
+ }
+
+ final nativeEntryCpIndex = cp.add(new ConstantNativeEntry(nativeName));
+ asm.emitNativeCall(nativeEntryCpIndex);
+ }
+
LibraryIndex _libraryIndex;
LibraryIndex get libraryIndex =>
_libraryIndex ??= new LibraryIndex.coreLibraries(component);
diff --git a/pkg/vm/lib/transformations/type_flow/native_code.dart b/pkg/vm/lib/transformations/type_flow/native_code.dart
index 8e4a171..cc078c6 100644
--- a/pkg/vm/lib/transformations/type_flow/native_code.dart
+++ b/pkg/vm/lib/transformations/type_flow/native_code.dart
@@ -10,12 +10,9 @@
import 'dart:io' show File;
import 'package:kernel/ast.dart';
-import 'package:kernel/library_index.dart' show LibraryIndex;
import 'package:kernel/core_types.dart' show CoreTypes;
-
-// TODO(alexmarkov): Move findNativeName out of treeshaker and avoid dependency
-// on unrelated transformation.
-import 'package:kernel/transformations/treeshaker.dart' show findNativeName;
+import 'package:kernel/external_name.dart' show getExternalName;
+import 'package:kernel/library_index.dart' show LibraryIndex;
import 'calls.dart';
import 'types.dart';
@@ -108,7 +105,7 @@
/// using [entryPointsListener]. Returns result type of the native method.
Type handleNativeProcedure(
Member member, EntryPointsListener entryPointsListener) {
- final String nativeName = findNativeName(member);
+ final String nativeName = getExternalName(member);
Type returnType = null;
final nativeActions = _nativeMethods[nativeName];
diff --git a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
index c6c2c4e..8df59b8 100644
--- a/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
+++ b/pkg/vm/testcases/bytecode/bootstrapping.dart.expect
@@ -67,11 +67,44 @@
] constructor _() → void
: super core::Object::•()
;
- @_in::ExternalName::•("Namespace_Create")
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ Push FP[-6]
+ Push FP[-5]
+ NativeCall CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = NativeEntry Namespace_Create
+}
+] @_in::ExternalName::•("Namespace_Create")
external static method _create(self::_NamespaceImpl namespace, dynamic n) → self::_NamespaceImpl;
- @_in::ExternalName::•("Namespace_GetPointer")
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ Push FP[-5]
+ NativeCall CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = NativeEntry Namespace_GetPointer
+}
+] @_in::ExternalName::•("Namespace_GetPointer")
external static method _getPointer(self::_NamespaceImpl namespace) → core::int;
- @_in::ExternalName::•("Namespace_GetDefault")
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ NativeCall CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = NativeEntry Namespace_GetDefault
+}
+] @_in::ExternalName::•("Namespace_GetDefault")
external static method _getDefault() → core::int;
[@vm.bytecode=
Bytecode {
@@ -512,7 +545,18 @@
}
]static field core::int _stderrFD = 2;
static field core::String _rawScript;
-@_in::ExternalName::•("Builtin_PrintString")
+[@vm.bytecode=
+Bytecode {
+ Entry 0
+ CheckStack
+ Push FP[-5]
+ NativeCall CP#0
+ ReturnTOS
+}
+ConstantPool {
+ [0] = NativeEntry Builtin_PrintString
+}
+]@_in::ExternalName::•("Builtin_PrintString")
external static method _printString(core::String s) → void;
[@vm.bytecode=
Bytecode {