[vm/concurrency] Avoid calling into Dart code for evaluation of enum constants

There is no need to call from C++ into Dart code just so the Dart code
can return a constant. Instead we can get the constant directly in C++
code.

This avoids calling into Dart code during class finalization to evaluate
enum constants.

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: Ia0b09482188be2d0ce83ce9ffc20e5c15e52d070
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/170540
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Martin Kustermann 2020-11-06 10:41:43 +00:00 committed by commit-bot@chromium.org
parent 4c7740ab07
commit 7e28ecc270
6 changed files with 68 additions and 8 deletions

View file

@ -19,6 +19,19 @@ ConstantReader::ConstantReader(KernelReaderHelper* helper,
script_(helper->script()),
result_(Instance::Handle(zone_)) {}
InstancePtr ConstantReader::ReadConstantInitializer() {
Tag tag = helper_->ReadTag(); // read tag.
switch (tag) {
case kSomething:
return ReadConstantExpression();
default:
H.ReportError(script_, TokenPosition::kNoSource,
"Not a constant expression: unexpected kernel tag %s (%d)",
Reader::TagName(tag), tag);
}
return result_.raw();
}
InstancePtr ConstantReader::ReadConstantExpression() {
Tag tag = helper_->ReadTag(); // read tag.
switch (tag) {

View file

@ -23,6 +23,7 @@ class ConstantReader {
virtual ~ConstantReader() {}
InstancePtr ReadConstantInitializer();
InstancePtr ReadConstantExpression();
ObjectPtr ReadAnnotations();

View file

@ -33,6 +33,17 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() {
FieldHelper field_helper(this);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
// Constants are directly accessed at use sites of Dart code. In C++ - if
// we need to access static constants - we do so directly using the kernel
// evaluation instead of invoking the initializer function in Dart code.
//
// If the field is marked as @pragma('vm:entry-point') then the embedder might
// invoke the getter, so we'll generate the initializer function.
ASSERT(!field_helper.IsConst() ||
Field::Handle(Z, parsed_function()->function().accessor_field())
.VerifyEntryPoint(EntryPointPragma::kGetterOnly) ==
Error::null());
Tag initializer_tag = ReadTag(); // read first part of initializer.
if (initializer_tag != kSomething) {
UNREACHABLE();
@ -45,14 +56,8 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() {
Fragment body(normal_entry);
body += B->CheckStackOverflowInPrologue(field_helper.position_);
if (field_helper.IsConst()) {
// This will read the initializer.
body += Constant(
Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression()));
} else {
body += SetupCapturedParameters(parsed_function()->function());
body += BuildExpression(); // read initializer.
}
body += SetupCapturedParameters(parsed_function()->function());
body += BuildExpression(); // read initializer.
body += Return(TokenPosition::kNoSource);
PrologueInfo prologue_info(-1, -1);

View file

@ -347,6 +347,38 @@ void CollectTokenPositionsFor(const Script& interesting_script) {
script.set_debug_positions(array_object);
}
ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field) {
ASSERT(field.is_static() && field.is_const());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
TranslationHelper helper(thread);
Script& script = Script::Handle(zone, field.Script());
helper.InitFromScript(script);
const Class& owner_class = Class::Handle(zone, field.Owner());
ActiveClass active_class;
ActiveClassScope active_class_scope(&active_class, &owner_class);
KernelReaderHelper kernel_reader(
zone, &helper, script,
ExternalTypedData::Handle(zone, field.KernelData()),
field.KernelDataProgramOffset());
kernel_reader.SetOffset(field.kernel_offset());
ConstantReader constant_reader(&kernel_reader, &active_class);
FieldHelper field_helper(&kernel_reader);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
ASSERT(field_helper.IsConst());
return constant_reader.ReadConstantInitializer();
} else {
return Thread::Current()->StealStickyError();
}
}
class MetadataEvaluator : public KernelReaderHelper {
public:
MetadataEvaluator(Zone* zone,

View file

@ -195,6 +195,7 @@ class KernelLineStartsReader {
void CollectTokenPositionsFor(const Script& script);
ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field);
ObjectPtr EvaluateMetadata(const Field& metadata_field,
bool is_annotations_offset);
ObjectPtr BuildParameterDescriptor(const Function& function);

View file

@ -10611,6 +10611,14 @@ ErrorPtr Field::InitializeStatic() const {
ObjectPtr Field::EvaluateInitializer() const {
Thread* const thread = Thread::Current();
ASSERT(thread->IsMutatorThread());
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_static() && is_const()) {
ASSERT(!FLAG_precompiled_mode);
return kernel::EvaluateStaticConstFieldInitializer(*this);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
NoOOBMessageScope no_msg_scope(thread);
NoReloadScope no_reload_scope(thread->isolate(), thread);
const Function& initializer = Function::Handle(EnsureInitializerFunction());