| // 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) { |
| TypedData& line_starts_data = TypedData::Handle( |
| TypedData::New(kTypedDataInt8ArrayCid, line_start_count, Heap::kOld)); |
| |
| const intptr_t start_offset = offset(); |
| intptr_t i = 0; |
| for (; i < line_start_count; ++i) { |
| const intptr_t delta = ReadUInt(); |
| if (delta > kMaxInt8) { |
| break; |
| } |
| line_starts_data.SetInt8(i, static_cast<int8_t>(delta)); |
| } |
| |
| if (i < line_start_count) { |
| // Slow path: choose representation between Int16 and Int32 typed data. |
| set_offset(start_offset); |
| intptr_t max_delta = 0; |
| for (intptr_t i = 0; i < line_start_count; ++i) { |
| const intptr_t delta = ReadUInt(); |
| if (delta > max_delta) { |
| max_delta = delta; |
| } |
| } |
| |
| ASSERT(max_delta > kMaxInt8); |
| const intptr_t cid = (max_delta <= kMaxInt16) ? kTypedDataInt16ArrayCid |
| : kTypedDataInt32ArrayCid; |
| line_starts_data = TypedData::New(cid, line_start_count, Heap::kOld); |
| |
| set_offset(start_offset); |
| for (intptr_t i = 0; i < line_start_count; ++i) { |
| const intptr_t delta = ReadUInt(); |
| if (cid == kTypedDataInt16ArrayCid) { |
| line_starts_data.SetInt16(i << 1, static_cast<int16_t>(delta)); |
| } else { |
| line_starts_data.SetInt32(i << 2, delta); |
| } |
| } |
| } |
| |
| 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"; |
| |
| 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; |
| } |
| |
| uint32_t formatVersion = reader->ReadUInt32(); |
| if ((formatVersion < kMinSupportedKernelFormatVersion) || |
| (formatVersion > kMaxSupportedKernelFormatVersion)) { |
| if (error != nullptr) { |
| *error = kKernelInvalidBinaryFormatVersion; |
| } |
| return nullptr; |
| } |
| |
| uint8_t sdkHash[kSdkHashSizeInBytes + 1]; |
| reader->ReadBytes(sdkHash, kSdkHashSizeInBytes); |
| sdkHash[kSdkHashSizeInBytes] = 0; // Null terminate. |
| if (strcmp(Version::SdkHash(), kSdkHashNull) != 0 && |
| strcmp((const char*)sdkHash, kSdkHashNull) != 0 && |
| strcmp((const char*)sdkHash, Version::SdkHash()) != 0) { |
| if (error != nullptr) { |
| *error = kKernelInvalidSdkHash; |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<Program> program(new Program()); |
| program->binary_version_ = formatVersion; |
| program->binary_.typed_data = reader->typed_data(); |
| program->binary_.kernel_data = reader->buffer(); |
| program->binary_.kernel_data_size = reader->size(); |
| |
| // 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 == NULL) { |
| 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::Handle( |
| thread->zone(), ExternalTypedData::RawCast(ret.ptr())); |
| 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) { |
| kernel::Reader reader(buffer, buffer_length); |
| 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) |