blob: 3f64d7b8c9112aa6c392ec1c05a90c328aa22f16 [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 Lesinskie78fd612015-10-22 12:48:43 -0700136 DiagMessage msg(style->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 Lesinskie78fd612015-10-22 12:48:43 -0700146 DiagMessage msg(style->getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147 msg << "style attribute '";
148 if (entry.key.name) {
149 msg << entry.key.name.value().package << ":" << entry.key.name.value().entry;
150 } else {
151 msg << entry.key.id.value();
152 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800153 msg << "' " << errStr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700154 mContext->getDiagnostics()->error(msg);
Adam Lesinski467f1712015-11-16 17:35:44 -0800155 mContext->getDiagnostics()->note(DiagMessage(style->getSource()) << entry.key);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700156 mError = true;
157 }
158 }
159 }
160
Adam Lesinski467f1712015-11-16 17:35:44 -0800161 bool hasError() {
162 return mError;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700163 }
164};
165
Adam Lesinski467f1712015-11-16 17:35:44 -0800166} // namespace
167
168/**
169 * The symbol is visible if it is public, or if the reference to it is requesting private access
170 * or if the callsite comes from the same package.
171 */
172bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
173 const CallSite& callSite) {
174 if (!symbol.isPublic && !ref.privateReference) {
175 if (ref.name) {
176 return callSite.resource.package == ref.name.value().package;
177 } else if (ref.id) {
178 return ref.id.value().packageId() == symbol.id.packageId();
179 } else {
180 return false;
181 }
182 }
183 return true;
184}
185
186const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
187 NameMangler* mangler,
188 ISymbolTable* symbols) {
189 if (reference.name) {
190 Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
191 return symbols->findByName(mangled ? mangled.value() : reference.name.value());
192 } else if (reference.id) {
193 return symbols->findById(reference.id.value());
194 } else {
195 return nullptr;
196 }
197}
198
199const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
200 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
201 CallSite* callSite, std::string* outError) {
202 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
203 if (!symbol) {
204 std::stringstream errStr;
205 errStr << "not found";
206 if (outError) *outError = errStr.str();
207 return nullptr;
208 }
209
210 if (!isSymbolVisible(*symbol, reference, *callSite)) {
211 std::stringstream errStr;
212 errStr << "is private";
213 if (outError) *outError = errStr.str();
214 return nullptr;
215 }
216 return symbol;
217}
218
219const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
220 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
221 CallSite* callSite, std::string* outError) {
222 const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
223 symbols, callSite,
224 outError);
225 if (!symbol) {
226 return nullptr;
227 }
228
229 if (!symbol->attribute) {
230 std::stringstream errStr;
231 errStr << "is not an attribute";
232 if (outError) *outError = errStr.str();
233 return nullptr;
234 }
235 return symbol;
236}
237
238Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
239 NameMangler* nameMangler,
240 ISymbolTable* symbols,
241 CallSite* callSite,
242 std::string* outError) {
243 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
244 if (!symbol) {
245 return {};
246 }
247
248 if (!symbol->attribute) {
249 std::stringstream errStr;
250 errStr << "is not an attribute";
251 if (outError) *outError = errStr.str();
252 return {};
253 }
254 return xml::AaptAttribute{ symbol->id, *symbol->attribute };
255}
256
257bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
258 ISymbolTable* symbols, xml::IPackageDeclStack* decls,
259 CallSite* callSite) {
260 assert(reference);
261 assert(reference->name || reference->id);
262
263 Reference transformedReference = *reference;
264 transformReferenceFromNamespace(decls, context->getCompilationPackage(),
265 &transformedReference);
266
267 std::string errStr;
268 const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
269 transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
270 if (s) {
271 reference->id = s->id;
272 return true;
273 }
274
275 DiagMessage errorMsg(reference->getSource());
276 errorMsg << "resource ";
277 if (reference->name) {
278 errorMsg << reference->name.value();
279 if (transformedReference.name.value() != reference->name.value()) {
280 errorMsg << " (aka " << transformedReference.name.value() << ")";
281 }
282 } else {
283 errorMsg << reference->id.value();
284 }
285
286 errorMsg << " " << errStr;
287 context->getDiagnostics()->error(errorMsg);
288 return false;
289}
290
291namespace {
292
293struct EmptyDeclStack : public xml::IPackageDeclStack {
294 Maybe<xml::ExtractedPackage> transformPackageAlias(
295 const StringPiece16& alias, const StringPiece16& localPackage) const override {
296 if (alias.empty()) {
297 return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700298 }
299 return {};
300 }
301};
302
303} // namespace
304
305bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
306 EmptyDeclStack declStack;
307 bool error = false;
308 for (auto& package : table->packages) {
309 for (auto& type : package->types) {
310 for (auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700311 // Symbol state information may be lost if there is no value for the resource.
312 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
313 context->getDiagnostics()->error(
314 DiagMessage(entry->symbolStatus.source)
315 << "no definition for declared symbol '"
316 << ResourceNameRef(package->name, type->type, entry->name)
317 << "'");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700318 error = true;
319 }
320
Adam Lesinski467f1712015-11-16 17:35:44 -0800321 CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
322 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
323 &table->stringPool, &declStack, &callSite);
324
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700325 for (auto& configValue : entry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700326 configValue.value->accept(&visitor);
Adam Lesinski467f1712015-11-16 17:35:44 -0800327 }
328
329 if (visitor.hasError()) {
330 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700331 }
332 }
333 }
334 }
335 return !error;
336}
337
338} // namespace aapt