[vm] Support definition of entry-points via @pragma('vm.extern') annotations.
The `@pragma` annotations are evaluated by the constants transformation and
visible to TFA and the precompiler, which match on the "options" field of the
annotation to determine whether to mark the class/procedure as a root.
This required enabling the transformation of annotation constants by default.
# Test Plan
The "vmservice_io.main" entry-point is removed from `main.cc` and annotated with
`@pragma`. All precompiler tests will crash if "vmservice_io.main" is not
available at runtime.
Debug/release precompiler bots are visible in "cl-linux" button.
Change-Id: I03c5d6ba7918672ed9905fcaee8dabe675a93a5d
Reviewed-on: https://dart-review.googlesource.com/56660
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index 13b28bf..6eb4d87 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -900,6 +900,7 @@
type InstanceConstant extends Constant {
Byte tag = 7;
+ CanonicalNameReference class;
List<DartType> typeArguments;
List<[FieldReference, ConstantReference]> values;
}
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index e0fe377..de05b25 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -98,6 +98,10 @@
/// The `dart:mirrors` library, or `null` if the component does not use it.
Library _mirrorsLibrary;
+ Class _pragmaClass;
+ Field _pragmaName;
+ Field _pragmaOptions;
+
CoreTypes(Component component)
: index = new LibraryIndex.coreLibraries(component);
@@ -318,6 +322,18 @@
return _objectEquals ??= index.getMember('dart:core', 'Object', '==');
}
+ Class get pragmaClass {
+ return _pragmaClass ??= index.getClass('dart:core', 'pragma');
+ }
+
+ Field get pragmaName {
+ return _pragmaName ??= index.getMember('dart:core', 'pragma', 'name');
+ }
+
+ Field get pragmaOptions {
+ return _pragmaOptions ??= index.getMember('dart:core', 'pragma', 'options');
+ }
+
Procedure get printProcedure {
return _printProcedure ??= index.getTopLevelMember('dart:core', 'print');
}
diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart
index 332017e..72a6607 100644
--- a/pkg/vm/lib/kernel_front_end.dart
+++ b/pkg/vm/lib/kernel_front_end.dart
@@ -163,7 +163,7 @@
constants.transformComponent(component, vmConstants,
keepFields: true,
strongMode: true,
- evaluateAnnotations: false,
+ evaluateAnnotations: true,
enableAsserts: enableAsserts,
errorReporter:
new ForwardConstantEvaluationErrors(context, typeEnvironment));
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 40a3e79..4c6eff1 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -12,6 +12,7 @@
import 'package:kernel/ast.dart' hide Statement, StatementVisitor;
import 'package:kernel/class_hierarchy.dart' show ClosedWorldClassHierarchy;
import 'package:kernel/library_index.dart' show LibraryIndex;
+import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_environment.dart';
import 'calls.dart';
@@ -1148,7 +1149,7 @@
final Map<Member, Summary> _summaries = <Member, Summary>{};
final Map<Field, _FieldValue> _fieldValues = <Field, _FieldValue>{};
- TypeFlowAnalysis(
+ TypeFlowAnalysis(Component component, CoreTypes coreTypes,
ClosedWorldClassHierarchy hierarchy, this.environment, this.libraryIndex,
{List<String> entryPointsJSONFiles})
: nativeCodeOracle = new NativeCodeOracle(libraryIndex) {
@@ -1161,6 +1162,8 @@
if (entryPointsJSONFiles != null) {
nativeCodeOracle.processEntryPointsJSONFiles(entryPointsJSONFiles, this);
}
+
+ component.accept(new PragmaEntryPointsVisitor(coreTypes, this));
}
_Invocation get currentInvocation => workList.callStack.last;
diff --git a/pkg/vm/lib/transformations/type_flow/native_code.dart b/pkg/vm/lib/transformations/type_flow/native_code.dart
index d3f72d6..8e4a171 100644
--- a/pkg/vm/lib/transformations/type_flow/native_code.dart
+++ b/pkg/vm/lib/transformations/type_flow/native_code.dart
@@ -11,6 +11,7 @@
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.
@@ -31,6 +32,66 @@
ConcreteType addAllocatedClass(Class c);
}
+/// Some entry points are not listed in any JSON file but are marked with the
+/// `@pragma('vm.entry_point', ...)` annotation instead.
+///
+/// Currently Procedure`s (action "call") can be annotated in this way.
+//
+// TODO(sjindel): Support all types of entry points.
+class PragmaEntryPointsVisitor extends RecursiveVisitor {
+ final EntryPointsListener entryPoints;
+ final CoreTypes coreTypes;
+
+ PragmaEntryPointsVisitor(this.coreTypes, this.entryPoints);
+
+ bool _definesRoot(InstanceConstant constant) {
+ if (constant.classReference.node != coreTypes.pragmaClass) return false;
+
+ Constant name = constant.fieldValues[coreTypes.pragmaName.reference];
+ assertx(name != null);
+ if (name is! StringConstant ||
+ (name as StringConstant).value != "vm.entry_point") {
+ return false;
+ }
+
+ Constant options = constant.fieldValues[coreTypes.pragmaOptions.reference];
+ assertx(options != null);
+ if (options is NullConstant) return true;
+ return options is BoolConstant && options.value;
+ }
+
+ bool _annotationsDefineRoot(List<Expression> annotations) {
+ for (var annotation in annotations) {
+ if (annotation is ConstantExpression) {
+ Constant constant = annotation.constant;
+ if (constant is InstanceConstant) {
+ if (_definesRoot(constant)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @override
+ visitClass(Class klass) {
+ if (_annotationsDefineRoot(klass.annotations)) {
+ entryPoints.addAllocatedClass(klass);
+ }
+ klass.visitChildren(this);
+ }
+
+ @override
+ visitProcedure(Procedure proc) {
+ if (_annotationsDefineRoot(proc.annotations)) {
+ entryPoints.addRawCall(proc.isInstanceMember
+ ? new InterfaceSelector(proc, callKind: CallKind.Method)
+ : new DirectSelector(proc, callKind: CallKind.Method));
+ }
+ }
+}
+
/// Provides insights into the behavior of native code.
class NativeCodeOracle {
final Map<String, List<Map<String, dynamic>>> _nativeMethods =
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index b3a037a..cfcbec7 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -51,7 +51,8 @@
Statistics.reset();
final analysisStopWatch = new Stopwatch()..start();
- final typeFlowAnalysis = new TypeFlowAnalysis(hierarchy, types, libraryIndex,
+ final typeFlowAnalysis = new TypeFlowAnalysis(
+ component, coreTypes, hierarchy, types, libraryIndex,
entryPointsJSONFiles: entryPoints);
Procedure main = component.mainMethod;
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index d46910b..7b8346b 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -857,7 +857,6 @@
{"dart:isolate", "::", "_getIsolateScheduleImmediateClosure"},
{"dart:isolate", "::", "_setupHooks"},
{"dart:isolate", "::", "_startMainIsolate"},
- {"dart:vmservice_io", "::", "main"},
// Fields
{"dart:_builtin", "::", "_isolateId"},
{"dart:_builtin", "::", "_loadPort"},
diff --git a/runtime/bin/vmservice/vmservice_io.dart b/runtime/bin/vmservice/vmservice_io.dart
index 14f6cb6..9dc5954 100644
--- a/runtime/bin/vmservice/vmservice_io.dart
+++ b/runtime/bin/vmservice/vmservice_io.dart
@@ -220,6 +220,7 @@
_signalSubscription = _signalWatch(ProcessSignal.SIGQUIT).listen(_onSignal);
}
+@pragma("vm.entry_point")
main() {
// Set embedder hooks.
VMServiceEmbedderHooks.cleanup = cleanupCallback;
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 5b2415a..414b63a 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -313,6 +313,7 @@
// Start with the allocations and invocations that happen from C++.
AddRoots(embedder_entry_points);
+ AddAnnotatedRoots();
// Compile newly found targets and add their callees until we reach a
// fixed point.
@@ -339,6 +340,7 @@
Class& null_class = Class::Handle(Z);
Function& null_function = Function::Handle(Z);
I->object_store()->set_future_class(null_class);
+ I->object_store()->set_pragma_class(null_class);
I->object_store()->set_completer_class(null_class);
I->object_store()->set_stream_iterator_class(null_class);
I->object_store()->set_symbol_class(null_class);
@@ -732,6 +734,8 @@
}
void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) {
+ PrecompilerEntryPointsPrinter entry_points_printer(zone());
+
// Note that <rootlibrary>.main is not a root. The appropriate main will be
// discovered through _getMainClosure.
@@ -739,8 +743,6 @@
AddSelector(Symbols::Call()); // For speed, not correctness.
- PrecompilerEntryPointsPrinter entry_points_printer(zone());
-
// Allocated from C++.
Class& cls = Class::Handle(Z);
for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
@@ -1505,6 +1507,61 @@
}
}
+// Adds all values annotated with @pragma('vm.entry_point') as roots.
+void Precompiler::AddAnnotatedRoots() {
+ auto& lib = Library::Handle(Z);
+ auto& cls = Class::Handle(isolate()->object_store()->pragma_class());
+ auto& functions = Array::Handle(Z);
+ auto& function = Function::Handle(Z);
+ auto& metadata = Array::Handle(Z);
+ auto& pragma = Object::Handle(Z);
+ auto& pragma_options = Object::Handle(Z);
+ auto& pragma_name_field = Field::Handle(Z, cls.LookupField(Symbols::name()));
+ auto& pragma_options_field =
+ Field::Handle(Z, cls.LookupField(Symbols::options()));
+
+ for (intptr_t i = 0; i < libraries_.Length(); i++) {
+ lib ^= libraries_.At(i);
+ ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
+ while (it.HasNext()) {
+ cls = it.GetNextClass();
+ functions = cls.functions();
+ for (intptr_t k = 0; k < functions.Length(); k++) {
+ function ^= functions.At(k);
+ if (!function.has_pragma()) continue;
+ metadata ^= lib.GetMetadata(function);
+ if (metadata.IsNull()) continue;
+
+ bool is_entry_point = false;
+ for (intptr_t i = 0; i < metadata.Length(); i++) {
+ pragma = metadata.At(i);
+ if (pragma.clazz() != isolate()->object_store()->pragma_class()) {
+ continue;
+ }
+ if (Instance::Cast(pragma).GetField(pragma_name_field) !=
+ Symbols::vm_entry_point().raw()) {
+ continue;
+ }
+ pragma_options =
+ Instance::Cast(pragma).GetField(pragma_options_field);
+ if (pragma_options.raw() == Bool::null() ||
+ pragma_options.raw() == Bool::True().raw()) {
+ is_entry_point = true;
+ break;
+ }
+ }
+
+ if (!is_entry_point) continue;
+
+ AddFunction(function);
+ if (function.IsGenerativeConstructor()) {
+ AddInstantiatedClass(cls);
+ }
+ }
+ }
+ }
+}
+
void Precompiler::CheckForNewDynamicFunctions() {
Library& lib = Library::Handle(Z);
Class& cls = Class::Handle(Z);
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index 93e8326..ccbac6c 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -351,6 +351,7 @@
void DoCompileAll(Dart_QualifiedFunctionName embedder_entry_points[]);
void AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]);
+ void AddAnnotatedRoots();
void AddEntryPoints(Dart_QualifiedFunctionName entry_points[],
PrecompilerEntryPointsPrinter* entry_points_printer);
void Iterate();
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 93d70f3..fc17971 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -11097,10 +11097,10 @@
intptr_t list_length = ReadListLength(); // read list length.
const Array& metadata_values =
Array::Handle(Z, Array::New(list_length, H.allocation_space()));
+ Instance& value = Instance::Handle(Z);
for (intptr_t i = 0; i < list_length; ++i) {
// this will (potentially) read the expression, but reset the position.
- Instance& value = Instance::ZoneHandle(
- Z, constant_evaluator_.EvaluateExpression(ReaderOffset()));
+ value = constant_evaluator_.EvaluateExpression(ReaderOffset());
SkipExpression(); // read (actual) initializer.
metadata_values.SetAt(i, value);
}
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 607c1e9..cabed58 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -432,23 +432,14 @@
ASSERT(constant_table.Release().raw() == constant_table_array.raw());
}
-RawString* KernelLoader::DetectExternalName() {
+RawString* KernelLoader::DetectExternalNameCtor() {
builder_.ReadTag();
builder_.ReadPosition();
NameIndex annotation_class = H.EnclosingName(
builder_.ReadCanonicalNameReference()); // read target reference,
- ASSERT(H.IsClass(annotation_class));
- StringIndex class_name_index = H.CanonicalNameString(annotation_class);
- // Just compare by name, do not generate the annotation class.
- if (!H.StringEquals(class_name_index, "ExternalName")) {
- builder_.SkipArguments();
- return String::null();
- }
- ASSERT(H.IsLibrary(H.CanonicalNameParent(annotation_class)));
- StringIndex library_name_index =
- H.CanonicalNameString(H.CanonicalNameParent(annotation_class));
- if (!H.StringEquals(library_name_index, "dart:_internal")) {
+ if (!IsClassName(annotation_class, Symbols::DartInternal(),
+ Symbols::ExternalName())) {
builder_.SkipArguments();
return String::null();
}
@@ -471,6 +462,30 @@
return result.raw();
}
+bool KernelLoader::IsClassName(NameIndex name,
+ const String& library,
+ const String& klass) {
+ ASSERT(H.IsClass(name));
+ StringIndex class_name_index = H.CanonicalNameString(name);
+
+ if (!H.StringEquals(class_name_index, klass.ToCString())) {
+ return false;
+ }
+ ASSERT(H.IsLibrary(H.CanonicalNameParent(name)));
+ StringIndex library_name_index =
+ H.CanonicalNameString(H.CanonicalNameParent(name));
+ return H.StringEquals(library_name_index, library.ToCString());
+}
+
+bool KernelLoader::DetectPragmaCtor() {
+ builder_.ReadTag();
+ builder_.ReadPosition();
+ NameIndex annotation_class = H.EnclosingName(
+ builder_.ReadCanonicalNameReference()); // read target reference
+ builder_.SkipArguments();
+ return IsClassName(annotation_class, Symbols::DartCore(), Symbols::Pragma());
+}
+
void KernelLoader::LoadNativeExtensionLibraries(
const Array& constant_table_array) {
const intptr_t length = !potential_extension_libraries_.IsNull()
@@ -510,7 +525,7 @@
}
} else if (tag == kConstructorInvocation ||
tag == kConstConstructorInvocation) {
- uri_path = DetectExternalName();
+ uri_path = DetectExternalNameCtor();
} else {
builder_.SkipExpression();
}
@@ -1288,6 +1303,106 @@
class_index, &class_helper);
}
+// Read annotations on a procedure to identify potential VM-specific directives.
+//
+// Output parameters:
+//
+// `native_name`: non-null if `ExternalName(<name>)` was identified.
+//
+// `is_potential_native`: non-null if there may be an `ExternalName`
+// annotation and we need to re-try after reading the constants table.
+//
+// `has_pragma_annotation`: non-null if @pragma(...) was found (no information
+// is given on the kind of pragma directive).
+//
+void KernelLoader::ReadProcedureAnnotations(intptr_t annotation_count,
+ String* native_name,
+ bool* is_potential_native,
+ bool* has_pragma_annotation) {
+ *is_potential_native = false;
+ *has_pragma_annotation = false;
+ String& detected_name = String::Handle(Z);
+ Class& pragma_class = Class::Handle(Z, I->object_store()->pragma_class());
+ for (intptr_t i = 0; i < annotation_count; ++i) {
+ const intptr_t tag = builder_.PeekTag();
+ if (tag == kConstructorInvocation || tag == kConstConstructorInvocation) {
+ const intptr_t start = builder_.ReaderOffset();
+ detected_name = DetectExternalNameCtor();
+ if (!detected_name.IsNull()) {
+ *native_name = detected_name.raw();
+ continue;
+ }
+
+ builder_.SetOffset(start);
+ if (DetectPragmaCtor()) {
+ *has_pragma_annotation = true;
+ }
+ } else if (tag == kConstantExpression) {
+ const Array& constant_table_array =
+ Array::Handle(kernel_program_info_.constants());
+ if (constant_table_array.IsNull()) {
+ // We can only read in the constant table once all classes have been
+ // finalized (otherwise we can't create instances of the classes!).
+ //
+ // We therefore delay the scanning for `ExternalName {name: ... }`
+ // constants in the annotation list to later.
+ *is_potential_native = true;
+
+ if (program_ == nullptr) {
+ builder_.SkipExpression();
+ continue;
+ }
+
+ // For pragma annotations, we seek into the constants table and peek
+ // into the Kernel representation of the constant.
+ //
+ // TODO(sjindel): Refactor `ExternalName` handling to do this as well
+ // and avoid the "potential natives" list.
+
+ builder_.ReadByte(); // Skip the tag.
+
+ const intptr_t offset_in_constant_table = builder_.ReadUInt();
+
+ AlternativeReadingScope scope(&builder_.reader_,
+ program_->constant_table_offset());
+
+ // Seek into the position within the constant table where we can inspect
+ // this constant's Kernel representation.
+ builder_.ReadUInt(); // skip constant table size
+ builder_.SetOffset(builder_.ReaderOffset() + offset_in_constant_table);
+ uint8_t tag = builder_.ReadTag();
+ if (tag == kInstanceConstant) {
+ *has_pragma_annotation =
+ IsClassName(builder_.ReadCanonicalNameReference(),
+ Symbols::DartCore(), Symbols::Pragma());
+ }
+ } else {
+ KernelConstantsMap constant_table(constant_table_array.raw());
+ builder_.ReadByte(); // Skip the tag.
+
+ // Obtain `dart:_internal::ExternalName.name`.
+ EnsureExternalClassIsLookedUp();
+
+ const intptr_t constant_table_index = builder_.ReadUInt();
+ const Object& constant =
+ Object::Handle(constant_table.GetOrDie(constant_table_index));
+ if (constant.clazz() == external_name_class_.raw()) {
+ const Instance& instance =
+ Instance::Handle(Instance::RawCast(constant.raw()));
+ *native_name =
+ String::RawCast(instance.GetField(external_name_field_));
+ } else if (constant.clazz() == pragma_class.raw()) {
+ *has_pragma_annotation = true;
+ }
+ ASSERT(constant_table.Release().raw() == constant_table_array.raw());
+ }
+ } else {
+ builder_.SkipExpression();
+ continue;
+ }
+ }
+}
+
void KernelLoader::LoadProcedure(const Library& library,
const Class& owner,
bool in_class,
@@ -1304,78 +1419,14 @@
bool is_method = in_class && !procedure_helper.IsStatic();
bool is_abstract = procedure_helper.IsAbstract();
bool is_external = procedure_helper.IsExternal();
- String* native_name = NULL;
- intptr_t annotation_count;
- bool is_potential_native = false;
- if (is_external) {
- // Maybe it has a native implementation, which is not external as far as
- // the VM is concerned because it does have an implementation. Check for
- // an ExternalName annotation and extract the string from it.
- annotation_count = builder_.ReadListLength(); // read list length.
- for (int i = 0; i < annotation_count; ++i) {
- const intptr_t tag = builder_.PeekTag();
- if (tag == kConstructorInvocation || tag == kConstConstructorInvocation) {
- String& detected_name = String::Handle(DetectExternalName());
- if (detected_name.IsNull()) continue;
-
- is_external = false;
- native_name = &detected_name;
-
- // Skip remaining annotations
- for (++i; i < annotation_count; ++i) {
- builder_.SkipExpression(); // read ith annotation.
- }
- } else if (tag == kConstantExpression) {
- if (kernel_program_info_.constants() == Array::null()) {
- // We can only read in the constant table once all classes have been
- // finalized (otherwise we can't create instances of the classes!).
- //
- // We therefore delay the scanning for `ExternalName {name: ... }`
- // constants in the annotation list to later.
- is_potential_native = true;
- builder_.SkipExpression();
- } else {
- builder_.ReadByte(); // Skip the tag.
-
- // Obtain `dart:_internal::ExternalName.name`.
- EnsureExternalClassIsLookedUp();
-
- const Array& constant_table_array =
- Array::Handle(kernel_program_info_.constants());
- KernelConstantsMap constant_table(constant_table_array.raw());
-
- // We have a candiate. Let's look if it's an instance of the
- // ExternalName class.
- const intptr_t constant_table_index = builder_.ReadUInt();
- const Object& constant =
- Object::Handle(constant_table.GetOrDie(constant_table_index));
- ASSERT(constant_table.Release().raw() == constant_table_array.raw());
- if (constant.clazz() == external_name_class_.raw()) {
- const Instance& instance =
- Instance::Handle(Instance::RawCast(constant.raw()));
-
- // We found the annotation, let's flag the function as native and
- // set the native name!
- native_name = &String::Handle(
- String::RawCast(instance.GetField(external_name_field_)));
-
- // Skip remaining annotations
- for (++i; i < annotation_count; ++i) {
- builder_.SkipExpression(); // read ith annotation.
- }
- break;
- }
- }
- } else {
- builder_.SkipExpression();
- continue;
- }
- }
- procedure_helper.SetJustRead(ProcedureHelper::kAnnotations);
- } else {
- procedure_helper.ReadUntilIncluding(ProcedureHelper::kAnnotations);
- annotation_count = procedure_helper.annotation_count_;
- }
+ String& native_name = String::Handle(Z);
+ bool is_potential_native;
+ bool has_pragma_annotation;
+ const intptr_t annotation_count = builder_.ReadListLength();
+ ReadProcedureAnnotations(annotation_count, &native_name, &is_potential_native,
+ &has_pragma_annotation);
+ is_external = is_external && native_name.IsNull();
+ procedure_helper.SetJustRead(ProcedureHelper::kAnnotations);
const Object& script_class =
ClassForScriptAt(owner, procedure_helper.source_uri_index_);
RawFunction::Kind kind = GetFunctionType(procedure_helper.kind_);
@@ -1384,8 +1435,9 @@
!is_method, // is_static
false, // is_const
is_abstract, is_external,
- native_name != NULL, // is_native
+ !native_name.IsNull(), // is_native
script_class, procedure_helper.position_));
+ function.set_has_pragma(has_pragma_annotation);
function.set_end_token_pos(procedure_helper.end_position_);
functions_.Add(&function);
function.set_kernel_offset(procedure_offset);
@@ -1426,8 +1478,8 @@
}
ASSERT(function_node_helper.async_marker_ == FunctionNodeHelper::kSync);
- if (native_name != NULL) {
- function.set_native_name(*native_name);
+ if (!native_name.IsNull()) {
+ function.set_native_name(native_name);
}
if (is_potential_native) {
EnsurePotentialNatives();
@@ -1451,7 +1503,7 @@
.IsNull());
}
- if (FLAG_enable_mirrors && annotation_count > 0) {
+ if (annotation_count > 0) {
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
procedure_offset);
}
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index 21f4bb2..b8a70a9 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -142,10 +142,26 @@
static void FinishLoading(const Class& klass);
const Array& ReadConstantTable();
- RawString* DetectExternalName();
+
+ // Check for the presence of a (possibly const) constructor for the
+ // 'ExternalName' class. If found, returns the name parameter to the
+ // constructor.
+ RawString* DetectExternalNameCtor();
+
+ // Check for the presence of a (possibly const) constructor for the 'pragma'
+ // class. Returns whether it was found (no details about the type of pragma).
+ bool DetectPragmaCtor();
+
+ bool IsClassName(NameIndex name, const String& library, const String& klass);
+
void AnnotateNativeProcedures(const Array& constant_table);
void LoadNativeExtensionLibraries(const Array& constant_table);
+ void ReadProcedureAnnotations(intptr_t annotation_count,
+ String* native_name,
+ bool* is_potential_native,
+ bool* has_pragma_annotation);
+
const String& DartSymbolPlain(StringIndex index) {
return translation_helper_.DartSymbolPlain(index);
}
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index de61db2..ad5aff3 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -7079,6 +7079,7 @@
result.set_is_intrinsic(false);
result.set_is_redirecting(false);
result.set_is_generated_body(false);
+ result.set_has_pragma(false);
result.set_always_inline(false);
result.set_is_polymorphic_target(false);
NOT_IN_PRECOMPILED(result.set_state_bits(0));
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 1d8dc12..78e036a 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2870,7 +2870,8 @@
V(External, is_external) \
V(GeneratedBody, is_generated_body) \
V(AlwaysInline, always_inline) \
- V(PolymorphicTarget, is_polymorphic_target)
+ V(PolymorphicTarget, is_polymorphic_target) \
+ V(HasPragma, has_pragma)
#define DEFINE_ACCESSORS(name, accessor_name) \
void set_##accessor_name(bool value) const { \
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 2d62110..e2d676d 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -220,6 +220,10 @@
ASSERT(!cls.IsNull());
set_compiletime_error_class(cls);
+ cls = core_lib.LookupClassAllowPrivate(Symbols::Pragma());
+ ASSERT(!cls.IsNull());
+ set_pragma_class(cls);
+
// Cache the core private functions used for fast instance of checks.
simple_instance_of_function_ =
PrivateObjectLookup(Symbols::_simpleInstanceOf());
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 891760f..5c9e90f 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -56,6 +56,7 @@
RW(TypeArguments, type_argument_string) \
RW(TypeArguments, type_argument_int) \
RW(Class, compiletime_error_class) \
+ RW(Class, pragma_class) \
RW(Class, future_class) \
RW(Class, completer_class) \
RW(Class, stream_iterator_class) \
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index ac5cd08..0226504 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -5585,6 +5585,16 @@
return IsSymbol(Symbols::Patch());
}
+bool Parser::IsPragmaAnnotation(TokenPosition pos) {
+ if (pos == TokenPosition::kNoSource) {
+ return false;
+ }
+ TokenPosScope saved_pos(this);
+ SetPosition(pos);
+ ExpectToken(Token::kAT);
+ return IsSymbol(Symbols::Pragma());
+}
+
TokenPosition Parser::SkipMetadata() {
if (CurrentToken() != Token::kAT) {
return TokenPosition::kNoSource;
@@ -5966,6 +5976,8 @@
ConsumeToken();
is_external = true;
}
+ const bool has_pragma = IsPragmaAnnotation(metadata_pos);
+
// Parse optional result type.
if (IsFunctionReturnType()) {
// It is too early to resolve the type here, since it can be a result type
@@ -5999,7 +6011,7 @@
/* is_abstract = */ false, is_external,
/* is_native = */ false, // May change.
owner, decl_begin_pos));
-
+ func.set_has_pragma(has_pragma);
ASSERT(innermost_function().IsNull());
innermost_function_ = func.raw();
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 12d0557..ca45d6c 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -414,6 +414,7 @@
void SkipBlock();
TokenPosition SkipMetadata();
bool IsPatchAnnotation(TokenPosition pos);
+ bool IsPragmaAnnotation(TokenPosition pos);
void SkipTypeArguments();
void SkipTypeParameters();
void SkipType(bool allow_void);
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index a68bce5..f220ca3 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -170,6 +170,7 @@
V(_MixinAppType, "_MixinAppType") \
V(TypeArguments, "TypeArguments") \
V(Patch, "patch") \
+ V(Pragma, "pragma") \
V(PatchClass, "PatchClass") \
V(Function, "Function") \
V(_Closure, "_Closure") \
@@ -444,6 +445,7 @@
V(DartLibraryMirrors, "dart.library.mirrors") \
V(_name, "_name") \
V(name, "name") \
+ V(options, "options") \
V(_classRangeCheck, "_classRangeCheck") \
V(_classRangeCheckNegative, "_classRangeCheckNegative") \
V(_classRangeAssert, "_classRangeAssert") \
@@ -454,7 +456,8 @@
V(DartDeveloperCausalAsyncStacks, "dart.developer.causal_async_stacks") \
V(_AsyncStarListenHelper, "_asyncStarListenHelper") \
V(GrowRegExpStack, "_growRegExpStack") \
- V(DebugProcedureName, ":Eval")
+ V(DebugProcedureName, ":Eval") \
+ V(vm_entry_point, "vm.entry_point")
// Contains a list of frequently used strings in a canonicalized form. This
// list is kept in the vm_isolate in order to share the copy across isolates