blob: ef3fe4f58d413f11a60e0ddb4f268ec5a2eba98d [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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
Adam Lesinski467f1712015-11-16 17:35:44 -080017#include "ReferenceLinker.h"
18
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#include "Diagnostics.h"
20#include "ResourceTable.h"
21#include "ResourceUtils.h"
22#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "link/Linkers.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070025#include "process/IResourceTableConsumer.h"
26#include "process/SymbolTable.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080027#include "util/Util.h"
28#include "xml/XmlUtil.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070029
30#include <androidfw/ResourceTypes.h>
31#include <cassert>
32
33namespace aapt {
34
35namespace {
36
37/**
38 * The ReferenceLinkerVisitor will follow all references and make sure they point
39 * to resources that actually exist, either in the local resource table, or as external
40 * symbols. Once the target resource has been found, the ID of the resource will be assigned
41 * to the reference object.
42 *
43 * NOTE: All of the entries in the ResourceTable must be assigned IDs.
44 */
Adam Lesinski467f1712015-11-16 17:35:44 -080045class ReferenceLinkerVisitor : public ValueVisitor {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046private:
Adam Lesinski1ab598f2015-08-14 14:26:04 -070047 IAaptContext* mContext;
48 ISymbolTable* mSymbols;
Adam Lesinski467f1712015-11-16 17:35:44 -080049 xml::IPackageDeclStack* mPackageDecls;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050 StringPool* mStringPool;
Adam Lesinski467f1712015-11-16 17:35:44 -080051 CallSite* mCallSite;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070052 bool mError = false;
53
Adam Lesinski1ab598f2015-08-14 14:26:04 -070054 /**
55 * Transform a RawString value into a more specific, appropriate value, based on the
56 * Attribute. If a non RawString value is passed in, this is an identity transform.
57 */
58 std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
59 const Attribute* attr) {
60 if (RawString* rawString = valueCast<RawString>(value.get())) {
Adam Lesinski467f1712015-11-16 17:35:44 -080061 std::unique_ptr<Item> transformed =
62 ResourceUtils::parseItemForAttribute(*rawString->value, attr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -070063
64 // If we could not parse as any specific type, try a basic STRING.
65 if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
66 util::StringBuilder stringBuilder;
67 stringBuilder.append(*rawString->value);
68 if (stringBuilder) {
69 transformed = util::make_unique<String>(
70 mStringPool->makeRef(stringBuilder.str()));
71 }
72 }
73
74 if (transformed) {
75 return transformed;
76 }
77 };
78 return value;
79 }
80
Adam Lesinski1ab598f2015-08-14 14:26:04 -070081public:
82 using ValueVisitor::visit;
83
Adam Lesinski467f1712015-11-16 17:35:44 -080084 ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
85 xml::IPackageDeclStack* decl,CallSite* callSite) :
86 mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
87 mCallSite(callSite) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070088 }
89
Adam Lesinski467f1712015-11-16 17:35:44 -080090 void visit(Reference* ref) override {
91 if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
92 mError = true;
93 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070094 }
95
96 /**
97 * We visit the Style specially because during this phase, values of attributes are
98 * all RawString values. Now that we are expected to resolve all symbols, we can
99 * lookup the attributes to find out which types are allowed for the attributes' values.
100 */
101 void visit(Style* style) override {
102 if (style->parent) {
103 visit(&style->parent.value());
104 }
105
106 for (Style::Entry& entry : style->entries) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800107 std::string errStr;
108
109 // Transform the attribute reference so that it is using the fully qualified package
110 // name. This will also mark the reference as being able to see private resources if
111 // there was a '*' in the reference or if the package came from the private namespace.
112 Reference transformedReference = entry.key;
113 transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
114 &transformedReference);
115
116 // Find the attribute in the symbol table and check if it is visible from this callsite.
117 const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
118 transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
119 if (symbol) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700120 // Assign our style key the correct ID.
Adam Lesinski467f1712015-11-16 17:35:44 -0800121 entry.key.id = symbol->id;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700122
123 // Try to convert the value to a more specific, typed value based on the
124 // attribute it is set to.
Adam Lesinski467f1712015-11-16 17:35:44 -0800125 entry.value = parseValueWithAttribute(std::move(entry.value),
126 symbol->attribute.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700127
128 // Link/resolve the final value (mostly if it's a reference).
129 entry.value->accept(this);
130
131 // Now verify that the type of this item is compatible with the attribute it
Adam Lesinskia5870652015-11-20 15:32:30 -0800132 // is defined for. We pass `nullptr` as the DiagMessage so that this check is
133 // fast and we avoid creating a DiagMessage when the match is successful.
134 if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700135 // The actual type of this item is incompatible with the attribute.
Adam Lesinski28cacf02015-11-23 14:22:47 -0800136 DiagMessage msg(entry.key.getSource());
Adam Lesinskia5870652015-11-20 15:32:30 -0800137
138 // Call the matches method again, this time with a DiagMessage so we fill
139 // in the actual error message.
140 symbol->attribute->matches(entry.value.get(), &msg);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700141 mContext->getDiagnostics()->error(msg);
142 mError = true;
143 }
Adam Lesinskia5870652015-11-20 15:32:30 -0800144
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145 } else {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800146 DiagMessage msg(entry.key.getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147 msg << "style attribute '";
Adam Lesinski28cacf02015-11-23 14:22:47 -0800148 ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
Adam Lesinski467f1712015-11-16 17:35:44 -0800149 msg << "' " << errStr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700150 mContext->getDiagnostics()->error(msg);
151 mError = true;
152 }
153 }
154 }
155
Adam Lesinski467f1712015-11-16 17:35:44 -0800156 bool hasError() {
157 return mError;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700158 }
159};
160
Adam Lesinski467f1712015-11-16 17:35:44 -0800161} // namespace
162
163/**
164 * The symbol is visible if it is public, or if the reference to it is requesting private access
165 * or if the callsite comes from the same package.
166 */
167bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
168 const CallSite& callSite) {
169 if (!symbol.isPublic && !ref.privateReference) {
170 if (ref.name) {
171 return callSite.resource.package == ref.name.value().package;
172 } else if (ref.id) {
173 return ref.id.value().packageId() == symbol.id.packageId();
174 } else {
175 return false;
176 }
177 }
178 return true;
179}
180
181const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
182 NameMangler* mangler,
183 ISymbolTable* symbols) {
184 if (reference.name) {
185 Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
186 return symbols->findByName(mangled ? mangled.value() : reference.name.value());
187 } else if (reference.id) {
188 return symbols->findById(reference.id.value());
189 } else {
190 return nullptr;
191 }
192}
193
194const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
195 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
196 CallSite* callSite, std::string* outError) {
197 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
198 if (!symbol) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800199 if (outError) *outError = "not found";
Adam Lesinski467f1712015-11-16 17:35:44 -0800200 return nullptr;
201 }
202
203 if (!isSymbolVisible(*symbol, reference, *callSite)) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800204 if (outError) *outError = "is private";
Adam Lesinski467f1712015-11-16 17:35:44 -0800205 return nullptr;
206 }
207 return symbol;
208}
209
210const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
211 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
212 CallSite* callSite, std::string* outError) {
213 const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
214 symbols, callSite,
215 outError);
216 if (!symbol) {
217 return nullptr;
218 }
219
220 if (!symbol->attribute) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800221 if (outError) *outError = "is not an attribute";
Adam Lesinski467f1712015-11-16 17:35:44 -0800222 return nullptr;
223 }
224 return symbol;
225}
226
227Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
228 NameMangler* nameMangler,
229 ISymbolTable* symbols,
230 CallSite* callSite,
231 std::string* outError) {
232 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
233 if (!symbol) {
234 return {};
235 }
236
237 if (!symbol->attribute) {
Adam Lesinski28cacf02015-11-23 14:22:47 -0800238 if (outError) *outError = "is not an attribute";
Adam Lesinski467f1712015-11-16 17:35:44 -0800239 return {};
240 }
241 return xml::AaptAttribute{ symbol->id, *symbol->attribute };
242}
243
Adam Lesinski28cacf02015-11-23 14:22:47 -0800244void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
245 const Reference& transformed) {
246 assert(outMsg);
247
248 if (orig.name) {
249 *outMsg << orig.name.value();
250 if (transformed.name.value() != orig.name.value()) {
251 *outMsg << " (aka " << transformed.name.value() << ")";
252 }
253 } else {
254 *outMsg << orig.id.value();
255 }
256}
257
Adam Lesinski467f1712015-11-16 17:35:44 -0800258bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
259 ISymbolTable* symbols, xml::IPackageDeclStack* decls,
260 CallSite* callSite) {
261 assert(reference);
262 assert(reference->name || reference->id);
263
264 Reference transformedReference = *reference;
265 transformReferenceFromNamespace(decls, context->getCompilationPackage(),
266 &transformedReference);
267
268 std::string errStr;
269 const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
270 transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
271 if (s) {
272 reference->id = s->id;
273 return true;
274 }
275
276 DiagMessage errorMsg(reference->getSource());
277 errorMsg << "resource ";
Adam Lesinski28cacf02015-11-23 14:22:47 -0800278 writeResourceName(&errorMsg, *reference, transformedReference);
Adam Lesinski467f1712015-11-16 17:35:44 -0800279 errorMsg << " " << errStr;
280 context->getDiagnostics()->error(errorMsg);
281 return false;
282}
283
284namespace {
285
286struct EmptyDeclStack : public xml::IPackageDeclStack {
287 Maybe<xml::ExtractedPackage> transformPackageAlias(
288 const StringPiece16& alias, const StringPiece16& localPackage) const override {
289 if (alias.empty()) {
290 return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700291 }
292 return {};
293 }
294};
295
296} // namespace
297
298bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
299 EmptyDeclStack declStack;
300 bool error = false;
301 for (auto& package : table->packages) {
302 for (auto& type : package->types) {
303 for (auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700304 // Symbol state information may be lost if there is no value for the resource.
305 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
306 context->getDiagnostics()->error(
307 DiagMessage(entry->symbolStatus.source)
308 << "no definition for declared symbol '"
309 << ResourceNameRef(package->name, type->type, entry->name)
310 << "'");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 error = true;
312 }
313
Adam Lesinski467f1712015-11-16 17:35:44 -0800314 CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
315 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
316 &table->stringPool, &declStack, &callSite);
317
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700318 for (auto& configValue : entry->values) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800319 configValue->value->accept(&visitor);
Adam Lesinski467f1712015-11-16 17:35:44 -0800320 }
321
322 if (visitor.hasError()) {
323 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700324 }
325 }
326 }
327 }
328 return !error;
329}
330
331} // namespace aapt