blob: 587eb27f41de460a1782a4cbf5a57ceb5a0a41b5 [file] [log] [blame]
// 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) {}
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(PRODUCT)
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(PRODUCT)
const intptr_t node_offset = function.kernel_offset();
const intptr_t md_offset = GetNextMetadataPayloadOffset(node_offset);
if (md_offset < 0) {
return;
}
ASSERT(Thread::Current()->IsMutatorThread());
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 String* simpleInstanceOf = 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);
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);
#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);
pool.SetTypeAt(i, ObjectPool::kNativeEntryData,
ObjectPool::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(
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) {
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());
ASSERT(Thread::Current()->IsMutatorThread());
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)