[vm/bytecode] Support library dependencies in bytecode for mirrors and vm-service.

Library dependencies are serialized in bytecode as extended annotations.

Fixes the following tests with bytecode:
co19_2/Language/Metadata/before_export_t01
co19_2/Language/Metadata/before_import_t01
service/library_dependency_test

Change-Id: Idfbac6fcd56318d994d179864f0f5fd7d95089a9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114928
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
This commit is contained in:
Alexander Markov 2019-08-29 21:07:06 +00:00 committed by commit-bot@chromium.org
parent aa0af27497
commit d7c944b4c0
4 changed files with 228 additions and 23 deletions

View file

@ -327,7 +327,7 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
flags |= ClassDeclaration.hasSourcePositionsFlag;
position = library.fileOffset;
}
Annotations annotations = getAnnotations(library.annotations);
Annotations annotations = getLibraryAnnotations(library);
if (annotations.object != null) {
flags |= ClassDeclaration.hasAnnotationsFlag;
if (annotations.hasPragma) {
@ -420,6 +420,66 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
return new Annotations(functionDecl, hasPragma);
}
// Insert annotations for library and its dependencies into the
// annotations section. Returns annotations for the library only.
// Bytecode reader will implicitly find library dependencies by reading
// an extra object after reading library annotations.
Annotations getLibraryAnnotations(Library library) {
Annotations annotations = getAnnotations(library.annotations);
final bool emitDependencies =
options.emitAnnotations && library.dependencies.isNotEmpty;
if (annotations.object == null && !emitDependencies) {
return annotations;
}
// We need to emit both annotations and dependencies objects, appending
// null if an object is missing.
if (annotations.object == null) {
final annotationsDecl = new AnnotationsDeclaration(null);
bytecodeComponent.annotations.add(annotationsDecl);
annotations = new Annotations(annotationsDecl, false);
}
if (!emitDependencies) {
bytecodeComponent.annotations.add(new AnnotationsDeclaration(null));
return annotations;
}
// Create a constant object representing library dependencies.
// These objects are used by dart:mirrors and vm-service implementation.
final deps = <Constant>[];
for (var dependency in library.dependencies) {
final prefix = dependency.name != null
? StringConstant(dependency.name)
: NullConstant();
final showNames = dependency.combinators
.where((c) => c.isShow)
.expand((c) => c.names)
.map((name) => StringConstant(name))
.toList();
final hideNames = dependency.combinators
.where((c) => c.isHide)
.expand((c) => c.names)
.map((name) => StringConstant(name))
.toList();
final depAnnots = dependency.annotations.map(_getConstant).toList();
deps.add(ListConstant(const DynamicType(), <Constant>[
StringConstant(dependency.targetLibrary.importUri.toString()),
BoolConstant(dependency.isExport),
BoolConstant(dependency.isDeferred),
prefix,
ListConstant(const DynamicType(), showNames),
ListConstant(const DynamicType(), hideNames),
ListConstant(const DynamicType(), depAnnots),
]));
}
final ObjectHandle dependenciesObject =
objectTable.getHandle(ListConstant(const DynamicType(), deps));
final dependenciesDecl = new AnnotationsDeclaration(dependenciesObject);
bytecodeComponent.annotations.add(dependenciesDecl);
return annotations;
}
FieldDeclaration getFieldDeclaration(Field field, Code initializer) {
int flags = 0;
Constant value;

View file

@ -366,11 +366,14 @@ static RawInstance* CreateCombinatorMirror(const Object& identifiers,
static RawInstance* CreateLibraryDependencyMirror(Thread* thread,
const Instance& importer,
const Namespace& ns,
const Library& importee,
const Array& show_names,
const Array& hide_names,
const Object& metadata,
const LibraryPrefix& prefix,
const String& prefix_name,
const bool is_import,
const bool is_deferred) {
const Library& importee = Library::Handle(ns.library());
const Instance& importee_mirror =
Instance::Handle(CreateLibraryMirror(thread, importee));
if (importee_mirror.IsNull()) {
@ -378,8 +381,6 @@ static RawInstance* CreateLibraryDependencyMirror(Thread* thread,
return Instance::null();
}
const Array& show_names = Array::Handle(ns.show_names());
const Array& hide_names = Array::Handle(ns.hide_names());
intptr_t n = show_names.IsNull() ? 0 : show_names.Length();
intptr_t m = hide_names.IsNull() ? 0 : hide_names.Length();
const Array& combinators = Array::Handle(Array::New(n + m));
@ -396,12 +397,6 @@ static RawInstance* CreateLibraryDependencyMirror(Thread* thread,
combinators.SetAt(i++, t);
}
Object& metadata = Object::Handle(ns.GetMetadata());
if (metadata.IsError()) {
Exceptions::PropagateError(Error::Cast(metadata));
UNREACHABLE();
}
const Array& args = Array::Handle(Array::New(7));
args.SetAt(0, importer);
if (importee.Loaded() || prefix.IsNull()) {
@ -413,14 +408,109 @@ static RawInstance* CreateLibraryDependencyMirror(Thread* thread,
args.SetAt(1, prefix);
}
args.SetAt(2, combinators);
args.SetAt(3, prefix.IsNull() ? Object::null_object()
: String::Handle(prefix.name()));
args.SetAt(3, prefix_name);
args.SetAt(4, Bool::Get(is_import));
args.SetAt(5, Bool::Get(is_deferred));
args.SetAt(6, metadata);
return CreateMirror(Symbols::_LocalLibraryDependencyMirror(), args);
}
static RawInstance* CreateLibraryDependencyMirror(Thread* thread,
const Instance& importer,
const Namespace& ns,
const LibraryPrefix& prefix,
const bool is_import,
const bool is_deferred) {
const Library& importee = Library::Handle(ns.library());
const Array& show_names = Array::Handle(ns.show_names());
const Array& hide_names = Array::Handle(ns.hide_names());
Object& metadata = Object::Handle(ns.GetMetadata());
if (metadata.IsError()) {
Exceptions::PropagateError(Error::Cast(metadata));
UNREACHABLE();
}
auto& prefix_name = String::Handle();
if (!prefix.IsNull()) {
prefix_name = prefix.name();
}
return CreateLibraryDependencyMirror(thread, importer, importee, show_names,
hide_names, metadata, prefix,
prefix_name, is_import, is_deferred);
}
static RawGrowableObjectArray* CreateBytecodeLibraryDependencies(
Thread* thread,
const Library& lib,
const Instance& lib_mirror) {
ASSERT(lib.is_declared_in_bytecode());
// Make sure top level class (containing annotations) is fully loaded.
lib.EnsureTopLevelClassIsFinalized();
const auto& deps = GrowableObjectArray::Handle(GrowableObjectArray::New());
Array& metadata = Array::Handle(lib.GetExtendedMetadata(lib, 1));
if (metadata.Length() == 0) {
return deps.raw();
}
// Library has the only element in the extended metadata.
metadata ^= metadata.At(0);
if (metadata.IsNull()) {
return deps.raw();
}
auto& desc = Array::Handle();
auto& target_uri = String::Handle();
auto& importee = Library::Handle();
auto& is_export = Bool::Handle();
auto& is_deferred = Bool::Handle();
auto& prefix_name = String::Handle();
auto& show_names = Array::Handle();
auto& hide_names = Array::Handle();
auto& dep_metadata = Instance::Handle();
auto& dep = Instance::Handle();
const auto& no_prefix = LibraryPrefix::Handle();
for (intptr_t i = 0, n = metadata.Length(); i < n; ++i) {
desc ^= metadata.At(i);
// Each dependency is represented as an array with the following layout:
// [0] = target library URI (String)
// [1] = is_export (bool)
// [2] = is_deferred (bool)
// [3] = prefix (String or null)
// [4] = list of show names (List<String>)
// [5] = list of hide names (List<String>)
// [6] = annotations
// The library dependencies are encoded by getLibraryAnnotations(),
// pkg/vm/lib/bytecode/gen_bytecode.dart.
target_uri ^= desc.At(0);
is_export ^= desc.At(1);
is_deferred ^= desc.At(2);
prefix_name ^= desc.At(3);
show_names ^= desc.At(4);
hide_names ^= desc.At(5);
dep_metadata ^= desc.At(6);
importee = Library::LookupLibrary(thread, target_uri);
if (importee.IsNull()) {
continue;
}
ASSERT(importee.Loaded());
dep = CreateLibraryDependencyMirror(
thread, lib_mirror, importee, show_names, hide_names, dep_metadata,
no_prefix, prefix_name, !is_export.value(), is_deferred.value());
if (!dep.IsNull()) {
deps.Add(dep);
}
}
return deps.raw();
}
DEFINE_NATIVE_ENTRY(LibraryMirror_fromPrefix, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(LibraryPrefix, prefix,
arguments->NativeArgAt(0));
@ -436,6 +526,10 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_libraryDependencies, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1));
const Library& lib = Library::Handle(ref.GetLibraryReferent());
if (lib.is_declared_in_bytecode()) {
return CreateBytecodeLibraryDependencies(thread, lib, lib_mirror);
}
Array& ports = Array::Handle();
Namespace& ns = Namespace::Handle();
Instance& dep = Instance::Handle();

View file

@ -10264,11 +10264,17 @@ RawObject* Library::GetMetadata(const Object& obj) const {
metadata = kernel::EvaluateMetadata(
field, /* is_annotations_offset = */ obj.IsLibrary());
}
if (metadata.IsArray()) {
ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
field.SetStaticValue(Array::Cast(metadata), true);
if (metadata.IsArray() || metadata.IsNull()) {
ASSERT(metadata.raw() != Object::empty_array().raw());
field.SetStaticValue(
metadata.IsNull() ? Object::null_array() : Array::Cast(metadata),
true);
}
}
if (metadata.IsNull()) {
// Metadata field exists in order to reference extended metadata.
return Object::empty_array().raw();
}
return metadata.raw();
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
@ -10278,9 +10284,7 @@ RawArray* Library::GetExtendedMetadata(const Object& obj,
#if defined(DART_PRECOMPILED_RUNTIME)
return Object::empty_array().raw();
#else
if (!obj.IsFunction()) {
UNREACHABLE();
}
RELEASE_ASSERT(obj.IsFunction() || obj.IsLibrary());
const String& metaname = String::Handle(MakeMetadataName(obj));
Field& field = Field::Handle(GetMetadataField(metaname));
if (field.IsNull()) {

View file

@ -532,7 +532,7 @@ void Library::PrintJSONImpl(JSONStream* stream, bool ref) const {
DictionaryIterator entries(*this);
Object& entry = Object::Handle();
LibraryPrefix& prefix = LibraryPrefix::Handle();
String& prefixName = String::Handle();
String& prefix_name = String::Handle();
while (entries.HasNext()) {
entry = entries.GetNext();
if (entry.IsLibraryPrefix()) {
@ -547,15 +547,62 @@ void Library::PrintJSONImpl(JSONStream* stream, bool ref) const {
jsdep.AddProperty("isDeferred", prefix.is_deferred_load());
jsdep.AddProperty("isExport", false);
jsdep.AddProperty("isImport", true);
prefixName = prefix.name();
ASSERT(!prefixName.IsNull());
jsdep.AddProperty("prefix", prefixName.ToCString());
prefix_name = prefix.name();
ASSERT(!prefix_name.IsNull());
jsdep.AddProperty("prefix", prefix_name.ToCString());
target = ns.library();
jsdep.AddProperty("target", target);
}
}
}
}
if (is_declared_in_bytecode()) {
// Make sure top level class (containing annotations) is fully loaded.
EnsureTopLevelClassIsFinalized();
Array& metadata = Array::Handle(GetExtendedMetadata(*this, 1));
if (metadata.Length() != 0) {
// Library has the only element in the extended metadata.
metadata ^= metadata.At(0);
if (!metadata.IsNull()) {
Thread* thread = Thread::Current();
auto& desc = Array::Handle();
auto& target_uri = String::Handle();
auto& is_export = Bool::Handle();
auto& is_deferred = Bool::Handle();
for (intptr_t i = 0, n = metadata.Length(); i < n; ++i) {
desc ^= metadata.At(i);
// Each dependency is represented as an array with the following
// layout:
// [0] = target library URI (String)
// [1] = is_export (bool)
// [2] = is_deferred (bool)
// [3] = prefix (String or null)
// ...
// The library dependencies are encoded by getLibraryAnnotations(),
// pkg/vm/lib/bytecode/gen_bytecode.dart.
target_uri ^= desc.At(0);
is_export ^= desc.At(1);
is_deferred ^= desc.At(2);
prefix_name ^= desc.At(3);
target = Library::LookupLibrary(thread, target_uri);
if (target.IsNull()) {
continue;
}
JSONObject jsdep(&jsarr);
jsdep.AddProperty("isDeferred", is_deferred.value());
jsdep.AddProperty("isExport", is_export.value());
jsdep.AddProperty("isImport", !is_export.value());
if (!prefix_name.IsNull()) {
jsdep.AddProperty("prefix", prefix_name.ToCString());
}
jsdep.AddProperty("target", target);
}
}
}
}
}
{
JSONArray jsarr(&jsobj, "variables");