blob: 0dfb01c181c680193599e69d01faec99a5a695c0 [file] [log] [blame]
Adam Lesinski59e04c62016-02-04 15:59:23 -08001/*
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 "ResourceTable.h"
18#include "ResourceUtils.h"
19#include "ValueVisitor.h"
20#include "proto/ProtoHelpers.h"
21#include "proto/ProtoSerialize.h"
Adam Lesinski59e04c62016-02-04 15:59:23 -080022
23#include <androidfw/ResourceTypes.h>
24
25namespace aapt {
26
27namespace {
28
29class ReferenceIdToNameVisitor : public ValueVisitor {
Adam Lesinskib54ef102016-10-21 13:38:42 -070030 public:
31 using ValueVisitor::visit;
Adam Lesinski59e04c62016-02-04 15:59:23 -080032
Adam Lesinskib54ef102016-10-21 13:38:42 -070033 explicit ReferenceIdToNameVisitor(
34 const std::map<ResourceId, ResourceNameRef>* mapping)
35 : mMapping(mapping) {
36 assert(mMapping);
37 }
38
39 void visit(Reference* reference) override {
40 if (!reference->id || !reference->id.value().isValid()) {
41 return;
Adam Lesinski59e04c62016-02-04 15:59:23 -080042 }
43
Adam Lesinskib54ef102016-10-21 13:38:42 -070044 ResourceId id = reference->id.value();
45 auto cacheIter = mMapping->find(id);
46 if (cacheIter != mMapping->end()) {
47 reference->name = cacheIter->second.toResourceName();
Adam Lesinski59e04c62016-02-04 15:59:23 -080048 }
Adam Lesinskib54ef102016-10-21 13:38:42 -070049 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080050
Adam Lesinskib54ef102016-10-21 13:38:42 -070051 private:
52 const std::map<ResourceId, ResourceNameRef>* mMapping;
Adam Lesinski59e04c62016-02-04 15:59:23 -080053};
54
55class PackagePbDeserializer {
Adam Lesinskib54ef102016-10-21 13:38:42 -070056 public:
57 PackagePbDeserializer(const android::ResStringPool* valuePool,
58 const android::ResStringPool* sourcePool,
59 const android::ResStringPool* symbolPool,
60 const Source& source, IDiagnostics* diag)
61 : mValuePool(valuePool),
62 mSourcePool(sourcePool),
63 mSymbolPool(symbolPool),
64 mSource(source),
65 mDiag(diag) {}
66
67 public:
68 bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
69 Maybe<uint8_t> id;
70 if (pbPackage.has_package_id()) {
71 id = static_cast<uint8_t>(pbPackage.package_id());
Adam Lesinski59e04c62016-02-04 15:59:23 -080072 }
73
Adam Lesinskib54ef102016-10-21 13:38:42 -070074 std::map<ResourceId, ResourceNameRef> idIndex;
Adam Lesinski59e04c62016-02-04 15:59:23 -080075
Adam Lesinskib54ef102016-10-21 13:38:42 -070076 ResourceTablePackage* pkg =
77 table->createPackage(pbPackage.package_name(), id);
78 for (const pb::Type& pbType : pbPackage.types()) {
79 const ResourceType* resType = parseResourceType(pbType.name());
80 if (!resType) {
81 mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name()
82 << "'");
83 return {};
84 }
Adam Lesinski59e04c62016-02-04 15:59:23 -080085
Adam Lesinskib54ef102016-10-21 13:38:42 -070086 ResourceTableType* type = pkg->findOrCreateType(*resType);
87
88 for (const pb::Entry& pbEntry : pbType.entries()) {
89 ResourceEntry* entry = type->findOrCreateEntry(pbEntry.name());
90
91 // Deserialize the symbol status (public/private with source and
92 // comments).
93 if (pbEntry.has_symbol_status()) {
94 const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
95 if (pbStatus.has_source()) {
96 deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
97 &entry->symbolStatus.source);
98 }
99
100 if (pbStatus.has_comment()) {
101 entry->symbolStatus.comment = pbStatus.comment();
102 }
103
104 SymbolState visibility =
105 deserializeVisibilityFromPb(pbStatus.visibility());
106 entry->symbolStatus.state = visibility;
107
108 if (visibility == SymbolState::kPublic) {
109 // This is a public symbol, we must encode the ID now if there is
110 // one.
111 if (pbEntry.has_id()) {
112 entry->id = static_cast<uint16_t>(pbEntry.id());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800113 }
114
Adam Lesinskib54ef102016-10-21 13:38:42 -0700115 if (type->symbolStatus.state != SymbolState::kPublic) {
116 // If the type has not been made public, do so now.
117 type->symbolStatus.state = SymbolState::kPublic;
118 if (pbType.has_id()) {
119 type->id = static_cast<uint8_t>(pbType.id());
120 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800121 }
Adam Lesinskib54ef102016-10-21 13:38:42 -0700122 } else if (visibility == SymbolState::kPrivate) {
123 if (type->symbolStatus.state == SymbolState::kUndefined) {
124 type->symbolStatus.state = SymbolState::kPrivate;
125 }
126 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800127 }
128
Adam Lesinskib54ef102016-10-21 13:38:42 -0700129 ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
130 if (resId.isValid()) {
131 idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
132 }
133
134 for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
135 const pb::ConfigDescription& pbConfig = pbConfigValue.config();
136
137 ConfigDescription config;
138 if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
139 mDiag->error(DiagMessage(mSource) << "invalid configuration");
140 return {};
141 }
142
143 ResourceConfigValue* configValue =
144 entry->findOrCreateValue(config, pbConfig.product());
145 if (configValue->value) {
146 // Duplicate config.
147 mDiag->error(DiagMessage(mSource) << "duplicate configuration");
148 return {};
149 }
150
151 configValue->value = deserializeValueFromPb(
152 pbConfigValue.value(), config, &table->stringPool);
153 if (!configValue->value) {
154 return {};
155 }
156 }
157 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800158 }
159
Adam Lesinskib54ef102016-10-21 13:38:42 -0700160 ReferenceIdToNameVisitor visitor(&idIndex);
161 visitAllValuesInPackage(pkg, &visitor);
162 return true;
163 }
164
165 private:
166 std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
167 const ConfigDescription& config,
168 StringPool* pool) {
169 if (pbItem.has_ref()) {
170 const pb::Reference& pbRef = pbItem.ref();
171 std::unique_ptr<Reference> ref = util::make_unique<Reference>();
172 if (!deserializeReferenceFromPb(pbRef, ref.get())) {
173 return {};
174 }
175 return std::move(ref);
176
177 } else if (pbItem.has_prim()) {
178 const pb::Primitive& pbPrim = pbItem.prim();
179 android::Res_value prim = {};
180 prim.dataType = static_cast<uint8_t>(pbPrim.type());
181 prim.data = pbPrim.data();
182 return util::make_unique<BinaryPrimitive>(prim);
183
184 } else if (pbItem.has_id()) {
185 return util::make_unique<Id>();
186
187 } else if (pbItem.has_str()) {
188 const uint32_t idx = pbItem.str().idx();
189 const std::string str = util::getString(*mValuePool, idx);
190
191 const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
192 if (spans && spans->name.index != android::ResStringPool_span::END) {
193 StyleString styleStr = {str};
194 while (spans->name.index != android::ResStringPool_span::END) {
195 styleStr.spans.push_back(
196 Span{util::getString(*mValuePool, spans->name.index),
197 spans->firstChar, spans->lastChar});
198 spans++;
199 }
200 return util::make_unique<StyledString>(pool->makeRef(
201 styleStr,
202 StringPool::Context(StringPool::Context::kStylePriority, config)));
203 }
204 return util::make_unique<String>(
205 pool->makeRef(str, StringPool::Context(config)));
206
207 } else if (pbItem.has_raw_str()) {
208 const uint32_t idx = pbItem.raw_str().idx();
209 const std::string str = util::getString(*mValuePool, idx);
210 return util::make_unique<RawString>(
211 pool->makeRef(str, StringPool::Context(config)));
212
213 } else if (pbItem.has_file()) {
214 const uint32_t idx = pbItem.file().path_idx();
215 const std::string str = util::getString(*mValuePool, idx);
216 return util::make_unique<FileReference>(pool->makeRef(
217 str,
218 StringPool::Context(StringPool::Context::kHighPriority, config)));
219
220 } else {
221 mDiag->error(DiagMessage(mSource) << "unknown item");
222 }
223 return {};
224 }
225
226 std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
Adam Lesinski59e04c62016-02-04 15:59:23 -0800227 const ConfigDescription& config,
228 StringPool* pool) {
Adam Lesinskib54ef102016-10-21 13:38:42 -0700229 const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800230
Adam Lesinskib54ef102016-10-21 13:38:42 -0700231 std::unique_ptr<Value> value;
232 if (pbValue.has_item()) {
233 value = deserializeItemFromPb(pbValue.item(), config, pool);
234 if (!value) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800235 return {};
Adam Lesinskib54ef102016-10-21 13:38:42 -0700236 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800237
Adam Lesinskib54ef102016-10-21 13:38:42 -0700238 } else if (pbValue.has_compound_value()) {
239 const pb::CompoundValue& pbCompoundValue = pbValue.compound_value();
240 if (pbCompoundValue.has_attr()) {
241 const pb::Attribute& pbAttr = pbCompoundValue.attr();
242 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
243 attr->typeMask = pbAttr.format_flags();
244 attr->minInt = pbAttr.min_int();
245 attr->maxInt = pbAttr.max_int();
246 for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
247 Attribute::Symbol symbol;
248 deserializeItemCommon(pbSymbol, &symbol.symbol);
249 if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800250 return {};
Adam Lesinskib54ef102016-10-21 13:38:42 -0700251 }
252 symbol.value = pbSymbol.value();
253 attr->symbols.push_back(std::move(symbol));
254 }
255 value = std::move(attr);
256
257 } else if (pbCompoundValue.has_style()) {
258 const pb::Style& pbStyle = pbCompoundValue.style();
259 std::unique_ptr<Style> style = util::make_unique<Style>();
260 if (pbStyle.has_parent()) {
261 style->parent = Reference();
262 if (!deserializeReferenceFromPb(pbStyle.parent(),
263 &style->parent.value())) {
264 return {};
265 }
266
267 if (pbStyle.has_parent_source()) {
268 Source parentSource;
269 deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
270 &parentSource);
271 style->parent.value().setSource(std::move(parentSource));
272 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800273 }
274
Adam Lesinskib54ef102016-10-21 13:38:42 -0700275 for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
276 Style::Entry entry;
277 deserializeItemCommon(pbEntry, &entry.key);
278 if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
279 return {};
280 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800281
Adam Lesinskib54ef102016-10-21 13:38:42 -0700282 entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
283 if (!entry.value) {
284 return {};
285 }
286
287 deserializeItemCommon(pbEntry, entry.value.get());
288 style->entries.push_back(std::move(entry));
289 }
290 value = std::move(style);
291
292 } else if (pbCompoundValue.has_styleable()) {
293 const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
294 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
295 for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
296 Reference attrRef;
297 deserializeItemCommon(pbEntry, &attrRef);
298 deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
299 styleable->entries.push_back(std::move(attrRef));
300 }
301 value = std::move(styleable);
302
303 } else if (pbCompoundValue.has_array()) {
304 const pb::Array& pbArray = pbCompoundValue.array();
305 std::unique_ptr<Array> array = util::make_unique<Array>();
306 for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
307 std::unique_ptr<Item> item =
308 deserializeItemFromPb(pbEntry.item(), config, pool);
309 if (!item) {
310 return {};
311 }
312
313 deserializeItemCommon(pbEntry, item.get());
314 array->items.push_back(std::move(item));
315 }
316 value = std::move(array);
317
318 } else if (pbCompoundValue.has_plural()) {
319 const pb::Plural& pbPlural = pbCompoundValue.plural();
320 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
321 for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
322 size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
323 plural->values[pluralIdx] =
324 deserializeItemFromPb(pbEntry.item(), config, pool);
325 if (!plural->values[pluralIdx]) {
326 return {};
327 }
328
329 deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
330 }
331 value = std::move(plural);
332
333 } else {
334 mDiag->error(DiagMessage(mSource) << "unknown compound value");
335 return {};
336 }
337 } else {
338 mDiag->error(DiagMessage(mSource) << "unknown value");
339 return {};
Adam Lesinski59e04c62016-02-04 15:59:23 -0800340 }
341
Adam Lesinskib54ef102016-10-21 13:38:42 -0700342 assert(value && "forgot to set value");
Adam Lesinski59e04c62016-02-04 15:59:23 -0800343
Adam Lesinskib54ef102016-10-21 13:38:42 -0700344 value->setWeak(isWeak);
345 deserializeItemCommon(pbValue, value.get());
346 return value;
347 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800348
Adam Lesinskib54ef102016-10-21 13:38:42 -0700349 bool deserializeReferenceFromPb(const pb::Reference& pbRef,
350 Reference* outRef) {
351 outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
352 outRef->privateReference = pbRef.private_();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800353
Adam Lesinskib54ef102016-10-21 13:38:42 -0700354 if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
355 return false;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800356 }
357
Adam Lesinskib54ef102016-10-21 13:38:42 -0700358 if (pbRef.has_id()) {
359 outRef->id = ResourceId(pbRef.id());
Adam Lesinski59e04c62016-02-04 15:59:23 -0800360 }
361
Adam Lesinskib54ef102016-10-21 13:38:42 -0700362 if (pbRef.has_symbol_idx()) {
363 const std::string strSymbol =
364 util::getString(*mSymbolPool, pbRef.symbol_idx());
365 ResourceNameRef nameRef;
366 if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
367 mDiag->error(DiagMessage(mSource) << "invalid reference name '"
368 << strSymbol << "'");
369 return false;
370 }
371
372 outRef->name = nameRef.toResourceName();
373 }
374 return true;
375 }
376
377 template <typename T>
378 void deserializeItemCommon(const T& pbItem, Value* outValue) {
379 if (pbItem.has_source()) {
380 Source source;
381 deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
382 outValue->setSource(std::move(source));
383 }
384
385 if (pbItem.has_comment()) {
386 outValue->setComment(pbItem.comment());
387 }
388 }
389
390 private:
391 const android::ResStringPool* mValuePool;
392 const android::ResStringPool* mSourcePool;
393 const android::ResStringPool* mSymbolPool;
394 const Source mSource;
395 IDiagnostics* mDiag;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800396};
397
Adam Lesinskib54ef102016-10-21 13:38:42 -0700398} // namespace
Adam Lesinski59e04c62016-02-04 15:59:23 -0800399
Adam Lesinskib54ef102016-10-21 13:38:42 -0700400std::unique_ptr<ResourceTable> deserializeTableFromPb(
401 const pb::ResourceTable& pbTable, const Source& source,
402 IDiagnostics* diag) {
403 // We import the android namespace because on Windows NO_ERROR is a macro, not
404 // an enum, which
405 // causes errors when qualifying it with android::
406 using namespace android;
Adam Lesinski803c7c82016-04-06 16:09:43 -0700407
Adam Lesinskib54ef102016-10-21 13:38:42 -0700408 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800409
Adam Lesinskib54ef102016-10-21 13:38:42 -0700410 if (!pbTable.has_string_pool()) {
411 diag->error(DiagMessage(source) << "no string pool found");
412 return {};
413 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800414
Adam Lesinskib54ef102016-10-21 13:38:42 -0700415 ResStringPool valuePool;
416 status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
417 pbTable.string_pool().data().size());
418 if (result != NO_ERROR) {
419 diag->error(DiagMessage(source) << "invalid string pool");
420 return {};
421 }
422
423 ResStringPool sourcePool;
424 if (pbTable.has_source_pool()) {
425 result = sourcePool.setTo(pbTable.source_pool().data().data(),
426 pbTable.source_pool().data().size());
Adam Lesinski803c7c82016-04-06 16:09:43 -0700427 if (result != NO_ERROR) {
Adam Lesinskib54ef102016-10-21 13:38:42 -0700428 diag->error(DiagMessage(source) << "invalid source pool");
429 return {};
Adam Lesinski59e04c62016-02-04 15:59:23 -0800430 }
Adam Lesinskib54ef102016-10-21 13:38:42 -0700431 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800432
Adam Lesinskib54ef102016-10-21 13:38:42 -0700433 ResStringPool symbolPool;
434 if (pbTable.has_symbol_pool()) {
435 result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
436 pbTable.symbol_pool().data().size());
437 if (result != NO_ERROR) {
438 diag->error(DiagMessage(source) << "invalid symbol pool");
439 return {};
Adam Lesinski59e04c62016-02-04 15:59:23 -0800440 }
Adam Lesinskib54ef102016-10-21 13:38:42 -0700441 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800442
Adam Lesinskib54ef102016-10-21 13:38:42 -0700443 PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool,
444 &symbolPool, source, diag);
445 for (const pb::Package& pbPackage : pbTable.packages()) {
446 if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
447 return {};
Adam Lesinski59e04c62016-02-04 15:59:23 -0800448 }
Adam Lesinskib54ef102016-10-21 13:38:42 -0700449 }
450 return table;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800451}
452
Adam Lesinskib54ef102016-10-21 13:38:42 -0700453std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(
454 const pb::CompiledFile& pbFile, const Source& source, IDiagnostics* diag) {
455 std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800456
Adam Lesinskib54ef102016-10-21 13:38:42 -0700457 ResourceNameRef nameRef;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800458
Adam Lesinskib54ef102016-10-21 13:38:42 -0700459 // Need to create an lvalue here so that nameRef can point to something real.
460 if (!ResourceUtils::parseResourceName(pbFile.resource_name(), &nameRef)) {
461 diag->error(DiagMessage(source)
462 << "invalid resource name in compiled file header: "
463 << pbFile.resource_name());
464 return {};
465 }
466 file->name = nameRef.toResourceName();
467 file->source.path = pbFile.source_path();
468 deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
469
470 for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
471 // Need to create an lvalue here so that nameRef can point to something
472 // real.
473 if (!ResourceUtils::parseResourceName(pbSymbol.resource_name(), &nameRef)) {
474 diag->error(DiagMessage(source)
475 << "invalid resource name for exported symbol in "
476 "compiled file header: "
477 << pbFile.resource_name());
478 return {};
Adam Lesinski59e04c62016-02-04 15:59:23 -0800479 }
Adam Lesinskib54ef102016-10-21 13:38:42 -0700480 file->exportedSymbols.push_back(
481 SourcedResourceName{nameRef.toResourceName(), pbSymbol.line_no()});
482 }
483 return file;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800484}
485
Adam Lesinskib54ef102016-10-21 13:38:42 -0700486} // namespace aapt