Files
sdk/runtime/vm/compiler/frontend/bytecode_reader.cc
T
Alexander Markov 4d37977473 [vm/bytecode] Do not mark null-initialized instance fields as nullable too early
In bytecode we track null-initialized fields for each constructor,
so there is no need to record stores to such fields eagerly.

This enables more accurate tracking of fields which don't have initializer
or initialized with null, but they are always overwritten by initializers
in constructors.

Change-Id: I08edc65c18a644f69109f0eced736e5e859f1715
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/98624
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2019-04-03 21:35:23 +00:00

2394 lines
88 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_entry.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/reusable_handles.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) {}
bool BytecodeMetadataHelper::HasBytecode(intptr_t node_offset) {
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
return (md_offset >= 0);
}
void BytecodeMetadataHelper::ReadMetadata(const Function& function) {
#if defined(SUPPORT_TIMELINE)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadMetadata");
// 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)
switch (function.kind()) {
case RawFunction::kImplicitGetter:
function.AttachBytecode(Object::implicit_getter_bytecode());
return;
case RawFunction::kImplicitSetter:
function.AttachBytecode(Object::implicit_setter_bytecode());
return;
default: {
}
}
intptr_t code_offset = 0;
if (function.is_declared_in_bytecode()) {
code_offset = function.bytecode_offset();
if (code_offset == 0) {
return;
}
} else {
const intptr_t node_offset = function.kernel_offset();
code_offset = GetNextMetadataPayloadOffset(node_offset);
if (code_offset < 0) {
return;
}
}
ASSERT(Thread::Current()->IsMutatorThread());
BytecodeComponentData bytecode_component(
Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(helper_, active_class_,
&bytecode_component);
bytecode_reader.ReadCode(function, code_offset);
}
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());
// No parsing is needed if function has bytecode attached.
// With one exception: implicit getters and setters have artificial
// bytecodes, but they are still handled by shared flow graph builder
// which requires scopes/parsing.
if (function.HasBytecode() && !function.IsImplicitGetterFunction() &&
!function.IsImplicitSetterFunction()) {
return;
}
BytecodeComponentData bytecode_component(
Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(helper_, active_class_,
&bytecode_component);
bytecode_reader.ParseBytecodeFunction(parsed_function, function);
}
bool BytecodeMetadataHelper::ReadMembers(intptr_t node_offset,
const Class& cls,
bool discard_fields) {
TIMELINE_DURATION(Thread::Current(), Compiler,
"BytecodeMetadataHelper::ReadMembers");
ASSERT(node_offset > 0);
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return false;
}
ASSERT(Thread::Current()->IsMutatorThread());
BytecodeComponentData bytecode_component(
Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(helper_, active_class_,
&bytecode_component);
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
md_offset);
intptr_t members_offset = helper_->reader_.ReadUInt();
bytecode_reader.ReadMembers(cls, members_offset, discard_fields);
return true;
}
RawObject* BytecodeMetadataHelper::ReadAnnotation(intptr_t annotation_offset) {
ASSERT(Thread::Current()->IsMutatorThread());
BytecodeComponentData bytecode_component(
Array::Handle(helper_->zone_, GetBytecodeComponent()));
BytecodeReaderHelper bytecode_reader(helper_, active_class_,
&bytecode_component);
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
annotation_offset);
return bytecode_reader.ReadObject();
}
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(helper_, active_class_,
&bytecode_component);
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
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(helper_, nullptr, nullptr);
return component_reader.ReadBytecodeComponent(md_offset);
}
BytecodeReaderHelper::BytecodeReaderHelper(
KernelReaderHelper* helper,
ActiveClass* active_class,
BytecodeComponentData* bytecode_component)
: helper_(helper),
translation_helper_(helper->translation_helper_),
active_class_(active_class),
zone_(helper_->zone_),
bytecode_component_(bytecode_component),
scoped_function_(Function::Handle(helper_->zone_)),
scoped_function_name_(String::Handle(helper_->zone_)),
scoped_function_class_(Class::Handle(helper_->zone_)) {}
void BytecodeReaderHelper::ReadCode(const Function& function,
intptr_t code_offset) {
ASSERT(Thread::Current()->IsMutatorThread());
ASSERT(!function.IsImplicitGetterFunction() &&
!function.IsImplicitSetterFunction());
ASSERT(code_offset > 0);
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
code_offset);
const intptr_t flags = helper_->reader_.ReadUInt();
const bool has_exceptions_table =
(flags & Code::kHasExceptionsTableFlag) != 0;
const bool has_source_positions =
(flags & Code::kHasSourcePositionsFlag) != 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 = helper_->reader_.ReadUInt();
ASSERT(num_params ==
function.NumParameters() - function.NumImplicitParameters());
for (intptr_t i = 0; i < num_params; ++i) {
helper_->reader_.ReadUInt();
}
}
if (has_forwarding_stub_target) {
helper_->reader_.ReadUInt();
}
if (has_default_function_type_args) {
helper_->reader_.ReadUInt();
}
intptr_t num_closures = 0;
if (has_closures) {
num_closures = helper_->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 = helper_->reader_.ReadListLength();
const ObjectPool& pool = ObjectPool::Handle(Z, ObjectPool::New(obj_count));
{
// While reading pool entries, deopt_ids are allocated for
// ICData objects.
//
// TODO(alexmarkov): allocate deopt_ids for closures separately
DeoptIdScope deopt_id_scope(H.thread(), 0);
ReadConstantPool(function, pool);
}
// Read bytecode and attach to function.
const Bytecode& bytecode = Bytecode::Handle(Z, ReadBytecode(pool));
function.AttachBytecode(bytecode);
ASSERT(bytecode.GetBinary(Z) == helper_->reader_.typed_data()->raw());
ReadExceptionsTable(bytecode, has_exceptions_table);
ReadSourcePositions(bytecode, has_source_positions);
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 = helper_->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 = helper_->reader_.ReadUInt();
const bool has_exceptions_table =
(flags & Code::kHasExceptionsTableFlag) != 0;
const bool has_source_positions =
(flags & Code::kHasSourcePositionsFlag) != 0;
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
ASSERT(bytecode.GetBinary(Z) == helper_->reader_.typed_data()->raw());
ReadExceptionsTable(closure_bytecode, has_exceptions_table);
ReadSourcePositions(closure_bytecode, has_source_positions);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
}
}
}
void BytecodeReaderHelper::ReadClosureDeclaration(const Function& function,
intptr_t closureIndex) {
const int kHasOptionalPositionalParams = 1 << 0;
const int kHasOptionalNamedParams = 1 << 1;
const int kHasTypeParams = 1 << 2;
const intptr_t flags = helper_->reader_.ReadUInt();
Object& parent = Object::Handle(Z, ReadObject());
if (!parent.IsFunction()) {
ASSERT(parent.IsField());
ASSERT(function.kind() == RawFunction::kImplicitStaticFinalGetter);
// Closure in a static field initializer, so use current function as parent.
parent = function.raw();
}
String& name = String::CheckedHandle(Z, ReadObject());
ASSERT(name.IsSymbol());
const Function& closure = Function::Handle(
Z, Function::NewClosureFunction(name, Function::Cast(parent),
TokenPosition::kNoSource));
closures_->SetAt(closureIndex, closure);
Type& signature_type =
Type::Handle(Z, ReadFunctionSignature(
closure, (flags & kHasOptionalPositionalParams) != 0,
(flags & kHasOptionalNamedParams) != 0,
(flags & kHasTypeParams) != 0,
/* has_positional_param_names = */ true));
closure.SetSignatureType(signature_type);
}
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);
function_type_type_parameters_ =
&TypeArguments::Handle(Z, func.type_parameters());
}
const intptr_t kImplicitClosureParam = 1;
const intptr_t num_params =
kImplicitClosureParam + helper_->reader_.ReadUInt();
intptr_t num_required_params = num_params;
if (has_optional_positional_params || has_optional_named_params) {
num_required_params = kImplicitClosureParam + helper_->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();
type ^= ClassFinalizer::FinalizeType(*(active_class_->klass), type);
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 = helper_->reader_.ReadUInt();
ASSERT(num_type_params > 0);
// First setup the type parameters, so if any of the following code uses it
// (in a recursive way) we're fine.
//
// Step a) Create 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);
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);
}
// 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();
parameter.set_bound(bound);
}
}
void BytecodeReaderHelper::ReadConstantPool(const Function& function,
const ObjectPool& pool) {
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,
kNull, // TODO(alexmarkov): obsolete, remove
kString, // TODO(alexmarkov): obsolete, remove
kInt, // TODO(alexmarkov): obsolete, remove
kDouble, // TODO(alexmarkov): obsolete, remove
kBool, // TODO(alexmarkov): obsolete, remove
kArgDesc,
kICData,
kStaticICData,
kStaticField,
kInstanceField,
kClass,
kTypeArgumentsField,
kTearOff, // TODO(alexmarkov): obsolete, remove
kType,
kTypeArguments, // TODO(alexmarkov): obsolete, remove
kList, // TODO(alexmarkov): obsolete, remove
kInstance, // TODO(alexmarkov): obsolete, remove
kTypeArgumentsForInstanceAllocation, // TODO(alexmarkov): obsolete, remove
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
kPartialTearOffInstantiation, // TODO(alexmarkov): obsolete, remove
kEmptyTypeArguments,
kSymbol, // TODO(alexmarkov): obsolete, remove
kInterfaceCallV1, // TODO(alexmarkov): obsolete, remove
kObjectRef,
kDirectCall,
kInterfaceCall,
};
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);
TypeArguments& type_args = TypeArguments::Handle(Z);
Class* symbol_class = nullptr;
Field* symbol_name_field = nullptr;
const String* simpleInstanceOf = nullptr;
const intptr_t obj_count = pool.Length();
for (intptr_t i = 0; i < obj_count; ++i) {
const intptr_t tag = helper_->ReadTag();
switch (tag) {
case ConstantPoolTag::kInvalid:
UNREACHABLE();
case ConstantPoolTag::kNull:
obj = Object::null();
break;
case ConstantPoolTag::kString:
obj = ReadString();
ASSERT(obj.IsString() && obj.IsCanonical());
break;
case ConstantPoolTag::kInt: {
uint32_t low_bits = helper_->ReadUInt32();
int64_t value = helper_->ReadUInt32();
value = (value << 32) | low_bits;
obj = Integer::New(value, Heap::kOld);
obj = H.Canonicalize(Integer::Cast(obj));
} break;
case ConstantPoolTag::kDouble: {
uint32_t low_bits = helper_->ReadUInt32();
uint64_t bits = helper_->ReadUInt32();
bits = (bits << 32) | low_bits;
double value = bit_cast<double, uint64_t>(bits);
obj = Double::New(value, Heap::kOld);
obj = H.Canonicalize(Double::Cast(obj));
} break;
case ConstantPoolTag::kBool:
if (helper_->ReadByte() == 1) {
obj = Bool::True().raw();
} else {
obj = Bool::False().raw();
}
break;
case ConstantPoolTag::kArgDesc: {
intptr_t num_arguments = helper_->ReadUInt();
intptr_t num_type_args = helper_->ReadUInt();
intptr_t num_arg_names = helper_->ReadListLength();
if (num_arg_names == 0) {
obj = ArgumentsDescriptor::New(num_type_args, num_arguments);
} else {
array = Array::New(num_arg_names);
for (intptr_t j = 0; j < num_arg_names; j++) {
name = ReadString();
array.SetAt(j, name);
}
obj = ArgumentsDescriptor::New(num_type_args, num_arguments, array);
}
} break;
case ConstantPoolTag::kICData: {
intptr_t flags = helper_->ReadByte();
InvocationKind kind =
static_cast<InvocationKind>(flags & kInvocationKindMask);
bool isDynamic = (flags & kFlagDynamic) != 0;
name ^= ReadObject();
ASSERT(name.IsSymbol());
intptr_t arg_desc_index = helper_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
if (simpleInstanceOf == nullptr) {
simpleInstanceOf =
&Library::PrivateCoreLibName(Symbols::_simpleInstanceOf());
}
intptr_t checked_argument_count = 1;
if ((kind == InvocationKind::method) &&
((MethodTokenRecognizer::RecognizeTokenKind(name) !=
Token::kILLEGAL) ||
(name.raw() == simpleInstanceOf->raw()))) {
intptr_t argument_count = ArgumentsDescriptor(array).Count();
ASSERT(argument_count <= 2);
checked_argument_count = argument_count;
}
// 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) &&
!FLAG_precompiled_mode && I->should_emit_strong_mode_checks() &&
(name.raw() != Symbols::EqualOperator().raw()) &&
(name.raw() != Symbols::Call().raw())) {
name = Function::CreateDynamicInvocationForwarderName(name);
}
obj =
ICData::New(function, name,
array, // Arguments descriptor.
H.thread()->compiler_state().GetNextDeoptId(),
checked_argument_count, ICData::RebindRule::kInstance);
} break;
case ConstantPoolTag::kStaticICData: {
elem = ReadObject();
ASSERT(elem.IsFunction());
name = Function::Cast(elem).name();
const int num_args_checked =
MethodRecognizer::NumArgsCheckedForStaticCall(Function::Cast(elem));
intptr_t arg_desc_index = helper_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
obj = ICData::New(function, name,
array, // Arguments descriptor.
H.thread()->compiler_state().GetNextDeoptId(),
num_args_checked, ICData::RebindRule::kStatic);
ICData::Cast(obj).AddTarget(Function::Cast(elem));
} 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::kTearOff:
obj = ReadObject();
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitClosureFunction();
ASSERT(obj.IsFunction());
obj = Function::Cast(obj).ImplicitStaticClosure();
ASSERT(obj.IsInstance());
obj = H.Canonicalize(Instance::Cast(obj));
break;
case ConstantPoolTag::kType:
obj = ReadObject();
ASSERT(obj.IsAbstractType());
break;
case ConstantPoolTag::kTypeArguments:
cls = Class::null();
obj = ReadTypeArguments(cls);
ASSERT(obj.IsNull() || obj.IsTypeArguments());
break;
case ConstantPoolTag::kList: {
obj = ReadObject();
ASSERT(obj.IsAbstractType());
const intptr_t length = helper_->ReadListLength();
array = Array::New(length, AbstractType::Cast(obj));
for (intptr_t j = 0; j < length; j++) {
intptr_t elem_index = helper_->ReadUInt();
ASSERT(elem_index < i);
elem = pool.ObjectAt(elem_index);
array.SetAt(j, elem);
}
array.MakeImmutable();
obj = H.Canonicalize(Array::Cast(array));
ASSERT(!obj.IsNull());
} break;
case ConstantPoolTag::kInstance: {
cls ^= ReadObject();
obj = Instance::New(cls, Heap::kOld);
intptr_t type_args_index = helper_->ReadUInt();
ASSERT(type_args_index < i);
type_args ^= pool.ObjectAt(type_args_index);
if (!type_args.IsNull()) {
Instance::Cast(obj).SetTypeArguments(type_args);
}
intptr_t num_fields = helper_->ReadUInt();
for (intptr_t j = 0; j < num_fields; j++) {
field ^= ReadObject();
intptr_t elem_index = helper_->ReadUInt();
ASSERT(elem_index < i);
elem = pool.ObjectAt(elem_index);
Instance::Cast(obj).SetField(field, elem);
}
obj = H.Canonicalize(Instance::Cast(obj));
} break;
case ConstantPoolTag::kTypeArgumentsForInstanceAllocation: {
cls ^= ReadObject();
obj = ReadTypeArguments(cls);
ASSERT(obj.IsNull() || obj.IsTypeArguments());
} break;
case ConstantPoolTag::kClosureFunction: {
intptr_t closure_index = helper_->ReadUInt();
obj = closures_->At(closure_index);
ASSERT(obj.IsFunction());
} break;
case ConstantPoolTag::kEndClosureFunctionScope: {
// Entry is not used and set to null.
obj = Object::null();
} break;
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::kPartialTearOffInstantiation: {
intptr_t tearoff_index = helper_->ReadUInt();
ASSERT(tearoff_index < i);
const Closure& old_closure =
Closure::CheckedHandle(Z, pool.ObjectAt(tearoff_index));
intptr_t type_args_index = helper_->ReadUInt();
ASSERT(type_args_index < i);
type_args ^= pool.ObjectAt(type_args_index);
obj = Closure::New(
TypeArguments::Handle(Z, old_closure.instantiator_type_arguments()),
TypeArguments::Handle(Z, old_closure.function_type_arguments()),
type_args, Function::Handle(Z, old_closure.function()),
Context::Handle(Z, old_closure.context()), Heap::kOld);
obj = H.Canonicalize(Instance::Cast(obj));
} break;
case ConstantPoolTag::kEmptyTypeArguments:
obj = Object::empty_type_arguments().raw();
break;
case ConstantPoolTag::kSymbol: {
name ^= ReadObject();
ASSERT(name.IsSymbol());
if (symbol_class == nullptr) {
elem = Library::InternalLibrary();
ASSERT(!elem.IsNull());
symbol_class = &Class::Handle(
Z, Library::Cast(elem).LookupClass(Symbols::Symbol()));
ASSERT(!symbol_class->IsNull());
symbol_name_field = &Field::Handle(
Z,
symbol_class->LookupInstanceFieldAllowPrivate(Symbols::_name()));
ASSERT(!symbol_name_field->IsNull());
}
obj = Instance::New(*symbol_class, Heap::kOld);
Instance::Cast(obj).SetField(*symbol_name_field, name);
obj = H.Canonicalize(Instance::Cast(obj));
} break;
case ConstantPoolTag::kInterfaceCallV1: {
UNREACHABLE();
} 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;
default:
UNREACHABLE();
}
pool.SetTypeAt(i, ObjectPool::EntryType::kTaggedObject,
ObjectPool::Patchability::kNotPatchable);
pool.SetObjectAt(i, obj);
}
}
RawBytecode* BytecodeReaderHelper::ReadBytecode(const ObjectPool& pool) {
#if defined(SUPPORT_TIMELINE)
TIMELINE_DURATION(Thread::Current(), CompilerVerbose,
"BytecodeReaderHelper::ReadBytecode");
#endif // defined(SUPPORT_TIMELINE)
intptr_t size = helper_->ReadUInt();
intptr_t offset = Utils::RoundUp(helper_->reader_.offset(), sizeof(KBCInstr));
const uint8_t* data = helper_->reader_.BufferAt(offset);
ASSERT(Utils::IsAligned(data, sizeof(KBCInstr)));
helper_->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 ? helper_->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::ZoneHandle(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 = helper_->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(
helper_->reader_.ReadUInt(), /* is_return_address = */ true);
intptr_t end_pc = KernelBytecode::BytecodePcToOffset(
helper_->reader_.ReadUInt(), /* is_return_address = */ true);
intptr_t handler_pc = KernelBytecode::BytecodePcToOffset(
helper_->reader_.ReadUInt(), /* is_return_address = */ false);
uint8_t flags = helper_->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 = helper_->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 = helper_->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, -1);
exception_handlers_list->AddHandler(
try_index, outer_try_index, handler_pc, TokenPosition::kNoSource,
is_generated, handler_types, 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 = helper_->reader_.ReadUInt();
bytecode.set_source_positions_binary_offset(
bytecode_component_->GetSourcePositionsOffset() + 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::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:
break;
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(&helper_->reader_, &H.metadata_payloads(),
md_offset);
const intptr_t start_offset = helper_->reader_.offset();
intptr_t magic = helper_->reader_.ReadUInt32();
if (magic != KernelBytecode::kMagicValue) {
return ReadBytecodeComponentV2(md_offset);
}
const intptr_t version = helper_->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);
}
helper_->reader_.ReadUInt32(); // Skip stringTable.numItems
const intptr_t string_table_offset =
start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip objectTable.numItems
const intptr_t object_table_offset =
start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip main.numItems
const intptr_t main_offset = start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip members.numItems
const intptr_t members_offset = start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip codes.numItems
const intptr_t codes_offset = start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip sourcePositions.numItems
const intptr_t sources_positions_offset =
start_offset + helper_->reader_.ReadUInt32();
helper_->reader_.ReadUInt32(); // Skip annotations.numItems
const intptr_t annotations_offset =
start_offset + helper_->reader_.ReadUInt32();
// Read header of string table.
helper_->reader_.set_offset(string_table_offset);
const intptr_t num_one_byte_strings = helper_->reader_.ReadUInt32();
const intptr_t num_two_byte_strings = helper_->reader_.ReadUInt32();
const intptr_t strings_contents_offset =
helper_->reader_.offset() +
(num_one_byte_strings + num_two_byte_strings) * 4;
// Read header of object table.
helper_->reader_.set_offset(object_table_offset);
const intptr_t num_objects = helper_->reader_.ReadUInt();
const intptr_t objects_size = helper_->reader_.ReadUInt();
// Skip over contents of objects.
const intptr_t objects_contents_offset = helper_->reader_.offset();
helper_->reader_.set_offset(objects_contents_offset + objects_size);
const Array& bytecode_component_array = Array::Handle(
Z,
BytecodeComponentData::New(
Z, version, num_objects, string_table_offset, strings_contents_offset,
objects_contents_offset, main_offset, members_offset, codes_offset,
sources_positions_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(helper_->reader_.ReadUInt());
bytecode_component.SetObject(i, offs);
}
H.SetBytecodeComponent(bytecode_component_array);
return bytecode_component_array.raw();
}
// TODO(alexmarkov): obsolete, remove when dropping support for old bytecode
// format version.
RawArray* BytecodeReaderHelper::ReadBytecodeComponentV2(intptr_t md_offset) {
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
md_offset);
const intptr_t kMinVersion = 1;
const intptr_t kMaxVersion = 2;
const intptr_t version = helper_->reader_.ReadUInt();
if ((version < kMinVersion) || (version > kMaxVersion)) {
FATAL1("Unsupported Dart bytecode format version %" Pd ".", version);
}
const intptr_t strings_size = helper_->reader_.ReadUInt();
helper_->reader_.ReadUInt(); // Objects table size.
// Read header of strings table.
const intptr_t strings_header_offset = helper_->reader_.offset();
const intptr_t num_one_byte_strings = helper_->reader_.ReadUInt32();
const intptr_t num_two_byte_strings = helper_->reader_.ReadUInt32();
const intptr_t strings_contents_offset =
helper_->reader_.offset() +
(num_one_byte_strings + num_two_byte_strings) * 4;
// Read header of objects table.
helper_->reader_.set_offset(strings_header_offset + strings_size);
const intptr_t num_objects = helper_->reader_.ReadUInt();
const intptr_t objects_size = helper_->reader_.ReadUInt();
// Skip over contents of objects.
const intptr_t objects_contents_offset = helper_->reader_.offset();
helper_->reader_.set_offset(objects_contents_offset + objects_size);
const Array& bytecode_component_array =
Array::Handle(Z, BytecodeComponentData::New(
Z, version, num_objects, strings_header_offset,
strings_contents_offset, objects_contents_offset, 0,
0, 0, 0, 0, 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(helper_->reader_.ReadUInt());
bytecode_component.SetObject(i, offs);
}
H.SetBytecodeComponent(bytecode_component_array);
return bytecode_component_array.raw();
}
RawObject* BytecodeReaderHelper::ReadObject() {
uint32_t header = helper_->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(&helper_->reader_, &H.metadata_payloads(),
offset);
header = helper_->reader_.ReadUInt();
obj = ReadObjectContents(header);
ASSERT(obj->IsHeapObject());
{
Thread* thread = H.thread();
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(H.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,
kSimpleType,
kTypeParameter,
kGenericType,
kFunctionType,
kName,
kTypeArguments,
kFinalizedGenericType,
kConstObject,
kArgDesc,
};
// 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;
// SimpleType flags, must be in sync with _SimpleTypeHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const intptr_t kFlagIsDynamic = kFlagBit0;
const intptr_t kFlagIsVoid = kFlagBit1;
// FunctionType flags, must be in sync with _FunctionTypeHandle constants in
// pkg/vm/lib/bytecode/object_table.dart.
const int kFlagHasOptionalPositionalParams = kFlagBit0;
const int kFlagHasOptionalNamedParams = kFlagBit1;
const int kFlagHasTypeParams = kFlagBit2;
// 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;
const intptr_t kind = (header >> kKindShift) & kKindMask;
const intptr_t flags = header & kFlagsMask;
switch (kind) {
case kInvalid:
UNREACHABLE();
break;
case kLibrary: {
const String& uri = String::Handle(Z, ReadString());
RawLibrary* library = Library::LookupLibrary(H.thread(), uri);
if (library == Library::null()) {
FATAL1("Unable to find library %s", uri.ToCString());
}
return library;
}
case kClass: {
const Library& library = Library::CheckedHandle(Z, ReadObject());
const String& class_name = String::CheckedHandle(Z, ReadObject());
if (class_name.raw() == Symbols::Empty().raw()) {
return library.toplevel_class();
}
RawClass* cls = library.LookupClassAllowPrivate(class_name);
if (cls == Class::null()) {
FATAL2("Unable to find class %s in %s", class_name.ToCString(),
library.ToCString());
}
return cls;
}
case kMember: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
String& name = String::CheckedHandle(Z, ReadObject());
if ((flags & kFlagIsField) != 0) {
RawField* field = cls.LookupFieldAllowPrivate(name);
if (field == Field::null()) {
FATAL2("Unable to find field %s in %s", name.ToCString(),
cls.ToCString());
}
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.LookupFunctionAllowPrivate(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.LookupFunctionAllowPrivate(method_name);
if (function != Function::null()) {
function = Function::Handle(Z, function).GetMethodExtractor(name);
if (function != Function::null()) {
return function;
}
}
}
FATAL2("Unable to find function %s in %s", name.ToCString(),
cls.ToCString());
}
return function;
}
}
case kClosure: {
ReadObject(); // Skip enclosing member.
const intptr_t closure_index = helper_->reader_.ReadUInt();
return closures_->At(closure_index);
}
case kSimpleType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
if ((flags & kFlagIsDynamic) != 0) {
ASSERT(cls.IsNull());
return AbstractType::dynamic_type().raw();
}
if ((flags & kFlagIsVoid) != 0) {
ASSERT(cls.IsNull());
return AbstractType::void_type().raw();
}
return cls.DeclarationType();
}
case kTypeParameter: {
Object& parent = Object::Handle(Z, ReadObject());
const intptr_t index_in_parent = helper_->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();
}
AbstractType& type =
AbstractType::Handle(Z, type_parameters.TypeAt(index_in_parent));
// TODO(alexmarkov): figure out how to skip this type finalization
// (consider finalizing type parameters of classes/functions eagerly).
return ClassFinalizer::FinalizeType(*active_class_->klass, type);
}
case kGenericType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
const TypeArguments& type_arguments =
TypeArguments::Handle(Z, ReadTypeArguments(Class::Handle(Z)));
const Type& type = Type::Handle(
Z, Type::New(cls, type_arguments, TokenPosition::kNoSource));
return ClassFinalizer::FinalizeType(*active_class_->klass, type);
}
case kFunctionType: {
Function& signature_function = Function::ZoneHandle(
Z, Function::NewSignatureFunction(*active_class_->klass,
active_class_->enclosing != NULL
? *active_class_->enclosing
: Function::null_function(),
TokenPosition::kNoSource));
return ReadFunctionSignature(
signature_function, (flags & kFlagHasOptionalPositionalParams) != 0,
(flags & kFlagHasOptionalNamedParams) != 0,
(flags & kFlagHasTypeParams) != 0,
/* has_positional_param_names = */ false);
}
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(Class::Handle(Z));
}
case kFinalizedGenericType: {
const Class& cls = Class::CheckedHandle(Z, ReadObject());
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 kConstObject: {
const intptr_t tag = flags / kFlagBit0;
return ReadConstObject(tag);
}
case kArgDesc: {
const intptr_t num_arguments = helper_->ReadUInt();
const intptr_t num_type_args =
((flags & kFlagHasTypeArgs) != 0) ? helper_->ReadUInt() : 0;
if ((flags & kFlagHasNamedArgs) == 0) {
return ArgumentsDescriptor::New(num_type_args, num_arguments);
} else {
const intptr_t num_arg_names = helper_->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);
}
}
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 = helper_->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 = helper_->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 = helper_->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 = helper_->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 = helper_->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();
}
RawString* BytecodeReaderHelper::ReadString(bool is_canonical) {
const int kFlagTwoByteString = 1;
const int kHeaderFields = 2;
const int kUInt32Size = 4;
uint32_t ref = helper_->reader_.ReadUInt();
const bool isOneByteString = (ref & kFlagTwoByteString) == 0;
intptr_t index = ref >> 1;
if (!isOneByteString) {
const uint32_t num_one_byte_strings = helper_->reader_.ReadUInt32At(
bytecode_component_->GetStringsHeaderOffset());
index += num_one_byte_strings;
}
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
bytecode_component_->GetStringsHeaderOffset() +
(kHeaderFields + index - 1) * kUInt32Size);
intptr_t start_offs = helper_->ReadUInt32();
intptr_t end_offs = helper_->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 = helper_->reader_.BufferAt(
bytecode_component_->GetStringsContentsOffset() + start_offs);
if (is_canonical) {
if (isOneByteString) {
return Symbols::FromLatin1(H.thread(), data, end_offs - start_offs);
} else {
return Symbols::FromUTF16(H.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);
}
}
}
RawTypeArguments* BytecodeReaderHelper::ReadTypeArguments(
const Class& instantiator) {
const intptr_t length = helper_->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);
}
type_arguments = type_arguments.Canonicalize();
if (instantiator.IsNull()) {
return type_arguments.raw();
}
if (type_arguments.IsNull() && instantiator.NumTypeArguments() == length) {
return type_arguments.raw();
}
// We make a temporary [Type] object and use `ClassFinalizer::FinalizeType` to
// finalize the argument types.
// (This can for example make the [type_arguments] vector larger)
type = Type::New(instantiator, type_arguments, TokenPosition::kNoSource);
type ^= ClassFinalizer::FinalizeType(*active_class_->klass, type);
return type.arguments();
}
void BytecodeReaderHelper::ReadMembers(const Class& cls,
intptr_t members_offset,
bool discard_fields) {
ASSERT(Thread::Current()->IsMutatorThread());
ASSERT(cls.is_type_finalized());
ASSERT(!cls.is_loaded());
const intptr_t offset =
bytecode_component_->GetMembersOffset() + members_offset;
AlternativeReadingScope alt_md(&helper_->reader_, &H.metadata_payloads(),
offset);
const intptr_t num_functions = helper_->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 num_fields = helper_->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 = helper_->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;
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 = helper_->ReadPosition();
end_position = helper_->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);
if (!has_initializer) {
value ^= ReadObject();
if (is_static) {
field.SetStaticValue(value, true);
} else {
// 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);
}
}
}
}
if (has_initializer && is_static) {
const intptr_t code_offset = helper_->reader_.ReadUInt();
field.set_bytecode_offset(code_offset +
bytecode_component_->GetCodesOffset());
field.SetStaticValue(Object::sentinel(), true);
}
if ((flags & kHasGetterFlag) != 0) {
name ^= ReadObject();
function =
Function::New(name,
is_static ? RawFunction::kImplicitStaticFinalGetter
: 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);
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);
H.SetupFieldAccessorFunction(cls, function, type);
functions_->SetAt(function_index_++, function);
}
if ((flags & kHasAnnotationsFlag) != 0) {
intptr_t annotations_offset = helper_->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.
Thread* thread = H.thread();
NoOOBMessageScope no_msg_scope(thread);
NoReloadScope no_reload_scope(thread->isolate(), thread);
library.GetMetadata(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 intptr_t num_functions = helper_->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 = helper_->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;
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 = helper_->ReadPosition();
end_position = helper_->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);
}
// Expression evaluation functions are not supported yet.
ASSERT(!name.Equals(Symbols::DebugProcedureName()));
function = Function::New(name, kind, is_static, (flags & kIsConstFlag) != 0,
(flags & kIsAbstractFlag) != 0,
(flags & kIsExternalFlag) != 0, is_native,
script_class, position);
// 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);
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 + helper_->reader_.ReadUInt();
intptr_t num_required_params = num_params;
if ((flags & (kHasOptionalPositionalParamsFlag |
kHasOptionalNamedParamsFlag)) != 0) {
num_required_params = num_implicit_params + helper_->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) {
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 = helper_->reader_.ReadUInt();
function.set_bytecode_offset(code_offset +
bytecode_component_->GetCodesOffset());
}
if ((flags & kHasAnnotationsFlag) != 0) {
const intptr_t annotations_offset =
helper_->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);
}
}
}
}
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::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(&helper_->reader_, &H.metadata_payloads(),
function.bytecode_offset());
const intptr_t code_flags = helper_->reader_.ReadUInt();
if ((code_flags & Code::kHasParameterFlagsFlag) != 0) {
const intptr_t num_explicit_params = helper_->reader_.ReadUInt();
ASSERT(num_params ==
function.NumImplicitParameters() + num_explicit_params);
for (intptr_t i = function.NumImplicitParameters(); i < num_params; ++i) {
const intptr_t flags = helper_->reader_.ReadUInt();
if ((flags & Parameter::kIsCovariantFlag) != 0) {
is_covariant->Add(i);
}
if ((flags & Parameter::kIsGenericCovariantImplFlag) != 0) {
is_generic_covariant_impl->Add(i);
}
}
}
}
void BytecodeReaderHelper::ParseBytecodeFunction(
ParsedFunction* parsed_function,
const Function& function) {
switch (function.kind()) {
case RawFunction::kImplicitClosureFunction:
ParseForwarderFunction(parsed_function, function,
Function::Handle(Z, function.parent_function()));
break;
case RawFunction::kDynamicInvocationForwarder:
ParseForwarderFunction(
parsed_function, function,
Function::Handle(Z,
function.GetTargetOfDynamicInvocationForwarder()));
break;
case RawFunction::kImplicitGetter:
case RawFunction::kImplicitSetter:
case RawFunction::kImplicitStaticFinalGetter:
if (IsBytecodeFieldInitializer(function, Z)) {
ReadCode(function, function.bytecode_offset());
} else {
BytecodeScopeBuilder(parsed_function).BuildScopes();
}
break;
case RawFunction::kMethodExtractor:
BytecodeScopeBuilder(parsed_function).BuildScopes();
break;
case RawFunction::kRegularFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
case RawFunction::kClosureFunction:
case RawFunction::kConstructor:
ReadCode(function, function.bytecode_offset());
break;
case RawFunction::kNoSuchMethodDispatcher:
case RawFunction::kInvokeFieldDispatcher:
case RawFunction::kSignatureFunction:
case RawFunction::kIrregexpFunction:
case RawFunction::kFfiTrampoline:
UNREACHABLE();
break;
}
}
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(&helper_->reader_, &H.metadata_payloads(),
target.bytecode_offset());
const intptr_t flags = helper_->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) {
const intptr_t num_params = helper_->reader_.ReadUInt();
const intptr_t num_implicit_params = function.NumImplicitParameters();
for (intptr_t i = 0; i < num_params; ++i) {
const intptr_t flags = helper_->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;
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 = helper_->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 = helper_->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<KBCInstr*>(target_bytecode.PayloadStart());
KBCInstr entry = raw_bytecode[0];
ASSERT(KernelBytecode::DecodeOpcode(entry) ==
KernelBytecode::kEntryOptional);
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[1 + i];
ASSERT(KernelBytecode::DecodeOpcode(load) ==
KernelBytecode::kLoadConstant);
const auto& value = Instance::CheckedZoneHandle(
Z, obj_pool.ObjectAt(KernelBytecode::DecodeD(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[1 + 2 * i];
const KBCInstr load_value = raw_bytecode[1 + 2 * i + 1];
ASSERT(KernelBytecode::DecodeOpcode(load_name) ==
KernelBytecode::kLoadConstant);
ASSERT(KernelBytecode::DecodeOpcode(load_value) ==
KernelBytecode::kLoadConstant);
param_name ^= obj_pool.ObjectAt(KernelBytecode::DecodeD(load_name));
const auto& value = Instance::CheckedZoneHandle(
Z, obj_pool.ObjectAt(KernelBytecode::DecodeD(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::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::GetMembersOffset() const {
return Smi::Value(Smi::RawCast(data_.At(kMembersOffset)));
}
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::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 objects_contents_offset,
intptr_t main_offset,
intptr_t members_offset,
intptr_t codes_offset,
intptr_t source_positions_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(objects_contents_offset);
data.SetAt(kObjectsContentsOffset, smi_handle);
smi_handle = Smi::New(main_offset);
data.SetAt(kMainOffset, smi_handle);
smi_handle = Smi::New(members_offset);
data.SetAt(kMembersOffset, 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(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);
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
StackZone stack_zone(thread);
Zone* const zone = stack_zone.GetZone();
HANDLESCOPE(thread);
CompilerState compiler_state(thread);
const Script& script = Script::Handle(zone, function.script());
TranslationHelper translation_helper(thread);
translation_helper.InitFromScript(script);
KernelReaderHelper reader_helper(
zone, &translation_helper, script,
ExternalTypedData::Handle(zone, function.KernelData()),
function.KernelDataProgramOffset());
ActiveClass active_class;
BytecodeMetadataHelper bytecode_metadata_helper(&reader_helper,
&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);
ActiveTypeParametersScope active_type_params(&active_class, function, zone);
bytecode_metadata_helper.ReadMetadata(function);
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);
KernelReaderHelper reader_helper(
zone, &translation_helper, script,
ExternalTypedData::Handle(zone, annotation_field.KernelData()),
annotation_field.KernelDataProgramOffset());
ActiveClass active_class;
BytecodeMetadataHelper bytecode_metadata_helper(&reader_helper,
&active_class);
return bytecode_metadata_helper.ReadAnnotation(
annotation_field.bytecode_offset());
}
bool IsBytecodeFieldInitializer(const Function& function, Zone* zone) {
if (IsFieldInitializer(function, zone)) {
return true;
}
if (function.kind() == RawFunction::kImplicitStaticFinalGetter) {
const auto& field = Field::Handle(zone, function.accessor_field());
return field.is_declared_in_bytecode() && field.is_const() &&
field.has_initializer();
}
return false;
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)