mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 15:21:31 +00:00
[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:
parent
30f0026022
commit
d44ffba254
|
@ -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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
Loading…
Reference in a new issue