Files
sdk/runtime/vm/compiler/frontend/bytecode_reader.cc
T
asiva 01ebf92dde [VM] Consume extension member/is late flag setting when reading kernel file.
Consume the extension flag setting for functions and the extension flag and is late
flags for fields when reading kernel file and set bit in Function/Field objects.

Change-Id: I661d30d53e817d968151d2b95474a6daf918de13
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118000
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
2019-10-07 22:43:19 +00:00

3651 lines
134 KiB
C++

// Copyright (c) 2018, 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/compiler/frontend/bytecode_reader.h"
#include "vm/bit_vector.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/code_descriptors.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/frontend/bytecode_scope_builder.h"
#include "vm/constants_kbc.h"
#include "vm/dart_api_impl.h" // For Api::IsFfiEnabled().
#include "vm/dart_entry.h"
#include "vm/debugger.h"
#include "vm/hash.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/reusable_handles.h"
#include "vm/scopes.h"
#include "vm/stack_frame_kbc.h"
#include "vm/timeline.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#define Z (zone_)
#define H (translation_helper_)
#define I (translation_helper_.isolate())
namespace dart {
DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode");
namespace kernel {
BytecodeMetadataHelper::BytecodeMetadataHelper(KernelReaderHelper* helper,
ActiveClass* active_class)
: MetadataHelper(helper, tag(), /* precompiler_only = */ false),
active_class_(active_class) {}
void BytecodeMetadataHelper::ParseBytecodeFunction(
ParsedFunction* parsed_function) {
TIMELINE_DURATION(Thread::Current(), CompilerVerbose,
"BytecodeMetadataHelper::ParseBytecodeFunction");
const Function& function = parsed_function->function();
ASSERT(function.is_declared_in_bytecode());
BytecodeComponentData bytecode_component(
&Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
bytecode_reader.ParseBytecodeFunction(parsed_function, function);
}
bool BytecodeMetadataHelper::ReadLibraries() {
TIMELINE_DURATION(Thread::Current(), Compiler,
"BytecodeMetadataHelper::ReadLibraries");
ASSERT(Thread::Current()->IsMutatorThread());
if (translation_helper_.GetBytecodeComponent() == Array::null()) {
return false;
}
BytecodeComponentData bytecode_component(
&Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
bytecode_component.GetLibraryIndexOffset());
bytecode_reader.ReadLibraryDeclarations(bytecode_component.GetNumLibraries());
return true;
}
void BytecodeMetadataHelper::ReadLibrary(const Library& library) {
TIMELINE_DURATION(Thread::Current(), Compiler,
"BytecodeMetadataHelper::ReadLibrary");
ASSERT(Thread::Current()->IsMutatorThread());
ASSERT(!library.Loaded());
if (translation_helper_.GetBytecodeComponent() == Array::null()) {
return;
}
BytecodeComponentData bytecode_component(
&Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
bytecode_component.GetLibraryIndexOffset());
bytecode_reader.FindAndReadSpecificLibrary(
library, bytecode_component.GetNumLibraries());
}
bool BytecodeMetadataHelper::FindModifiedLibrariesForHotReload(
BitVector* modified_libs,
bool* is_empty_program,
intptr_t* p_num_classes,
intptr_t* p_num_procedures) {
ASSERT(Thread::Current()->IsMutatorThread());
if (translation_helper_.GetBytecodeComponent() == Array::null()) {
return false;
}
BytecodeComponentData bytecode_component(
&Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
bytecode_component.GetLibraryIndexOffset());
bytecode_reader.FindModifiedLibrariesForHotReload(
modified_libs, bytecode_component.GetNumLibraries());
if (is_empty_program != nullptr) {
*is_empty_program = (bytecode_component.GetNumLibraries() == 0);
}
if (p_num_classes != nullptr) {
*p_num_classes = bytecode_component.GetNumClasses();
}
if (p_num_procedures != nullptr) {
*p_num_procedures = bytecode_component.GetNumCodes();
}
return true;
}
RawLibrary* BytecodeMetadataHelper::GetMainLibrary() {
const intptr_t md_offset = GetComponentMetadataPayloadOffset();
if (md_offset < 0) {
return Library::null();
}
BytecodeComponentData bytecode_component(
&Array::Handle(helper_->zone_, GetBytecodeComponent()));
const intptr_t main_offset = bytecode_component.GetMainOffset();
if (main_offset == 0) {
return Library::null();
}
BytecodeReaderHelper bytecode_reader(&H, active_class_, &bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(), main_offset);
return bytecode_reader.ReadMain();
}
RawArray* BytecodeMetadataHelper::GetBytecodeComponent() {
RawArray* array = translation_helper_.GetBytecodeComponent();
if (array == Array::null()) {
array = ReadBytecodeComponent();
ASSERT(array != Array::null());
}
return array;
}
RawArray* BytecodeMetadataHelper::ReadBytecodeComponent() {
const intptr_t md_offset = GetComponentMetadataPayloadOffset();
if (md_offset < 0) {
return Array::null();
}
BytecodeReaderHelper component_reader(&H, nullptr, nullptr);
return component_reader.ReadBytecodeComponent(md_offset);
}
BytecodeReaderHelper::BytecodeReaderHelper(
TranslationHelper* translation_helper,
ActiveClass* active_class,
BytecodeComponentData* bytecode_component)
: reader_(translation_helper->metadata_payloads()),
translation_helper_(*translation_helper),
active_class_(active_class),
thread_(translation_helper->thread()),
zone_(translation_helper->zone()),
bytecode_component_(bytecode_component),
scoped_function_(Function::Handle(translation_helper->zone())),
scoped_function_name_(String::Handle(translation_helper->zone())),
scoped_function_class_(Class::Handle(translation_helper->zone())) {}
void BytecodeReaderHelper::ReadCode(const Function& function,
intptr_t code_offset) {
ASSERT(Thread::Current()->IsMutatorThread());
ASSERT(!function.IsImplicitGetterFunction() &&
!function.IsImplicitSetterFunction());
if (code_offset == 0) {
FATAL2("Function %s (kind %s) doesn't have bytecode",
function.ToFullyQualifiedCString(),
Function::KindToCString(function.kind()));
}
AlternativeReadingScope alt(&reader_, code_offset);
// This scope is needed to set active_class_->enclosing_ which is used to
// assign parent function for function types.
ActiveEnclosingFunctionScope active_enclosing_function(active_class_,
&function);
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_parameters_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;
if (has_parameters_flags) {
intptr_t num_params = reader_.ReadUInt();
ASSERT(num_params ==
function.NumParameters() - function.NumImplicitParameters());
for (intptr_t i = 0; i < num_params; ++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));
function.AttachBytecode(bytecode);
ASSERT(bytecode.GetBinary(Z) == reader_.typed_data()->raw());
ReadExceptionsTable(bytecode, has_exceptions_table);
ReadSourcePositions(bytecode, has_source_positions);
ReadLocalVariables(bytecode, has_local_variables);
if (FLAG_dump_kernel_bytecode) {
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 (I->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;
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
ASSERT(bytecode.GetBinary(Z) == reader_.typed_data()->raw());
ReadExceptionsTable(closure_bytecode, has_exceptions_table);
ReadSourcePositions(closure_bytecode, has_source_positions);
ReadLocalVariables(closure_bytecode, has_local_variables);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
#if !defined(PRODUCT)
thread_->isolate()->debugger()->NotifyBytecodeLoaded(closure);
#endif
}
}
#if !defined(PRODUCT)
thread_->isolate()->debugger()->NotifyBytecodeLoaded(function);
#endif
}
static intptr_t IndexFor(Zone* zone,
const Function& function,
const String& name) {
const Bytecode& bc = Bytecode::Handle(zone, function.bytecode());
const ObjectPool& pool = ObjectPool::Handle(zone, bc.object_pool());
const KBCInstr* pc = reinterpret_cast<const KBCInstr*>(bc.PayloadStart());
ASSERT(KernelBytecode::IsEntryOptionalOpcode(pc));
ASSERT(KernelBytecode::DecodeB(pc) ==
function.NumOptionalPositionalParameters());
ASSERT(KernelBytecode::DecodeC(pc) == function.NumOptionalNamedParameters());
pc = KernelBytecode::Next(pc);
const intptr_t num_opt_params = function.NumOptionalParameters();
const intptr_t num_fixed_params = function.num_fixed_parameters();
for (intptr_t i = 0; i < num_opt_params; i++) {
const KBCInstr* load_name = pc;
const KBCInstr* load_value = KernelBytecode::Next(load_name);
pc = KernelBytecode::Next(load_value);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
if (pool.ObjectAt(KernelBytecode::DecodeE(load_name)) == name.raw()) {
return num_fixed_params + i;
}
}
UNREACHABLE();
return -1;
}
RawArray* BytecodeReaderHelper::CreateForwarderChecks(
const Function& function) {
ASSERT(function.kind() != RawFunction::kDynamicInvocationForwarder);
ASSERT(function.is_declared_in_bytecode());
TypeArguments& default_args = TypeArguments::Handle(Z);
if (function.bytecode_offset() != 0) {
AlternativeReadingScope alt(&reader_, function.bytecode_offset());
const intptr_t flags = reader_.ReadUInt();
const bool has_parameters_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;
if (has_parameters_flags) {
intptr_t num_params = reader_.ReadUInt();
ASSERT(num_params ==
function.NumParameters() - function.NumImplicitParameters());
for (intptr_t i = 0; i < num_params; ++i) {
reader_.ReadUInt();
}
}
if (has_forwarding_stub_target) {
reader_.ReadUInt();
}
if (has_default_function_type_args) {
const intptr_t index = reader_.ReadUInt();
const Bytecode& code = Bytecode::Handle(Z, function.bytecode());
const ObjectPool& pool = ObjectPool::Handle(Z, code.object_pool());
default_args ^= pool.ObjectAt(index);
}
}
auto& name = String::Handle(Z);
auto& check = ParameterTypeCheck::Handle(Z);
auto& checks = GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
checks.Add(function);
checks.Add(default_args);
const auto& type_params =
TypeArguments::Handle(Z, function.type_parameters());
if (!type_params.IsNull()) {
auto& type_param = TypeParameter::Handle(Z);
auto& bound = AbstractType::Handle(Z);
for (intptr_t i = 0, n = type_params.Length(); i < n; ++i) {
type_param ^= type_params.TypeAt(i);
bound = type_param.bound();
if (!bound.IsTopType() && !type_param.IsGenericCovariantImpl()) {
name = type_param.name();
ASSERT(type_param.IsFinalized());
check = ParameterTypeCheck::New();
check.set_param(type_param);
check.set_type_or_bound(bound);
check.set_name(name);
checks.Add(check);
}
}
}
const intptr_t num_params = function.NumParameters();
const intptr_t num_pos_params = function.HasOptionalNamedParameters()
? function.num_fixed_parameters()
: num_params;
BitVector is_covariant(Z, num_params);
BitVector is_generic_covariant_impl(Z, num_params);
ReadParameterCovariance(function, &is_covariant, &is_generic_covariant_impl);
auto& type = AbstractType::Handle(Z);
auto& cache = SubtypeTestCache::Handle(Z);
const bool has_optional_parameters = function.HasOptionalParameters();
for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
type = function.ParameterTypeAt(i);
if (!type.IsTopType() && !is_generic_covariant_impl.Contains(i) &&
!is_covariant.Contains(i)) {
name = function.ParameterNameAt(i);
intptr_t index;
if (i >= num_pos_params) {
// Named parameter.
index = IndexFor(Z, function, name);
} else if (has_optional_parameters) {
// Fixed or optional parameter.
index = i;
} else {
// Fixed parameter.
index = -kKBCParamEndSlotFromFp - num_params + i;
}
check = ParameterTypeCheck::New();
check.set_index(index);
check.set_type_or_bound(type);
check.set_name(name);
cache = SubtypeTestCache::New();
check.set_cache(cache);
checks.Add(check);
}
}
return Array::MakeFixedLength(checks);
}
void BytecodeReaderHelper::ReadClosureDeclaration(const Function& function,
intptr_t closureIndex) {
// Closure flags, must be in sync with ClosureDeclaration constants in
// pkg/vm/lib/bytecode/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 intptr_t flags = reader_.ReadUInt();
Object& parent = Object::Handle(Z, ReadObject());
if (!parent.IsFunction()) {
ASSERT(parent.IsField());
ASSERT(function.kind() == RawFunction::kFieldInitializer);
// Closure in a static field initializer, so use current function as parent.
parent = function.raw();
}
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));
closure.set_is_declared_in_bytecode(true);
closure.set_end_token_pos(end_position);
if ((flags & kIsSyncStarFlag) != 0) {
closure.set_modifier(RawFunction::kSyncGen);
} else if ((flags & kIsAsyncFlag) != 0) {
closure.set_modifier(RawFunction::kAsync);
closure.set_is_inlinable(!FLAG_causal_async_stacks);
} else if ((flags & kIsAsyncStarFlag) != 0) {
closure.set_modifier(RawFunction::kAsyncGen);
closure.set_is_inlinable(!FLAG_causal_async_stacks);
}
if (Function::Cast(parent).IsAsyncOrGenerator()) {
closure.set_is_generated_body(true);
}
closure.set_is_debuggable((flags & kIsDebuggableFlag) != 0);
closures_->SetAt(closureIndex, closure);
Type& signature_type = Type::Handle(
Z, ReadFunctionSignature(closure,
(flags & kHasOptionalPositionalParamsFlag) != 0,
(flags & kHasOptionalNamedParamsFlag) != 0,
(flags & kHasTypeParamsFlag) != 0,
/* has_positional_param_names = */ true));
closure.SetSignatureType(signature_type);
I->AddClosureFunction(closure);
}
static bool IsNonCanonical(const AbstractType& type) {
return type.IsTypeRef() || (type.IsType() && !type.IsCanonical());
}
static bool HasNonCanonicalTypes(Zone* zone, const Function& func) {
auto& type = AbstractType::Handle(zone);
for (intptr_t i = 0; i < func.NumParameters(); ++i) {
type = func.ParameterTypeAt(i);
if (IsNonCanonical(type)) {
return true;
}
}
type = func.result_type();
if (IsNonCanonical(type)) {
return true;
}
const auto& type_params = TypeArguments::Handle(zone, func.type_parameters());
if (!type_params.IsNull()) {
for (intptr_t i = 0; i < type_params.Length(); ++i) {
type = type_params.TypeAt(i);
type = TypeParameter::Cast(type).bound();
if (IsNonCanonical(type)) {
return true;
}
}
}
return false;
}
RawType* BytecodeReaderHelper::ReadFunctionSignature(
const Function& func,
bool has_optional_positional_params,
bool has_optional_named_params,
bool has_type_params,
bool has_positional_param_names) {
FunctionTypeScope function_type_scope(this);
if (has_type_params) {
ReadTypeParametersDeclaration(Class::Handle(Z), func);
}
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();
}
func.set_num_fixed_parameters(num_required_params);
func.SetNumOptionalParameters(num_params - num_required_params,
!has_optional_named_params);
const Array& parameter_types =
Array::Handle(Z, Array::New(num_params, Heap::kOld));
func.set_parameter_types(parameter_types);
const Array& parameter_names =
Array::Handle(Z, Array::New(num_params, Heap::kOld));
func.set_parameter_names(parameter_names);
intptr_t i = 0;
parameter_types.SetAt(i, AbstractType::dynamic_type());
parameter_names.SetAt(i, Symbols::ClosureParameter());
++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();
} else {
name = Symbols::NotNamed().raw();
}
parameter_names.SetAt(i, name);
type ^= ReadObject();
parameter_types.SetAt(i, type);
}
type ^= ReadObject();
func.set_result_type(type);
// Finalize function type.
type = func.SignatureType();
ClassFinalizer::FinalizationKind finalization = ClassFinalizer::kCanonicalize;
if (pending_recursive_types_ != nullptr && HasNonCanonicalTypes(Z, func)) {
// This function type is a part of recursive type. Avoid canonicalization
// as not all TypeRef objects are filled up at this point.
finalization = ClassFinalizer::kFinalize;
}
type =
ClassFinalizer::FinalizeType(*(active_class_->klass), type, finalization);
return Type::Cast(type).raw();
}
void BytecodeReaderHelper::ReadTypeParametersDeclaration(
const Class& parameterized_class,
const Function& parameterized_function) {
ASSERT(parameterized_class.IsNull() != parameterized_function.IsNull());
const intptr_t num_type_params = reader_.ReadUInt();
ASSERT(num_type_params > 0);
intptr_t offset;
if (!parameterized_class.IsNull()) {
offset = parameterized_class.NumTypeArguments() - num_type_params;
} else {
offset = parameterized_function.NumParentTypeParameters();
}
// First setup the type parameters, so if any of the following code uses it
// (in a recursive way) we're fine.
//
// Step a) Create array of [TypeParameter] objects (without bound).
const TypeArguments& type_parameters =
TypeArguments::Handle(Z, TypeArguments::New(num_type_params));
String& name = String::Handle(Z);
TypeParameter& parameter = TypeParameter::Handle(Z);
AbstractType& bound = AbstractType::Handle(Z);
for (intptr_t i = 0; i < num_type_params; ++i) {
name ^= ReadObject();
ASSERT(name.IsSymbol());
parameter = TypeParameter::New(
parameterized_class, parameterized_function, i, name, bound,
/* is_generic_covariant_impl = */ false, TokenPosition::kNoSource);
parameter.set_index(offset + i);
parameter.SetIsFinalized();
type_parameters.SetTypeAt(i, parameter);
}
if (!parameterized_class.IsNull()) {
parameterized_class.set_type_parameters(type_parameters);
} else if (!parameterized_function.IsFactory()) {
// Do not set type parameters for factories, as VM uses class type
// parameters instead.
parameterized_function.set_type_parameters(type_parameters);
if (parameterized_function.IsSignatureFunction()) {
if (function_type_type_parameters_ == nullptr) {
function_type_type_parameters_ = &type_parameters;
} else {
function_type_type_parameters_ = &TypeArguments::Handle(
Z, function_type_type_parameters_->ConcatenateTypeParameters(
Z, type_parameters));
}
} else {
ASSERT(function_type_type_parameters_ == nullptr);
}
}
// Step b) Fill in the bounds of all [TypeParameter]s.
for (intptr_t i = 0; i < num_type_params; ++i) {
parameter ^= type_parameters.TypeAt(i);
bound ^= ReadObject();
// Convert dynamic to Object in bounds of type parameters so
// they are equivalent when doing subtype checks for function types.
// TODO(https://github.com/dart-lang/language/issues/495): revise this
// when function subtyping is fixed.
if (bound.IsDynamicType()) {
bound = I->object_store()->object_type();
}
parameter.set_bound(bound);
}
}
intptr_t BytecodeReaderHelper::ReadConstantPool(const Function& function,
const ObjectPool& pool,
intptr_t start_index) {
TIMELINE_DURATION(Thread::Current(), CompilerVerbose,
"BytecodeReaderHelper::ReadConstantPool");
// These enums and the code below reading the constant pool from kernel must
// be kept in sync with pkg/vm/lib/bytecode/constant_pool.dart.
enum ConstantPoolTag {
kInvalid,
kUnused1,
kUnused2,
kUnused3,
kUnused4,
kUnused5,
kUnused6,
kICData, // Obsolete in bytecode v20.
kUnused7,
kStaticField,
kInstanceField,
kClass,
kTypeArgumentsField,
kUnused8,
kType,
kUnused9,
kUnused10,
kUnused11,
kUnused12,
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
kUnused13,
kEmptyTypeArguments,
kUnused14,
kUnused15,
kObjectRef,
kDirectCall,
kInterfaceCall,
kInstantiatedInterfaceCall,
kDynamicCall,
kDirectCallViaDynamicForwarder,
};
enum InvocationKind {
method, // x.foo(...) or foo(...)
getter, // x.foo
setter // x.foo = ...
};
const int kInvocationKindMask = 0x3;
const int kFlagDynamic = 1 << 2;
Object& obj = Object::Handle(Z);
Object& elem = Object::Handle(Z);
Array& array = Array::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_.ReadTag();
switch (tag) {
case ConstantPoolTag::kInvalid:
UNREACHABLE();
case ConstantPoolTag::kICData: {
static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 20,
"Cleanup ICData constant pool entry");
intptr_t flags = reader_.ReadByte();
InvocationKind kind =
static_cast<InvocationKind>(flags & kInvocationKindMask);
bool isDynamic = (flags & kFlagDynamic) != 0;
name ^= ReadObject();
ASSERT(name.IsSymbol());
intptr_t arg_desc_index = reader_.ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
// Do not mangle == or call:
// * operator == takes an Object so its either not checked or checked
// at the entry because the parameter is marked covariant, neither
// of those cases require a dynamic invocation forwarder;
// * we assume that all closures are entered in a checked way.
if (isDynamic && (kind != InvocationKind::getter) &&
I->should_emit_strong_mode_checks() &&
(name.raw() != Symbols::EqualOperator().raw()) &&
(name.raw() != Symbols::Call().raw())) {
name = Function::CreateDynamicInvocationForwarderName(name);
}
obj = UnlinkedCall::New();
UnlinkedCall::Cast(obj).set_target_name(name);
UnlinkedCall::Cast(obj).set_args_descriptor(array);
} break;
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.Offset() / kWordSize);
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// The second entry is used for field object.
obj = field.raw();
break;
case ConstantPoolTag::kClass:
obj = ReadObject();
ASSERT(obj.IsClass());
break;
case ConstantPoolTag::kTypeArgumentsField:
cls ^= ReadObject();
obj = Smi::New(cls.type_arguments_field_offset() / kWordSize);
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);
pool.SetObjectAt(i, obj);
// This scope is needed to set active_class_->enclosing_ which is used
// to assign parent function for function types.
ActiveEnclosingFunctionScope active_enclosing_function(
active_class_, &Function::Cast(obj));
// 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);
pool.SetObjectAt(i, obj);
return i;
}
case ConstantPoolTag::kNativeEntry: {
name = ReadString();
obj = NativeEntry(function, name);
pool.SetTypeAt(i, ObjectPool::EntryType::kNativeEntryData,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
continue;
}
case ConstantPoolTag::kSubtypeTestCache: {
obj = SubtypeTestCache::New();
} break;
case ConstantPoolTag::kEmptyTypeArguments:
obj = Object::empty_type_arguments().raw();
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);
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);
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);
pool.SetObjectAt(i, elem);
++i;
ASSERT(i < obj_count);
// 2) Arguments descriptor.
obj = ReadObject();
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// 3) Static receiver type.
obj = ReadObject();
} break;
case ConstantPoolTag::kDynamicCall: {
name ^= ReadObject();
ASSERT(name.IsSymbol());
array ^= ReadObject();
// Do not mangle == or call:
// * 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;
// * we assume that all closures are entered in a checked way.
if (!Field::IsGetterName(name) && I->should_emit_strong_mode_checks() &&
(name.raw() != Symbols::EqualOperator().raw()) &&
(name.raw() != Symbols::Call().raw())) {
name = Function::CreateDynamicInvocationForwarderName(name);
}
static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 20,
"Can use 2 slots in object pool");
// DynamicCall constant occupies 2 entries: selector and arguments
// descriptor. For backwards compatibility with ICData constants
// selector and arguments descriptor are packaged into UnlinkedCall
// object. The 2nd slot is filled with null.
obj = UnlinkedCall::New();
UnlinkedCall::Cast(obj).set_target_name(name);
UnlinkedCall::Cast(obj).set_args_descriptor(array);
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
obj = Object::null();
} break;
case ConstantPoolTag::kDirectCallViaDynamicForwarder: {
// DirectCallViaDynamicForwarder constant occupies 2 entries.
// The first entry is used for target function.
obj = ReadObject();
ASSERT(obj.IsFunction());
name = Function::Cast(obj).name();
name = Function::CreateDynamicInvocationForwarderName(name);
obj = Function::Cast(obj).GetDynamicInvocationForwarder(name);
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
++i;
ASSERT(i < obj_count);
// The second entry is used for arguments descriptor.
obj = ReadObject();
} break;
default:
UNREACHABLE();
}
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
}
return obj_count - 1;
}
RawBytecode* BytecodeReaderHelper::ReadBytecode(const ObjectPool& pool) {
#if defined(SUPPORT_TIMELINE)
TIMELINE_DURATION(Thread::Current(), CompilerVerbose,
"BytecodeReaderHelper::ReadBytecode");
#endif // defined(SUPPORT_TIMELINE)
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, pool);
}
void BytecodeReaderHelper::ReadExceptionsTable(const Bytecode& bytecode,
bool has_exceptions_table) {
#if defined(SUPPORT_TIMELINE)
TIMELINE_DURATION(Thread::Current(), CompilerVerbose,
"BytecodeReaderHelper::ReadExceptionsTable");
#endif
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(64);
ExceptionHandlerList* exception_handlers_list =
new (Z) ExceptionHandlerList();
// Encoding of ExceptionsTable is described in
// pkg/vm/lib/bytecode/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(RawPcDescriptors::kOther, start_pc,
DeoptId::kNone,
TokenPosition::kNoSource, try_index);
pc_descriptors_list->AddDescriptor(RawPcDescriptors::kOther, end_pc,
DeoptId::kNone,
TokenPosition::kNoSource, try_index);
// 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.raw()), 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();
bytecode.set_local_variables_binary_offset(
bytecode_component_->GetLocalVariablesOffset() + offset);
}
RawTypedData* BytecodeReaderHelper::NativeEntry(const Function& function,
const String& external_name) {
MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function);
// This list of recognized methods must be kept in sync with the list of
// methods handled specially by the NativeCall bytecode in the interpreter.
switch (kind) {
case MethodRecognizer::kObjectEquals:
case MethodRecognizer::kStringBaseLength:
case MethodRecognizer::kStringBaseIsEmpty:
case MethodRecognizer::kGrowableArrayLength:
case MethodRecognizer::kObjectArrayLength:
case MethodRecognizer::kImmutableArrayLength:
case MethodRecognizer::kTypedListLength:
case MethodRecognizer::kTypedListViewLength:
case MethodRecognizer::kByteDataViewLength:
case MethodRecognizer::kByteDataViewOffsetInBytes:
case MethodRecognizer::kTypedDataViewOffsetInBytes:
case MethodRecognizer::kByteDataViewTypedData:
case MethodRecognizer::kTypedDataViewTypedData:
case MethodRecognizer::kClassIDgetID:
case MethodRecognizer::kGrowableArrayCapacity:
case MethodRecognizer::kListFactory:
case MethodRecognizer::kObjectArrayAllocate:
case MethodRecognizer::kLinkedHashMap_getIndex:
case MethodRecognizer::kLinkedHashMap_setIndex:
case MethodRecognizer::kLinkedHashMap_getData:
case MethodRecognizer::kLinkedHashMap_setData:
case MethodRecognizer::kLinkedHashMap_getHashMask:
case MethodRecognizer::kLinkedHashMap_setHashMask:
case MethodRecognizer::kLinkedHashMap_getUsedData:
case MethodRecognizer::kLinkedHashMap_setUsedData:
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
case MethodRecognizer::kFfiAbi:
break;
case MethodRecognizer::kAsyncStackTraceHelper:
// If causal async stacks are disabled the interpreter.cc will handle this
// native call specially.
if (!FLAG_causal_async_stacks) {
break;
}
FALL_THROUGH;
default:
kind = MethodRecognizer::kUnknown;
}
NativeFunctionWrapper trampoline = NULL;
NativeFunction native_function = NULL;
intptr_t argc_tag = 0;
if (kind == MethodRecognizer::kUnknown) {
if (!FLAG_link_natives_lazily) {
const Class& cls = Class::Handle(Z, function.Owner());
const Library& library = Library::Handle(Z, cls.library());
Dart_NativeEntryResolver resolver = library.native_entry_resolver();
const bool is_bootstrap_native = Bootstrap::IsBootstrapResolver(resolver);
const int num_params =
NativeArguments::ParameterCountForResolution(function);
bool is_auto_scope = true;
native_function = NativeEntry::ResolveNative(library, external_name,
num_params, &is_auto_scope);
if (native_function == nullptr) {
Report::MessageF(Report::kError, Script::Handle(function.script()),
function.token_pos(), Report::AtLocation,
"native function '%s' (%" Pd
" arguments) cannot be found",
external_name.ToCString(), function.NumParameters());
}
if (is_bootstrap_native) {
trampoline = &NativeEntry::BootstrapNativeCallWrapper;
} else if (is_auto_scope) {
trampoline = &NativeEntry::AutoScopeNativeCallWrapper;
} else {
trampoline = &NativeEntry::NoScopeNativeCallWrapper;
}
}
argc_tag = NativeArguments::ComputeArgcTag(function);
}
return NativeEntryData::New(kind, trampoline, native_function, argc_tag);
}
RawArray* BytecodeReaderHelper::ReadBytecodeComponent(intptr_t md_offset) {
ASSERT(Thread::Current()->IsMutatorThread());
AlternativeReadingScope alt(&reader_, md_offset);
const intptr_t start_offset = reader_.offset();
intptr_t magic = reader_.ReadUInt32();
if (magic != KernelBytecode::kMagicValue) {
FATAL1("Unexpected Dart bytecode magic %" Px, magic);
}
const intptr_t version = reader_.ReadUInt32();
if ((version < KernelBytecode::kMinSupportedBytecodeFormatVersion) ||
(version > KernelBytecode::kMaxSupportedBytecodeFormatVersion)) {
FATAL3("Unsupported Dart bytecode format version %" Pd
". "
"This version of Dart VM supports bytecode format versions from %" Pd
" to %" Pd ".",
version, KernelBytecode::kMinSupportedBytecodeFormatVersion,
KernelBytecode::kMaxSupportedBytecodeFormatVersion);
}
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 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, 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, 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);
}
H.SetBytecodeComponent(bytecode_component_array);
return bytecode_component_array.raw();
}
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);
}
}
RawObject* BytecodeReaderHelper::ReadObject() {
uint32_t header = reader_.ReadUInt();
if ((header & kReferenceBit) != 0) {
intptr_t index = header >> kIndexShift;
if (index == 0) {
return Object::null();
}
RawObject* 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);
}
RawString* 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);
}
RawObject* BytecodeReaderHelper::ReadObjectContents(uint32_t header) {
ASSERT(((header & kReferenceBit) == 0));
// Must be in sync with enum ObjectKind in
// pkg/vm/lib/bytecode/object_table.dart.
enum ObjectKind {
kInvalid,
kLibrary,
kClass,
kMember,
kClosure,
kUnused1,
kUnused2,
kUnused3,
kUnused4,
kName,
kTypeArguments,
kUnused5,
kConstObject,
kArgDesc,
kScript,
kType,
};
// Member flags, must be in sync with _MemberHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const intptr_t kFlagIsField = kFlagBit0;
const intptr_t kFlagIsConstructor = kFlagBit1;
// ArgDesc flags, must be in sync with _ArgDescHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const int kFlagHasNamedArgs = kFlagBit0;
const int kFlagHasTypeArgs = kFlagBit1;
// Script flags, must be in sync with _ScriptHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const int kFlagHasSourceFile = 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());
RawLibrary* library = Library::LookupLibrary(thread_, uri);
if (library == Library::null()) {
// We do not register expression evaluation libraries 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.raw() == Symbols::EvalSourceUri().raw()) {
ASSERT(expression_evaluation_library_ != nullptr);
return expression_evaluation_library_->raw();
}
#if !defined(PRODUCT)
ASSERT(Isolate::Current()->HasAttemptedReload());
const String& msg = String::Handle(
Z,
String::NewFormatted("Unable to find library %s", uri.ToCString()));
Report::LongJump(LanguageError::Handle(Z, LanguageError::New(msg)));
#else
FATAL1("Unable to find library %s", uri.ToCString());
#endif
}
return library;
}
case kClass: {
const Library& library = Library::CheckedHandle(Z, ReadObject());
const String& class_name = String::CheckedHandle(Z, ReadObject());
if (class_name.raw() == Symbols::Empty().raw()) {
NoSafepointScope no_safepoint_scope(thread_);
RawClass* cls = library.toplevel_class();
if (cls == Class::null()) {
FATAL1("Unable to find toplevel class %s", library.ToCString());
}
return cls;
}
RawClass* cls = library.LookupLocalClass(class_name);
if (cls == Class::null()) {
if (IsExpressionEvaluationLibrary(library)) {
return H.GetExpressionEvaluationRealClass();
}
#if !defined(PRODUCT)
ASSERT(Isolate::Current()->HasAttemptedReload());
const String& msg = String::Handle(
Z,
String::NewFormatted("Unable to find class %s in %s",
class_name.ToCString(), library.ToCString()));
Report::LongJump(LanguageError::Handle(Z, LanguageError::New(msg)));
#else
FATAL2("Unable to find class %s in %s", class_name.ToCString(),
library.ToCString());
#endif
}
return cls;
}
case kMember: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
String& name = String::CheckedHandle(Z, ReadObject());
if ((flags & kFlagIsField) != 0) {
RawField* field = cls.LookupField(name);
if (field == Field::null()) {
#if !defined(PRODUCT)
ASSERT(Isolate::Current()->HasAttemptedReload());
const String& msg = String::Handle(
Z, String::NewFormatted("Unable to find field %s in %s",
name.ToCString(), cls.ToCString()));
Report::LongJump(LanguageError::Handle(Z, LanguageError::New(msg)));
#else
FATAL2("Unable to find field %s in %s", name.ToCString(),
cls.ToCString());
#endif
}
return field;
} else {
if ((flags & kFlagIsConstructor) != 0) {
name = ConstructorName(cls, name);
}
ASSERT(!name.IsNull() && name.IsSymbol());
if (name.raw() == scoped_function_name_.raw() &&
cls.raw() == scoped_function_class_.raw()) {
return scoped_function_.raw();
}
RawFunction* function = cls.LookupFunction(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 = cls.LookupFunction(method_name);
if (function != Function::null()) {
function =
Function::Handle(Z, function).CreateMethodExtractor(name);
if (function != Function::null()) {
return function;
}
}
}
#if !defined(PRODUCT)
ASSERT(Isolate::Current()->HasAttemptedReload());
const String& msg = String::Handle(
Z, String::NewFormatted("Unable to find function %s in %s",
name.ToCString(), cls.ToCString()));
Report::LongJump(LanguageError::Handle(Z, LanguageError::New(msg)));
#else
FATAL2("Unable to find function %s in %s", name.ToCString(),
cls.ToCString());
#endif
}
return function;
}
}
case kClosure: {
ReadObject(); // Skip enclosing member.
const intptr_t closure_index = reader_.ReadUInt();
return closures_->At(closure_index);
}
case kName: {
const Library& library = Library::CheckedHandle(Z, ReadObject());
if (library.IsNull()) {
return ReadString();
} else {
const String& name =
String::Handle(Z, ReadString(/* is_canonical = */ false));
return library.PrivateName(name);
}
}
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::New(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::New(num_type_args, num_arguments, array);
}
}
case kScript: {
const String& uri = String::CheckedHandle(Z, ReadObject());
Script& script = Script::Handle(Z);
if ((flags & kFlagHasSourceFile) != 0) {
// TODO(alexmarkov): read source and line starts only when needed.
script =
ReadSourceFile(uri, bytecode_component_->GetSourceFilesOffset() +
reader_.ReadUInt());
} else {
script = Script::New(uri, Object::null_string(), RawScript::kKernelTag);
}
script.set_kernel_program_info(H.GetKernelProgramInfo());
return script.raw();
}
case kType: {
const intptr_t tag = flags / kFlagBit0;
return ReadType(tag);
}
default:
UNREACHABLE();
}
return Object::null();
}
RawObject* BytecodeReaderHelper::ReadConstObject(intptr_t tag) {
// Must be in sync with enum ConstTag in
// pkg/vm/lib/bytecode/object_table.dart.
enum ConstTag {
kInvalid,
kInstance,
kInt,
kDouble,
kList,
kTearOff,
kBool,
kSymbol,
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 (type.arguments() != TypeArguments::null()) {
const TypeArguments& type_args =
TypeArguments::Handle(Z, type.arguments());
obj.SetTypeArguments(type_args);
}
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 H.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 H.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 H.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 H.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 H.Canonicalize(Instance::Cast(obj));
}
case kBool: {
bool is_true = reader_.ReadByte() != 0;
return is_true ? Bool::True().raw() : Bool::False().raw();
}
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 H.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()),
Context::Handle(Z, closure.context()), Heap::kOld);
return H.Canonicalize(closure);
}
default:
UNREACHABLE();
}
return Object::null();
}
RawObject* BytecodeReaderHelper::ReadType(intptr_t tag) {
// Must be in sync with enum TypeTag in
// pkg/vm/lib/bytecode/object_table.dart.
enum TypeTag {
kInvalid,
kDynamic,
kVoid,
kSimpleType,
kTypeParameter,
kGenericType,
kRecursiveGenericType,
kRecursiveTypeRef,
kFunctionType,
};
// FunctionType flags, must be in sync with _FunctionTypeHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const int kFlagHasOptionalPositionalParams = 1 << 0;
const int kFlagHasOptionalNamedParams = 1 << 1;
const int kFlagHasTypeParams = 1 << 2;
switch (tag) {
case kInvalid:
UNREACHABLE();
break;
case kDynamic:
return AbstractType::dynamic_type().raw();
case kVoid:
return AbstractType::void_type().raw();
case kSimpleType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if (!cls.is_declaration_loaded()) {
LoadReferencedClass(cls);
}
return cls.DeclarationType();
}
case kTypeParameter: {
Object& parent = Object::Handle(Z, ReadObject());
const intptr_t index_in_parent = reader_.ReadUInt();
TypeArguments& type_parameters = TypeArguments::Handle(Z);
if (parent.IsClass()) {
type_parameters = Class::Cast(parent).type_parameters();
} else if (parent.IsFunction()) {
if (Function::Cast(parent).IsFactory()) {
// For factory constructors VM uses type parameters of a class
// instead of constructor's type parameters.
parent = Function::Cast(parent).Owner();
type_parameters = Class::Cast(parent).type_parameters();
} else {
type_parameters = Function::Cast(parent).type_parameters();
}
} else if (parent.IsNull()) {
ASSERT(function_type_type_parameters_ != nullptr);
type_parameters = function_type_type_parameters_->raw();
} else {
UNREACHABLE();
}
return type_parameters.TypeAt(index_in_parent);
}
case kGenericType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if (!cls.is_declaration_loaded()) {
LoadReferencedClass(cls);
}
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(Z, ReadObject());
const Type& type = Type::Handle(
Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
type.SetIsFinalized();
return type.Canonicalize();
}
case kRecursiveGenericType: {
const intptr_t id = reader_.ReadUInt();
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if (!cls.is_declaration_loaded()) {
LoadReferencedClass(cls);
}
const auto saved_pending_recursive_types = pending_recursive_types_;
if (id == 0) {
pending_recursive_types_ = &GrowableObjectArray::Handle(
Z, GrowableObjectArray::New(Heap::kOld));
}
ASSERT(id == pending_recursive_types_->Length());
const auto& type_ref =
TypeRef::Handle(Z, TypeRef::New(AbstractType::null_abstract_type()));
pending_recursive_types_->Add(type_ref);
reading_type_arguments_of_recursive_type_ = true;
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(Z, ReadObject());
reading_type_arguments_of_recursive_type_ = false;
ASSERT(id == pending_recursive_types_->Length() - 1);
ASSERT(pending_recursive_types_->At(id) == type_ref.raw());
pending_recursive_types_->SetLength(id);
pending_recursive_types_ = saved_pending_recursive_types;
Type& type = Type::Handle(
Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
type_ref.set_type(type);
type.SetIsFinalized();
if (id != 0) {
// Do not canonicalize non-root recursive types
// as not all TypeRef objects are filled up at this point.
return type.raw();
}
return type.Canonicalize();
}
case kRecursiveTypeRef: {
const intptr_t id = reader_.ReadUInt();
ASSERT(pending_recursive_types_ != nullptr);
ASSERT(pending_recursive_types_->Length() >= id);
return pending_recursive_types_->At(id);
}
case kFunctionType: {
const intptr_t flags = reader_.ReadUInt();
Function& signature_function = Function::ZoneHandle(
Z, Function::NewSignatureFunction(*active_class_->klass,
active_class_->enclosing != NULL
? *active_class_->enclosing
: Function::null_function(),
TokenPosition::kNoSource));
// This scope is needed to set active_class_->enclosing_ which is used to
// assign parent function for function types.
ActiveEnclosingFunctionScope active_enclosing_function(
active_class_, &signature_function);
// TODO(alexmarkov): skip type finalization
return ReadFunctionSignature(
signature_function, (flags & kFlagHasOptionalPositionalParams) != 0,
(flags & kFlagHasOptionalNamedParams) != 0,
(flags & kFlagHasTypeParams) != 0,
/* has_positional_param_names = */ false);
}
default:
UNREACHABLE();
}
return Object::null();
}
RawString* 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);
}
}
}
RawScript* BytecodeReaderHelper::ReadSourceFile(const String& uri,
intptr_t offset) {
// SourceFile flags, must be in sync with SourceFile constants in
// pkg/vm/lib/bytecode/declarations.dart.
const int kHasLineStartsFlag = 1 << 0;
const int kHasSourceFlag = 1 << 1;
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) {
// TODO(alexmarkov): read line starts only when needed.
const intptr_t line_starts_offset =
bytecode_component_->GetLineStartsOffset() + reader_.ReadUInt();
AlternativeReadingScope alt(&reader_, line_starts_offset);
const intptr_t num_line_starts = reader_.ReadUInt();
line_starts = reader_.ReadLineStartsData(num_line_starts);
}
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, RawScript::kKernelTag));
script.set_line_starts(line_starts);
if (source.IsNull() && line_starts.IsNull()) {
// This script provides a uri only, but no source or line_starts array.
// This could be a reference to a Script in another kernel binary.
// Make an attempt to find source and line starts when needed.
script.SetLazyLookupSourceAndLineStarts(true);
}
return script.raw();
}
RawTypeArguments* BytecodeReaderHelper::ReadTypeArguments() {
const bool is_recursive = reading_type_arguments_of_recursive_type_;
reading_type_arguments_of_recursive_type_ = false;
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);
}
if (is_recursive) {
// Avoid canonicalization of type arguments of recursive type
// as not all TypeRef objects are filled up at this point.
// Type arguments will be canoncialized when the root recursive
// type is canonicalized.
ASSERT(pending_recursive_types_ != nullptr);
return type_arguments.raw();
}
return type_arguments.Canonicalize();
}
void BytecodeReaderHelper::ReadAttributes(const Object& key) {
ASSERT(key.IsFunction() || key.IsField());
const auto& value = Object::Handle(Z, ReadObject());
Array& attributes =
Array::Handle(Z, I->object_store()->bytecode_attributes());
if (attributes.IsNull()) {
attributes = HashTables::New<BytecodeAttributesMap>(16, Heap::kOld);
}
BytecodeAttributesMap map(attributes.raw());
bool present = map.UpdateOrInsert(key, value);
ASSERT(!present);
I->object_store()->set_bytecode_attributes(map.Release());
if (key.IsField()) {
const Field& field = Field::Cast(key);
const auto& inferred_type_attr =
Array::CheckedHandle(Z, BytecodeReader::GetBytecodeAttribute(
key, Symbols::vm_inferred_type_metadata()));
if (!inferred_type_attr.IsNull() &&
(InferredTypeBytecodeAttribute::GetPCAt(inferred_type_attr, 0) ==
InferredTypeBytecodeAttribute::kFieldTypePC)) {
const InferredTypeMetadata type =
InferredTypeBytecodeAttribute::GetInferredTypeAt(
Z, inferred_type_attr, 0);
if (!type.IsTrivial()) {
field.set_guarded_cid(type.cid);
field.set_is_nullable(type.IsNullable());
field.set_guarded_list_length(Field::kNoFixedLength);
}
}
}
}
void BytecodeReaderHelper::ReadMembers(const Class& cls, bool discard_fields) {
ASSERT(Thread::Current()->IsMutatorThread());
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);
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/vm/lib/bytecode/declarations.dart.
const int kHasInitializerFlag = 1 << 0;
const int kHasGetterFlag = 1 << 1;
const int kHasSetterFlag = 1 << 2;
const int kIsReflectableFlag = 1 << 3;
const int kIsStaticFlag = 1 << 4;
const int kIsConstFlag = 1 << 5;
const int kIsFinalFlag = 1 << 6;
const int kIsCovariantFlag = 1 << 7;
const int kIsGenericCovariantImplFlag = 1 << 8;
const int kHasSourcePositionsFlag = 1 << 9;
const int kHasAnnotationsFlag = 1 << 10;
const int kHasPragmaFlag = 1 << 11;
const int kHasCustomScriptFlag = 1 << 12;
const int kHasInitializerCodeFlag = 1 << 13;
const int kHasAttributesFlag = 1 << 14;
const int kIsLateFlag = 1 << 15;
const int kIsExtensionMemberFlag = 1 << 16;
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);
Instance& value = Instance::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 has_initializer = (flags & kHasInitializerFlag) != 0;
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
const bool is_extension_member = (flags & kIsExtensionMemberFlag) != 0;
name ^= ReadObject();
type ^= ReadObject();
if ((flags & kHasCustomScriptFlag) != 0) {
Script& script = Script::CheckedHandle(Z, ReadObject());
script_class = GetPatchClass(cls, script);
} else {
script_class = cls.raw();
}
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, script_class, type,
position, end_position);
field.set_is_declared_in_bytecode(true);
field.set_has_pragma(has_pragma);
field.set_is_covariant((flags & kIsCovariantFlag) != 0);
field.set_is_generic_covariant_impl((flags & kIsGenericCovariantImplFlag) !=
0);
field.set_has_initializer(has_initializer);
field.set_is_late((flags & kIsLateFlag) != 0);
field.set_is_extension_member(is_extension_member);
if (!has_initializer) {
value ^= ReadObject();
if (is_static) {
field.SetStaticValue(value, true);
} else {
field.set_saved_initial_value(value);
// Null-initialized instance fields are tracked separately for each
// constructor (see handling of kHasNullableFieldsFlag).
if (!value.IsNull()) {
// Note: optimizer relies on DoubleInitialized bit in its
// field-unboxing heuristics.
// See JitCallSpecializer::VisitStoreInstanceField for more details.
field.RecordStore(value);
if (value.IsDouble()) {
field.set_is_double_initialized(true);
}
}
}
}
static_assert(KernelBytecode::kMinSupportedBytecodeFormatVersion < 14,
"Cleanup support for old bytecode format versions");
const bool has_initializer_code =
bytecode_component_->GetVersion() >= 14
? (flags & kHasInitializerCodeFlag) != 0
: has_initializer && is_static;
if (has_initializer_code) {
const intptr_t code_offset = reader_.ReadUInt();
field.set_bytecode_offset(code_offset +
bytecode_component_->GetCodesOffset());
if (is_static) {
field.SetStaticValue(Object::sentinel(), true);
}
}
if ((flags & kHasGetterFlag) != 0) {
name ^= ReadObject();
function = Function::New(name,
is_static ? RawFunction::kImplicitStaticGetter
: RawFunction::kImplicitGetter,
is_static, is_const,
false, // is_abstract
false, // is_external
false, // is_native
script_class, position);
function.set_end_token_pos(end_position);
function.set_result_type(type);
function.set_is_debuggable(false);
function.set_accessor_field(field);
function.set_is_declared_in_bytecode(true);
function.set_is_extension_member(is_extension_member);
if (is_const && has_initializer) {
function.set_bytecode_offset(field.bytecode_offset());
}
H.SetupFieldAccessorFunction(cls, function, type);
functions_->SetAt(function_index_++, function);
}
if ((flags & kHasSetterFlag) != 0) {
ASSERT((!is_static) && (!is_final) && (!is_const));
name ^= ReadObject();
function = Function::New(name, RawFunction::kImplicitSetter,
false, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
script_class, position);
function.set_end_token_pos(end_position);
function.set_result_type(Object::void_type());
function.set_is_debuggable(false);
function.set_accessor_field(field);
function.set_is_declared_in_bytecode(true);
function.set_is_extension_member(is_extension_member);
H.SetupFieldAccessorFunction(cls, function, type);
functions_->SetAt(function_index_++, function);
}
if ((flags & kHasAnnotationsFlag) != 0) {
intptr_t annotations_offset =
reader_.ReadUInt() + bytecode_component_->GetAnnotationsOffset();
ASSERT(annotations_offset > 0);
if (FLAG_enable_mirrors || has_pragma) {
Library& library = Library::Handle(Z, cls.library());
library.AddFieldMetadata(field, TokenPosition::kNoSource, 0,
annotations_offset);
if (has_pragma) {
// TODO(alexmarkov): read annotations right away using
// annotations_offset.
NoOOBMessageScope no_msg_scope(thread_);
NoReloadScope no_reload_scope(thread_->isolate(), thread_);
library.GetMetadata(field);
}
}
}
if ((flags & kHasAttributesFlag) != 0) {
ReadAttributes(field);
}
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, cls, Object::dynamic_type(),
TokenPosition::kNoSource, TokenPosition::kNoSource);
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);
}
}
}
RawPatchClass* BytecodeReaderHelper::GetPatchClass(const Class& cls,
const Script& script) {
if (patch_class_ != nullptr && patch_class_->patched_class() == cls.raw() &&
patch_class_->script() == script.raw()) {
return patch_class_->raw();
}
if (patch_class_ == nullptr) {
patch_class_ = &PatchClass::Handle(Z);
}
*patch_class_ = PatchClass::New(cls, script);
return patch_class_->raw();
}
void BytecodeReaderHelper::ReadFunctionDeclarations(const Class& cls) {
// Function flags, must be in sync with FunctionDeclaration constants in
// pkg/vm/lib/bytecode/declarations.dart.
const int kIsConstructorFlag = 1 << 0;
const int kIsGetterFlag = 1 << 1;
const int kIsSetterFlag = 1 << 2;
const int kIsFactoryFlag = 1 << 3;
const int kIsStaticFlag = 1 << 4;
const int kIsAbstractFlag = 1 << 5;
const int kIsConstFlag = 1 << 6;
const int kHasOptionalPositionalParamsFlag = 1 << 7;
const int kHasOptionalNamedParamsFlag = 1 << 8;
const int kHasTypeParamsFlag = 1 << 9;
const int kIsReflectableFlag = 1 << 10;
const int kIsDebuggableFlag = 1 << 11;
const int kIsAsyncFlag = 1 << 12;
const int kIsAsyncStarFlag = 1 << 13;
const int kIsSyncStarFlag = 1 << 14;
// const int kIsForwardingStubFlag = 1 << 15;
const int kIsNoSuchMethodForwarderFlag = 1 << 16;
const int kIsNativeFlag = 1 << 17;
const int kIsExternalFlag = 1 << 18;
const int kHasSourcePositionsFlag = 1 << 19;
const int kHasAnnotationsFlag = 1 << 20;
const int kHasPragmaFlag = 1 << 21;
const int kHasCustomScriptFlag = 1 << 22;
const int kHasAttributesFlag = 1 << 23;
const int kIsExtensionMemberFlag = 1 << 24;
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);
Function& function = Function::Handle(Z);
Array& parameter_types = Array::Handle(Z);
Array& parameter_names = Array::Handle(Z);
AbstractType& type = AbstractType::Handle(Z);
for (intptr_t i = 0; i < num_functions; ++i) {
intptr_t flags = reader_.ReadUInt();
const bool is_static = (flags & kIsStaticFlag) != 0;
const bool is_factory = (flags & kIsFactoryFlag) != 0;
const bool is_native = (flags & kIsNativeFlag) != 0;
const bool has_pragma = (flags & kHasPragmaFlag) != 0;
const bool is_extension_member = (flags & kIsExtensionMemberFlag) != 0;
name ^= ReadObject();
if ((flags & kHasCustomScriptFlag) != 0) {
Script& script = Script::CheckedHandle(Z, ReadObject());
script_class = GetPatchClass(cls, script);
} else {
script_class = cls.raw();
}
TokenPosition position = TokenPosition::kNoSource;
TokenPosition end_position = TokenPosition::kNoSource;
if ((flags & kHasSourcePositionsFlag) != 0) {
position = reader_.ReadPosition();
end_position = reader_.ReadPosition();
}
RawFunction::Kind kind = RawFunction::kRegularFunction;
if ((flags & kIsGetterFlag) != 0) {
kind = RawFunction::kGetterFunction;
} else if ((flags & kIsSetterFlag) != 0) {
kind = RawFunction::kSetterFunction;
} else if ((flags & (kIsConstructorFlag | kIsFactoryFlag)) != 0) {
kind = RawFunction::kConstructor;
name = ConstructorName(cls, name);
}
function = Function::New(name, kind, is_static, (flags & kIsConstFlag) != 0,
(flags & kIsAbstractFlag) != 0,
(flags & kIsExternalFlag) != 0, is_native,
script_class, position);
const bool is_expression_evaluation =
(name.raw() == Symbols::DebugProcedureName().raw());
// 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);
function.set_is_declared_in_bytecode(true);
function.set_has_pragma(has_pragma);
function.set_end_token_pos(end_position);
function.set_is_no_such_method_forwarder(
(flags & kIsNoSuchMethodForwarderFlag) != 0);
function.set_is_reflectable((flags & kIsReflectableFlag) != 0);
function.set_is_debuggable((flags & kIsDebuggableFlag) != 0);
function.set_is_extension_member(is_extension_member);
if ((flags & kIsSyncStarFlag) != 0) {
function.set_modifier(RawFunction::kSyncGen);
} else if ((flags & kIsAsyncFlag) != 0) {
function.set_modifier(RawFunction::kAsync);
function.set_is_inlinable(!FLAG_causal_async_stacks);
} else if ((flags & kIsAsyncStarFlag) != 0) {
function.set_modifier(RawFunction::kAsyncGen);
function.set_is_inlinable(!FLAG_causal_async_stacks);
}
if ((flags & kHasTypeParamsFlag) != 0) {
ReadTypeParametersDeclaration(Class::Handle(Z), function);
}
const intptr_t num_implicit_params = (!is_static || is_factory) ? 1 : 0;
const intptr_t num_params = num_implicit_params + reader_.ReadUInt();
intptr_t num_required_params = num_params;
if ((flags & (kHasOptionalPositionalParamsFlag |
kHasOptionalNamedParamsFlag)) != 0) {
num_required_params = num_implicit_params + reader_.ReadUInt();
}
function.set_num_fixed_parameters(num_required_params);
function.SetNumOptionalParameters(
num_params - num_required_params,
(flags & kHasOptionalNamedParamsFlag) == 0);
parameter_types = Array::New(num_params, Heap::kOld);
function.set_parameter_types(parameter_types);
parameter_names = Array::New(num_params, Heap::kOld);
function.set_parameter_names(parameter_names);
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.
function.SetParameterTypeAt(param_index, AbstractType::dynamic_type());
} else {
function.SetParameterTypeAt(param_index, H.GetDeclarationType(cls));
}
function.SetParameterNameAt(param_index, Symbols::This());
++param_index;
} else if (is_factory) {
function.SetParameterTypeAt(param_index, AbstractType::dynamic_type());
function.SetParameterNameAt(param_index,
Symbols::TypeArgumentsParameter());
++param_index;
}
for (; param_index < num_params; ++param_index) {
name ^= ReadObject();
parameter_names.SetAt(param_index, name);
type ^= ReadObject();
parameter_types.SetAt(param_index, type);
}
type ^= ReadObject();
function.set_result_type(type);
if (is_native) {
name ^= ReadObject();
function.set_native_name(name);
}
if ((flags & kIsAbstractFlag) == 0) {
const intptr_t code_offset = reader_.ReadUInt();
function.set_bytecode_offset(code_offset +
bytecode_component_->GetCodesOffset());
}
if ((flags & kHasAnnotationsFlag) != 0) {
const intptr_t annotations_offset =
reader_.ReadUInt() + bytecode_component_->GetAnnotationsOffset();
ASSERT(annotations_offset > 0);
if (FLAG_enable_mirrors || has_pragma) {
Library& library = Library::Handle(Z, cls.library());
library.AddFunctionMetadata(function, TokenPosition::kNoSource, 0,
annotations_offset);
if (has_pragma) {
if (H.constants().IsNull() &&
library.raw() == Library::CoreLibrary()) {
// Bootstrapping, need to postpone evaluation of pragma annotations
// as classes are not fully loaded/finalized yet.
const auto& pragma_funcs = GrowableObjectArray::Handle(
Z, H.EnsurePotentialPragmaFunctions());
pragma_funcs.Add(function);
} else {
// TODO(alexmarkov): read annotations right away using
// annotations_offset.
Thread* thread = H.thread();
NoOOBMessageScope no_msg_scope(thread);
NoReloadScope no_reload_scope(thread->isolate(), thread);
library.GetMetadata(function);
}
}
}
}
if ((flags & kHasAttributesFlag) != 0) {
ASSERT(!is_expression_evaluation);
ReadAttributes(function);
}
if (is_expression_evaluation) {
H.SetExpressionEvaluationFunction(function);
// Read bytecode of expression evaluation function eagerly,
// while expression_evaluation_library_ and FunctionScope
// are still set, as its constant pool may reference back to a library
// or a function which are not registered and cannot be looked up.
ASSERT(!function.is_abstract());
ASSERT(function.bytecode_offset() != 0);
// Replace class of the function in scope as we're going to look for
// expression evaluation function in a real class.
if (!cls.IsTopLevel()) {
scoped_function_class_ = H.GetExpressionEvaluationRealClass();
}
CompilerState compiler_state(thread_);
ReadCode(function, function.bytecode_offset());
}
functions_->SetAt(function_index_++, function);
}
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;
}
const auto& script = Script::Handle(Z, cls.script());
if (H.GetKernelProgramInfo().raw() != script.kernel_program_info()) {
// Class comes from a different binary.
cls.EnsureDeclarationLoaded();
return;
}
// We can reuse current BytecodeReaderHelper.
ActiveClassScope active_class_scope(active_class_, &cls);
AlternativeReadingScope alt(&reader_, cls.bytecode_offset());
ReadClassDeclaration(cls);
}
void BytecodeReaderHelper::ReadClassDeclaration(const Class& cls) {
// Class flags, must be in sync with ClassDeclaration constants in
// pkg/vm/lib/bytecode/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;
// Class is allocated when reading library declaration in
// BytecodeReaderHelper::ReadLibraryDeclaration.
// Its cid is set in Class::New / Isolate::RegisterClass /
// ClassTable::Register, unless it was loaded for expression evaluation.
ASSERT(cls.is_declared_in_bytecode());
ASSERT(!cls.is_declaration_loaded() || loading_native_wrappers_library_);
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();
cls.set_token_pos(position);
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();
}
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, Function::null_function());
}
auto& type = AbstractType::CheckedHandle(Z, ReadObject());
cls.set_super_type(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) {
intptr_t annotations_offset =
reader_.ReadUInt() + bytecode_component_->GetAnnotationsOffset();
ASSERT(annotations_offset > 0);
if (FLAG_enable_mirrors || has_pragma) {
const auto& library = Library::Handle(Z, cls.library());
if (cls.IsTopLevel()) {
ASSERT(!has_pragma);
library.AddLibraryMetadata(cls, TokenPosition::kNoSource, 0,
annotations_offset);
} else {
const auto& top_level_class =
Class::Handle(Z, library.toplevel_class());
library.AddClassMetadata(cls, top_level_class, TokenPosition::kNoSource,
0, annotations_offset);
}
}
}
const intptr_t members_offset = reader_.ReadUInt();
cls.set_bytecode_offset(members_offset +
bytecode_component_->GetMembersOffset());
// All types are finalized if loading from bytecode.
// TODO(alexmarkov): revise early stamping of native wrapper classes
// as type-finalized.
if (!cls.is_type_finalized()) {
cls.set_is_type_finalized();
}
// Avoid registering expression evaluation class in a hierarchy, as
// it doesn't have cid and shouldn't be found when enumerating subclasses.
if (expression_evaluation_library_ == nullptr) {
// TODO(alexmarkov): move this to class finalization.
ClassFinalizer::RegisterClassInHierarchy(Z, cls);
}
}
void BytecodeReaderHelper::ReadLibraryDeclaration(const Library& library,
bool lookup_classes) {
// Library flags, must be in sync with LibraryDeclaration constants in
// pkg/vm/lib/bytecode/declarations.dart.
const int kUsesDartMirrorsFlag = 1 << 0;
const int kUsesDartFfiFlag = 1 << 1;
const int kHasExtensionsFlag = 1 << 2;
ASSERT(library.is_declared_in_bytecode());
ASSERT(!library.Loaded());
ASSERT(library.toplevel_class() == Object::null());
// TODO(alexmarkov): fill in library.owned_scripts.
const intptr_t flags = reader_.ReadUInt();
if (((flags & kUsesDartMirrorsFlag) != 0) && !FLAG_enable_mirrors) {
H.ReportError(
"import of dart:mirrors is not supported in the current Dart runtime");
}
if (((flags & kUsesDartFfiFlag) != 0) && !Api::IsFfiEnabled()) {
H.ReportError(
"import of dart:ffi is not supported in the current Dart runtime");
}
auto& name = String::CheckedHandle(Z, ReadObject());
library.SetName(name);
const auto& script = Script::CheckedHandle(Z, ReadObject());
if ((flags & kHasExtensionsFlag) != 0) {
const intptr_t num_extensions = reader_.ReadUInt();
auto& import_namespace = Namespace::Handle(Z);
auto& native_library = Library::Handle(Z);
for (intptr_t i = 0; i < num_extensions; ++i) {
name ^= ReadObject();
ASSERT(name.StartsWith(Symbols::DartExtensionScheme()));
// Create a dummy library and add it as an import to the current library.
// Actual loading occurs in KernelLoader::LoadNativeExtensionLibraries().
// This also allows later to discover and reload this native extension,
// e.g. when running from an app-jit snapshot.
// See Loader::ReloadNativeExtensions(...) which relies on
// Dart_GetImportsOfScheme('dart-ext').
native_library = Library::New(name);
import_namespace = Namespace::New(native_library, Array::null_array(),
Array::null_array());
library.AddImport(import_namespace);
}
H.AddPotentialExtensionLibrary(library);
}
// The bootstrapper will take care of creating the native wrapper classes,
// but we will add the synthetic constructors to them here.
if (name.raw() ==
Symbols::Symbol(Symbols::kDartNativeWrappersLibNameId).raw()) {
ASSERT(library.LoadInProgress());
loading_native_wrappers_library_ = true;
} else {
loading_native_wrappers_library_ = false;
library.SetLoadInProgress();
}
const bool register_class = !IsExpressionEvaluationLibrary(library);
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.raw() == Symbols::Empty().raw());
cls = Class::New(library, Symbols::TopLevel(), script,
TokenPosition::kNoSource, register_class);
library.set_toplevel_class(cls);
} else {
if (lookup_classes) {
cls = library.LookupLocalClass(name);
}
if (lookup_classes && !cls.IsNull()) {
ASSERT(!cls.is_declaration_loaded() ||
loading_native_wrappers_library_);
cls.set_script(script);
} else {
cls = Class::New(library, name, script, TokenPosition::kNoSource,
register_class);
if (register_class) {
library.AddClass(cls);
}
}
}
cls.set_is_declared_in_bytecode(true);
cls.set_bytecode_offset(class_offset);
if (loading_native_wrappers_library_ || !register_class) {
AlternativeReadingScope alt(&reader_, class_offset);
ReadClassDeclaration(cls);
ActiveClassScope active_class_scope(active_class_, &cls);
AlternativeReadingScope alt2(&reader_, cls.bytecode_offset());
ReadMembers(cls, /* discard_fields = */ false);
}
}
ASSERT(!library.Loaded());
library.SetLoaded();
loading_native_wrappers_library_ = false;
}
void BytecodeReaderHelper::ReadLibraryDeclarations(intptr_t num_libraries) {
auto& library = Library::Handle(Z);
auto& uri = String::Handle(Z);
for (intptr_t i = 0; i < num_libraries; ++i) {
uri ^= ReadObject();
const intptr_t library_offset =
bytecode_component_->GetLibrariesOffset() + reader_.ReadUInt();
if (!FLAG_precompiled_mode && !I->should_load_vmservice()) {
if (uri.raw() == Symbols::DartVMServiceIO().raw()) {
continue;
}
}
bool lookup_classes = true;
library = Library::LookupLibrary(thread_, uri);
if (library.IsNull()) {
lookup_classes = false;
library = Library::New(uri);
if (uri.raw() == Symbols::EvalSourceUri().raw()) {
ASSERT(expression_evaluation_library_ == nullptr);
expression_evaluation_library_ = &Library::Handle(Z, library.raw());
} else {
library.Register(thread_);
}
}
if (library.Loaded()) {
continue;
}
library.set_is_declared_in_bytecode(true);
library.set_bytecode_offset(library_offset);
AlternativeReadingScope alt(&reader_, library_offset);
ReadLibraryDeclaration(library, lookup_classes);
}
}
void BytecodeReaderHelper::FindAndReadSpecificLibrary(const Library& library,
intptr_t num_libraries) {
auto& uri = String::Handle(Z);
for (intptr_t i = 0; i < num_libraries; ++i) {
uri ^= ReadObject();
const intptr_t library_offset =
bytecode_component_->GetLibrariesOffset() + reader_.ReadUInt();
if (uri.raw() == library.url()) {
library.set_is_declared_in_bytecode(true);
library.set_bytecode_offset(library_offset);
AlternativeReadingScope alt(&reader_, library_offset);
ReadLibraryDeclaration(library, /* lookup_classes = */ true);
return;
}
}
}
void BytecodeReaderHelper::FindModifiedLibrariesForHotReload(
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,
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_, function.bytecode_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::kIsGenericCovariantImplFlag) != 0) {
is_generic_covariant_impl->Add(i);
}
}
}
}
RawObject* BytecodeReaderHelper::BuildParameterDescriptor(
const Function& function) {
ASSERT(function.is_declared_in_bytecode());
Object& result = Object::Handle(Z);
if (!function.HasBytecode()) {
result = BytecodeReader::ReadFunctionBytecode(Thread::Current(), function);
if (result.IsError()) {
return result.raw();
}
}
const intptr_t num_params = function.NumParameters();
const intptr_t num_implicit_params = function.NumImplicitParameters();
const intptr_t num_explicit_params = num_params - num_implicit_params;
const Array& descriptor = Array::Handle(
Z, Array::New(num_explicit_params * Parser::kParameterEntrySize));
// 1. Find isFinal in the Code declaration.
bool found_final = false;
if (!function.is_abstract()) {
AlternativeReadingScope alt(&reader_, function.bytecode_offset());
const intptr_t code_flags = reader_.ReadUInt();
if ((code_flags & Code::kHasParameterFlagsFlag) != 0) {
const intptr_t num_explicit_params_written = reader_.ReadUInt();
ASSERT(num_explicit_params == num_explicit_params_written);
for (intptr_t i = 0; i < num_explicit_params; ++i) {
const intptr_t flags = reader_.ReadUInt();
descriptor.SetAt(
i * Parser::kParameterEntrySize + Parser::kParameterIsFinalOffset,
Bool::Get((flags & Parameter::kIsFinalFlag) != 0));
}
found_final = true;
}
}
if (!found_final) {
for (intptr_t i = 0; i < num_explicit_params; ++i) {
descriptor.SetAt(
i * Parser::kParameterEntrySize + Parser::kParameterIsFinalOffset,
Bool::Get(false));
}
}
// 2. Find metadata implicitly after the function declaration's metadata.
const Class& klass = Class::Handle(Z, function.Owner());
const Library& library = Library::Handle(Z, klass.library());
const Object& metadata = Object::Handle(
Z, library.GetExtendedMetadata(function, num_explicit_params));
if (metadata.IsError()) {
return metadata.raw();
}
if (Array::Cast(metadata).Length() != 0) {
for (intptr_t i = 0; i < num_explicit_params; i++) {
result = Array::Cast(metadata).At(i);
descriptor.SetAt(
i * Parser::kParameterEntrySize + Parser::kParameterMetadataOffset,
result);
}
}
// 3. Find the defaultValues in the EntryOptional sequence.
if (!function.is_abstract()) {
const Bytecode& bytecode = Bytecode::Handle(Z, function.bytecode());
ASSERT(!bytecode.IsNull());
const ObjectPool& constants = ObjectPool::Handle(Z, bytecode.object_pool());
ASSERT(!constants.IsNull());
const KBCInstr* instr =
reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart());
if (KernelBytecode::IsEntryOptionalOpcode(instr)) {
// Note that number of fixed parameters may not match 'A' operand of
// EntryOptional bytecode as [function] could be an implicit closure
// function with an extra implicit argument, while bytecode corresponds
// to a static function without any implicit arguments.
const intptr_t num_fixed_params = function.num_fixed_parameters();
const intptr_t num_opt_pos_params = KernelBytecode::DecodeB(instr);
const intptr_t num_opt_named_params = KernelBytecode::DecodeC(instr);
instr = KernelBytecode::Next(instr);
ASSERT(num_opt_pos_params == function.NumOptionalPositionalParameters());
ASSERT(num_opt_named_params == function.NumOptionalNamedParameters());
ASSERT((num_opt_pos_params == 0) || (num_opt_named_params == 0));
for (intptr_t i = 0; i < num_opt_pos_params; i++) {
const KBCInstr* load_value_instr = instr;
instr = KernelBytecode::Next(instr);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
result = constants.ObjectAt(KernelBytecode::DecodeE(load_value_instr));
descriptor.SetAt((num_fixed_params - num_implicit_params + i) *
Parser::kParameterEntrySize +
Parser::kParameterDefaultValueOffset,
result);
}
for (intptr_t i = 0; i < num_opt_named_params; i++) {
const KBCInstr* load_name_instr = instr;
const KBCInstr* load_value_instr =
KernelBytecode::Next(load_name_instr);
instr = KernelBytecode::Next(load_value_instr);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name_instr));
result = constants.ObjectAt(KernelBytecode::DecodeE(load_name_instr));
intptr_t param_index;
for (param_index = num_fixed_params; param_index < num_params;
param_index++) {
if (function.ParameterNameAt(param_index) == result.raw()) {
break;
}
}
ASSERT(param_index < num_params);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
result = constants.ObjectAt(KernelBytecode::DecodeE(load_value_instr));
descriptor.SetAt(
(param_index - num_implicit_params) * Parser::kParameterEntrySize +
Parser::kParameterDefaultValueOffset,
result);
}
}
}
return descriptor.raw();
}
void BytecodeReaderHelper::ParseBytecodeFunction(
ParsedFunction* parsed_function,
const Function& function) {
// Handle function kinds which don't have a user-defined body first.
switch (function.kind()) {
case RawFunction::kImplicitClosureFunction:
ParseForwarderFunction(parsed_function, function,
Function::Handle(Z, function.parent_function()));
return;
case RawFunction::kDynamicInvocationForwarder:
ParseForwarderFunction(parsed_function, function,
Function::Handle(Z, function.ForwardingTarget()));
return;
case RawFunction::kImplicitGetter:
case RawFunction::kImplicitSetter:
case RawFunction::kMethodExtractor:
BytecodeScopeBuilder(parsed_function).BuildScopes();
return;
case RawFunction::kImplicitStaticGetter: {
if (IsStaticFieldGetterGeneratedAsInitializer(function, Z)) {
break;
} else {
BytecodeScopeBuilder(parsed_function).BuildScopes();
return;
}
}
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kClosureFunction:
case RawFunction::kConstructor:
case RawFunction::kFieldInitializer:
break;
case RawFunction::kNoSuchMethodDispatcher:
case RawFunction::kInvokeFieldDispatcher:
case RawFunction::kSignatureFunction:
case RawFunction::kIrregexpFunction:
case RawFunction::kFfiTrampoline:
UNREACHABLE();
break;
}
// We only reach here if function has a bytecode body. Make sure it is
// loaded and collect information about covariant parameters.
if (!function.HasBytecode()) {
ReadCode(function, function.bytecode_offset());
ASSERT(function.HasBytecode());
}
// TODO(alexmarkov): simplify access to covariant / generic_covariant_impl
// flags of parameters so we won't need to read them separately.
if (!parsed_function->HasCovariantParametersInfo()) {
const intptr_t num_params = function.NumParameters();
BitVector* covariant_parameters = new (Z) BitVector(Z, num_params);
BitVector* generic_covariant_impl_parameters =
new (Z) BitVector(Z, num_params);
ReadParameterCovariance(function, covariant_parameters,
generic_covariant_impl_parameters);
parsed_function->SetCovariantParameters(covariant_parameters);
parsed_function->SetGenericCovariantImplParameters(
generic_covariant_impl_parameters);
}
}
void BytecodeReaderHelper::ParseForwarderFunction(
ParsedFunction* parsed_function,
const Function& function,
const Function& target) {
ASSERT(function.IsImplicitClosureFunction() ||
function.IsDynamicInvocationForwarder());
ASSERT(target.is_declared_in_bytecode());
if (function.IsDynamicInvocationForwarder() &&
target.IsImplicitSetterFunction()) {
BytecodeScopeBuilder(parsed_function).BuildScopes();
return;
}
if (!target.HasBytecode()) {
ReadCode(target, target.bytecode_offset());
}
BytecodeScopeBuilder(parsed_function).BuildScopes();
const auto& target_bytecode = Bytecode::Handle(Z, target.bytecode());
const auto& obj_pool = ObjectPool::Handle(Z, target_bytecode.object_pool());
AlternativeReadingScope alt(&reader_, target.bytecode_offset());
const intptr_t flags = reader_.ReadUInt();
const bool has_parameters_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 auto proc_attrs = kernel::ProcedureAttributesOf(target, Z);
// TODO(alexmarkov): fix building of flow graph for implicit closures so
// it would include missing checks and remove 'proc_attrs.has_tearoff_uses'
// from this condition.
const bool body_has_generic_covariant_impl_type_checks =
proc_attrs.has_non_this_uses || proc_attrs.has_tearoff_uses;
if (has_parameters_flags) {
const intptr_t num_params = reader_.ReadUInt();
const intptr_t num_implicit_params = function.NumImplicitParameters();
for (intptr_t i = 0; i < num_params; ++i) {
const intptr_t flags = reader_.ReadUInt();
bool is_covariant = (flags & Parameter::kIsCovariantFlag) != 0;
bool is_generic_covariant_impl =
(flags & Parameter::kIsGenericCovariantImplFlag) != 0;
LocalVariable* variable =
parsed_function->ParameterVariable(num_implicit_params + i);
if (is_covariant) {
variable->set_is_explicit_covariant_parameter();
}
const bool checked_in_method_body =
is_covariant || (is_generic_covariant_impl &&
body_has_generic_covariant_impl_type_checks);
if (checked_in_method_body) {
variable->set_type_check_mode(LocalVariable::kSkipTypeCheck);
} else {
ASSERT(variable->type_check_mode() == LocalVariable::kDoTypeCheck);
}
}
}
if (has_forwarding_stub_target) {
const intptr_t cp_index = reader_.ReadUInt();
const auto& forwarding_stub_target =
Function::CheckedZoneHandle(Z, obj_pool.ObjectAt(cp_index));
parsed_function->MarkForwardingStub(&forwarding_stub_target);
}
if (has_default_function_type_args) {
ASSERT(function.IsGeneric());
const intptr_t cp_index = reader_.ReadUInt();
const auto& type_args =
TypeArguments::CheckedHandle(Z, obj_pool.ObjectAt(cp_index));
parsed_function->SetDefaultFunctionTypeArguments(type_args);
}
if (function.HasOptionalParameters()) {
const KBCInstr* raw_bytecode =
reinterpret_cast<const KBCInstr*>(target_bytecode.PayloadStart());
const KBCInstr* entry = raw_bytecode;
raw_bytecode = KernelBytecode::Next(raw_bytecode);
ASSERT(KernelBytecode::IsEntryOptionalOpcode(entry));
ASSERT(KernelBytecode::DecodeB(entry) ==
function.NumOptionalPositionalParameters());
ASSERT(KernelBytecode::DecodeC(entry) ==
function.NumOptionalNamedParameters());
const intptr_t num_opt_params = function.NumOptionalParameters();
ZoneGrowableArray<const Instance*>* default_values =
new (Z) ZoneGrowableArray<const Instance*>(Z, num_opt_params);
if (function.HasOptionalPositionalParameters()) {
for (intptr_t i = 0, n = function.NumOptionalPositionalParameters();
i < n; ++i) {
const KBCInstr* load = raw_bytecode;
raw_bytecode = KernelBytecode::Next(raw_bytecode);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load));
const auto& value = Instance::CheckedZoneHandle(
Z, obj_pool.ObjectAt(KernelBytecode::DecodeE(load)));
default_values->Add(&value);
}
} else {
const intptr_t num_fixed_params = function.num_fixed_parameters();
auto& param_name = String::Handle(Z);
default_values->EnsureLength(num_opt_params, nullptr);
for (intptr_t i = 0; i < num_opt_params; ++i) {
const KBCInstr* load_name = raw_bytecode;
const KBCInstr* load_value = KernelBytecode::Next(load_name);
raw_bytecode = KernelBytecode::Next(load_value);
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name));
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value));
param_name ^= obj_pool.ObjectAt(KernelBytecode::DecodeE(load_name));
const auto& value = Instance::CheckedZoneHandle(
Z, obj_pool.ObjectAt(KernelBytecode::DecodeE(load_value)));
const intptr_t num_params = function.NumParameters();
intptr_t param_index = num_fixed_params;
for (; param_index < num_params; ++param_index) {
if (function.ParameterNameAt(param_index) == param_name.raw()) {
break;
}
}
ASSERT(param_index < num_params);
ASSERT(default_values->At(param_index - num_fixed_params) == nullptr);
(*default_values)[param_index - num_fixed_params] = &value;
}
}
parsed_function->set_default_parameter_values(default_values);
}
}
RawLibrary* BytecodeReaderHelper::ReadMain() {
return Library::RawCast(ReadObject());
}
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::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);
}
RawObject* BytecodeComponentData::GetObject(intptr_t index) const {
return data_.At(kNumFields + index);
}
RawArray* BytecodeComponentData::New(Zone* zone,
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 annotations_offset,
Heap::Space space) {
const Array& data =
Array::Handle(zone, Array::New(kNumFields + num_objects, space));
Smi& smi_handle = Smi::Handle(zone);
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(annotations_offset);
data.SetAt(kAnnotationsOffset, smi_handle);
return data.raw();
}
RawError* BytecodeReader::ReadFunctionBytecode(Thread* thread,
const Function& function) {
ASSERT(!FLAG_precompiled_mode);
ASSERT(!function.HasBytecode());
ASSERT(thread->sticky_error() == Error::null());
ASSERT(Thread::Current()->IsMutatorThread());
VMTagScope tagScope(thread, VMTag::kLoadBytecodeTagId);
#if defined(SUPPORT_TIMELINE)
TimelineDurationScope tds(thread, Timeline::GetCompilerStream(),
"BytecodeReader::ReadFunctionBytecode");
// This increases bytecode reading time by ~7%, so only keep it around for
// debugging.
#if defined(DEBUG)
tds.SetNumArguments(1);
tds.CopyArgument(0, "Function", function.ToQualifiedCString());
#endif // defined(DEBUG)
#endif // !defined(SUPPORT_TIMELINE)
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
StackZone stack_zone(thread);
Zone* const zone = stack_zone.GetZone();
HANDLESCOPE(thread);
auto& bytecode = Bytecode::Handle(zone);
switch (function.kind()) {
case RawFunction::kImplicitGetter:
bytecode = Object::implicit_getter_bytecode().raw();
break;
case RawFunction::kImplicitSetter:
bytecode = Object::implicit_setter_bytecode().raw();
break;
case RawFunction::kImplicitStaticGetter:
if (!IsStaticFieldGetterGeneratedAsInitializer(function, zone)) {
bytecode = Object::implicit_static_getter_bytecode().raw();
}
break;
case RawFunction::kMethodExtractor:
bytecode = Object::method_extractor_bytecode().raw();
break;
case RawFunction::kInvokeFieldDispatcher:
if (Class::Handle(zone, function.Owner()).id() == kClosureCid) {
bytecode = Object::invoke_closure_bytecode().raw();
} else {
bytecode = Object::invoke_field_bytecode().raw();
}
break;
case RawFunction::kNoSuchMethodDispatcher:
bytecode = Object::nsm_dispatcher_bytecode().raw();
break;
case RawFunction::kDynamicInvocationForwarder: {
const Function& target =
Function::Handle(zone, function.ForwardingTarget());
if (!target.HasBytecode()) {
// The forwarder will use the target's bytecode to handle optional
// parameters.
const Error& error =
Error::Handle(zone, ReadFunctionBytecode(thread, target));
if (!error.IsNull()) {
return error.raw();
}
}
{
const Script& script = Script::Handle(zone, target.script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(
&translation_helper, &active_class, &bytecode_component);
const Array& checks = Array::Handle(
zone, bytecode_reader.CreateForwarderChecks(target));
function.SetForwardingChecks(checks);
}
bytecode = Object::dynamic_invocation_forwarder_bytecode().raw();
} break;
default:
break;
}
if (!bytecode.IsNull()) {
function.AttachBytecode(bytecode);
} else if (function.is_declared_in_bytecode()) {
const intptr_t code_offset = function.bytecode_offset();
if (code_offset != 0) {
CompilerState compiler_state(thread);
const Script& script = Script::Handle(zone, function.script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
// Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be
// used e.g. for type translation.
const Class& klass = Class::Handle(zone, function.Owner());
Function& outermost_function =
Function::Handle(zone, function.GetOutermostFunction());
ActiveClassScope active_class_scope(&active_class, &klass);
ActiveMemberScope active_member(&active_class, &outermost_function);
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
bytecode_reader.ReadCode(function, code_offset);
}
}
return Error::null();
} else {
return thread->StealStickyError();
}
}
RawObject* BytecodeReader::ReadAnnotation(const Field& annotation_field) {
ASSERT(annotation_field.is_declared_in_bytecode());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(thread->IsMutatorThread());
const Script& script = Script::Handle(zone, annotation_field.Script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
annotation_field.bytecode_offset());
return bytecode_reader.ReadObject();
}
RawArray* BytecodeReader::ReadExtendedAnnotations(const Field& annotation_field,
intptr_t count) {
ASSERT(annotation_field.is_declared_in_bytecode());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(thread->IsMutatorThread());
const Script& script = Script::Handle(zone, annotation_field.Script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(),
annotation_field.bytecode_offset());
bytecode_reader.ReadObject(); // Discard main annotation.
Array& result = Array::Handle(zone, Array::New(count));
Object& element = Object::Handle(zone);
for (intptr_t i = 0; i < count; i++) {
element = bytecode_reader.ReadObject();
result.SetAt(i, element);
}
return result.raw();
}
void BytecodeReader::ResetObjectTable(const KernelProgramInfo& info) {
Thread* thread = Thread::Current();
TranslationHelper translation_helper(thread);
translation_helper.InitFromKernelProgramInfo(info);
ActiveClass active_class;
BytecodeComponentData bytecode_component(&Array::Handle(
thread->zone(), translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
bytecode_reader.ResetObjects();
}
void BytecodeReader::LoadClassDeclaration(const Class& cls) {
TIMELINE_DURATION(Thread::Current(), Compiler,
"BytecodeReader::LoadClassDeclaration");
ASSERT(cls.is_declared_in_bytecode());
ASSERT(!cls.is_declaration_loaded());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(thread->IsMutatorThread());
const Script& script = Script::Handle(zone, cls.script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
ActiveClassScope active_class_scope(&active_class, &cls);
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(), cls.bytecode_offset());
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(thread->IsMutatorThread());
const Script& script = Script::Handle(zone, cls.script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
ActiveClass active_class;
ActiveClassScope active_class_scope(&active_class, &cls);
BytecodeComponentData bytecode_component(
&Array::Handle(zone, translation_helper.GetBytecodeComponent()));
ASSERT(!bytecode_component.IsNull());
BytecodeReaderHelper bytecode_reader(&translation_helper, &active_class,
&bytecode_component);
AlternativeReadingScope alt(&bytecode_reader.reader(), cls.bytecode_offset());
// 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);
}
RawObject* BytecodeReader::GetBytecodeAttribute(const Object& key,
const String& name) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const auto* object_store = thread->isolate()->object_store();
if (object_store->bytecode_attributes() == Object::null()) {
return Object::null();
}
BytecodeAttributesMap map(object_store->bytecode_attributes());
const auto& attrs = Array::CheckedHandle(zone, map.GetOrNull(key));
ASSERT(map.Release().raw() == object_store->bytecode_attributes());
if (attrs.IsNull()) {
return Object::null();
}
auto& obj = Object::Handle(zone);
for (intptr_t i = 0, n = attrs.Length(); i + 1 < n; i += 2) {
obj = attrs.At(i);
if (obj.raw() == name.raw()) {
return attrs.At(i + 1);
}
}
return Object::null();
}
InferredTypeMetadata InferredTypeBytecodeAttribute::GetInferredTypeAt(
Zone* zone,
const Array& attr,
intptr_t index) {
ASSERT(index + kNumElements <= attr.Length());
const auto& type = AbstractType::CheckedHandle(zone, attr.At(index + 1));
const intptr_t flags = Smi::Value(Smi::RawCast(attr.At(index + 2)));
if (!type.IsNull()) {
intptr_t cid = Type::Cast(type).type_class_id();
return InferredTypeMetadata(cid, flags);
} else {
return InferredTypeMetadata(kDynamicCid, flags);
}
}
#if !defined(PRODUCT)
RawLocalVarDescriptors* BytecodeReader::ComputeLocalVarDescriptors(
Zone* zone,
const Function& function,
const Bytecode& bytecode) {
ASSERT(function.is_declared_in_bytecode());
ASSERT(function.HasBytecode());
ASSERT(!bytecode.IsNull());
ASSERT(function.bytecode() == bytecode.raw());
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.bytecode());
const auto& parent_vars = LocalVarDescriptors::Handle(
zone, parent_bytecode.GetLocalVarDescriptors());
for (intptr_t i = 0; i < parent_vars.Length(); ++i) {
RawLocalVarDescriptors::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() == RawLocalVarDescriptors::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(RawLocalVarDescriptors::kContextVar);
desc.info.scope_id = context_level;
desc.info.set_index(local_vars.Index());
} else {
desc.info.set_kind(RawLocalVarDescriptors::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(RawLocalVarDescriptors::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)
bool IsStaticFieldGetterGeneratedAsInitializer(const Function& function,
Zone* zone) {
ASSERT(function.kind() == RawFunction::kImplicitStaticGetter);
const auto& field = Field::Handle(zone, function.accessor_field());
return field.is_declared_in_bytecode() && field.is_const() &&
field.has_initializer();
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)