SymbolTable.cpp revision 467f171315f9c2037fcd3eb5edcfabc40671bf7b
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 "ConfigDescription.h"
18#include "Resource.h"
19#include "ValueVisitor.h"
20
21#include "process/SymbolTable.h"
22#include "util/Comparators.h"
23#include "util/Util.h"
24
25#include <androidfw/AssetManager.h>
26#include <androidfw/ResourceTypes.h>
27
28namespace aapt {
29
30const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& name) {
31    if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
32        return s.get();
33    }
34
35    Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
36    if (!result) {
37        if (name.type == ResourceType::kAttr) {
38            // Recurse and try looking up a private attribute.
39            return findByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
40        }
41        return {};
42    }
43
44    ResourceTable::SearchResult sr = result.value();
45
46    // If no ID exists, we treat the symbol as missing. SymbolTables are used to
47    // find symbols to link.
48    if (!sr.package->id || !sr.type->id || !sr.entry->id) {
49        return {};
50    }
51
52    std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
53    symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
54    symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
55
56    if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
57        const ConfigDescription kDefaultConfig;
58        auto iter = std::lower_bound(sr.entry->values.begin(), sr.entry->values.end(),
59                                     kDefaultConfig, cmp::lessThanConfig);
60
61        if (iter != sr.entry->values.end() && iter->config == kDefaultConfig) {
62            // This resource has an Attribute.
63            if (Attribute* attr = valueCast<Attribute>(iter->value.get())) {
64                symbol->attribute = std::unique_ptr<Attribute>(attr->clone(nullptr));
65            } else {
66                return {};
67            }
68        }
69    }
70
71    if (name.type == ResourceType::kAttrPrivate) {
72        // Masquerade this entry as kAttr.
73        mCache.put(ResourceName(name.package, ResourceType::kAttr, name.entry), symbol);
74    } else {
75        mCache.put(name, symbol);
76    }
77    return symbol.get();
78}
79
80
81static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
82                                                                    ResourceId id) {
83    // Try as a bag.
84    const android::ResTable::bag_entry* entry;
85    ssize_t count = table.lockBag(id.id, &entry);
86    if (count < 0) {
87        table.unlockBag(entry);
88        return nullptr;
89    }
90
91    // We found a resource.
92    std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
93    s->id = id;
94
95    // Check to see if it is an attribute.
96    for (size_t i = 0; i < (size_t) count; i++) {
97        if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
98            s->attribute = util::make_unique<Attribute>(false);
99            s->attribute->typeMask = entry[i].map.value.data;
100            break;
101        }
102    }
103
104    if (s->attribute) {
105        for (size_t i = 0; i < (size_t) count; i++) {
106            if (!Res_INTERNALID(entry[i].map.name.ident)) {
107                android::ResTable::resource_name entryName;
108                if (!table.getResourceName(entry[i].map.name.ident, false, &entryName)) {
109                    table.unlockBag(entry);
110                    return nullptr;
111                }
112
113                const ResourceType* parsedType = parseResourceType(
114                        StringPiece16(entryName.type, entryName.typeLen));
115                if (!parsedType) {
116                    table.unlockBag(entry);
117                    return nullptr;
118                }
119
120                Attribute::Symbol symbol;
121                symbol.symbol.name = ResourceNameRef(
122                        StringPiece16(entryName.package, entryName.packageLen),
123                        *parsedType,
124                        StringPiece16(entryName.name, entryName.nameLen)).toResourceName();
125                symbol.symbol.id = ResourceId(entry[i].map.name.ident);
126                symbol.value = entry[i].map.value.data;
127                s->attribute->symbols.push_back(std::move(symbol));
128            }
129        }
130    }
131    table.unlockBag(entry);
132    return s;
133}
134
135const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName(
136        const ResourceName& name) {
137    if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
138        return s.get();
139    }
140
141    for (const auto& asset : mAssets) {
142        const android::ResTable& table = asset->getResources(false);
143        StringPiece16 typeStr = toString(name.type);
144        uint32_t typeSpecFlags = 0;
145        ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
146                                                   typeStr.data(), typeStr.size(),
147                                                   name.package.data(), name.package.size(),
148                                                   &typeSpecFlags);
149        if (!resId.isValid()) {
150            continue;
151        }
152
153        std::shared_ptr<Symbol> s;
154        if (name.type == ResourceType::kAttr) {
155            s = lookupAttributeInTable(table, resId);
156        } else {
157            s = std::make_shared<Symbol>();
158            s->id = resId;
159        }
160
161        if (s) {
162            s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
163            mCache.put(name, s);
164            return s.get();
165        }
166    }
167    return nullptr;
168}
169
170static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
171    android::ResTable::resource_name resName;
172    if (!table.getResourceName(id.id, true, &resName)) {
173        return {};
174    }
175
176    ResourceName name;
177    if (resName.package) {
178        name.package = StringPiece16(resName.package, resName.packageLen).toString();
179    }
180
181    const ResourceType* type;
182    if (resName.type) {
183        type = parseResourceType(StringPiece16(resName.type, resName.typeLen));
184
185    } else if (resName.type8) {
186        type = parseResourceType(util::utf8ToUtf16(StringPiece(resName.type8, resName.typeLen)));
187    } else {
188        return {};
189    }
190
191    if (!type) {
192        return {};
193    }
194
195    name.type = *type;
196
197    if (resName.name) {
198        name.entry = StringPiece16(resName.name, resName.nameLen).toString();
199    } else if (resName.name8) {
200        name.entry = util::utf8ToUtf16(StringPiece(resName.name8, resName.nameLen));
201    } else {
202        return {};
203    }
204
205    return name;
206}
207
208const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
209        ResourceId id) {
210    if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
211        return s.get();
212    }
213
214    for (const auto& asset : mAssets) {
215        const android::ResTable& table = asset->getResources(false);
216
217        Maybe<ResourceName> maybeName = getResourceName(table, id);
218        if (!maybeName) {
219            continue;
220        }
221
222        uint32_t typeSpecFlags = 0;
223        table.getResourceFlags(id.id, &typeSpecFlags);
224
225        std::shared_ptr<Symbol> s;
226        if (maybeName.value().type == ResourceType::kAttr) {
227            s = lookupAttributeInTable(table, id);
228        } else {
229            s = std::make_shared<Symbol>();
230            s->id = id;
231        }
232
233        if (s) {
234            s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
235            mIdCache.put(id, s);
236            return s.get();
237        }
238    }
239    return nullptr;
240}
241
242const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName(
243        const ResourceName& name) {
244    for (auto& symbolTable : mSymbolTables) {
245        if (const Symbol* s = symbolTable->findByName(name)) {
246            return s;
247        }
248    }
249    return {};
250}
251
252const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findById(ResourceId id) {
253    for (auto& symbolTable : mSymbolTables) {
254        if (const Symbol* s = symbolTable->findById(id)) {
255            return s;
256        }
257    }
258    return {};
259}
260
261} // namespace aapt
262