Reland "[vm] Support definition of entry-points via @pragma('vm.extern') annotations.""

When the constant transformation is enabled on annotations, we need to fix handling
of @ExternalName annotations in the kernel_loader to ensure that we are setting
is_external = false on native methods.

The original revision is in patchset 1.

# Test Plan

The only regression was on benchmarks, Golem results are pending.

Change-Id: Ib80bb9f532299056e770a3b378cc5ad9ee451f57
Reviewed-on: https://dart-review.googlesource.com/56960
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
Samir Jindel 2018-05-30 20:06:40 +00:00 committed by commit-bot@chromium.org
parent fb4f887eff
commit 3d2b66074c
20 changed files with 335 additions and 101 deletions

View file

@ -903,6 +903,7 @@ type ListConstant extends Constant {
type InstanceConstant extends Constant {
Byte tag = 7;
CanonicalNameReference class;
List<DartType> typeArguments;
List<[FieldReference, ConstantReference]> values;
}

View file

@ -98,6 +98,10 @@ class CoreTypes {
/// 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 @@ class CoreTypes {
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');
}

View file

@ -163,7 +163,7 @@ Future _performConstantEvaluation(
constants.transformComponent(component, vmConstants,
keepFields: true,
strongMode: true,
evaluateAnnotations: false,
evaluateAnnotations: true,
enableAsserts: enableAsserts,
errorReporter:
new ForwardConstantEvaluationErrors(context, typeEnvironment));

View file

@ -12,6 +12,7 @@ import 'dart:math' show max;
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 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
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 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
if (entryPointsJSONFiles != null) {
nativeCodeOracle.processEntryPointsJSONFiles(entryPointsJSONFiles, this);
}
component.accept(new PragmaEntryPointsVisitor(coreTypes, this));
}
_Invocation get currentInvocation => workList.callStack.last;

View file

@ -11,6 +11,7 @@ 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.
@ -31,6 +32,66 @@ abstract class EntryPointsListener {
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 =

View file

@ -51,7 +51,8 @@ Component transformComponent(
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;

View file

@ -857,7 +857,6 @@ static Dart_QualifiedFunctionName standalone_entry_points[] = {
{"dart:isolate", "::", "_getIsolateScheduleImmediateClosure"},
{"dart:isolate", "::", "_setupHooks"},
{"dart:isolate", "::", "_startMainIsolate"},
{"dart:vmservice_io", "::", "main"},
// Fields
{"dart:_builtin", "::", "_isolateId"},
{"dart:_builtin", "::", "_loadPort"},

View file

@ -220,6 +220,7 @@ _registerSignalHandler() {
_signalSubscription = _signalWatch(ProcessSignal.SIGQUIT).listen(_onSignal);
}
@pragma("vm.entry_point")
main() {
// Set embedder hooks.
VMServiceEmbedderHooks.cleanup = cleanupCallback;

View file

@ -313,6 +313,7 @@ void Precompiler::DoCompileAll(
// 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 @@ void Precompiler::DoCompileAll(
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 PrecompilerEntryPointsPrinter::DescribeClass(JSONWriter* writer,
}
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 @@ void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) {
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 @@ void Precompiler::AddInstantiatedClass(const Class& cls) {
}
}
// 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);

View file

@ -351,6 +351,7 @@ class Precompiler : public ValueObject {
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();

View file

@ -11097,10 +11097,10 @@ RawObject* StreamingFlowGraphBuilder::EvaluateMetadata(
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);
}

View file

@ -416,6 +416,7 @@ void KernelLoader::AnnotateNativeProcedures(const Array& constant_table_array) {
native_name ^= constant.GetField(external_name_field_);
function.set_is_native(true);
function.set_native_name(native_name);
function.set_is_external(false);
break;
}
} else {
@ -432,23 +433,14 @@ void KernelLoader::AnnotateNativeProcedures(const Array& constant_table_array) {
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 +463,30 @@ RawString* KernelLoader::DetectExternalName() {
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 +526,7 @@ void KernelLoader::LoadNativeExtensionLibraries(
}
} else if (tag == kConstructorInvocation ||
tag == kConstConstructorInvocation) {
uri_path = DetectExternalName();
uri_path = DetectExternalNameCtor();
} else {
builder_.SkipExpression();
}
@ -1292,6 +1308,106 @@ void KernelLoader::FinishLoading(const Class& klass) {
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,
@ -1308,78 +1424,16 @@ void KernelLoader::LoadProcedure(const Library& library,
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);
// If this is a potential native, we'll unset is_external in
// AnnotateNativeProcedures instead.
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_);
@ -1388,8 +1442,9 @@ void KernelLoader::LoadProcedure(const Library& library,
!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);
@ -1430,8 +1485,8 @@ void KernelLoader::LoadProcedure(const Library& library,
}
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();
@ -1455,7 +1510,7 @@ void KernelLoader::LoadProcedure(const Library& library,
.IsNull());
}
if (FLAG_enable_mirrors && annotation_count > 0) {
if (annotation_count > 0) {
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
procedure_offset);
}

View file

@ -142,10 +142,26 @@ class KernelLoader : public ValueObject {
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);
}

View file

@ -7084,6 +7084,7 @@ RawFunction* Function::New(const String& name,
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));

View file

@ -2884,7 +2884,8 @@ class Function : public Object {
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 { \

View file

@ -220,6 +220,10 @@ void ObjectStore::InitKnownObjects() {
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());

View file

@ -56,6 +56,7 @@ class ObjectPointerVisitor;
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) \

View file

@ -5585,6 +5585,16 @@ bool Parser::IsPatchAnnotation(TokenPosition pos) {
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 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
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 @@ void Parser::ParseTopLevelFunction(TopLevel* top_level,
/* 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();

View file

@ -414,6 +414,7 @@ class Parser : public ValueObject {
void SkipBlock();
TokenPosition SkipMetadata();
bool IsPatchAnnotation(TokenPosition pos);
bool IsPragmaAnnotation(TokenPosition pos);
void SkipTypeArguments();
void SkipTypeParameters();
void SkipType(bool allow_void);

View file

@ -170,6 +170,7 @@ class ObjectPointerVisitor;
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 @@ class ObjectPointerVisitor;
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 @@ class ObjectPointerVisitor;
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