blob: 7fe09565816a11afc8b0c9dc96a31cd85fd00387 [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 Lesinski64587af2016-02-18 18:33:06 -080017#include "ReferenceLinker.h"
Adam Lesinskicacb28f2016-10-19 12:18:14 -070018#include "Diagnostics.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#include "ResourceTable.h"
20#include "ResourceUtils.h"
21#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070022#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "link/Linkers.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070024#include "process/IResourceTableConsumer.h"
25#include "process/SymbolTable.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080026#include "util/Util.h"
27#include "xml/XmlUtil.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070028
29#include <androidfw/ResourceTypes.h>
30#include <cassert>
31
32namespace aapt {
33
34namespace {
35
36/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -070037 * The ReferenceLinkerVisitor will follow all references and make sure they
38 * point
39 * to resources that actually exist, either in the local resource table, or as
40 * external
41 * symbols. Once the target resource has been found, the ID of the resource will
42 * be assigned
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043 * to the reference object.
44 *
45 * NOTE: All of the entries in the ResourceTable must be assigned IDs.
46 */
Adam Lesinski467f1712015-11-16 17:35:44 -080047class ReferenceLinkerVisitor : public ValueVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070048 public:
49 using ValueVisitor::visit;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050
Adam Lesinskicacb28f2016-10-19 12:18:14 -070051 ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols,
52 StringPool* stringPool, xml::IPackageDeclStack* decl,
53 CallSite* callSite)
54 : mContext(context),
55 mSymbols(symbols),
56 mPackageDecls(decl),
57 mStringPool(stringPool),
58 mCallSite(callSite) {}
59
60 void visit(Reference* ref) override {
61 if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls,
62 mCallSite)) {
63 mError = true;
64 }
65 }
66
67 /**
68 * We visit the Style specially because during this phase, values of
69 * attributes are
70 * all RawString values. Now that we are expected to resolve all symbols, we
71 * can
72 * lookup the attributes to find out which types are allowed for the
73 * attributes' values.
74 */
75 void visit(Style* style) override {
76 if (style->parent) {
77 visit(&style->parent.value());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070078 }
79
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 for (Style::Entry& entry : style->entries) {
81 std::string errStr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070082
Adam Lesinskicacb28f2016-10-19 12:18:14 -070083 // Transform the attribute reference so that it is using the fully
84 // qualified package
85 // name. This will also mark the reference as being able to see private
86 // resources if
87 // there was a '*' in the reference or if the package came from the
88 // private namespace.
89 Reference transformedReference = entry.key;
90 transformReferenceFromNamespace(mPackageDecls,
91 mContext->getCompilationPackage(),
92 &transformedReference);
93
94 // Find the attribute in the symbol table and check if it is visible from
95 // this callsite.
96 const SymbolTable::Symbol* symbol =
97 ReferenceLinker::resolveAttributeCheckVisibility(
98 transformedReference, mContext->getNameMangler(), mSymbols,
99 mCallSite, &errStr);
100 if (symbol) {
101 // Assign our style key the correct ID.
102 // The ID may not exist.
103 entry.key.id = symbol->id;
104
105 // Try to convert the value to a more specific, typed value based on the
106 // attribute it is set to.
107 entry.value = parseValueWithAttribute(std::move(entry.value),
108 symbol->attribute.get());
109
110 // Link/resolve the final value (mostly if it's a reference).
111 entry.value->accept(this);
112
113 // Now verify that the type of this item is compatible with the
114 // attribute it
115 // is defined for. We pass `nullptr` as the DiagMessage so that this
116 // check is
117 // fast and we avoid creating a DiagMessage when the match is
118 // successful.
119 if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
120 // The actual type of this item is incompatible with the attribute.
121 DiagMessage msg(entry.key.getSource());
122
123 // Call the matches method again, this time with a DiagMessage so we
124 // fill
125 // in the actual error message.
126 symbol->attribute->matches(entry.value.get(), &msg);
127 mContext->getDiagnostics()->error(msg);
128 mError = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700129 }
130
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700131 } else {
132 DiagMessage msg(entry.key.getSource());
133 msg << "style attribute '";
134 ReferenceLinker::writeResourceName(&msg, entry.key,
135 transformedReference);
136 msg << "' " << errStr;
137 mContext->getDiagnostics()->error(msg);
138 mError = true;
139 }
140 }
141 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800142
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700143 bool hasError() { return mError; }
Adam Lesinski467f1712015-11-16 17:35:44 -0800144
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700145 private:
146 IAaptContext* mContext;
147 SymbolTable* mSymbols;
148 xml::IPackageDeclStack* mPackageDecls;
149 StringPool* mStringPool;
150 CallSite* mCallSite;
151 bool mError = false;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700152
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700153 /**
154 * Transform a RawString value into a more specific, appropriate value, based
155 * on the
156 * Attribute. If a non RawString value is passed in, this is an identity
157 * transform.
158 */
159 std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
160 const Attribute* attr) {
161 if (RawString* rawString = valueCast<RawString>(value.get())) {
162 std::unique_ptr<Item> transformed =
163 ResourceUtils::tryParseItemForAttribute(*rawString->value, attr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700164
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700165 // If we could not parse as any specific type, try a basic STRING.
166 if (!transformed &&
167 (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
168 util::StringBuilder stringBuilder;
169 stringBuilder.append(*rawString->value);
170 if (stringBuilder) {
171 transformed = util::make_unique<String>(
172 mStringPool->makeRef(stringBuilder.str()));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700173 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700174 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700175
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700176 if (transformed) {
177 return transformed;
178 }
179 };
180 return value;
181 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700182};
183
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700184} // namespace
Adam Lesinski467f1712015-11-16 17:35:44 -0800185
186/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700187 * The symbol is visible if it is public, or if the reference to it is
188 * requesting private access
Adam Lesinski467f1712015-11-16 17:35:44 -0800189 * or if the callsite comes from the same package.
190 */
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700191bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol,
192 const Reference& ref,
Adam Lesinski467f1712015-11-16 17:35:44 -0800193 const CallSite& callSite) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194 if (!symbol.isPublic && !ref.privateReference) {
195 if (ref.name) {
196 return callSite.resource.package == ref.name.value().package;
197 } else if (ref.id && symbol.id) {
198 return ref.id.value().packageId() == symbol.id.value().packageId();
199 } else {
200 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800201 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 }
203 return true;
Adam Lesinski467f1712015-11-16 17:35:44 -0800204}
205
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700206const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(
207 const Reference& reference, NameMangler* mangler, SymbolTable* symbols) {
208 if (reference.name) {
209 Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
210 return symbols->findByName(mangled ? mangled.value()
211 : reference.name.value());
212 } else if (reference.id) {
213 return symbols->findById(reference.id.value());
214 } else {
215 return nullptr;
216 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800217}
218
Adam Lesinski64587af2016-02-18 18:33:06 -0800219const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
221 CallSite* callSite, std::string* outError) {
222 const SymbolTable::Symbol* symbol =
223 resolveSymbol(reference, nameMangler, symbols);
224 if (!symbol) {
225 if (outError) *outError = "not found";
226 return nullptr;
227 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800228
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700229 if (!isSymbolVisible(*symbol, reference, *callSite)) {
230 if (outError) *outError = "is private";
231 return nullptr;
232 }
233 return symbol;
Adam Lesinski467f1712015-11-16 17:35:44 -0800234}
235
Adam Lesinski64587af2016-02-18 18:33:06 -0800236const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700237 const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
238 CallSite* callSite, std::string* outError) {
239 const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(
240 reference, nameMangler, symbols, callSite, outError);
241 if (!symbol) {
242 return nullptr;
243 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800244
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700245 if (!symbol->attribute) {
246 if (outError) *outError = "is not an attribute";
247 return nullptr;
248 }
249 return symbol;
Adam Lesinski467f1712015-11-16 17:35:44 -0800250}
251
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700252Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(
253 const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
254 CallSite* callSite, std::string* outError) {
255 const SymbolTable::Symbol* symbol =
256 resolveSymbol(reference, nameMangler, symbols);
257 if (!symbol) {
258 if (outError) *outError = "not found";
259 return {};
260 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800261
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700262 if (!symbol->attribute) {
263 if (outError) *outError = "is not an attribute";
264 return {};
265 }
266 return xml::AaptAttribute{symbol->id, *symbol->attribute};
Adam Lesinski467f1712015-11-16 17:35:44 -0800267}
268
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700269void ReferenceLinker::writeResourceName(DiagMessage* outMsg,
270 const Reference& orig,
Adam Lesinski28cacf02015-11-23 14:22:47 -0800271 const Reference& transformed) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700272 assert(outMsg);
Adam Lesinski28cacf02015-11-23 14:22:47 -0800273
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700274 if (orig.name) {
275 *outMsg << orig.name.value();
276 if (transformed.name.value() != orig.name.value()) {
277 *outMsg << " (aka " << transformed.name.value() << ")";
Adam Lesinski28cacf02015-11-23 14:22:47 -0800278 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700279 } else {
280 *outMsg << orig.id.value();
281 }
Adam Lesinski28cacf02015-11-23 14:22:47 -0800282}
283
Adam Lesinski467f1712015-11-16 17:35:44 -0800284bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700285 SymbolTable* symbols,
286 xml::IPackageDeclStack* decls,
Adam Lesinski467f1712015-11-16 17:35:44 -0800287 CallSite* callSite) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700288 assert(reference);
289 assert(reference->name || reference->id);
Adam Lesinski467f1712015-11-16 17:35:44 -0800290
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700291 Reference transformedReference = *reference;
292 transformReferenceFromNamespace(decls, context->getCompilationPackage(),
293 &transformedReference);
Adam Lesinski467f1712015-11-16 17:35:44 -0800294
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700295 std::string errStr;
296 const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
297 transformedReference, context->getNameMangler(), symbols, callSite,
298 &errStr);
299 if (s) {
300 // The ID may not exist. This is fine because of the possibility of building
301 // against
302 // libraries without assigned IDs.
303 // Ex: Linking against own resources when building a static library.
304 reference->id = s->id;
305 return true;
306 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800307
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700308 DiagMessage errorMsg(reference->getSource());
309 errorMsg << "resource ";
310 writeResourceName(&errorMsg, *reference, transformedReference);
311 errorMsg << " " << errStr;
312 context->getDiagnostics()->error(errorMsg);
313 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800314}
315
316namespace {
317
318struct EmptyDeclStack : public xml::IPackageDeclStack {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700319 Maybe<xml::ExtractedPackage> transformPackageAlias(
320 const StringPiece& alias,
321 const StringPiece& localPackage) const override {
322 if (alias.empty()) {
323 return xml::ExtractedPackage{localPackage.toString(), true /* private */};
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700324 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700325 return {};
326 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327};
328
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700329} // namespace
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700330
331bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700332 EmptyDeclStack declStack;
333 bool error = false;
334 for (auto& package : table->packages) {
335 for (auto& type : package->types) {
336 for (auto& entry : type->entries) {
337 // Symbol state information may be lost if there is no value for the
338 // resource.
339 if (entry->symbolStatus.state != SymbolState::kUndefined &&
340 entry->values.empty()) {
341 context->getDiagnostics()->error(
342 DiagMessage(entry->symbolStatus.source)
343 << "no definition for declared symbol '"
344 << ResourceNameRef(package->name, type->type, entry->name)
345 << "'");
346 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700347 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700348
349 CallSite callSite = {
350 ResourceNameRef(package->name, type->type, entry->name)};
351 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
352 &table->stringPool, &declStack,
353 &callSite);
354
355 for (auto& configValue : entry->values) {
356 configValue->value->accept(&visitor);
357 }
358
359 if (visitor.hasError()) {
360 error = true;
361 }
362 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700363 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700364 }
365 return !error;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700366}
367
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700368} // namespace aapt