mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:44:27 +00:00
62893f9b00
This reverts commit9a87cf9174
. Reason for revert: Broken test disabled Original change's description: > Revert "[vm] Check prefix.loadLibrary is called and returns before prefix members are used." > > This reverts commitb0484ecbde
. > > Reason for revert: timeouts on Flutter integration tests > (https://github.com/dart-lang/sdk/issues/42350). > > Original change's description: > > [vm] Check prefix.loadLibrary is called and returns before prefix members are used. > > > > Restore checks against reloading a library with deferred prefixes. > > > > No loading is actually deferred. > > > > Bug: https://github.com/dart-lang/sdk/issues/26878 > > Bug: https://github.com/dart-lang/sdk/issues/41974 > > Change-Id: Iec2662de117453d596cca28dd9481a9751091ce9 > > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/149613 > > Commit-Queue: Ryan Macnak <rmacnak@google.com> > > Reviewed-by: Alexander Markov <alexmarkov@google.com> > > Reviewed-by: Siva Annamalai <asiva@google.com> > > TBR=rmacnak@google.com,alexmarkov@google.com,asiva@google.com > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Bug: https://github.com/dart-lang/sdk/issues/26878, https://github.com/dart-lang/sdk/issues/41974 > Change-Id: I78709650e91d206b84a8ddd9171ef66d6cf1b008 > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151169 > Reviewed-by: Alexander Markov <alexmarkov@google.com> > Commit-Queue: Alexander Markov <alexmarkov@google.com> TBR=rmacnak@google.com,alexmarkov@google.com,asiva@google.com # Not skipping CQ checks because this is a reland. Bug: https://github.com/dart-lang/sdk/issues/26878, https://github.com/dart-lang/sdk/issues/41974 Change-Id: Ife76bd51db65ca58e08655a9b8406c8ca483447f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151326 Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
2416 lines
95 KiB
C++
2416 lines
95 KiB
C++
// 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.
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/kernel_loader.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/compiler/frontend/constant_reader.h"
|
|
#include "vm/compiler/frontend/kernel_translation_helper.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/heap/heap.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"
|
|
|
|
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);
|
|
};
|
|
|
|
ArrayPtr KernelLoader::MakeFieldsArray() {
|
|
const intptr_t len = fields_.length();
|
|
const Array& res = Array::Handle(zone_, Array::New(len, Heap::kOld));
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
res.SetAt(i, *fields_[i]);
|
|
}
|
|
return res.raw();
|
|
}
|
|
|
|
ArrayPtr 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();
|
|
}
|
|
|
|
LibraryPtr BuildingTranslationHelper::LookupLibraryByKernelLibrary(
|
|
NameIndex library) {
|
|
return loader_->LookupLibrary(library);
|
|
}
|
|
|
|
ClassPtr BuildingTranslationHelper::LookupClassByKernelClass(NameIndex klass) {
|
|
#if defined(DEBUG)
|
|
LibraryLookupHandleScope library_lookup_handle_scope(library_lookup_handle_);
|
|
#endif // defined(DEBUG)
|
|
library_lookup_handle_ = loader_->LookupLibraryFromClass(klass);
|
|
return loader_->LookupClass(library_lookup_handle_, klass);
|
|
}
|
|
|
|
LibraryIndex::LibraryIndex(const ExternalTypedData& kernel_data,
|
|
uint32_t binary_version)
|
|
: reader_(kernel_data), binary_version_(binary_version) {
|
|
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;
|
|
|
|
source_references_offset_ = -1;
|
|
source_references_offset_ = reader_.ReadUInt32At(class_index_offset_ - 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;
|
|
}
|
|
|
|
using UriToSourceTable = DirectChainedHashMap<UriToSourceTableTrait>;
|
|
|
|
KernelLoader::KernelLoader(Program* program,
|
|
UriToSourceTable* uri_to_source_table)
|
|
: program_(program),
|
|
thread_(Thread::Current()),
|
|
zone_(thread_->zone()),
|
|
isolate_(thread_->isolate()),
|
|
patch_classes_(Array::ZoneHandle(zone_)),
|
|
active_class_(),
|
|
library_kernel_offset_(-1), // Set to the correct value in LoadLibrary
|
|
kernel_binary_version_(program->binary_version()),
|
|
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_, Heap::kOld),
|
|
helper_(zone_,
|
|
&translation_helper_,
|
|
program_->kernel_data(),
|
|
program_->kernel_data_size(),
|
|
0),
|
|
constant_reader_(&helper_, &active_class_),
|
|
type_translator_(&helper_,
|
|
&constant_reader_,
|
|
&active_class_,
|
|
/* finalize= */ false),
|
|
inferred_type_metadata_helper_(&helper_, &constant_reader_),
|
|
bytecode_metadata_helper_(&helper_, &active_class_),
|
|
external_name_class_(Class::Handle(Z)),
|
|
external_name_field_(Field::Handle(Z)),
|
|
potential_natives_(GrowableObjectArray::Handle(Z)),
|
|
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
|
|
pragma_class_(Class::Handle(Z)),
|
|
name_index_handle_(Smi::Handle(Z)),
|
|
expression_evaluation_library_(Library::Handle(Z)) {
|
|
if (!program->is_single_program()) {
|
|
FATAL(
|
|
"Trying to load a concatenated dill file at a time where that is "
|
|
"not allowed");
|
|
}
|
|
InitializeFields(uri_to_source_table);
|
|
}
|
|
|
|
void KernelLoader::ReadObfuscationProhibitions() {
|
|
ObfuscationProhibitionsMetadataHelper helper(&helper_);
|
|
helper.ReadProhibitions();
|
|
}
|
|
|
|
Object& KernelLoader::LoadEntireProgram(Program* program,
|
|
bool process_pending_classes) {
|
|
Thread* thread = Thread::Current();
|
|
TIMELINE_DURATION(thread, Isolate, "LoadKernel");
|
|
|
|
if (program->is_single_program()) {
|
|
KernelLoader loader(program, /*uri_to_source_table=*/nullptr);
|
|
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);
|
|
|
|
Zone* zone = thread->zone();
|
|
Library& library = Library::Handle(zone);
|
|
intptr_t subprogram_count = subprogram_file_starts.length() - 1;
|
|
|
|
// First index all source tables.
|
|
UriToSourceTable uri_to_source_table;
|
|
UriToSourceTableEntry wrapper;
|
|
for (intptr_t i = subprogram_count - 1; i >= 0; --i) {
|
|
intptr_t subprogram_start = subprogram_file_starts.At(i);
|
|
intptr_t subprogram_end = subprogram_file_starts.At(i + 1);
|
|
Thread* thread_ = Thread::Current();
|
|
Zone* zone_ = thread_->zone();
|
|
TranslationHelper translation_helper(thread);
|
|
KernelReaderHelper helper_(zone_, &translation_helper,
|
|
program->kernel_data() + subprogram_start,
|
|
subprogram_end - subprogram_start, 0);
|
|
const intptr_t source_table_size = helper_.SourceTableSize();
|
|
for (intptr_t index = 0; index < source_table_size; ++index) {
|
|
const String& uri_string = helper_.SourceTableUriFor(index);
|
|
wrapper.uri = &uri_string;
|
|
TypedData& line_starts =
|
|
TypedData::Handle(Z, helper_.GetLineStartsFor(index));
|
|
if (line_starts.Length() == 0) continue;
|
|
const String& script_source = helper_.GetSourceFor(index);
|
|
wrapper.uri = &uri_string;
|
|
UriToSourceTableEntry* pair = uri_to_source_table.LookupValue(&wrapper);
|
|
if (pair != NULL) {
|
|
// At least two entries with content. Unless the content is the same
|
|
// that's not valid.
|
|
if (pair->sources->CompareTo(script_source) != 0 ||
|
|
!pair->line_starts->CanonicalizeEquals(line_starts)) {
|
|
FATAL(
|
|
"Invalid kernel binary: Contains at least two source entries "
|
|
"that do not agree.");
|
|
}
|
|
} else {
|
|
UriToSourceTableEntry* tmp = new UriToSourceTableEntry();
|
|
tmp->uri = &uri_string;
|
|
tmp->sources = &script_source;
|
|
tmp->line_starts = &line_starts;
|
|
uri_to_source_table.Insert(tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create "fake programs" for each sub-program.
|
|
for (intptr_t i = subprogram_count - 1; i >= 0; --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);
|
|
std::unique_ptr<Program> subprogram = Program::ReadFrom(&reader);
|
|
ASSERT(subprogram->is_single_program());
|
|
KernelLoader loader(subprogram.get(), &uri_to_source_table);
|
|
Object& load_result = Object::Handle(loader.LoadProgram(false));
|
|
if (load_result.IsError()) return load_result;
|
|
|
|
if (load_result.IsLibrary()) {
|
|
library ^= load_result.raw();
|
|
}
|
|
}
|
|
|
|
if (process_pending_classes && !ClassFinalizer::ProcessPendingClasses()) {
|
|
// Class finalization failed -> sticky error would be set.
|
|
return Error::Handle(thread->StealStickyError());
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
StringPtr KernelLoader::FindSourceForScript(const uint8_t* kernel_buffer,
|
|
intptr_t kernel_buffer_length,
|
|
const String& uri) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
TranslationHelper translation_helper(thread);
|
|
KernelReaderHelper reader(zone, &translation_helper, kernel_buffer,
|
|
kernel_buffer_length, 0);
|
|
intptr_t source_table_size = reader.SourceTableSize();
|
|
for (intptr_t i = 0; i < source_table_size; ++i) {
|
|
const String& source_uri = reader.SourceTableUriFor(i);
|
|
if (source_uri.EndsWith(uri)) {
|
|
return reader.GetSourceFor(i).raw();
|
|
}
|
|
}
|
|
return String::null();
|
|
}
|
|
|
|
void KernelLoader::InitializeFields(UriToSourceTable* uri_to_source_table) {
|
|
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()));
|
|
ASSERT(Utils::IsAligned(metadata_payloads.DataAddr(0), kWordSize));
|
|
|
|
// Create view of metadata mappings.
|
|
const ExternalTypedData& metadata_mappings = ExternalTypedData::Handle(
|
|
Z, reader.ExternalDataFromTo(program_->metadata_mappings_offset(),
|
|
program_->string_table_offset()));
|
|
|
|
#if defined(DEBUG)
|
|
MetadataHelper::VerifyMetadataMappings(metadata_mappings);
|
|
#endif
|
|
|
|
const Array& libraries_cache =
|
|
Array::Handle(Z, HashTables::New<UnorderedHashMap<SmiTraits>>(
|
|
program_->library_count(), Heap::kOld));
|
|
|
|
const intptr_t kClassesPerLibraryGuess = 5;
|
|
const Array& classes_cache = Array::Handle(
|
|
Z, HashTables::New<UnorderedHashMap<SmiTraits>>(
|
|
kClassesPerLibraryGuess * program_->library_count(), Heap::kOld));
|
|
|
|
kernel_program_info_ = KernelProgramInfo::New(
|
|
offsets, data, names, metadata_payloads, metadata_mappings,
|
|
constants_table, scripts, libraries_cache, classes_cache,
|
|
program_->typed_data() == nullptr ? Object::null_object()
|
|
: *program_->typed_data(),
|
|
program_->binary_version());
|
|
|
|
H.InitFromKernelProgramInfo(kernel_program_info_);
|
|
|
|
Script& script = Script::Handle(Z);
|
|
for (intptr_t index = 0; index < source_table_size; ++index) {
|
|
script = LoadScriptAt(index, uri_to_source_table);
|
|
scripts.SetAt(index, script);
|
|
}
|
|
|
|
bytecode_metadata_helper_.ReadBytecodeComponent();
|
|
}
|
|
|
|
KernelLoader::KernelLoader(const Script& script,
|
|
const ExternalTypedData& kernel_data,
|
|
intptr_t data_program_offset,
|
|
uint32_t kernel_binary_version)
|
|
: program_(NULL),
|
|
thread_(Thread::Current()),
|
|
zone_(thread_->zone()),
|
|
isolate_(thread_->isolate()),
|
|
patch_classes_(Array::ZoneHandle(zone_)),
|
|
library_kernel_offset_(data_program_offset),
|
|
kernel_binary_version_(kernel_binary_version),
|
|
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_, Heap::kOld),
|
|
helper_(zone_, &translation_helper_, script, kernel_data, 0),
|
|
constant_reader_(&helper_, &active_class_),
|
|
type_translator_(&helper_,
|
|
&constant_reader_,
|
|
&active_class_,
|
|
/* finalize= */ false),
|
|
inferred_type_metadata_helper_(&helper_, &constant_reader_),
|
|
bytecode_metadata_helper_(&helper_, &active_class_),
|
|
external_name_class_(Class::Handle(Z)),
|
|
external_name_field_(Field::Handle(Z)),
|
|
potential_natives_(GrowableObjectArray::Handle(Z)),
|
|
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
|
|
pragma_class_(Class::Handle(Z)),
|
|
name_index_handle_(Smi::Handle(Z)),
|
|
expression_evaluation_library_(Library::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_);
|
|
}
|
|
|
|
void KernelLoader::EvaluateDelayedPragmas() {
|
|
potential_pragma_functions_ =
|
|
kernel_program_info_.potential_pragma_functions();
|
|
if (potential_pragma_functions_.IsNull()) return;
|
|
|
|
Thread* thread = Thread::Current();
|
|
NoOOBMessageScope no_msg_scope(thread);
|
|
NoReloadScope no_reload_scope(thread->isolate(), thread);
|
|
|
|
Function& function = Function::Handle();
|
|
Library& library = Library::Handle();
|
|
Class& klass = Class::Handle();
|
|
for (int i = 0; i < potential_pragma_functions_.Length(); ++i) {
|
|
function ^= potential_pragma_functions_.At(i);
|
|
klass = function.Owner();
|
|
library = klass.library();
|
|
library.GetMetadata(function);
|
|
}
|
|
|
|
potential_pragma_functions_ = GrowableObjectArray::null();
|
|
kernel_program_info_.set_potential_pragma_functions(
|
|
GrowableObjectArray::Handle(Z));
|
|
}
|
|
|
|
void KernelLoader::AnnotateNativeProcedures() {
|
|
potential_natives_ = kernel_program_info_.potential_natives();
|
|
const intptr_t length =
|
|
!potential_natives_.IsNull() ? potential_natives_.Length() : 0;
|
|
if (length == 0) return;
|
|
|
|
// Prepare lazy constant reading.
|
|
ConstantReader constant_reader(&helper_, &active_class_);
|
|
|
|
// 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.
|
|
helper_.ReadPosition(); // Skip fileOffset.
|
|
helper_.SkipDartType(); // Skip type.
|
|
|
|
// We have a candidate. Let's look if it's an instance of the
|
|
// ExternalName class.
|
|
const intptr_t constant_table_offset = helper_.ReadUInt();
|
|
if (constant_reader.IsInstanceConstant(constant_table_offset,
|
|
external_name_class_)) {
|
|
constant = constant_reader.ReadConstant(constant_table_offset);
|
|
ASSERT(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_);
|
|
}
|
|
|
|
StringPtr 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 auto& potential_extension_libraries =
|
|
GrowableObjectArray::Handle(Z, H.GetPotentialExtensionLibraries());
|
|
if (potential_extension_libraries.IsNull()) {
|
|
return;
|
|
}
|
|
|
|
// Prepare lazy constant reading.
|
|
ConstantReader constant_reader(&helper_, &active_class_);
|
|
|
|
// Obtain `dart:_internal::ExternalName.name`.
|
|
EnsureExternalClassIsLookedUp();
|
|
|
|
Instance& constant = Instance::Handle(Z);
|
|
String& uri_path = String::Handle(Z);
|
|
Library& library = Library::Handle(Z);
|
|
|
|
const intptr_t length = potential_extension_libraries.Length();
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
library ^= potential_extension_libraries.At(i);
|
|
|
|
if (library.is_declared_in_bytecode()) {
|
|
const auto& imports = Array::Handle(Z, library.imports());
|
|
auto& ns = Namespace::Handle(Z);
|
|
auto& importee = Library::Handle(Z);
|
|
for (intptr_t j = 0; j < imports.Length(); ++j) {
|
|
ns ^= imports.At(j);
|
|
if (ns.IsNull()) continue;
|
|
importee = ns.library();
|
|
uri_path = importee.url();
|
|
if (uri_path.StartsWith(Symbols::DartExtensionScheme())) {
|
|
LoadNativeExtension(library, uri_path);
|
|
}
|
|
}
|
|
} else {
|
|
helper_.SetOffset(library.kernel_offset());
|
|
|
|
LibraryHelper library_helper(&helper_, kernel_binary_version_);
|
|
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.
|
|
helper_.ReadPosition(); // Skip fileOffset.
|
|
helper_.SkipDartType(); // Skip type.
|
|
|
|
// We have a candidate. Let's look if it's an instance of the
|
|
// ExternalName class.
|
|
const intptr_t constant_table_offset = helper_.ReadUInt();
|
|
if (constant_reader.IsInstanceConstant(constant_table_offset,
|
|
external_name_class_)) {
|
|
constant = constant_reader.ReadConstant(constant_table_offset);
|
|
ASSERT(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;
|
|
|
|
LoadNativeExtension(library, uri_path);
|
|
|
|
// Create a dummy library and add it as an import to the current
|
|
// library. This allows later to discover and reload this native
|
|
// extension, e.g. when running from an app-jit snapshot.
|
|
// See Loader::ReloadNativeExtensions(...) which relies on
|
|
// Dart_GetImportsOfScheme('dart-ext').
|
|
const auto& native_library = Library::Handle(Library::New(uri_path));
|
|
library.AddImport(Namespace::Handle(Namespace::New(
|
|
native_library, Array::null_array(), Array::null_array())));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KernelLoader::LoadNativeExtension(const Library& library,
|
|
const String& uri_path) {
|
|
#if !defined(DART_PRECOMPILER)
|
|
if (!I->HasTagHandler()) {
|
|
H.ReportError("no library handler registered.");
|
|
}
|
|
|
|
I->BlockClassFinalization();
|
|
const auto& result = Object::Handle(
|
|
Z, I->CallTagHandler(Dart_kImportExtensionTag, library, uri_path));
|
|
I->UnblockClassFinalization();
|
|
|
|
if (result.IsError()) {
|
|
H.ReportError(Error::Cast(result), "library handler failed");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ObjectPtr 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) {
|
|
if (!bytecode_metadata_helper_.ReadLibraries()) {
|
|
// Note that `problemsAsJson` on Component is implicitly skipped.
|
|
const intptr_t length = program_->library_count();
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
LoadLibrary(i);
|
|
}
|
|
}
|
|
|
|
// Finalize still pending classes if requested.
|
|
if (process_pending_classes) {
|
|
if (!ClassFinalizer::ProcessPendingClasses()) {
|
|
// Class finalization failed -> sticky error would be set.
|
|
return H.thread()->StealStickyError();
|
|
}
|
|
}
|
|
|
|
// Sets the constants array to an empty hash and leaves the constant
|
|
// table's raw bytes in place for lazy reading. We can fix up all
|
|
// "pending" processing now, and must ensure we don't create new
|
|
// ones from this point on.
|
|
ASSERT(kernel_program_info_.constants_table() != ExternalTypedData::null());
|
|
const Array& array =
|
|
Array::Handle(Z, HashTables::New<KernelConstantsMap>(16, Heap::kOld));
|
|
kernel_program_info_.set_constants(array);
|
|
H.SetConstants(array); // for caching
|
|
AnnotateNativeProcedures();
|
|
LoadNativeExtensionLibraries();
|
|
EvaluateDelayedPragmas();
|
|
|
|
NameIndex main = program_->main_method();
|
|
if (main != -1) {
|
|
NameIndex main_library = H.EnclosingName(main);
|
|
return LookupLibrary(main_library);
|
|
}
|
|
|
|
return bytecode_metadata_helper_.GetMainLibrary();
|
|
}
|
|
|
|
// Either class finalization failed or we caught a compile error.
|
|
// In both cases sticky error would be set.
|
|
return Thread::Current()->StealStickyError();
|
|
}
|
|
|
|
void KernelLoader::LoadLibrary(const Library& library) {
|
|
ASSERT(!library.Loaded());
|
|
|
|
bytecode_metadata_helper_.ReadLibrary(library);
|
|
if (library.Loaded()) {
|
|
return;
|
|
}
|
|
const auto& uri = String::Handle(Z, library.url());
|
|
const intptr_t num_libraries = program_->library_count();
|
|
for (intptr_t i = 0; i < num_libraries; ++i) {
|
|
const String& library_uri = LibraryUri(i);
|
|
if (library_uri.Equals(uri)) {
|
|
LoadLibrary(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ObjectPtr 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_, 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(H.GetExpressionEvaluationFunction().IsNull());
|
|
H.SetExpressionEvaluationRealClass(real_class);
|
|
const Object& result = Object::Handle(Z, LoadProgram(true));
|
|
if (result.IsError()) {
|
|
return result.raw();
|
|
}
|
|
const Function& function = H.GetExpressionEvaluationFunction();
|
|
ASSERT(!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 script,
|
|
// kernel data and parent.
|
|
const auto& eval_script = Script::Handle(Z, function.script());
|
|
auto& kernel_data = ExternalTypedData::Handle(Z);
|
|
intptr_t kernel_offset = -1;
|
|
if (!function.is_declared_in_bytecode()) {
|
|
ASSERT(!expression_evaluation_library_.IsNull());
|
|
kernel_data = expression_evaluation_library_.kernel_data();
|
|
kernel_offset = expression_evaluation_library_.kernel_offset();
|
|
}
|
|
function.SetKernelDataAndScript(eval_script, kernel_data, kernel_offset);
|
|
|
|
function.set_owner(real_class);
|
|
|
|
return function.raw();
|
|
}
|
|
|
|
void KernelLoader::FindModifiedLibraries(Program* program,
|
|
Isolate* isolate,
|
|
BitVector* modified_libs,
|
|
bool force_reload,
|
|
bool* is_empty_program,
|
|
intptr_t* p_num_classes,
|
|
intptr_t* p_num_procedures) {
|
|
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;
|
|
}
|
|
|
|
if (p_num_classes != nullptr) {
|
|
*p_num_classes = 0;
|
|
}
|
|
if (p_num_procedures != nullptr) {
|
|
*p_num_procedures = 0;
|
|
}
|
|
|
|
// 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, /*uri_to_source_table=*/nullptr);
|
|
loader.walk_incremental_kernel(modified_libs, is_empty_program,
|
|
p_num_classes, p_num_procedures);
|
|
}
|
|
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);
|
|
std::unique_ptr<Program> subprogram = Program::ReadFrom(&reader);
|
|
ASSERT(subprogram->is_single_program());
|
|
KernelLoader loader(subprogram.get(), /*uri_to_source_table=*/nullptr);
|
|
loader.walk_incremental_kernel(modified_libs, is_empty_program,
|
|
p_num_classes, p_num_procedures);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KernelLoader::walk_incremental_kernel(BitVector* modified_libs,
|
|
bool* is_empty_program,
|
|
intptr_t* p_num_classes,
|
|
intptr_t* p_num_procedures) {
|
|
if (bytecode_metadata_helper_.FindModifiedLibrariesForHotReload(
|
|
modified_libs, is_empty_program, p_num_classes, p_num_procedures)) {
|
|
return;
|
|
}
|
|
intptr_t length = program_->library_count();
|
|
*is_empty_program = *is_empty_program && (length == 0);
|
|
bool collect_library_stats =
|
|
p_num_classes != nullptr || p_num_procedures != nullptr;
|
|
intptr_t num_classes = 0;
|
|
intptr_t num_procedures = 0;
|
|
Library& lib = Library::Handle(Z);
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
intptr_t kernel_offset = library_offset(i);
|
|
helper_.SetOffset(kernel_offset);
|
|
LibraryHelper library_helper(&helper_, kernel_binary_version_);
|
|
library_helper.ReadUntilIncluding(LibraryHelper::kCanonicalName);
|
|
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());
|
|
}
|
|
if (collect_library_stats) {
|
|
intptr_t library_end = library_offset(i + 1);
|
|
library_kernel_data_ =
|
|
helper_.reader_.ExternalDataFromTo(kernel_offset, library_end);
|
|
LibraryIndex library_index(library_kernel_data_, kernel_binary_version_);
|
|
num_classes += library_index.class_count();
|
|
num_procedures += library_index.procedure_count();
|
|
}
|
|
}
|
|
if (p_num_classes != nullptr) {
|
|
*p_num_classes += num_classes;
|
|
}
|
|
if (p_num_procedures != nullptr) {
|
|
*p_num_procedures += num_procedures;
|
|
}
|
|
}
|
|
|
|
void KernelLoader::ReadInferredType(const Field& field,
|
|
intptr_t kernel_offset) {
|
|
const InferredTypeMetadata type =
|
|
inferred_type_metadata_helper_.GetInferredType(kernel_offset,
|
|
/*read_constant=*/false);
|
|
if (type.IsTrivial()) {
|
|
return;
|
|
}
|
|
field.set_guarded_cid(type.cid);
|
|
field.set_is_nullable(type.IsNullable());
|
|
field.set_guarded_list_length(Field::kNoFixedLength);
|
|
if (FLAG_precompiled_mode) {
|
|
field.set_is_unboxing_candidate(
|
|
!field.is_late() && !field.is_static() &&
|
|
((field.guarded_cid() == kDoubleCid &&
|
|
FlowGraphCompiler::SupportsUnboxedDoubles()) ||
|
|
(field.guarded_cid() == kFloat32x4Cid &&
|
|
FlowGraphCompiler::SupportsUnboxedSimd128()) ||
|
|
(field.guarded_cid() == kFloat64x2Cid &&
|
|
FlowGraphCompiler::SupportsUnboxedSimd128()) ||
|
|
type.IsInt()) &&
|
|
!field.is_nullable());
|
|
field.set_is_non_nullable_integer(!field.is_nullable() && type.IsInt());
|
|
}
|
|
}
|
|
|
|
void KernelLoader::CheckForInitializer(const Field& field) {
|
|
if (helper_.PeekTag() == kSomething) {
|
|
field.set_has_initializer(true);
|
|
SimpleExpressionConverter converter(&H, &helper_);
|
|
const bool has_simple_initializer =
|
|
converter.IsSimple(helper_.ReaderOffset() + 1);
|
|
if (!has_simple_initializer || !converter.SimpleValue().IsNull()) {
|
|
field.set_has_nontrivial_initializer(true);
|
|
return;
|
|
}
|
|
}
|
|
field.set_has_initializer(false);
|
|
field.set_has_nontrivial_initializer(false);
|
|
}
|
|
|
|
LibraryPtr 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_, kernel_binary_version_);
|
|
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_));
|
|
|
|
// The Kernel library is external implies that it is already loaded.
|
|
ASSERT(!library_helper.IsExternal() || library.Loaded());
|
|
if (library.Loaded()) return library.raw();
|
|
|
|
library.set_is_nnbd(library_helper.IsNonNullableByDefault());
|
|
const NNBDCompiledMode mode =
|
|
library_helper.GetNonNullableByDefaultCompiledMode();
|
|
if (!I->null_safety() && mode == NNBDCompiledMode::kStrong) {
|
|
H.ReportError(
|
|
"Library '%s' was compiled with null safety (in strong mode) and it "
|
|
"requires --null-safety option at runtime",
|
|
String::Handle(library.url()).ToCString());
|
|
}
|
|
if (I->null_safety() && (mode == NNBDCompiledMode::kWeak ||
|
|
mode == NNBDCompiledMode::kDisabled)) {
|
|
H.ReportError(
|
|
"Library '%s' was compiled without null safety (in weak mode) and it "
|
|
"cannot be used with --null-safety at runtime",
|
|
String::Handle(library.url()).ToCString());
|
|
}
|
|
library.set_nnbd_compiled_mode(mode);
|
|
|
|
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_, kernel_binary_version_);
|
|
intptr_t class_count = library_index.class_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();
|
|
}
|
|
|
|
library_helper.ReadUntilIncluding(LibraryHelper::kSourceUriIndex);
|
|
const Script& script =
|
|
Script::Handle(Z, ScriptAt(library_helper.source_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) {
|
|
// This must wait until we can evaluate constants.
|
|
// So put on the "pending" list.
|
|
H.AddPotentialExtensionLibrary(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_declaration_loaded();
|
|
toplevel_class.set_is_type_finalized();
|
|
library.set_toplevel_class(toplevel_class);
|
|
|
|
library_helper.ReadUntilExcluding(LibraryHelper::kDependencies);
|
|
LoadLibraryImportsAndExports(&library, toplevel_class);
|
|
library_helper.SetJustRead(LibraryHelper::kDependencies);
|
|
|
|
// Everything up til the classes are skipped implicitly, and library_helper
|
|
// is no longer used.
|
|
|
|
const GrowableObjectArray& classes =
|
|
GrowableObjectArray::Handle(Z, I->object_store()->pending_classes());
|
|
|
|
// Load all classes.
|
|
intptr_t next_class_offset = library_index.ClassOffset(0);
|
|
Class& klass = Class::Handle(Z);
|
|
for (intptr_t i = 0; i < class_count; ++i) {
|
|
helper_.SetOffset(next_class_offset);
|
|
next_class_offset = library_index.ClassOffset(i + 1);
|
|
LoadClass(library, toplevel_class, next_class_offset, &klass);
|
|
if (register_class) {
|
|
classes.Add(klass, Heap::kOld);
|
|
}
|
|
}
|
|
|
|
if (loading_native_wrappers_library_ || !register_class) {
|
|
FinishTopLevelClassLoading(toplevel_class, library, library_index);
|
|
}
|
|
|
|
if (FLAG_enable_mirrors && annotation_count > 0) {
|
|
ASSERT(annotations_kernel_offset > 0);
|
|
library.AddLibraryMetadata(toplevel_class, TokenPosition::kNoSource,
|
|
annotations_kernel_offset, 0);
|
|
}
|
|
|
|
if (register_class) {
|
|
helper_.SetOffset(library_index.SourceReferencesOffset());
|
|
intptr_t count = helper_.ReadUInt();
|
|
const GrowableObjectArray& used_scripts =
|
|
GrowableObjectArray::Handle(library.used_scripts());
|
|
Script& script = Script::Handle(Z);
|
|
for (intptr_t i = 0; i < count; i++) {
|
|
intptr_t uri_index = helper_.ReadUInt();
|
|
script = ScriptAt(uri_index);
|
|
used_scripts.Add(script);
|
|
}
|
|
}
|
|
if (!library.Loaded()) library.SetLoaded();
|
|
|
|
return library.raw();
|
|
}
|
|
|
|
void KernelLoader::FinishTopLevelClassLoading(
|
|
const Class& toplevel_class,
|
|
const Library& library,
|
|
const LibraryIndex& library_index) {
|
|
if (toplevel_class.is_loaded()) {
|
|
return;
|
|
}
|
|
TIMELINE_DURATION(Thread::Current(), Isolate, "FinishTopLevelClassLoading");
|
|
|
|
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
|
|
|
|
// Offsets within library index are whole program offsets and not
|
|
// relative to the library.
|
|
const intptr_t correction = correction_offset_ - library_kernel_offset_;
|
|
helper_.SetOffset(library_index.ClassOffset(library_index.class_count()) +
|
|
correction);
|
|
|
|
if (kernel_binary_version_ >= 30) {
|
|
const intptr_t extension_count = helper_.ReadListLength();
|
|
for (intptr_t i = 0; i < extension_count; ++i) {
|
|
helper_.ReadTag(); // read tag.
|
|
helper_.SkipCanonicalNameReference(); // skip canonical name.
|
|
helper_.SkipStringReference(); // skip name.
|
|
helper_.ReadUInt(); // read source uri index.
|
|
helper_.ReadPosition(); // read file offset.
|
|
helper_.SkipTypeParametersList(); // skip type parameter list.
|
|
helper_.SkipDartType(); // skip on-type.
|
|
|
|
const intptr_t extension_member_count = helper_.ReadListLength();
|
|
for (intptr_t j = 0; j < extension_member_count; ++j) {
|
|
helper_.SkipName(); // skip name.
|
|
helper_.ReadByte(); // read kind.
|
|
helper_.ReadByte(); // read flags.
|
|
helper_.SkipCanonicalNameReference(); // skip member reference
|
|
}
|
|
}
|
|
}
|
|
|
|
fields_.Clear();
|
|
functions_.Clear();
|
|
|
|
// Load toplevel fields.
|
|
const 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(library, annotation_count, &native_name_unused,
|
|
&is_potential_native_unused, &has_pragma_annotation);
|
|
}
|
|
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();
|
|
// Only instance fields could be covariant.
|
|
ASSERT(!field_helper.IsCovariant() &&
|
|
!field_helper.IsGenericCovariantImpl());
|
|
const bool is_late = field_helper.IsLate();
|
|
const bool is_extension_member = field_helper.IsExtensionMember();
|
|
const Field& field = Field::Handle(
|
|
Z, Field::NewTopLevel(name, is_final, field_helper.IsConst(), is_late,
|
|
script_class, field_helper.position_,
|
|
field_helper.end_position_));
|
|
field.set_kernel_offset(field_offset);
|
|
field.set_has_pragma(has_pragma_annotation);
|
|
field.set_is_extension_member(is_extension_member);
|
|
const AbstractType& type = T.BuildType(); // read type.
|
|
field.SetFieldType(type);
|
|
ReadInferredType(field, field_offset + library_kernel_offset_);
|
|
CheckForInitializer(field);
|
|
// In NNBD libraries, static fields with initializers are
|
|
// implicitly late.
|
|
if (field.has_initializer() && library.is_nnbd()) {
|
|
field.set_is_late(true);
|
|
}
|
|
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,
|
|
0);
|
|
}
|
|
fields_.Add(&field);
|
|
}
|
|
|
|
ASSERT(!toplevel_class.is_loaded());
|
|
|
|
// Load toplevel procedures.
|
|
intptr_t next_procedure_offset =
|
|
library_index.ProcedureOffset(0) + correction;
|
|
const intptr_t procedure_count = library_index.procedure_count();
|
|
for (intptr_t i = 0; i < procedure_count; ++i) {
|
|
helper_.SetOffset(next_procedure_offset);
|
|
next_procedure_offset = library_index.ProcedureOffset(i + 1) + correction;
|
|
LoadProcedure(library, toplevel_class, false, next_procedure_offset);
|
|
// LoadProcedure calls Library::GetMetadata which invokes Dart code
|
|
// which may recursively trigger class finalization and
|
|
// FinishTopLevelClassLoading.
|
|
// In such case, return immediately and avoid overwriting already finalized
|
|
// functions with freshly loaded and not yet finalized.
|
|
if (toplevel_class.is_loaded()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
toplevel_class.SetFields(Array::Handle(MakeFieldsArray()));
|
|
toplevel_class.SetFunctions(Array::Handle(MakeFunctionsArray()));
|
|
|
|
String& name = String::Handle(Z);
|
|
for (intptr_t i = 0, n = fields_.length(); i < n; ++i) {
|
|
const Field* field = fields_.At(i);
|
|
name = field->name();
|
|
library.AddObject(*field, name);
|
|
}
|
|
for (intptr_t i = 0, n = functions_.length(); i < n; ++i) {
|
|
const Function* function = functions_.At(i);
|
|
name = function->name();
|
|
library.AddObject(*function, name);
|
|
}
|
|
|
|
ASSERT(!toplevel_class.is_loaded());
|
|
toplevel_class.set_is_loaded(true);
|
|
}
|
|
|
|
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();
|
|
const Array& deps = Array::Handle(Array::New(deps_count));
|
|
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) != 0) {
|
|
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 = Library::Handle(
|
|
Z, LookupLibrary(dependency_helper.target_library_canonical_name_));
|
|
if (!FLAG_enable_mirrors &&
|
|
target_library.url() == Symbols::DartMirrors().raw()) {
|
|
H.ReportError(
|
|
"import of dart:mirrors is not supported in the current Dart "
|
|
"runtime");
|
|
}
|
|
if (!Api::IsFfiEnabled() &&
|
|
target_library.url() == Symbols::DartFfi().raw()) {
|
|
H.ReportError(
|
|
"import of dart:ffi is not supported in the current Dart runtime");
|
|
}
|
|
String& prefix = H.DartSymbolPlain(dependency_helper.name_index_);
|
|
ns = Namespace::New(target_library, show_names, hide_names);
|
|
if ((dependency_helper.flags_ & LibraryDependencyHelper::Export) != 0) {
|
|
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) !=
|
|
0,
|
|
*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);
|
|
}
|
|
|
|
if (prefix.IsNull()) {
|
|
deps.SetAt(dep, ns);
|
|
} else {
|
|
deps.SetAt(dep, library_prefix);
|
|
}
|
|
}
|
|
|
|
library->set_dependencies(deps);
|
|
}
|
|
|
|
void KernelLoader::LoadPreliminaryClass(ClassHelper* class_helper,
|
|
intptr_t type_parameter_count) {
|
|
const Class* klass = active_class_.klass;
|
|
|
|
// Enable access to type_parameters().
|
|
klass->set_is_declaration_loaded();
|
|
|
|
// 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).
|
|
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.
|
|
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();
|
|
}
|
|
if (class_helper->has_const_constructor()) {
|
|
klass->set_is_const();
|
|
}
|
|
}
|
|
|
|
void KernelLoader::LoadClass(const Library& library,
|
|
const Class& toplevel_class,
|
|
intptr_t class_end,
|
|
Class* out_class) {
|
|
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);
|
|
*out_class = LookupClass(library, class_helper.canonical_name_);
|
|
out_class->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 (out_class->script() == Script::null()) {
|
|
class_helper.ReadUntilIncluding(ClassHelper::kSourceUriIndex);
|
|
const Script& script =
|
|
Script::Handle(Z, ScriptAt(class_helper.source_uri_index_));
|
|
out_class->set_script(script);
|
|
}
|
|
if (out_class->token_pos() == TokenPosition::kNoSource) {
|
|
class_helper.ReadUntilIncluding(ClassHelper::kEndPosition);
|
|
out_class->set_token_pos(class_helper.start_position_);
|
|
out_class->set_end_token_pos(class_helper.end_position_);
|
|
}
|
|
|
|
class_helper.ReadUntilIncluding(ClassHelper::kFlags);
|
|
if (class_helper.is_enum_class()) {
|
|
out_class->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(library, annotation_count, &native_name_unused,
|
|
&is_potential_native_unused, &has_pragma_annotation);
|
|
}
|
|
if (has_pragma_annotation) {
|
|
out_class->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_, out_class);
|
|
if (!out_class->is_declaration_loaded()) {
|
|
LoadPreliminaryClass(&class_helper, type_parameter_counts);
|
|
} else {
|
|
ASSERT(type_parameter_counts == 0);
|
|
class_helper.SetJustRead(ClassHelper::kTypeParameters);
|
|
}
|
|
|
|
if ((FLAG_enable_mirrors || has_pragma_annotation) && annotation_count > 0) {
|
|
library.AddClassMetadata(*out_class, toplevel_class,
|
|
TokenPosition::kNoSource,
|
|
class_offset - correction_offset_, 0);
|
|
}
|
|
|
|
// 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(*out_class, library, toplevel_class, class_offset,
|
|
class_index, &class_helper);
|
|
}
|
|
|
|
helper_.SetOffset(class_end);
|
|
}
|
|
|
|
void KernelLoader::FinishClassLoading(const Class& klass,
|
|
const Library& library,
|
|
const Class& toplevel_class,
|
|
intptr_t class_offset,
|
|
const ClassIndex& class_index,
|
|
ClassHelper* class_helper) {
|
|
if (klass.is_loaded()) {
|
|
return;
|
|
}
|
|
|
|
TIMELINE_DURATION(Thread::Current(), Isolate, "FinishClassLoading");
|
|
|
|
ActiveClassScope active_class_scope(&active_class_, &klass);
|
|
|
|
// If this is a dart:internal.ClassID class ignore field declarations
|
|
// contained in the Kernel file and instead inject our own const
|
|
// fields.
|
|
const bool discard_fields = klass.InjectCIDFields();
|
|
|
|
fields_.Clear();
|
|
functions_.Clear();
|
|
if (!discard_fields) {
|
|
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(library, annotation_count, &native_name_unused,
|
|
&is_potential_native_unused, &has_pragma_annotation);
|
|
}
|
|
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();
|
|
const bool is_late = field_helper.IsLate();
|
|
const bool is_extension_member = field_helper.IsExtensionMember();
|
|
Field& field = Field::Handle(
|
|
Z, Field::New(name, field_helper.IsStatic(), is_final,
|
|
field_helper.IsConst(), is_reflectable, is_late,
|
|
script_class, type, field_helper.position_,
|
|
field_helper.end_position_));
|
|
field.set_kernel_offset(field_offset);
|
|
field.set_has_pragma(has_pragma_annotation);
|
|
field.set_is_covariant(field_helper.IsCovariant());
|
|
field.set_is_generic_covariant_impl(
|
|
field_helper.IsGenericCovariantImpl());
|
|
field.set_is_extension_member(is_extension_member);
|
|
ReadInferredType(field, field_offset + library_kernel_offset_);
|
|
CheckForInitializer(field);
|
|
// In NNBD libraries, static fields with initializers are
|
|
// implicitly late.
|
|
if (field_helper.IsStatic() && field.has_initializer() &&
|
|
library.is_nnbd()) {
|
|
field.set_is_late(true);
|
|
}
|
|
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,
|
|
0);
|
|
}
|
|
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,
|
|
/* is_late = */ false, klass, Object::dynamic_type(),
|
|
TokenPosition::kNoSource, TokenPosition::kNoSource);
|
|
fields_.Add(&deleted_enum_sentinel);
|
|
}
|
|
|
|
// Due to ReadVMAnnotations(), the klass may have been loaded at this point
|
|
// (loading the class while evaluating annotations).
|
|
if (klass.is_loaded()) {
|
|
return;
|
|
}
|
|
|
|
klass.SetFields(Array::Handle(Z, MakeFieldsArray()));
|
|
}
|
|
|
|
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(library, 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, FunctionLayout::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);
|
|
T.SetupUnboxingInfoMetadata(function, library_kernel_offset_);
|
|
|
|
if (library.is_dart_scheme() &&
|
|
H.IsPrivate(constructor_helper.canonical_name_)) {
|
|
function.set_is_reflectable(false);
|
|
}
|
|
|
|
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, 0);
|
|
}
|
|
}
|
|
|
|
// Due to ReadVMAnnotations(), the klass may have been loaded at this point
|
|
// (loading the class while evaluating annotations).
|
|
if (klass.is_loaded()) {
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
// LoadProcedure calls Library::GetMetadata which invokes Dart code
|
|
// which may recursively trigger class finalization and FinishClassLoading.
|
|
// In such case, return immediately and avoid overwriting already finalized
|
|
// functions with freshly loaded and not yet finalized.
|
|
if (klass.is_loaded()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
klass.SetFunctions(Array::Handle(MakeFunctionsArray()));
|
|
|
|
ASSERT(!klass.is_loaded());
|
|
klass.set_is_loaded(true);
|
|
}
|
|
|
|
void KernelLoader::FinishLoading(const Class& klass) {
|
|
ASSERT(!klass.is_declared_in_bytecode());
|
|
ASSERT(klass.IsTopLevel() || (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 KernelProgramInfo& info =
|
|
KernelProgramInfo::Handle(zone, script.kernel_program_info());
|
|
|
|
KernelLoader kernel_loader(script, library_kernel_data, library_kernel_offset,
|
|
info.kernel_binary_version());
|
|
LibraryIndex library_index(library_kernel_data, info.kernel_binary_version());
|
|
|
|
if (klass.IsTopLevel()) {
|
|
ASSERT(klass.raw() == toplevel_class.raw());
|
|
kernel_loader.FinishTopLevelClassLoading(klass, library, library_index);
|
|
return;
|
|
}
|
|
|
|
const intptr_t class_offset = klass.kernel_offset();
|
|
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(const Library& library,
|
|
intptr_t annotation_count,
|
|
String* native_name,
|
|
bool* is_potential_native,
|
|
bool* has_pragma_annotation) {
|
|
*is_potential_native = false;
|
|
*has_pragma_annotation = false;
|
|
Instance& constant = Instance::Handle(Z);
|
|
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.
|
|
helper_.ReadPosition(); // Skip fileOffset.
|
|
helper_.SkipDartType(); // Skip type.
|
|
const intptr_t offset_in_constant_table = helper_.ReadUInt();
|
|
|
|
AlternativeReadingScopeWithNewData 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 =
|
|
*has_pragma_annotation ||
|
|
IsClassName(helper_.ReadCanonicalNameReference(),
|
|
Symbols::DartCore(), Symbols::Pragma());
|
|
}
|
|
} else {
|
|
// Prepare lazy constant reading.
|
|
const dart::Class& toplevel_class =
|
|
Class::Handle(Z, library.toplevel_class());
|
|
ActiveClassScope active_class_scope(&active_class_, &toplevel_class);
|
|
ConstantReader constant_reader(&helper_, &active_class_);
|
|
|
|
helper_.ReadByte(); // Skip the tag.
|
|
|
|
// Obtain `dart:_internal::ExternalName.name`.
|
|
EnsureExternalClassIsLookedUp();
|
|
|
|
// Obtain `dart:_internal::pragma`.
|
|
EnsurePragmaClassIsLookedUp();
|
|
|
|
if (tag == kConstantExpression) {
|
|
helper_.ReadPosition(); // Skip fileOffset.
|
|
helper_.SkipDartType(); // Skip type.
|
|
}
|
|
const intptr_t constant_table_offset = helper_.ReadUInt();
|
|
// We have a candidate. Let's look if it's an instance of the
|
|
// ExternalName or Pragma class.
|
|
if (constant_reader.IsInstanceConstant(constant_table_offset,
|
|
external_name_class_)) {
|
|
constant = constant_reader.ReadConstant(constant_table_offset);
|
|
ASSERT(constant.clazz() == external_name_class_.raw());
|
|
*native_name ^= constant.GetField(external_name_field_);
|
|
} else if (constant_reader.IsInstanceConstant(constant_table_offset,
|
|
pragma_class_)) {
|
|
*has_pragma_annotation = true;
|
|
}
|
|
}
|
|
} 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();
|
|
bool is_extension_member = procedure_helper.IsExtensionMember();
|
|
String& native_name = String::Handle(Z);
|
|
bool is_potential_native;
|
|
bool has_pragma_annotation;
|
|
const intptr_t annotation_count = helper_.ReadListLength();
|
|
ReadVMAnnotations(library, 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_);
|
|
FunctionLayout::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_);
|
|
function.set_is_synthetic(procedure_helper.IsNoSuchMethodForwarder() ||
|
|
procedure_helper.IsMemberSignature());
|
|
if (register_function) {
|
|
functions_.Add(&function);
|
|
} else {
|
|
H.SetExpressionEvaluationFunction(function);
|
|
}
|
|
function.set_kernel_offset(procedure_offset);
|
|
function.set_is_extension_member(is_extension_member);
|
|
if ((library.is_dart_scheme() &&
|
|
H.IsPrivate(procedure_helper.canonical_name_)) ||
|
|
(function.is_static() && (library.raw() == Library::InternalLibrary()))) {
|
|
function.set_is_reflectable(false);
|
|
}
|
|
if (procedure_helper.IsMemberSignature()) {
|
|
function.set_is_reflectable(false);
|
|
}
|
|
|
|
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);
|
|
|
|
const bool is_async_await_completer_owner =
|
|
Symbols::_AsyncAwaitCompleter().Equals(
|
|
String::Handle(Z, owner.ScrubbedName()));
|
|
|
|
// _AsyncAwaitCompleter.future should be made non-debuggable, otherwise
|
|
// stepping out of async methods will keep hitting breakpoint resulting in
|
|
// infinite loop.
|
|
const bool is_async_await_completer_future =
|
|
is_async_await_completer_owner &&
|
|
Symbols::CompleterGetFuture().Equals(name);
|
|
function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
|
|
FunctionNodeHelper::kSync &&
|
|
!is_async_await_completer_future);
|
|
|
|
// _AsyncAwaitCompleter.start should be made non-visible in stack traces,
|
|
// since it is an implementation detail of our await/async desugaring.
|
|
if (is_async_await_completer_owner &&
|
|
Symbols::_AsyncAwaitStart().Equals(name)) {
|
|
function.set_is_visible(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_async_stacks);
|
|
}
|
|
|
|
switch (function_node_helper.dart_async_marker_) {
|
|
case FunctionNodeHelper::kSyncStar:
|
|
function.set_modifier(FunctionLayout::kSyncGen);
|
|
function.set_is_visible(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_async_stacks);
|
|
break;
|
|
case FunctionNodeHelper::kAsync:
|
|
function.set_modifier(FunctionLayout::kAsync);
|
|
function.set_is_inlinable(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_async_stacks);
|
|
function.set_is_visible(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_async_stacks);
|
|
break;
|
|
case FunctionNodeHelper::kAsyncStar:
|
|
function.set_modifier(FunctionLayout::kAsyncGen);
|
|
function.set_is_inlinable(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_async_stacks);
|
|
function.set_is_visible(!FLAG_causal_async_stacks &&
|
|
!FLAG_lazy_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) {
|
|
// Cannot be processed right now, so put on "pending" list.
|
|
EnsurePotentialNatives();
|
|
potential_natives_.Add(function);
|
|
}
|
|
|
|
function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
|
|
T.SetupFunctionParameters(owner, function, is_method,
|
|
false, // is_closure
|
|
&function_node_helper);
|
|
T.SetupUnboxingInfoMetadata(function, library_kernel_offset_);
|
|
|
|
// Everything else is skipped implicitly, and procedure_helper and
|
|
// function_node_helper are no longer used.
|
|
helper_.SetOffset(procedure_end);
|
|
|
|
if (annotation_count > 0) {
|
|
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
|
|
procedure_offset, 0);
|
|
}
|
|
|
|
if (has_pragma_annotation) {
|
|
if (kernel_program_info_.constants() == Array::null()) {
|
|
// Any potential pragma function before point at which
|
|
// constant table could be loaded goes to "pending".
|
|
EnsurePotentialPragmaFunctions();
|
|
potential_pragma_functions_.Add(function);
|
|
} else {
|
|
Thread* thread = Thread::Current();
|
|
NoOOBMessageScope no_msg_scope(thread);
|
|
NoReloadScope no_reload_scope(thread->isolate(), thread);
|
|
library.GetMetadata(function);
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
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;
|
|
}
|
|
|
|
ScriptPtr KernelLoader::LoadScriptAt(intptr_t index,
|
|
UriToSourceTable* uri_to_source_table) {
|
|
const String& uri_string = helper_.SourceTableUriFor(index);
|
|
const String& import_uri_string =
|
|
helper_.SourceTableImportUriFor(index, program_->binary_version());
|
|
|
|
String& sources = String::Handle(Z);
|
|
TypedData& line_starts = TypedData::Handle(Z);
|
|
|
|
if (uri_to_source_table != nullptr) {
|
|
UriToSourceTableEntry wrapper;
|
|
wrapper.uri = &uri_string;
|
|
UriToSourceTableEntry* pair = uri_to_source_table->LookupValue(&wrapper);
|
|
if (pair != nullptr) {
|
|
sources = pair->sources->raw();
|
|
line_starts = pair->line_starts->raw();
|
|
}
|
|
}
|
|
|
|
if (sources.IsNull() || line_starts.IsNull()) {
|
|
const String& script_source = helper_.GetSourceFor(index);
|
|
line_starts = helper_.GetLineStartsFor(index);
|
|
|
|
if (script_source.raw() == Symbols::Empty().raw() &&
|
|
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()) {
|
|
sources = script.Source();
|
|
line_starts = script.line_starts();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
sources = script_source.raw();
|
|
}
|
|
}
|
|
|
|
const Script& script =
|
|
Script::Handle(Z, Script::New(import_uri_string, uri_string, sources));
|
|
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::null_array());
|
|
return script.raw();
|
|
}
|
|
|
|
void KernelLoader::GenerateFieldAccessors(const Class& klass,
|
|
const Field& field,
|
|
FieldHelper* field_helper) {
|
|
const Tag tag = helper_.PeekTag();
|
|
const bool has_initializer = (tag == kSomething);
|
|
if (has_initializer) {
|
|
SimpleExpressionConverter converter(&H, &helper_);
|
|
const bool has_simple_initializer =
|
|
converter.IsSimple(helper_.ReaderOffset() + 1); // ignore the tag.
|
|
if (has_simple_initializer) {
|
|
if (field_helper->IsStatic()) {
|
|
// We do not need a getter.
|
|
field.SetStaticValue(converter.SimpleValue(), true);
|
|
return;
|
|
} else {
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (field_helper->IsStatic()) {
|
|
if (!has_initializer && !field_helper->IsLate()) {
|
|
// Static fields without an initializer are implicitly initialized to
|
|
// null. We do not need a getter.
|
|
field.SetStaticValue(Instance::null_instance(), true);
|
|
return;
|
|
}
|
|
|
|
// We do need a getter that evaluates the initializer if necessary.
|
|
field.SetStaticValue(Object::sentinel(), true);
|
|
}
|
|
ASSERT(field.NeedsGetter());
|
|
|
|
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() ? FunctionLayout::kImplicitStaticGetter
|
|
: FunctionLayout::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);
|
|
getter.set_accessor_field(field);
|
|
getter.set_is_extension_member(field.is_extension_member());
|
|
H.SetupFieldAccessorFunction(klass, getter, field_type);
|
|
|
|
if (field.NeedsSetter()) {
|
|
// 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, FunctionLayout::kImplicitSetter,
|
|
field_helper->IsStatic(),
|
|
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);
|
|
setter.set_accessor_field(field);
|
|
setter.set_is_extension_member(field.is_extension_member());
|
|
H.SetupFieldAccessorFunction(klass, setter, field_type);
|
|
}
|
|
}
|
|
|
|
LibraryPtr KernelLoader::LookupLibraryOrNull(NameIndex library) {
|
|
LibraryPtr result;
|
|
name_index_handle_ = Smi::New(library);
|
|
{
|
|
result = kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
|
|
NoSafepointScope no_safepoint_scope(thread_);
|
|
if (result != Library::null()) {
|
|
return result;
|
|
}
|
|
}
|
|
const String& url = H.DartString(H.CanonicalNameString(library));
|
|
{
|
|
result = Library::LookupLibrary(thread_, url);
|
|
NoSafepointScope no_safepoint_scope(thread_);
|
|
if (result == Library::null()) {
|
|
return result;
|
|
}
|
|
}
|
|
const Library& handle = Library::Handle(Z, result);
|
|
name_index_handle_ = Smi::New(library);
|
|
return kernel_program_info_.InsertLibrary(thread_, name_index_handle_,
|
|
handle);
|
|
}
|
|
|
|
LibraryPtr KernelLoader::LookupLibrary(NameIndex library) {
|
|
name_index_handle_ = Smi::New(library);
|
|
{
|
|
LibraryPtr result =
|
|
kernel_program_info_.LookupLibrary(thread_, name_index_handle_);
|
|
NoSafepointScope no_safepoint_scope(thread_);
|
|
if (result != Library::null()) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
Library& 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 (expression_evaluation_library_.IsNull()) {
|
|
handle = Library::New(url);
|
|
expression_evaluation_library_ = handle.raw();
|
|
}
|
|
return expression_evaluation_library_.raw();
|
|
}
|
|
handle = Library::LookupLibrary(thread_, url);
|
|
if (handle.IsNull()) {
|
|
handle = Library::New(url);
|
|
handle.Register(thread_);
|
|
}
|
|
ASSERT(!handle.IsNull());
|
|
name_index_handle_ = Smi::New(library);
|
|
return kernel_program_info_.InsertLibrary(thread_, name_index_handle_,
|
|
handle);
|
|
}
|
|
|
|
LibraryPtr KernelLoader::LookupLibraryFromClass(NameIndex klass) {
|
|
return LookupLibrary(H.CanonicalNameParent(klass));
|
|
}
|
|
|
|
ClassPtr KernelLoader::LookupClass(const Library& library, NameIndex klass) {
|
|
name_index_handle_ = Smi::New(klass);
|
|
{
|
|
ClassPtr raw_class =
|
|
kernel_program_info_.LookupClass(thread_, name_index_handle_);
|
|
NoSafepointScope no_safepoint_scope(thread_);
|
|
if (raw_class != Class::null()) {
|
|
return raw_class;
|
|
}
|
|
}
|
|
|
|
ASSERT(!library.IsNull());
|
|
const String& name = H.DartClassName(klass);
|
|
Class& handle = Class::Handle(Z, library.LookupLocalClass(name));
|
|
bool register_class = true;
|
|
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.
|
|
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);
|
|
}
|
|
}
|
|
ASSERT(!handle.IsNull());
|
|
if (register_class) {
|
|
name_index_handle_ = Smi::New(klass);
|
|
kernel_program_info_.InsertClass(thread_, name_index_handle_, handle);
|
|
}
|
|
return handle.raw();
|
|
}
|
|
|
|
FunctionLayout::Kind KernelLoader::GetFunctionType(
|
|
ProcedureHelper::Kind procedure_kind) {
|
|
intptr_t lookuptable[] = {
|
|
FunctionLayout::kRegularFunction, // Procedure::kMethod
|
|
FunctionLayout::kGetterFunction, // Procedure::kGetter
|
|
FunctionLayout::kSetterFunction, // Procedure::kSetter
|
|
FunctionLayout::kRegularFunction, // Procedure::kOperator
|
|
FunctionLayout::kConstructor, // Procedure::kFactory
|
|
};
|
|
intptr_t kind = static_cast<int>(procedure_kind);
|
|
ASSERT(0 <= kind && kind <= ProcedureHelper::kFactory);
|
|
return static_cast<FunctionLayout::Kind>(lookuptable[kind]);
|
|
}
|
|
|
|
FunctionPtr CreateFieldInitializerFunction(Thread* thread,
|
|
Zone* zone,
|
|
const Field& field) {
|
|
ASSERT(field.InitializerFunction() == Function::null());
|
|
|
|
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());
|
|
if (!lib.is_declared_in_bytecode()) {
|
|
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, FunctionLayout::kFieldInitializer,
|
|
field.is_static(), // is_static
|
|
false, // is_const
|
|
false, // is_abstract
|
|
false, // is_external
|
|
false, // is_native
|
|
initializer_owner, TokenPosition::kNoSource));
|
|
if (!field.is_static()) {
|
|
initializer_fun.set_num_fixed_parameters(1);
|
|
initializer_fun.set_parameter_types(
|
|
Array::Handle(zone, Array::New(1, Heap::kOld)));
|
|
initializer_fun.set_parameter_names(
|
|
Array::Handle(zone, Array::New(1, Heap::kOld)));
|
|
initializer_fun.SetParameterTypeAt(
|
|
0, AbstractType::Handle(zone, field_owner.DeclarationType()));
|
|
initializer_fun.SetParameterNameAt(0, Symbols::This());
|
|
}
|
|
initializer_fun.set_result_type(AbstractType::Handle(zone, field.type()));
|
|
initializer_fun.set_is_reflectable(false);
|
|
initializer_fun.set_is_inlinable(false);
|
|
initializer_fun.set_token_pos(field.token_pos());
|
|
initializer_fun.set_end_token_pos(field.end_token_pos());
|
|
initializer_fun.set_accessor_field(field);
|
|
initializer_fun.InheritBinaryDeclarationFrom(field);
|
|
initializer_fun.set_is_extension_member(field.is_extension_member());
|
|
field.SetInitializerFunction(initializer_fun);
|
|
return initializer_fun.raw();
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|