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 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 25 | using google::protobuf::io::CodedOutputStream; |
| 26 | using google::protobuf::io::CodedInputStream; |
| 27 | using google::protobuf::io::ZeroCopyOutputStream; |
| 28 | |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 29 | namespace aapt { |
| 30 | |
| 31 | namespace { |
| 32 | |
| 33 | class PbSerializerVisitor : public RawValueVisitor { |
| 34 | public: |
| 35 | using RawValueVisitor::visit; |
| 36 | |
| 37 | /** |
| 38 | * Constructor to use when expecting to serialize any value. |
| 39 | */ |
| 40 | PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) : |
| 41 | mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue), |
| 42 | mOutPbItem(nullptr) { |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * Constructor to use when expecting to serialize an Item. |
| 47 | */ |
| 48 | PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) : |
| 49 | mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr), |
| 50 | mOutPbItem(outPbItem) { |
| 51 | } |
| 52 | |
| 53 | void visit(Reference* ref) override { |
| 54 | serializeReferenceToPb(*ref, getPbItem()->mutable_ref()); |
| 55 | } |
| 56 | |
| 57 | void visit(String* str) override { |
| 58 | getPbItem()->mutable_str()->set_idx(str->value.getIndex()); |
| 59 | } |
| 60 | |
| 61 | void visit(StyledString* str) override { |
| 62 | getPbItem()->mutable_str()->set_idx(str->value.getIndex()); |
| 63 | } |
| 64 | |
| 65 | void visit(FileReference* file) override { |
| 66 | getPbItem()->mutable_file()->set_path_idx(file->path.getIndex()); |
| 67 | } |
| 68 | |
| 69 | void visit(Id* id) override { |
| 70 | getPbItem()->mutable_id(); |
| 71 | } |
| 72 | |
| 73 | void visit(RawString* rawStr) override { |
| 74 | getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex()); |
| 75 | } |
| 76 | |
| 77 | void visit(BinaryPrimitive* prim) override { |
| 78 | android::Res_value val = {}; |
| 79 | prim->flatten(&val); |
| 80 | |
| 81 | pb::Primitive* pbPrim = getPbItem()->mutable_prim(); |
| 82 | pbPrim->set_type(val.dataType); |
| 83 | pbPrim->set_data(val.data); |
| 84 | } |
| 85 | |
| 86 | void visitItem(Item* item) override { |
| 87 | assert(false && "unimplemented item"); |
| 88 | } |
| 89 | |
| 90 | void visit(Attribute* attr) override { |
| 91 | pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr(); |
| 92 | pbAttr->set_format_flags(attr->typeMask); |
| 93 | pbAttr->set_min_int(attr->minInt); |
| 94 | pbAttr->set_max_int(attr->maxInt); |
| 95 | |
| 96 | for (auto& symbol : attr->symbols) { |
| 97 | pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols(); |
| 98 | serializeItemCommonToPb(symbol.symbol, pbSymbol); |
| 99 | serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name()); |
| 100 | pbSymbol->set_value(symbol.value); |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | void visit(Style* style) override { |
| 105 | pb::Style* pbStyle = getPbCompoundValue()->mutable_style(); |
| 106 | if (style->parent) { |
| 107 | serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent()); |
| 108 | serializeSourceToPb(style->parent.value().getSource(), |
| 109 | mSourcePool, |
| 110 | pbStyle->mutable_parent_source()); |
| 111 | } |
| 112 | |
| 113 | for (Style::Entry& entry : style->entries) { |
| 114 | pb::Style_Entry* pbEntry = pbStyle->add_entries(); |
| 115 | serializeReferenceToPb(entry.key, pbEntry->mutable_key()); |
| 116 | |
| 117 | pb::Item* pbItem = pbEntry->mutable_item(); |
Adam Lesinski | 5f7c461 | 2016-02-12 23:38:08 -0800 | [diff] [blame] | 118 | serializeItemCommonToPb(entry.key, pbEntry); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 119 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem); |
| 120 | entry.value->accept(&subVisitor); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | void visit(Styleable* styleable) override { |
| 125 | pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable(); |
| 126 | for (Reference& entry : styleable->entries) { |
| 127 | pb::Styleable_Entry* pbEntry = pbStyleable->add_entries(); |
| 128 | serializeItemCommonToPb(entry, pbEntry); |
| 129 | serializeReferenceToPb(entry, pbEntry->mutable_attr()); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | void visit(Array* array) override { |
| 134 | pb::Array* pbArray = getPbCompoundValue()->mutable_array(); |
| 135 | for (auto& value : array->items) { |
| 136 | pb::Array_Entry* pbEntry = pbArray->add_entries(); |
| 137 | serializeItemCommonToPb(*value, pbEntry); |
| 138 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item()); |
| 139 | value->accept(&subVisitor); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | void visit(Plural* plural) override { |
| 144 | pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural(); |
| 145 | const size_t count = plural->values.size(); |
| 146 | for (size_t i = 0; i < count; i++) { |
| 147 | if (!plural->values[i]) { |
| 148 | // No plural value set here. |
| 149 | continue; |
| 150 | } |
| 151 | |
| 152 | pb::Plural_Entry* pbEntry = pbPlural->add_entries(); |
| 153 | pbEntry->set_arity(serializePluralEnumToPb(i)); |
| 154 | pb::Item* pbElement = pbEntry->mutable_item(); |
| 155 | serializeItemCommonToPb(*plural->values[i], pbEntry); |
| 156 | PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement); |
| 157 | plural->values[i]->accept(&subVisitor); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | private: |
| 162 | pb::Item* getPbItem() { |
| 163 | if (mOutPbValue) { |
| 164 | return mOutPbValue->mutable_item(); |
| 165 | } |
| 166 | return mOutPbItem; |
| 167 | } |
| 168 | |
| 169 | pb::CompoundValue* getPbCompoundValue() { |
| 170 | assert(mOutPbValue); |
| 171 | return mOutPbValue->mutable_compound_value(); |
| 172 | } |
| 173 | |
| 174 | template <typename T> |
| 175 | void serializeItemCommonToPb(const Item& item, T* pbItem) { |
| 176 | serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source()); |
| 177 | if (!item.getComment().empty()) { |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 178 | pbItem->set_comment(item.getComment()); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 179 | } |
| 180 | } |
| 181 | |
| 182 | void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) { |
| 183 | if (ref.id) { |
| 184 | pbRef->set_id(ref.id.value().id); |
Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | if (ref.name) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 188 | StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString()); |
| 189 | pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex())); |
| 190 | } |
Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 191 | |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 192 | pbRef->set_private_(ref.privateReference); |
| 193 | pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType)); |
| 194 | } |
| 195 | |
| 196 | StringPool* mSourcePool; |
| 197 | StringPool* mSymbolPool; |
| 198 | pb::Value* mOutPbValue; |
| 199 | pb::Item* mOutPbItem; |
| 200 | }; |
| 201 | |
| 202 | } // namespace |
| 203 | |
| 204 | std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) { |
| 205 | // We must do this before writing the resources, since the string pool IDs may change. |
| 206 | table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { |
| 207 | int diff = a.context.priority - b.context.priority; |
| 208 | if (diff < 0) return true; |
| 209 | if (diff > 0) return false; |
| 210 | diff = a.context.config.compare(b.context.config); |
| 211 | if (diff < 0) return true; |
| 212 | if (diff > 0) return false; |
| 213 | return a.value < b.value; |
| 214 | }); |
| 215 | table->stringPool.prune(); |
| 216 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 217 | auto pbTable = util::make_unique<pb::ResourceTable>(); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 218 | serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool()); |
| 219 | |
| 220 | StringPool sourcePool, symbolPool; |
| 221 | |
| 222 | for (auto& package : table->packages) { |
| 223 | pb::Package* pbPackage = pbTable->add_packages(); |
| 224 | if (package->id) { |
| 225 | pbPackage->set_package_id(package->id.value()); |
| 226 | } |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 227 | pbPackage->set_package_name(package->name); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 228 | |
| 229 | for (auto& type : package->types) { |
| 230 | pb::Type* pbType = pbPackage->add_types(); |
| 231 | if (type->id) { |
| 232 | pbType->set_id(type->id.value()); |
| 233 | } |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 234 | pbType->set_name(toString(type->type).toString()); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 235 | |
| 236 | for (auto& entry : type->entries) { |
| 237 | pb::Entry* pbEntry = pbType->add_entries(); |
| 238 | if (entry->id) { |
| 239 | pbEntry->set_id(entry->id.value()); |
| 240 | } |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 241 | pbEntry->set_name(entry->name); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 242 | |
| 243 | // Write the SymbolStatus struct. |
| 244 | pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status(); |
| 245 | pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state)); |
| 246 | serializeSourceToPb(entry->symbolStatus.source, &sourcePool, |
| 247 | pbStatus->mutable_source()); |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 248 | pbStatus->set_comment(entry->symbolStatus.comment); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 249 | |
| 250 | for (auto& configValue : entry->values) { |
| 251 | pb::ConfigValue* pbConfigValue = pbEntry->add_config_values(); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 252 | serializeConfig(configValue->config, pbConfigValue->mutable_config()); |
| 253 | if (!configValue->product.empty()) { |
| 254 | pbConfigValue->mutable_config()->set_product(configValue->product); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 255 | } |
| 256 | |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 257 | pb::Value* pbValue = pbConfigValue->mutable_value(); |
| 258 | serializeSourceToPb(configValue->value->getSource(), &sourcePool, |
| 259 | pbValue->mutable_source()); |
| 260 | if (!configValue->value->getComment().empty()) { |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 261 | pbValue->set_comment(configValue->value->getComment()); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 262 | } |
| 263 | |
| 264 | if (configValue->value->isWeak()) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 265 | pbValue->set_weak(true); |
| 266 | } |
| 267 | |
| 268 | PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue); |
Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 269 | configValue->value->accept(&visitor); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 270 | } |
| 271 | } |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool()); |
| 276 | serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool()); |
| 277 | return pbTable; |
| 278 | } |
| 279 | |
| 280 | std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) { |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 281 | auto pbFile = util::make_unique<pb::CompiledFile>(); |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 282 | pbFile->set_resource_name(file.name.toString()); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 283 | pbFile->set_source_path(file.source.path); |
| 284 | serializeConfig(file.config, pbFile->mutable_config()); |
| 285 | |
| 286 | for (const SourcedResourceName& exported : file.exportedSymbols) { |
| 287 | pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols(); |
Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 288 | pbSymbol->set_resource_name(exported.name.toString()); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 289 | pbSymbol->set_line_no(exported.line); |
| 290 | } |
| 291 | return pbFile; |
| 292 | } |
| 293 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 294 | CompiledFileOutputStream::CompiledFileOutputStream(ZeroCopyOutputStream* out) : mOut(out) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 295 | } |
| 296 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 297 | void CompiledFileOutputStream::ensureAlignedWrite() { |
| 298 | const int padding = mOut.ByteCount() % 4; |
| 299 | if (padding > 0) { |
| 300 | uint32_t zero = 0u; |
| 301 | mOut.WriteRaw(&zero, padding); |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 302 | } |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 303 | } |
| 304 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 305 | void CompiledFileOutputStream::WriteLittleEndian32(uint32_t val) { |
| 306 | ensureAlignedWrite(); |
| 307 | mOut.WriteLittleEndian32(val); |
| 308 | } |
| 309 | |
| 310 | void CompiledFileOutputStream::WriteCompiledFile(const pb::CompiledFile* compiledFile) { |
| 311 | ensureAlignedWrite(); |
| 312 | mOut.WriteLittleEndian64(static_cast<uint64_t>(compiledFile->ByteSize())); |
| 313 | compiledFile->SerializeWithCachedSizes(&mOut); |
| 314 | } |
| 315 | |
| 316 | void CompiledFileOutputStream::WriteData(const BigBuffer* buffer) { |
| 317 | ensureAlignedWrite(); |
| 318 | mOut.WriteLittleEndian64(static_cast<uint64_t>(buffer->size())); |
| 319 | for (const BigBuffer::Block& block : *buffer) { |
| 320 | mOut.WriteRaw(block.buffer.get(), block.size); |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | void CompiledFileOutputStream::WriteData(const void* data, size_t len) { |
| 325 | ensureAlignedWrite(); |
| 326 | mOut.WriteLittleEndian64(static_cast<uint64_t>(len)); |
| 327 | mOut.WriteRaw(data, len); |
| 328 | } |
| 329 | |
| 330 | bool CompiledFileOutputStream::HadError() { |
| 331 | return mOut.HadError(); |
| 332 | } |
| 333 | |
| 334 | CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) : |
| 335 | mIn(static_cast<const uint8_t*>(data), size) { |
| 336 | } |
| 337 | |
| 338 | void CompiledFileInputStream::ensureAlignedRead() { |
| 339 | const int padding = mIn.CurrentPosition() % 4; |
| 340 | if (padding > 0) { |
| 341 | // Reads are always 4 byte aligned. |
| 342 | mIn.Skip(padding); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | bool CompiledFileInputStream::ReadLittleEndian32(uint32_t* outVal) { |
| 347 | ensureAlignedRead(); |
| 348 | return mIn.ReadLittleEndian32(outVal); |
| 349 | } |
| 350 | |
| 351 | bool CompiledFileInputStream::ReadCompiledFile(pb::CompiledFile* outVal) { |
| 352 | ensureAlignedRead(); |
| 353 | |
| 354 | uint64_t pbSize = 0u; |
| 355 | if (!mIn.ReadLittleEndian64(&pbSize)) { |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 356 | return false; |
| 357 | } |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 358 | |
| 359 | CodedInputStream::Limit l = mIn.PushLimit(static_cast<int>(pbSize)); |
| 360 | |
| 361 | // Check that we haven't tried to read past the end. |
| 362 | if (static_cast<uint64_t>(mIn.BytesUntilLimit()) != pbSize) { |
| 363 | mIn.PopLimit(l); |
| 364 | mIn.PushLimit(0); |
| 365 | return false; |
| 366 | } |
| 367 | |
| 368 | if (!outVal->ParsePartialFromCodedStream(&mIn)) { |
| 369 | mIn.PopLimit(l); |
| 370 | mIn.PushLimit(0); |
| 371 | return false; |
| 372 | } |
| 373 | |
| 374 | mIn.PopLimit(l); |
| 375 | return true; |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 376 | } |
| 377 | |
Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame] | 378 | bool CompiledFileInputStream::ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen) { |
| 379 | ensureAlignedRead(); |
| 380 | |
| 381 | uint64_t pbSize = 0u; |
| 382 | if (!mIn.ReadLittleEndian64(&pbSize)) { |
| 383 | return false; |
| 384 | } |
| 385 | |
| 386 | // Check that we aren't trying to read past the end. |
| 387 | if (pbSize > static_cast<uint64_t>(mIn.BytesUntilLimit())) { |
| 388 | mIn.PushLimit(0); |
| 389 | return false; |
| 390 | } |
| 391 | |
| 392 | uint64_t offset = static_cast<uint64_t>(mIn.CurrentPosition()); |
| 393 | if (!mIn.Skip(pbSize)) { |
| 394 | return false; |
| 395 | } |
| 396 | |
| 397 | *outOffset = offset; |
| 398 | *outLen = pbSize; |
| 399 | return true; |
Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 400 | } |
| 401 | |
| 402 | } // namespace aapt |