[vm/tfa] Stop accepting non-empty entry points json files

Entry points JSON files are deprecated (replaced with pragmas).
So TFA should not require them. As a sanity check, it will complain
if an entry points JSON file is not empty.

Change-Id: Ie64121ab4cdd0c330f81a8cb71a671e1df82f136
Reviewed-on: https://dart-review.googlesource.com/75423
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2018-09-19 01:41:30 +00:00 committed by commit-bot@chromium.org
parent 30f0026022
commit d44ffba254
4 changed files with 10 additions and 288 deletions

View file

@ -11,7 +11,6 @@ import 'dart:io' show File;
import 'package:kernel/ast.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 'calls.dart';
@ -217,8 +216,6 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
/// Provides insights into the behavior of native code.
class NativeCodeOracle {
final Map<String, List<Map<String, dynamic>>> _nativeMethods =
<String, List<Map<String, dynamic>>>{};
final LibraryIndex _libraryIndex;
final Set<Member> _membersReferencedFromNativeCode = new Set<Member>();
final PragmaAnnotationParser _matcher;
@ -238,11 +235,8 @@ class NativeCodeOracle {
/// using [entryPointsListener]. Returns result type of the native method.
Type handleNativeProcedure(
Member member, EntryPointsListener entryPointsListener) {
final String nativeName = getExternalName(member);
Type returnType = null;
final nativeActions = _nativeMethods[nativeName];
for (var annotation in member.annotations) {
ParsedPragma pragma = _matcher.parsePragma(annotation);
if (pragma == null) continue;
@ -281,32 +275,6 @@ class NativeCodeOracle {
}
}
if (returnType != null) {
assertx(nativeActions == null || nativeActions.length == 0);
return returnType;
}
if (nativeActions != null) {
for (var action in nativeActions) {
if (action['action'] == 'return') {
final c = _libraryIndex.getClass(action['library'], action['class']);
final concreteClass = entryPointsListener.addAllocatedClass(c);
final nullable = action['nullable'];
if (nullable == false) {
returnType = concreteClass;
} else if ((nullable == true) || (nullable == null)) {
returnType = new Type.nullable(concreteClass);
} else {
throw 'Bad entry point: unexpected nullable: "$nullable" in $action';
}
} else {
_addRoot(action, entryPointsListener);
}
}
}
if (returnType != null) {
return returnType;
} else {
@ -314,146 +282,19 @@ class NativeCodeOracle {
}
}
void _addRoot(
Map<String, String> rootDesc, EntryPointsListener entryPointsListener) {
final String library = rootDesc['library'];
final String class_ = rootDesc['class'];
final String name = rootDesc['name'];
final String action = rootDesc['action'];
final libraryIndex = _libraryIndex;
if ((action == 'create-instance') || ((action == null) && (name == null))) {
if (name != null) {
throw 'Bad entry point: unexpected "name" element in $rootDesc';
}
final Class cls = libraryIndex.getClass(library, class_);
if (cls.isAbstract) {
throw 'Bad entry point: abstract class listed in $rootDesc';
}
entryPointsListener.addAllocatedClass(cls);
} else if ((action == 'call') ||
(action == 'get') ||
(action == 'set') ||
((action == null) && (name != null))) {
if (name == null) {
throw 'Bad entry point: expected "name" element in $rootDesc';
}
final String prefix = {
'get': LibraryIndex.getterPrefix,
'set': LibraryIndex.setterPrefix
}[action] ??
'';
Member member;
if (class_ != null) {
final classDotPrefix = class_ + '.';
if ((name == class_) || name.startsWith(classDotPrefix)) {
// constructor
if (action != 'call' && action != null) {
throw 'Bad entry point: action "$action" is not applicable to'
' constructor in $rootDesc';
}
final constructorName =
(name == class_) ? '' : name.substring(classDotPrefix.length);
member = libraryIndex.getMember(library, class_, constructorName);
} else {
member = libraryIndex.tryGetMember(library, class_, prefix + name);
if (member == null) {
member = libraryIndex.getMember(library, class_, name);
}
}
} else {
member = libraryIndex.tryGetTopLevelMember(
library, /* unused */ null, prefix + name);
if (member == null) {
member = libraryIndex.getTopLevelMember(library, name);
}
}
assertx(member != null);
CallKind callKind;
if (action == null) {
if ((member is Field) || ((member is Procedure) && member.isGetter)) {
callKind = CallKind.PropertyGet;
} else if ((member is Procedure) && member.isSetter) {
callKind = CallKind.PropertySet;
} else {
callKind = CallKind.Method;
}
} else {
callKind = const {
'get': CallKind.PropertyGet,
'set': CallKind.PropertySet,
'call': CallKind.Method
}[action];
}
assertx(callKind != null);
final Selector selector = member.isInstanceMember
? new InterfaceSelector(member, callKind: callKind)
: new DirectSelector(member, callKind: callKind);
entryPointsListener.addRawCall(selector);
if ((action == null) && (member is Field) && !member.isFinal) {
Selector selector = member.isInstanceMember
? new InterfaceSelector(member, callKind: CallKind.PropertySet)
: new DirectSelector(member, callKind: CallKind.PropertySet);
entryPointsListener.addRawCall(selector);
}
_membersReferencedFromNativeCode.add(member);
} else {
throw 'Bad entry point: unrecognized action "$action" in $rootDesc';
}
}
/// Reads JSON describing entry points and native methods from [jsonString].
/// Adds all global entry points using [entryPointsListener].
///
/// The format of the JSON descriptor is described in
/// 'runtime/vm/compiler/aot/entry_points_json.md'.
void processEntryPointsJSON(
String jsonString, EntryPointsListener entryPointsListener) {
final jsonObject = json.decode(jsonString);
final roots = jsonObject['roots'];
if (roots != null) {
for (var root in roots) {
_addRoot(new Map<String, String>.from(root), entryPointsListener);
}
}
final nativeMethods = jsonObject['native-methods'];
if (nativeMethods != null) {
nativeMethods.forEach((name, actions) {
_nativeMethods[name] = new List<Map<String, dynamic>>.from(
actions.map((action) => new Map<String, dynamic>.from(action)));
});
}
}
/// Reads JSON files [jsonFiles] describing entry points and native methods.
/// Adds all global entry points using [entryPointsListener].
///
/// The format of the JSON descriptor is described in
/// 'runtime/vm/compiler/aot/entry_points_json.md'.
/// Currently just checks that JSON file is empty as it is deprecated.
void processEntryPointsJSONFiles(
List<String> jsonFiles, EntryPointsListener entryPointsListener) {
for (var file in jsonFiles) {
processEntryPointsJSON(
new File(file).readAsStringSync(), entryPointsListener);
String jsonString = new File(file).readAsStringSync();
final jsonObject = json.decode(jsonString);
final roots = jsonObject['roots'];
if (roots != null && roots.isNotEmpty) {
throw "Error: Found non-empty entry points JSON file $file."
" Use the @pragma('vm:entry-point') annotation instead.";
}
}
}
}

View file

@ -35,10 +35,6 @@ const bool kDumpClassHierarchy =
Component transformComponent(Target target, CoreTypes coreTypes,
Component component, List<String> entryPoints,
[PragmaAnnotationParser matcher]) {
if ((entryPoints == null) || entryPoints.isEmpty) {
throw 'Error: unable to perform global type flow analysis without entry points.';
}
void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
final hierarchy = new ClassHierarchy(component,
onAmbiguousSupertypes: ignoreAmbiguousSupertypes);

View file

@ -24,12 +24,7 @@ runTestCase(Uri source) async {
final coreTypes = new CoreTypes(component);
final entryPoints = [
pkgVmDir + '/lib/transformations/type_flow/entry_points.json',
pkgVmDir + '/lib/transformations/type_flow/entry_points_extra.json',
];
component = transformComponent(target, coreTypes, component, entryPoints,
component = transformComponent(target, coreTypes, component, [],
new ExpressionPragmaAnnotationParser(coreTypes));
final actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);

View file

@ -1,110 +0,0 @@
# Entry points file format
Dart VM precompiler (AOT compiler) performs whole-program optimizations such as
tree shaking in order to decrease size of the resulting compiled apps and
improve their performance. Such optimizations assume that compiler can see
the whole Dart program, and is able to discover and analyze all Dart functions
and members which can be potentially executed at run time. While the Dart code
is fully available for precompiler, native code of the embedder and native
methods are out of reach of the compiler. Such native code can call back to
Dart via native Dart API.
In order to aid precompiler, programmer can explicitly list entry
points (roots) - Dart classes and members which are accessed from native code.
Note that listing entry points is not optional: as long as program defines
native methods which call into Dart, the entry points are required for the
correctness of compilation.
This memo describes _new_ format of entry points file, which is intended to
replace old comma-separated lists of entry points. At the time of writing,
new format is not fully adopted yet.
The native entry points are described in a JSON text file. The descriptor has the form
```json
{
"roots": [
<root1>,
...
<rootN>
],
“native-methods”: {
<native1_name>” : [
<native1_root1>,
...
<native1_rootM1>,
],
...
<nativeK_name>” : [
<nativeK_root1>,
...
<nativeK_rootMK>,
]
}
```
## "roots" element
The “roots” element describes entry points which can be accessed by arbitrary native code.
Each root has the following elements:
```json
{
"library": "<library URI>",
"class": "<class name>",
"name": "<member name>",
"action": "<action>"
}
```
| Element | Meaning | Can be omitted |
| ------- | ----------------------------------------- | ---------------------------------- |
| library | Library URI of the entry point. | No. |
| class | Dart class name. | Omitted for top-level functions. |
| name | Dart function name or member name. | Omitted for class-related actions. |
| action | Specifies kind of the entry point access. | Depends on the entry point. |
The following actions are supported:
* _"create-instance"_ - native code creates an instance of given Dart class.
* _"call"_ - native code calls given Dart function or member.
* _"get"_ - native code calls given getter or retrieves value of a given field.
* _“set”_ - native code calls given setter or sets value to a given field.
If action element is omitted, the following actions are assumed by default:
* For classes - “create-instance”.
* For fields - both “get” and “set” (only “get” if a field is final).
* For others - “call”.
If needed, the description of an entry point can be extended by supporting
more elements or actions.
## “native-methods” element
The “native-methods” section contains description of entry points accessed from
specific native methods. It can be used to declare behavior of a native method
more accurately.
Each element in “native-methods” section is identified by the native name - the
name specified after the native clause in the Dart method or function declaration.
Native method descriptor may contain arbitrary number of entry points.
In addition to the declaration of entry points described above, native methods
may contain the root with the action “return”, which describes the specific
concrete type of a Dart instance returned from the native method:
```json
{
"action": "return",
"library": "<library URI>",
"class": "<class name>",
"nullable": "false|true"
}
```
“nullable” attribute may be omitted defaulting to “true”.
If “nullable” is “true” (or omitted), then native method can return an instance
of the given class or null.