Files
sdk/runtime/vm/compiler/frontend/bytecode_reader.cc
T
Alexander Markov 62d154f6a3 [vm/bytecode] Record null-initialized fields in bytecode
If a field is initialized with null (either explicitly or implicitly),
field store can be omitted in bytecode. In such case, bytecode should
still convey the information about this initialization to VM for
field guards to work correctly.

Change-Id: I1fd45b858c3c521b97fa5dbffe0e15b1ea75d92f
Reviewed-on: https://dart-review.googlesource.com/76060
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
2018-09-24 17:56:35 +00:00

802 lines
33 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/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/code_descriptors.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/constants_kbc.h"
#include "vm/dart_entry.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/timeline.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
#define Z (zone_)
#define H (translation_helper_)
#define T (type_translator_)
#define I Isolate::Current()
namespace dart {
DEFINE_FLAG(bool, dump_kernel_bytecode, false, "Dump kernel bytecode");
namespace kernel {
BytecodeMetadataHelper::BytecodeMetadataHelper(KernelReaderHelper* helper,
TypeTranslator* type_translator,
ActiveClass* active_class)
: MetadataHelper(helper, tag(), /* precompiler_only = */ false),
type_translator_(*type_translator),
active_class_(active_class) {}
void BytecodeMetadataHelper::ReadMetadata(const Function& function) {
#if !defined(PRODUCT)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadMetadata");
#endif // !defined(PRODUCT)
const intptr_t node_offset = function.kernel_offset();
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return;
}
AlternativeReadingScope alt(&helper_->reader_, &H.metadata_payloads(),
md_offset);
const int kHasExceptionsTableFlag = 1 << 0;
const int kHasNullableFieldsFlag = 1 << 1;
const int kHasClosuresFlag = 1 << 2;
const intptr_t flags = helper_->reader_.ReadUInt();
const bool has_exceptions_table = (flags & kHasExceptionsTableFlag) != 0;
const bool has_nullable_fields = (flags & kHasNullableFieldsFlag) != 0;
const bool has_closures = (flags & kHasClosuresFlag) != 0;
// Create object pool and read pool entries.
const intptr_t obj_count = helper_->reader_.ReadListLength();
const ObjectPool& pool =
ObjectPool::Handle(helper_->zone_, 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);
ReadPoolEntries(function, function, pool, 0);
}
// Read bytecode and attach to function.
const Code& bytecode = Code::Handle(helper_->zone_, ReadBytecode(pool));
function.AttachBytecode(bytecode);
// Read exceptions table.
ReadExceptionsTable(bytecode, has_exceptions_table);
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(helper_->zone_);
for (intptr_t i = 0; i < num_fields; i++) {
NameIndex name_index = helper_->ReadCanonicalNameReference();
field = H.LookupFieldByKernelField(name_index);
field.RecordStore(Object::null_object());
}
} else {
for (intptr_t i = 0; i < num_fields; i++) {
helper_->SkipCanonicalNameReference();
}
}
}
// Read closures.
if (has_closures) {
Function& closure = Function::Handle(helper_->zone_);
Code& closure_bytecode = Code::Handle(helper_->zone_);
const intptr_t num_closures = helper_->ReadListLength();
for (intptr_t i = 0; i < num_closures; i++) {
intptr_t closure_index = helper_->ReadUInt();
ASSERT(closure_index < obj_count);
closure ^= pool.ObjectAt(closure_index);
// Read closure bytecode and attach to closure function.
closure_bytecode = ReadBytecode(pool);
closure.AttachBytecode(closure_bytecode);
// Read closure exceptions table.
ReadExceptionsTable(closure_bytecode);
if (FLAG_dump_kernel_bytecode) {
KernelBytecodeDisassembler::Disassemble(closure);
}
}
}
}
intptr_t BytecodeMetadataHelper::ReadPoolEntries(const Function& function,
const Function& inner_function,
const ObjectPool& pool,
intptr_t from_index) {
#if !defined(PRODUCT)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadPoolEntries");
#endif // !defined(PRODUCT)
// 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,
kString,
kInt,
kDouble,
kBool,
kArgDesc,
kICData,
kStaticICData,
kStaticField,
kInstanceField,
kClass,
kTypeArgumentsField,
kTearOff,
kType,
kTypeArguments,
kList,
kInstance,
kTypeArgumentsForInstanceAllocation,
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
kPartialTearOffInstantiation,
kEmptyTypeArguments,
kSymbol,
};
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(helper_->zone_);
Object& elem = Object::Handle(helper_->zone_);
Array& array = Array::Handle(helper_->zone_);
Field& field = Field::Handle(helper_->zone_);
Class& cls = Class::Handle(helper_->zone_);
Library& lib = Library::Handle(helper_->zone_);
String& name = String::Handle(helper_->zone_);
TypeArguments& type_args = TypeArguments::Handle(helper_->zone_);
Class* symbol_class = nullptr;
Field* symbol_name_field = nullptr;
const intptr_t obj_count = pool.Length();
for (intptr_t i = from_index; 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 = H.DartString(helper_->ReadStringReference()).raw();
ASSERT(obj.IsString());
obj = H.Canonicalize(String::Cast(obj));
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_->ReadUInt() == 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++) {
array.SetAt(j, H.DartSymbolPlain(helper_->ReadStringReference()));
}
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;
if (kind == InvocationKind::getter) {
name = helper_->ReadNameAsGetterName().raw();
} else if (kind == InvocationKind::setter) {
name = helper_->ReadNameAsSetterName().raw();
} else {
ASSERT(kind == InvocationKind::method);
name = helper_->ReadNameAsMethodName().raw();
}
intptr_t arg_desc_index = helper_->ReadUInt();
ASSERT(arg_desc_index < i);
array ^= pool.ObjectAt(arg_desc_index);
intptr_t checked_argument_count = 1;
if ((kind == InvocationKind::method) &&
(MethodTokenRecognizer::RecognizeTokenKind(name) !=
Token::kILLEGAL)) {
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);
#if defined(TAG_IC_DATA)
ICData::Cast(obj).set_tag(ICData::Tag::kInstanceCall);
#endif
} break;
case ConstantPoolTag::kStaticICData: {
InvocationKind kind = static_cast<InvocationKind>(helper_->ReadByte());
NameIndex target = helper_->ReadCanonicalNameReference();
if (H.IsConstructor(target)) {
name = H.DartConstructorName(target).raw();
elem = H.LookupConstructorByKernelConstructor(target);
} else if (H.IsField(target)) {
if (kind == InvocationKind::getter) {
name = H.DartGetterName(target).raw();
} else if (kind == InvocationKind::setter) {
name = H.DartSetterName(target).raw();
} else {
ASSERT(kind == InvocationKind::method);
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
field = H.LookupFieldByKernelField(target);
cls = field.Owner();
elem = cls.LookupFunctionAllowPrivate(name);
} else {
if ((kind == InvocationKind::method) && H.IsGetter(target)) {
UNIMPLEMENTED(); // TODO(regis): Revisit.
}
name = H.DartProcedureName(target).raw();
elem = H.LookupStaticMethodByKernelProcedure(target);
if ((kind == InvocationKind::getter) && !H.IsGetter(target)) {
// Tear-off
name = H.DartGetterName(target).raw();
elem = Function::Cast(elem).GetMethodExtractor(name);
}
}
const int num_args_checked =
MethodRecognizer::NumArgsCheckedForStaticCall(Function::Cast(elem));
ASSERT(elem.IsFunction());
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));
#if defined(TAG_IC_DATA)
ICData::Cast(obj).set_tag(ICData::Tag::kStaticCall);
#endif
} break;
case ConstantPoolTag::kStaticField:
obj = H.LookupFieldByKernelField(helper_->ReadCanonicalNameReference());
ASSERT(obj.IsField());
break;
case ConstantPoolTag::kInstanceField:
field =
H.LookupFieldByKernelField(helper_->ReadCanonicalNameReference());
// InstanceField constant occupies 2 entries.
// The first entry is used for field offset.
obj = Smi::New(field.Offset() / kWordSize);
pool.SetTypeAt(i, ObjectPool::kTaggedObject, ObjectPool::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 = H.LookupClassByKernelClass(helper_->ReadCanonicalNameReference());
ASSERT(obj.IsClass());
break;
case ConstantPoolTag::kTypeArgumentsField:
cls = H.LookupClassByKernelClass(helper_->ReadCanonicalNameReference());
obj = Smi::New(cls.type_arguments_field_offset() / kWordSize);
break;
case ConstantPoolTag::kTearOff:
obj = H.LookupStaticMethodByKernelProcedure(
helper_->ReadCanonicalNameReference());
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 = type_translator_.BuildType().raw();
ASSERT(obj.IsAbstractType());
break;
case ConstantPoolTag::kTypeArguments:
obj = type_translator_.BuildTypeArguments(helper_->ReadListLength())
.raw();
ASSERT(obj.IsNull() || obj.IsTypeArguments());
break;
case ConstantPoolTag::kList: {
obj = type_translator_.BuildType().raw();
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 = H.LookupClassByKernelClass(helper_->ReadCanonicalNameReference());
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++) {
NameIndex field_name = helper_->ReadCanonicalNameReference();
ASSERT(H.IsField(field_name));
field = H.LookupFieldByKernelField(field_name);
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 = H.LookupClassByKernelClass(helper_->ReadCanonicalNameReference());
obj =
type_translator_
.BuildInstantiatedTypeArguments(cls, helper_->ReadListLength())
.raw();
ASSERT(obj.IsNull() || obj.IsTypeArguments());
} break;
case ConstantPoolTag::kClosureFunction: {
name = H.DartSymbolPlain(helper_->ReadStringReference()).raw();
const Function& closure = Function::Handle(
helper_->zone_,
Function::NewClosureFunction(name, inner_function,
TokenPosition::kNoSource));
FunctionNodeHelper function_node_helper(helper_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kTypeParameters);
type_translator_.LoadAndSetupTypeParameters(
active_class_, closure, helper_->ReadListLength(), closure);
function_node_helper.SetJustRead(FunctionNodeHelper::kTypeParameters);
// Scope remains opened until ConstantPoolTag::kEndClosureFunctionScope.
ActiveTypeParametersScope scope(
active_class_, &closure,
TypeArguments::Handle(helper_->zone_, closure.type_parameters()),
helper_->zone_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t required_parameter_count =
function_node_helper.required_parameter_count_;
intptr_t total_parameter_count =
function_node_helper.total_parameter_count_;
intptr_t positional_parameter_count = helper_->ReadListLength();
intptr_t named_parameter_count =
total_parameter_count - positional_parameter_count;
const intptr_t extra_parameters = 1;
closure.set_num_fixed_parameters(extra_parameters +
required_parameter_count);
if (named_parameter_count > 0) {
closure.SetNumOptionalParameters(named_parameter_count, false);
} else {
closure.SetNumOptionalParameters(
positional_parameter_count - required_parameter_count, true);
}
intptr_t parameter_count = extra_parameters + total_parameter_count;
closure.set_parameter_types(Array::Handle(
helper_->zone_, Array::New(parameter_count, Heap::kOld)));
closure.set_parameter_names(Array::Handle(
helper_->zone_, Array::New(parameter_count, Heap::kOld)));
intptr_t pos = 0;
closure.SetParameterTypeAt(pos, AbstractType::dynamic_type());
closure.SetParameterNameAt(pos, Symbols::ClosureParameter());
pos++;
lib = active_class_->klass->library();
for (intptr_t j = 0; j < positional_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(helper_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type = type_translator_.BuildVariableType();
Tag tag = helper_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
helper_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartIdentifier(lib, helper.name_index_));
}
intptr_t named_parameter_count_check = helper_->ReadListLength();
ASSERT(named_parameter_count_check == named_parameter_count);
for (intptr_t j = 0; j < named_parameter_count; ++j, ++pos) {
VariableDeclarationHelper helper(helper_);
helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
const AbstractType& type = type_translator_.BuildVariableType();
Tag tag = helper_->ReadTag(); // read (first part of) initializer.
if (tag == kSomething) {
helper_->SkipExpression(); // read (actual) initializer.
}
closure.SetParameterTypeAt(pos, type);
closure.SetParameterNameAt(pos,
H.DartIdentifier(lib, helper.name_index_));
}
function_node_helper.SetJustRead(FunctionNodeHelper::kNamedParameters);
const AbstractType& return_type = type_translator_.BuildVariableType();
closure.set_result_type(return_type);
function_node_helper.SetJustRead(FunctionNodeHelper::kReturnType);
// The closure has no body.
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
// Finalize function type.
Type& signature_type =
Type::Handle(helper_->zone_, closure.SignatureType());
signature_type ^= ClassFinalizer::FinalizeType(*(active_class_->klass),
signature_type);
closure.SetSignatureType(signature_type);
pool.SetTypeAt(i, ObjectPool::kTaggedObject, ObjectPool::kNotPatchable);
pool.SetObjectAt(i, closure);
// Continue reading the constant pool entries inside the opened
// ActiveTypeParametersScope until the scope gets closed by a
// kEndClosureFunctionScope tag, in which case control returns here.
i = ReadPoolEntries(function, closure, pool, i + 1);
// Pool entry at index i has been set to null, because it was a
// kEndClosureFunctionScope.
ASSERT(pool.ObjectAt(i) == Object::null());
continue;
}
case ConstantPoolTag::kEndClosureFunctionScope: {
// Entry is not used and set to null.
obj = Object::null();
pool.SetTypeAt(i, ObjectPool::kTaggedObject, ObjectPool::kNotPatchable);
pool.SetObjectAt(i, obj);
return i; // The caller will close the scope.
} break;
case ConstantPoolTag::kNativeEntry: {
name = H.DartString(helper_->ReadStringReference()).raw();
obj = NativeEntry(function, name);
} break;
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(
helper_->zone_, 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(helper_->zone_,
old_closure.instantiator_type_arguments()),
TypeArguments::Handle(helper_->zone_,
old_closure.function_type_arguments()),
type_args, Function::Handle(helper_->zone_, old_closure.function()),
Context::Handle(helper_->zone_, 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: {
const NameIndex lib_index = helper_->ReadCanonicalNameReference();
lib = Library::null();
if (!H.IsRoot(lib_index)) {
lib = H.LookupLibraryByKernelLibrary(lib_index);
}
const String& symbol =
H.DartIdentifier(lib, helper_->ReadStringReference());
if (symbol_class == nullptr) {
elem = Library::InternalLibrary();
ASSERT(!elem.IsNull());
symbol_class = &Class::Handle(
helper_->zone_,
Library::Cast(elem).LookupClass(Symbols::Symbol()));
ASSERT(!symbol_class->IsNull());
symbol_name_field = &Field::Handle(
helper_->zone_,
symbol_class->LookupInstanceFieldAllowPrivate(Symbols::_name()));
ASSERT(!symbol_name_field->IsNull());
}
obj = Instance::New(*symbol_class, Heap::kOld);
Instance::Cast(obj).SetField(*symbol_name_field, symbol);
obj = H.Canonicalize(Instance::Cast(obj));
} break;
default:
UNREACHABLE();
}
pool.SetTypeAt(i, ObjectPool::kTaggedObject, ObjectPool::kNotPatchable);
pool.SetObjectAt(i, obj);
}
// Return the index of the last read pool entry.
return obj_count - 1;
}
RawCode* BytecodeMetadataHelper::ReadBytecode(const ObjectPool& pool) {
#if !defined(PRODUCT)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadBytecode");
#endif // !defined(PRODUCT)
intptr_t size = helper_->reader_.ReadUInt();
intptr_t offset = helper_->reader_.offset();
const uint8_t* data = helper_->reader_.BufferAt(offset);
helper_->reader_.set_offset(offset + size);
// Create and return code object.
return Code::FinalizeBytecode(reinterpret_cast<const void*>(data), size,
pool);
}
void BytecodeMetadataHelper::ReadExceptionsTable(const Code& bytecode,
bool has_exceptions_table) {
#if !defined(PRODUCT)
TimelineDurationScope tds(Thread::Current(), Timeline::GetCompilerStream(),
"BytecodeMetadataHelper::ReadExceptionsTable");
#endif // !defined(PRODUCT)
const intptr_t try_block_count =
has_exceptions_table ? helper_->reader_.ReadListLength() : 0;
if (try_block_count > 0) {
const ObjectPool& pool =
ObjectPool::Handle(helper_->zone_, bytecode.object_pool());
AbstractType& handler_type = AbstractType::Handle(helper_->zone_);
Array& handler_types = Array::ZoneHandle(helper_->zone_);
DescriptorList* pc_descriptors_list =
new (helper_->zone_) DescriptorList(64);
ExceptionHandlerList* exception_handlers_list =
new (helper_->zone_) 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(
helper_->zone_,
pc_descriptors_list->FinalizePcDescriptors(bytecode.PayloadStart()));
bytecode.set_pc_descriptors(descriptors);
const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
helper_->zone_, 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());
}
}
RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function,
const String& external_name) {
Zone* zone = helper_->zone_;
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::kTypedDataLength:
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) {
trampoline = &NativeEntry::BootstrapNativeCallWrapper;
native_function =
reinterpret_cast<NativeFunction>(&NativeEntry::LinkNativeCall);
} else {
const Class& cls = Class::Handle(zone, function.Owner());
const Library& library = Library::Handle(zone, 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);
ASSERT(native_function != NULL); // TODO(regis): Should we throw instead?
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);
}
RawError* BytecodeReader::ReadFunctionBytecode(Thread* thread,
const Function& function) {
ASSERT(!FLAG_precompiled_mode);
ASSERT(!function.HasBytecode());
ASSERT(thread->sticky_error() == Error::null());
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;
TypeTranslator type_translator(&reader_helper, &active_class,
/* finalize= */ true);
BytecodeMetadataHelper bytecode_metadata_helper(
&reader_helper, &type_translator, &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 {
StackZone stack_zone(thread);
Error& error = Error::Handle();
// We got an error during bytecode reading.
error = thread->sticky_error();
thread->clear_sticky_error();
return error.raw();
}
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)