Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "Resource.h" |
| 18 | #include "ResourceTable.h" |
| 19 | #include "StringPool.h" |
| 20 | #include "ValueVisitor.h" |
| 21 | #include "proto/ProtoHelpers.h" |
| 22 | #include "proto/ProtoSerialize.h" |
| 23 | #include "util/BigBuffer.h" |
| 24 | |
| 25 | namespace aapt { |
| 26 | |
| 27 | namespace { |
| 28 | |
| 29 | class PbSerializerVisitor : public RawValueVisitor { |
| 30 | public: |
| 31 | using RawValueVisitor::visit; |
| 32 | |
| 33 | /** |
| 34 | * Constructor to use when expecting to serialize any value. |
| 35 | */ |
| 36 | PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) : |
| 37 | mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue), |
| 38 | mOutPbItem(nullptr) { |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * Constructor to use when expecting to serialize an Item. |
| 43 | */ |
| 44 | PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) : |
| 45 | mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr), |
| 46 | mOutPbItem(outPbItem) { |
| 47 | } |
| 48 | |
| 49 | void visit(Reference* ref) override { |
| 50 | serializeReferenceToPb(*ref, getPbItem()->mutable_ref()); |
| 51 | } |
| 52 | |
| 53 | void visit(String* str) override { |
| 54 | getPbItem()->mutable_str()->set_idx(str->value.getIndex()); |
| 55 | } |
| 56 | |
| 57 | void visit(StyledString* str) override { |
| 58 | getPbItem()->mutable_str()->set_idx(str->value.getIndex()); |
| 59 | } |
| 60 | |
| 61 | void visit(FileReference* file) override { |
| 62 | getPbItem()->mutable_file()->set_path_idx(file->path.getIndex()); |
| 63 | } |
| 64 | |
| 65 | void visit(Id* id) override { |
| 66 | getPbItem()->mutable_id(); |
| 67 | } |
| 68 | |
| 69 | void visit(RawString* rawStr) override { |
| 70 | getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex()); |
| 71 | } |
| 72 | |
| 73 | void visit(BinaryPrimitive* prim) override { |
| 74 | android::Res_value val = {}; |
| 75 | prim->flatten(&val); |
| 76 | |
| 77 | pb::Primitive* pbPrim = getPbItem()->mutable_prim(); |
| 78 | pbPrim->set_type(val.dataType); |
| 79 | pbPrim->set_data(val.data); |
| 80 | } |
| 81 | |
| 82 | void visitItem(Item* item) override { |
| 83 | assert(false && "unimplemented item"); |
| 84 | } |
| 85 | |
| 86 | void visit(Attribute* attr) override { |
| 87 | pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr(); |
| 88 | pbAttr->set_format_flags(attr->typeMask); |
| 89 | pbAttr->set_min_int(attr->minInt); |
| 90 | pbAttr->set_max_int(attr->maxInt); |
| 91 | |
| 92 | for (auto& symbol : attr->symbols) { |
| 93 | pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols(); |
| 94 | serializeItemCommonToPb(symbol.symbol, pbSymbol); |
| 95 | serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name()); |
| 96 | pbSymbol->set_value(symbol.value); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | void visit(Style* style) override { |
| 101 | pb::Style* pbStyle = getPbCompoundValue()->mutable_style(); |
| 102 | if (style->parent) { |
| 103 | serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent()); |
| 104 | serializeSourceToPb(style->parent.value().getSource(), |
| 105 | mSourcePool, |
| 106 | pbStyle->mutable_parent_source()); |
| 107 | } |
| 108 | |
| 109 | for (Style::Entry& entry : style->entries) { |
| 110 | pb::Style_Entry* pbEntry = pbStyle->add_entries(); |
| 111 | serializeReferenceToPb(entry.key, pbEntry->mutable_key()); |
| 112 | |
| 113 | pb::Item* pbItem = pbEntry->mutable_item(); |
Adam Lesinski | 5f7c461 | 2016-02-12 23:38:08 -0800 | [diff] [blame] | 114 | serializeItemCommonToPb(entry.key, pbEntry); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 115 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem); |
| 116 | entry.value->accept(&subVisitor); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | void visit(Styleable* styleable) override { |
| 121 | pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable(); |
| 122 | for (Reference& entry : styleable->entries) { |
| 123 | pb::Styleable_Entry* pbEntry = pbStyleable->add_entries(); |
| 124 | serializeItemCommonToPb(entry, pbEntry); |
| 125 | serializeReferenceToPb(entry, pbEntry->mutable_attr()); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | void visit(Array* array) override { |
| 130 | pb::Array* pbArray = getPbCompoundValue()->mutable_array(); |
| 131 | for (auto& value : array->items) { |
| 132 | pb::Array_Entry* pbEntry = pbArray->add_entries(); |
| 133 | serializeItemCommonToPb(*value, pbEntry); |
| 134 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item()); |
| 135 | value->accept(&subVisitor); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | void visit(Plural* plural) override { |
| 140 | pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural(); |
| 141 | const size_t count = plural->values.size(); |
| 142 | for (size_t i = 0; i < count; i++) { |
| 143 | if (!plural->values[i]) { |
| 144 | // No plural value set here. |
| 145 | continue; |
| 146 | } |
| 147 | |
| 148 | pb::Plural_Entry* pbEntry = pbPlural->add_entries(); |
| 149 | pbEntry->set_arity(serializePluralEnumToPb(i)); |
| 150 | pb::Item* pbElement = pbEntry->mutable_item(); |
| 151 | serializeItemCommonToPb(*plural->values[i], pbEntry); |
| 152 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement); |
| 153 | plural->values[i]->accept(&subVisitor); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | private: |
| 158 | pb::Item* getPbItem() { |
| 159 | if (mOutPbValue) { |
| 160 | return mOutPbValue->mutable_item(); |
| 161 | } |
| 162 | return mOutPbItem; |
| 163 | } |
| 164 | |
| 165 | pb::CompoundValue* getPbCompoundValue() { |
| 166 | assert(mOutPbValue); |
| 167 | return mOutPbValue->mutable_compound_value(); |
| 168 | } |
| 169 | |
| 170 | template <typename T> |
| 171 | void serializeItemCommonToPb(const Item& item, T* pbItem) { |
| 172 | serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source()); |
| 173 | if (!item.getComment().empty()) { |
| 174 | pbItem->set_comment(util::utf16ToUtf8(item.getComment())); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) { |
| 179 | if (ref.id) { |
| 180 | pbRef->set_id(ref.id.value().id); |
Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | if (ref.name) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 184 | StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString()); |
| 185 | pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex())); |
| 186 | } |
Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 187 | |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 188 | pbRef->set_private_(ref.privateReference); |
| 189 | pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType)); |
| 190 | } |
| 191 | |
| 192 | StringPool* mSourcePool; |
| 193 | StringPool* mSymbolPool; |
| 194 | pb::Value* mOutPbValue; |
| 195 | pb::Item* mOutPbItem; |
| 196 | }; |
| 197 | |
| 198 | } // namespace |
| 199 | |
| 200 | std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) { |
| 201 | // We must do this before writing the resources, since the string pool IDs may change. |
| 202 | table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { |
| 203 | int diff = a.context.priority - b.context.priority; |
| 204 | if (diff < 0) return true; |
| 205 | if (diff > 0) return false; |
| 206 | diff = a.context.config.compare(b.context.config); |
| 207 | if (diff < 0) return true; |
| 208 | if (diff > 0) return false; |
| 209 | return a.value < b.value; |
| 210 | }); |
| 211 | table->stringPool.prune(); |
| 212 | |
| 213 | std::unique_ptr<pb::ResourceTable> pbTable = util::make_unique<pb::ResourceTable>(); |
| 214 | serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool()); |
| 215 | |
| 216 | StringPool sourcePool, symbolPool; |
| 217 | |
| 218 | for (auto& package : table->packages) { |
| 219 | pb::Package* pbPackage = pbTable->add_packages(); |
| 220 | if (package->id) { |
| 221 | pbPackage->set_package_id(package->id.value()); |
| 222 | } |
| 223 | pbPackage->set_package_name(util::utf16ToUtf8(package->name)); |
| 224 | |
| 225 | for (auto& type : package->types) { |
| 226 | pb::Type* pbType = pbPackage->add_types(); |
| 227 | if (type->id) { |
| 228 | pbType->set_id(type->id.value()); |
| 229 | } |
| 230 | pbType->set_name(util::utf16ToUtf8(toString(type->type))); |
| 231 | |
| 232 | for (auto& entry : type->entries) { |
| 233 | pb::Entry* pbEntry = pbType->add_entries(); |
| 234 | if (entry->id) { |
| 235 | pbEntry->set_id(entry->id.value()); |
| 236 | } |
| 237 | pbEntry->set_name(util::utf16ToUtf8(entry->name)); |
| 238 | |
| 239 | // Write the SymbolStatus struct. |
| 240 | pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status(); |
| 241 | pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state)); |
| 242 | serializeSourceToPb(entry->symbolStatus.source, &sourcePool, |
| 243 | pbStatus->mutable_source()); |
| 244 | pbStatus->set_comment(util::utf16ToUtf8(entry->symbolStatus.comment)); |
| 245 | |
| 246 | for (auto& configValue : entry->values) { |
| 247 | pb::ConfigValue* pbConfigValue = pbEntry->add_config_values(); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 248 | serializeConfig(configValue->config, pbConfigValue->mutable_config()); |
| 249 | if (!configValue->product.empty()) { |
| 250 | pbConfigValue->mutable_config()->set_product(configValue->product); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 251 | } |
| 252 | |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 253 | pb::Value* pbValue = pbConfigValue->mutable_value(); |
| 254 | serializeSourceToPb(configValue->value->getSource(), &sourcePool, |
| 255 | pbValue->mutable_source()); |
| 256 | if (!configValue->value->getComment().empty()) { |
| 257 | pbValue->set_comment(util::utf16ToUtf8(configValue->value->getComment())); |
| 258 | } |
| 259 | |
| 260 | if (configValue->value->isWeak()) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 261 | pbValue->set_weak(true); |
| 262 | } |
| 263 | |
| 264 | PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 265 | configValue->value->accept(&visitor); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 266 | } |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool()); |
| 272 | serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool()); |
| 273 | return pbTable; |
| 274 | } |
| 275 | |
| 276 | std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) { |
| 277 | std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>(); |
| 278 | pbFile->set_resource_name(util::utf16ToUtf8(file.name.toString())); |
| 279 | pbFile->set_source_path(file.source.path); |
| 280 | serializeConfig(file.config, pbFile->mutable_config()); |
| 281 | |
| 282 | for (const SourcedResourceName& exported : file.exportedSymbols) { |
| 283 | pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols(); |
| 284 | pbSymbol->set_resource_name(util::utf16ToUtf8(exported.name.toString())); |
| 285 | pbSymbol->set_line_no(exported.line); |
| 286 | } |
| 287 | return pbFile; |
| 288 | } |
| 289 | |
| 290 | CompiledFileOutputStream::CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out, |
| 291 | pb::CompiledFile* pbFile) : |
| 292 | mOut(out), mPbFile(pbFile) { |
| 293 | } |
| 294 | |
| 295 | bool CompiledFileOutputStream::ensureFileWritten() { |
| 296 | if (mPbFile) { |
| 297 | const uint64_t pbSize = mPbFile->ByteSize(); |
| 298 | mOut.WriteLittleEndian64(pbSize); |
| 299 | mPbFile->SerializeWithCachedSizes(&mOut); |
| 300 | const size_t padding = 4 - (pbSize & 0x03); |
| 301 | if (padding > 0) { |
| 302 | uint32_t zero = 0u; |
| 303 | mOut.WriteRaw(&zero, padding); |
| 304 | } |
| 305 | mPbFile = nullptr; |
| 306 | } |
| 307 | return !mOut.HadError(); |
| 308 | } |
| 309 | |
| 310 | bool CompiledFileOutputStream::Write(const void* data, int size) { |
| 311 | if (!ensureFileWritten()) { |
| 312 | return false; |
| 313 | } |
| 314 | mOut.WriteRaw(data, size); |
| 315 | return !mOut.HadError(); |
| 316 | } |
| 317 | |
| 318 | bool CompiledFileOutputStream::Finish() { |
| 319 | return ensureFileWritten(); |
| 320 | } |
| 321 | |
| 322 | } // namespace aapt |