mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 14:49:43 +00:00
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:
parent
fb4f887eff
commit
3d2b66074c
|
@ -903,6 +903,7 @@ type ListConstant extends Constant {
|
|||
|
||||
type InstanceConstant extends Constant {
|
||||
Byte tag = 7;
|
||||
CanonicalNameReference class;
|
||||
List<DartType> typeArguments;
|
||||
List<[FieldReference, ConstantReference]> values;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -220,6 +220,7 @@ _registerSignalHandler() {
|
|||
_signalSubscription = _signalWatch(ProcessSignal.SIGQUIT).listen(_onSignal);
|
||||
}
|
||||
|
||||
@pragma("vm.entry_point")
|
||||
main() {
|
||||
// Set embedder hooks.
|
||||
VMServiceEmbedderHooks.cleanup = cleanupCallback;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 { \
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue