blob: 3559f435329b031701bdfb8753edcf8f81b87f30 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
2 * Copyright (C) 2015 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 "BinaryResourceParser.h"
18#include "Logger.h"
19#include "ResChunkPullParser.h"
Adam Lesinski769de982015-04-10 19:43:55 -070020#include "Resolver.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080021#include "ResourceParser.h"
22#include "ResourceTable.h"
23#include "ResourceTypeExtensions.h"
24#include "ResourceValues.h"
25#include "Source.h"
26#include "Util.h"
27
28#include <androidfw/ResourceTypes.h>
29#include <androidfw/TypeWrappers.h>
30#include <map>
31#include <string>
32
33namespace aapt {
34
35using namespace android;
36
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080037/*
38 * Visitor that converts a reference's resource ID to a resource name,
39 * given a mapping from resource ID to resource name.
40 */
41struct ReferenceIdToNameVisitor : ValueVisitor {
Adam Lesinski24aad162015-04-24 19:19:30 -070042 ReferenceIdToNameVisitor(const std::shared_ptr<IResolver>& resolver,
Adam Lesinski769de982015-04-10 19:43:55 -070043 std::map<ResourceId, ResourceName>* cache) :
44 mResolver(resolver), mCache(cache) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080045 }
46
47 void visit(Reference& reference, ValueVisitorArgs&) override {
48 idToName(reference);
49 }
50
51 void visit(Attribute& attr, ValueVisitorArgs&) override {
52 for (auto& entry : attr.symbols) {
53 idToName(entry.symbol);
54 }
55 }
56
57 void visit(Style& style, ValueVisitorArgs&) override {
58 if (style.parent.id.isValid()) {
59 idToName(style.parent);
60 }
61
62 for (auto& entry : style.entries) {
63 idToName(entry.key);
64 entry.value->accept(*this, {});
65 }
66 }
67
68 void visit(Styleable& styleable, ValueVisitorArgs&) override {
69 for (auto& attr : styleable.entries) {
70 idToName(attr);
71 }
72 }
73
74 void visit(Array& array, ValueVisitorArgs&) override {
75 for (auto& item : array.items) {
76 item->accept(*this, {});
77 }
78 }
79
80 void visit(Plural& plural, ValueVisitorArgs&) override {
81 for (auto& item : plural.values) {
82 if (item) {
83 item->accept(*this, {});
84 }
85 }
86 }
87
88private:
89 void idToName(Reference& reference) {
90 if (!reference.id.isValid()) {
91 return;
92 }
93
Adam Lesinski769de982015-04-10 19:43:55 -070094 auto cacheIter = mCache->find(reference.id);
95 if (cacheIter != mCache->end()) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 reference.name = cacheIter->second;
97 reference.id = 0;
Adam Lesinski769de982015-04-10 19:43:55 -070098 } else {
Adam Lesinski24aad162015-04-24 19:19:30 -070099 Maybe<ResourceName> result = mResolver->findName(reference.id);
100 if (result) {
101 reference.name = result.value();
Adam Lesinski769de982015-04-10 19:43:55 -0700102
103 // Add to cache.
104 mCache->insert({reference.id, reference.name});
Adam Lesinski24aad162015-04-24 19:19:30 -0700105
106 reference.id = 0;
Adam Lesinski769de982015-04-10 19:43:55 -0700107 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800108 }
109 }
110
Adam Lesinski24aad162015-04-24 19:19:30 -0700111 std::shared_ptr<IResolver> mResolver;
Adam Lesinski769de982015-04-10 19:43:55 -0700112 std::map<ResourceId, ResourceName>* mCache;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800113};
114
115
Adam Lesinski769de982015-04-10 19:43:55 -0700116BinaryResourceParser::BinaryResourceParser(const std::shared_ptr<ResourceTable>& table,
Adam Lesinski24aad162015-04-24 19:19:30 -0700117 const std::shared_ptr<IResolver>& resolver,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800118 const Source& source,
119 const void* data,
120 size_t len) :
Adam Lesinski769de982015-04-10 19:43:55 -0700121 mTable(table), mResolver(resolver), mSource(source), mData(data), mDataLen(len) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800122}
123
124bool BinaryResourceParser::parse() {
125 ResChunkPullParser parser(mData, mDataLen);
126
127 bool error = false;
128 while(ResChunkPullParser::isGoodEvent(parser.next())) {
129 if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
130 Logger::warn(mSource)
131 << "unknown chunk of type '"
132 << parser.getChunk()->type
133 << "'."
134 << std::endl;
135 continue;
136 }
137
138 error |= !parseTable(parser.getChunk());
139 }
140
141 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
142 Logger::error(mSource)
143 << "bad document: "
144 << parser.getLastError()
145 << "."
146 << std::endl;
147 return false;
148 }
149 return !error;
150}
151
152bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
153 if (!mSymbolEntries || mSymbolEntryCount == 0) {
154 return false;
155 }
156
Adam Lesinskica2fc352015-04-03 12:08:26 -0700157 if (reinterpret_cast<uintptr_t>(data) < reinterpret_cast<uintptr_t>(mData)) {
158 return false;
159 }
160
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800161 // We only support 32 bit offsets right now.
Adam Lesinskica2fc352015-04-03 12:08:26 -0700162 const uintptr_t offset = reinterpret_cast<uintptr_t>(data) -
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800163 reinterpret_cast<uintptr_t>(mData);
164 if (offset > std::numeric_limits<uint32_t>::max()) {
165 return false;
166 }
167
168 for (size_t i = 0; i < mSymbolEntryCount; i++) {
169 if (mSymbolEntries[i].offset == offset) {
170 // This offset is a symbol!
171 const StringPiece16 str = util::getString(mSymbolPool,
172 mSymbolEntries[i].stringIndex);
173 StringPiece16 typeStr;
174 ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr,
175 &outSymbol->entry);
176 const ResourceType* type = parseResourceType(typeStr);
177 if (!type) {
178 return false;
179 }
180 outSymbol->type = *type;
181
182 // Since we scan the symbol table in order, we can start looking for the
183 // next symbol from this point.
184 mSymbolEntryCount -= i + 1;
185 mSymbolEntries += i + 1;
186 return true;
187 }
188 }
189 return false;
190}
191
192bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
193 const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk);
194 if (!symbolTableHeader) {
195 Logger::error(mSource)
196 << "could not parse chunk as SymbolTable_header."
197 << std::endl;
198 return false;
199 }
200
201 const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry);
202 if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) {
203 Logger::error(mSource)
204 << "entries extend beyond chunk."
205 << std::endl;
206 return false;
207 }
208
209 mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>(
210 getChunkData(symbolTableHeader->header));
211 mSymbolEntryCount = symbolTableHeader->count;
212
213 ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes,
214 getChunkDataLen(symbolTableHeader->header) - entrySizeBytes);
215 if (!ResChunkPullParser::isGoodEvent(parser.next())) {
216 Logger::error(mSource)
217 << "failed to parse chunk: "
218 << parser.getLastError()
219 << "."
220 << std::endl;
221 return false;
222 }
223
224 if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) {
225 Logger::error(mSource)
226 << "expected Symbol string pool."
227 << std::endl;
228 return false;
229 }
230
Adam Lesinskica2fc352015-04-03 12:08:26 -0700231 if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800232 Logger::error(mSource)
233 << "failed to parse symbol string pool with code: "
234 << mSymbolPool.getError()
235 << "."
236 << std::endl;
237 return false;
238 }
239 return true;
240}
241
242bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
243 const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
244 if (!tableHeader) {
245 Logger::error(mSource)
246 << "could not parse chunk as ResTable_header."
247 << std::endl;
248 return false;
249 }
250
251 ResChunkPullParser parser(getChunkData(tableHeader->header),
252 getChunkDataLen(tableHeader->header));
253 while (ResChunkPullParser::isGoodEvent(parser.next())) {
254 switch (parser.getChunk()->type) {
255 case android::RES_STRING_POOL_TYPE:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700256 if (mValuePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800257 if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700258 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800259 Logger::error(mSource)
260 << "failed to parse value string pool with code: "
261 << mValuePool.getError()
262 << "."
263 << std::endl;
264 return false;
265 }
266
267 // Reserve some space for the strings we are going to add.
268 mTable->getValueStringPool().hintWillAdd(
269 mValuePool.size(), mValuePool.styleCount());
270 } else {
271 Logger::warn(mSource)
272 << "unexpected string pool."
273 << std::endl;
274 }
275 break;
276
277 case RES_TABLE_SYMBOL_TABLE_TYPE:
278 if (!parseSymbolTable(parser.getChunk())) {
279 return false;
280 }
281 break;
282
283 case RES_TABLE_SOURCE_POOL_TYPE: {
284 if (mSourcePool.setTo(getChunkData(*parser.getChunk()),
Adam Lesinskica2fc352015-04-03 12:08:26 -0700285 getChunkDataLen(*parser.getChunk())) != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800286 Logger::error(mSource)
287 << "failed to parse source pool with code: "
288 << mSourcePool.getError()
289 << "."
290 << std::endl;
291 return false;
292 }
293 break;
294 }
295
296 case android::RES_TABLE_PACKAGE_TYPE:
297 if (!parsePackage(parser.getChunk())) {
298 return false;
299 }
300 break;
301
302 default:
303 Logger::warn(mSource)
304 << "unexpected chunk of type "
305 << parser.getChunk()->type
306 << "."
307 << std::endl;
308 break;
309 }
310 }
311
312 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
313 Logger::error(mSource)
314 << "bad resource table: " << parser.getLastError()
315 << "."
316 << std::endl;
317 return false;
318 }
319 return true;
320}
321
322bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700323 if (mValuePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800324 Logger::error(mSource)
325 << "no value string pool for ResTable."
326 << std::endl;
327 return false;
328 }
329
330 const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
331 if (!packageHeader) {
332 Logger::error(mSource)
333 << "could not parse chunk as ResTable_header."
334 << std::endl;
335 return false;
336 }
337
338 if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) {
339 // This is the first time the table has it's package ID set.
340 mTable->setPackageId(packageHeader->id);
341 } else if (mTable->getPackageId() != packageHeader->id) {
342 Logger::error(mSource)
343 << "ResTable_package has package ID "
344 << std::hex << packageHeader->id << std::dec
345 << " but ResourceTable has package ID "
346 << std::hex << mTable->getPackageId() << std::dec
347 << std::endl;
348 return false;
349 }
350
351 size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name),
352 sizeof(packageHeader->name) / sizeof(packageHeader->name[0]));
353 mTable->setPackage(StringPiece16(reinterpret_cast<const char16_t*>(packageHeader->name), len));
354
355 ResChunkPullParser parser(getChunkData(packageHeader->header),
356 getChunkDataLen(packageHeader->header));
357 while (ResChunkPullParser::isGoodEvent(parser.next())) {
358 switch (parser.getChunk()->type) {
359 case android::RES_STRING_POOL_TYPE:
Adam Lesinskica2fc352015-04-03 12:08:26 -0700360 if (mTypePool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800361 if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700362 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800363 Logger::error(mSource)
364 << "failed to parse type string pool with code "
365 << mTypePool.getError()
366 << "."
367 << std::endl;
368 return false;
369 }
Adam Lesinskica2fc352015-04-03 12:08:26 -0700370 } else if (mKeyPool.getError() == NO_INIT) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800371 if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
Adam Lesinskica2fc352015-04-03 12:08:26 -0700372 NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800373 Logger::error(mSource)
374 << "failed to parse key string pool with code "
375 << mKeyPool.getError()
376 << "."
377 << std::endl;
378 return false;
379 }
380 } else {
381 Logger::warn(mSource)
382 << "unexpected string pool."
383 << std::endl;
384 }
385 break;
386
387 case android::RES_TABLE_TYPE_SPEC_TYPE:
388 if (!parseTypeSpec(parser.getChunk())) {
389 return false;
390 }
391 break;
392
393 case android::RES_TABLE_TYPE_TYPE:
394 if (!parseType(parser.getChunk())) {
395 return false;
396 }
397 break;
398
Adam Lesinski6ff19662015-04-30 17:40:46 -0700399 case RES_TABLE_PUBLIC_TYPE:
400 if (!parsePublic(parser.getChunk())) {
401 return false;
402 }
403 break;
404
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800405 default:
406 Logger::warn(mSource)
407 << "unexpected chunk of type "
408 << parser.getChunk()->type
409 << "."
410 << std::endl;
411 break;
412 }
413 }
414
415 if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
416 Logger::error(mSource)
417 << "bad package: "
418 << parser.getLastError()
419 << "."
420 << std::endl;
421 return false;
422 }
423
424 // Now go through the table and change resource ID references to
425 // symbolic references.
426
Adam Lesinski769de982015-04-10 19:43:55 -0700427 ReferenceIdToNameVisitor visitor(mResolver, &mIdIndex);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800428 for (auto& type : *mTable) {
429 for (auto& entry : type->entries) {
430 for (auto& configValue : entry->values) {
431 configValue.value->accept(visitor, {});
432 }
433 }
434 }
435 return true;
436}
437
Adam Lesinski6ff19662015-04-30 17:40:46 -0700438bool BinaryResourceParser::parsePublic(const ResChunk_header* chunk) {
439 const Public_header* header = convertTo<Public_header>(chunk);
440
441 if (header->typeId == 0) {
442 Logger::error(mSource)
443 << "invalid type ID " << header->typeId << std::endl;
444 return false;
445 }
446
447 const ResourceType* parsedType = parseResourceType(util::getString(mTypePool,
448 header->typeId - 1));
449 if (!parsedType) {
450 Logger::error(mSource)
451 << "invalid type " << util::getString(mTypePool, header->typeId - 1) << std::endl;
452 return false;
453 }
454
455 const uintptr_t chunkEnd = reinterpret_cast<uintptr_t>(chunk) + chunk->size;
456 const Public_entry* entry = reinterpret_cast<const Public_entry*>(
457 getChunkData(header->header));
458 for (uint32_t i = 0; i < header->count; i++) {
459 if (reinterpret_cast<uintptr_t>(entry) + sizeof(*entry) > chunkEnd) {
460 Logger::error(mSource)
461 << "Public_entry extends beyond chunk."
462 << std::endl;
463 return false;
464 }
465
466 const ResourceId resId = { mTable->getPackageId(), header->typeId, entry->entryId };
467 const ResourceName name = {
468 mTable->getPackage(),
469 *parsedType,
470 util::getString(mKeyPool, entry->key.index).toString() };
471
472 SourceLine source;
473 if (mSourcePool.getError() == NO_ERROR) {
474 source.path = util::utf16ToUtf8(util::getString(mSourcePool, entry->source.index));
475 source.line = entry->sourceLine;
476 }
477
Adam Lesinski330edcd2015-05-04 17:40:56 -0700478 if (!mTable->markPublicAllowMangled(name, resId, source)) {
Adam Lesinski6ff19662015-04-30 17:40:46 -0700479 return false;
480 }
481
Adam Lesinski330edcd2015-05-04 17:40:56 -0700482 // Add this resource name->id mapping to the index so
483 // that we can resolve all ID references to name references.
484 auto cacheIter = mIdIndex.find(resId);
485 if (cacheIter == mIdIndex.end()) {
486 mIdIndex.insert({ resId, name });
487 }
488
Adam Lesinski6ff19662015-04-30 17:40:46 -0700489 entry++;
490 }
491 return true;
492}
493
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800494bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700495 if (mTypePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800496 Logger::error(mSource)
497 << "no type string pool available for ResTable_typeSpec."
498 << std::endl;
499 return false;
500 }
501
502 const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
503 if (!typeSpec) {
504 Logger::error(mSource)
505 << "could not parse chunk as ResTable_typeSpec."
506 << std::endl;
507 return false;
508 }
509
510 if (typeSpec->id == 0) {
511 Logger::error(mSource)
512 << "ResTable_typeSpec has invalid id: "
513 << typeSpec->id
514 << "."
515 << std::endl;
516 return false;
517 }
518 return true;
519}
520
521bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700522 if (mTypePool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800523 Logger::error(mSource)
524 << "no type string pool available for ResTable_typeSpec."
525 << std::endl;
526 return false;
527 }
528
Adam Lesinskica2fc352015-04-03 12:08:26 -0700529 if (mKeyPool.getError() != NO_ERROR) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800530 Logger::error(mSource)
531 << "no key string pool available for ResTable_type."
532 << std::endl;
533 return false;
534 }
535
536 const ResTable_type* type = convertTo<ResTable_type>(chunk);
537 if (!type) {
538 Logger::error(mSource)
539 << "could not parse chunk as ResTable_type."
540 << std::endl;
541 return false;
542 }
543
544 if (type->id == 0) {
545 Logger::error(mSource)
546 << "ResTable_type has invalid id: "
547 << type->id
548 << "."
549 << std::endl;
550 return false;
551 }
552
553 const ConfigDescription config(type->config);
554 const StringPiece16 typeName = util::getString(mTypePool, type->id - 1);
555
556 const ResourceType* parsedType = parseResourceType(typeName);
557 if (!parsedType) {
558 Logger::error(mSource)
559 << "invalid type name '"
560 << typeName
561 << "' for type with ID "
562 << uint32_t(type->id)
563 << "." << std::endl;
564 return false;
565 }
566
567 android::TypeVariant tv(type);
568 for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
569 if (!*it) {
570 continue;
571 }
572
573 const ResTable_entry* entry = *it;
574 const ResourceName name = {
575 mTable->getPackage(),
576 *parsedType,
577 util::getString(mKeyPool, entry->key.index).toString()
578 };
579
580 const ResourceId resId = { mTable->getPackageId(), type->id, it.index() };
581
582 std::unique_ptr<Value> resourceValue;
583 const ResTable_entry_source* sourceBlock = nullptr;
584 if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
585 const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
586 if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
587 const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry);
588 data += mapEntry->size - sizeof(*sourceBlock);
589 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
590 }
591
592 // TODO(adamlesinski): Check that the entry count is valid.
593 resourceValue = parseMapEntry(name, config, mapEntry);
594 } else {
595 if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) {
596 const uint8_t* data = reinterpret_cast<const uint8_t*>(entry);
597 data += entry->size - sizeof(*sourceBlock);
598 sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
599 }
600
601 const Res_value* value = reinterpret_cast<const Res_value*>(
602 reinterpret_cast<const uint8_t*>(entry) + entry->size);
603 resourceValue = parseValue(name, config, value, entry->flags);
604 }
605
606 if (!resourceValue) {
607 // TODO(adamlesinski): For now this is ok, but it really shouldn't be.
608 continue;
609 }
610
611 SourceLine source = mSource.line(0);
612 if (sourceBlock) {
613 size_t len;
614 const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len);
615 if (str) {
616 source.path.assign(str, len);
617 }
618 source.line = sourceBlock->line;
619 }
620
Adam Lesinski330edcd2015-05-04 17:40:56 -0700621 if (!mTable->addResourceAllowMangled(name, config, source, std::move(resourceValue))) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800622 return false;
623 }
624
625 if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700626 if (!mTable->markPublicAllowMangled(name, resId, mSource.line(0))) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800627 return false;
628 }
629 }
630
631 // Add this resource name->id mapping to the index so
632 // that we can resolve all ID references to name references.
633 auto cacheIter = mIdIndex.find(resId);
634 if (cacheIter == mIdIndex.end()) {
635 mIdIndex.insert({ resId, name });
636 }
637 }
638 return true;
639}
640
641std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
642 const ConfigDescription& config,
643 const Res_value* value,
644 uint16_t flags) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700645 if (name.type == ResourceType::kId) {
646 return util::make_unique<Id>();
647 }
648
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800649 if (value->dataType == Res_value::TYPE_STRING) {
650 StringPiece16 str = util::getString(mValuePool, value->data);
651
652 const ResStringPool_span* spans = mValuePool.styleAt(value->data);
653 if (spans != nullptr) {
654 StyleString styleStr = { str.toString() };
655 while (spans->name.index != ResStringPool_span::END) {
656 styleStr.spans.push_back(Span{
657 util::getString(mValuePool, spans->name.index).toString(),
658 spans->firstChar,
659 spans->lastChar
660 });
661 spans++;
662 }
663 return util::make_unique<StyledString>(
664 mTable->getValueStringPool().makeRef(
665 styleStr, StringPool::Context{1, config}));
666 } else {
Adam Lesinskid5c4f872015-04-21 13:56:10 -0700667 if (name.type != ResourceType::kString &&
668 util::stringStartsWith<char16_t>(str, u"res/")) {
669 // This must be a FileReference.
670 return util::make_unique<FileReference>(mTable->getValueStringPool().makeRef(
671 str, StringPool::Context{ 0, config }));
672 }
673
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800674 // There are no styles associated with this string, so treat it as
675 // a simple string.
676 return util::make_unique<String>(
677 mTable->getValueStringPool().makeRef(
678 str, StringPool::Context{1, config}));
679 }
680 }
681
682 if (value->dataType == Res_value::TYPE_REFERENCE ||
683 value->dataType == Res_value::TYPE_ATTRIBUTE) {
684 const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
685 Reference::Type::kResource : Reference::Type::kAttribute;
686
687 if (value->data != 0) {
688 // This is a normal reference.
689 return util::make_unique<Reference>(value->data, type);
690 }
691
692 // This reference has an invalid ID. Check if it is an unresolved symbol.
693 ResourceNameRef symbol;
694 if (getSymbol(&value->data, &symbol)) {
695 return util::make_unique<Reference>(symbol, type);
696 }
697
698 // This is not an unresolved symbol, so it must be the magic @null reference.
699 Res_value nullType = {};
Adam Lesinskidfa5e072015-05-12 21:42:59 -0700700 nullType.dataType = Res_value::TYPE_REFERENCE;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800701 return util::make_unique<BinaryPrimitive>(nullType);
702 }
703
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800704 if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
705 return util::make_unique<RawString>(
706 mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data),
707 StringPool::Context{ 1, config }));
708 }
709
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800710 // Treat this as a raw binary primitive.
711 return util::make_unique<BinaryPrimitive>(*value);
712}
713
714std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
715 const ConfigDescription& config,
716 const ResTable_map_entry* map) {
717 switch (name.type) {
718 case ResourceType::kStyle:
719 return parseStyle(name, config, map);
720 case ResourceType::kAttr:
721 return parseAttr(name, config, map);
722 case ResourceType::kArray:
723 return parseArray(name, config, map);
724 case ResourceType::kStyleable:
725 return parseStyleable(name, config, map);
726 case ResourceType::kPlurals:
727 return parsePlural(name, config, map);
728 default:
729 break;
730 }
731 return {};
732}
733
734std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
735 const ConfigDescription& config,
736 const ResTable_map_entry* map) {
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700737 std::unique_ptr<Style> style = util::make_unique<Style>();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800738 if (map->parent.ident == 0) {
739 // The parent is either not set or it is an unresolved symbol.
740 // Check to see if it is a symbol.
741 ResourceNameRef symbol;
742 if (getSymbol(&map->parent.ident, &symbol)) {
743 style->parent.name = symbol.toResourceName();
744 }
745 } else {
746 // The parent is a regular reference to a resource.
747 style->parent.id = map->parent.ident;
748 }
749
750 for (const ResTable_map& mapEntry : map) {
751 style->entries.emplace_back();
752 Style::Entry& styleEntry = style->entries.back();
753
754 if (mapEntry.name.ident == 0) {
755 // The map entry's key (attribute) is not set. This must be
756 // a symbol reference, so resolve it.
757 ResourceNameRef symbol;
758 bool result = getSymbol(&mapEntry.name.ident, &symbol);
759 assert(result);
760 styleEntry.key.name = symbol.toResourceName();
761 } else {
762 // The map entry's key (attribute) is a regular reference.
763 styleEntry.key.id = mapEntry.name.ident;
764 }
765
766 // Parse the attribute's value.
767 styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
768 assert(styleEntry.value);
769 }
770 return style;
771}
772
773std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
774 const ConfigDescription& config,
775 const ResTable_map_entry* map) {
776 const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
777 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
778
779 // First we must discover what type of attribute this is. Find the type mask.
780 auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
781 return entry.name.ident == ResTable_map::ATTR_TYPE;
782 });
783
784 if (typeMaskIter != end(map)) {
785 attr->typeMask = typeMaskIter->value.data;
786 }
787
788 if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
789 for (const ResTable_map& mapEntry : map) {
790 if (Res_INTERNALID(mapEntry.name.ident)) {
791 continue;
792 }
793
Adam Lesinski330edcd2015-05-04 17:40:56 -0700794 Attribute::Symbol symbol;
795 symbol.value = mapEntry.value.data;
796 if (mapEntry.name.ident == 0) {
797 // The map entry's key (id) is not set. This must be
798 // a symbol reference, so resolve it.
799 ResourceNameRef symbolName;
800 bool result = getSymbol(&mapEntry.name.ident, &symbolName);
801 assert(result);
802 symbol.symbol.name = symbolName.toResourceName();
803 } else {
804 // The map entry's key (id) is a regular reference.
805 symbol.symbol.id = mapEntry.name.ident;
806 }
807
808 attr->symbols.push_back(std::move(symbol));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800809 }
810 }
811
812 // TODO(adamlesinski): Find min, max, i80n, etc attributes.
813 return attr;
814}
815
816std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
817 const ConfigDescription& config,
818 const ResTable_map_entry* map) {
819 std::unique_ptr<Array> array = util::make_unique<Array>();
820 for (const ResTable_map& mapEntry : map) {
821 array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
822 }
823 return array;
824}
825
826std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
827 const ConfigDescription& config,
828 const ResTable_map_entry* map) {
829 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
830 for (const ResTable_map& mapEntry : map) {
Adam Lesinski769de982015-04-10 19:43:55 -0700831 if (mapEntry.name.ident == 0) {
832 // The map entry's key (attribute) is not set. This must be
833 // a symbol reference, so resolve it.
834 ResourceNameRef symbol;
835 bool result = getSymbol(&mapEntry.name.ident, &symbol);
836 assert(result);
837 styleable->entries.emplace_back(symbol);
838 } else {
839 // The map entry's key (attribute) is a regular reference.
840 styleable->entries.emplace_back(mapEntry.name.ident);
841 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800842 }
843 return styleable;
844}
845
846std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
847 const ConfigDescription& config,
848 const ResTable_map_entry* map) {
849 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
850 for (const ResTable_map& mapEntry : map) {
851 std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
852
853 switch (mapEntry.name.ident) {
854 case android::ResTable_map::ATTR_ZERO:
855 plural->values[Plural::Zero] = std::move(item);
856 break;
857 case android::ResTable_map::ATTR_ONE:
858 plural->values[Plural::One] = std::move(item);
859 break;
860 case android::ResTable_map::ATTR_TWO:
861 plural->values[Plural::Two] = std::move(item);
862 break;
863 case android::ResTable_map::ATTR_FEW:
864 plural->values[Plural::Few] = std::move(item);
865 break;
866 case android::ResTable_map::ATTR_MANY:
867 plural->values[Plural::Many] = std::move(item);
868 break;
869 case android::ResTable_map::ATTR_OTHER:
870 plural->values[Plural::Other] = std::move(item);
871 break;
872 }
873 }
874 return plural;
875}
876
877} // namespace aapt