blob: 17f062d74445dad7483ae64d53a06f298080e628 [file] [log] [blame]
// Copyright (c) 2016, 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/kernel_loader.h"
#include <string.h>
#include "vm/compiler/frontend/constant_evaluator.h"
#include "vm/compiler/frontend/kernel_translation_helper.h"
#include "vm/dart_api_impl.h"
#include "vm/flags.h"
#include "vm/kernel_binary.h"
#include "vm/longjump.h"
#include "vm/object_store.h"
#include "vm/parser.h"
#include "vm/reusable_handles.h"
#include "vm/service_isolate.h"
#include "vm/symbols.h"
#include "vm/thread.h"
#if !defined(DART_PRECOMPILED_RUNTIME)
namespace dart {
namespace kernel {
#define Z (zone_)
#define I (isolate_)
#define T (type_translator_)
#define H (translation_helper_)
static const char* const kVMServiceIOLibraryUri = "dart:vmservice_io";
class SimpleExpressionConverter {
public:
SimpleExpressionConverter(TranslationHelper* translation_helper,
KernelReaderHelper* reader_helper)
: translation_helper_(*translation_helper),
zone_(translation_helper_.zone()),
simple_value_(NULL),
helper_(reader_helper) {}
bool IsSimple(intptr_t kernel_offset) {
AlternativeReadingScope alt(&helper_->reader_, kernel_offset);
uint8_t payload = 0;
Tag tag = helper_->ReadTag(&payload); // read tag.
switch (tag) {
case kBigIntLiteral: {
const String& literal_str =
H.DartString(helper_->ReadStringReference(),
Heap::kOld); // read index into string table.
simple_value_ = &Integer::ZoneHandle(Z, Integer::New(literal_str));
if (simple_value_->IsNull()) {
H.ReportError("Integer literal %s is out of range",
literal_str.ToCString());
UNREACHABLE();
}
*simple_value_ = H.Canonicalize(*simple_value_);
return true;
}
case kStringLiteral:
simple_value_ = &H.DartSymbolPlain(
helper_->ReadStringReference()); // read index into string table.
return true;
case kSpecializedIntLiteral:
simple_value_ =
&Integer::ZoneHandle(Z, Integer::New(static_cast<int32_t>(payload) -
SpecializedIntLiteralBias,
Heap::kOld));
*simple_value_ = H.Canonicalize(*simple_value_);
return true;
case kNegativeIntLiteral:
simple_value_ = &Integer::ZoneHandle(
Z, Integer::New(-static_cast<int64_t>(helper_->ReadUInt()),
Heap::kOld)); // read value.
*simple_value_ = H.Canonicalize(*simple_value_);
return true;
case kPositiveIntLiteral:
simple_value_ = &Integer::ZoneHandle(
Z, Integer::New(static_cast<int64_t>(helper_->ReadUInt()),
Heap::kOld)); // read value.
*simple_value_ = H.Canonicalize(*simple_value_);
return true;
case kDoubleLiteral:
simple_value_ = &Double::ZoneHandle(
Z, Double::New(helper_->ReadDouble(), Heap::kOld)); // read value.
*simple_value_ = H.Canonicalize(*simple_value_);
return true;
case kTrueLiteral:
simple_value_ = &Bool::Handle(Z, Bool::Get(true).raw());
return true;
case kFalseLiteral:
simple_value_ = &Bool::Handle(Z, Bool::Get(false).raw());
return true;
case kNullLiteral:
simple_value_ = &Instance::ZoneHandle(Z, Instance::null());
return true;
default:
return false;
}
}
const Instance& SimpleValue() { return *simple_value_; }
Zone* zone() const { return zone_; }
private:
TranslationHelper& translation_helper_;
Zone* zone_;
Instance* simple_value_;
KernelReaderHelper* helper_;
DISALLOW_COPY_AND_ASSIGN(SimpleExpressionConverter);
};
RawArray* KernelLoader::MakeFunctionsArray() {
const intptr_t len = functions_.length();
const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld));
for (intptr_t i = 0; i < len; i++) {
res.SetAt(i, *functions_[i]);
}
return res.raw();
}
RawLibrary* BuildingTranslationHelper::LookupLibraryByKernelLibrary(
NameIndex library) {
return loader_->LookupLibrary(library).raw();
}
RawClass* BuildingTranslationHelper::LookupClassByKernelClass(NameIndex klass) {
return loader_->LookupClass(klass).raw();
}
LibraryIndex::LibraryIndex(const ExternalTypedData& kernel_data)
: reader_(kernel_data) {
intptr_t data_size = reader_.size();
procedure_count_ = reader_.ReadUInt32At(data_size - 4);
procedure_index_offset_ = data_size - 4 - (procedure_count_ + 1) * 4;
class_count_ = reader_.ReadUInt32At(procedure_index_offset_ - 4);
class_index_offset_ = procedure_index_offset_ - 4 - (class_count_ + 1) * 4;
}
ClassIndex::ClassIndex(const uint8_t* buffer,
intptr_t buffer_size,
intptr_t class_offset,
intptr_t class_size)
: reader_(buffer, buffer_size) {
Init(class_offset, class_size);
}
ClassIndex::ClassIndex(const ExternalTypedData& library_kernel_data,
intptr_t class_offset,
intptr_t class_size)
: reader_(library_kernel_data) {
Init(class_offset, class_size);
}
void ClassIndex::Init(intptr_t class_offset, intptr_t class_size) {
procedure_count_ = reader_.ReadUInt32At(class_offset + class_size - 4);
procedure_index_offset_ =
class_offset + class_size - 4 - (procedure_count_ + 1) * 4;
}
KernelLoader::KernelLoader(Program* program)
: program_(program),
thread_(Thread::Current()),
zone_(thread_->zone()),
isolate_(thread_->isolate()),
patch_classes_(Array::ZoneHandle(zone_)),
library_kernel_offset_(-1), // Set to the correct value in LoadLibrary
correction_offset_(-1), // Set to the correct value in LoadLibrary
loading_native_wrappers_library_(false),
library_kernel_data_(ExternalTypedData::ZoneHandle(zone_)),
kernel_program_info_(KernelProgramInfo::ZoneHandle(zone_)),
translation_helper_(this, thread_),
helper_(zone_,
&translation_helper_,
program_->kernel_data(),
program_->kernel_data_size(),
0),
type_translator_(&helper_, &active_class_, /* finalize= */ false),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
pragma_class_(Class::Handle(Z)),
expression_evaluation_library_(Library::Handle(Z)),
expression_evaluation_function_(Function::Handle(Z)) {
if (!program->is_single_program()) {
FATAL(
"Trying to load a concatenated dill file at a time where that is "
"not allowed");
}
InitializeFields();
}
Object& KernelLoader::LoadEntireProgram(Program* program,
bool process_pending_classes) {
if (program->is_single_program()) {
KernelLoader loader(program);
return Object::Handle(loader.LoadProgram(process_pending_classes));
}
kernel::Reader reader(program->kernel_data(), program->kernel_data_size());
GrowableArray<intptr_t> subprogram_file_starts;
index_programs(&reader, &subprogram_file_starts);
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
Library& library = Library::Handle(zone);
// Create "fake programs" for each sub-program.
intptr_t subprogram_count = subprogram_file_starts.length() - 1;
for (intptr_t i = 0; i < subprogram_count; ++i) {
intptr_t subprogram_start = subprogram_file_starts.At(i);
intptr_t subprogram_end = subprogram_file_starts.At(i + 1);
reader.set_raw_buffer(program->kernel_data() + subprogram_start);
reader.set_size(subprogram_end - subprogram_start);
reader.set_offset(0);
Program* subprogram = Program::ReadFrom(&reader);
ASSERT(subprogram->is_single_program());
KernelLoader loader(subprogram);
Object& load_result = Object::Handle(loader.LoadProgram(false));
if (load_result.IsError()) return load_result;
if (library.IsNull() && load_result.IsLibrary()) {
library ^= load_result.raw();
}
delete subprogram;
}
if (process_pending_classes && !ClassFinalizer::ProcessPendingClasses()) {
// Class finalization failed -> sticky error would be set.
Error& error = Error::Handle(zone);
error = thread->sticky_error();
thread->clear_sticky_error();
return error;
}
return library;
}
void KernelLoader::index_programs(
kernel::Reader* reader,
GrowableArray<intptr_t>* subprogram_file_starts) {
// Dill files can be concatenated (e.g. cat a.dill b.dill > c.dill), so we
// need to first index the (possibly combined) file.
// First entry becomes last entry.
// Last entry is for ease of calculating size of last subprogram.
subprogram_file_starts->Add(reader->size());
reader->set_offset(reader->size() - 4);
while (reader->offset() > 0) {
intptr_t size = reader->ReadUInt32();
intptr_t start = reader->offset() - size;
if (start < 0) {
FATAL("Invalid kernel binary: Indicated size is invalid.");
}
subprogram_file_starts->Add(start);
reader->set_offset(start - 4);
}
subprogram_file_starts->Reverse();
}
void KernelLoader::InitializeFields() {
const intptr_t source_table_size = helper_.SourceTableSize();
const Array& scripts =
Array::Handle(Z, Array::New(source_table_size, Heap::kOld));
patch_classes_ = Array::New(source_table_size, Heap::kOld);
// Copy the Kernel string offsets out of the binary and into the VM's heap.
ASSERT(program_->string_table_offset() >= 0);
Reader reader(program_->kernel_data(), program_->kernel_data_size());
reader.set_offset(program_->string_table_offset());
intptr_t count = reader.ReadUInt() + 1;
TypedData& offsets = TypedData::Handle(
Z, TypedData::New(kTypedDataUint32ArrayCid, count, Heap::kOld));
offsets.SetUint32(0, 0);
intptr_t end_offset = 0;
for (intptr_t i = 1; i < count; ++i) {
end_offset = reader.ReadUInt();
offsets.SetUint32(i << 2, end_offset);
}
// Create view of the string data.
const ExternalTypedData& data = ExternalTypedData::Handle(
Z,
reader.ExternalDataFromTo(reader.offset(), reader.offset() + end_offset));
// Create a view of the constants table. The trailing ComponentIndex is
// negligible in size.
const ExternalTypedData& constants_table = ExternalTypedData::Handle(
Z, reader.ExternalDataFromTo(program_->constant_table_offset(),
program_->kernel_data_size()));
// Copy the canonical names into the VM's heap. Encode them as unsigned, so
// the parent indexes are adjusted when extracted.
reader.set_offset(program_->name_table_offset());
count = reader.ReadUInt() * 2;
TypedData& names = TypedData::Handle(
Z, TypedData::New(kTypedDataUint32ArrayCid, count, Heap::kOld));
for (intptr_t i = 0; i < count; ++i) {
names.SetUint32(i << 2, reader.ReadUInt());
}
// Create view of metadata payloads.
const ExternalTypedData& metadata_payloads = ExternalTypedData::Handle(
Z, reader.ExternalDataFromTo(program_->metadata_payloads_offset(),
program_->metadata_mappings_offset()));
// Create view of metadata mappings.
const ExternalTypedData& metadata_mappings = ExternalTypedData::Handle(
Z, reader.ExternalDataFromTo(program_->metadata_mappings_offset(),
program_->string_table_offset()));
kernel_program_info_ =
KernelProgramInfo::New(offsets, data, names, metadata_payloads,
metadata_mappings, constants_table, scripts);
H.InitFromKernelProgramInfo(kernel_program_info_);
Script& script = Script::Handle(Z);
for (intptr_t index = 0; index < source_table_size; ++index) {
script = LoadScriptAt(index);
scripts.SetAt(index, script);
}
}
KernelLoader::KernelLoader(const Script& script,
const ExternalTypedData& kernel_data,
intptr_t data_program_offset)
: program_(NULL),
thread_(Thread::Current()),
zone_(thread_->zone()),
isolate_(thread_->isolate()),
patch_classes_(Array::ZoneHandle(zone_)),
library_kernel_offset_(data_program_offset),
correction_offset_(0),
loading_native_wrappers_library_(false),
library_kernel_data_(ExternalTypedData::ZoneHandle(zone_)),
kernel_program_info_(
KernelProgramInfo::ZoneHandle(zone_, script.kernel_program_info())),
translation_helper_(this, thread_),
helper_(zone_, &translation_helper_, script, kernel_data, 0),
type_translator_(&helper_, &active_class_, /* finalize= */ false),
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
pragma_class_(Class::Handle(Z)),
expression_evaluation_library_(Library::Handle(Z)),
expression_evaluation_function_(Function::Handle(Z)) {
ASSERT(T.active_class_ == &active_class_);
T.finalize_ = false;
const Array& scripts = Array::Handle(Z, kernel_program_info_.scripts());
patch_classes_ = Array::New(scripts.Length(), Heap::kOld);
library_kernel_data_ = kernel_data.raw();
H.InitFromKernelProgramInfo(kernel_program_info_);
}
const Array& KernelLoader::ReadConstantTable() {
// We use the very first library's toplevel class as an owner for an
// [ActiveClassScope]
//
// Though since constants cannot refer to types containing type parameter
// references, the only purpose of the class is to serve as an owner for
// signature functions (which get created for function types).
const dart::Library& owner_library = LookupLibrary(library_canonical_name(0));
const dart::Class& toplevel_class =
Class::Handle(Z, owner_library.toplevel_class());
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
helper_.SetOffset(program_->constant_table_offset());
TypeTranslator type_translator_(&helper_, &active_class_,
true /* finalize */);
ASSERT(type_translator_.active_class_ == &active_class_);
ConstantHelper helper(Z, &helper_, &type_translator_, &active_class_,
skip_vmservice_library_);
return helper.ReadConstantTable();
}
void KernelLoader::AnnotateNativeProcedures(const Array& constant_table_array) {
KernelConstantsMap constant_table(constant_table_array.raw());
potential_natives_ = kernel_program_info_.potential_natives();
const intptr_t length =
!potential_natives_.IsNull() ? potential_natives_.Length() : 0;
if (length > 0) {
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
Instance& constant = Instance::Handle(Z);
String& native_name = String::Handle(Z);
// Start scanning all candidates in [potential_natives] for the annotation
// constant. If the annotation is found, flag the [Function] as native and
// attach the native name to it.
Function& function = Function::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
function ^= potential_natives_.At(i);
helper_.SetOffset(function.KernelDataProgramOffset() +
function.kernel_offset());
{
ProcedureHelper procedure_helper(&helper_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
}
const intptr_t annotation_count = helper_.ReadListLength();
for (intptr_t j = 0; j < annotation_count; ++j) {
const intptr_t tag = helper_.PeekTag();
if (tag == kConstantExpression) {
helper_.ReadByte(); // Skip the tag.
// We have a candiate. Let's look if it's an instance of the
// ExternalName class.
const intptr_t constant_table_offset = helper_.ReadUInt();
constant ^= constant_table.GetOrDie(constant_table_offset);
if (constant.clazz() == external_name_class_.raw()) {
// We found the annotation, let's flag the function as native and
// set the native name!
native_name ^= constant.GetField(external_name_field_);
function.set_is_native(true);
function.set_native_name(native_name);
function.set_is_external(false);
break;
}
} else {
helper_.SkipExpression();
}
}
}
// Clear out the list of [Function] objects which might need their native
// name to be set after reading the constant table from the kernel blob.
potential_natives_ = GrowableObjectArray::null();
kernel_program_info_.set_potential_natives(potential_natives_);
}
ASSERT(constant_table.Release().raw() == constant_table_array.raw());
}
RawString* KernelLoader::DetectExternalNameCtor() {
helper_.ReadTag();
helper_.ReadPosition();
NameIndex annotation_class = H.EnclosingName(
helper_.ReadCanonicalNameReference()); // read target reference,
if (!IsClassName(annotation_class, Symbols::DartInternal(),
Symbols::ExternalName())) {
helper_.SkipArguments();
return String::null();
}
// Read arguments:
intptr_t total_arguments = helper_.ReadUInt(); // read argument count.
helper_.SkipListOfDartTypes(); // read list of types.
intptr_t positional_arguments = helper_.ReadListLength();
ASSERT(total_arguments == 1 && positional_arguments == 1);
Tag tag = helper_.ReadTag();
ASSERT(tag == kStringLiteral);
String& result = H.DartSymbolPlain(
helper_.ReadStringReference()); // read index into string table.
// List of named.
intptr_t list_length = helper_.ReadListLength(); // read list length.
ASSERT(list_length == 0);
return result.raw();
}
bool KernelLoader::IsClassName(NameIndex name,
const String& library,
const String& klass) {
ASSERT(H.IsClass(name));
StringIndex class_name_index = H.CanonicalNameString(name);
if (!H.StringEquals(class_name_index, klass.ToCString())) {
return false;
}
ASSERT(H.IsLibrary(H.CanonicalNameParent(name)));
StringIndex library_name_index =
H.CanonicalNameString(H.CanonicalNameParent(name));
return H.StringEquals(library_name_index, library.ToCString());
}
bool KernelLoader::DetectPragmaCtor() {
helper_.ReadTag();
helper_.ReadPosition();
NameIndex annotation_class = H.EnclosingName(
helper_.ReadCanonicalNameReference()); // read target reference
helper_.SkipArguments();
return IsClassName(annotation_class, Symbols::DartCore(), Symbols::Pragma());
}
void KernelLoader::LoadNativeExtensionLibraries(
const Array& constant_table_array) {
const intptr_t length = !potential_extension_libraries_.IsNull()
? potential_extension_libraries_.Length()
: 0;
if (length == 0) return;
KernelConstantsMap constant_table(constant_table_array.raw());
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
Instance& constant = Instance::Handle(Z);
String& uri_path = String::Handle(Z);
Library& library = Library::Handle(Z);
Object& result = Object::Handle(Z);
for (intptr_t i = 0; i < length; ++i) {
library ^= potential_extension_libraries_.At(i);
helper_.SetOffset(library.kernel_offset());
LibraryHelper library_helper(&helper_);
library_helper.ReadUntilExcluding(LibraryHelper::kAnnotations);
const intptr_t annotation_count = helper_.ReadListLength();
for (intptr_t j = 0; j < annotation_count; ++j) {
uri_path = String::null();
const intptr_t tag = helper_.PeekTag();
if (tag == kConstantExpression) {
helper_.ReadByte(); // Skip the tag.
const intptr_t constant_table_index = helper_.ReadUInt();
constant ^= constant_table.GetOrDie(constant_table_index);
if (constant.clazz() == external_name_class_.raw()) {
uri_path ^= constant.GetField(external_name_field_);
}
} else if (tag == kConstructorInvocation ||
tag == kConstConstructorInvocation) {
uri_path = DetectExternalNameCtor();
} else {
helper_.SkipExpression();
}
if (uri_path.IsNull()) continue;
Dart_LibraryTagHandler handler = I->library_tag_handler();
if (handler == NULL) {
H.ReportError("no library handler registered.");
}
I->BlockClassFinalization();
{
TransitionVMToNative transition(thread_);
Api::Scope api_scope(thread_);
Dart_Handle retval = handler(Dart_kImportExtensionTag,
Api::NewHandle(thread_, library.raw()),
Api::NewHandle(thread_, uri_path.raw()));
result = Api::UnwrapHandle(retval);
}
I->UnblockClassFinalization();
if (result.IsError()) {
H.ReportError(Error::Cast(result), "library handler failed");
}
}
}
potential_extension_libraries_ = GrowableObjectArray::null();
ASSERT(constant_table.Release().raw() == constant_table_array.raw());
}
RawObject* KernelLoader::LoadProgram(bool process_pending_classes) {
ASSERT(kernel_program_info_.constants() == Array::null());
if (!program_->is_single_program()) {
FATAL(
"Trying to load a concatenated dill file at a time where that is "
"not allowed");
}
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
const intptr_t length = program_->library_count();
Object& last_library = Library::Handle(Z);
for (intptr_t i = 0; i < length; i++) {
last_library = LoadLibrary(i);
}
if (process_pending_classes) {
if (!ClassFinalizer::ProcessPendingClasses()) {
// Class finalization failed -> sticky error would be set.
RawError* error = H.thread()->sticky_error();
H.thread()->clear_sticky_error();
return error;
}
}
// All classes were successfully loaded, so let's:
// a) load & canonicalize the constant table
const Array& constants = ReadConstantTable();
// b) set the native names for native functions which have been created
// so far (the rest will be directly set during LoadProcedure)
AnnotateNativeProcedures(constants);
LoadNativeExtensionLibraries(constants);
// c) update all scripts with the constants array
ASSERT(kernel_program_info_.constants() == Array::null());
kernel_program_info_.set_constants(constants);
kernel_program_info_.set_constants_table(ExternalTypedData::Handle(Z));
NameIndex main = program_->main_method();
if (main == -1) {
return Library::null();
}
NameIndex main_library = H.EnclosingName(main);
Library& library = LookupLibrary(main_library);
return library.raw();
}
// Either class finalization failed or we caught a compile error.
// In both cases sticky error would be set.
RawError* error = thread_->sticky_error();
thread_->clear_sticky_error();
return error;
}
RawObject* KernelLoader::LoadExpressionEvaluationFunction(
const String& library_url,
const String& klass) {
// Find the original context, i.e. library/class, in which the evaluation will
// happen.
const Library& real_library = Library::Handle(
Z, Library::LookupLibrary(Thread::Current(), library_url));
ASSERT(!real_library.IsNull());
const Class& real_class = Class::Handle(
Z, klass.IsNull() ? real_library.toplevel_class()
: real_library.LookupClassAllowPrivate(klass));
ASSERT(!real_class.IsNull());
const intptr_t num_cids = I->class_table()->NumCids();
const intptr_t num_libs =
GrowableObjectArray::Handle(I->object_store()->libraries()).Length();
// Load the "evaluate:source" expression evaluation library.
ASSERT(expression_evaluation_library_.IsNull());
ASSERT(expression_evaluation_function_.IsNull());
const Object& result = Object::Handle(Z, LoadProgram(true));
if (result.IsError()) {
return result.raw();
}
ASSERT(!expression_evaluation_library_.IsNull());
ASSERT(!expression_evaluation_function_.IsNull());
ASSERT(GrowableObjectArray::Handle(I->object_store()->libraries()).Length() ==
num_libs);
ASSERT(I->class_table()->NumCids() == num_cids);
// Make the expression evaluation function have the right kernel data and
// parent.
auto& eval_data = ExternalTypedData::Handle(
Z, expression_evaluation_library_.kernel_data());
auto& eval_script =
Script::Handle(Z, expression_evaluation_function_.script());
expression_evaluation_function_.SetKernelDataAndScript(
eval_script, eval_data, expression_evaluation_library_.kernel_offset());
expression_evaluation_function_.set_owner(real_class);
return expression_evaluation_function_.raw();
}
void KernelLoader::FindModifiedLibraries(Program* program,
Isolate* isolate,
BitVector* modified_libs,
bool force_reload,
bool* is_empty_program) {
LongJumpScope jump;
Zone* zone = Thread::Current()->zone();
if (setjmp(*jump.Set()) == 0) {
if (force_reload) {
// If a reload is being forced we mark all libraries as having
// been modified.
const GrowableObjectArray& libs =
GrowableObjectArray::Handle(isolate->object_store()->libraries());
intptr_t num_libs = libs.Length();
Library& lib = dart::Library::Handle(zone);
for (intptr_t i = 0; i < num_libs; i++) {
lib ^= libs.At(i);
if (!lib.is_dart_scheme()) {
modified_libs->Add(lib.index());
}
}
return;
}
// Now go through all the libraries that are present in the incremental
// kernel files, these will constitute the modified libraries.
*is_empty_program = true;
if (program->is_single_program()) {
KernelLoader loader(program);
return loader.walk_incremental_kernel(modified_libs, is_empty_program);
} else {
kernel::Reader reader(program->kernel_data(),
program->kernel_data_size());
GrowableArray<intptr_t> subprogram_file_starts;
index_programs(&reader, &subprogram_file_starts);
// Create "fake programs" for each sub-program.
intptr_t subprogram_count = subprogram_file_starts.length() - 1;
for (intptr_t i = 0; i < subprogram_count; ++i) {
intptr_t subprogram_start = subprogram_file_starts.At(i);
intptr_t subprogram_end = subprogram_file_starts.At(i + 1);
reader.set_raw_buffer(program->kernel_data() + subprogram_start);
reader.set_size(subprogram_end - subprogram_start);
reader.set_offset(0);
Program* subprogram = Program::ReadFrom(&reader);
ASSERT(subprogram->is_single_program());
KernelLoader loader(subprogram);
loader.walk_incremental_kernel(modified_libs, is_empty_program);
delete subprogram;
}
}
}
}
void KernelLoader::walk_incremental_kernel(BitVector* modified_libs,
bool* is_empty_program) {
intptr_t length = program_->library_count();
*is_empty_program = *is_empty_program && (length == 0);
for (intptr_t i = 0; i < length; i++) {
intptr_t kernel_offset = library_offset(i);
helper_.SetOffset(kernel_offset);
LibraryHelper library_helper(&helper_);
library_helper.ReadUntilIncluding(LibraryHelper::kCanonicalName);
dart::Library& lib = LookupLibraryOrNull(library_helper.canonical_name_);
if (!lib.IsNull() && !lib.is_dart_scheme()) {
// This is a library that already exists so mark it as being modified.
modified_libs->Add(lib.index());
}
}
}
void KernelLoader::CheckForInitializer(const Field& field) {
if (helper_.PeekTag() == kSomething) {
SimpleExpressionConverter converter(&H, &helper_);
const bool has_simple_initializer =
converter.IsSimple(helper_.ReaderOffset() + 1);
if (!has_simple_initializer || !converter.SimpleValue().IsNull()) {
field.set_has_initializer(true);
return;
}
}
field.set_has_initializer(false);
}
RawLibrary* KernelLoader::LoadLibrary(intptr_t index) {
if (!program_->is_single_program()) {
FATAL(
"Trying to load a concatenated dill file at a time where that is "
"not allowed");
}
// Read library index.
library_kernel_offset_ = library_offset(index);
correction_offset_ = library_kernel_offset_;
intptr_t library_end = library_offset(index + 1);
intptr_t library_size = library_end - library_kernel_offset_;
// NOTE: Since |helper_| is used to load the overall kernel program,
// it's reader's offset is an offset into the overall kernel program.
// Hence, when setting the kernel offsets of field and functions, one
// has to subtract the library's kernel offset from the reader's
// offset.
helper_.SetOffset(library_kernel_offset_);
LibraryHelper library_helper(&helper_);
library_helper.ReadUntilIncluding(LibraryHelper::kCanonicalName);
if (!FLAG_precompiled_mode && !I->should_load_vmservice()) {
StringIndex lib_name_index =
H.CanonicalNameString(library_helper.canonical_name_);
if (H.StringEquals(lib_name_index, kVMServiceIOLibraryUri)) {
// We are not the service isolate and we are not generating an AOT
// snapshot so we skip loading 'dart:vmservice_io'.
skip_vmservice_library_ = library_helper.canonical_name_;
ASSERT(H.IsLibrary(skip_vmservice_library_));
return Library::null();
}
}
Library& library =
Library::Handle(Z, LookupLibrary(library_helper.canonical_name_).raw());
// The Kernel library is external implies that it is already loaded.
ASSERT(!library_helper.IsExternal() || library.Loaded());
if (library.Loaded()) return library.raw();
library_kernel_data_ = helper_.reader_.ExternalDataFromTo(
library_kernel_offset_, library_kernel_offset_ + library_size);
library.set_kernel_data(library_kernel_data_);
library.set_kernel_offset(library_kernel_offset_);
LibraryIndex library_index(library_kernel_data_);
intptr_t class_count = library_index.class_count();
intptr_t procedure_count = library_index.procedure_count();
library_helper.ReadUntilIncluding(LibraryHelper::kName);
library.SetName(H.DartSymbolObfuscate(library_helper.name_index_));
// The bootstrapper will take care of creating the native wrapper classes, but
// we will add the synthetic constructors to them here.
if (library.name() ==
Symbols::Symbol(Symbols::kDartNativeWrappersLibNameId).raw()) {
ASSERT(library.LoadInProgress());
loading_native_wrappers_library_ = true;
} else {
loading_native_wrappers_library_ = false;
library.SetLoadInProgress();
}
StringIndex import_uri_index =
H.CanonicalNameString(library_helper.canonical_name_);
library_helper.ReadUntilIncluding(LibraryHelper::kSourceUriIndex);
const Script& script = Script::Handle(
Z, ScriptAt(library_helper.source_uri_index_, import_uri_index));
library_helper.ReadUntilExcluding(LibraryHelper::kAnnotations);
intptr_t annotations_kernel_offset =
helper_.ReaderOffset() - correction_offset_;
intptr_t annotation_count = helper_.ReadListLength(); // read list length.
if (annotation_count > 0) {
EnsurePotentialExtensionLibraries();
potential_extension_libraries_.Add(library);
}
for (intptr_t i = 0; i < annotation_count; ++i) {
helper_.SkipExpression(); // read ith annotation.
}
library_helper.SetJustRead(LibraryHelper::kAnnotations);
// Setup toplevel class (which contains library fields/procedures).
// We do not register expression evaluation classes with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
const bool register_class =
library.raw() != expression_evaluation_library_.raw();
Class& toplevel_class =
Class::Handle(Z, Class::New(library, Symbols::TopLevel(), script,
TokenPosition::kNoSource, register_class));
toplevel_class.set_is_cycle_free();
library.set_toplevel_class(toplevel_class);
library_helper.ReadUntilExcluding(LibraryHelper::kDependencies);
LoadLibraryImportsAndExports(&library, toplevel_class);
library_helper.SetJustRead(LibraryHelper::kDependencies);
const GrowableObjectArray& classes =
GrowableObjectArray::Handle(Z, I->object_store()->pending_classes());
// Everything up til the classes are skipped implicitly, and library_helper
// is no longer used.
// Load all classes.
intptr_t next_class_offset = library_index.ClassOffset(0);
for (intptr_t i = 0; i < class_count; ++i) {
helper_.SetOffset(next_class_offset);
next_class_offset = library_index.ClassOffset(i + 1);
const Class& klass = LoadClass(library, toplevel_class, next_class_offset);
if (register_class) {
classes.Add(klass, Heap::kOld);
}
}
helper_.SetOffset(next_class_offset);
fields_.Clear();
functions_.Clear();
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
// Load toplevel fields.
intptr_t field_count = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < field_count; ++i) {
intptr_t field_offset = helper_.ReaderOffset() - correction_offset_;
ActiveMemberScope active_member_scope(&active_class_, NULL);
FieldHelper field_helper(&helper_);
field_helper.ReadUntilExcluding(FieldHelper::kName);
const String& name = helper_.ReadNameAsFieldName();
field_helper.SetJustRead(FieldHelper::kName);
field_helper.ReadUntilExcluding(FieldHelper::kAnnotations);
intptr_t annotation_count = helper_.ReadListLength();
bool has_pragma_annotation;
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
ReadVMAnnotations(annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
if (has_pragma_annotation) {
toplevel_class.set_has_pragma(true);
}
field_helper.SetJustRead(FieldHelper::kAnnotations);
field_helper.ReadUntilExcluding(FieldHelper::kType);
const Object& script_class =
ClassForScriptAt(toplevel_class, field_helper.source_uri_index_);
// In the VM all const fields are implicitly final whereas in Kernel they
// are not final because they are not explicitly declared that way.
const bool is_final = field_helper.IsConst() || field_helper.IsFinal();
Field& field = Field::Handle(
Z,
Field::NewTopLevel(name, is_final, field_helper.IsConst(), script_class,
field_helper.position_, field_helper.end_position_));
field.set_kernel_offset(field_offset);
const AbstractType& type = T.BuildType(); // read type.
field.SetFieldType(type);
CheckForInitializer(field);
field_helper.SetJustRead(FieldHelper::kType);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
intptr_t field_initializer_offset = helper_.ReaderOffset();
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
{
// GenerateFieldAccessors reads (some of) the initializer.
AlternativeReadingScope alt(&helper_.reader_, field_initializer_offset);
GenerateFieldAccessors(toplevel_class, field, &field_helper);
}
if ((FLAG_enable_mirrors || has_pragma_annotation) &&
annotation_count > 0) {
library.AddFieldMetadata(field, TokenPosition::kNoSource, field_offset);
}
fields_.Add(&field);
library.AddObject(field, name);
}
toplevel_class.AddFields(fields_);
// Load toplevel procedures.
intptr_t next_procedure_offset = library_index.ProcedureOffset(0);
for (intptr_t i = 0; i < procedure_count; ++i) {
helper_.SetOffset(next_procedure_offset);
next_procedure_offset = library_index.ProcedureOffset(i + 1);
LoadProcedure(library, toplevel_class, false, next_procedure_offset);
}
if (FLAG_enable_mirrors && annotation_count > 0) {
ASSERT(annotations_kernel_offset > 0);
library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
annotations_kernel_offset);
}
toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray()));
if (register_class) {
classes.Add(toplevel_class, Heap::kOld);
}
if (!library.Loaded()) library.SetLoaded();
return library.raw();
}
void KernelLoader::LoadLibraryImportsAndExports(Library* library,
const Class& toplevel_class) {
GrowableObjectArray& show_list = GrowableObjectArray::Handle(Z);
GrowableObjectArray& hide_list = GrowableObjectArray::Handle(Z);
Array& show_names = Array::Handle(Z);
Array& hide_names = Array::Handle(Z);
Namespace& ns = Namespace::Handle(Z);
LibraryPrefix& library_prefix = LibraryPrefix::Handle(Z);
const intptr_t deps_count = helper_.ReadListLength();
for (intptr_t dep = 0; dep < deps_count; ++dep) {
LibraryDependencyHelper dependency_helper(&helper_);
dependency_helper.ReadUntilExcluding(LibraryDependencyHelper::kAnnotations);
intptr_t annotations_kernel_offset =
helper_.ReaderOffset() - correction_offset_;
dependency_helper.ReadUntilExcluding(LibraryDependencyHelper::kCombinators);
// Ignore the dependency if the target library is invalid.
// The error will be caught during compilation.
if (dependency_helper.target_library_canonical_name_ < 0) {
const intptr_t combinator_count = helper_.ReadListLength();
for (intptr_t c = 0; c < combinator_count; ++c) {
helper_.SkipLibraryCombinator();
}
continue;
}
// Prepare show and hide lists.
show_list = GrowableObjectArray::New(Heap::kOld);
hide_list = GrowableObjectArray::New(Heap::kOld);
const intptr_t combinator_count = helper_.ReadListLength();
for (intptr_t c = 0; c < combinator_count; ++c) {
uint8_t flags = helper_.ReadFlags();
intptr_t name_count = helper_.ReadListLength();
for (intptr_t n = 0; n < name_count; ++n) {
String& show_hide_name =
H.DartSymbolObfuscate(helper_.ReadStringReference());
if (flags & LibraryDependencyHelper::Show) {
show_list.Add(show_hide_name, Heap::kOld);
} else {
hide_list.Add(show_hide_name, Heap::kOld);
}
}
}
if (show_list.Length() > 0) {
show_names = Array::MakeFixedLength(show_list);
} else {
show_names = Array::null();
}
if (hide_list.Length() > 0) {
hide_names = Array::MakeFixedLength(hide_list);
} else {
hide_names = Array::null();
}
Library& target_library =
LookupLibrary(dependency_helper.target_library_canonical_name_);
if (!FLAG_enable_mirrors &&
target_library.url() == Symbols::DartMirrors().raw()) {
H.ReportError("import of dart:mirrors with --enable-mirrors=false");
}
String& prefix = H.DartSymbolPlain(dependency_helper.name_index_);
ns = Namespace::New(target_library, show_names, hide_names);
if (dependency_helper.flags_ & LibraryDependencyHelper::Export) {
library->AddExport(ns);
} else {
if (prefix.IsNull() || prefix.Length() == 0) {
library->AddImport(ns);
} else {
library_prefix = library->LookupLocalLibraryPrefix(prefix);
if (!library_prefix.IsNull()) {
library_prefix.AddImport(ns);
} else {
library_prefix = LibraryPrefix::New(
prefix, ns,
dependency_helper.flags_ & LibraryDependencyHelper::Deferred,
*library);
library->AddObject(library_prefix, prefix);
}
}
}
if (FLAG_enable_mirrors && dependency_helper.annotation_count_ > 0) {
ASSERT(annotations_kernel_offset > 0);
ns.AddMetadata(toplevel_class, TokenPosition::kNoSource,
annotations_kernel_offset);
}
}
}
void KernelLoader::LoadPreliminaryClass(ClassHelper* class_helper,
intptr_t type_parameter_count) {
const Class* klass = active_class_.klass;
// Note: This assumes that ClassHelper is exactly at the position where
// the length of the type parameters have been read, and that the order in
// the binary is as follows: [...], kTypeParameters, kSuperClass, kMixinType,
// kImplementedClasses, [...].
// Set type parameters.
T.LoadAndSetupTypeParameters(&active_class_, *klass, type_parameter_count,
Function::Handle(Z));
// Set super type. Some classes (e.g., Object) do not have one.
Tag type_tag = helper_.ReadTag(); // read super class type (part 1).
if (type_tag == kSomething) {
AbstractType& super_type =
T.BuildTypeWithoutFinalization(); // read super class type (part 2).
if (super_type.IsMalformed()) H.ReportError("Malformed super type");
klass->set_super_type(super_type);
}
class_helper->SetJustRead(ClassHelper::kSuperClass);
class_helper->ReadUntilIncluding(ClassHelper::kMixinType);
// Build implemented interface types
intptr_t interface_count = helper_.ReadListLength();
const Array& interfaces =
Array::Handle(Z, Array::New(interface_count, Heap::kOld));
for (intptr_t i = 0; i < interface_count; i++) {
const AbstractType& type =
T.BuildTypeWithoutFinalization(); // read ith type.
if (type.IsMalformed()) H.ReportError("Malformed interface type.");
interfaces.SetAt(i, type);
}
class_helper->SetJustRead(ClassHelper::kImplementedClasses);
klass->set_interfaces(interfaces);
if (class_helper->is_abstract()) klass->set_is_abstract();
if (class_helper->is_transformed_mixin_application()) {
klass->set_is_transformed_mixin_application();
}
}
// Workaround for http://dartbug.com/32087: currently Kernel front-end
// embeds absolute build-time paths to core library sources into Kernel
// binaries this introduces discrepancy between how stack traces were
// looked like in legacy pipeline and how they look in Dart 2 pipeline and
// breaks users' code that attempts to pattern match and filter various
// irrelevant frames (e.g. frames from dart:async).
// This also breaks debugging experience in external debuggers because
// debugger attempts to open files that don't exist in the local file
// system.
// To work around this issue we reformat urls of scripts belonging to
// dart:-scheme libraries to look like they looked like in legacy pipeline:
//
// dart:libname/filename.dart
// dart:libname/runtime/lib/filename.dart
// dart:libname/runtime/bin/filename.dart
//
void KernelLoader::FixCoreLibraryScriptUri(const Library& library,
const Script& script) {
struct Helper {
static bool EndsWithCString(const String& haystack,
const char* needle,
intptr_t needle_length,
intptr_t end_pos) {
const intptr_t start = end_pos - needle_length + 1;
if (start >= 0) {
for (intptr_t i = 0; i < needle_length; i++) {
if (haystack.CharAt(start + i) != needle[i]) {
return false;
}
}
return true;
}
return false;
}
};
if (library.is_dart_scheme()) {
String& url = String::Handle(zone_, script.url());
if (!url.StartsWith(Symbols::DartScheme())) {
// Search backwards until '/' is found. That gives us the filename.
// Note: can't use reusable handle in the code below because
// concat also needs it.
intptr_t pos = url.Length() - 1;
while (pos >= 0 && url.CharAt(pos) != '/') {
pos--;
}
static const char* kRuntimeLib = "runtime/lib/";
static const intptr_t kRuntimeLibLen = strlen(kRuntimeLib);
const bool inside_runtime_lib =
Helper::EndsWithCString(url, kRuntimeLib, kRuntimeLibLen, pos);
static const char* kRuntimeBin = "runtime/bin/";
static const intptr_t kRuntimeBinLen = strlen(kRuntimeBin);
const bool inside_runtime_bin =
Helper::EndsWithCString(url, kRuntimeBin, kRuntimeBinLen, pos);
String& tmp = String::Handle(zone_);
url = String::SubString(url, pos + 1);
if (inside_runtime_lib) {
tmp = String::New("runtime/lib", Heap::kNew);
url = String::Concat(tmp, url);
} else if (inside_runtime_bin) {
tmp = String::New("runtime/bin", Heap::kNew);
url = String::Concat(tmp, url);
}
tmp = library.url();
url = String::Concat(Symbols::Slash(), url);
url = String::Concat(tmp, url);
script.set_url(url);
}
}
}
Class& KernelLoader::LoadClass(const Library& library,
const Class& toplevel_class,
intptr_t class_end) {
intptr_t class_offset = helper_.ReaderOffset();
ClassIndex class_index(program_->kernel_data(), program_->kernel_data_size(),
class_offset, class_end - class_offset);
ClassHelper class_helper(&helper_);
class_helper.ReadUntilIncluding(ClassHelper::kCanonicalName);
Class& klass = LookupClass(class_helper.canonical_name_);
klass.set_kernel_offset(class_offset - correction_offset_);
// The class needs to have a script because all the functions in the class
// will inherit it. The predicate Function::IsOptimizable uses the absence of
// a script to detect test functions that should not be optimized.
if (klass.script() == Script::null()) {
class_helper.ReadUntilIncluding(ClassHelper::kSourceUriIndex);
const Script& script =
Script::Handle(Z, ScriptAt(class_helper.source_uri_index_));
klass.set_script(script);
FixCoreLibraryScriptUri(library, script);
}
if (klass.token_pos() == TokenPosition::kNoSource) {
class_helper.ReadUntilIncluding(ClassHelper::kStartPosition);
klass.set_token_pos(class_helper.start_position_);
}
class_helper.ReadUntilIncluding(ClassHelper::kFlags);
if (class_helper.is_enum_class()) klass.set_is_enum_class();
class_helper.ReadUntilExcluding(ClassHelper::kAnnotations);
intptr_t annotation_count = helper_.ReadListLength();
bool has_pragma_annotation = false;
{
String& native_name_unused = String::Handle(Z);
bool is_potential_native_unused = false;
ReadVMAnnotations(annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
if (has_pragma_annotation) {
klass.set_has_pragma(true);
}
class_helper.SetJustRead(ClassHelper::kAnnotations);
class_helper.ReadUntilExcluding(ClassHelper::kTypeParameters);
intptr_t type_parameter_counts =
helper_.ReadListLength(); // read type_parameters list length.
ActiveClassScope active_class_scope(&active_class_, &klass);
if (!klass.is_cycle_free()) {
LoadPreliminaryClass(&class_helper, type_parameter_counts);
} else {
for (intptr_t i = 0; i < type_parameter_counts; ++i) {
helper_.SkipStringReference(); // read ith name index.
helper_.SkipDartType(); // read ith bound.
}
class_helper.SetJustRead(ClassHelper::kTypeParameters);
}
if ((FLAG_enable_mirrors || has_pragma_annotation) && annotation_count > 0) {
library.AddClassMetadata(klass, toplevel_class, TokenPosition::kNoSource,
class_offset - correction_offset_);
}
// We do not register expression evaluation classes with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
const bool register_class =
library.raw() != expression_evaluation_library_.raw();
if (loading_native_wrappers_library_ || !register_class) {
FinishClassLoading(klass, library, toplevel_class, class_offset,
class_index, &class_helper);
}
helper_.SetOffset(class_end);
return klass;
}
void KernelLoader::FinishClassLoading(const Class& klass,
const Library& library,
const Class& toplevel_class,
intptr_t class_offset,
const ClassIndex& class_index,
ClassHelper* class_helper) {
fields_.Clear();
functions_.Clear();
ActiveClassScope active_class_scope(&active_class_, &klass);
if (library.raw() == Library::InternalLibrary() &&
klass.Name() == Symbols::ClassID().raw()) {
// If this is a dart:internal.ClassID class ignore field declarations
// contained in the Kernel file and instead inject our own const
// fields.
klass.InjectCIDFields();
} else {
class_helper->ReadUntilExcluding(ClassHelper::kFields);
int field_count = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < field_count; ++i) {
intptr_t field_offset = helper_.ReaderOffset() - correction_offset_;
ActiveMemberScope active_member(&active_class_, NULL);
FieldHelper field_helper(&helper_);
field_helper.ReadUntilIncluding(FieldHelper::kSourceUriIndex);
const Object& script_class =
ClassForScriptAt(klass, field_helper.source_uri_index_);
field_helper.ReadUntilExcluding(FieldHelper::kName);
const String& name = helper_.ReadNameAsFieldName();
field_helper.SetJustRead(FieldHelper::kName);
field_helper.ReadUntilExcluding(FieldHelper::kAnnotations);
intptr_t annotation_count = helper_.ReadListLength();
bool has_pragma_annotation;
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
ReadVMAnnotations(annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
if (has_pragma_annotation) {
klass.set_has_pragma(true);
}
field_helper.SetJustRead(FieldHelper::kAnnotations);
field_helper.ReadUntilExcluding(FieldHelper::kType);
const AbstractType& type =
T.BuildTypeWithoutFinalization(); // read type.
field_helper.SetJustRead(FieldHelper::kType);
const bool is_reflectable =
field_helper.position_.IsReal() &&
!(library.is_dart_scheme() && library.IsPrivate(name));
// In the VM all const fields are implicitly final whereas in Kernel they
// are not final because they are not explicitly declared that way.
const bool is_final = field_helper.IsConst() || field_helper.IsFinal();
Field& field = Field::Handle(
Z,
Field::New(name, field_helper.IsStatic(), is_final,
field_helper.IsConst(), is_reflectable, script_class, type,
field_helper.position_, field_helper.end_position_));
field.set_kernel_offset(field_offset);
CheckForInitializer(field);
field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
intptr_t field_initializer_offset = helper_.ReaderOffset();
field_helper.ReadUntilExcluding(FieldHelper::kEnd);
{
// GenerateFieldAccessors reads (some of) the initializer.
AlternativeReadingScope alt(&helper_.reader_, field_initializer_offset);
GenerateFieldAccessors(klass, field, &field_helper);
}
if ((FLAG_enable_mirrors || has_pragma_annotation) &&
annotation_count > 0) {
library.AddFieldMetadata(field, TokenPosition::kNoSource, field_offset);
}
fields_.Add(&field);
}
class_helper->SetJustRead(ClassHelper::kFields);
if (klass.is_enum_class()) {
// Add static field 'const _deleted_enum_sentinel'.
// This field does not need to be of type E.
Field& deleted_enum_sentinel = Field::ZoneHandle(Z);
deleted_enum_sentinel = Field::New(
Symbols::_DeletedEnumSentinel(),
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
/* is_reflectable = */ false, klass, Object::dynamic_type(),
TokenPosition::kNoSource, TokenPosition::kNoSource);
fields_.Add(&deleted_enum_sentinel);
}
klass.AddFields(fields_);
}
class_helper->ReadUntilExcluding(ClassHelper::kConstructors);
int constructor_count = helper_.ReadListLength(); // read list length.
for (intptr_t i = 0; i < constructor_count; ++i) {
intptr_t constructor_offset = helper_.ReaderOffset() - correction_offset_;
ActiveMemberScope active_member_scope(&active_class_, NULL);
ConstructorHelper constructor_helper(&helper_);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kAnnotations);
intptr_t annotation_count = helper_.ReadListLength();
bool has_pragma_annotation;
{
String& native_name_unused = String::Handle();
bool is_potential_native_unused;
ReadVMAnnotations(annotation_count, &native_name_unused,
&is_potential_native_unused, &has_pragma_annotation);
}
constructor_helper.SetJustRead(ConstructorHelper::kAnnotations);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kFunction);
const String& name =
H.DartConstructorName(constructor_helper.canonical_name_);
// We can have synthetic constructors, which will not have a source uri
// attached to them (which means the index into the source uri table is 0,
// see `package:kernel/binary/ast_to_binary::writeUriReference`.
const Object* owner = &klass;
const intptr_t source_uri_index = constructor_helper.source_uri_index_;
if (source_uri_index != 0) {
owner = &ClassForScriptAt(klass, source_uri_index);
}
Function& function = Function::ZoneHandle(
Z, Function::New(name, RawFunction::kConstructor,
false, // is_static
constructor_helper.IsConst(),
false, // is_abstract
constructor_helper.IsExternal(),
false, // is_native
*owner, constructor_helper.start_position_));
function.set_end_token_pos(constructor_helper.end_position_);
functions_.Add(&function);
function.set_kernel_offset(constructor_offset);
function.set_result_type(T.ReceiverType(klass));
function.set_has_pragma(has_pragma_annotation);
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kTypeParameters);
T.SetupFunctionParameters(klass, function,
true, // is_method
false, // is_closure
&function_node_helper);
if (constructor_helper.IsSynthetic()) {
function.set_is_debuggable(false);
}
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
constructor_helper.SetJustRead(ConstructorHelper::kFunction);
constructor_helper.ReadUntilExcluding(ConstructorHelper::kEnd);
if ((FLAG_enable_mirrors || has_pragma_annotation) &&
annotation_count > 0) {
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
constructor_offset);
}
}
// Everything up til the procedures are skipped implicitly, and class_helper
// is no longer used.
intptr_t procedure_count = class_index.procedure_count();
// Procedure offsets within a class index are whole program offsets and not
// relative to the library of the class. Hence, we need a correction to get
// the currect procedure offset within the current data.
intptr_t correction = correction_offset_ - library_kernel_offset_;
intptr_t next_procedure_offset = class_index.ProcedureOffset(0) + correction;
for (intptr_t i = 0; i < procedure_count; ++i) {
helper_.SetOffset(next_procedure_offset);
next_procedure_offset = class_index.ProcedureOffset(i + 1) + correction;
LoadProcedure(library, klass, true, next_procedure_offset);
}
klass.SetFunctions(Array::Handle(MakeFunctionsArray()));
}
void KernelLoader::FinishLoading(const Class& klass) {
ASSERT(klass.kernel_offset() > 0);
Zone* zone = Thread::Current()->zone();
const Script& script = Script::Handle(zone, klass.script());
const Library& library = Library::Handle(zone, klass.library());
const Class& toplevel_class = Class::Handle(zone, library.toplevel_class());
const ExternalTypedData& library_kernel_data =
ExternalTypedData::Handle(zone, library.kernel_data());
ASSERT(!library_kernel_data.IsNull());
const intptr_t library_kernel_offset = library.kernel_offset();
ASSERT(library_kernel_offset > 0);
const intptr_t class_offset = klass.kernel_offset();
KernelLoader kernel_loader(script, library_kernel_data,
library_kernel_offset);
LibraryIndex library_index(library_kernel_data);
ClassIndex class_index(
library_kernel_data, class_offset,
// Class offsets in library index are whole program offsets.
// Hence, we need to add |library_kernel_offset| to
// |class_offset| to lookup the entry for the class in the library
// index.
library_index.SizeOfClassAtOffset(class_offset + library_kernel_offset));
kernel_loader.helper_.SetOffset(class_offset);
ClassHelper class_helper(&kernel_loader.helper_);
kernel_loader.FinishClassLoading(klass, library, toplevel_class, class_offset,
class_index, &class_helper);
}
// Read annotations on a procedure to identify potential VM-specific directives.
//
// Output parameters:
//
// `native_name`: non-null if `@ExternalName(...)` was identified.
//
// `is_potential_native`: non-null if there may be an `@ExternalName(...)`
// annotation and we need to re-try after reading the constants table.
//
// `has_pragma_annotation`: non-null if @pragma(...) was found (no information
// is given on the kind of pragma directive).
//
void KernelLoader::ReadVMAnnotations(intptr_t annotation_count,
String* native_name,
bool* is_potential_native,
bool* has_pragma_annotation) {
*is_potential_native = false;
*has_pragma_annotation = false;
String& detected_name = String::Handle(Z);
for (intptr_t i = 0; i < annotation_count; ++i) {
const intptr_t tag = helper_.PeekTag();
if (tag == kConstructorInvocation || tag == kConstConstructorInvocation) {
const intptr_t start = helper_.ReaderOffset();
detected_name = DetectExternalNameCtor();
if (!detected_name.IsNull()) {
*native_name = detected_name.raw();
continue;
}
helper_.SetOffset(start);
if (DetectPragmaCtor()) {
*has_pragma_annotation = true;
}
} else if (tag == kConstantExpression) {
const Array& constant_table_array =
Array::Handle(kernel_program_info_.constants());
if (constant_table_array.IsNull()) {
// We can only read in the constant table once all classes have been
// finalized (otherwise we can't create instances of the classes!).
//
// We therefore delay the scanning for `ExternalName {name: ... }`
// constants in the annotation list to later.
*is_potential_native = true;
ASSERT(kernel_program_info_.constants_table() !=
ExternalTypedData::null());
// For pragma annotations, we seek into the constants table and peek
// into the Kernel representation of the constant.
//
// TODO(sjindel): Refactor `ExternalName` handling to do this as well
// and avoid the "potential natives" list.
helper_.ReadByte(); // Skip the tag.
const intptr_t offset_in_constant_table = helper_.ReadUInt();
AlternativeReadingScope scope(
&helper_.reader_,
&ExternalTypedData::Handle(Z,
kernel_program_info_.constants_table()),
0);
// Seek into the position within the constant table where we can inspect
// this constant's Kernel representation.
helper_.ReadUInt(); // skip constant table size
helper_.SkipBytes(offset_in_constant_table);
uint8_t tag = helper_.ReadTag();
if (tag == kInstanceConstant) {
*has_pragma_annotation =
IsClassName(helper_.ReadCanonicalNameReference(),
Symbols::DartCore(), Symbols::Pragma());
}
} else {
KernelConstantsMap constant_table(constant_table_array.raw());
helper_.ReadByte(); // Skip the tag.
// Obtain `dart:_internal::ExternalName.name`.
EnsureExternalClassIsLookedUp();
// Obtain `dart:_internal::pragma`.
EnsurePragmaClassIsLookedUp();
const intptr_t constant_table_index = helper_.ReadUInt();
const Object& constant =
Object::Handle(constant_table.GetOrDie(constant_table_index));
if (constant.clazz() == external_name_class_.raw()) {
const Instance& instance =
Instance::Handle(Instance::RawCast(constant.raw()));
*native_name =
String::RawCast(instance.GetField(external_name_field_));
} else if (constant.clazz() == pragma_class_.raw()) {
*has_pragma_annotation = true;
}
ASSERT(constant_table.Release().raw() == constant_table_array.raw());
}
} else {
helper_.SkipExpression();
continue;
}
}
}
void KernelLoader::LoadProcedure(const Library& library,
const Class& owner,
bool in_class,
intptr_t procedure_end) {
intptr_t procedure_offset = helper_.ReaderOffset() - correction_offset_;
ProcedureHelper procedure_helper(&helper_);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kAnnotations);
if (procedure_helper.IsRedirectingFactoryConstructor()) {
helper_.SetOffset(procedure_end);
return;
}
const String& name = H.DartProcedureName(procedure_helper.canonical_name_);
bool is_method = in_class && !procedure_helper.IsStatic();
bool is_abstract = procedure_helper.IsAbstract();
bool is_external = procedure_helper.IsExternal();
String& native_name = String::Handle(Z);
bool is_potential_native;
bool has_pragma_annotation;
const intptr_t annotation_count = helper_.ReadListLength();
ReadVMAnnotations(annotation_count, &native_name, &is_potential_native,
&has_pragma_annotation);
// If this is a potential native, we'll unset is_external in
// AnnotateNativeProcedures instead.
is_external = is_external && native_name.IsNull();
procedure_helper.SetJustRead(ProcedureHelper::kAnnotations);
const Object& script_class =
ClassForScriptAt(owner, procedure_helper.source_uri_index_);
RawFunction::Kind kind = GetFunctionType(procedure_helper.kind_);
// We do not register expression evaluation libraries with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
const bool register_function = !name.Equals(Symbols::DebugProcedureName());
Function& function = Function::ZoneHandle(
Z, Function::New(name, kind,
!is_method, // is_static
false, // is_const
is_abstract, is_external,
!native_name.IsNull(), // is_native
script_class, procedure_helper.start_position_));
function.set_has_pragma(has_pragma_annotation);
function.set_end_token_pos(procedure_helper.end_position_);
if (register_function) {
functions_.Add(&function);
} else {
expression_evaluation_function_ = function.raw();
}
function.set_kernel_offset(procedure_offset);
function.set_is_reflectable(function.is_reflectable() &&
library.raw() != Library::InternalLibrary());
ActiveMemberScope active_member(&active_class_, &function);
procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
Tag function_node_tag = helper_.ReadTag();
ASSERT(function_node_tag == kSomething);
FunctionNodeHelper function_node_helper(&helper_);
function_node_helper.ReadUntilIncluding(FunctionNodeHelper::kDartAsyncMarker);
// _AsyncAwaitCompleter.future should be made non-debuggable, otherwise
// stepping out of async methods will keep hitting breakpoint resulting in
// infinite loop.
bool isAsyncAwaitCompleterFuture =
Symbols::_AsyncAwaitCompleter().Equals(
String::Handle(owner.ScrubbedName())) &&
Symbols::CompleterGetFuture().Equals(String::Handle(function.name()));
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
FunctionNodeHelper::kSync &&
!isAsyncAwaitCompleterFuture);
switch (function_node_helper.dart_async_marker_) {
case FunctionNodeHelper::kSyncStar:
function.set_modifier(RawFunction::kSyncGen);
break;
case FunctionNodeHelper::kAsync:
function.set_modifier(RawFunction::kAsync);
function.set_is_inlinable(!FLAG_causal_async_stacks);
break;
case FunctionNodeHelper::kAsyncStar:
function.set_modifier(RawFunction::kAsyncGen);
function.set_is_inlinable(!FLAG_causal_async_stacks);
break;
default:
// no special modifier
break;
}
ASSERT(function_node_helper.async_marker_ == FunctionNodeHelper::kSync);
if (!native_name.IsNull()) {
function.set_native_name(native_name);
}
if (is_potential_native) {
EnsurePotentialNatives();
potential_natives_.Add(function);
}
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
T.SetupFunctionParameters(owner, function, is_method,
false, // is_closure
&function_node_helper);
// Everything else is skipped implicitly, and procedure_helper and
// function_node_helper are no longer used.
helper_.SetOffset(procedure_end);
if (!in_class) {
library.AddObject(function, name);
ASSERT(!Object::Handle(
Z, library.LookupObjectAllowPrivate(
H.DartProcedureName(procedure_helper.canonical_name_)))
.IsNull());
}
if (annotation_count > 0) {
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
procedure_offset);
}
}
const Object& KernelLoader::ClassForScriptAt(const Class& klass,
intptr_t source_uri_index) {
const Script& correct_script = Script::Handle(Z, ScriptAt(source_uri_index));
if (klass.script() != correct_script.raw()) {
// Use cache for patch classes. This works best for in-order usages.
PatchClass& patch_class = PatchClass::ZoneHandle(Z);
patch_class ^= patch_classes_.At(source_uri_index);
if (patch_class.IsNull() || patch_class.origin_class() != klass.raw()) {
ASSERT(!library_kernel_data_.IsNull());
FixCoreLibraryScriptUri(Library::Handle(klass.library()), correct_script);
patch_class = PatchClass::New(klass, correct_script);
patch_class.set_library_kernel_data(library_kernel_data_);
patch_class.set_library_kernel_offset(library_kernel_offset_);
patch_classes_.SetAt(source_uri_index, patch_class);
}
return patch_class;
}
return klass;
}
RawScript* KernelLoader::LoadScriptAt(intptr_t index) {
const String& uri_string = helper_.SourceTableUriFor(index);
String& sources = helper_.GetSourceFor(index);
TypedData& line_starts =
TypedData::Handle(Z, helper_.GetLineStartsFor(index));
if (sources.Length() == 0 && line_starts.Length() == 0 &&
uri_string.Length() > 0) {
// Entry included only to provide URI - actual source should already exist
// in the VM, so try to find it.
Library& lib = Library::Handle(Z);
Script& script = Script::Handle(Z);
const GrowableObjectArray& libs =
GrowableObjectArray::Handle(isolate_->object_store()->libraries());
for (intptr_t i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
script = lib.LookupScript(uri_string, /* useResolvedUri = */ true);
if (!script.IsNull() && script.kind() == RawScript::kKernelTag) {
sources ^= script.Source();
line_starts ^= script.line_starts();
break;
}
}
}
const Script& script = Script::Handle(
Z, Script::New(uri_string, sources, RawScript::kKernelTag));
String& script_url = String::Handle();
script_url = script.url();
script.set_kernel_script_index(index);
script.set_kernel_program_info(kernel_program_info_);
script.set_line_starts(line_starts);
script.set_debug_positions(Array::Handle(Array::null()));
script.set_yield_positions(Array::Handle(Array::null()));
return script.raw();
}
RawScript* KernelLoader::ScriptAt(intptr_t index, StringIndex import_uri) {
if (import_uri != -1) {
const Script& script =
Script::Handle(Z, kernel_program_info_.ScriptAt(index));
script.set_url(H.DartString(import_uri, Heap::kOld));
return script.raw();
}
return kernel_program_info_.ScriptAt(index);
}
void KernelLoader::GenerateFieldAccessors(const Class& klass,
const Field& field,
FieldHelper* field_helper) {
Tag tag = helper_.PeekTag();
if (field_helper->IsStatic() && tag == kNothing) {
// Static fields without an initializer are implicitly initialized to null.
// We do not need a getter.
field.SetStaticValue(Instance::Handle(Z), true);
return;
}
if (tag == kSomething) {
SimpleExpressionConverter converter(&H, &helper_);
const bool has_simple_initializer =
converter.IsSimple(helper_.ReaderOffset() + 1); // ignore the tag.
if (field_helper->IsStatic()) {
// Static fields with initializers either have the static value set to the
// initializer value if it is simple enough or else set to an
// uninitialized sentinel.
if (has_simple_initializer) {
// We do not need a getter.
field.SetStaticValue(converter.SimpleValue(), true);
return;
}
// We do need a getter that evaluates the initializer if necessary.
field.SetStaticValue(Object::sentinel(), true);
} else if (has_simple_initializer) {
// Note: optimizer relies on DoubleInitialized bit in its field-unboxing
// heuristics. See JitCallSpecializer::VisitStoreInstanceField for more
// details.
field.RecordStore(converter.SimpleValue());
if (!converter.SimpleValue().IsNull() &&
converter.SimpleValue().IsDouble()) {
field.set_is_double_initialized(true);
}
}
}
const String& getter_name = H.DartGetterName(field_helper->canonical_name_);
const Object& script_class =
ClassForScriptAt(klass, field_helper->source_uri_index_);
Function& getter = Function::ZoneHandle(
Z,
Function::New(
getter_name,
field_helper->IsStatic() ? RawFunction::kImplicitStaticFinalGetter
: RawFunction::kImplicitGetter,
field_helper->IsStatic(),
// The functions created by the parser have is_const for static fields
// that are const (not just final) and they have is_const for
// non-static
// fields that are final.
field_helper->IsStatic() ? field_helper->IsConst()
: field_helper->IsFinal(),
false, // is_abstract
false, // is_external
false, // is_native
script_class, field_helper->position_));
functions_.Add(&getter);
getter.set_end_token_pos(field_helper->end_position_);
getter.set_kernel_offset(field.kernel_offset());
const AbstractType& field_type = AbstractType::Handle(Z, field.type());
getter.set_result_type(field_type);
getter.set_is_debuggable(false);
SetupFieldAccessorFunction(klass, getter, field_type);
if (!field_helper->IsStatic() && !field_helper->IsFinal()) {
// Only static fields can be const.
ASSERT(!field_helper->IsConst());
const String& setter_name = H.DartSetterName(field_helper->canonical_name_);
Function& setter = Function::ZoneHandle(
Z, Function::New(setter_name, RawFunction::kImplicitSetter,
false, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
script_class, field_helper->position_));
functions_.Add(&setter);
setter.set_end_token_pos(field_helper->end_position_);
setter.set_kernel_offset(field.kernel_offset());
setter.set_result_type(Object::void_type());
setter.set_is_debuggable(false);
SetupFieldAccessorFunction(klass, setter, field_type);
}
}
void KernelLoader::SetupFieldAccessorFunction(const Class& klass,
const Function& function,
const AbstractType& field_type) {
bool is_setter = function.IsImplicitSetterFunction();
bool is_method = !function.IsStaticFunction();
intptr_t parameter_count = (is_method ? 1 : 0) + (is_setter ? 1 : 0);
function.SetNumOptionalParameters(0, false);
function.set_num_fixed_parameters(parameter_count);
function.set_parameter_types(
Array::Handle(Z, Array::New(parameter_count, Heap::kOld)));
function.set_parameter_names(
Array::Handle(Z, Array::New(parameter_count, Heap::kOld)));
intptr_t pos = 0;
if (is_method) {
function.SetParameterTypeAt(pos, T.ReceiverType(klass));
function.SetParameterNameAt(pos, Symbols::This());
pos++;
}
if (is_setter) {
function.SetParameterTypeAt(pos, field_type);
function.SetParameterNameAt(pos, Symbols::Value());
pos++;
}
}
Library& KernelLoader::LookupLibraryOrNull(NameIndex library) {
Library* handle = NULL;
if (!libraries_.Lookup(library, &handle)) {
const String& url = H.DartString(H.CanonicalNameString(library));
handle = &Library::Handle(Z, Library::LookupLibrary(thread_, url));
if (!handle->IsNull()) {
libraries_.Insert(library, handle);
}
}
return *handle;
}
Library& KernelLoader::LookupLibrary(NameIndex library) {
Library* handle = NULL;
if (!libraries_.Lookup(library, &handle)) {
handle = &Library::Handle(Z);
const String& url = H.DartSymbolPlain(H.CanonicalNameString(library));
// We do not register expression evaluation libraries with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
if (url.Equals(Symbols::EvalSourceUri())) {
if (handle->IsNull()) {
*handle = Library::New(url);
expression_evaluation_library_ = handle->raw();
}
} else {
*handle = Library::LookupLibrary(thread_, url);
if (handle->IsNull()) {
*handle = Library::New(url);
handle->Register(thread_);
}
}
ASSERT(!handle->IsNull());
libraries_.Insert(library, handle);
}
return *handle;
}
Class& KernelLoader::LookupClass(NameIndex klass) {
Class* handle = NULL;
if (!classes_.Lookup(klass, &handle)) {
Library& library = LookupLibrary(H.CanonicalNameParent(klass));
const String& name = H.DartClassName(klass);
handle = &Class::Handle(Z, library.LookupLocalClass(name));
if (handle->IsNull()) {
// We do not register expression evaluation classes with the VM:
// The expression evaluation functions should be GC-able as soon as
// they are not reachable anymore and we never look them up by name.
const bool register_class =
library.raw() != expression_evaluation_library_.raw();
*handle = Class::New(library, name, Script::Handle(Z),
TokenPosition::kNoSource, register_class);
if (register_class) {
library.AddClass(*handle);
}
}
// Insert the class in the cache before calling ReadPreliminaryClass so
// we do not risk allocating the class again by calling LookupClass
// recursively from ReadPreliminaryClass for the same class.
classes_.Insert(klass, handle);
}
return *handle;
}
RawFunction::Kind KernelLoader::GetFunctionType(
ProcedureHelper::Kind procedure_kind) {
intptr_t lookuptable[] = {
RawFunction::kRegularFunction, // Procedure::kMethod
RawFunction::kGetterFunction, // Procedure::kGetter
RawFunction::kSetterFunction, // Procedure::kSetter
RawFunction::kRegularFunction, // Procedure::kOperator
RawFunction::kConstructor, // Procedure::kFactory
};
intptr_t kind = static_cast<int>(procedure_kind);
ASSERT(0 <= kind && kind <= ProcedureHelper::kFactory);
return static_cast<RawFunction::Kind>(lookuptable[kind]);
}
RawFunction* CreateFieldInitializerFunction(Thread* thread,
Zone* zone,
const Field& field) {
String& init_name = String::Handle(zone, field.name());
init_name = Symbols::FromConcat(thread, Symbols::InitPrefix(), init_name);
// Static field initializers are not added as members of their owning class,
// so they must be pre-emptively given a patch class to avoid the meaning of
// their kernel/token position changing during a reload. Compare
// Class::PatchFieldsAndFunctions().
// This might also be necessary for lazy computation of local var descriptors.
// Compare https://codereview.chromium.org//1317753004
const Script& script = Script::Handle(zone, field.Script());
const Class& field_owner = Class::Handle(zone, field.Owner());
const PatchClass& initializer_owner =
PatchClass::Handle(zone, PatchClass::New(field_owner, script));
const Library& lib = Library::Handle(zone, field_owner.library());
initializer_owner.set_library_kernel_data(
ExternalTypedData::Handle(zone, lib.kernel_data()));
initializer_owner.set_library_kernel_offset(lib.kernel_offset());
// Create a static initializer.
const Function& initializer_fun = Function::Handle(
zone, Function::New(init_name,
// TODO(alexmarkov): Consider creating a separate
// function kind for field initializers.
RawFunction::kImplicitStaticFinalGetter,
true, // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
initializer_owner, TokenPosition::kNoSource));
initializer_fun.set_kernel_offset(field.kernel_offset());
initializer_fun.set_result_type(AbstractType::Handle(zone, field.type()));
initializer_fun.set_is_debuggable(false);
initializer_fun.set_is_reflectable(false);
initializer_fun.set_is_inlinable(false);
return initializer_fun.raw();
}
ParsedFunction* ParseStaticFieldInitializer(Zone* zone, const Field& field) {
Thread* thread = Thread::Current();
const Function& initializer_fun = Function::ZoneHandle(
zone, CreateFieldInitializerFunction(thread, zone, field));
return new (zone) ParsedFunction(thread, initializer_fun);
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)