blob: 4b8253752d96f5c60524a289cdea2127a161cfe4 [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
81 void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
82 const Item* value) {
83 *msg << "expected";
84 if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
85 *msg << " boolean";
86 }
87
88 if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
89 *msg << " color";
90 }
91
92 if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
93 *msg << " dimension";
94 }
95
96 if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
97 *msg << " enum";
98 }
99
100 if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
101 *msg << " flags";
102 }
103
104 if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
105 *msg << " float";
106 }
107
108 if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
109 *msg << " fraction";
110 }
111
112 if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
113 *msg << " integer";
114 }
115
116 if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
117 *msg << " reference";
118 }
119
120 if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
121 *msg << " string";
122 }
123
124 *msg << " but got " << *value;
125 }
126
127public:
128 using ValueVisitor::visit;
129
Adam Lesinski467f1712015-11-16 17:35:44 -0800130 ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
131 xml::IPackageDeclStack* decl,CallSite* callSite) :
132 mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
133 mCallSite(callSite) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700134 }
135
Adam Lesinski467f1712015-11-16 17:35:44 -0800136 void visit(Reference* ref) override {
137 if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
138 mError = true;
139 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700140 }
141
142 /**
143 * We visit the Style specially because during this phase, values of attributes are
144 * all RawString values. Now that we are expected to resolve all symbols, we can
145 * lookup the attributes to find out which types are allowed for the attributes' values.
146 */
147 void visit(Style* style) override {
148 if (style->parent) {
149 visit(&style->parent.value());
150 }
151
152 for (Style::Entry& entry : style->entries) {
Adam Lesinski467f1712015-11-16 17:35:44 -0800153 std::string errStr;
154
155 // Transform the attribute reference so that it is using the fully qualified package
156 // name. This will also mark the reference as being able to see private resources if
157 // there was a '*' in the reference or if the package came from the private namespace.
158 Reference transformedReference = entry.key;
159 transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
160 &transformedReference);
161
162 // Find the attribute in the symbol table and check if it is visible from this callsite.
163 const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
164 transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
165 if (symbol) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700166 // Assign our style key the correct ID.
Adam Lesinski467f1712015-11-16 17:35:44 -0800167 entry.key.id = symbol->id;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700168
169 // Try to convert the value to a more specific, typed value based on the
170 // attribute it is set to.
Adam Lesinski467f1712015-11-16 17:35:44 -0800171 entry.value = parseValueWithAttribute(std::move(entry.value),
172 symbol->attribute.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700173
174 // Link/resolve the final value (mostly if it's a reference).
175 entry.value->accept(this);
176
177 // Now verify that the type of this item is compatible with the attribute it
178 // is defined for.
179 android::Res_value val = {};
180 entry.value->flatten(&val);
181
182 // Always allow references.
Adam Lesinski467f1712015-11-16 17:35:44 -0800183 const uint32_t typeMask = symbol->attribute->typeMask |
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700184 android::ResTable_map::TYPE_REFERENCE;
185
186 if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
187 // The actual type of this item is incompatible with the attribute.
Adam Lesinskie78fd612015-10-22 12:48:43 -0700188 DiagMessage msg(style->getSource());
Adam Lesinski467f1712015-11-16 17:35:44 -0800189 buildAttributeMismatchMessage(&msg, symbol->attribute.get(), entry.value.get());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700190 mContext->getDiagnostics()->error(msg);
191 mError = true;
192 }
193 } else {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700194 DiagMessage msg(style->getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700195 msg << "style attribute '";
196 if (entry.key.name) {
197 msg << entry.key.name.value().package << ":" << entry.key.name.value().entry;
198 } else {
199 msg << entry.key.id.value();
200 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800201 msg << "' " << errStr;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700202 mContext->getDiagnostics()->error(msg);
Adam Lesinski467f1712015-11-16 17:35:44 -0800203 mContext->getDiagnostics()->note(DiagMessage(style->getSource()) << entry.key);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700204 mError = true;
205 }
206 }
207 }
208
Adam Lesinski467f1712015-11-16 17:35:44 -0800209 bool hasError() {
210 return mError;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700211 }
212};
213
Adam Lesinski467f1712015-11-16 17:35:44 -0800214} // namespace
215
216/**
217 * The symbol is visible if it is public, or if the reference to it is requesting private access
218 * or if the callsite comes from the same package.
219 */
220bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
221 const CallSite& callSite) {
222 if (!symbol.isPublic && !ref.privateReference) {
223 if (ref.name) {
224 return callSite.resource.package == ref.name.value().package;
225 } else if (ref.id) {
226 return ref.id.value().packageId() == symbol.id.packageId();
227 } else {
228 return false;
229 }
230 }
231 return true;
232}
233
234const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
235 NameMangler* mangler,
236 ISymbolTable* symbols) {
237 if (reference.name) {
238 Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
239 return symbols->findByName(mangled ? mangled.value() : reference.name.value());
240 } else if (reference.id) {
241 return symbols->findById(reference.id.value());
242 } else {
243 return nullptr;
244 }
245}
246
247const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
248 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
249 CallSite* callSite, std::string* outError) {
250 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
251 if (!symbol) {
252 std::stringstream errStr;
253 errStr << "not found";
254 if (outError) *outError = errStr.str();
255 return nullptr;
256 }
257
258 if (!isSymbolVisible(*symbol, reference, *callSite)) {
259 std::stringstream errStr;
260 errStr << "is private";
261 if (outError) *outError = errStr.str();
262 return nullptr;
263 }
264 return symbol;
265}
266
267const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
268 const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
269 CallSite* callSite, std::string* outError) {
270 const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
271 symbols, callSite,
272 outError);
273 if (!symbol) {
274 return nullptr;
275 }
276
277 if (!symbol->attribute) {
278 std::stringstream errStr;
279 errStr << "is not an attribute";
280 if (outError) *outError = errStr.str();
281 return nullptr;
282 }
283 return symbol;
284}
285
286Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
287 NameMangler* nameMangler,
288 ISymbolTable* symbols,
289 CallSite* callSite,
290 std::string* outError) {
291 const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
292 if (!symbol) {
293 return {};
294 }
295
296 if (!symbol->attribute) {
297 std::stringstream errStr;
298 errStr << "is not an attribute";
299 if (outError) *outError = errStr.str();
300 return {};
301 }
302 return xml::AaptAttribute{ symbol->id, *symbol->attribute };
303}
304
305bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
306 ISymbolTable* symbols, xml::IPackageDeclStack* decls,
307 CallSite* callSite) {
308 assert(reference);
309 assert(reference->name || reference->id);
310
311 Reference transformedReference = *reference;
312 transformReferenceFromNamespace(decls, context->getCompilationPackage(),
313 &transformedReference);
314
315 std::string errStr;
316 const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
317 transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
318 if (s) {
319 reference->id = s->id;
320 return true;
321 }
322
323 DiagMessage errorMsg(reference->getSource());
324 errorMsg << "resource ";
325 if (reference->name) {
326 errorMsg << reference->name.value();
327 if (transformedReference.name.value() != reference->name.value()) {
328 errorMsg << " (aka " << transformedReference.name.value() << ")";
329 }
330 } else {
331 errorMsg << reference->id.value();
332 }
333
334 errorMsg << " " << errStr;
335 context->getDiagnostics()->error(errorMsg);
336 return false;
337}
338
339namespace {
340
341struct EmptyDeclStack : public xml::IPackageDeclStack {
342 Maybe<xml::ExtractedPackage> transformPackageAlias(
343 const StringPiece16& alias, const StringPiece16& localPackage) const override {
344 if (alias.empty()) {
345 return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700346 }
347 return {};
348 }
349};
350
351} // namespace
352
353bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
354 EmptyDeclStack declStack;
355 bool error = false;
356 for (auto& package : table->packages) {
357 for (auto& type : package->types) {
358 for (auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700359 // Symbol state information may be lost if there is no value for the resource.
360 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
361 context->getDiagnostics()->error(
362 DiagMessage(entry->symbolStatus.source)
363 << "no definition for declared symbol '"
364 << ResourceNameRef(package->name, type->type, entry->name)
365 << "'");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700366 error = true;
367 }
368
Adam Lesinski467f1712015-11-16 17:35:44 -0800369 CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
370 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
371 &table->stringPool, &declStack, &callSite);
372
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700373 for (auto& configValue : entry->values) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700374 configValue.value->accept(&visitor);
Adam Lesinski467f1712015-11-16 17:35:44 -0800375 }
376
377 if (visitor.hasError()) {
378 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700379 }
380 }
381 }
382 }
383 return !error;
384}
385
386} // namespace aapt