[dart/vm] Replaces eager with lazy reading of constants table
Rationale:
Previously all constants were read from the constants table,
whether they were needed (at that point or at all) or not. This
CL replaces that eager reading with a lazy reading that makes
the raw bytes of the constants table available through
KernelProgramInfo and attaches a hashmap to KernelProgramInfo,
which maps offsets into the constant table to evaluated constants.
The maps starts empty and on a miss, the constant is evaluated,
but no sooner than when it it needed.
https://github.com/dart-lang/sdk/issues/36220
Change-Id: Ief4df3d70c950c4beb61d896632ce06cfd12d525
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99088
Commit-Queue: Aart Bik <ajcbik@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index 688f192..42e1d22 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1197,14 +1197,7 @@
kernel::BytecodeReader::LoadClassDeclaration(cls);
}
#endif
- // TODO(36584) : We expect is_type_finalized to be true for all classes
- // here, but with eager reading of the constant table we get into
- // situations where we see classes whose types have not been finalized yet,
- // the real solution is to implement lazy evaluation of constants. This is
- // a temporary workaround until lazy evaluation is implemented.
- if (!cls.is_type_finalized()) {
- FinalizeTypesInClass(cls);
- }
+ ASSERT(cls.is_type_finalized());
ClassFinalizer::FinalizeClass(cls);
return Error::null();
} else {
diff --git a/runtime/vm/compiler/frontend/constant_evaluator.cc b/runtime/vm/compiler/frontend/constant_evaluator.cc
index a69ee37..e5065e2 100644
--- a/runtime/vm/compiler/frontend/constant_evaluator.cc
+++ b/runtime/vm/compiler/frontend/constant_evaluator.cc
@@ -168,10 +168,12 @@
EvaluateNullLiteral();
break;
case kConstantExpression:
- EvaluateConstantExpression(tag);
+ helper_->ReadPosition();
+ helper_->SkipDartType();
+ result_ = EvaluateConstantExpression(helper_->ReadUInt());
break;
case kDeprecated_ConstantExpression:
- EvaluateConstantExpression(tag);
+ result_ = EvaluateConstantExpression(helper_->ReadUInt());
break;
default:
H.ReportError(
@@ -291,6 +293,248 @@
return metadata_values.raw();
}
+RawInstance* ConstantEvaluator::EvaluateConstantExpression(
+ intptr_t constant_offset) {
+ ASSERT(!H.constants().IsNull());
+ ASSERT(!H.constants_table().IsNull()); // raw bytes
+
+ // For kernel-level cache (in contrast with script-level caching),
+ // we need to access the raw constants array inside the shared
+ // KernelProgramInfo directly, so that all scripts will see the
+ // results after new insertions. These accesses at kernel-level
+ // must be locked since mutator and background compiler can
+ // access the array at the same time.
+ {
+ SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
+ KernelConstantsMap constant_map(H.info().constants());
+ result_ ^= constant_map.GetOrNull(constant_offset);
+ ASSERT(constant_map.Release().raw() == H.info().constants());
+ }
+
+ // On miss, evaluate, and insert value.
+ if (result_.IsNull()) {
+ result_ = EvaluateConstant(constant_offset);
+ SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
+ KernelConstantsMap constant_map(H.info().constants());
+ auto insert = constant_map.InsertNewOrGetValue(constant_offset, result_);
+ ASSERT(insert == result_.raw());
+ H.info().set_constants(constant_map.Release()); // update!
+ }
+ return result_.raw();
+}
+
+RawInstance* ConstantEvaluator::EvaluateConstant(intptr_t constant_offset) {
+ // Get reader directly into raw bytes of constant table.
+ KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
+ reader.ReadUInt(); // skip variable-sized int for adjusted constant offset
+ reader.SetOffset(reader.ReaderOffset() + constant_offset);
+ // Construct constant from raw bytes.
+ Instance& instance = Instance::Handle(Z);
+ const intptr_t constant_tag = reader.ReadByte();
+ switch (constant_tag) {
+ case kNullConstant:
+ instance = Instance::null();
+ break;
+ case kBoolConstant:
+ instance = reader.ReadByte() == 1 ? Object::bool_true().raw()
+ : Object::bool_false().raw();
+ break;
+ case kIntConstant: {
+ uint8_t payload = 0;
+ Tag integer_tag = reader.ReadTag(&payload); // read tag.
+ switch (integer_tag) {
+ case kBigIntLiteral: {
+ const String& value = H.DartString(reader.ReadStringReference());
+ instance = Integer::New(value, Heap::kOld);
+ break;
+ }
+ case kSpecializedIntLiteral: {
+ const int64_t value =
+ static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
+ instance = Integer::New(value, Heap::kOld);
+ break;
+ }
+ case kNegativeIntLiteral: {
+ const int64_t value = -static_cast<int64_t>(reader.ReadUInt());
+ instance = Integer::New(value, Heap::kOld);
+ break;
+ }
+ case kPositiveIntLiteral: {
+ const int64_t value = reader.ReadUInt();
+ instance = Integer::New(value, Heap::kOld);
+ break;
+ }
+ default:
+ H.ReportError(
+ script_, TokenPosition::kNoSource,
+ "Cannot lazily read integer: unexpected kernel tag %s (%d)",
+ Reader::TagName(integer_tag), integer_tag);
+ }
+ break;
+ }
+ case kDoubleConstant:
+ instance = Double::New(reader.ReadDouble(), Heap::kOld);
+ break;
+ case kStringConstant:
+ instance = H.DartSymbolPlain(reader.ReadStringReference()).raw();
+ break;
+ case kSymbolConstant: {
+ Library& library = Library::Handle(Z);
+ library = Library::InternalLibrary();
+ const Class& symbol_class =
+ Class::Handle(Z, library.LookupClass(Symbols::Symbol()));
+ const Field& symbol_name_field = Field::Handle(
+ Z, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
+ ASSERT(!symbol_name_field.IsNull());
+ const NameIndex index = reader.ReadCanonicalNameReference();
+ if (index == -1) {
+ library = Library::null();
+ } else {
+ library = H.LookupLibraryByKernelLibrary(index);
+ }
+ const String& symbol =
+ H.DartIdentifier(library, reader.ReadStringReference());
+ instance = Instance::New(symbol_class, Heap::kOld);
+ instance.SetField(symbol_name_field, symbol);
+ break;
+ }
+ case kListConstant: {
+ const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
+ const Class& list_class =
+ Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List()));
+ // Build type from the raw bytes (needs temporary translator).
+ TypeTranslator type_translator(&reader, active_class_, true);
+ TypeArguments& type_arguments =
+ TypeArguments::ZoneHandle(Z, TypeArguments::New(1, Heap::kOld));
+ AbstractType& type = type_translator.BuildType();
+ type_arguments.SetTypeAt(0, type);
+ // Instantiate class.
+ type = Type::New(list_class, type_arguments, TokenPosition::kNoSource);
+ type = ClassFinalizer::FinalizeType(*active_class_->klass, type,
+ ClassFinalizer::kCanonicalize);
+ type_arguments = type.arguments();
+ // Fill array with constant elements.
+ const intptr_t length = reader.ReadUInt();
+ const Array& array =
+ Array::Handle(Z, ImmutableArray::New(length, Heap::kOld));
+ array.SetTypeArguments(type_arguments);
+ Instance& constant = Instance::Handle(Z);
+ for (intptr_t j = 0; j < length; ++j) {
+ // Recurse into lazily evaluating all "sub" constants
+ // needed to evaluate the current constant.
+ const intptr_t entry_offset = reader.ReadUInt();
+ ASSERT(entry_offset < constant_offset); // DAG!
+ constant = EvaluateConstantExpression(entry_offset);
+ array.SetAt(j, constant);
+ }
+ instance = array.raw();
+ break;
+ }
+ case kInstanceConstant: {
+ const NameIndex index = reader.ReadCanonicalNameReference();
+ const Class& klass = Class::Handle(Z, H.LookupClassByKernelClass(index));
+ const Object& obj =
+ Object::Handle(Z, klass.EnsureIsFinalized(H.thread()));
+ ASSERT(obj.IsNull());
+ instance = Instance::New(klass, Heap::kOld);
+ // Build type from the raw bytes (needs temporary translator).
+ TypeTranslator type_translator(&reader, active_class_, true);
+ const intptr_t number_of_type_arguments = reader.ReadUInt();
+ if (klass.NumTypeArguments() > 0) {
+ TypeArguments& type_arguments = TypeArguments::ZoneHandle(
+ Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
+ for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
+ type_arguments.SetTypeAt(j, type_translator.BuildType());
+ }
+ // Instantiate class.
+ AbstractType& type = AbstractType::Handle(
+ Z, Type::New(klass, type_arguments, TokenPosition::kNoSource));
+ type = ClassFinalizer::FinalizeType(*active_class_->klass, type,
+ ClassFinalizer::kCanonicalize);
+ type_arguments = type.arguments();
+ instance.SetTypeArguments(type_arguments);
+ } else {
+ ASSERT(number_of_type_arguments == 0);
+ }
+ // Set the fields.
+ const intptr_t number_of_fields = reader.ReadUInt();
+ Field& field = Field::Handle(Z);
+ Instance& constant = Instance::Handle(Z);
+ for (intptr_t j = 0; j < number_of_fields; ++j) {
+ field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
+ // Recurse into lazily evaluating all "sub" constants
+ // needed to evaluate the current constant.
+ const intptr_t entry_offset = reader.ReadUInt();
+ ASSERT(entry_offset < constant_offset); // DAG!
+ constant = EvaluateConstantExpression(entry_offset);
+ instance.SetField(field, constant);
+ }
+ break;
+ }
+ case kPartialInstantiationConstant: {
+ // Recurse into lazily evaluating the "sub" constant
+ // needed to evaluate the current constant.
+ const intptr_t entry_offset = reader.ReadUInt();
+ ASSERT(entry_offset < constant_offset); // DAG!
+ Instance& constant =
+ Instance::Handle(Z, EvaluateConstantExpression(entry_offset));
+ // Happens if the tearoff was in the vmservice library and we have
+ // [skip_vm_service_library] enabled.
+ if (constant.IsNull()) {
+ instance = Instance::null();
+ break;
+ }
+ // Build type from the raw bytes (needs temporary translator).
+ TypeTranslator type_translator(&reader, active_class_, true);
+ const intptr_t number_of_type_arguments = reader.ReadUInt();
+ ASSERT(number_of_type_arguments > 0);
+ TypeArguments& type_arguments = TypeArguments::ZoneHandle(
+ Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
+ for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
+ type_arguments.SetTypeAt(j, type_translator.BuildType());
+ }
+ // Make a copy of the old closure, and set delayed type arguments.
+ Closure& closure = Closure::Handle(Z, Closure::RawCast(constant.raw()));
+ Function& function = Function::Handle(Z, closure.function());
+ TypeArguments& type_arguments2 =
+ TypeArguments::ZoneHandle(Z, closure.instantiator_type_arguments());
+ TypeArguments& type_arguments3 =
+ TypeArguments::ZoneHandle(Z, closure.function_type_arguments());
+ Context& context = Context::Handle(Z, closure.context());
+ instance = Closure::New(type_arguments2, Object::null_type_arguments(),
+ type_arguments3, function, context,
+ Heap::kOld); // was type_arguments?
+ break;
+ }
+ case kTearOffConstant: {
+ const NameIndex index = reader.ReadCanonicalNameReference();
+ Function& function =
+ Function::Handle(Z, H.LookupStaticMethodByKernelProcedure(index));
+ function = function.ImplicitClosureFunction();
+ instance = function.ImplicitStaticClosure();
+ break;
+ }
+ case kTypeLiteralConstant: {
+ // Build type from the raw bytes (needs temporary translator).
+ TypeTranslator type_translator(&reader, active_class_, true);
+ instance = type_translator.BuildType().raw();
+ break;
+ }
+ default:
+ // Set literals (kSetConstant) are currently desugared in the frontend
+ // and will not reach the VM. See http://dartbug.com/35124 for some
+ // discussion. Map constants (kMapConstant ) are already lowered to
+ // InstanceConstant or ListConstant. We should never see unevaluated
+ // constants (kUnevaluatedConstant) in the constant table, they should
+ // have been fully evaluated before we get them.
+ H.ReportError(script_, TokenPosition::kNoSource,
+ "Cannot lazily read constant: unexpected kernel tag (%" Pd
+ ")",
+ constant_tag);
+ }
+ return H.Canonicalize(instance);
+}
+
void ConstantEvaluator::BailoutIfBackgroundCompilation() {
if (Compiler::IsBackgroundCompilation()) {
Compiler::AbortBackgroundCompilation(
@@ -868,20 +1112,6 @@
result_ = Instance::null();
}
-void ConstantEvaluator::EvaluateConstantExpression(Tag tag) {
- // Please note that this constants array is constructed exactly once, see
- // ReadConstantTable() and is immutable from that point on, so there is no
- // need to guard against concurrent access between mutator and background
- // compiler.
- KernelConstantsMap constant_map(H.constants().raw());
- if (tag == kConstantExpression) {
- helper_->ReadPosition();
- helper_->SkipDartType();
- }
- result_ ^= constant_map.GetOrDie(helper_->ReadUInt());
- ASSERT(constant_map.Release().raw() == H.constants().raw());
-}
-
// This depends on being about to read the list of positionals on arguments.
const Object& ConstantEvaluator::RunFunction(TokenPosition position,
const Function& function,
@@ -1109,273 +1339,6 @@
}
}
-ConstantHelper::ConstantHelper(Zone* zone,
- KernelReaderHelper* helper,
- TypeTranslator* type_translator,
- ActiveClass* active_class,
- NameIndex skip_vmservice_library)
- : zone_(zone),
- helper_(*helper),
- type_translator_(*type_translator),
- active_class_(active_class),
- const_evaluator_(helper, type_translator, active_class, nullptr),
- translation_helper_(helper->translation_helper_),
- skip_vmservice_library_(skip_vmservice_library),
- symbol_class_(Class::Handle(zone)),
- symbol_name_field_(Field::Handle(zone)),
- temp_type_(AbstractType::Handle(zone)),
- temp_type_arguments_(TypeArguments::Handle(zone)),
- temp_type_arguments2_(TypeArguments::Handle(zone)),
- temp_type_arguments3_(TypeArguments::Handle(zone)),
- temp_object_(Object::Handle(zone)),
- temp_string_(String::Handle(zone)),
- temp_array_(Array::Handle(zone)),
- temp_instance_(Instance::Handle(zone)),
- temp_field_(Field::Handle(zone)),
- temp_class_(Class::Handle(zone)),
- temp_library_(Library::Handle(zone)),
- temp_function_(Function::Handle(zone)),
- temp_closure_(Closure::Handle(zone)),
- temp_context_(Context::Handle(zone)),
- temp_integer_(Integer::Handle(zone)) {
- temp_library_ = Library::InternalLibrary();
- ASSERT(!temp_library_.IsNull());
-
- symbol_class_ = temp_library_.LookupClass(Symbols::Symbol());
- ASSERT(!symbol_class_.IsNull());
-
- symbol_name_field_ =
- symbol_class_.LookupInstanceFieldAllowPrivate(Symbols::_name());
- ASSERT(!symbol_name_field_.IsNull());
-}
-
-const Array& ConstantHelper::ReadConstantTable() {
- const intptr_t number_of_constants = helper_.ReadUInt();
- if (number_of_constants == 0) {
- return Array::empty_array();
- }
-
- const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
- const Class& list_class =
- Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List()));
-
- // Eagerly finalize _ImmutableList (instead of doing it on every list
- // constant).
- temp_class_ = I->class_table()->At(kImmutableArrayCid);
- temp_object_ = temp_class_.EnsureIsFinalized(H.thread());
- ASSERT(temp_object_.IsNull());
-
- KernelConstantsMap constants(
- HashTables::New<KernelConstantsMap>(number_of_constants, Heap::kOld));
-
- const intptr_t start_offset = helper_.ReaderOffset();
-
- for (intptr_t i = 0; i < number_of_constants; ++i) {
- const intptr_t offset = helper_.ReaderOffset();
- const intptr_t constant_tag = helper_.ReadByte();
- switch (constant_tag) {
- case kNullConstant:
- temp_instance_ = Instance::null();
- break;
- case kBoolConstant:
- temp_instance_ = helper_.ReadByte() == 1 ? Object::bool_true().raw()
- : Object::bool_false().raw();
- break;
- case kIntConstant: {
- temp_instance_ = const_evaluator_.EvaluateExpression(
- helper_.ReaderOffset(), false /* reset position */);
- break;
- }
- case kDoubleConstant: {
- temp_instance_ = Double::New(helper_.ReadDouble(), Heap::kOld);
- temp_instance_ = H.Canonicalize(temp_instance_);
- break;
- }
- case kStringConstant: {
- temp_instance_ =
- H.Canonicalize(H.DartString(helper_.ReadStringReference()));
- break;
- }
- case kSymbolConstant: {
- const NameIndex index = helper_.ReadCanonicalNameReference();
- if (index == -1) {
- temp_library_ = Library::null();
- } else {
- temp_library_ = H.LookupLibraryByKernelLibrary(index);
- }
- const String& symbol =
- H.DartIdentifier(temp_library_, helper_.ReadStringReference());
- temp_instance_ = Instance::New(symbol_class_, Heap::kOld);
- temp_instance_.SetField(symbol_name_field_, symbol);
- temp_instance_ = H.Canonicalize(temp_instance_);
- break;
- }
- case kListConstant: {
- temp_type_arguments_ = TypeArguments::New(1, Heap::kOld);
- const AbstractType& type = type_translator_.BuildType();
- temp_type_arguments_.SetTypeAt(0, type);
- InstantiateTypeArguments(list_class, &temp_type_arguments_);
-
- const intptr_t length = helper_.ReadUInt();
- temp_array_ = ImmutableArray::New(length, Heap::kOld);
- temp_array_.SetTypeArguments(temp_type_arguments_);
- for (intptr_t j = 0; j < length; ++j) {
- const intptr_t entry_offset = helper_.ReadUInt();
- ASSERT(entry_offset < (offset - start_offset)); // We have a DAG!
- temp_object_ = constants.GetOrDie(entry_offset);
- temp_array_.SetAt(j, temp_object_);
- }
-
- temp_instance_ = H.Canonicalize(temp_array_);
- break;
- }
- case kSetConstant:
- // Set literals are currently desugared in the frontend and will not
- // reach the VM. See http://dartbug.com/35124 for discussion.
- H.ReportError(script(), TokenPosition::kNoSource,
- "Unexpected set constant, this constant"
- " is expected to be evaluated at this point (%" Pd ")",
- constant_tag);
- break;
- case kInstanceConstant: {
- const NameIndex index = helper_.ReadCanonicalNameReference();
- if (ShouldSkipConstant(index)) {
- temp_instance_ = Instance::null();
- break;
- }
-
- temp_class_ = H.LookupClassByKernelClass(index);
- temp_object_ = temp_class_.EnsureIsFinalized(H.thread());
- ASSERT(temp_object_.IsNull());
-
- temp_instance_ = Instance::New(temp_class_, Heap::kOld);
-
- const intptr_t number_of_type_arguments = helper_.ReadUInt();
- if (temp_class_.NumTypeArguments() > 0) {
- temp_type_arguments_ =
- TypeArguments::New(number_of_type_arguments, Heap::kOld);
- for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
- temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType());
- }
- InstantiateTypeArguments(temp_class_, &temp_type_arguments_);
- temp_instance_.SetTypeArguments(temp_type_arguments_);
- } else {
- ASSERT(number_of_type_arguments == 0);
- }
-
- const intptr_t number_of_fields = helper_.ReadUInt();
- for (intptr_t j = 0; j < number_of_fields; ++j) {
- temp_field_ =
- H.LookupFieldByKernelField(helper_.ReadCanonicalNameReference());
- const intptr_t entry_offset = helper_.ReadUInt();
- ASSERT(entry_offset < (offset - start_offset)); // We have a DAG!
- temp_object_ = constants.GetOrDie(entry_offset);
- temp_instance_.SetField(temp_field_, temp_object_);
- }
-
- temp_instance_ = H.Canonicalize(temp_instance_);
- break;
- }
- case kPartialInstantiationConstant: {
- const intptr_t entry_offset = helper_.ReadUInt();
- ASSERT(entry_offset < (offset - start_offset)); // We have a DAG!
- temp_object_ = constants.GetOrDie(entry_offset);
-
- // Happens if the tearoff was in the vmservice library and we have
- // [skip_vm_service_library] enabled.
- if (temp_object_.IsNull()) {
- temp_instance_ = Instance::null();
- break;
- }
-
- const intptr_t number_of_type_arguments = helper_.ReadUInt();
- ASSERT(number_of_type_arguments > 0);
- temp_type_arguments_ =
- TypeArguments::New(number_of_type_arguments, Heap::kOld);
- for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
- temp_type_arguments_.SetTypeAt(j, type_translator_.BuildType());
- }
- temp_type_arguments_ = temp_type_arguments_.Canonicalize();
-
- // Make a copy of the old closure, with the delayed type arguments
- // set to [temp_type_arguments_].
- temp_closure_ = Closure::RawCast(temp_object_.raw());
- temp_function_ = temp_closure_.function();
- temp_type_arguments2_ = temp_closure_.instantiator_type_arguments();
- temp_type_arguments3_ = temp_closure_.function_type_arguments();
- temp_context_ = temp_closure_.context();
- temp_closure_ = Closure::New(
- temp_type_arguments2_, Object::null_type_arguments(),
- temp_type_arguments_, temp_function_, temp_context_, Heap::kOld);
- temp_instance_ = H.Canonicalize(temp_closure_);
- break;
- }
- case kTearOffConstant: {
- const NameIndex index = helper_.ReadCanonicalNameReference();
- if (ShouldSkipConstant(index)) {
- temp_instance_ = Instance::null();
- break;
- }
-
- temp_function_ = H.LookupStaticMethodByKernelProcedure(index);
- temp_function_ = temp_function_.ImplicitClosureFunction();
- temp_instance_ = temp_function_.ImplicitStaticClosure();
- temp_instance_ = H.Canonicalize(temp_instance_);
- break;
- }
- case kTypeLiteralConstant: {
- temp_instance_ = type_translator_.BuildType().raw();
- break;
- }
- case kMapConstant:
- // Note: This is already lowered to InstanceConstant/ListConstant.
- H.ReportError(script(), TokenPosition::kNoSource,
- "Unexpected map constant, this constant"
- " is expected to be evaluated at this point (%" Pd ")",
- constant_tag);
- break;
- case kUnevaluatedConstant:
- // We should not see unevaluated constants in the constant table, they
- // should have been fully evaluated before we get them.
- H.ReportError(
- script(), TokenPosition::kNoSource,
- "Unexpected unevaluated constant, All constant expressions"
- " are expected to be evaluated at this point (%" Pd ")",
- constant_tag);
- break;
- default:
- UNREACHABLE();
- }
- constants.InsertNewOrGetValue(offset - start_offset, temp_instance_);
- }
- return Array::Handle(Z, constants.Release().raw());
-}
-
-void ConstantHelper::InstantiateTypeArguments(const Class& receiver_class,
- TypeArguments* type_arguments) {
- // We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to
- // finalize the argument types.
- // (This can for example make the [type_arguments] vector larger)
- temp_type_ =
- Type::New(receiver_class, *type_arguments, TokenPosition::kNoSource);
- temp_type_ = ClassFinalizer::FinalizeType(*active_class_->klass, temp_type_,
- ClassFinalizer::kCanonicalize);
- *type_arguments = temp_type_.arguments();
-}
-
-// If [index] has `dart:vm_service` as a parent and we are skipping the VM
-// service library, this method returns `true`, otherwise `false`.
-bool ConstantHelper::ShouldSkipConstant(NameIndex index) {
- if (index == NameIndex::kInvalidName) {
- return false;
- }
- while (!H.IsLibrary(index)) {
- index = H.CanonicalNameParent(index);
- }
- ASSERT(H.IsLibrary(index));
- return index == skip_vmservice_library_;
-}
-
} // namespace kernel
} // namespace dart
diff --git a/runtime/vm/compiler/frontend/constant_evaluator.h b/runtime/vm/compiler/frontend/constant_evaluator.h
index 0483baa..7981835 100644
--- a/runtime/vm/compiler/frontend/constant_evaluator.h
+++ b/runtime/vm/compiler/frontend/constant_evaluator.h
@@ -56,7 +56,13 @@
RawObject* EvaluateExpressionSafe(intptr_t offset);
RawObject* EvaluateAnnotations();
+ // Evaluates a constant at the given offset (possibly by recursing
+ // into sub-constants).
+ RawInstance* EvaluateConstantExpression(intptr_t constant_offset);
+
private:
+ RawInstance* EvaluateConstant(intptr_t constant_offset);
+
void BailoutIfBackgroundCompilation();
bool IsBuildingFlowGraph() const;
@@ -88,7 +94,6 @@
void EvaluateDoubleLiteral();
void EvaluateBoolLiteral(bool value);
void EvaluateNullLiteral();
- void EvaluateConstantExpression(Tag tag);
void EvaluateGetStringLength(intptr_t expression_offset,
TokenPosition position);
@@ -140,59 +145,6 @@
DISALLOW_COPY_AND_ASSIGN(ConstantEvaluator);
};
-// Helper class that reads a kernel Constant from binary.
-class ConstantHelper {
- public:
- ConstantHelper(Zone* zone,
- KernelReaderHelper* helper,
- TypeTranslator* type_translator,
- ActiveClass* active_class,
- NameIndex skip_vmservice_library);
-
- // Reads the constant table from the binary.
- //
- // This method assumes the Reader is positioned already at the constant table
- // and an active class scope is setup.
- const Array& ReadConstantTable();
-
- private:
- const Script& script() const { return helper_.script_; }
-
- void InstantiateTypeArguments(const Class& receiver_class,
- TypeArguments* type_arguments);
-
- // If [index] has `dart:vm_service` as a parent and we are skipping the VM
- // service library, this method returns `true`, otherwise `false`.
- bool ShouldSkipConstant(NameIndex index);
-
- Zone* zone_;
- KernelReaderHelper& helper_;
- TypeTranslator& type_translator_;
- ActiveClass* const active_class_;
- ConstantEvaluator const_evaluator_;
- TranslationHelper& translation_helper_;
- NameIndex skip_vmservice_library_;
- Class& symbol_class_;
- Field& symbol_name_field_;
- AbstractType& temp_type_;
- TypeArguments& temp_type_arguments_;
- TypeArguments& temp_type_arguments2_;
- TypeArguments& temp_type_arguments3_;
- Object& temp_object_;
- String& temp_string_;
- Array& temp_array_;
- Instance& temp_instance_;
- Field& temp_field_;
- Class& temp_class_;
- Library& temp_library_;
- Function& temp_function_;
- Closure& temp_closure_;
- Context& temp_context_;
- Integer& temp_integer_;
-
- DISALLOW_COPY_AND_ASSIGN(ConstantHelper);
-};
-
class KernelConstMapKeyEqualsTraits : public AllStatic {
public:
static const char* Name() { return "KernelConstMapKeyEqualsTraits"; }
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index d018655..89a647e 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -3776,10 +3776,8 @@
}
if (position != nullptr) *position = p;
const intptr_t constant_offset = ReadUInt();
- KernelConstantsMap constant_map(H.constants().raw());
- Fragment result =
- Constant(Object::ZoneHandle(Z, constant_map.GetOrDie(constant_offset)));
- ASSERT(constant_map.Release().raw() == H.constants().raw());
+ Fragment result = Constant(Object::ZoneHandle(
+ Z, constant_evaluator_.EvaluateConstantExpression(constant_offset)));
return result;
}
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index d70628f..a3555db 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -32,6 +32,7 @@
metadata_payloads_(ExternalTypedData::Handle(Z)),
metadata_mappings_(ExternalTypedData::Handle(Z)),
constants_(Array::Handle(Z)),
+ constants_table_(ExternalTypedData::Handle(Z)),
info_(KernelProgramInfo::Handle(Z)),
name_index_handle_(Smi::Handle(Z)) {}
@@ -46,6 +47,7 @@
metadata_payloads_(ExternalTypedData::Handle(Z)),
metadata_mappings_(ExternalTypedData::Handle(Z)),
constants_(Array::Handle(Z)),
+ constants_table_(ExternalTypedData::Handle(Z)),
info_(KernelProgramInfo::Handle(Z)),
name_index_handle_(Smi::Handle(Z)) {}
@@ -79,6 +81,7 @@
SetMetadataPayloads(ExternalTypedData::Handle(Z, info.metadata_payloads()));
SetMetadataMappings(ExternalTypedData::Handle(Z, info.metadata_mappings()));
SetConstants(Array::Handle(Z, info.constants()));
+ SetConstantsTable(ExternalTypedData::Handle(Z, info.constants_table()));
SetKernelProgramInfo(info);
}
@@ -126,6 +129,12 @@
constants_ = constants.raw();
}
+void TranslationHelper::SetConstantsTable(
+ const ExternalTypedData& constants_table) {
+ ASSERT(constants_table_.IsNull());
+ constants_table_ = constants_table.raw();
+}
+
void TranslationHelper::SetKernelProgramInfo(const KernelProgramInfo& info) {
info_ = info.raw();
}
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 1dc3f3c4..43efd07 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -41,24 +41,35 @@
Heap::Space allocation_space() { return allocation_space_; }
// Access to strings.
- const TypedData& string_offsets() { return string_offsets_; }
+ const TypedData& string_offsets() const { return string_offsets_; }
void SetStringOffsets(const TypedData& string_offsets);
- const ExternalTypedData& string_data() { return string_data_; }
+ const ExternalTypedData& string_data() const { return string_data_; }
void SetStringData(const ExternalTypedData& string_data);
- const TypedData& canonical_names() { return canonical_names_; }
+ const TypedData& canonical_names() const { return canonical_names_; }
void SetCanonicalNames(const TypedData& canonical_names);
- const ExternalTypedData& metadata_payloads() { return metadata_payloads_; }
+ const ExternalTypedData& metadata_payloads() const {
+ return metadata_payloads_;
+ }
void SetMetadataPayloads(const ExternalTypedData& metadata_payloads);
- const ExternalTypedData& metadata_mappings() { return metadata_mappings_; }
+ const ExternalTypedData& metadata_mappings() const {
+ return metadata_mappings_;
+ }
void SetMetadataMappings(const ExternalTypedData& metadata_mappings);
+ // Access to previously evaluated constants from the constants table.
const Array& constants() { return constants_; }
void SetConstants(const Array& constants);
+ // Access to the raw bytes of the constants table.
+ const ExternalTypedData& constants_table() const { return constants_table_; }
+ void SetConstantsTable(const ExternalTypedData& constants_table);
+
+ KernelProgramInfo& info() { return info_; }
+
RawGrowableObjectArray* EnsurePotentialPragmaFunctions();
void SetKernelProgramInfo(const KernelProgramInfo& info);
@@ -206,6 +217,7 @@
ExternalTypedData& metadata_payloads_;
ExternalTypedData& metadata_mappings_;
Array& constants_;
+ ExternalTypedData& constants_table_;
KernelProgramInfo& info_;
Smi& name_index_handle_;
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index d37c67d..f7ce8e0 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -190,6 +190,7 @@
zone_(thread_->zone()),
isolate_(thread_->isolate()),
patch_classes_(Array::ZoneHandle(zone_)),
+ active_class_(),
library_kernel_offset_(-1), // Set to the correct value in LoadLibrary
correction_offset_(-1), // Set to the correct value in LoadLibrary
loading_native_wrappers_library_(false),
@@ -206,6 +207,7 @@
bytecode_metadata_helper_(&helper_, &active_class_),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
+ evaluating_(GrowableObjectArray::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
@@ -455,6 +457,7 @@
bytecode_metadata_helper_(&helper_, &active_class_),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
+ evaluating_(GrowableObjectArray::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
@@ -472,32 +475,6 @@
H.InitFromKernelProgramInfo(kernel_program_info_);
}
-const Array& KernelLoader::ReadConstantTable() {
- if (program_->library_count() == 0) {
- return Array::empty_array();
- }
- // We use the very first library's toplevel class as an owner for an
- // [ActiveClassScope]
- //
- // Though since constants cannot refer to types containing type parameter
- // references, the only purpose of the class is to serve as an owner for
- // signature functions (which get created for function types).
- const dart::Library& owner_library =
- Library::Handle(Z, LookupLibrary(library_canonical_name(0)));
- const dart::Class& toplevel_class =
- Class::Handle(Z, owner_library.toplevel_class());
- ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
-
- helper_.SetOffset(program_->constant_table_offset());
- TypeTranslator type_translator_(&helper_, &active_class_,
- true /* finalize */);
- ASSERT(type_translator_.active_class_ == &active_class_);
-
- ConstantHelper helper(Z, &helper_, &type_translator_, &active_class_,
- skip_vmservice_library_);
- return helper.ReadConstantTable();
-}
-
void KernelLoader::EvaluateDelayedPragmas() {
potential_pragma_functions_ =
kernel_program_info_.potential_pragma_functions();
@@ -522,66 +499,68 @@
GrowableObjectArray::Handle(Z));
}
-void KernelLoader::AnnotateNativeProcedures(const Array& constant_table_array) {
- KernelConstantsMap constant_table(constant_table_array.raw());
+void KernelLoader::AnnotateNativeProcedures() {
potential_natives_ = kernel_program_info_.potential_natives();
const intptr_t length =
!potential_natives_.IsNull() ? potential_natives_.Length() : 0;
- if (length > 0) {
- // Obtain `dart:_internal::ExternalName.name`.
- EnsureExternalClassIsLookedUp();
- Instance& constant = Instance::Handle(Z);
- String& native_name = String::Handle(Z);
+ if (length == 0) return;
- // Start scanning all candidates in [potential_natives] for the annotation
- // constant. If the annotation is found, flag the [Function] as native and
- // attach the native name to it.
- Function& function = Function::Handle(Z);
- for (intptr_t i = 0; i < length; ++i) {
- function ^= potential_natives_.At(i);
- helper_.SetOffset(function.KernelDataProgramOffset() +
- function.kernel_offset());
- {
- ProcedureHelper procedure_helper(&helper_);
- procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
- }
+ // Prepare lazy constant reading.
+ ConstantEvaluator constant_evaluator(&helper_, &type_translator_,
+ &active_class_);
- const intptr_t annotation_count = helper_.ReadListLength();
- for (intptr_t j = 0; j < annotation_count; ++j) {
- const intptr_t tag = helper_.PeekTag();
- if (tag == kConstantExpression ||
- tag == kDeprecated_ConstantExpression) {
- helper_.ReadByte(); // Skip the tag.
+ // Obtain `dart:_internal::ExternalName.name`.
+ EnsureExternalClassIsLookedUp();
+ Instance& constant = Instance::Handle(Z);
+ String& native_name = String::Handle(Z);
- // We have a candiate. Let's look if it's an instance of the
- // ExternalName class.
- if (tag == kConstantExpression) {
- helper_.ReadPosition(); // Skip fileOffset.
- helper_.SkipDartType(); // Skip type.
- }
- const intptr_t constant_table_offset = helper_.ReadUInt();
- constant ^= constant_table.GetOrDie(constant_table_offset);
- if (constant.clazz() == external_name_class_.raw()) {
- // We found the annotation, let's flag the function as native and
- // set the native name!
- 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 {
- helper_.SkipExpression();
- }
- }
+ // Start scanning all candidates in [potential_natives] for the annotation
+ // constant. If the annotation is found, flag the [Function] as native and
+ // attach the native name to it.
+ Function& function = Function::Handle(Z);
+ for (intptr_t i = 0; i < length; ++i) {
+ function ^= potential_natives_.At(i);
+ helper_.SetOffset(function.KernelDataProgramOffset() +
+ function.kernel_offset());
+ {
+ ProcedureHelper procedure_helper(&helper_);
+ procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
}
- // Clear out the list of [Function] objects which might need their native
- // name to be set after reading the constant table from the kernel blob.
- potential_natives_ = GrowableObjectArray::null();
- kernel_program_info_.set_potential_natives(potential_natives_);
+ const intptr_t annotation_count = helper_.ReadListLength();
+ for (intptr_t j = 0; j < annotation_count; ++j) {
+ const intptr_t tag = helper_.PeekTag();
+ if (tag == kConstantExpression || tag == kDeprecated_ConstantExpression) {
+ helper_.ReadByte(); // Skip the tag.
+
+ // We have a candidate. Let's look if it's an instance of the
+ // ExternalName class.
+ if (tag == kConstantExpression) {
+ helper_.ReadPosition(); // Skip fileOffset.
+ helper_.SkipDartType(); // Skip type.
+ }
+ const intptr_t constant_table_offset = helper_.ReadUInt();
+ constant = constant_evaluator.EvaluateConstantExpression(
+ constant_table_offset);
+ if (constant.clazz() == external_name_class_.raw()) {
+ // We found the annotation, let's flag the function as native and
+ // set the native name!
+ 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 {
+ helper_.SkipExpression();
+ }
+ }
}
- ASSERT(constant_table.Release().raw() == constant_table_array.raw());
+
+ // Clear out the list of [Function] objects which might need their native
+ // name to be set after reading the constant table from the kernel blob.
+ potential_natives_ = GrowableObjectArray::null();
+ kernel_program_info_.set_potential_natives(potential_natives_);
}
RawString* KernelLoader::DetectExternalNameCtor() {
@@ -638,14 +617,15 @@
return IsClassName(annotation_class, Symbols::DartCore(), Symbols::Pragma());
}
-void KernelLoader::LoadNativeExtensionLibraries(
- const Array& constant_table_array) {
+void KernelLoader::LoadNativeExtensionLibraries() {
const intptr_t length = !potential_extension_libraries_.IsNull()
? potential_extension_libraries_.Length()
: 0;
if (length == 0) return;
- KernelConstantsMap constant_table(constant_table_array.raw());
+ // Prepare lazy constant reading.
+ ConstantEvaluator constant_evaluator(&helper_, &type_translator_,
+ &active_class_);
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
@@ -677,8 +657,9 @@
helper_.ReadPosition(); // Skip fileOffset.
helper_.SkipDartType(); // Skip type.
}
- const intptr_t constant_table_index = helper_.ReadUInt();
- constant ^= constant_table.GetOrDie(constant_table_index);
+ const intptr_t constant_table_offset = helper_.ReadUInt();
+ constant = constant_evaluator.EvaluateConstantExpression(
+ constant_table_offset);
if (constant.clazz() == external_name_class_.raw()) {
uri_path ^= constant.GetField(external_name_field_);
}
@@ -716,7 +697,6 @@
}
}
potential_extension_libraries_ = GrowableObjectArray::null();
- ASSERT(constant_table.Release().raw() == constant_table_array.raw());
}
RawObject* KernelLoader::LoadProgram(bool process_pending_classes) {
@@ -743,6 +723,7 @@
}
}
+ // Finalize still pending classes if requested.
if (process_pending_classes) {
if (!ClassFinalizer::ProcessPendingClasses()) {
// Class finalization failed -> sticky error would be set.
@@ -750,44 +731,17 @@
}
}
- // Set pending fields array to flag constant table loading.
- ASSERT(I->object_store()->pending_unevaluated_const_fields() ==
- GrowableObjectArray::null());
- GrowableObjectArray& pending_unevaluated_const_fields =
- GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
- I->object_store()->set_pending_unevaluated_const_fields(
- pending_unevaluated_const_fields);
-
- // All classes were successfully loaded, so let's:
- // a) load & canonicalize the constant table
- const Array& constants = ReadConstantTable();
-
- // b) set the native names for native functions which have been created
- // so far (the rest will be directly set during LoadProcedure)
- AnnotateNativeProcedures(constants);
- LoadNativeExtensionLibraries(constants);
-
- // c) update all scripts with the constants array
- ASSERT(kernel_program_info_.constants() == Array::null());
- kernel_program_info_.set_constants(constants);
- kernel_program_info_.set_constants_table(ExternalTypedData::Handle(Z));
-
- // d) evaluate pending field initializers
- Error& error = Error::Handle(Z);
- Field& field = Field::Handle(Z);
- for (intptr_t i = 0, n = pending_unevaluated_const_fields.Length(); i < n;
- i++) {
- field ^= pending_unevaluated_const_fields.At(i);
- error = field.Initialize();
- if (!error.IsNull()) {
- H.ReportError(error, "postponed field initializer");
- }
- }
- pending_unevaluated_const_fields = GrowableObjectArray::null();
- I->object_store()->set_pending_unevaluated_const_fields(
- pending_unevaluated_const_fields);
-
- // e) evaluate pragmas that were delayed
+ // Sets the constants array to an empty hash and leaves the constant
+ // table's raw bytes in place for lazy reading. We can fix up all
+ // "pending" processing now, and must ensure we don't create new
+ // ones from this point on.
+ ASSERT(kernel_program_info_.constants_table() != ExternalTypedData::null());
+ const Array& array =
+ Array::Handle(Z, HashTables::New<KernelConstantsMap>(16, Heap::kOld));
+ kernel_program_info_.set_constants(array);
+ H.SetConstants(array); // for caching
+ AnnotateNativeProcedures();
+ LoadNativeExtensionLibraries();
EvaluateDelayedPragmas();
NameIndex main = program_->main_method();
@@ -1067,6 +1021,8 @@
helper_.ReaderOffset() - correction_offset_;
intptr_t annotation_count = helper_.ReadListLength(); // read list length.
if (annotation_count > 0) {
+ // This must wait until we can evaluate constants.
+ // So put on the "pending" list.
EnsurePotentialExtensionLibraries();
potential_extension_libraries_.Add(library);
}
@@ -1148,7 +1104,6 @@
if (toplevel_class.is_loaded()) {
return;
}
-
TIMELINE_DURATION(Thread::Current(), Isolate, "FinishTopLevelClassLoading");
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
@@ -1190,7 +1145,7 @@
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
- ReadVMAnnotations(annotation_count, &native_name_unused,
+ ReadVMAnnotations(library, annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
field_helper.SetJustRead(FieldHelper::kAnnotations);
@@ -1451,7 +1406,7 @@
{
String& native_name_unused = String::Handle(Z);
bool is_potential_native_unused = false;
- ReadVMAnnotations(annotation_count, &native_name_unused,
+ ReadVMAnnotations(library, annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
if (has_pragma_annotation) {
@@ -1545,7 +1500,7 @@
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
- ReadVMAnnotations(annotation_count, &native_name_unused,
+ ReadVMAnnotations(library, annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
field_helper.SetJustRead(FieldHelper::kAnnotations);
@@ -1618,7 +1573,7 @@
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
- ReadVMAnnotations(annotation_count, &native_name_unused,
+ ReadVMAnnotations(library, annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
constructor_helper.SetJustRead(ConstructorHelper::kAnnotations);
@@ -1760,12 +1715,14 @@
// `has_pragma_annotation`: non-null if @pragma(...) was found (no information
// is given on the kind of pragma directive).
//
-void KernelLoader::ReadVMAnnotations(intptr_t annotation_count,
+void KernelLoader::ReadVMAnnotations(const Library& library,
+ intptr_t annotation_count,
String* native_name,
bool* is_potential_native,
bool* has_pragma_annotation) {
*is_potential_native = false;
*has_pragma_annotation = false;
+ Instance& constant = Instance::Handle(Z);
String& detected_name = String::Handle(Z);
for (intptr_t i = 0; i < annotation_count; ++i) {
const intptr_t tag = helper_.PeekTag();
@@ -1828,7 +1785,13 @@
Symbols::DartCore(), Symbols::Pragma());
}
} else {
- KernelConstantsMap constant_table(constant_table_array.raw());
+ // Prepare lazy constant reading.
+ const dart::Class& toplevel_class =
+ Class::Handle(Z, library.toplevel_class());
+ ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
+ ConstantEvaluator constant_evaluator(&helper_, &type_translator_,
+ &active_class_);
+
helper_.ReadByte(); // Skip the tag.
// Obtain `dart:_internal::ExternalName.name`.
@@ -1841,18 +1804,27 @@
helper_.ReadPosition(); // Skip fileOffset.
helper_.SkipDartType(); // Skip type.
}
- const intptr_t constant_table_index = helper_.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;
+ const intptr_t constant_table_offset = helper_.ReadUInt();
+ // A cycle in evaluating the same library instance occurs when we are
+ // trying to finalize a class while evaluation the constant. We break
+ // this cycle by ignoring the second evaluation, since the first
+ // evaluation will take care of inspecting the result.
+ // TODO(ajcbik): avoid cycle detection completely by peeking
+ // into the constants and proceed only for @pragma
+ // or @ExternalName
+ if (EnqueueLibraryForEvaluation(library)) {
+ constant = constant_evaluator.EvaluateConstantExpression(
+ constant_table_offset);
+ DequeueLibraryForEvaluation(library);
+ 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 {
helper_.SkipExpression();
@@ -1881,8 +1853,8 @@
bool is_potential_native;
bool has_pragma_annotation;
const intptr_t annotation_count = helper_.ReadListLength();
- ReadVMAnnotations(annotation_count, &native_name, &is_potential_native,
- &has_pragma_annotation);
+ ReadVMAnnotations(library, 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();
@@ -1959,6 +1931,7 @@
function.set_native_name(native_name);
}
if (is_potential_native) {
+ // Cannot be processed right now, so put on "pending" list.
EnsurePotentialNatives();
potential_natives_.Add(function);
}
@@ -1979,6 +1952,8 @@
if (has_pragma_annotation) {
if (kernel_program_info_.constants() == Array::null()) {
+ // Any potential pragma function before point at which
+ // constant table could be loaded goes to "pending".
EnsurePotentialPragmaFunctions();
potential_pragma_functions_.Add(function);
} else {
diff --git a/runtime/vm/kernel_loader.h b/runtime/vm/kernel_loader.h
index f6d7665..5eb7442 100644
--- a/runtime/vm/kernel_loader.h
+++ b/runtime/vm/kernel_loader.h
@@ -230,8 +230,6 @@
void ReadObfuscationProhibitions();
- const Array& ReadConstantTable();
-
// Check for the presence of a (possibly const) constructor for the
// 'ExternalName' class. If found, returns the name parameter to the
// constructor.
@@ -243,11 +241,12 @@
bool IsClassName(NameIndex name, const String& library, const String& klass);
- void AnnotateNativeProcedures(const Array& constant_table);
- void LoadNativeExtensionLibraries(const Array& constant_table);
+ void AnnotateNativeProcedures();
+ void LoadNativeExtensionLibraries();
void EvaluateDelayedPragmas();
- void ReadVMAnnotations(intptr_t annotation_count,
+ void ReadVMAnnotations(const Library& library,
+ intptr_t annotation_count,
String* native_name,
bool* is_potential_native,
bool* has_pragma_annotation);
@@ -396,6 +395,37 @@
}
}
+ // Returns `true` if the [library] was newly enqueued or `false`
+ // if it was already enqueued. Allocates storage on first enqueue.
+ bool EnqueueLibraryForEvaluation(const Library& library) {
+ evaluating_ = kernel_program_info_.evaluating();
+ if (evaluating_.IsNull()) {
+ evaluating_ = GrowableObjectArray::New();
+ kernel_program_info_.set_evaluating(evaluating_);
+ ASSERT(!evaluating_.IsNull());
+ } else {
+ for (intptr_t i = 0, n = evaluating_.Length(); i < n; i++) {
+ if (library.raw() == evaluating_.At(i)) {
+ return false;
+ }
+ }
+ }
+ evaluating_.Add(library);
+ return true;
+ }
+
+ // Dequeues most recent libary. Releases storage when empty.
+ void DequeueLibraryForEvaluation(const Library& library) {
+ ASSERT(!evaluating_.IsNull());
+ RawObject* object = evaluating_.RemoveLast();
+ ASSERT(library.raw() == object);
+ if (evaluating_.Length() == 0) {
+ evaluating_ = GrowableObjectArray::null();
+ kernel_program_info_.set_evaluating(evaluating_);
+ ASSERT(evaluating_.IsNull());
+ }
+ }
+
Program* program_;
Thread* thread_;
@@ -423,6 +453,7 @@
Class& external_name_class_;
Field& external_name_field_;
+ GrowableObjectArray& evaluating_;
GrowableObjectArray& potential_natives_;
GrowableObjectArray& potential_pragma_functions_;
GrowableObjectArray& potential_extension_libraries_;
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e98d24c..adba2b0 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -12149,6 +12149,11 @@
StorePointer(&raw_ptr()->constants_table_, value.raw());
}
+void KernelProgramInfo::set_evaluating(
+ const GrowableObjectArray& evaluating) const {
+ StorePointer(&raw_ptr()->evaluating_, evaluating.raw());
+}
+
void KernelProgramInfo::set_potential_natives(
const GrowableObjectArray& candidates) const {
StorePointer(&raw_ptr()->potential_natives_, candidates.raw());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index bbfa0f7..58f52b3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4402,6 +4402,10 @@
RawArray* constants() const { return raw_ptr()->constants_; }
void set_constants(const Array& constants) const;
+ // Records libraries under evaluation to break evaluation cycles.
+ RawGrowableObjectArray* evaluating() const { return raw_ptr()->evaluating_; }
+ void set_evaluating(const GrowableObjectArray& evaluating) const;
+
// If we load a kernel blob with evaluated constants, then we delay setting
// the native names of [Function] objects until we've read the constant table
// (since native names are encoded as constants).
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 88dfaf9..a13c5ba6 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1258,6 +1258,7 @@
RawArray* bytecode_component_;
RawGrowableObjectArray* potential_natives_;
RawGrowableObjectArray* potential_pragma_functions_;
+ RawGrowableObjectArray* evaluating_; // detects cycles
RawExternalTypedData* constants_table_;
RawArray* libraries_cache_;
RawArray* classes_cache_;
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 35d4fdc..c3a3410 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -93,6 +93,7 @@
F(KernelProgramInfo, bytecode_component_) \
F(KernelProgramInfo, potential_natives_) \
F(KernelProgramInfo, potential_pragma_functions_) \
+ F(KernelProgramInfo, evaluating_) \
F(KernelProgramInfo, constants_table_) \
F(KernelProgramInfo, libraries_cache_) \
F(KernelProgramInfo, classes_cache_) \