/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "file_items.h" #include "file_item_container.h" #include "macros.h" #include "utils/bit_utils.h" #include "utils/leb128.h" #include "utils/utf.h" #include <iomanip> namespace panda::panda_file { bool IsDynamicLanguage(panda::panda_file::SourceLang lang) { return lang == panda::panda_file::SourceLang::ECMASCRIPT; } std::optional<panda::panda_file::SourceLang> LanguageFromString(std::string_view lang) { if (lang == "ECMAScript") { return panda::panda_file::SourceLang::ECMASCRIPT; } return panda::panda_file::SourceLang::PANDA_ASSEMBLY; } const char *LanguageToString(panda::panda_file::SourceLang lang) { if (lang == panda::panda_file::SourceLang::ECMASCRIPT) { return "ECMAScript"; } return "PandaAssembly"; } const char *GetCtorName([[maybe_unused]] panda::panda_file::SourceLang lang) { return ".ctor"; } const char *GetCctorName([[maybe_unused]] panda::panda_file::SourceLang lang) { return ".cctor"; } const char *GetStringClassDescriptor(panda::panda_file::SourceLang lang) { if (lang == panda::panda_file::SourceLang::ECMASCRIPT) { return "Lpanda/JSString;"; } return "Lpanda/String;"; } template <class Tag, class Val> static bool WriteUlebTaggedValue(Writer *writer, Tag tag, Val v) { if (!writer->WriteByte(static_cast<uint8_t>(tag))) { return false; } if (!writer->WriteUleb128(v)) { return false; } return true; } template <class Tag, class Val> static bool WriteSlebTaggedValue(Writer *writer, Tag tag, Val v) { if (!writer->WriteByte(static_cast<uint8_t>(tag))) { return false; } if (!writer->WriteSleb128(v)) { return false; } return true; } template <class Tag, class Val> static bool WriteTaggedValue(Writer *writer, Tag tag, Val v) { if (!writer->WriteByte(static_cast<uint8_t>(tag))) { return false; } if (!writer->Write(v)) { return false; } return true; } template <class Tag> static bool WriteIdTaggedValue(Writer *writer, Tag tag, BaseItem *item) { ASSERT(item->GetOffset() != 0); return WriteTaggedValue(writer, tag, item->GetOffset()); } std::string ItemTypeToString(ItemTypes type) { switch (type) { case ItemTypes::ANNOTATION_ITEM: return "annotation_item"; case ItemTypes::CATCH_BLOCK_ITEM: return "catch_block_item"; case ItemTypes::CLASS_INDEX_ITEM: return "class_index_item"; case ItemTypes::CLASS_ITEM: return "class_item"; case ItemTypes::CODE_ITEM: return "code_item"; case ItemTypes::DEBUG_INFO_ITEM: return "debug_info_item"; case ItemTypes::END_ITEM: return "end_item"; case ItemTypes::FIELD_INDEX_ITEM: return "field_index_item"; case ItemTypes::FIELD_ITEM: return "field_item"; case ItemTypes::FOREIGN_CLASS_ITEM: return "foreign_class_item"; case ItemTypes::FOREIGN_FIELD_ITEM: return "foreign_field_item"; case ItemTypes::FOREIGN_METHOD_ITEM: return "foreign_method_item"; case ItemTypes::INDEX_HEADER: return "index_header"; case ItemTypes::INDEX_SECTION: return "index_section"; case ItemTypes::LINE_NUMBER_PROGRAM_INDEX_ITEM: return "line_number_program_index_item"; case ItemTypes::LINE_NUMBER_PROGRAM_ITEM: return "line_number_program_item"; case ItemTypes::LITERAL_ARRAY_ITEM: return "literal_array_item"; case ItemTypes::LITERAL_ITEM: return "literal_item"; case ItemTypes::METHOD_HANDLE_ITEM: return "method_handle_item"; case ItemTypes::METHOD_INDEX_ITEM: return "method_index_item"; case ItemTypes::METHOD_ITEM: return "method_item"; case ItemTypes::PARAM_ANNOTATIONS_ITEM: return "param_annotations_item"; case ItemTypes::PRIMITIVE_TYPE_ITEM: return "primitive_type_item"; case ItemTypes::PROTO_INDEX_ITEM: return "proto_index_item"; case ItemTypes::PROTO_ITEM: return "proto_item"; case ItemTypes::STRING_ITEM: return "string_item"; case ItemTypes::TRY_BLOCK_ITEM: return "try_block_item"; case ItemTypes::VALUE_ITEM: return "value_item"; default: return ""; } } std::string BaseItem::GetName() const { return ItemTypeToString(GetItemType()); } IndexedItem::IndexedItem(ItemContainer *container) { if (container != nullptr) { item_global_index_ = container->GetIndexedItemCount(); container->IncIndexedItemCount(); } } StringItem::StringItem(std::string str, ItemContainer *container) : IndexedItem(container), str_(std::move(str)) { str_.push_back(0); utf16_length_ = utf::MUtf8ToUtf16Size(utf::CStringAsMutf8(str_.data())); is_ascii_ = 1; for (auto c : str_) { if (static_cast<uint8_t>(c) > utf::MUTF8_1B_MAX) { is_ascii_ = 0; break; } } } StringItem::StringItem(File::StringData data, ItemContainer *container) : IndexedItem(container), str_(reinterpret_cast<const char *>(data.data)), utf16_length_(data.utf16_length) { } size_t StringItem::CalculateSize() const { size_t n = str_.size(); return leb128::UnsignedEncodingSize((utf16_length_ << 1U) | is_ascii_) + n; } bool StringItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); constexpr size_t MAX_LENGTH = 0x7fffffffU; if (utf16_length_ > MAX_LENGTH) { LOG(ERROR, PANDAFILE) << "Writing StringItem with size greater than 0x7fffffffU is not supported!"; return false; } if (!writer->WriteUleb128((utf16_length_ << 1U) | is_ascii_)) { return false; } for (auto c : str_) { if (!writer->WriteByte(static_cast<uint8_t>(c))) { return false; } } return true; } size_t BaseClassItem::CalculateSize() const { return name_.GetSize(); } void BaseClassItem::ComputeLayout() { uint32_t offset = GetOffset(); ASSERT(offset != 0); name_.SetOffset(offset); } bool BaseClassItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); return name_.Write(writer); } size_t ClassItem::CalculateSizeWithoutFieldsAndMethods() const { size_t size = BaseClassItem::CalculateSize() + ID_SIZE + leb128::UnsignedEncodingSize(access_flags_); size += leb128::UnsignedEncodingSize(fields_.size()); size += leb128::UnsignedEncodingSize(methods_.size()); if (!ifaces_.empty()) { size += TAG_SIZE + leb128::UnsignedEncodingSize(ifaces_.size()) + IDX_SIZE * ifaces_.size(); } if (source_lang_ != SourceLang::PANDA_ASSEMBLY) { size += TAG_SIZE + sizeof(SourceLang); } size += (TAG_SIZE + ID_SIZE) * runtime_annotations_.size(); size += (TAG_SIZE + ID_SIZE) * annotations_.size(); size += (TAG_SIZE + ID_SIZE) * runtime_type_annotations_.size(); size += (TAG_SIZE + ID_SIZE) * type_annotations_.size(); if (source_file_ != nullptr) { size += TAG_SIZE + ID_SIZE; } size += TAG_SIZE; // null tag return size; } size_t ClassItem::CalculateSize() const { size_t size = CalculateSizeWithoutFieldsAndMethods(); for (auto &field : fields_) { size += field->GetSize(); } for (auto &method : methods_) { size += method->GetSize(); } return size; } void ClassItem::ComputeLayout() { BaseClassItem::ComputeLayout(); uint32_t offset = GetOffset(); offset += CalculateSizeWithoutFieldsAndMethods(); for (auto &field : fields_) { field->SetOffset(offset); field->ComputeLayout(); offset += field->GetSize(); } for (auto &method : methods_) { method->SetOffset(offset); method->ComputeLayout(); offset += method->GetSize(); } } bool ClassItem::WriteIfaces(Writer *writer) { if (!ifaces_.empty()) { if (!writer->WriteByte(static_cast<uint8_t>(ClassTag::INTERFACES))) { return false; } if (!writer->WriteUleb128(ifaces_.size())) { return false; } for (auto iface : ifaces_) { ASSERT(iface->HasIndex(this)); if (!writer->Write<uint16_t>(iface->GetIndex(this))) { return false; } } } return true; } bool ClassItem::WriteAnnotations(Writer *writer) { for (auto runtime_annotation : runtime_annotations_) { if (!WriteIdTaggedValue(writer, ClassTag::RUNTIME_ANNOTATION, runtime_annotation)) { return false; } } for (auto annotation : annotations_) { if (!WriteIdTaggedValue(writer, ClassTag::ANNOTATION, annotation)) { return false; } } for (auto runtime_type_annotation : runtime_type_annotations_) { if (!WriteIdTaggedValue(writer, ClassTag::RUNTIME_TYPE_ANNOTATION, runtime_type_annotation)) { return false; } } for (auto type_annotation : type_annotations_) { if (!WriteIdTaggedValue(writer, ClassTag::TYPE_ANNOTATION, type_annotation)) { return false; } } return true; } bool ClassItem::WriteTaggedData(Writer *writer) { if (!WriteIfaces(writer)) { return false; } if (source_lang_ != SourceLang::PANDA_ASSEMBLY) { if (!WriteTaggedValue(writer, ClassTag::SOURCE_LANG, static_cast<uint8_t>(source_lang_))) { return false; } } if (!WriteAnnotations(writer)) { return false; } if (source_file_ != nullptr) { if (!WriteIdTaggedValue(writer, ClassTag::SOURCE_FILE, source_file_)) { return false; } } return writer->WriteByte(static_cast<uint8_t>(ClassTag::NOTHING)); } bool ClassItem::Write(Writer *writer) { if (!BaseClassItem::Write(writer)) { return false; } uint32_t offset = super_class_ != nullptr ? super_class_->GetOffset() : 0; if (!writer->Write(offset)) { return false; } if (!writer->WriteUleb128(access_flags_)) { return false; } if (!writer->WriteUleb128(fields_.size())) { return false; } if (!writer->WriteUleb128(methods_.size())) { return false; } if (!WriteTaggedData(writer)) { return false; } for (auto &field : fields_) { if (!field->Write(writer)) { return false; } } for (auto &method : methods_) { if (!method->Write(writer)) { return false; } } return true; } ParamAnnotationsItem::ParamAnnotationsItem(MethodItem *method, bool is_runtime_annotations) { for (const auto ¶m : method->GetParams()) { if (is_runtime_annotations) { annotations_.push_back(param.GetRuntimeAnnotations()); } else { annotations_.push_back(param.GetAnnotations()); } } if (is_runtime_annotations) { method->SetRuntimeParamAnnotationItem(this); } else { method->SetParamAnnotationItem(this); } } size_t ParamAnnotationsItem::CalculateSize() const { size_t size = sizeof(uint32_t); // size for (const auto ¶m_annotations : annotations_) { size += sizeof(uint32_t); // count size += param_annotations.size() * ID_SIZE; } return size; } bool ParamAnnotationsItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->Write(static_cast<uint32_t>(annotations_.size()))) { return false; } for (const auto ¶m_annotations : annotations_) { if (!writer->Write(static_cast<uint32_t>(param_annotations.size()))) { return false; } for (auto *item : param_annotations) { ASSERT(item->GetOffset() != 0); if (!writer->Write(item->GetOffset())) { return false; } } } return true; } ProtoItem::ProtoItem(TypeItem *ret_type, const std::vector<MethodParamItem> ¶ms, ItemContainer *itemContainer) : IndexedItem(itemContainer) { size_t n = 0; shorty_.push_back(0); AddType(ret_type, &n); for (auto &p : params) { AddType(p.GetType(), &n); } } void ProtoItem::AddType(TypeItem *type, size_t *n) { constexpr size_t SHORTY_ELEMS_COUNT = std::numeric_limits<uint16_t>::digits / SHORTY_ELEM_SIZE; uint16_t v = shorty_.back(); size_t shift = (*n % SHORTY_ELEMS_COUNT) * SHORTY_ELEM_SIZE; v |= static_cast<uint16_t>(static_cast<uint16_t>(type->GetType().GetEncoding()) << shift); shorty_.back() = v; if (!type->GetType().IsPrimitive()) { reference_types_.push_back(type); AddIndexDependency(type); } *n += 1; if (*n % SHORTY_ELEMS_COUNT == 0) { shorty_.push_back(0); } } bool ProtoItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); for (auto s : shorty_) { if (!writer->Write(s)) { return false; } } for (auto r : reference_types_) { ASSERT(r->HasIndex(this)); if (!writer->Write<uint16_t>(r->GetIndex(this))) { return false; } } return true; } BaseMethodItem::BaseMethodItem(BaseClassItem *cls, StringItem *name, ProtoItem *proto, uint32_t access_flags, ItemContainer *container) : IndexedItem(container), class_(cls), name_(name), proto_(proto), access_flags_(access_flags) { AddIndexDependency(cls); AddIndexDependency(proto); } size_t BaseMethodItem::CalculateSize() const { // class id + proto id + name id + access flags return IDX_SIZE + IDX_SIZE + ID_SIZE + leb128::UnsignedEncodingSize(access_flags_); } bool BaseMethodItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); ASSERT(class_ != nullptr); ASSERT(class_->HasIndex(this)); if (!writer->Write<uint16_t>(class_->GetIndex(this))) { return false; } ASSERT(proto_->HasIndex(this)); if (!writer->Write<uint16_t>(proto_->GetIndex(this))) { return false; } ASSERT(name_->GetOffset() != 0); if (!writer->Write(name_->GetOffset())) { return false; } return writer->WriteUleb128(access_flags_); } MethodItem::MethodItem(ClassItem *cls, StringItem *name, ProtoItem *proto, uint32_t access_flags, std::vector<MethodParamItem> params, ItemContainer *container) : BaseMethodItem(cls, name, proto, access_flags, container), params_(std::move(params)), source_lang_(SourceLang::PANDA_ASSEMBLY), code_(nullptr), debug_info_(nullptr) { } size_t MethodItem::CalculateSize() const { size_t size = BaseMethodItem::CalculateSize(); if (code_ != nullptr) { size += TAG_SIZE + ID_SIZE; } if (source_lang_ != SourceLang::PANDA_ASSEMBLY) { size += TAG_SIZE + sizeof(SourceLang); } size += (TAG_SIZE + ID_SIZE) * runtime_annotations_.size(); if (runtime_param_annotations_ != nullptr) { size += TAG_SIZE + ID_SIZE; } size += (TAG_SIZE + ID_SIZE) * annotations_.size(); if (param_annotations_ != nullptr) { size += TAG_SIZE + ID_SIZE; } size += (TAG_SIZE + ID_SIZE) * runtime_type_annotations_.size(); size += (TAG_SIZE + ID_SIZE) * type_annotations_.size(); if (debug_info_ != nullptr) { size += TAG_SIZE + ID_SIZE; } size += TAG_SIZE; // null tag return size; } bool MethodItem::WriteRuntimeAnnotations(Writer *writer) { for (auto runtime_annotation : runtime_annotations_) { if (!WriteIdTaggedValue(writer, MethodTag::RUNTIME_ANNOTATION, runtime_annotation)) { return false; } } if (runtime_param_annotations_ != nullptr) { if (!WriteIdTaggedValue(writer, MethodTag::RUNTIME_PARAM_ANNOTATION, runtime_param_annotations_)) { return false; } } return true; } bool MethodItem::WriteTypeAnnotations(Writer *writer) { for (auto runtime_type_annotation : runtime_type_annotations_) { if (!WriteIdTaggedValue(writer, MethodTag::RUNTIME_TYPE_ANNOTATION, runtime_type_annotation)) { return false; } } for (auto type_annotation : type_annotations_) { if (!WriteIdTaggedValue(writer, MethodTag::TYPE_ANNOTATION, type_annotation)) { return false; } } return true; } bool MethodItem::WriteTaggedData(Writer *writer) { if (code_ != nullptr) { if (!WriteIdTaggedValue(writer, MethodTag::CODE, code_)) { return false; } } if (source_lang_ != SourceLang::PANDA_ASSEMBLY) { if (!WriteTaggedValue(writer, MethodTag::SOURCE_LANG, static_cast<uint8_t>(source_lang_))) { return false; } } if (!WriteRuntimeAnnotations(writer)) { return false; } if (debug_info_ != nullptr) { if (!WriteIdTaggedValue(writer, MethodTag::DEBUG_INFO, debug_info_)) { return false; } } for (auto annotation : annotations_) { if (!WriteIdTaggedValue(writer, MethodTag::ANNOTATION, annotation)) { return false; } } if (!WriteTypeAnnotations(writer)) { return false; } if (param_annotations_ != nullptr) { if (!WriteIdTaggedValue(writer, MethodTag::PARAM_ANNOTATION, param_annotations_)) { return false; } } return writer->WriteByte(static_cast<uint8_t>(MethodTag::NOTHING)); } bool MethodItem::Write(Writer *writer) { if (!BaseMethodItem::Write(writer)) { return false; } return WriteTaggedData(writer); } size_t CodeItem::CatchBlock::CalculateSize() const { ASSERT(type_ == nullptr || type_->HasIndex(method_)); uint32_t type_off = type_ != nullptr ? type_->GetIndex(method_) + 1 : 0; return leb128::UnsignedEncodingSize(type_off) + leb128::UnsignedEncodingSize(handler_pc_) + leb128::UnsignedEncodingSize(code_size_); } bool CodeItem::CatchBlock::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); ASSERT(type_ == nullptr || type_->HasIndex(method_)); uint32_t type_off = type_ != nullptr ? type_->GetIndex(method_) + 1 : 0; if (!writer->WriteUleb128(type_off)) { return false; } if (!writer->WriteUleb128(handler_pc_)) { return false; } if (!writer->WriteUleb128(code_size_)) { return false; } return true; } void CodeItem::TryBlock::ComputeLayout() { size_t offset = GetOffset(); offset += CalculateSizeWithoutCatchBlocks(); for (auto &catch_block : catch_blocks_) { catch_block.SetOffset(offset); catch_block.ComputeLayout(); offset += catch_block.GetSize(); } } size_t CodeItem::TryBlock::CalculateSizeWithoutCatchBlocks() const { return leb128::UnsignedEncodingSize(start_pc_) + leb128::UnsignedEncodingSize(length_) + leb128::UnsignedEncodingSize(catch_blocks_.size()); } size_t CodeItem::TryBlock::CalculateSize() const { size_t size = CalculateSizeWithoutCatchBlocks(); for (auto &catch_block : catch_blocks_) { size += catch_block.GetSize(); } return size; } bool CodeItem::TryBlock::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->WriteUleb128(start_pc_)) { return false; } if (!writer->WriteUleb128(length_)) { return false; } if (!writer->WriteUleb128(catch_blocks_.size())) { return false; } for (auto &catch_block : catch_blocks_) { if (!catch_block.Write(writer)) { return false; } } return true; } void CodeItem::ComputeLayout() { uint32_t offset = GetOffset(); offset += CalculateSizeWithoutTryBlocks(); for (auto &try_block : try_blocks_) { try_block.SetOffset(offset); try_block.ComputeLayout(); offset += try_block.GetSize(); } } size_t CodeItem::CalculateSizeWithoutTryBlocks() const { size_t size = leb128::UnsignedEncodingSize(num_vregs_) + leb128::UnsignedEncodingSize(num_args_) + leb128::UnsignedEncodingSize(instructions_.size()) + leb128::UnsignedEncodingSize(try_blocks_.size()); size += instructions_.size(); return size; } size_t CodeItem::GetCodeSize() const { return instructions_.size(); } size_t CodeItem::CalculateSize() const { size_t size = CalculateSizeWithoutTryBlocks(); for (auto &try_block : try_blocks_) { size += try_block.GetSize(); } return size; } bool CodeItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->WriteUleb128(num_vregs_)) { return false; } if (!writer->WriteUleb128(num_args_)) { return false; } if (!writer->WriteUleb128(instructions_.size())) { return false; } if (!writer->WriteUleb128(try_blocks_.size())) { return false; } if (!writer->WriteBytes(instructions_)) { return false; } for (auto &try_block : try_blocks_) { if (!try_block.Write(writer)) { return false; } } return true; } ScalarValueItem *ValueItem::GetAsScalar() { ASSERT(!IsArray()); return static_cast<ScalarValueItem *>(this); } ArrayValueItem *ValueItem::GetAsArray() { ASSERT(IsArray()); return static_cast<ArrayValueItem *>(this); } size_t ScalarValueItem::GetULeb128EncodedSize() { switch (GetType()) { case Type::INTEGER: return leb128::UnsignedEncodingSize(GetValue<uint32_t>()); case Type::LONG: return leb128::UnsignedEncodingSize(GetValue<uint64_t>()); case Type::ID: return leb128::UnsignedEncodingSize(GetId().GetOffset()); default: return 0; } } size_t ScalarValueItem::GetSLeb128EncodedSize() { switch (GetType()) { case Type::INTEGER: return leb128::SignedEncodingSize(static_cast<int32_t>(GetValue<uint32_t>())); case Type::LONG: return leb128::SignedEncodingSize(static_cast<int64_t>(GetValue<uint64_t>())); default: return 0; } } size_t ScalarValueItem::CalculateSize() const { size_t size = 0; switch (GetType()) { case Type::INTEGER: { size = sizeof(uint32_t); break; } case Type::LONG: { size = sizeof(uint64_t); break; } case Type::FLOAT: { size = sizeof(float); break; } case Type::DOUBLE: { size = sizeof(double); break; } case Type::ID: { size = ID_SIZE; break; } default: { UNREACHABLE(); break; } } return size; } size_t ScalarValueItem::Alignment() { return GetSize(); } bool ScalarValueItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); switch (GetType()) { case Type::INTEGER: return writer->Write(GetValue<uint32_t>()); case Type::LONG: return writer->Write(GetValue<uint64_t>()); case Type::FLOAT: return writer->Write(bit_cast<uint32_t>(GetValue<float>())); case Type::DOUBLE: return writer->Write(bit_cast<uint64_t>(GetValue<double>())); case Type::ID: { ASSERT(GetId().IsValid()); return writer->Write(GetId().GetOffset()); } default: { UNREACHABLE(); break; } } return true; } bool ScalarValueItem::WriteAsUleb128(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); switch (GetType()) { case Type::INTEGER: return writer->WriteUleb128(GetValue<uint32_t>()); case Type::LONG: return writer->WriteUleb128(GetValue<uint64_t>()); case Type::ID: { ASSERT(GetId().IsValid()); return writer->WriteUleb128(GetId().GetOffset()); } default: return false; } } size_t ArrayValueItem::CalculateSize() const { size_t size = leb128::UnsignedEncodingSize(items_.size()) + items_.size() * GetComponentSize(); return size; } void ArrayValueItem::ComputeLayout() { uint32_t offset = GetOffset(); ASSERT(offset != 0); offset += leb128::UnsignedEncodingSize(items_.size()); for (auto &item : items_) { item.SetOffset(offset); offset += GetComponentSize(); } } bool ArrayValueItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->WriteUleb128(items_.size())) { return false; } switch (component_type_.GetId()) { case panda_file::Type::TypeId::U1: case panda_file::Type::TypeId::I8: case panda_file::Type::TypeId::U8: { for (auto &item : items_) { auto value = static_cast<uint8_t>(item.GetValue<uint32_t>()); if (!writer->Write(value)) { return false; } } break; } case panda_file::Type::TypeId::I16: case panda_file::Type::TypeId::U16: { for (auto &item : items_) { auto value = static_cast<uint16_t>(item.GetValue<uint32_t>()); if (!writer->Write(value)) { return false; } } break; } default: { for (auto &item : items_) { if (!item.Write(writer)) { return false; } } break; } } return true; } size_t ArrayValueItem::GetComponentSize() const { switch (component_type_.GetId()) { case panda_file::Type::TypeId::U1: case panda_file::Type::TypeId::I8: case panda_file::Type::TypeId::U8: return sizeof(uint8_t); case panda_file::Type::TypeId::I16: case panda_file::Type::TypeId::U16: return sizeof(uint16_t); case panda_file::Type::TypeId::I32: case panda_file::Type::TypeId::U32: case panda_file::Type::TypeId::F32: return sizeof(uint32_t); case panda_file::Type::TypeId::I64: case panda_file::Type::TypeId::U64: case panda_file::Type::TypeId::F64: return sizeof(uint64_t); case panda_file::Type::TypeId::REFERENCE: return ID_SIZE; case panda_file::Type::TypeId::VOID: return 0; default: { UNREACHABLE(); // Avoid cpp warning return 0; } } } size_t LiteralItem::CalculateSize() const { size_t size = 0; switch (GetType()) { case Type::B1: { size = sizeof(uint8_t); break; } case Type::B2: { size = sizeof(uint16_t); break; } case Type::B4: { size = sizeof(uint32_t); break; } case Type::B8: { size = sizeof(uint64_t); break; } case Type::STRING: case Type::METHOD: case Type::LITERALARRAY: { size = ID_SIZE; break; } default: { UNREACHABLE(); break; } } return size; } size_t LiteralItem::Alignment() { return GetSize(); } File::EntityId LiteralItem::GetLiteralArrayFileId() const { return File::EntityId(GetValue<LiteralArrayItem *>()->GetOffset()); } bool LiteralItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); switch (GetType()) { case Type::B1: { return writer->Write(GetValue<uint8_t>()); } case Type::B2: { return writer->Write(GetValue<uint16_t>()); } case Type::B4: { return writer->Write(GetValue<uint32_t>()); } case Type::B8: { return writer->Write(GetValue<uint64_t>()); } case Type::STRING: { ASSERT(GetId().IsValid()); return writer->Write(GetId().GetOffset()); } case Type::METHOD: { ASSERT(GetMethodId().IsValid()); return writer->Write(GetMethodId().GetOffset()); } case Type::LITERALARRAY: { ASSERT(GetLiteralArrayFileId().IsValid()); return writer->Write(GetLiteralArrayFileId().GetOffset()); } default: { UNREACHABLE(); break; } } return true; } void LiteralArrayItem::AddItems(const std::vector<LiteralItem> &item) { items_.assign(item.begin(), item.end()); } size_t LiteralArrayItem::CalculateSize() const { size_t size = sizeof(uint32_t); for (auto &item : items_) { size += item.CalculateSize(); } return size; } void LiteralArrayItem::ComputeLayout() { uint32_t offset = GetOffset(); ASSERT(offset != 0); offset += sizeof(uint32_t); for (auto &item : items_) { item.SetOffset(offset); offset += item.CalculateSize(); } } bool LiteralArrayItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->Write(static_cast<uint32_t>(items_.size()))) { return false; } for (auto &item : items_) { if (!item.Write(writer)) { return false; } } return true; } BaseFieldItem::BaseFieldItem(BaseClassItem *cls, StringItem *name, TypeItem *type, ItemContainer *container) : IndexedItem(container), class_(cls), name_(name), type_(type) { AddIndexDependency(cls); AddIndexDependency(type); } size_t BaseFieldItem::CalculateSize() const { // class id + type id + name id return IDX_SIZE + IDX_SIZE + ID_SIZE; } bool BaseFieldItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); ASSERT(class_->HasIndex(this)); ASSERT(type_->HasIndex(this)); if (!writer->Write<uint16_t>(class_->GetIndex(this))) { return false; } if (!writer->Write<uint16_t>(type_->GetIndex(this))) { return false; } return writer->Write(name_->GetOffset()); } FieldItem::FieldItem(ClassItem *cls, StringItem *name, TypeItem *type, uint32_t access_flags, ItemContainer *container) : BaseFieldItem(cls, name, type, container), access_flags_(access_flags), value_(nullptr) { } void FieldItem::SetValue(ValueItem *value) { value_ = value; value_->SetNeedsEmit(!value_->Is32bit()); } size_t FieldItem::CalculateSize() const { size_t size = BaseFieldItem::CalculateSize() + leb128::UnsignedEncodingSize(access_flags_); if (value_ != nullptr) { if (value_->GetType() == ValueItem::Type::INTEGER) { size += TAG_SIZE + value_->GetAsScalar()->GetSLeb128EncodedSize(); } else { size += TAG_SIZE + ID_SIZE; } } size += (TAG_SIZE + ID_SIZE) * runtime_annotations_.size(); size += (TAG_SIZE + ID_SIZE) * annotations_.size(); size += (TAG_SIZE + ID_SIZE) * runtime_type_annotations_.size(); size += (TAG_SIZE + ID_SIZE) * type_annotations_.size(); size += TAG_SIZE; // null tag return size; } bool FieldItem::WriteValue(Writer *writer) { if (value_ == nullptr) { return true; } if (value_->GetType() == ValueItem::Type::INTEGER) { auto v = static_cast<int32_t>(value_->GetAsScalar()->GetValue<uint32_t>()); if (!WriteSlebTaggedValue(writer, FieldTag::INT_VALUE, v)) { return false; } } else if (value_->GetType() == ValueItem::Type::FLOAT) { auto v = bit_cast<uint32_t>(value_->GetAsScalar()->GetValue<float>()); if (!WriteTaggedValue(writer, FieldTag::VALUE, v)) { return false; } } else if (value_->GetType() == ValueItem::Type::ID) { auto id = value_->GetAsScalar()->GetId(); ASSERT(id.GetOffset() != 0); if (!WriteTaggedValue(writer, FieldTag::VALUE, id.GetOffset())) { return false; } } else { ASSERT(!value_->Is32bit()); if (!WriteIdTaggedValue(writer, FieldTag::VALUE, value_)) { return false; } } return true; } bool FieldItem::WriteAnnotations(Writer *writer) { for (auto runtime_annotation : runtime_annotations_) { if (!WriteIdTaggedValue(writer, FieldTag::RUNTIME_ANNOTATION, runtime_annotation)) { return false; } } for (auto annotation : annotations_) { if (!WriteIdTaggedValue(writer, FieldTag::ANNOTATION, annotation)) { return false; } } for (auto runtime_type_annotation : runtime_type_annotations_) { if (!WriteIdTaggedValue(writer, FieldTag::RUNTIME_TYPE_ANNOTATION, runtime_type_annotation)) { return false; } } for (auto type_annotation : type_annotations_) { if (!WriteIdTaggedValue(writer, FieldTag::TYPE_ANNOTATION, type_annotation)) { return false; } } return true; } bool FieldItem::WriteTaggedData(Writer *writer) { if (!WriteValue(writer)) { return false; } if (!WriteAnnotations(writer)) { return false; } return writer->WriteByte(static_cast<uint8_t>(FieldTag::NOTHING)); } bool FieldItem::Write(Writer *writer) { if (!BaseFieldItem::Write(writer)) { return false; } if (!writer->WriteUleb128(access_flags_)) { return false; } return WriteTaggedData(writer); } size_t AnnotationItem::CalculateSize() const { // class id + count + (name id + value id) * count + tag size * count size_t size = IDX_SIZE + sizeof(uint16_t) + (ID_SIZE + ID_SIZE) * elements_.size() + sizeof(uint8_t) * tags_.size(); return size; } bool AnnotationItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); ASSERT(class_->HasIndex(this)); if (!writer->Write<uint16_t>(class_->GetIndex(this))) { return false; } if (!writer->Write(static_cast<uint16_t>(elements_.size()))) { return false; } for (auto elem : elements_) { ASSERT(elem.GetName()->GetOffset() != 0); if (!writer->Write(elem.GetName()->GetOffset())) { return false; } ValueItem *value_item = elem.GetValue(); switch (value_item->GetType()) { case ValueItem::Type::INTEGER: { if (!writer->Write(value_item->GetAsScalar()->GetValue<uint32_t>())) { return false; } break; } case ValueItem::Type::FLOAT: { if (!writer->Write(bit_cast<uint32_t>(value_item->GetAsScalar()->GetValue<float>()))) { return false; } break; } case ValueItem::Type::ID: { if (!writer->Write(value_item->GetAsScalar()->GetId().GetOffset())) { return false; } break; } default: { ASSERT(value_item->GetOffset() != 0); if (!writer->Write(value_item->GetOffset())) { return false; } break; } } } for (auto tag : tags_) { if (!writer->Write(tag.GetItem())) { return false; } } return true; } void LineNumberProgramItem::EmitEnd() { EmitOpcode(Opcode::END_SEQUENCE); } void LineNumberProgramItem::EmitAdvancePc(std::vector<uint8_t> *constant_pool, uint32_t value) { EmitOpcode(Opcode::ADVANCE_PC); EmitUleb128(constant_pool, value); } void LineNumberProgramItem::EmitAdvanceLine(std::vector<uint8_t> *constant_pool, int32_t value) { EmitOpcode(Opcode::ADVANCE_LINE); EmitSleb128(constant_pool, value); } void LineNumberProgramItem::EmitColumn(std::vector<uint8_t> *constant_pool, uint32_t pc_inc, uint32_t column) { if (pc_inc != 0U) { EmitAdvancePc(constant_pool, pc_inc); } EmitOpcode(Opcode::SET_COLUMN); EmitUleb128(constant_pool, column); } void LineNumberProgramItem::EmitStartLocal(std::vector<uint8_t> *constant_pool, int32_t register_number, StringItem *name, StringItem *type) { EmitStartLocalExtended(constant_pool, register_number, name, type, nullptr); } void LineNumberProgramItem::EmitStartLocalExtended(std::vector<uint8_t> *constant_pool, int32_t register_number, StringItem *name, StringItem *type, StringItem *type_signature) { ASSERT(name != nullptr); ASSERT(type != nullptr); EmitOpcode(type_signature == nullptr ? Opcode::START_LOCAL : Opcode::START_LOCAL_EXTENDED); EmitRegister(register_number); ASSERT(name->GetOffset() != 0); EmitUleb128(constant_pool, name->GetOffset()); ASSERT(type->GetOffset() != 0); EmitUleb128(constant_pool, type->GetOffset()); if (type_signature != nullptr) { ASSERT(type_signature->GetOffset() != 0); EmitUleb128(constant_pool, type_signature->GetOffset()); } } void LineNumberProgramItem::EmitEndLocal(int32_t register_number) { EmitOpcode(Opcode::END_LOCAL); EmitRegister(register_number); } void LineNumberProgramItem::EmitRestartLocal(int32_t register_number) { EmitOpcode(Opcode::RESTART_LOCAL); EmitRegister(register_number); } bool LineNumberProgramItem::EmitSpecialOpcode(uint32_t pc_inc, int32_t line_inc) { if (line_inc < LINE_BASE || (line_inc - LINE_BASE) >= LINE_RANGE) { return false; } auto opcode = static_cast<size_t>(line_inc - LINE_BASE) + static_cast<size_t>(pc_inc * LINE_RANGE) + OPCODE_BASE; if (opcode > std::numeric_limits<uint8_t>::max()) { return false; } data_.push_back(static_cast<uint8_t>(opcode)); return true; } void LineNumberProgramItem::EmitPrologEnd() { EmitOpcode(Opcode::SET_PROLOGUE_END); } void LineNumberProgramItem::EmitEpilogBegin() { EmitOpcode(Opcode::SET_EPILOGUE_BEGIN); } void LineNumberProgramItem::EmitSetFile(std::vector<uint8_t> *constant_pool, StringItem *source_file) { EmitOpcode(Opcode::SET_FILE); if (source_file == nullptr) { return; } ASSERT(source_file->GetOffset() != 0); EmitUleb128(constant_pool, source_file->GetOffset()); } void LineNumberProgramItem::EmitSetSourceCode(std::vector<uint8_t> *constant_pool, StringItem *source_code) { EmitOpcode(Opcode::SET_SOURCE_CODE); if (source_code == nullptr) { return; } ASSERT(source_code->GetOffset() != 0); EmitUleb128(constant_pool, source_code->GetOffset()); } void LineNumberProgramItem::EmitOpcode(Opcode opcode) { data_.push_back(static_cast<uint8_t>(opcode)); } void LineNumberProgramItem::EmitRegister(int32_t register_number) { EmitSleb128(&data_, register_number); } /* static */ void LineNumberProgramItem::EmitUleb128(std::vector<uint8_t> *data, uint32_t value) { size_t n = leb128::UnsignedEncodingSize(value); std::vector<uint8_t> out(n); leb128::EncodeUnsigned(value, out.data()); if (data == nullptr) { return; } data->insert(data->end(), out.cbegin(), out.cend()); } /* static */ void LineNumberProgramItem::EmitSleb128(std::vector<uint8_t> *data, int32_t value) { size_t n = leb128::SignedEncodingSize(value); std::vector<uint8_t> out(n); leb128::EncodeSigned(value, out.data()); data->insert(data->end(), out.cbegin(), out.cend()); } size_t LineNumberProgramItem::CalculateSize() const { return data_.size(); } bool LineNumberProgramItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); return writer->WriteBytes(data_); } void LineNumberProgramItem::SetData(std::vector<uint8_t> &&data) { data_ = std::move(data); } size_t DebugInfoItem::CalculateSize() const { size_t n = leb128::UnsignedEncodingSize(line_num_) + leb128::UnsignedEncodingSize(parameters_.size()); for (auto *p : parameters_) { ASSERT(p == nullptr || p->GetOffset() != 0); n += leb128::UnsignedEncodingSize(p == nullptr ? 0 : p->GetOffset()); } n += leb128::UnsignedEncodingSize(constant_pool_.size()); n += constant_pool_.size(); n += leb128::UnsignedEncodingSize(program_->GetIndex(this)); return n; } bool DebugInfoItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->WriteUleb128(line_num_)) { return false; } if (!writer->WriteUleb128(parameters_.size())) { return false; } for (auto *p : parameters_) { ASSERT(p == nullptr || p->GetOffset() != 0); if (!writer->WriteUleb128(p == nullptr ? 0 : p->GetOffset())) { return false; } } if (!writer->WriteUleb128(constant_pool_.size())) { return false; } if (!writer->WriteBytes(constant_pool_)) { return false; } ASSERT(program_ != nullptr); ASSERT(program_->HasIndex(this)); return writer->WriteUleb128(program_->GetIndex(this)); } void DebugInfoItem::Dump(std::ostream &os) const { os << "line_start = " << line_num_ << std::endl; os << "num_parameters = " << parameters_.size() << std::endl; for (auto *item : parameters_) { if (item != nullptr) { os << " string_item[" << item->GetOffset() << "]" << std::endl; } else { os << " string_item[INVALID_OFFSET]" << std::endl; } } os << "constant_pool = ["; for (size_t i = 0; i < constant_pool_.size(); i++) { size_t b = constant_pool_[i]; os << "0x" << std::setfill('0') << std::setw(2U) << std::right << std::hex << b << std::dec; if (i < constant_pool_.size() - 1) { os << ", "; } } os << "]" << std::endl; os << "line_number_program = line_number_program_idx["; if (program_ != nullptr && program_->HasIndex(this)) { os << program_->GetIndex(this); } else { os << "NO_INDEX"; } os << "]"; } bool MethodHandleItem::Write(Writer *writer) { ASSERT(GetOffset() == writer->GetOffset()); if (!writer->WriteByte(static_cast<uint8_t>(type_))) { return false; } return writer->WriteUleb128(entity_->GetOffset()); } } // namespace panda::panda_file