diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md index d94fb18d634..3159a05f98a 100644 --- a/pkg/kernel/binary.md +++ b/pkg/kernel/binary.md @@ -903,6 +903,7 @@ type ListConstant extends Constant { type InstanceConstant extends Constant { Byte tag = 7; + CanonicalNameReference class; List typeArguments; List<[FieldReference, ConstantReference]> values; } diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart index e0fe377b750..de05b25bedc 100644 --- a/pkg/kernel/lib/core_types.dart +++ b/pkg/kernel/lib/core_types.dart @@ -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'); } diff --git a/pkg/vm/lib/kernel_front_end.dart b/pkg/vm/lib/kernel_front_end.dart index 332017e3285..72a6607f667 100644 --- a/pkg/vm/lib/kernel_front_end.dart +++ b/pkg/vm/lib/kernel_front_end.dart @@ -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)); diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart index 40a3e79a688..4c6eff14bc3 100644 --- a/pkg/vm/lib/transformations/type_flow/analysis.dart +++ b/pkg/vm/lib/transformations/type_flow/analysis.dart @@ -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 _summaries = {}; final Map _fieldValues = {}; - TypeFlowAnalysis( + TypeFlowAnalysis(Component component, CoreTypes coreTypes, ClosedWorldClassHierarchy hierarchy, this.environment, this.libraryIndex, {List 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; diff --git a/pkg/vm/lib/transformations/type_flow/native_code.dart b/pkg/vm/lib/transformations/type_flow/native_code.dart index d3f72d6a9da..8e4a1719b64 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 '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 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>> _nativeMethods = diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart index b3a037ace42..cfcbec76fef 100644 --- a/pkg/vm/lib/transformations/type_flow/transformer.dart +++ b/pkg/vm/lib/transformations/type_flow/transformer.dart @@ -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; diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index d46910b061b..7b8346b1111 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -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"}, diff --git a/runtime/bin/vmservice/vmservice_io.dart b/runtime/bin/vmservice/vmservice_io.dart index 14f6cb6d0d5..9dc5954632f 100644 --- a/runtime/bin/vmservice/vmservice_io.dart +++ b/runtime/bin/vmservice/vmservice_io.dart @@ -220,6 +220,7 @@ _registerSignalHandler() { _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 5b2415a8f6d..414b63a6eae 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -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 .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); diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h index 93e83260a33..ccbac6caa9b 100644 --- a/runtime/vm/compiler/aot/precompiler.h +++ b/runtime/vm/compiler/aot/precompiler.h @@ -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(); diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc index 93d70f3b58b..fc1797145c3 100644 --- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc +++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc @@ -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); } diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc index e7f45d2e698..e47323929a2 100644 --- a/runtime/vm/kernel_loader.cc +++ b/runtime/vm/kernel_loader.cc @@ -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()` 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); } diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h index 21f4bb26fec..b8a70a9636d 100644 --- a/runtime/vm/kernel_loader.h +++ b/runtime/vm/kernel_loader.h @@ -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); } diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index d379d222983..d6a253320c5 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -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)); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index ee047997898..41efcde4d49 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -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 { \ diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc index 2d621109570..e2d676dc2f0 100644 --- a/runtime/vm/object_store.cc +++ b/runtime/vm/object_store.cc @@ -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()); diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index 891760f74a1..5c9e90fb66d 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -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) \ diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc index ac5cd0887af..0226504b92d 100644 --- a/runtime/vm/parser.cc +++ b/runtime/vm/parser.cc @@ -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(); diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h index 12d0557be37..ca45d6ceec5 100644 --- a/runtime/vm/parser.h +++ b/runtime/vm/parser.h @@ -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); diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h index a68bce58207..f220ca3fb11 100644 --- a/runtime/vm/symbols.h +++ b/runtime/vm/symbols.h @@ -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