0ae3368fb6
a) Remove 2 fields in our object representation: * PatchClass::library_data_ * Library::kernel_data_ => This saves O(#libraries + #patch-classes) which can amount up to 10+ MB for big apps, as we save not just the slots but the [ExternalTypedData] objects they used to reference. Instead we'll compute the KernelLibraryData when we need it by making a [TypedDataView] of sub parts of the component. b) We make a kernel binary be represented by a single [ExternalTypedData]. Whenever we need a sub-part of a kernel binary (e.g. one for each component for concatinated kernels, or one for a library inside a component) we use proper [TypedDataView]s for that. c) As we sometimes need to create a view of only a particular library within a component we need to find start/end of a library based on index. => We store the library index instead of the library offset on Library/PatchClass. => We can easily derive the start/end of a library from it's index by looking at the kernel component encoding. d) We make the [Reader] object work purely based on a pointer - instead of making it have if/else when reading bytes (either from pointer or from a view). e) We make the [KernelProgramInfo] store the kernel_component and various TD views into it. TEST=ci Change-Id: Ibe160881ff48635e834c3d647a977a144b5d0565 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/313561 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
243 lines
8.1 KiB
C++
243 lines
8.1 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_binary.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "platform/globals.h"
|
|
#include "vm/compiler/frontend/kernel_to_il.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/kernel.h"
|
|
#include "vm/object.h"
|
|
#include "vm/os.h"
|
|
#include "vm/version.h"
|
|
|
|
namespace dart {
|
|
|
|
namespace kernel {
|
|
|
|
const char* Reader::TagName(Tag tag) {
|
|
switch (tag) {
|
|
#define CASE(Name, value) \
|
|
case k##Name: \
|
|
return #Name;
|
|
KERNEL_TAG_LIST(CASE)
|
|
#undef CASE
|
|
default:
|
|
break;
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
TypedDataPtr Reader::ReadLineStartsData(intptr_t line_start_count) {
|
|
const intptr_t start_offset = offset();
|
|
|
|
// Choose representation between Uint16 and Uint32 typed data.
|
|
intptr_t max_start = 0;
|
|
for (intptr_t i = 0; i < line_start_count; ++i) {
|
|
const intptr_t delta = ReadUInt();
|
|
max_start += delta;
|
|
}
|
|
|
|
const intptr_t cid = (max_start <= kMaxUint16) ? kTypedDataUint16ArrayCid
|
|
: kTypedDataUint32ArrayCid;
|
|
const TypedData& line_starts_data =
|
|
TypedData::Handle(TypedData::New(cid, line_start_count, Heap::kOld));
|
|
|
|
set_offset(start_offset);
|
|
intptr_t current_start = 0;
|
|
for (intptr_t i = 0; i < line_start_count; ++i) {
|
|
const intptr_t delta = ReadUInt();
|
|
current_start += delta;
|
|
if (cid == kTypedDataUint16ArrayCid) {
|
|
line_starts_data.SetUint16(i << 1, static_cast<uint16_t>(current_start));
|
|
} else {
|
|
line_starts_data.SetUint32(i << 2, current_start);
|
|
}
|
|
}
|
|
|
|
return line_starts_data.ptr();
|
|
}
|
|
|
|
const char* kKernelInvalidFilesize =
|
|
"File size is too small to be a valid kernel file";
|
|
const char* kKernelInvalidMagicIdentifier = "Invalid magic identifier";
|
|
const char* kKernelInvalidBinaryFormatVersion =
|
|
"Invalid kernel binary format version";
|
|
const char* kKernelInvalidSizeIndicated =
|
|
"Invalid kernel binary: Indicated size is invalid";
|
|
const char* kKernelInvalidSdkHash = "Invalid SDK hash";
|
|
|
|
const int kSdkHashSizeInBytes = 10;
|
|
const char* kSdkHashNull = "0000000000";
|
|
|
|
bool IsValidSdkHash(const uint8_t* sdk_hash) {
|
|
if (memcmp(Version::SdkHash(), kSdkHashNull, kSdkHashSizeInBytes) != 0 &&
|
|
memcmp(sdk_hash, kSdkHashNull, kSdkHashSizeInBytes) != 0 &&
|
|
memcmp(sdk_hash, Version::SdkHash(), kSdkHashSizeInBytes) != 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NNBDCompiledMode Program::DetectNullSafety(const uint8_t* buffer,
|
|
intptr_t buffer_length) {
|
|
Reader reader(buffer, buffer_length);
|
|
std::unique_ptr<Program> program = Program::ReadFrom(&reader, nullptr);
|
|
if (program == nullptr) return NNBDCompiledMode::kInvalid;
|
|
return program->compilation_mode_;
|
|
}
|
|
|
|
std::unique_ptr<Program> Program::ReadFrom(Reader* reader, const char** error) {
|
|
if (reader->size() < 70) {
|
|
// A kernel file (v43) currently contains at least the following:
|
|
// * Magic number (32)
|
|
// * Kernel version (32)
|
|
// * SDK Hash (10 * 8)
|
|
// * List of problems (8)
|
|
// * Length of source map (32)
|
|
// * Length of canonical name table (8)
|
|
// * Metadata length (32)
|
|
// * Length of string table (8)
|
|
// * Length of constant table (8)
|
|
// * Component index (11 * 32)
|
|
//
|
|
// so is at least 74 bytes.
|
|
// (Technically it will also contain an empty entry in both source map and
|
|
// string table, taking up another 8 bytes.)
|
|
if (error != nullptr) {
|
|
*error = kKernelInvalidFilesize;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
uint32_t magic = reader->ReadUInt32();
|
|
if (magic != kMagicProgramFile) {
|
|
if (error != nullptr) {
|
|
*error = kKernelInvalidMagicIdentifier;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const uint32_t format_version = reader->ReadUInt32();
|
|
if (format_version != kSupportedKernelFormatVersion) {
|
|
if (error != nullptr) {
|
|
*error = kKernelInvalidBinaryFormatVersion;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
if (!IsValidSdkHash(reader->BufferAt(reader->offset()))) {
|
|
if (error != nullptr) {
|
|
*error = kKernelInvalidSdkHash;
|
|
}
|
|
return nullptr;
|
|
}
|
|
reader->set_offset(reader->offset() + kSdkHashSizeInBytes);
|
|
|
|
std::unique_ptr<Program> program(new Program(reader->typed_data()));
|
|
|
|
// Dill files can be concatenated (e.g. cat a.dill b.dill > c.dill). Find out
|
|
// if this dill contains more than one program.
|
|
int subprogram_count = 0;
|
|
reader->set_offset(reader->size() - 4);
|
|
while (reader->offset() > 0) {
|
|
intptr_t size = reader->ReadUInt32();
|
|
intptr_t start = reader->offset() - size;
|
|
if (start < 0 || size <= 0) {
|
|
if (error != nullptr) {
|
|
*error = kKernelInvalidSizeIndicated;
|
|
}
|
|
return nullptr;
|
|
}
|
|
++subprogram_count;
|
|
if (subprogram_count > 1) break;
|
|
reader->set_offset(start - 4);
|
|
}
|
|
program->single_program_ = subprogram_count == 1;
|
|
|
|
// Read backwards at the end.
|
|
program->library_count_ = reader->ReadFromIndexNoReset(
|
|
reader->size_, LibraryCountFieldCountFromEnd, 1, 0);
|
|
intptr_t count_from_first_library_offset =
|
|
SourceTableFieldCountFromFirstLibraryOffset;
|
|
program->source_table_offset_ = reader->ReadFromIndexNoReset(
|
|
reader->size_,
|
|
LibraryCountFieldCountFromEnd + 1 + program->library_count_ + 1 +
|
|
count_from_first_library_offset,
|
|
1, 0);
|
|
program->constant_table_offset_ = reader->ReadUInt32();
|
|
reader->ReadUInt32(); // offset for constant table index.
|
|
program->name_table_offset_ = reader->ReadUInt32();
|
|
program->metadata_payloads_offset_ = reader->ReadUInt32();
|
|
program->metadata_mappings_offset_ = reader->ReadUInt32();
|
|
program->string_table_offset_ = reader->ReadUInt32();
|
|
// The below includes any 8-bit alignment; denotes the end of the previous
|
|
// block.
|
|
program->component_index_offset_ = reader->ReadUInt32();
|
|
|
|
program->main_method_reference_ = NameIndex(reader->ReadUInt32() - 1);
|
|
NNBDCompiledMode compilation_mode =
|
|
static_cast<NNBDCompiledMode>(reader->ReadUInt32());
|
|
program->compilation_mode_ = compilation_mode;
|
|
|
|
return program;
|
|
}
|
|
|
|
std::unique_ptr<Program> Program::ReadFromFile(
|
|
const char* script_uri, const char** error /* = nullptr */) {
|
|
Thread* thread = Thread::Current();
|
|
auto isolate_group = thread->isolate_group();
|
|
if (script_uri == nullptr) {
|
|
return nullptr;
|
|
}
|
|
if (!isolate_group->HasTagHandler()) {
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<kernel::Program> kernel_program;
|
|
|
|
const String& uri = String::Handle(String::New(script_uri));
|
|
const Object& ret = Object::Handle(isolate_group->CallTagHandler(
|
|
Dart_kKernelTag, Object::null_object(), uri));
|
|
if (ret.IsExternalTypedData()) {
|
|
const auto& typed_data = ExternalTypedData::Cast(ret);
|
|
kernel_program = kernel::Program::ReadFromTypedData(typed_data);
|
|
return kernel_program;
|
|
} else if (error != nullptr) {
|
|
Api::Scope api_scope(thread);
|
|
Dart_Handle retval = Api::NewHandle(thread, ret.ptr());
|
|
{
|
|
TransitionVMToNative transition(thread);
|
|
*error = Dart_GetError(retval);
|
|
}
|
|
}
|
|
return kernel_program;
|
|
}
|
|
|
|
std::unique_ptr<Program> Program::ReadFromBuffer(const uint8_t* buffer,
|
|
intptr_t buffer_length,
|
|
const char** error) {
|
|
// Whoever called this method (e.g. embedder) has to ensure the buffer stays
|
|
// alive until the VM is done with the last usage (e.g. isolate shutdown).
|
|
const auto& binary = ExternalTypedData::Handle(ExternalTypedData::New(
|
|
kExternalTypedDataUint8ArrayCid, const_cast<uint8_t*>(buffer),
|
|
buffer_length, Heap::kNew));
|
|
kernel::Reader reader(binary);
|
|
return kernel::Program::ReadFrom(&reader, error);
|
|
}
|
|
|
|
std::unique_ptr<Program> Program::ReadFromTypedData(
|
|
const ExternalTypedData& typed_data, const char** error) {
|
|
kernel::Reader reader(typed_data);
|
|
return kernel::Program::ReadFrom(&reader, error);
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|