Files
sdk/runtime/vm/bytecode_reader.cc
Tess Strickland 52cfd29cbb [vm,dynamic_modules] Add RecordCoverage instruction.
The RecordCoverage instruction has an A/E encoding. The A argument
is the type of coverage being recorded, whereas the E argument is
the logical index into the coverage array for updating whether that
source position has been hit.

Also adds new metadata to the bytecode component for the coverage
arrays associated with bytecode containing RecordCoverage instructions
and a new runtime entry for lazily allocate the coverage array for
an interpreted function when needed.

The type of coverage is encoded in the RecordCoverage instruction,
despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at
runtime doesn't require either accessing the coverage array (which may
be lazily allocated), forcing allocation of the coverage array just to
discover that type of coverage is currently disabled, or reading the
serialized bytecode component to avoid that forced allocation.

------

Other changes:

Source reporting now treats unexecuted interpreted functions when
not forcing compilation as if they were uncompiled native functions,
so that the source report from running the same code gives the same
result whether using the interpreter or the native compiler.

Bytecode closures are no longer skipped in source reports. Previously
any closure without a context scope was skipped, but bytecode closures
don't have those.

TEST=vm/cc/SourceReport_Coverage

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
Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2026-05-19 04:27:39 -07:00

3171 lines
114 KiB
C++

// Copyright (c) 2024, 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.
#include "vm/bytecode_reader.h"
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/bit_vector.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/class_id.h"
#include "vm/closure_functions_cache.h"
#include "vm/code_descriptors.h"
#include "vm/compiler/api/deopt_id.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/constants_kbc.h"
#include "vm/dart_entry.h"
#include "vm/flags.h"
#include "vm/hash.h"
#include "vm/hash_map.h"
#include "vm/hash_table.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/reusable_handles.h"
#include "vm/scopes.h"
#include "vm/stack_frame_kbc.h"
#include "vm/symbols.h"
#include "vm/timeline.h"
#define Z (zone_)
#define IG (thread_->isolate_group())
namespace dart {
DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode");
DEFINE_FLAG(charp,
dump_kernel_bytecode_filter,
nullptr,
"Dump only kernel bytecode of functions with matching names");
namespace bytecode {
static bool ShouldPrint(const Function& function) {
return function.NamePassesFilter(FLAG_dump_kernel_bytecode_filter);
}
class BytecodeOffsetsMapTraits {
public:
static const char* Name() { return "BytecodeOffsetsMapTraits"; }
static bool ReportStats() { return false; }
static bool IsMatch(const Object& a, const Object& b) {
return (a.ptr() == b.ptr());
}
static uword Hash(const Object& key) {
if (key.IsClass()) {
return Class::Cast(key).id();
} else if (key.IsFunction()) {
return Function::Cast(key).Hash();
} else if (key.IsField()) {
return Field::Cast(key).Hash();
} else if (key.IsScript()) {
return String::Hash(Script::Cast(key).url());
} else {
UNREACHABLE();
}
}
};
using BytecodeOffsetsMap = UnorderedHashMap<BytecodeOffsetsMapTraits>;
BytecodeLoader::BytecodeLoader(Thread* thread, const TypedDataBase& binary)
: thread_(thread),
binary_(binary),
bytecode_component_array_(Array::Handle(thread->zone())),
pending_objects_(GrowableObjectArray::Handle(thread->zone(),
GrowableObjectArray::New())),
bytecode_offsets_map_(
Array::Handle(thread->zone(),
HashTables::New<BytecodeOffsetsMap>(16))) {
ASSERT(thread_ == Thread::Current());
ASSERT(thread_->bytecode_loader() == nullptr);
thread_->set_bytecode_loader(this);
ASSERT(!binary_.IsNull());
ASSERT(binary_.IsExternalOrExternalView());
}
BytecodeLoader::~BytecodeLoader() {
ASSERT(thread_->bytecode_loader() == this);
thread_->set_bytecode_loader(nullptr);
}
FunctionPtr BytecodeLoader::LoadBytecode(bool load_code) {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
if (bytecode_component_array_.IsNull()) {
BytecodeReaderHelper component_reader(thread_, binary_);
bytecode_component_array_ = component_reader.ReadBytecodeComponent();
}
BytecodeComponentData bytecode_component(bytecode_component_array_);
BytecodeReaderHelper bytecode_reader(thread_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
bytecode_component.GetLibraryIndexOffset());
bytecode_reader.ReadLibraryDeclarations(bytecode_component.GetNumLibraries(),
pending_objects_, load_code);
if (bytecode_component.GetMainOffset() == 0) {
return Function::null();
}
AlternativeReadingScope alt2(&bytecode_reader.reader(),
bytecode_component.GetMainOffset());
return Function::RawCast(bytecode_reader.ReadObject());
}
void BytecodeLoader::LoadPendingCode() {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!bytecode_component_array_.IsNull());
BytecodeComponentData bytecode_component(bytecode_component_array_);
BytecodeReaderHelper bytecode_reader(thread_, &bytecode_component);
bytecode_reader.ReadPendingCode(pending_objects_);
}
void BytecodeLoader::SetOffset(const Object& obj, intptr_t offset) {
BytecodeOffsetsMap map(bytecode_offsets_map_.ptr());
map.UpdateOrInsert(obj, Smi::Handle(thread_->zone(), Smi::New(offset)));
bytecode_offsets_map_ = map.Release().ptr();
}
intptr_t BytecodeLoader::GetOffset(const Object& obj) const {
BytecodeOffsetsMap map(bytecode_offsets_map_.ptr());
const auto value = map.GetOrNull(obj);
ASSERT(value != Object::null());
const intptr_t offset = Smi::Value(Smi::RawCast(value));
ASSERT(map.Release().ptr() == bytecode_offsets_map_.ptr());
return offset;
}
bool BytecodeLoader::HasOffset(const Object& obj) const {
BytecodeOffsetsMap map(bytecode_offsets_map_.ptr());
const auto value = map.GetOrNull(obj);
ASSERT(map.Release().ptr() == bytecode_offsets_map_.ptr());
return value != Object::null();
}
void BytecodeLoader::FindModifiedLibraries(BitVector* modified_libs,
intptr_t* p_num_libraries,
intptr_t* p_num_classes,
intptr_t* p_num_procedures) {
if (bytecode_component_array_.IsNull()) {
BytecodeReaderHelper component_reader(thread_, binary_);
bytecode_component_array_ = component_reader.ReadBytecodeComponent();
}
BytecodeComponentData bytecode_component(bytecode_component_array_);
BytecodeReaderHelper bytecode_reader(thread_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
bytecode_component.GetLibraryIndexOffset());
bytecode_reader.FindModifiedLibraries(modified_libs,
bytecode_component.GetNumLibraries());
if (p_num_libraries != nullptr) {
*p_num_libraries = bytecode_component.GetNumLibraries();
}
if (p_num_classes != nullptr) {
*p_num_classes = bytecode_component.GetNumClasses();
}
if (p_num_procedures != nullptr) {
*p_num_procedures = bytecode_component.GetNumCodes();
}
}
BytecodeReaderHelper::BytecodeReaderHelper(Thread* thread,
const TypedDataBase& typed_data)
: reader_(typed_data),
thread_(thread),
zone_(thread->zone()),
bytecode_component_(nullptr),
scoped_function_(Function::Handle(thread->zone())),
scoped_function_name_(String::Handle(thread->zone())),
scoped_function_class_(Class::Handle(thread->zone())) {}
BytecodeReaderHelper::BytecodeReaderHelper(
Thread* thread,
BytecodeComponentData* bytecode_component)
: reader_(TypedDataBase::Handle(thread->zone(),
bytecode_component->GetTypedData())),
thread_(thread),
zone_(thread->zone()),
bytecode_component_(bytecode_component),
scoped_function_(Function::Handle(thread->zone())),
scoped_function_name_(String::Handle(thread->zone())),
scoped_function_class_(Class::Handle(thread->zone())) {}
void BytecodeReaderHelper::ReadCode(const Function& function,
intptr_t code_offset) {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!function.IsImplicitGetterFunction() &&
!function.IsImplicitSetterFunction());
if (code_offset == 0) {
FATAL("Function %s (kind %s) doesn't have bytecode",
function.ToFullyQualifiedCString(),
Function::KindToCString(function.kind()));
}
AlternativeReadingScope alt(&reader_, code_offset);
const auto& signature = FunctionType::Handle(Z, function.signature());
FunctionTypeScope function_type_scope(this, signature);
const intptr_t flags = reader_.ReadUInt();
const bool has_exceptions_table =
(flags & Code::kHasExceptionsTableFlag) != 0;
const bool has_source_positions =
(flags & Code::kHasSourcePositionsFlag) != 0;
const bool has_local_variables = (flags & Code::kHasLocalVariablesFlag) != 0;
const bool has_nullable_fields = (flags & Code::kHasNullableFieldsFlag) != 0;
const bool has_closures = (flags & Code::kHasClosuresFlag) != 0;
const bool has_parameter_flags = (flags & Code::kHasParameterFlagsFlag) != 0;
const bool has_forwarding_stub_target =
(flags & Code::kHasForwardingStubTargetFlag) != 0;
const bool has_default_function_type_args =
(flags & Code::kHasDefaultFunctionTypeArgsFlag) != 0;
const bool has_recorded_coverage =
(flags & Code::kHasRecordedCoverageFlag) != 0;
if (has_parameter_flags) {
intptr_t num_flags = reader_.ReadUInt();
for (intptr_t i = 0; i < num_flags; ++i) {
reader_.ReadUInt();
}
}
if (has_forwarding_stub_target) {
reader_.ReadUInt();
}
if (has_default_function_type_args) {
reader_.ReadUInt();
}
intptr_t num_closures = 0;
if (has_closures) {
num_closures = reader_.ReadListLength();
closures_ = &Array::Handle(Z, Array::New(num_closures));
for (intptr_t i = 0; i < num_closures; i++) {
ReadClosureDeclaration(function, i);
}
}
// Create object pool and read pool entries.
const intptr_t obj_count = reader_.ReadListLength();
const ObjectPool& pool = ObjectPool::Handle(Z, ObjectPool::New(obj_count));
ReadConstantPool(function, pool, 0);
// Read bytecode and attach to function.
const Bytecode& bytecode = Bytecode::Handle(Z, ReadBytecode(pool));
bytecode.set_code_offset(code_offset);
function.AttachBytecode(bytecode);
ReadExceptionsTable(function, bytecode, has_exceptions_table);
ReadSourcePositions(bytecode, has_source_positions);
ReadLocalVariables(bytecode, has_local_variables);
ReadRecordedCoverage(bytecode, has_recorded_coverage);
if (FLAG_dump_kernel_bytecode) {
if (ShouldPrint(function)) {
KernelBytecodeDisassembler::Disassemble(function);
}
}
// Initialization of fields with null literal is elided from bytecode.
// Record the corresponding stores if field guards are enabled.
if (has_nullable_fields) {
ASSERT(function.IsGenerativeConstructor());
const intptr_t num_fields = reader_.ReadListLength();
if (IG->use_field_guards()) {
Field& field = Field::Handle(Z);
for (intptr_t i = 0; i < num_fields; i++) {
field ^= ReadObject();
field.RecordStore(Object::null_object());
}
} else {
for (intptr_t i = 0; i < num_fields; i++) {
ReadObject();
}
}
}
// Read closures.
if (has_closures) {
Function& closure = Function::Handle(Z);
Bytecode& closure_bytecode = Bytecode::Handle(Z);
for (intptr_t i = 0; i < num_closures; i++) {
closure ^= closures_->At(i);
const intptr_t flags = reader_.ReadUInt();
const bool has_exceptions_table =
(flags & ClosureCode::kHasExceptionsTableFlag) != 0;
const bool has_source_positions =
(flags & ClosureCode::kHasSourcePositionsFlag) != 0;
const bool has_local_variables =
(flags & ClosureCode::kHasLocalVariablesFlag) != 0;
const bool captures_only_final_not_late_vars =
(flags & ClosureCode::kCapturesOnlyFinalNotLateVarsFlag) != 0;
const bool has_recorded_coverage =
(flags & ClosureCode::kHasRecordedCoverageFlag) != 0;
intptr_t local_function_id = -1;
if ((flags & ClosureCode::kHasLocalFunctionIdFlag) != 0) {
local_function_id = reader_.ReadUInt();
ASSERT(local_function_id > 0);
}
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.set_captures_only_final_not_late_vars(
captures_only_final_not_late_vars);
closure.AttachBytecode(closure_bytecode);
ReadExceptionsTable(closure, closure_bytecode, has_exceptions_table);
ReadSourcePositions(closure_bytecode, has_source_positions);
ReadLocalVariables(closure_bytecode, has_local_variables);
ReadRecordedCoverage(closure_bytecode, has_recorded_coverage);
if (FLAG_dump_kernel_bytecode) {
if (ShouldPrint(closure)) {
KernelBytecodeDisassembler::Disassemble(closure);
}
}
ClosureFunctionsCache::AddClosureFunctionLocked(closure,
local_function_id);
}
}
}
void BytecodeReaderHelper::ReadCoveredConstConstructors(const Script& script,
intptr_t offset) {
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
AlternativeReadingScope alt(&reader_, offset);
const intptr_t len = reader_.ReadListLength();
const auto& constant_coverage = Array::Handle(Z, Array::New(len, Heap::kOld));
auto& function = Function::Handle(Z);
for (intptr_t i = 0; i < len; i++) {
function ^= ReadObject();
// Const constructors from extension types can be desugared into procedures.
ASSERT(function.IsFunction() && function.is_const());
constant_coverage.SetAt(i, function);
}
script.set_collected_constant_coverage(constant_coverage);
#else
UNREACHABLE();
#endif
}
void BytecodeReaderHelper::ReadClosureDeclaration(const Function& function,
intptr_t closureIndex) {
// Closure flags, must be in sync with ClosureDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
const int kHasOptionalPositionalParamsFlag = 1 << 0;
const int kHasOptionalNamedParamsFlag = 1 << 1;
const int kHasTypeParamsFlag = 1 << 2;
const int kHasSourcePositionsFlag = 1 << 3;
const int kIsAsyncFlag = 1 << 4;
const int kIsAsyncStarFlag = 1 << 5;
const int kIsSyncStarFlag = 1 << 6;
const int kIsDebuggableFlag = 1 << 7;
const int kHasParameterFlagsFlag = 1 << 8;
const int kHasAnnotationsFlag = 1 << 9;
const int kHasPragmaFlag = 1 << 10;
const int kIsInvisibleFlag = 1 << 11;
const intptr_t flags = reader_.ReadUInt();
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
Object& parent = Object::Handle(Z, ReadObject());
if (!parent.IsFunction()) {
ASSERT(parent.IsField());
ASSERT(function.kind() == UntaggedFunction::kFieldInitializer);
// Closure in a static field initializer, so use current function as parent.
parent = function.ptr();
}
String& name = String::CheckedHandle(Z, ReadObject());
ASSERT(name.IsSymbol());
TokenPosition position = TokenPosition::kNoSource;
TokenPosition end_position = TokenPosition::kNoSource;
if ((flags & kHasSourcePositionsFlag) != 0) {
position = reader_.ReadPosition();
end_position = reader_.ReadPosition();
}
const Function& closure = Function::Handle(
Z, Function::NewClosureFunction(name, Function::Cast(parent), position));
NOT_IN_PRECOMPILED(closure.set_end_token_pos(end_position));
if ((flags & kIsSyncStarFlag) != 0) {
closure.set_modifier(UntaggedFunction::kSyncGen);
closure.set_is_inlinable(false);
} else if ((flags & kIsAsyncFlag) != 0) {
closure.set_modifier(UntaggedFunction::kAsync);
closure.set_is_inlinable(false);
} else if ((flags & kIsAsyncStarFlag) != 0) {
closure.set_modifier(UntaggedFunction::kAsyncGen);
closure.set_is_inlinable(false);
}
closure.set_is_debuggable((flags & kIsDebuggableFlag) != 0);
closure.set_is_visible((flags & kIsInvisibleFlag) == 0);
closure.set_has_pragma(has_pragma);
closures_->SetAt(closureIndex, closure);
auto& signature = FunctionType::Handle(Z, closure.signature());
signature = ReadFunctionSignature(
signature, closure, (flags & kHasOptionalPositionalParamsFlag) != 0,
(flags & kHasOptionalNamedParamsFlag) != 0,
(flags & kHasTypeParamsFlag) != 0,
/* has_positional_param_names = */ true,
(flags & kHasParameterFlagsFlag) != 0);
closure.SetSignature(signature);
if ((flags & kHasAnnotationsFlag) != 0) {
ReadAnnotations(Class::Handle(Z, Function::Cast(parent).Owner()), closure,
has_pragma);
}
}
FunctionTypePtr BytecodeReaderHelper::ReadFunctionSignature(
const FunctionType& signature,
const Function& closure_function,
bool has_optional_positional_params,
bool has_optional_named_params,
bool has_type_params,
bool has_positional_param_names,
bool has_parameter_flags) {
FunctionTypeScope function_type_scope(this, signature);
if (has_type_params) {
ReadTypeParametersDeclaration(Class::Handle(Z), signature);
}
const intptr_t kImplicitClosureParam = 1;
const intptr_t num_params = kImplicitClosureParam + reader_.ReadUInt();
intptr_t num_required_params = num_params;
if (has_optional_positional_params || has_optional_named_params) {
num_required_params = kImplicitClosureParam + reader_.ReadUInt();
}
signature.set_num_implicit_parameters(kImplicitClosureParam);
signature.set_num_fixed_parameters(num_required_params);
signature.SetNumOptionalParameters(num_params - num_required_params,
!has_optional_named_params);
signature.set_parameter_types(
Array::Handle(Z, Array::New(num_params, Heap::kOld)));
signature.CreateNameArrayIncludingFlags(Heap::kOld);
#if !defined(DART_PRECOMPILED_RUNTIME)
if (has_positional_param_names) {
closure_function.CreateNameArray();
}
#endif
intptr_t i = 0;
signature.SetParameterTypeAt(i, AbstractType::dynamic_type());
++i;
AbstractType& type = AbstractType::Handle(Z);
String& name = String::Handle(Z);
for (; i < num_params; ++i) {
if (has_positional_param_names ||
(has_optional_named_params && (i >= num_required_params))) {
name ^= ReadObject();
if (has_optional_named_params && (i >= num_required_params)) {
signature.SetParameterNameAt(i, name);
#if !defined(DART_PRECOMPILED_RUNTIME)
} else {
closure_function.SetParameterNameAt(i, name);
#endif
}
}
type ^= ReadObject();
signature.SetParameterTypeAt(i, type);
}
if (has_parameter_flags) {
ASSERT(has_optional_named_params);
intptr_t num_flags = reader_.ReadUInt();
for (intptr_t i = 0; i < num_flags; ++i) {
intptr_t flag = reader_.ReadUInt();
if ((flag & Parameter::kIsRequiredFlag) != 0) {
signature.SetIsRequiredAt(num_required_params + i);
}
}
}
type ^= ReadObject();
signature.set_result_type(type);
// Finalize function type.
return FunctionType::RawCast(
ClassFinalizer::FinalizeType(signature, ClassFinalizer::kCanonicalize));
}
void BytecodeReaderHelper::ReadTypeParametersDeclaration(
const Class& parameterized_class,
const FunctionType& parameterized_signature) {
ASSERT(parameterized_class.IsNull() != parameterized_signature.IsNull());
const intptr_t num_type_params = reader_.ReadUInt();
ASSERT(num_type_params > 0);
// First setup the type parameters, so if any of the following code uses it
// (in a recursive way) we're fine.
//
// Step a) Create TypeParameters object (without bounds and defaults).
const TypeParameters& type_parameters =
TypeParameters::Handle(Z, TypeParameters::New(num_type_params));
if (!parameterized_class.IsNull()) {
ASSERT(parameterized_class.type_parameters() == TypeParameters::null());
parameterized_class.set_type_parameters(type_parameters);
} else {
ASSERT(parameterized_signature.type_parameters() == TypeParameters::null());
parameterized_signature.SetTypeParameters(type_parameters);
}
String& name = String::Handle(Z);
AbstractType& type = AbstractType::Handle(Z);
for (intptr_t i = 0; i < num_type_params; ++i) {
name ^= ReadObject();
ASSERT(name.IsSymbol());
type_parameters.SetNameAt(i, name);
// Set bound temporarily to dynamic in order to
// allow type finalization of type parameter types.
type_parameters.SetBoundAt(i, Object::dynamic_type());
}
// Step b) Fill in the bounds and defaults of all [TypeParameter]s.
for (intptr_t i = 0; i < num_type_params; ++i) {
type ^= ReadObject();
type_parameters.SetBoundAt(i, type);
type ^= ReadObject();
type_parameters.SetDefaultAt(i, type);
}
}
intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
const ObjectPool& pool,
intptr_t start_index) {
// These enums and the code below reading the constant pool from kernel must
// be kept in sync with pkg/dart2bytecode/lib/constant_pool.dart.
enum ConstantPoolTag {
kInvalid,
kObjectRef,
kClass,
kType,
kStaticField,
kInstanceField,
kTypeArgumentsField,
kClosureFunction,
kEndClosureFunctionScope,
kSubtypeTestCache,
kEmptyTypeArguments,
kDirectCall,
kInterfaceCall,
kInstantiatedInterfaceCall,
kDynamicCall,
kExternalCall,
kFfiCall,
kDeferredLibraryPrefix,
kAllocateClosure,
};
Object& obj = Object::Handle(Z);
Object& elem = Object::Handle(Z);
Field& field = Field::Handle(Z);
Class& cls = Class::Handle(Z);
String& name = String::Handle(Z);
const intptr_t obj_count = pool.Length();
for (intptr_t i = start_index; i < obj_count; ++i) {
const intptr_t tag = reader_.ReadByte();
switch (tag) {
case ConstantPoolTag::kInvalid:
UNREACHABLE();
case ConstantPoolTag::kStaticField:
obj = ReadObject();
ASSERT(obj.IsField());
break;
case ConstantPoolTag::kInstanceField:
field ^= ReadObject();
// InstanceField constant occupies 2 entries.
// The first entry is used for field offset.
obj = Smi::New(field.HostOffset() / kCompressedWordSize);
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// The second entry is used for field object.
obj = field.ptr();
break;
case ConstantPoolTag::kClass:
obj = ReadObject();
ASSERT(obj.IsClass());
break;
case ConstantPoolTag::kTypeArgumentsField:
cls ^= ReadObject();
obj = Smi::New(cls.host_type_arguments_field_offset() /
kCompressedWordSize);
break;
case ConstantPoolTag::kType:
obj = ReadObject();
ASSERT(obj.IsAbstractType());
break;
case ConstantPoolTag::kClosureFunction: {
intptr_t closure_index = reader_.ReadUInt();
obj = closures_->At(closure_index);
ASSERT(obj.IsFunction());
// Set current entry.
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
const auto& signature =
FunctionType::Handle(Z, Function::Cast(obj).signature());
FunctionTypeScope function_type_scope(this, signature);
// Read constant pool until corresponding EndClosureFunctionScope.
i = ReadConstantPool(function, pool, i + 1);
// Proceed with the rest of entries.
continue;
}
case ConstantPoolTag::kEndClosureFunctionScope: {
// EndClosureFunctionScope entry is not used and set to null.
obj = Object::null();
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
return i;
}
case ConstantPoolTag::kSubtypeTestCache: {
obj = SubtypeTestCache::New(SubtypeTestCache::kMaxInputs);
} break;
case ConstantPoolTag::kEmptyTypeArguments:
obj = Object::empty_type_arguments().ptr();
break;
case ConstantPoolTag::kObjectRef:
obj = ReadObject();
break;
case ConstantPoolTag::kDirectCall: {
// DirectCall constant occupies 2 entries.
// The first entry is used for target function.
obj = ReadObject();
ASSERT(obj.IsFunction());
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// The second entry is used for arguments descriptor.
obj = ReadObject();
} break;
case ConstantPoolTag::kInterfaceCall: {
elem = ReadObject();
ASSERT(elem.IsFunction());
// InterfaceCall constant occupies 2 entries.
// The first entry is used for interface target.
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, elem);
++i;
ASSERT(i < obj_count);
// The second entry is used for arguments descriptor.
obj = ReadObject();
} break;
case ConstantPoolTag::kInstantiatedInterfaceCall: {
elem = ReadObject();
ASSERT(elem.IsFunction());
// InstantiatedInterfaceCall constant occupies 3 entries:
// 1) Interface target.
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, elem);
++i;
ASSERT(i < obj_count);
// 2) Arguments descriptor.
obj = ReadObject();
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// 3) Static receiver type.
obj = ReadObject();
} break;
case ConstantPoolTag::kDynamicCall: {
name ^= ReadObject();
ASSERT(name.IsSymbol());
// Do not mangle ==:
// * operator == takes an Object so it is either not checked or
// checked at the entry because the parameter is marked covariant,
// neither of those cases require a dynamic invocation forwarder
if (!Field::IsGetterName(name) &&
(name.ptr() != Symbols::EqualOperator().ptr())) {
name = Function::CreateDynamicInvocationForwarderName(name);
}
// DynamicCall constant occupies 2 entries: selector and arguments
// descriptor.
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, name);
++i;
ASSERT(i < obj_count);
// The second entry is used for arguments descriptor.
obj = ReadObject();
} break;
case ConstantPoolTag::kExternalCall: {
// ExternalCall constant occupies 2 entries:
// trampoline and native function.
pool.SetTypeAt(i, ObjectPool::EntryType::kNativeFunction,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetRawValueAt(i, 0);
++i;
ASSERT(i < obj_count);
pool.SetTypeAt(i, ObjectPool::EntryType::kNativeFunction,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetRawValueAt(i, 0);
continue;
}
case ConstantPoolTag::kFfiCall: {
// FfiCall constant has 1 raw value entry.
pool.SetTypeAt(i, ObjectPool::EntryType::kNativeFunction,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetRawValueAt(i, 0);
continue;
}
case ConstantPoolTag::kDeferredLibraryPrefix: {
name ^= ReadObject();
ASSERT(name.IsSymbol());
const auto& enclosing_library = Library::CheckedHandle(Z, ReadObject());
const auto& target_library = Library::CheckedHandle(Z, ReadObject());
obj = enclosing_library.LookupLocalLibraryPrefix(name);
if (obj.IsNull()) {
obj = Namespace::New(target_library, Object::null_array(),
Object::null_array(), enclosing_library);
obj = LibraryPrefix::New(name, Namespace::Cast(obj),
/*deferred_load=*/true, enclosing_library);
enclosing_library.AddObject(LibraryPrefix::Cast(obj), name);
}
ASSERT(LibraryPrefix::Cast(obj).GetLibrary(0) == target_library.ptr());
} break;
case ConstantPoolTag::kAllocateClosure: {
const intptr_t closure_index = reader_.ReadUInt();
const intptr_t num_elements = reader_.ReadUInt();
const intptr_t flags = reader_.ReadUInt();
// AllocateClosure flags, must be in sync with ConstantAllocateClosure
// constants in pkg/dart2bytecode/lib/constant_pool.dart.
const intptr_t kHasDelayedTypeArguments = 1 << 0;
const intptr_t kHasInstantiatorTypeArguments = 1 << 1;
const intptr_t kHasFunctionTypeArguments = 1 << 2;
// AllocateClosure constant occupies 2 entries:
// function and length_and_flags.
obj = closures_->At(closure_index);
ASSERT(obj.IsFunction());
// Set current entry.
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// The second entry is used for encoded length and flags.
obj = Smi::New(UntaggedClosure::EncodeLengthAndFlags(
(flags & kHasDelayedTypeArguments) != 0,
(flags & kHasInstantiatorTypeArguments) != 0,
(flags & kHasFunctionTypeArguments) != 0, num_elements));
} break;
default:
UNREACHABLE();
}
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable,
ObjectPool::SnapshotBehavior::kNotSnapshotable);
pool.SetObjectAt(i, obj);
}
return obj_count - 1;
}
BytecodePtr BytecodeReaderHelper::ReadBytecode(const ObjectPool& pool) {
const intptr_t size = reader_.ReadUInt();
const intptr_t offset = reader_.offset();
const uint8_t* data = reader_.BufferAt(offset);
reader_.set_offset(offset + size);
// Create and return bytecode object.
return Bytecode::New(reinterpret_cast<uword>(data), size, offset,
*(reader_.typed_data()), pool);
}
void BytecodeReaderHelper::ReadExceptionsTable(const Function& function,
const Bytecode& bytecode,
bool has_exceptions_table) {
const intptr_t try_block_count =
has_exceptions_table ? reader_.ReadListLength() : 0;
if (try_block_count > 0) {
const ObjectPool& pool = ObjectPool::Handle(Z, bytecode.object_pool());
AbstractType& handler_type = AbstractType::Handle(Z);
Array& handler_types = Array::Handle(Z);
DescriptorList* pc_descriptors_list = new (Z) DescriptorList(Z);
ExceptionHandlerList* exception_handlers_list =
new (Z) ExceptionHandlerList(function);
// Encoding of ExceptionsTable is described in
// pkg/dart2bytecode/lib/exceptions.dart.
for (intptr_t try_index = 0; try_index < try_block_count; try_index++) {
intptr_t outer_try_index_plus1 = reader_.ReadUInt();
intptr_t outer_try_index = outer_try_index_plus1 - 1;
// PcDescriptors are expressed in terms of return addresses.
intptr_t start_pc =
KernelBytecode::BytecodePcToOffset(reader_.ReadUInt(),
/* is_return_address = */ true);
intptr_t end_pc =
KernelBytecode::BytecodePcToOffset(reader_.ReadUInt(),
/* is_return_address = */ true);
intptr_t handler_pc =
KernelBytecode::BytecodePcToOffset(reader_.ReadUInt(),
/* is_return_address = */ false);
uint8_t flags = reader_.ReadByte();
const uint8_t kFlagNeedsStackTrace = 1 << 0;
const uint8_t kFlagIsSynthetic = 1 << 1;
const bool needs_stacktrace = (flags & kFlagNeedsStackTrace) != 0;
const bool is_generated = (flags & kFlagIsSynthetic) != 0;
intptr_t type_count = reader_.ReadListLength();
ASSERT(type_count > 0);
handler_types = Array::New(type_count, Heap::kOld);
for (intptr_t i = 0; i < type_count; i++) {
intptr_t type_index = reader_.ReadUInt();
ASSERT(type_index < pool.Length());
handler_type ^= pool.ObjectAt(type_index);
handler_types.SetAt(i, handler_type);
}
pc_descriptors_list->AddDescriptor(
UntaggedPcDescriptors::kOther, start_pc, DeoptId::kNone,
TokenPosition::kNoSource, try_index,
UntaggedPcDescriptors::kInvalidYieldIndex);
pc_descriptors_list->AddDescriptor(
UntaggedPcDescriptors::kOther, end_pc, DeoptId::kNone,
TokenPosition::kNoSource, try_index,
UntaggedPcDescriptors::kInvalidYieldIndex);
// The exception handler keeps a zone handle of the types array, rather
// than a raw pointer. Do not share the handle across iterations to avoid
// clobbering the array.
exception_handlers_list->AddHandler(
try_index, outer_try_index, handler_pc, is_generated,
Array::ZoneHandle(Z, handler_types.ptr()), needs_stacktrace);
}
const PcDescriptors& descriptors = PcDescriptors::Handle(
Z, pc_descriptors_list->FinalizePcDescriptors(bytecode.PayloadStart()));
bytecode.set_pc_descriptors(descriptors);
const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
Z, exception_handlers_list->FinalizeExceptionHandlers(
bytecode.PayloadStart()));
bytecode.set_exception_handlers(handlers);
} else {
bytecode.set_pc_descriptors(Object::empty_descriptors());
bytecode.set_exception_handlers(Object::empty_exception_handlers());
}
}
void BytecodeReaderHelper::ReadSourcePositions(const Bytecode& bytecode,
bool has_source_positions) {
if (!has_source_positions) {
return;
}
intptr_t offset = reader_.ReadUInt();
bytecode.set_source_positions_binary_offset(
bytecode_component_->GetSourcePositionsOffset() + offset);
}
void BytecodeReaderHelper::ReadLocalVariables(const Bytecode& bytecode,
bool has_local_variables) {
if (!has_local_variables) {
return;
}
const intptr_t offset = reader_.ReadUInt();
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
bytecode.set_local_variables_binary_offset(
bytecode_component_->GetLocalVariablesOffset() + offset);
#else
USE(offset);
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
}
void BytecodeReaderHelper::ReadRecordedCoverage(const Bytecode& bytecode,
bool has_recorded_coverage) {
if (!has_recorded_coverage) {
return;
}
const intptr_t offset = reader_.ReadUInt();
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
bytecode.set_recorded_coverage_binary_offset(
bytecode_component_->GetRecordedCoverageOffset() + offset);
#else
USE(offset);
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
}
ArrayPtr BytecodeReaderHelper::ReadBytecodeComponent() {
AlternativeReadingScope alt(&reader_, 0);
const intptr_t start_offset = reader_.offset();
intptr_t magic = reader_.ReadUInt32();
if (magic != KernelBytecode::kMagicValue) {
FATAL("Unexpected Dart bytecode magic %" Px, magic);
}
const intptr_t version = reader_.ReadUInt32();
if (version != KernelBytecode::kBytecodeFormatVersion) {
FATAL("Unsupported Dart bytecode format version %" Pd
". "
"This version of Dart VM supports bytecode format version %" Pd ".",
version, KernelBytecode::kBytecodeFormatVersion);
}
reader_.ReadUInt32(); // Skip stringTable.numItems
const intptr_t string_table_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip objectTable.numItems
const intptr_t object_table_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip main.numItems
const intptr_t main_offset = start_offset + reader_.ReadUInt32();
const intptr_t num_libraries = reader_.ReadUInt32();
const intptr_t library_index_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip libraries.numItems
const intptr_t libraries_offset = start_offset + reader_.ReadUInt32();
const intptr_t num_classes = reader_.ReadUInt32();
const intptr_t classes_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip members.numItems
const intptr_t members_offset = start_offset + reader_.ReadUInt32();
const intptr_t num_codes = reader_.ReadUInt32();
const intptr_t codes_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip sourcePositions.numItems
const intptr_t source_positions_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip sourceFiles.numItems
const intptr_t source_files_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip lineStarts.numItems
const intptr_t line_starts_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip localVariables.numItems
const intptr_t local_variables_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip recordedCoverage.numItems
const intptr_t recorded_coverage_offset = start_offset + reader_.ReadUInt32();
reader_.ReadUInt32(); // Skip annotations.numItems
const intptr_t annotations_offset = start_offset + reader_.ReadUInt32();
// Read header of string table.
reader_.set_offset(string_table_offset);
const intptr_t num_one_byte_strings = reader_.ReadUInt32();
const intptr_t num_two_byte_strings = reader_.ReadUInt32();
const intptr_t strings_contents_offset =
reader_.offset() + (num_one_byte_strings + num_two_byte_strings) * 4;
// Read header of object table.
reader_.set_offset(object_table_offset);
const intptr_t num_objects = reader_.ReadUInt();
const intptr_t objects_size = reader_.ReadUInt();
// Skip over contents of objects.
const intptr_t objects_contents_offset = reader_.offset();
const intptr_t object_offsets_offset = objects_contents_offset + objects_size;
reader_.set_offset(object_offsets_offset);
auto& bytecode_component_array = Array::Handle(
Z,
BytecodeComponentData::New(
Z, *(reader_.typed_data()), version, num_objects, string_table_offset,
strings_contents_offset, object_offsets_offset,
objects_contents_offset, main_offset, num_libraries,
library_index_offset, libraries_offset, num_classes, classes_offset,
members_offset, num_codes, codes_offset, source_positions_offset,
source_files_offset, line_starts_offset, local_variables_offset,
recorded_coverage_offset, annotations_offset, Heap::kOld));
BytecodeComponentData bytecode_component(bytecode_component_array);
// Read object offsets.
Smi& offs = Smi::Handle(Z);
for (intptr_t i = 0; i < num_objects; ++i) {
offs = Smi::New(reader_.ReadUInt());
bytecode_component.SetObject(i, offs);
}
return bytecode_component_array.ptr();
}
void BytecodeReaderHelper::ResetObjects() {
reader_.set_offset(bytecode_component_->GetObjectOffsetsOffset());
const intptr_t num_objects = bytecode_component_->GetNumObjects();
// Read object offsets.
Smi& offs = Smi::Handle(Z);
for (intptr_t i = 0; i < num_objects; ++i) {
offs = Smi::New(reader_.ReadUInt());
bytecode_component_->SetObject(i, offs);
}
}
ObjectPtr BytecodeReaderHelper::ReadObject() {
uint32_t header = reader_.ReadUInt();
if ((header & kReferenceBit) != 0) {
intptr_t index = header >> kIndexShift;
if (index == 0) {
return Object::null();
}
ObjectPtr obj = bytecode_component_->GetObject(index);
if (obj->IsHeapObject()) {
return obj;
}
// Object is not loaded yet.
intptr_t offset = bytecode_component_->GetObjectsContentsOffset() +
Smi::Value(Smi::RawCast(obj));
AlternativeReadingScope alt(&reader_, offset);
header = reader_.ReadUInt();
obj = ReadObjectContents(header);
ASSERT(obj->IsHeapObject());
{
REUSABLE_OBJECT_HANDLESCOPE(thread_);
Object& obj_handle = thread_->ObjectHandle();
obj_handle = obj;
bytecode_component_->SetObject(index, obj_handle);
}
return obj;
}
return ReadObjectContents(header);
}
StringPtr BytecodeReaderHelper::ConstructorName(const Class& cls,
const String& name) {
GrowableHandlePtrArray<const String> pieces(Z, 3);
pieces.Add(String::Handle(Z, cls.Name()));
pieces.Add(Symbols::Dot());
pieces.Add(name);
return Symbols::FromConcatAll(thread_, pieces);
}
ObjectPtr BytecodeReaderHelper::ReadObjectContents(uint32_t header) {
ASSERT(((header & kReferenceBit) == 0));
// Must be in sync with enum ObjectKind in
// pkg/dart2bytecode/lib/object_table.dart.
enum ObjectKind {
kInvalid,
kLibrary,
kScript,
kClass,
kMember,
kClosure,
kName,
kConstObject,
kType,
kTypeArguments,
kArgDesc,
};
// Member flags, must be in sync with _MemberHandle constants in
// pkg/dart2bytecode/lib/object_table.dart.
const intptr_t kFlagIsField = kFlagBit0;
const intptr_t kFlagIsConstructor = kFlagBit1;
// ArgDesc flags, must be in sync with _ArgDescHandle constants in
// pkg/dart2bytecode/lib/object_table.dart.
const int kFlagHasNamedArgs = kFlagBit0;
const int kFlagHasTypeArgs = kFlagBit1;
// Script flags, must be in sync with _ScriptHandle constants in
// pkg/dart2bytecode/lib/object_table.dart.
const int kFlagHasSourceFile = kFlagBit0;
// Name flags, must be in sync with _NameHandle constants in
// pkg/dart2bytecode/lib/object_table.dart.
const intptr_t kFlagIsPublic = kFlagBit0;
const intptr_t kind = (header >> kKindShift) & kKindMask;
const intptr_t flags = header & kFlagsMask;
switch (kind) {
case kInvalid:
UNREACHABLE();
break;
case kLibrary: {
String& uri = String::CheckedHandle(Z, ReadObject());
LibraryPtr library = Library::LookupLibrary(thread_, uri);
if (library == Library::null()) {
// Expression evaluation libraries are not registered with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
if (uri.ptr() == Symbols::EvalSourceUri().ptr()) {
return thread_->bytecode_loader()->GetExpressionEvaluationLibrary();
}
FATAL("Unable to find library %s", uri.ToCString());
}
return library;
}
case kClass: {
const Library& library = Library::CheckedHandle(Z, ReadObject());
const String& class_name = String::CheckedHandle(Z, ReadObject());
if (class_name.ptr() == Symbols::Empty().ptr()) {
NoSafepointScope no_safepoint_scope(thread_);
ClassPtr cls = library.toplevel_class();
if (cls == Class::null()) {
FATAL("Unable to find toplevel class %s", library.ToCString());
}
return cls;
}
ClassPtr cls = library.LookupClass(class_name);
if (cls == Class::null()) {
// Expression evaluation class is not added to its library.
// Any reference to the expression evaluation class should be replaced
// with a real class.
if (library.url() == Symbols::EvalSourceUri().ptr()) {
ASSERT(thread_->bytecode_loader()->GetExpressionEvaluationLibrary() ==
library.ptr());
return thread_->bytecode_loader()->GetExpressionEvaluationRealClass();
}
FATAL("Unable to find class %s in %s", class_name.ToCString(),
library.ToCString());
}
return cls;
}
case kMember: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
String& name = String::CheckedHandle(Z, ReadObject());
if ((flags & kFlagIsField) != 0) {
FieldPtr field = cls.LookupField(name);
if (field == Field::null()) {
FATAL("Unable to find field %s in %s", name.ToCString(),
cls.ToCString());
}
return field;
} else {
const bool is_constructor = (flags & kFlagIsConstructor) != 0;
if (is_constructor) {
name = ConstructorName(cls, name);
}
ASSERT(!name.IsNull() && name.IsSymbol());
if (name.ptr() == scoped_function_name_.ptr() &&
cls.ptr() == scoped_function_class_.ptr()) {
return scoped_function_.ptr();
}
// If this is a reference to the expression evaluation function, then
// reading the class has substituted in the real class.
if (name.ptr() == Symbols::DebugProcedureName().ptr() &&
cls.ptr() == thread_->bytecode_loader()
->GetExpressionEvaluationRealClass()) {
return thread_->bytecode_loader()->GetExpressionEvaluationFunction();
}
FunctionPtr function = Function::null();
ErrorPtr finalize_err = is_constructor
? cls.EnsureIsAllocateFinalized(thread_)
: cls.EnsureIsFinalized(thread_);
if (finalize_err != Error::null()) {
FATAL("Unable to finalize class %s%s: %s", cls.ToCString(),
is_constructor ? "" : " for allocation",
Error::Handle(Z, finalize_err).ToErrorCString());
}
function = Resolver::ResolveFunction(Z, cls, name);
if (function == Function::null()) {
// When requesting a getter, also return method extractors.
if (Field::IsGetterName(name)) {
String& method_name =
String::Handle(Z, Field::NameFromGetter(name));
function = Resolver::ResolveFunction(Z, cls, method_name);
if (function != Function::null()) {
function = Function::Handle(Z, function).GetMethodExtractor(name);
if (function != Function::null()) {
return function;
}
}
}
FATAL("Unable to find function %s in %s", name.ToCString(),
cls.ToCString());
}
return function;
}
}
case kClosure: {
ReadObject(); // Skip enclosing member.
const intptr_t closure_index = reader_.ReadUInt();
return closures_->At(closure_index);
}
case kName: {
if ((flags & kFlagIsPublic) == 0) {
const Library& library = Library::CheckedHandle(Z, ReadObject());
ASSERT(!library.IsNull());
auto& name = String::Handle(Z, ReadString(/* is_canonical = */ false));
name = library.PrivateName(name);
return name.ptr();
}
return ReadString();
}
case kTypeArguments: {
return ReadTypeArguments();
}
case kConstObject: {
const intptr_t tag = flags / kFlagBit0;
return ReadConstObject(tag);
}
case kArgDesc: {
const intptr_t num_arguments = reader_.ReadUInt();
const intptr_t num_type_args =
((flags & kFlagHasTypeArgs) != 0) ? reader_.ReadUInt() : 0;
if ((flags & kFlagHasNamedArgs) == 0) {
return ArgumentsDescriptor::NewBoxed(num_type_args, num_arguments);
} else {
const intptr_t num_arg_names = reader_.ReadListLength();
const Array& array = Array::Handle(Z, Array::New(num_arg_names));
String& name = String::Handle(Z);
for (intptr_t i = 0; i < num_arg_names; ++i) {
name ^= ReadObject();
array.SetAt(i, name);
}
return ArgumentsDescriptor::NewBoxed(num_type_args, num_arguments,
array);
}
}
case kScript: {
auto& script = Script::Handle(Z);
const String& uri = String::CheckedHandle(Z, ReadObject());
if ((flags & kFlagHasSourceFile) != 0) {
script =
ReadSourceFile(uri, bytecode_component_->GetSourceFilesOffset() +
reader_.ReadUInt());
} else {
script = Script::New(uri, Object::null_string());
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// Covered const constructor information for scripts is loaded
// during ReadPendingCode. For any script that is not pending,
// set the collected constant coverage to the empty array.
BytecodeLoader* const loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
if (!loader->HasOffset(script)) {
script.set_collected_constant_coverage(Object::empty_array());
}
#endif
return script.ptr();
}
case kType: {
const intptr_t tag = (flags & kTagMask) / kFlagBit0;
const Nullability nullability = ((flags & kFlagIsNullable) != 0)
? Nullability::kNullable
: Nullability::kNonNullable;
return ReadType(tag, nullability);
}
default:
UNREACHABLE();
}
return Object::null();
}
ObjectPtr BytecodeReaderHelper::ReadConstObject(intptr_t tag) {
// Must be in sync with enum ConstTag in
// pkg/dart2bytecode/lib/object_table.dart.
enum ConstTag {
kInvalid,
kInt,
kDouble,
kBool,
kString,
kSymbol,
kInstance,
kList,
kMap,
kSet,
kRecord,
kTearOff,
kTearOffInstantiation,
};
switch (tag) {
case kInvalid:
UNREACHABLE();
break;
case kInstance: {
const Type& type = Type::CheckedHandle(Z, ReadObject());
const Class& cls = Class::Handle(Z, type.type_class());
const Instance& obj = Instance::Handle(Z, Instance::New(cls, Heap::kOld));
if (cls.NumTypeArguments() > 0) {
auto& type_args = TypeArguments::Handle(Z, type.arguments());
type_args = cls.GetInstanceTypeArguments(thread_, type_args);
obj.SetTypeArguments(type_args);
} else {
ASSERT(type.arguments() == TypeArguments::null());
}
const intptr_t num_fields = reader_.ReadUInt();
Field& field = Field::Handle(Z);
Object& value = Object::Handle(Z);
for (intptr_t i = 0; i < num_fields; ++i) {
field ^= ReadObject();
value = ReadObject();
obj.SetField(field, value);
}
return Canonicalize(obj);
}
case kInt: {
const int64_t value = reader_.ReadSLEB128AsInt64();
if (Smi::IsValid(value)) {
return Smi::New(static_cast<intptr_t>(value));
}
const Integer& obj = Integer::Handle(Z, Integer::New(value, Heap::kOld));
return Canonicalize(obj);
}
case kDouble: {
const int64_t bits = reader_.ReadSLEB128AsInt64();
double value = bit_cast<double, int64_t>(bits);
const Double& obj = Double::Handle(Z, Double::New(value, Heap::kOld));
return Canonicalize(obj);
}
case kList: {
const AbstractType& elem_type =
AbstractType::CheckedHandle(Z, ReadObject());
const intptr_t length = reader_.ReadUInt();
const Array& array = Array::Handle(Z, Array::New(length, elem_type));
Object& value = Object::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
value = ReadObject();
array.SetAt(i, value);
}
array.MakeImmutable();
return Canonicalize(array);
}
case kTearOff: {
Object& obj = Object::Handle(Z, ReadObject());
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitClosureFunction();
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitStaticClosure();
ASSERT(obj.IsInstance());
return Canonicalize(Instance::Cast(obj));
}
case kBool: {
bool is_true = reader_.ReadByte() != 0;
return is_true ? Bool::True().ptr() : Bool::False().ptr();
}
case kSymbol: {
const String& name = String::CheckedHandle(Z, ReadObject());
ASSERT(name.IsSymbol());
const Library& library = Library::Handle(Z, Library::InternalLibrary());
ASSERT(!library.IsNull());
const Class& cls =
Class::Handle(Z, library.LookupClass(Symbols::Symbol()));
ASSERT(!cls.IsNull());
const Field& field = Field::Handle(
Z, cls.LookupInstanceFieldAllowPrivate(Symbols::_name()));
ASSERT(!field.IsNull());
const Instance& obj = Instance::Handle(Z, Instance::New(cls, Heap::kOld));
obj.SetField(field, name);
return Canonicalize(obj);
}
case kTearOffInstantiation: {
Closure& closure = Closure::CheckedHandle(Z, ReadObject());
const TypeArguments& type_args =
TypeArguments::CheckedHandle(Z, ReadObject());
closure = Closure::New(
TypeArguments::Handle(Z, closure.instantiator_type_arguments()),
TypeArguments::Handle(Z, closure.function_type_arguments()),
type_args, Function::Handle(Z, closure.function()),
Object::Handle(Z, closure.RawContext()), Heap::kOld);
return Canonicalize(closure);
}
case kString:
return ReadString();
case kMap: {
const auto& map_type = Type::CheckedHandle(Z, ReadObject());
const intptr_t used_data = reader_.ReadUInt();
const auto& map_class =
Class::Handle(Z, IG->object_store()->const_map_impl_class());
ASSERT(!map_class.IsNull());
ASSERT(map_class.is_finalized());
auto& type_arguments = TypeArguments::Handle(Z, map_type.arguments());
type_arguments =
map_class.GetInstanceTypeArguments(thread_, type_arguments);
const auto& map = Map::Handle(Z, ConstMap::NewUninitialized(Heap::kOld));
ASSERT_EQUAL(map.GetClassId(), kConstMapCid);
map.SetTypeArguments(type_arguments);
map.set_used_data(used_data);
const auto& data = Array::Handle(Z, Array::New(used_data));
map.set_data(data);
map.set_deleted_keys(0);
map.ComputeAndSetHashMask();
Object& value = Object::Handle(Z);
for (intptr_t i = 0; i < used_data; ++i) {
value = ReadObject();
data.SetAt(i, value);
}
return Canonicalize(map);
}
case kSet: {
const AbstractType& elem_type =
AbstractType::CheckedHandle(Z, ReadObject());
const auto& set_class =
Class::Handle(Z, IG->object_store()->const_set_impl_class());
ASSERT(!set_class.IsNull());
ASSERT(set_class.is_finalized());
auto& type_arguments =
TypeArguments::Handle(Z, TypeArguments::New(1, Heap::kOld));
type_arguments.SetTypeAt(0, elem_type);
type_arguments =
set_class.GetInstanceTypeArguments(thread_, type_arguments);
const auto& set = Set::Handle(Z, ConstSet::NewUninitialized(Heap::kOld));
ASSERT_EQUAL(set.GetClassId(), kConstSetCid);
set.SetTypeArguments(type_arguments);
const intptr_t length = reader_.ReadUInt();
set.set_used_data(length);
const auto& data = Array::Handle(Z, Array::New(length));
set.set_data(data);
set.set_deleted_keys(0);
set.ComputeAndSetHashMask();
Object& value = Object::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
value = ReadObject();
data.SetAt(i, value);
}
return Canonicalize(set);
}
case kRecord: {
const RecordType& record_type =
RecordType::CheckedHandle(Z, ReadObject());
const intptr_t num_fields = reader_.ReadUInt();
ASSERT(num_fields == record_type.NumFields());
const RecordShape shape = record_type.shape();
const auto& record = Record::Handle(Z, Record::New(shape));
Object& value = Object::Handle(Z);
for (intptr_t i = 0; i < num_fields; ++i) {
value = ReadObject();
record.SetFieldAt(i, value);
}
return Canonicalize(record);
}
default:
UNREACHABLE();
}
return Object::null();
}
ObjectPtr BytecodeReaderHelper::ReadType(intptr_t tag,
Nullability nullability) {
// Must be in sync with enum TypeTag in
// pkg/dart2bytecode/lib/object_table.dart.
enum TypeTag {
kInvalid,
kDynamic,
kVoid,
kNull,
kNever,
kSimpleType,
kGenericType,
kTypeParameter,
kFunctionType,
kRecordType,
};
// FunctionType flags, must be in sync with _FunctionTypeHandle constants in
// pkg/dart2bytecode/lib/object_table.dart.
const int kFlagHasOptionalPositionalParams = 1 << 0;
const int kFlagHasOptionalNamedParams = 1 << 1;
const int kFlagHasTypeParams = 1 << 2;
const int kFlagHasEnclosingTypeParameters = 1 << 3;
const int kFlagHasParameterFlags = 1 << 4;
switch (tag) {
case kInvalid:
UNREACHABLE();
break;
case kDynamic:
return Type::DynamicType();
case kVoid:
return Type::VoidType();
case kNull:
return Type::NullType();
case kNever:
return Type::Handle(Z, Type::NeverType())
.ToNullability(nullability, Heap::kOld);
case kSimpleType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if (!cls.is_declaration_loaded()) {
LoadReferencedClass(cls);
}
const Type& type = Type::Handle(Z, cls.DeclarationType());
return type.ToNullability(nullability, Heap::kOld);
}
case kTypeParameter: {
Object& parent = Object::Handle(Z, ReadObject());
const intptr_t index_in_parent = reader_.ReadUInt();
auto& type = TypeParameter::Handle(Z);
if (parent.IsClass()) {
type =
Class::Cast(parent).TypeParameterAt(index_in_parent, nullability);
} else if (parent.IsFunction()) {
type = Function::Cast(parent).TypeParameterAt(index_in_parent,
nullability);
} else if (parent.IsNull()) {
ASSERT(!enclosing_function_types_.is_empty());
for (intptr_t i = enclosing_function_types_.length() - 1; i >= 0; --i) {
parent = enclosing_function_types_[i]->ptr();
ASSERT(index_in_parent <
FunctionType::Cast(parent).NumTypeArguments());
if (index_in_parent >=
FunctionType::Cast(parent).NumParentTypeArguments()) {
break;
}
}
type = FunctionType::Cast(parent).TypeParameterAt(
index_in_parent -
FunctionType::Cast(parent).NumParentTypeArguments(),
nullability);
} else {
UNREACHABLE();
}
return ClassFinalizer::FinalizeType(type, ClassFinalizer::kCanonicalize);
}
case kGenericType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if (!cls.is_declaration_loaded()) {
LoadReferencedClass(cls);
}
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(Z, ReadObject());
AbstractType& type =
AbstractType::Handle(Z, Type::New(cls, type_arguments, nullability));
type.SetIsFinalized();
type = type.NormalizeFutureOrType(Heap::kOld);
return type.Canonicalize(thread_);
}
case kFunctionType: {
const intptr_t flags = reader_.ReadUInt();
intptr_t num_parent_type_args = 0;
if ((flags & kFlagHasEnclosingTypeParameters) != 0) {
num_parent_type_args = reader_.ReadUInt();
ASSERT(num_parent_type_args > 0);
ASSERT(!enclosing_function_types_.is_empty());
ASSERT(num_parent_type_args ==
enclosing_function_types_.Last()->NumTypeArguments());
}
auto& signature_type = FunctionType::Handle(
Z, FunctionType::New(num_parent_type_args, nullability));
// TODO(alexmarkov): skip type finalization
return ReadFunctionSignature(
signature_type, Function::null_function(),
(flags & kFlagHasOptionalPositionalParams) != 0,
(flags & kFlagHasOptionalNamedParams) != 0,
(flags & kFlagHasTypeParams) != 0,
/* has_positional_param_names = */ false,
(flags & kFlagHasParameterFlags) != 0);
}
case kRecordType: {
const intptr_t num_positional = reader_.ReadUInt();
const intptr_t num_named = reader_.ReadUInt();
const intptr_t num_fields = num_positional + num_named;
const Array& field_types =
Array::Handle(Z, Array::New(num_fields, Heap::kOld));
const Array& field_names =
(num_named == 0)
? Object::empty_array()
: Array::Handle(Z, Array::New(num_named, Heap::kOld));
AbstractType& type = AbstractType::Handle(Z);
intptr_t pos = 0;
for (intptr_t i = 0; i < num_positional; ++i) {
type ^= ReadObject();
field_types.SetAt(pos++, type);
}
if (num_named > 0) {
String& name = String::Handle(Z);
for (intptr_t i = 0; i < num_named; ++i) {
name ^= ReadObject();
field_names.SetAt(i, name);
type ^= ReadObject();
field_types.SetAt(pos++, type);
}
field_names.MakeImmutable();
}
const RecordShape shape =
RecordShape::Register(thread_, num_fields, field_names);
type = RecordType::New(shape, field_types, nullability);
type.SetIsFinalized();
return type.Canonicalize(thread_);
}
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return Object::null();
}
StringPtr BytecodeReaderHelper::ReadString(bool is_canonical) {
const int kFlagTwoByteString = 1;
const int kHeaderFields = 2;
const int kUInt32Size = 4;
uint32_t ref = reader_.ReadUInt();
const bool isOneByteString = (ref & kFlagTwoByteString) == 0;
intptr_t index = ref >> 1;
if (!isOneByteString) {
const uint32_t num_one_byte_strings =
reader_.ReadUInt32At(bytecode_component_->GetStringsHeaderOffset());
index += num_one_byte_strings;
}
AlternativeReadingScope alt(&reader_,
bytecode_component_->GetStringsHeaderOffset() +
(kHeaderFields + index - 1) * kUInt32Size);
intptr_t start_offs = reader_.ReadUInt32();
intptr_t end_offs = reader_.ReadUInt32();
if (index == 0) {
// For the 0-th string we read a header field instead of end offset of
// the previous string.
start_offs = 0;
}
// Bytecode strings reside in ExternalTypedData which is not movable by GC,
// so it is OK to take a direct pointer to string characters even if
// symbol allocation triggers GC.
const uint8_t* data = reader_.BufferAt(
bytecode_component_->GetStringsContentsOffset() + start_offs);
if (is_canonical) {
if (isOneByteString) {
return Symbols::FromLatin1(thread_, data, end_offs - start_offs);
} else {
return Symbols::FromUTF16(thread_,
reinterpret_cast<const uint16_t*>(data),
(end_offs - start_offs) >> 1);
}
} else {
if (isOneByteString) {
return String::FromLatin1(data, end_offs - start_offs, Heap::kOld);
} else {
return String::FromUTF16(reinterpret_cast<const uint16_t*>(data),
(end_offs - start_offs) >> 1, Heap::kOld);
}
}
}
TypedDataPtr BytecodeReaderHelper::ReadLineStartsData(
intptr_t line_starts_offset) {
AlternativeReadingScope alt(&reader_, line_starts_offset);
const intptr_t num_line_starts = reader_.ReadUInt();
// Choose representation between Uint16 and Uint32 typed data.
intptr_t max_start = 0;
{
AlternativeReadingScope alt2(&reader_, reader_.offset());
for (intptr_t i = 0; i < num_line_starts; ++i) {
const intptr_t delta = reader_.ReadUInt();
max_start += delta;
}
}
const intptr_t cid = (max_start <= kMaxUint16) ? kTypedDataUint16ArrayCid
: kTypedDataUint32ArrayCid;
const TypedData& line_starts_data =
TypedData::Handle(Z, TypedData::New(cid, num_line_starts, Heap::kOld));
intptr_t current_start = 0;
for (intptr_t i = 0; i < num_line_starts; ++i) {
const intptr_t delta = reader_.ReadUInt();
current_start += delta;
if (cid == kTypedDataUint16ArrayCid) {
line_starts_data.SetUint16(i << 1, static_cast<uint16_t>(current_start));
} else {
line_starts_data.SetUint32(i << 2, current_start);
}
}
return line_starts_data.ptr();
}
ScriptPtr BytecodeReaderHelper::ReadSourceFile(const String& uri,
intptr_t offset) {
// SourceFile flags, must be in sync with SourceFile constants in
// pkg/dart2bytecode/lib/declarations.dart.
const int kHasLineStartsFlag = 1 << 0;
const int kHasSourceFlag = 1 << 1;
const int kHasCoveredConstConstructorsFlag = 1 << 2;
AlternativeReadingScope alt(&reader_, offset);
const intptr_t flags = reader_.ReadUInt();
const String& import_uri = String::CheckedHandle(Z, ReadObject());
TypedData& line_starts = TypedData::Handle(Z);
if ((flags & kHasLineStartsFlag) != 0) {
const intptr_t line_starts_offset =
bytecode_component_->GetLineStartsOffset() + reader_.ReadUInt();
line_starts = ReadLineStartsData(line_starts_offset);
}
String& source = String::Handle(Z);
if ((flags & kHasSourceFlag) != 0) {
source = ReadString(/* is_canonical = */ false);
}
const Script& script =
Script::Handle(Z, Script::New(import_uri, uri, source));
script.set_line_starts(line_starts);
if ((flags & kHasCoveredConstConstructorsFlag) != 0) {
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// Any covered const constructors are read later during
// ReadPendingCode, which then sets the collected constant
// coverage for these scripts.
BytecodeLoader* const loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
loader->AddPendingObject(script, reader_.offset());
#endif
}
return script.ptr();
}
TypeArgumentsPtr BytecodeReaderHelper::ReadTypeArguments() {
const intptr_t length = reader_.ReadUInt();
TypeArguments& type_arguments =
TypeArguments::ZoneHandle(Z, TypeArguments::New(length));
AbstractType& type = AbstractType::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
type ^= ReadObject();
type_arguments.SetTypeAt(i, type);
}
return type_arguments.Canonicalize(thread_);
}
void BytecodeReaderHelper::ReadAnnotations(const Class& cls,
const Object& declaration,
bool has_pragma) {
const intptr_t annotations_offset =
reader_.ReadUInt() + bytecode_component_->GetAnnotationsOffset();
ASSERT(annotations_offset > 0);
#if !defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_enable_mirrors || has_pragma) {
AlternativeReadingScope alt(&reader_, annotations_offset);
const auto& metadata = Object::Handle(Z, ReadObject());
ASSERT(metadata.IsArray());
const auto& library = Library::Handle(Z, cls.library());
library.AddMetadata(declaration, metadata);
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
}
void BytecodeReaderHelper::ReadMembers(const Class& cls, bool discard_fields) {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(cls.is_type_finalized());
ASSERT(!cls.is_loaded());
const intptr_t num_functions = reader_.ReadUInt();
functions_ = &Array::Handle(Z, Array::New(num_functions, Heap::kOld));
function_index_ = 0;
ReadFieldDeclarations(cls, discard_fields);
ReadFunctionDeclarations(cls);
ASSERT(!cls.is_loaded());
cls.set_is_loaded(true);
}
void BytecodeReaderHelper::ReadFieldDeclarations(const Class& cls,
bool discard_fields) {
// Field flags, must be in sync with FieldDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
const int kIsStaticFlag = 1 << 0;
const int kIsConstFlag = 1 << 1;
const int kIsFinalFlag = 1 << 2;
const int kIsLateFlag = 1 << 3;
const int kIsCovariantFlag = 1 << 4;
const int kIsCovariantByClassFlag = 1 << 5;
const int kIsExtensionMemberFlag = 1 << 6;
const int kIsReflectableFlag = 1 << 7;
const int kHasGetterFlag = 1 << 8;
const int kHasSetterFlag = 1 << 9;
const int kHasInitializerFlag = 1 << 10;
const int kHasNontrivialInitializerFlag = 1 << 11;
const int kHasInitializerCodeFlag = 1 << 12;
const int kHasSourcePositionsFlag = 1 << 13;
const int kHasAnnotationsFlag = 1 << 14;
const int kHasPragmaFlag = 1 << 15;
const int kHasCustomScriptFlag = 1 << 16;
const int kIsExtensionTypeMemberFlag = 1 << 17;
const int kIsSharedFlag = 1 << 18;
const int num_fields = reader_.ReadListLength();
if ((num_fields == 0) && !cls.is_enum_class()) {
return;
}
const Array& fields = Array::Handle(
Z, Array::New(num_fields + (cls.is_enum_class() ? 1 : 0), Heap::kOld));
String& name = String::Handle(Z);
Object& script_class = Object::Handle(Z);
AbstractType& type = AbstractType::Handle(Z);
Field& field = Field::Handle(Z);
Object& value = Object::Handle(Z);
Function& function = Function::Handle(Z);
for (intptr_t i = 0; i < num_fields; ++i) {
intptr_t flags = reader_.ReadUInt();
const bool is_static = (flags & kIsStaticFlag) != 0;
const bool is_final = (flags & kIsFinalFlag) != 0;
const bool is_const = (flags & kIsConstFlag) != 0;
const bool is_late = (flags & kIsLateFlag) != 0;
const bool has_nontrivial_initializer =
(flags & kHasNontrivialInitializerFlag) != 0;
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
const bool is_extension_member = (flags & kIsExtensionMemberFlag) != 0;
const bool is_extension_type_member =
(flags & kIsExtensionTypeMemberFlag) != 0;
const bool has_initializer = (flags & kHasInitializerFlag) != 0;
const bool is_shared = (flags & kIsSharedFlag) != 0;
name ^= ReadObject();
type ^= ReadObject();
if ((flags & kHasCustomScriptFlag) != 0) {
Script& script = Script::CheckedHandle(Z, ReadObject());
script_class = GetPatchClass(cls, script);
} else {
script_class = cls.ptr();
}
TokenPosition position = TokenPosition::kNoSource;
TokenPosition end_position = TokenPosition::kNoSource;
if ((flags & kHasSourcePositionsFlag) != 0) {
position = reader_.ReadPosition();
end_position = reader_.ReadPosition();
}
field = Field::New(name, is_static, is_final, is_const,
(flags & kIsReflectableFlag) != 0, is_late, script_class,
type, position, end_position);
field.set_is_unboxed(false);
field.set_has_pragma(has_pragma);
field.set_is_covariant((flags & kIsCovariantFlag) != 0);
field.set_is_generic_covariant_impl((flags & kIsCovariantByClassFlag) != 0);
field.set_has_nontrivial_initializer(has_nontrivial_initializer);
field.set_is_extension_member(is_extension_member);
field.set_is_extension_type_member(is_extension_type_member);
field.set_has_initializer(has_initializer);
field.set_is_shared(is_shared);
if (!has_nontrivial_initializer) {
value = ReadObject();
if (is_static) {
if (field.is_late() && !has_initializer) {
value = Object::sentinel().ptr();
}
} else {
// Null-initialized instance fields are tracked separately for each
// constructor (see handling of kHasNullableFieldsFlag).
if (!value.IsNull()) {
field.RecordStore(value);
}
}
}
if ((flags & kHasInitializerCodeFlag) != 0) {
const intptr_t code_offset = reader_.ReadUInt();
BytecodeLoader* loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
loader->SetOffset(field,
code_offset + bytecode_component_->GetCodesOffset());
if (is_static) {
value = Object::sentinel().ptr();
}
}
if ((flags & kHasGetterFlag) != 0) {
name ^= ReadObject();
const auto& signature = FunctionType::Handle(Z, FunctionType::New());
function =
Function::New(signature, name,
is_static ? UntaggedFunction::kImplicitStaticGetter
: UntaggedFunction::kImplicitGetter,
is_static, is_const,
false, // is_abstract
false, // is_external
false, // is_native
script_class, position);
NOT_IN_PRECOMPILED(function.set_end_token_pos(end_position));
signature.set_result_type(type);
function.set_is_debuggable(false);
function.set_accessor_field(field);
function.set_is_extension_member(is_extension_member);
function.set_is_extension_type_member(is_extension_type_member);
SetupFieldAccessorFunction(cls, function, type);
if (is_static) {
if (is_shared) {
function.AttachBytecode(
Object::implicit_shared_static_getter_bytecode());
} else {
function.AttachBytecode(Object::implicit_static_getter_bytecode());
}
} else {
function.AttachBytecode(Object::implicit_getter_bytecode());
}
functions_->SetAt(function_index_++, function);
}
if ((flags & kHasSetterFlag) != 0) {
ASSERT(is_late || ((!is_static) && (!is_final)) || is_shared);
ASSERT(!is_const);
name ^= ReadObject();
const auto& signature = FunctionType::Handle(Z, FunctionType::New());
function = Function::New(signature, name,
UntaggedFunction::kImplicitSetter, is_static,
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
script_class, position);
NOT_IN_PRECOMPILED(function.set_end_token_pos(end_position));
signature.set_result_type(Object::void_type());
function.set_is_debuggable(false);
function.set_accessor_field(field);
function.set_is_extension_member(is_extension_member);
function.set_is_extension_type_member(is_extension_type_member);
SetupFieldAccessorFunction(cls, function, type);
if (is_static) {
if (is_shared) {
function.AttachBytecode(
Object::implicit_shared_static_setter_bytecode());
} else {
function.AttachBytecode(Object::implicit_static_setter_bytecode());
}
} else {
function.AttachBytecode(Object::implicit_setter_bytecode());
}
functions_->SetAt(function_index_++, function);
}
if ((flags & kHasAnnotationsFlag) != 0) {
ReadAnnotations(cls, field, has_pragma);
}
if (field.is_static()) {
IG->RegisterStaticField(field, value);
}
fields.SetAt(i, field);
}
if (cls.is_enum_class()) {
// Add static field 'const _deleted_enum_sentinel'.
field = Field::New(Symbols::_DeletedEnumSentinel(),
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
/* is_reflectable = */ false,
/* is_late = */ false, cls, Object::dynamic_type(),
TokenPosition::kNoSource, TokenPosition::kNoSource);
IG->RegisterStaticField(field, Object::null_object());
fields.SetAt(num_fields, field);
}
if (!discard_fields) {
cls.SetFields(fields);
}
if (cls.IsTopLevel()) {
const Library& library = Library::Handle(Z, cls.library());
for (intptr_t i = 0, n = fields.Length(); i < n; ++i) {
field ^= fields.At(i);
name = field.name();
library.AddObject(field, name);
}
}
}
// TODO(alexmarkov): unify with
// TranslationHelper::SetupFieldAccessorFunction.
void BytecodeReaderHelper::SetupFieldAccessorFunction(
const Class& klass,
const Function& function,
const AbstractType& field_type) {
bool is_setter = function.IsImplicitSetterFunction();
bool is_method = !function.IsStaticFunction();
intptr_t parameter_count = (is_method ? 1 : 0) + (is_setter ? 1 : 0);
const FunctionType& signature = FunctionType::Handle(Z, function.signature());
signature.SetNumOptionalParameters(0, false);
signature.set_num_fixed_parameters(parameter_count);
if (parameter_count > 0) {
signature.set_parameter_types(
Array::Handle(Z, Array::New(parameter_count, Heap::kOld)));
}
NOT_IN_PRECOMPILED(function.CreateNameArray());
intptr_t pos = 0;
if (is_method) {
signature.SetParameterTypeAt(
pos, AbstractType::Handle(Z, klass.DeclarationType()));
NOT_IN_PRECOMPILED(function.SetParameterNameAt(pos, Symbols::This()));
pos++;
}
if (is_setter) {
signature.SetParameterTypeAt(pos, field_type);
NOT_IN_PRECOMPILED(function.SetParameterNameAt(pos, Symbols::Value()));
pos++;
}
}
PatchClassPtr BytecodeReaderHelper::GetPatchClass(const Class& cls,
const Script& script) {
if (patch_class_ != nullptr && patch_class_->wrapped_class() == cls.ptr() &&
patch_class_->script() == script.ptr()) {
return patch_class_->ptr();
}
if (patch_class_ == nullptr) {
patch_class_ = &PatchClass::Handle(Z);
}
*patch_class_ = PatchClass::New(cls, KernelProgramInfo::Handle(Z), script);
return patch_class_->ptr();
}
InstancePtr BytecodeReaderHelper::Canonicalize(const Instance& instance) {
if (instance.IsNull()) return instance.ptr();
return instance.Canonicalize(thread_);
}
void BytecodeReaderHelper::ReadFunctionDeclarations(const Class& cls) {
// Function flags, must be in sync with FunctionDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
const int kIsStaticFlag = 1 << 0;
const int kIsAbstractFlag = 1 << 1;
const int kIsGetterFlag = 1 << 2;
const int kIsSetterFlag = 1 << 3;
const int kIsConstructorFlag = 1 << 4;
const int kIsFactoryFlag = 1 << 5;
const int kIsConstFlag = 1 << 6;
const int kHasOptionalPositionalParamsFlag = 1 << 7;
const int kHasOptionalNamedParamsFlag = 1 << 8;
const int kHasTypeParamsFlag = 1 << 9;
const int kHasParameterFlagsFlag = 1 << 10;
const int kIsExtensionMemberFlag = 1 << 11;
const int kIsReflectableFlag = 1 << 12;
const int kIsDebuggableFlag = 1 << 13;
const int kIsAsyncFlag = 1 << 14;
const int kIsAsyncStarFlag = 1 << 15;
const int kIsSyncStarFlag = 1 << 16;
const int kIsNoSuchMethodForwarderFlag = 1 << 17;
const int kIsExternalFlag = 1 << 18;
const int kIsNativeFlag = 1 << 19;
const int kHasSourcePositionsFlag = 1 << 20;
const int kHasAnnotationsFlag = 1 << 21;
const int kHasPragmaFlag = 1 << 22;
const int kHasCustomScriptFlag = 1 << 23;
const int kIsExtensionTypeMemberFlag = 1 << 24;
const int kIsInvisibleFlag = 1 << 25;
const intptr_t num_functions = reader_.ReadListLength();
ASSERT(function_index_ + num_functions == functions_->Length());
if (function_index_ + num_functions == 0) {
return;
}
String& name = String::Handle(Z);
Object& script_class = Object::Handle(Z);
FunctionType& signature = FunctionType::Handle(Z);
Function& function = Function::Handle(Z);
Array& parameter_types = Array::Handle(Z);
AbstractType& type = AbstractType::Handle(Z);
name = cls.ScrubbedName();
for (intptr_t i = 0; i < num_functions; ++i) {
intptr_t flags = reader_.ReadUInt();
const bool is_static = (flags & kIsStaticFlag) != 0;
const bool is_native = (flags & kIsNativeFlag) != 0;
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
const bool is_extension_member = (flags & kIsExtensionMemberFlag) != 0;
const bool is_extension_type_member =
(flags & kIsExtensionTypeMemberFlag) != 0;
name ^= ReadObject();
if ((flags & kHasCustomScriptFlag) != 0) {
Script& script = Script::CheckedHandle(Z, ReadObject());
script_class = GetPatchClass(cls, script);
} else {
script_class = cls.ptr();
}
TokenPosition position = TokenPosition::kNoSource;
TokenPosition end_position = TokenPosition::kNoSource;
if ((flags & kHasSourcePositionsFlag) != 0) {
position = reader_.ReadPosition();
end_position = reader_.ReadPosition();
}
UntaggedFunction::Kind kind = UntaggedFunction::kRegularFunction;
if ((flags & kIsGetterFlag) != 0) {
kind = UntaggedFunction::kGetterFunction;
} else if ((flags & kIsSetterFlag) != 0) {
kind = UntaggedFunction::kSetterFunction;
} else if ((flags & (kIsConstructorFlag | kIsFactoryFlag)) != 0) {
kind = UntaggedFunction::kConstructor;
name = ConstructorName(cls, name);
}
signature = FunctionType::New();
function = Function::New(
signature, name, kind, is_static, (flags & kIsConstFlag) != 0,
(flags & kIsAbstractFlag) != 0, (flags & kIsExternalFlag) != 0,
is_native, script_class, position);
const bool is_expression_evaluation =
(name.ptr() == Symbols::DebugProcedureName().ptr());
// Declare function scope as types (type parameters) in function
// signature may back-reference to the function being declared.
// At this moment, owner class is not fully loaded yet and it won't be
// able to serve function lookup requests.
FunctionScope function_scope(this, function, name, cls);
FunctionTypeScope function_type_scope(this, signature);
// Set the expression evaluation function now so that references to it
// can be appropriately substituted as needed when reading objects below.
if (is_expression_evaluation) {
ASSERT(!function.is_abstract());
thread_->bytecode_loader()->SetExpressionEvaluationFunction(function);
}
function.set_has_pragma(has_pragma);
NOT_IN_PRECOMPILED(function.set_end_token_pos(end_position));
function.set_is_synthetic((flags & kIsNoSuchMethodForwarderFlag) != 0);
function.set_is_reflectable((flags & kIsReflectableFlag) != 0);
function.set_is_debuggable((flags & kIsDebuggableFlag) != 0);
function.set_is_visible((flags & kIsInvisibleFlag) == 0);
function.set_is_extension_member(is_extension_member);
function.set_is_extension_type_member(is_extension_type_member);
if ((flags & kIsSyncStarFlag) != 0) {
function.set_modifier(UntaggedFunction::kSyncGen);
function.set_is_inlinable(false);
} else if ((flags & kIsAsyncFlag) != 0) {
function.set_modifier(UntaggedFunction::kAsync);
function.set_is_inlinable(false);
} else if ((flags & kIsAsyncStarFlag) != 0) {
function.set_modifier(UntaggedFunction::kAsyncGen);
function.set_is_inlinable(false);
}
if ((flags & kHasTypeParamsFlag) != 0) {
ReadTypeParametersDeclaration(Class::Handle(Z), signature);
}
const intptr_t num_implicit_params = (!is_static) ? 1 : 0;
const intptr_t num_params = num_implicit_params + reader_.ReadUInt();
const bool has_optional_named_params =
((flags & kHasOptionalNamedParamsFlag) != 0);
intptr_t num_required_params = num_params;
if ((flags & (kHasOptionalPositionalParamsFlag |
kHasOptionalNamedParamsFlag)) != 0) {
num_required_params = num_implicit_params + reader_.ReadUInt();
}
signature.set_num_fixed_parameters(num_required_params);
signature.SetNumOptionalParameters(num_params - num_required_params,
!has_optional_named_params);
if (num_params > 0) {
parameter_types = Array::New(num_params, Heap::kOld);
signature.set_parameter_types(parameter_types);
signature.CreateNameArrayIncludingFlags(Heap::kOld);
NOT_IN_PRECOMPILED(function.CreateNameArray());
}
intptr_t param_index = 0;
if (!is_static) {
if (is_expression_evaluation) {
// Do not reference enclosing class as expression evaluation
// method logically belongs to another (real) class.
// Enclosing class is not registered and doesn't have
// a valid cid, so it can't be used in a type.
type = AbstractType::dynamic_type().ptr();
} else {
type = cls.DeclarationType();
}
signature.SetParameterTypeAt(param_index, type);
NOT_IN_PRECOMPILED(
function.SetParameterNameAt(param_index, Symbols::This()));
++param_index;
}
for (; param_index < num_params; ++param_index) {
name ^= ReadObject();
if (has_optional_named_params && (param_index >= num_required_params)) {
signature.SetParameterNameAt(param_index, name);
} else {
NOT_IN_PRECOMPILED(function.SetParameterNameAt(param_index, name));
}
type ^= ReadObject();
signature.SetParameterTypeAt(param_index, type);
}
if ((flags & kHasParameterFlagsFlag) != 0) {
RELEASE_ASSERT(function.HasOptionalNamedParameters());
const intptr_t length = reader_.ReadUInt();
for (intptr_t i = 0; i < length; i++) {
const intptr_t param_flags = reader_.ReadUInt();
if ((param_flags & Parameter::kIsRequiredFlag) != 0) {
signature.SetIsRequiredAt(num_required_params + i);
}
}
}
type ^= ReadObject();
signature.set_result_type(type);
if (is_native) {
name ^= ReadObject();
if (!name.IsNull()) {
function.set_native_name(name);
}
}
if ((flags & kIsAbstractFlag) == 0) {
const intptr_t code_offset = reader_.ReadUInt();
BytecodeLoader* loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
loader->SetOffset(function,
code_offset + bytecode_component_->GetCodesOffset());
}
if ((flags & kHasAnnotationsFlag) != 0) {
ASSERT(!is_expression_evaluation);
ReadAnnotations(cls, function, has_pragma);
}
if (is_expression_evaluation) {
ReadCode(function, thread_->bytecode_loader()->GetOffset(function));
}
functions_->SetAt(function_index_++, function);
}
{
Thread* thread = Thread::Current();
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
cls.SetFunctions(*functions_);
}
if (cls.IsTopLevel()) {
const Library& library = Library::Handle(Z, cls.library());
for (intptr_t i = 0, n = functions_->Length(); i < n; ++i) {
function ^= functions_->At(i);
name = function.name();
library.AddObject(function, name);
}
}
functions_ = nullptr;
}
void BytecodeReaderHelper::LoadReferencedClass(const Class& cls) {
ASSERT(!cls.is_declaration_loaded());
if (!cls.is_declared_in_bytecode()) {
cls.EnsureDeclarationLoaded();
return;
}
BytecodeLoader* loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
AlternativeReadingScope alt(&reader_, loader->GetOffset(cls));
ReadClassDeclaration(cls);
}
void BytecodeReaderHelper::ReadClassDeclaration(const Class& cls) {
// Class flags, must be in sync with ClassDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
const int kIsAbstractFlag = 1 << 0;
const int kIsEnumFlag = 1 << 1;
const int kHasTypeParamsFlag = 1 << 2;
const int kHasTypeArgumentsFlag = 1 << 3;
const int kIsTransformedMixinApplicationFlag = 1 << 4;
const int kHasSourcePositionsFlag = 1 << 5;
const int kHasAnnotationsFlag = 1 << 6;
const int kHasPragmaFlag = 1 << 7;
const int kHasConstConstructorFlag = 1 << 8;
const int kIsSealedFlag = 1 << 9;
const int kIsMixinClassFlag = 1 << 10;
const int kIsBaseClassFlag = 1 << 11;
const int kIsInterfaceFlag = 1 << 12;
const int kIsFinalFlag = 1 << 13;
const int kIsDeeplyImmutableFlag = 1 << 14;
// Class is allocated when reading library declaration in
// BytecodeReaderHelper::ReadLibraryDeclaration.
// Its cid is set in Class::New / IsolateGroup::RegisterClass /
// ClassTable::Register, unless it was loaded for expression evaluation.
ASSERT(cls.is_declared_in_bytecode());
ASSERT(!cls.is_declaration_loaded());
const intptr_t flags = reader_.ReadUInt();
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
// Set early to enable access to type_parameters().
// TODO(alexmarkov): revise early stamping of native wrapper classes
// as loaded.
if (!cls.is_declaration_loaded()) {
cls.set_is_declaration_loaded();
}
const auto& script = Script::CheckedHandle(Z, ReadObject());
cls.set_script(script);
TokenPosition position = TokenPosition::kNoSource;
TokenPosition end_position = TokenPosition::kNoSource;
if ((flags & kHasSourcePositionsFlag) != 0) {
position = reader_.ReadPosition();
end_position = reader_.ReadPosition();
NOT_IN_PRECOMPILED(cls.set_token_pos(position));
NOT_IN_PRECOMPILED(cls.set_end_token_pos(end_position));
}
cls.set_has_pragma(has_pragma);
if ((flags & kIsAbstractFlag) != 0) {
cls.set_is_abstract();
}
if ((flags & kIsEnumFlag) != 0) {
cls.set_is_enum_class();
}
if ((flags & kIsTransformedMixinApplicationFlag) != 0) {
cls.set_is_transformed_mixin_application();
}
if ((flags & kHasConstConstructorFlag) != 0) {
cls.set_is_const();
}
if ((flags & kIsSealedFlag) != 0) {
cls.set_is_sealed();
}
if ((flags & kIsMixinClassFlag) != 0) {
cls.set_is_mixin_class();
}
if ((flags & kIsBaseClassFlag) != 0) {
cls.set_is_base_class();
}
if ((flags & kIsInterfaceFlag) != 0) {
cls.set_is_interface_class();
}
if ((flags & kIsFinalFlag) != 0) {
cls.set_is_final();
}
if ((flags & kIsDeeplyImmutableFlag) != 0) {
cls.set_is_deeply_immutable(true);
}
intptr_t num_type_arguments = 0;
if ((flags & kHasTypeArgumentsFlag) != 0) {
num_type_arguments = reader_.ReadUInt();
}
cls.set_num_type_arguments(num_type_arguments);
if ((flags & kHasTypeParamsFlag) != 0) {
ReadTypeParametersDeclaration(cls, Object::null_function_type());
}
auto& type = AbstractType::CheckedHandle(Z, ReadObject());
if (!type.IsNull()) {
cls.set_super_type(Type::Cast(type));
}
const intptr_t num_interfaces = reader_.ReadUInt();
if (num_interfaces > 0) {
const auto& interfaces =
Array::Handle(Z, Array::New(num_interfaces, Heap::kOld));
for (intptr_t i = 0; i < num_interfaces; ++i) {
type ^= ReadObject();
interfaces.SetAt(i, type);
}
cls.set_interfaces(interfaces);
}
if ((flags & kHasAnnotationsFlag) != 0) {
ReadAnnotations(cls, cls, has_pragma);
}
const intptr_t members_offset = reader_.ReadUInt();
BytecodeLoader* loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
loader->SetOffset(cls,
members_offset + bytecode_component_->GetMembersOffset());
if (!cls.is_type_finalized()) {
ClassFinalizer::FinalizeTypesInClass(cls);
}
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
cls.SetUserVisibleNameInClassTable();
#endif
}
void BytecodeReaderHelper::ReadLibraryDeclaration(const Library& library,
bool register_classes) {
// Library flags, must be in sync with LibraryDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
// const int kUsesDartMirrorsFlag = 1 << 0;
// const int kUsesDartFfiFlag = 1 << 1;
ASSERT(!library.Loaded());
ASSERT(library.toplevel_class() == Object::null());
// TODO(alexmarkov): fill in library.used_scripts.
reader_.ReadUInt(); // Flags.
auto& name = String::CheckedHandle(Z, ReadObject());
ASSERT(name.ptr() !=
Symbols::Symbol(Symbols::kDartNativeWrappersLibNameId).ptr());
library.SetName(name);
const auto& script = Script::CheckedHandle(Z, ReadObject());
library.SetLoadInProgress();
const intptr_t num_classes = reader_.ReadUInt();
ASSERT(num_classes > 0);
auto& cls = Class::Handle(Z);
for (intptr_t i = 0; i < num_classes; ++i) {
name ^= ReadObject();
const intptr_t class_offset =
bytecode_component_->GetClassesOffset() + reader_.ReadUInt();
if (i == 0) {
ASSERT(name.ptr() == Symbols::Empty().ptr());
cls = Class::New(library, Symbols::TopLevel(), script,
TokenPosition::kNoSource, register_classes);
cls.set_is_declared_in_bytecode(true);
library.set_toplevel_class(cls);
} else {
cls = Class::New(library, name, script, TokenPosition::kNoSource,
register_classes);
cls.set_is_declared_in_bytecode(true);
if (register_classes) {
library.AddClass(cls);
}
}
BytecodeLoader* loader = thread_->bytecode_loader();
ASSERT(loader != nullptr);
loader->AddPendingObject(cls, class_offset);
}
ASSERT(!library.Loaded());
library.SetLoaded();
}
void BytecodeReaderHelper::ReadLibraryDeclarations(
intptr_t num_libraries,
const GrowableObjectArray& pending_objects,
bool load_code) {
auto& library = Library::Handle(Z);
auto& uri = String::Handle(Z);
// Verify that libraries in the dynamic module are not loaded yet.
{
AlternativeReadingScope alt(&reader_, reader_.offset());
for (intptr_t i = 0; i < num_libraries; ++i) {
uri ^= ReadObject();
reader_.ReadUInt(); // Skip library offset.
library = Library::LookupLibrary(thread_, uri);
if (!library.IsNull()) {
const String& msg = String::Handle(String::NewFormatted(
"Unable to load dynamic module: library \'%s\' is already loaded",
uri.ToCString()));
Exceptions::ThrowStateError(msg);
}
}
}
for (intptr_t i = 0; i < num_libraries; ++i) {
uri ^= ReadObject();
const intptr_t library_offset =
bytecode_component_->GetLibrariesOffset() + reader_.ReadUInt();
bool register_classes = true;
library = Library::New(uri);
if (uri.ptr() == Symbols::EvalSourceUri().ptr()) {
thread_->bytecode_loader()->SetExpressionEvaluationLibrary(library);
register_classes = false;
} else {
library.Register(thread_);
}
ASSERT(!library.Loaded());
AlternativeReadingScope alt(&reader_, library_offset);
ReadLibraryDeclaration(library, register_classes);
}
if (load_code) {
ReadPendingCode(pending_objects);
}
}
void BytecodeReaderHelper::ReadPendingCode(
const GrowableObjectArray& pending_objects) {
auto& obj = Object::Handle(Z);
auto& error = Error::Handle(Z);
auto& members = Array::Handle(Z);
auto& function = Function::Handle(Z);
auto& field = Field::Handle(Z);
// Handling pending objects can end up adding new pending objects
// to the array, so re-retrieve the length for each iteration.
for (intptr_t i = 0; i < pending_objects.Length(); ++i) {
obj = pending_objects.At(i);
ASSERT(!obj.IsNull());
if (obj.IsClass()) {
const auto& cls = Class::Cast(obj);
error = cls.EnsureIsFinalized(thread_);
if (!error.IsNull()) {
Exceptions::PropagateError(error);
UNREACHABLE();
}
members = cls.functions();
for (intptr_t j = 0, m = members.Length(); j < m; ++j) {
function ^= members.At(j);
if (!function.is_abstract() && !function.HasBytecode()) {
ReadCode(function, thread_->bytecode_loader()->GetOffset(function));
}
}
members = cls.fields();
for (intptr_t j = 0, m = members.Length(); j < m; ++j) {
field ^= members.At(j);
if (field.has_nontrivial_initializer()) {
if (field.is_static() || field.is_late() ||
thread_->bytecode_loader()->HasOffset(field)) {
function = field.EnsureInitializerFunction();
if (!function.HasBytecode()) {
ReadCode(function, thread_->bytecode_loader()->GetOffset(field));
}
}
}
}
} else if (obj.IsScript()) {
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
const auto& script = Script::Cast(obj);
ReadCoveredConstConstructors(
script, thread_->bytecode_loader()->GetOffset(script));
#else
UNREACHABLE();
#endif
} else {
FATAL("Unexpected object %s in pending objects list", obj.ToCString());
}
}
}
void BytecodeReaderHelper::FindModifiedLibraries(BitVector* modified_libs,
intptr_t num_libraries) {
auto& uri = String::Handle(Z);
auto& lib = Library::Handle(Z);
for (intptr_t i = 0; i < num_libraries; ++i) {
uri ^= ReadObject();
reader_.ReadUInt(); // Skip offset.
lib = Library::LookupLibrary(thread_, uri);
if (!lib.IsNull() && !lib.is_dart_scheme()) {
// This is a library that already exists so mark it as being modified.
modified_libs->Add(lib.index());
}
}
}
void BytecodeReaderHelper::ReadParameterCovariance(
const Function& function,
intptr_t code_offset,
BitVector* is_covariant,
BitVector* is_generic_covariant_impl) {
ASSERT(function.is_declared_in_bytecode());
const intptr_t num_params = function.NumParameters();
ASSERT(is_covariant->length() == num_params);
ASSERT(is_generic_covariant_impl->length() == num_params);
AlternativeReadingScope alt(&reader_, code_offset);
const intptr_t code_flags = reader_.ReadUInt();
if ((code_flags & Code::kHasParameterFlagsFlag) != 0) {
const intptr_t num_explicit_params = reader_.ReadUInt();
ASSERT(num_params ==
function.NumImplicitParameters() + num_explicit_params);
for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
const intptr_t flags = reader_.ReadUInt();
if ((flags & Parameter::kIsCovariantFlag) != 0) {
is_covariant->Add(i);
}
if ((flags & Parameter::kIsCovariantByClassFlag) != 0) {
is_generic_covariant_impl->Add(i);
}
}
}
}
LibraryPtr BytecodeReaderHelper::ReadMain() {
return Library::RawCast(ReadObject());
}
TypedDataBasePtr BytecodeComponentData::GetTypedData() const {
return TypedDataBase::RawCast(data_.At(kTypedData));
}
intptr_t BytecodeComponentData::GetVersion() const {
return Smi::Value(Smi::RawCast(data_.At(kVersion)));
}
intptr_t BytecodeComponentData::GetStringsHeaderOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kStringsHeaderOffset)));
}
intptr_t BytecodeComponentData::GetStringsContentsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kStringsContentsOffset)));
}
intptr_t BytecodeComponentData::GetObjectOffsetsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kObjectOffsetsOffset)));
}
intptr_t BytecodeComponentData::GetNumObjects() const {
return Smi::Value(Smi::RawCast(data_.At(kNumObjects)));
}
intptr_t BytecodeComponentData::GetObjectsContentsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kObjectsContentsOffset)));
}
intptr_t BytecodeComponentData::GetMainOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kMainOffset)));
}
intptr_t BytecodeComponentData::GetNumLibraries() const {
return Smi::Value(Smi::RawCast(data_.At(kNumLibraries)));
}
intptr_t BytecodeComponentData::GetLibraryIndexOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kLibraryIndexOffset)));
}
intptr_t BytecodeComponentData::GetLibrariesOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kLibrariesOffset)));
}
intptr_t BytecodeComponentData::GetNumClasses() const {
return Smi::Value(Smi::RawCast(data_.At(kNumClasses)));
}
intptr_t BytecodeComponentData::GetClassesOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kClassesOffset)));
}
intptr_t BytecodeComponentData::GetMembersOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kMembersOffset)));
}
intptr_t BytecodeComponentData::GetNumCodes() const {
return Smi::Value(Smi::RawCast(data_.At(kNumCodes)));
}
intptr_t BytecodeComponentData::GetCodesOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kCodesOffset)));
}
intptr_t BytecodeComponentData::GetSourcePositionsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kSourcePositionsOffset)));
}
intptr_t BytecodeComponentData::GetSourceFilesOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kSourceFilesOffset)));
}
intptr_t BytecodeComponentData::GetLineStartsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kLineStartsOffset)));
}
intptr_t BytecodeComponentData::GetLocalVariablesOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kLocalVariablesOffset)));
}
intptr_t BytecodeComponentData::GetRecordedCoverageOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kRecordedCoverageOffset)));
}
intptr_t BytecodeComponentData::GetAnnotationsOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kAnnotationsOffset)));
}
void BytecodeComponentData::SetObject(intptr_t index, const Object& obj) const {
data_.SetAt(kNumFields + index, obj);
}
ObjectPtr BytecodeComponentData::GetObject(intptr_t index) const {
return data_.At(kNumFields + index);
}
ArrayPtr BytecodeComponentData::New(Zone* zone,
const TypedDataBase& typed_data,
intptr_t version,
intptr_t num_objects,
intptr_t strings_header_offset,
intptr_t strings_contents_offset,
intptr_t object_offsets_offset,
intptr_t objects_contents_offset,
intptr_t main_offset,
intptr_t num_libraries,
intptr_t library_index_offset,
intptr_t libraries_offset,
intptr_t num_classes,
intptr_t classes_offset,
intptr_t members_offset,
intptr_t num_codes,
intptr_t codes_offset,
intptr_t source_positions_offset,
intptr_t source_files_offset,
intptr_t line_starts_offset,
intptr_t local_variables_offset,
intptr_t recorded_coverage_offset,
intptr_t annotations_offset,
Heap::Space space) {
const Array& data =
Array::Handle(zone, Array::New(kNumFields + num_objects, space));
Smi& smi_handle = Smi::Handle(zone);
data.SetAt(kTypedData, typed_data);
smi_handle = Smi::New(version);
data.SetAt(kVersion, smi_handle);
smi_handle = Smi::New(strings_header_offset);
data.SetAt(kStringsHeaderOffset, smi_handle);
smi_handle = Smi::New(strings_contents_offset);
data.SetAt(kStringsContentsOffset, smi_handle);
smi_handle = Smi::New(object_offsets_offset);
data.SetAt(kObjectOffsetsOffset, smi_handle);
smi_handle = Smi::New(num_objects);
data.SetAt(kNumObjects, smi_handle);
smi_handle = Smi::New(objects_contents_offset);
data.SetAt(kObjectsContentsOffset, smi_handle);
smi_handle = Smi::New(main_offset);
data.SetAt(kMainOffset, smi_handle);
smi_handle = Smi::New(num_libraries);
data.SetAt(kNumLibraries, smi_handle);
smi_handle = Smi::New(library_index_offset);
data.SetAt(kLibraryIndexOffset, smi_handle);
smi_handle = Smi::New(libraries_offset);
data.SetAt(kLibrariesOffset, smi_handle);
smi_handle = Smi::New(num_classes);
data.SetAt(kNumClasses, smi_handle);
smi_handle = Smi::New(classes_offset);
data.SetAt(kClassesOffset, smi_handle);
smi_handle = Smi::New(members_offset);
data.SetAt(kMembersOffset, smi_handle);
smi_handle = Smi::New(num_codes);
data.SetAt(kNumCodes, smi_handle);
smi_handle = Smi::New(codes_offset);
data.SetAt(kCodesOffset, smi_handle);
smi_handle = Smi::New(source_positions_offset);
data.SetAt(kSourcePositionsOffset, smi_handle);
smi_handle = Smi::New(source_files_offset);
data.SetAt(kSourceFilesOffset, smi_handle);
smi_handle = Smi::New(line_starts_offset);
data.SetAt(kLineStartsOffset, smi_handle);
smi_handle = Smi::New(local_variables_offset);
data.SetAt(kLocalVariablesOffset, smi_handle);
smi_handle = Smi::New(recorded_coverage_offset);
data.SetAt(kRecordedCoverageOffset, smi_handle);
smi_handle = Smi::New(annotations_offset);
data.SetAt(kAnnotationsOffset, smi_handle);
return data.ptr();
}
void BytecodeReader::LoadClassDeclaration(const Class& cls) {
ASSERT(cls.is_declared_in_bytecode());
ASSERT(!cls.is_declaration_loaded());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
BytecodeLoader* loader = thread->bytecode_loader();
ASSERT(loader != nullptr);
BytecodeComponentData bytecode_component(
Array::Handle(zone, loader->bytecode_component_array()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(thread, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
loader->GetOffset(cls));
bytecode_reader.ReadClassDeclaration(cls);
}
void BytecodeReader::FinishClassLoading(const Class& cls) {
ASSERT(cls.is_declared_in_bytecode());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
BytecodeLoader* loader = thread->bytecode_loader();
ASSERT(loader != nullptr);
BytecodeComponentData bytecode_component(
Array::Handle(zone, loader->bytecode_component_array()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(thread, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
loader->GetOffset(cls));
// If this is a dart:internal.ClassID class ignore field declarations
// contained in the Kernel file and instead inject our own const
// fields.
const bool discard_fields = cls.InjectCIDFields();
bytecode_reader.ReadMembers(cls, discard_fields);
}
void BytecodeReader::ReadParameterCovariance(
const Function& function,
BitVector* is_covariant,
BitVector* is_generic_covariant_impl) {
ASSERT(function.is_declared_in_bytecode());
ASSERT(!function.IsClosureFunction());
// Method extractors of abstract methods are only used as
// targets of interface calls, so covariance of parameters is irrelevant.
if (function.is_abstract()) {
return;
}
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
auto& binary = TypedDataBase::Handle(zone);
intptr_t offset = 0;
const auto& bytecode = Bytecode::Handle(zone, function.GetBytecode());
if (bytecode.IsNull()) {
BytecodeLoader* loader = thread->bytecode_loader();
ASSERT(loader != nullptr);
binary = loader->binary();
offset = loader->GetOffset(function);
} else {
binary = bytecode.binary();
ASSERT(!binary.IsNull());
offset = bytecode.code_offset();
ASSERT(offset > 0);
}
BytecodeReaderHelper bytecode_reader(thread, binary);
bytecode_reader.ReadParameterCovariance(function, offset, is_covariant,
is_generic_covariant_impl);
}
static void CollectTokenPosition(TokenPosition token_pos,
GrowableArray<intptr_t>* token_positions) {
if (!token_pos.IsReal()) {
return;
}
const intptr_t token_pos_value = token_pos.Serialize();
if (!token_positions->is_empty() &&
token_positions->Last() == token_pos_value) {
return;
}
token_positions->Add(token_pos_value);
}
static void CollectBytecodeFunctionTokenPositions(
Zone* zone,
const Function& function,
GrowableArray<intptr_t>* token_positions) {
ASSERT(function.is_declared_in_bytecode());
CollectTokenPosition(function.token_pos(), token_positions);
CollectTokenPosition(function.end_token_pos(), token_positions);
if (!function.HasBytecode()) {
return;
}
Bytecode& bytecode = Bytecode::Handle(zone, function.GetBytecode());
ASSERT(!bytecode.IsNull());
if (bytecode.HasSourcePositions()) {
BytecodeSourcePositionsIterator iter(zone, bytecode);
while (iter.MoveNext()) {
CollectTokenPosition(iter.TokenPos(), token_positions);
}
if (!function.IsNonImplicitClosureFunction()) {
// Find closure functions in the object pool.
const ObjectPool& pool = ObjectPool::Handle(zone, bytecode.object_pool());
Object& object = Object::Handle(zone);
for (intptr_t i = 0; i < pool.Length(); i++) {
ObjectPool::EntryType entry_type = pool.TypeAt(i);
if (entry_type != ObjectPool::EntryType::kTaggedObject) {
continue;
}
object = pool.ObjectAt(i);
if (object.IsFunction()) {
const auto& closure = Function::Cast(object);
if (closure.IsNonImplicitClosureFunction()) {
CollectBytecodeFunctionTokenPositions(zone, closure,
token_positions);
}
}
}
}
}
}
void BytecodeReader::CollectScriptTokenPositionsFromBytecode(
const Script& interesting_script,
GrowableArray<intptr_t>* token_positions) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const GrowableObjectArray& libs = GrowableObjectArray::Handle(
zone, thread->isolate_group()->object_store()->libraries());
auto& lib = Library::Handle(zone);
auto& cls = Class::Handle(zone);
auto& array = Array::Handle(zone);
auto& function = Function::Handle(zone);
auto& field = Field::Handle(zone);
for (intptr_t i = 0, n = libs.Length(); i < n; ++i) {
lib ^= libs.At(i);
cls = lib.toplevel_class();
if (!cls.is_declared_in_bytecode()) {
continue;
}
ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
while (it.HasNext()) {
cls = it.GetNextClass();
ASSERT(cls.is_declared_in_bytecode());
if (cls.script() == interesting_script.ptr()) {
CollectTokenPosition(cls.token_pos(), token_positions);
CollectTokenPosition(cls.end_token_pos(), token_positions);
}
array = cls.fields();
for (intptr_t i = 0, n = array.Length(); i < n; ++i) {
field ^= array.At(i);
if (field.Script() != interesting_script.ptr()) {
continue;
}
CollectTokenPosition(field.token_pos(), token_positions);
CollectTokenPosition(field.end_token_pos(), token_positions);
if ((field.is_static() || field.is_late()) &&
field.has_nontrivial_initializer()) {
function = field.EnsureInitializerFunction();
CollectBytecodeFunctionTokenPositions(zone, function,
token_positions);
}
}
array = cls.current_functions();
for (intptr_t i = 0, n = array.Length(); i < n; ++i) {
function ^= array.At(i);
if (function.script() != interesting_script.ptr()) {
continue;
}
CollectBytecodeFunctionTokenPositions(zone, function, token_positions);
}
}
}
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
const char* BytecodeLocalVariablesIterator::kKindNames[] = {
"Invalid",
"Scope",
"VariableDeclaration",
"ContextVariable",
};
LocalVarDescriptorsPtr BytecodeReader::ComputeLocalVarDescriptors(
Zone* zone,
const Function& function,
const Bytecode& bytecode) {
ASSERT(function.is_declared_in_bytecode());
ASSERT(function.HasBytecode());
ASSERT(!bytecode.IsNull());
ASSERT(function.GetBytecode() == bytecode.ptr());
LocalVarDescriptorsBuilder vars;
if (function.IsLocalFunction()) {
const auto& parent = Function::Handle(zone, function.parent_function());
ASSERT(parent.is_declared_in_bytecode() && parent.HasBytecode());
const auto& parent_bytecode = Bytecode::Handle(zone, parent.GetBytecode());
const auto& parent_vars = LocalVarDescriptors::Handle(
zone, parent_bytecode.GetLocalVarDescriptors());
for (intptr_t i = 0; i < parent_vars.Length(); ++i) {
UntaggedLocalVarDescriptors::VarInfo var_info;
parent_vars.GetInfo(i, &var_info);
// Include parent's context variable if variable's scope
// intersects with the local function range.
// It is not enough to check if local function is declared within the
// scope of variable, because in case of async functions closure has
// the same range as original function.
if (var_info.kind() == UntaggedLocalVarDescriptors::kContextVar &&
((var_info.begin_pos <= function.token_pos() &&
function.token_pos() <= var_info.end_pos) ||
(function.token_pos() <= var_info.begin_pos &&
var_info.begin_pos <= function.end_token_pos()))) {
vars.Add(LocalVarDescriptorsBuilder::VarDesc{
&String::Handle(zone, parent_vars.GetName(i)), var_info});
}
}
}
if (bytecode.HasLocalVariablesInfo()) {
intptr_t scope_id = 0;
intptr_t context_level = -1;
BytecodeLocalVariablesIterator local_vars(zone, bytecode);
while (local_vars.MoveNext()) {
switch (local_vars.Kind()) {
case BytecodeLocalVariablesIterator::kScope: {
++scope_id;
context_level = local_vars.ContextLevel();
} break;
case BytecodeLocalVariablesIterator::kVariableDeclaration: {
LocalVarDescriptorsBuilder::VarDesc desc;
desc.name = &String::Handle(zone, local_vars.Name());
if (local_vars.IsCaptured()) {
desc.info.set_kind(UntaggedLocalVarDescriptors::kContextVar);
desc.info.scope_id = context_level;
desc.info.set_index(local_vars.Index());
} else {
desc.info.set_kind(UntaggedLocalVarDescriptors::kStackVar);
desc.info.scope_id = scope_id;
if (local_vars.Index() < 0) {
// Parameter
ASSERT(local_vars.Index() < -kKBCParamEndSlotFromFp);
desc.info.set_index(-local_vars.Index() - kKBCParamEndSlotFromFp);
} else {
desc.info.set_index(-local_vars.Index());
}
}
desc.info.declaration_pos = local_vars.DeclarationTokenPos();
desc.info.begin_pos = local_vars.StartTokenPos();
desc.info.end_pos = local_vars.EndTokenPos();
vars.Add(desc);
} break;
case BytecodeLocalVariablesIterator::kContextVariable: {
ASSERT(local_vars.Index() >= 0);
const intptr_t context_variable_index = -local_vars.Index();
LocalVarDescriptorsBuilder::VarDesc desc;
desc.name = &Symbols::CurrentContextVar();
desc.info.set_kind(UntaggedLocalVarDescriptors::kSavedCurrentContext);
desc.info.scope_id = 0;
desc.info.declaration_pos = TokenPosition::kMinSource;
desc.info.begin_pos = TokenPosition::kMinSource;
desc.info.end_pos = TokenPosition::kMinSource;
desc.info.set_index(context_variable_index);
vars.Add(desc);
} break;
}
}
}
return vars.Done();
}
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
} // namespace bytecode
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)