Files
sdk/runtime/vm/kernel.cc
T
Tess Strickland 99a370942b [vm,dyn_modules] Update some coverage information for bytecode.
Add a flag to SourceFile which is true if the associated script
contains a list of covered const constructors, and serialize said
list if true.

Delay reading and resolving the list of covered const constructors in
the bytecode reader until code is read.

Update usage counters for interpreted functions in Entry instructions.

Fix up cases where SourceReport assumed compiled code as appropriate.

Remaining:
* Record call and assert coverage information if the current isolate
  group has coverage enabled.
* Record branch coverage information if the current isolate group
  has branch coverage enabled.

TEST=pkg/vm_service pkg/dart2bytecode

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-mac-debug-arm64-try
Change-Id: I14b6ffba1e175993e992c0fe939473557303bfeb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/449900
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
2025-10-01 09:51:59 -07:00

610 lines
23 KiB
C++

// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/kernel.h"
#include "vm/bit_vector.h"
#include "vm/compiler/frontend/constant_reader.h"
#include "vm/compiler/frontend/kernel_translation_helper.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/parser.h" // For Parser::kParameter* constants.
#include "vm/stack_frame.h"
namespace dart {
namespace kernel {
class KernelTokenPositionCollector : public KernelReaderHelper {
public:
KernelTokenPositionCollector(
Zone* zone,
TranslationHelper* translation_helper,
const Script& script,
const TypedDataView& data,
intptr_t data_program_offset,
intptr_t initial_script_index,
intptr_t record_for_script_id,
GrowableArray<intptr_t>* record_token_positions_into)
: KernelReaderHelper(zone, translation_helper, data, data_program_offset),
current_script_id_(initial_script_index),
record_for_script_id_(record_for_script_id),
record_token_positions_into_(record_token_positions_into) {}
void CollectTokenPositions(intptr_t kernel_offset);
void RecordTokenPosition(TokenPosition position) override;
void set_current_script_id(intptr_t id) override { current_script_id_ = id; }
private:
intptr_t current_script_id_;
intptr_t record_for_script_id_;
GrowableArray<intptr_t>* record_token_positions_into_;
DISALLOW_COPY_AND_ASSIGN(KernelTokenPositionCollector);
};
void KernelTokenPositionCollector::CollectTokenPositions(
intptr_t kernel_offset) {
SetOffset(kernel_offset);
const Tag tag = PeekTag();
if (tag == kProcedure) {
ProcedureHelper procedure_helper(this);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kEnd);
} else if (tag == kConstructor) {
ConstructorHelper constructor_helper(this);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd);
} else if (tag == kFunctionNode) {
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
} else if (tag == kField) {
FieldHelper field_helper(this);
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
} else if (tag == kClass) {
ClassHelper class_helper(this);
class_helper.ReadUntilExcluding(ClassHelper::kEnd);
} else {
ReportUnexpectedTag("a class or a member", tag);
UNREACHABLE();
}
}
void KernelTokenPositionCollector::RecordTokenPosition(TokenPosition position) {
if (record_for_script_id_ == current_script_id_ &&
record_token_positions_into_ != nullptr && position.IsReal()) {
record_token_positions_into_->Add(position.Serialize());
}
}
static void CollectKernelLibraryTokenPositions(
const TypedDataView& kernel_data,
const Script& script,
intptr_t kernel_offset,
intptr_t data_kernel_offset,
Zone* zone,
TranslationHelper* helper,
GrowableArray<intptr_t>* token_positions) {
if (kernel_data.IsNull()) {
return;
}
KernelTokenPositionCollector token_position_collector(
zone, helper, script, kernel_data, data_kernel_offset,
script.kernel_script_index(), script.kernel_script_index(),
token_positions);
token_position_collector.CollectTokenPositions(kernel_offset);
}
void CollectScriptTokenPositionsFromKernel(
const Script& interesting_script,
GrowableArray<intptr_t>* token_positions) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const auto& kernel_info =
KernelProgramInfo::Handle(zone, interesting_script.kernel_program_info());
TranslationHelper helper(thread);
helper.InitFromKernelProgramInfo(kernel_info);
auto isolate_group = thread->isolate_group();
const GrowableObjectArray& libs = GrowableObjectArray::Handle(
zone, isolate_group->object_store()->libraries());
Library& lib = Library::Handle(zone);
Object& entry = Object::Handle(zone);
Script& entry_script = Script::Handle(zone);
auto& data = TypedDataView::Handle(zone);
auto& temp_array = Array::Handle(zone);
auto& temp_field = Field::Handle(zone);
auto& temp_function = Function::Handle(zone);
for (intptr_t i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
lib.EnsureTopLevelClassIsFinalized();
DictionaryIterator it(lib);
while (it.HasNext()) {
entry = it.GetNext();
data = TypedDataView::null();
if (entry.IsClass()) {
const Class& klass = Class::Cast(entry);
if (klass.script() == interesting_script.ptr()) {
token_positions->Add(klass.token_pos().Serialize());
token_positions->Add(klass.end_token_pos().Serialize());
}
if (klass.is_finalized()) {
temp_array = klass.fields();
for (intptr_t i = 0; i < temp_array.Length(); ++i) {
temp_field ^= temp_array.At(i);
if (temp_field.kernel_offset() <= 0) {
// Skip artificially injected fields.
continue;
}
entry_script = temp_field.Script();
if (entry_script.ptr() != interesting_script.ptr()) {
continue;
}
data = temp_field.KernelLibrary();
CollectKernelLibraryTokenPositions(data, interesting_script,
temp_field.kernel_offset(),
temp_field.KernelLibraryOffset(),
zone, &helper, token_positions);
}
temp_array = klass.current_functions();
for (intptr_t i = 0; i < temp_array.Length(); ++i) {
temp_function ^= temp_array.At(i);
entry_script = temp_function.script();
if (entry_script.ptr() != interesting_script.ptr()) {
continue;
}
data = temp_function.KernelLibrary();
CollectKernelLibraryTokenPositions(
data, interesting_script, temp_function.kernel_offset(),
temp_function.KernelLibraryOffset(), zone, &helper,
token_positions);
}
} else {
// Class isn't finalized yet: read the data attached to it.
ASSERT(klass.kernel_offset() > 0);
data = lib.KernelLibrary();
ASSERT(!data.IsNull());
const intptr_t library_kernel_offset = lib.KernelLibraryOffset();
ASSERT(library_kernel_offset > 0);
const intptr_t class_offset = klass.kernel_offset();
entry_script = klass.script();
if (entry_script.ptr() != interesting_script.ptr()) {
continue;
}
CollectKernelLibraryTokenPositions(
data, interesting_script, class_offset, library_kernel_offset,
zone, &helper, token_positions);
}
} else if (entry.IsFunction()) {
temp_function ^= entry.ptr();
entry_script = temp_function.script();
if (entry_script.ptr() != interesting_script.ptr()) {
continue;
}
data = temp_function.KernelLibrary();
CollectKernelLibraryTokenPositions(data, interesting_script,
temp_function.kernel_offset(),
temp_function.KernelLibraryOffset(),
zone, &helper, token_positions);
} else if (entry.IsField()) {
const Field& field = Field::Cast(entry);
if (field.kernel_offset() <= 0) {
// Skip artificially injected fields.
continue;
}
entry_script = field.Script();
if (entry_script.ptr() != interesting_script.ptr()) {
continue;
}
data = field.KernelLibrary();
CollectKernelLibraryTokenPositions(
data, interesting_script, field.kernel_offset(),
field.KernelLibraryOffset(), zone, &helper, token_positions);
}
}
}
}
} // namespace kernel
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
ArrayPtr Script::CollectConstConstructorCoverageFromKernel() const {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
kernel::TranslationHelper helper(thread);
const auto& interesting_script = *this;
const auto& kernel_info =
KernelProgramInfo::Handle(zone, kernel_program_info());
helper.InitFromKernelProgramInfo(kernel_info);
const auto& data = TypedDataView::Handle(
zone, interesting_script.kernel_constant_coverage());
kernel::KernelReaderHelper kernel_reader(zone, &helper, data, 0);
// Read "constant coverage constructors".
const intptr_t constant_coverage_constructors =
kernel_reader.ReadListLength();
const Array& constructors =
Array::Handle(Array::New(constant_coverage_constructors));
for (intptr_t i = 0; i < constant_coverage_constructors; ++i) {
kernel::NameIndex kernel_name = kernel_reader.ReadCanonicalNameReference();
Class& klass = Class::ZoneHandle(
zone,
helper.LookupClassByKernelClass(helper.EnclosingName(kernel_name)));
const Function& target = Function::ZoneHandle(
zone, helper.LookupConstructorByKernelConstructor(klass, kernel_name));
constructors.SetAt(i, target);
}
return constructors.ptr();
}
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
namespace kernel {
ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field) {
ASSERT(field.is_static() && field.is_const());
Thread* thread = Thread::Current();
LongJumpScope jump(thread);
if (DART_SETJMP(*jump.Set()) == 0) {
Zone* zone = thread->zone();
TranslationHelper helper(thread);
auto& kernel_program_info =
KernelProgramInfo::Handle(zone, field.KernelProgramInfo());
helper.InitFromKernelProgramInfo(kernel_program_info);
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, TypedDataView::Handle(zone, field.KernelLibrary()),
field.KernelLibraryOffset());
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->StealStickyError();
}
}
class MetadataEvaluator : public KernelReaderHelper {
public:
MetadataEvaluator(Zone* zone,
TranslationHelper* translation_helper,
const TypedDataView& data,
intptr_t data_program_offset,
ActiveClass* active_class)
: KernelReaderHelper(zone, translation_helper, data, data_program_offset),
constant_reader_(this, active_class) {}
ObjectPtr EvaluateMetadata(intptr_t kernel_offset,
bool is_annotations_offset) {
SetOffset(kernel_offset);
// Library and LibraryDependency objects do not have a tag in kernel binary.
// Synthetic metadata fields corresponding to these objects keep kernel
// offset of annotations list instead of annotated object.
if (!is_annotations_offset) {
const Tag tag = PeekTag();
if (tag == kClass) {
ClassHelper class_helper(this);
class_helper.ReadUntilExcluding(ClassHelper::kAnnotations);
} else if (tag == kProcedure) {
ProcedureHelper procedure_helper(this);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
} else if (tag == kField) {
FieldHelper field_helper(this);
field_helper.ReadUntilExcluding(FieldHelper::kAnnotations);
} else if (tag == kConstructor) {
ConstructorHelper constructor_helper(this);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations);
} else if (tag == kFunctionDeclaration) {
ReadTag();
ReadPosition(); // fileOffset
VariableDeclarationHelper variable_declaration_helper(this);
variable_declaration_helper.ReadUntilExcluding(
VariableDeclarationHelper::kAnnotations);
} else {
FATAL("No support for metadata on this type of kernel node: %" Pd32
"\n",
tag);
}
}
return constant_reader_.ReadAnnotations();
}
private:
ConstantReader constant_reader_;
DISALLOW_COPY_AND_ASSIGN(MetadataEvaluator);
};
ObjectPtr EvaluateMetadata(const Library& library,
intptr_t kernel_offset,
bool is_annotations_offset) {
Thread* thread = Thread::Current();
LongJumpScope jump(thread);
if (DART_SETJMP(*jump.Set()) == 0) {
Zone* zone = thread->zone();
TranslationHelper helper(thread);
const auto& kernel_info =
KernelProgramInfo::Handle(zone, library.kernel_program_info());
helper.InitFromKernelProgramInfo(kernel_info);
const Class& owner_class = Class::Handle(zone, library.toplevel_class());
ActiveClass active_class;
ActiveClassScope active_class_scope(&active_class, &owner_class);
MetadataEvaluator metadata_evaluator(
zone, &helper, TypedDataView::Handle(zone, library.KernelLibrary()),
library.KernelLibraryOffset(), &active_class);
return metadata_evaluator.EvaluateMetadata(kernel_offset,
is_annotations_offset);
} else {
return thread->StealStickyError();
}
}
class ParameterDescriptorBuilder : public KernelReaderHelper {
public:
ParameterDescriptorBuilder(TranslationHelper* translation_helper,
Zone* zone,
const TypedDataView& data,
intptr_t data_program_offset,
ActiveClass* active_class)
: KernelReaderHelper(zone, translation_helper, data, data_program_offset),
constant_reader_(this, active_class) {}
ObjectPtr BuildParameterDescriptor(const Function& function);
private:
ConstantReader constant_reader_;
DISALLOW_COPY_AND_ASSIGN(ParameterDescriptorBuilder);
};
ObjectPtr ParameterDescriptorBuilder::BuildParameterDescriptor(
const Function& function) {
SetOffset(function.kernel_offset());
ReadUntilFunctionNode();
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t param_count = function_node_helper.total_parameter_count_;
intptr_t positional_count = ReadListLength(); // read list length.
intptr_t named_parameter_count = param_count - positional_count;
const Array& param_descriptor = Array::Handle(
Array::New(param_count * Parser::kParameterEntrySize, Heap::kOld));
for (intptr_t i = 0; i < param_count; ++i) {
const intptr_t entry_start = i * Parser::kParameterEntrySize;
if (i == positional_count) {
intptr_t named_parameter_count_check =
ReadListLength(); // read list length.
ASSERT(named_parameter_count_check == named_parameter_count);
}
// Read ith variable declaration.
intptr_t param_kernel_offset = reader_.offset();
VariableDeclarationHelper helper(this);
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
param_descriptor.SetAt(entry_start + Parser::kParameterIsFinalOffset,
helper.IsFinal() ? Bool::True() : Bool::False());
Tag tag = ReadTag(); // read (first part of) initializer.
if ((tag == kSomething) && !function.is_abstract()) {
// This will read the initializer.
Instance& constant = Instance::ZoneHandle(
zone_, constant_reader_.ReadConstantExpression());
param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
constant);
} else {
if (tag == kSomething) {
SkipExpression(); // Skip initializer.
}
param_descriptor.SetAt(entry_start + Parser::kParameterDefaultValueOffset,
Object::null_instance());
}
if (FLAG_enable_mirrors && (helper.annotation_count_ > 0)) {
AlternativeReadingScope alt(&reader_, param_kernel_offset);
VariableDeclarationHelper helper(this);
helper.ReadUntilExcluding(VariableDeclarationHelper::kAnnotations);
Object& metadata =
Object::ZoneHandle(zone_, constant_reader_.ReadAnnotations());
param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset,
metadata);
} else {
param_descriptor.SetAt(entry_start + Parser::kParameterMetadataOffset,
Object::null_instance());
}
}
return param_descriptor.ptr();
}
ObjectPtr BuildParameterDescriptor(const Function& function) {
Thread* thread = Thread::Current();
LongJumpScope jump(thread);
if (DART_SETJMP(*jump.Set()) == 0) {
Zone* zone = thread->zone();
const auto& kernel_info =
KernelProgramInfo::Handle(zone, function.KernelProgramInfo());
TranslationHelper helper(thread);
helper.InitFromKernelProgramInfo(kernel_info);
const Class& owner_class = Class::Handle(zone, function.Owner());
ActiveClass active_class;
ActiveClassScope active_class_scope(&active_class, &owner_class);
ParameterDescriptorBuilder builder(
&helper, zone, TypedDataView::Handle(zone, function.KernelLibrary()),
function.KernelLibraryOffset(), &active_class);
return builder.BuildParameterDescriptor(function);
} else {
return thread->StealStickyError();
}
}
void ReadParameterCovariance(const Function& function,
BitVector* is_covariant,
BitVector* is_generic_covariant_impl) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const intptr_t num_params = function.NumParameters();
ASSERT(is_covariant->length() == num_params);
ASSERT(is_generic_covariant_impl->length() == num_params);
const auto& kernel_info =
KernelProgramInfo::Handle(zone, function.KernelProgramInfo());
TranslationHelper translation_helper(thread);
translation_helper.InitFromKernelProgramInfo(kernel_info);
KernelReaderHelper reader_helper(
zone, &translation_helper,
TypedDataView::Handle(zone, function.KernelLibrary()),
function.KernelLibraryOffset());
reader_helper.SetOffset(function.kernel_offset());
reader_helper.ReadUntilFunctionNode();
FunctionNodeHelper function_node_helper(&reader_helper);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
// Positional.
const intptr_t num_positional_params = reader_helper.ReadListLength();
intptr_t param_index = function.NumImplicitParameters();
for (intptr_t i = 0; i < num_positional_params; ++i, ++param_index) {
VariableDeclarationHelper helper(&reader_helper);
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
if (helper.IsCovariant()) {
is_covariant->Add(param_index);
}
if (helper.IsGenericCovariantImpl()) {
is_generic_covariant_impl->Add(param_index);
}
}
// Named.
const intptr_t num_named_params = reader_helper.ReadListLength();
for (intptr_t i = 0; i < num_named_params; ++i, ++param_index) {
VariableDeclarationHelper helper(&reader_helper);
helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
if (helper.IsCovariant()) {
is_covariant->Add(param_index);
}
if (helper.IsGenericCovariantImpl()) {
is_generic_covariant_impl->Add(param_index);
}
}
}
static ProcedureAttributesMetadata ProcedureAttributesOf(
Zone* zone,
const KernelProgramInfo& kernel_program_info,
const TypedDataView& kernel_data,
intptr_t kernel_data_program_offset,
intptr_t kernel_offset) {
TranslationHelper translation_helper(Thread::Current());
translation_helper.InitFromKernelProgramInfo(kernel_program_info);
KernelReaderHelper reader_helper(zone, &translation_helper, kernel_data,
kernel_data_program_offset);
ProcedureAttributesMetadataHelper procedure_attributes_metadata_helper(
&reader_helper);
ProcedureAttributesMetadata attrs =
procedure_attributes_metadata_helper.GetProcedureAttributes(
kernel_offset);
return attrs;
}
ProcedureAttributesMetadata ProcedureAttributesOf(const Function& function,
Zone* zone) {
const auto& kernel_program_info =
KernelProgramInfo::Handle(zone, function.KernelProgramInfo());
return ProcedureAttributesOf(
zone, kernel_program_info,
TypedDataView::Handle(zone, function.KernelLibrary()),
function.KernelLibraryOffset(), function.kernel_offset());
}
ProcedureAttributesMetadata ProcedureAttributesOf(const Field& field,
Zone* zone) {
const auto& kernel_program_info =
KernelProgramInfo::Handle(zone, field.KernelProgramInfo());
return ProcedureAttributesOf(
zone, kernel_program_info,
TypedDataView::Handle(zone, field.KernelLibrary()),
field.KernelLibraryOffset(), field.kernel_offset());
}
static UnboxingInfoMetadata* UnboxingInfoMetadataOf(
Zone* zone,
const KernelProgramInfo& kernel_program_info,
const TypedDataView& kernel_data,
intptr_t kernel_data_program_offset,
intptr_t kernel_offset) {
TranslationHelper translation_helper(Thread::Current());
translation_helper.InitFromKernelProgramInfo(kernel_program_info);
KernelReaderHelper reader_helper(zone, &translation_helper, kernel_data,
kernel_data_program_offset);
UnboxingInfoMetadataHelper unboxing_info_metadata_helper(&reader_helper);
return unboxing_info_metadata_helper.GetUnboxingInfoMetadata(kernel_offset);
}
UnboxingInfoMetadata* UnboxingInfoMetadataOf(const Function& function,
Zone* zone) {
const auto& kernel_program_info =
KernelProgramInfo::Handle(zone, function.KernelProgramInfo());
return UnboxingInfoMetadataOf(
zone, kernel_program_info,
TypedDataView::Handle(zone, function.KernelLibrary()),
function.KernelLibraryOffset(), function.kernel_offset());
}
TableSelectorMetadata* TableSelectorMetadataForProgram(
const KernelProgramInfo& info,
Zone* zone) {
TranslationHelper translation_helper(Thread::Current());
translation_helper.InitFromKernelProgramInfo(info);
const auto& data = TypedDataView::Handle(zone, info.metadata_payloads());
KernelReaderHelper reader_helper(zone, &translation_helper, data, 0);
TableSelectorMetadataHelper table_selector_metadata_helper(&reader_helper);
return table_selector_metadata_helper.GetTableSelectorMetadata(zone);
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)