1/*
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 "Linker.h"
18#include "Logger.h"
19#include "NameMangler.h"
20#include "Resolver.h"
21#include "ResourceParser.h"
22#include "ResourceTable.h"
23#include "ResourceValues.h"
24#include "StringPiece.h"
25#include "Util.h"
26
27#include <androidfw/AssetManager.h>
28#include <array>
29#include <bitset>
30#include <iostream>
31#include <map>
32#include <ostream>
33#include <set>
34#include <sstream>
35#include <tuple>
36#include <vector>
37
38namespace aapt {
39
40Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
41}
42
43Linker::Linker(const std::shared_ptr<ResourceTable>& table,
44               const std::shared_ptr<IResolver>& resolver, const Options& options) :
45        mResolver(resolver), mTable(table), mOptions(options), mError(false) {
46}
47
48bool Linker::linkAndValidate() {
49    std::bitset<256> usedTypeIds;
50    std::array<std::set<uint16_t>, 256> usedIds;
51    usedTypeIds.set(0);
52
53    // Collect which resource IDs are already taken.
54    for (auto& type : *mTable) {
55        if (type->typeId != ResourceTableType::kUnsetTypeId) {
56            // The ID for this type has already been set. We
57            // mark this ID as taken so we don't re-assign it
58            // later.
59            usedTypeIds.set(type->typeId);
60        }
61
62        for (auto& entry : type->entries) {
63            if (type->typeId != ResourceTableType::kUnsetTypeId &&
64                    entry->entryId != ResourceEntry::kUnsetEntryId) {
65                // The ID for this entry has already been set. We
66                // mark this ID as taken so we don't re-assign it
67                // later.
68                usedIds[type->typeId].insert(entry->entryId);
69            }
70        }
71    }
72
73    // Assign resource IDs that are available.
74    size_t nextTypeIndex = 0;
75    for (auto& type : *mTable) {
76        if (type->typeId == ResourceTableType::kUnsetTypeId) {
77            while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) {
78                nextTypeIndex++;
79            }
80            type->typeId = nextTypeIndex++;
81        }
82
83        const auto endEntryIter = std::end(usedIds[type->typeId]);
84        auto nextEntryIter = std::begin(usedIds[type->typeId]);
85        size_t nextIndex = 0;
86        for (auto& entry : type->entries) {
87            if (entry->entryId == ResourceTableType::kUnsetTypeId) {
88                while (nextEntryIter != endEntryIter &&
89                        nextIndex == *nextEntryIter) {
90                    nextIndex++;
91                    ++nextEntryIter;
92                }
93                entry->entryId = nextIndex++;
94            }
95        }
96    }
97
98    // Now do reference linking.
99    for (auto& type : *mTable) {
100        for (auto& entry : type->entries) {
101            if (entry->publicStatus.isPublic && entry->values.empty()) {
102                // A public resource has no values. It will not be encoded
103                // properly without a symbol table. This is a unresolved symbol.
104                addUnresolvedSymbol(ResourceNameRef{
105                        mTable->getPackage(), type->type, entry->name },
106                        entry->publicStatus.source);
107                continue;
108            }
109
110            for (auto& valueConfig : entry->values) {
111                // Dispatch to the right method of this linker
112                // based on the value's type.
113                valueConfig.value->accept(*this, Args{
114                        ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
115                        valueConfig.source
116                });
117            }
118        }
119    }
120    return !mError;
121}
122
123const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
124    return mUnresolvedSymbols;
125}
126
127void Linker::doResolveReference(Reference& reference, const SourceLine& source) {
128    Maybe<ResourceId> result = mResolver->findId(reference.name);
129    if (!result) {
130        addUnresolvedSymbol(reference.name, source);
131        return;
132    }
133    assert(result.value().isValid());
134
135    if (mOptions.linkResourceIds) {
136        reference.id = result.value();
137    } else {
138        reference.id = 0;
139    }
140}
141
142const Attribute* Linker::doResolveAttribute(Reference& attribute, const SourceLine& source) {
143    Maybe<IResolver::Entry> result = mResolver->findAttribute(attribute.name);
144    if (!result || !result.value().attr) {
145        addUnresolvedSymbol(attribute.name, source);
146        return nullptr;
147    }
148
149    const IResolver::Entry& entry = result.value();
150    assert(entry.id.isValid());
151
152    if (mOptions.linkResourceIds) {
153        attribute.id = entry.id;
154    } else {
155        attribute.id = 0;
156    }
157    return entry.attr;
158}
159
160void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
161    Args& args = static_cast<Args&>(a);
162
163    if (reference.name.entry.empty()) {
164        // We can't have a completely bad reference.
165        if (!reference.id.isValid()) {
166            Logger::error() << "srsly? " << args.referrer << std::endl;
167            assert(reference.id.isValid());
168        }
169
170        // This reference has no name but has an ID.
171        // It is a really bad error to have no name and have the same
172        // package ID.
173        assert(reference.id.packageId() != mTable->getPackageId());
174
175        // The reference goes outside this package, let it stay as a
176        // resource ID because it will not change.
177        return;
178    }
179
180    doResolveReference(reference, args.source);
181
182    // TODO(adamlesinski): Verify the referencedType is another reference
183    // or a compatible primitive.
184}
185
186void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
187        const Attribute& attr, std::unique_ptr<Item>& value) {
188    std::unique_ptr<Item> convertedValue;
189    visitFunc<RawString>(*value, [&](RawString& str) {
190        // This is a raw string, so check if it can be converted to anything.
191        // We can NOT swap value with the converted value in here, since
192        // we called through the original value.
193
194        auto onCreateReference = [&](const ResourceName& name) {
195            // We should never get here. All references would have been
196            // parsed in the parser phase.
197            assert(false);
198        };
199
200        convertedValue = ResourceParser::parseItemForAttribute(*str.value, attr,
201                                                               onCreateReference);
202        if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
203            // Last effort is to parse as a string.
204            util::StringBuilder builder;
205            builder.append(*str.value);
206            if (builder) {
207                convertedValue = util::make_unique<String>(
208                        mTable->getValueStringPool().makeRef(builder.str()));
209            }
210        }
211    });
212
213    if (convertedValue) {
214        value = std::move(convertedValue);
215    }
216
217    // Process this new or old value (it can be a reference!).
218    value->accept(*this, Args{ name, source });
219
220    // Flatten the value to see what resource type it is.
221    android::Res_value resValue;
222    value->flatten(resValue);
223
224    // Always allow references.
225    const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
226    if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
227        Logger::error(source)
228                << *value
229                << " is not compatible with attribute "
230                << attr
231                << "."
232                << std::endl;
233        mError = true;
234    }
235}
236
237void Linker::visit(Style& style, ValueVisitorArgs& a) {
238    Args& args = static_cast<Args&>(a);
239
240    if (style.parent.name.isValid() || style.parent.id.isValid()) {
241        visit(style.parent, a);
242    }
243
244    for (Style::Entry& styleEntry : style.entries) {
245        const Attribute* attr = doResolveAttribute(styleEntry.key, args.source);
246        if (attr) {
247            processAttributeValue(args.referrer, args.source, *attr, styleEntry.value);
248        }
249    }
250}
251
252void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
253    static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
254            android::ResTable_map::TYPE_FLAGS;
255    if (attr.typeMask & kMask) {
256        for (auto& symbol : attr.symbols) {
257            visit(symbol.symbol, a);
258        }
259    }
260}
261
262void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
263    for (auto& attrRef : styleable.entries) {
264        visit(attrRef, a);
265    }
266}
267
268void Linker::visit(Array& array, ValueVisitorArgs& a) {
269    Args& args = static_cast<Args&>(a);
270
271    for (auto& item : array.items) {
272        item->accept(*this, Args{ args.referrer, args.source });
273    }
274}
275
276void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
277    Args& args = static_cast<Args&>(a);
278
279    for (auto& item : plural.values) {
280        if (item) {
281            item->accept(*this, Args{ args.referrer, args.source });
282        }
283    }
284}
285
286void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
287    mUnresolvedSymbols[name.toResourceName()].push_back(source);
288}
289
290} // namespace aapt
291