• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Diagnostics.h"
18 #include "ReferenceLinker.h"
19 #include "ResourceTable.h"
20 #include "ResourceUtils.h"
21 #include "ResourceValues.h"
22 #include "ValueVisitor.h"
23 #include "link/Linkers.h"
24 #include "process/IResourceTableConsumer.h"
25 #include "process/SymbolTable.h"
26 #include "util/Util.h"
27 #include "xml/XmlUtil.h"
28 
29 #include <androidfw/ResourceTypes.h>
30 #include <cassert>
31 
32 namespace aapt {
33 
34 namespace {
35 
36 /**
37  * The ReferenceLinkerVisitor will follow all references and make sure they point
38  * to resources that actually exist, either in the local resource table, or as external
39  * symbols. Once the target resource has been found, the ID of the resource will be assigned
40  * to the reference object.
41  *
42  * NOTE: All of the entries in the ResourceTable must be assigned IDs.
43  */
44 class ReferenceLinkerVisitor : public ValueVisitor {
45 public:
46     using ValueVisitor::visit;
47 
ReferenceLinkerVisitor(IAaptContext * context,SymbolTable * symbols,StringPool * stringPool,xml::IPackageDeclStack * decl,CallSite * callSite)48     ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
49                            xml::IPackageDeclStack* decl,CallSite* callSite) :
50             mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
51             mCallSite(callSite) {
52     }
53 
visit(Reference * ref)54     void visit(Reference* ref) override {
55         if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
56             mError = true;
57         }
58     }
59 
60     /**
61      * We visit the Style specially because during this phase, values of attributes are
62      * all RawString values. Now that we are expected to resolve all symbols, we can
63      * lookup the attributes to find out which types are allowed for the attributes' values.
64      */
visit(Style * style)65     void visit(Style* style) override {
66         if (style->parent) {
67             visit(&style->parent.value());
68         }
69 
70         for (Style::Entry& entry : style->entries) {
71             std::string errStr;
72 
73             // Transform the attribute reference so that it is using the fully qualified package
74             // name. This will also mark the reference as being able to see private resources if
75             // there was a '*' in the reference or if the package came from the private namespace.
76             Reference transformedReference = entry.key;
77             transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
78                                             &transformedReference);
79 
80             // Find the attribute in the symbol table and check if it is visible from this callsite.
81             const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
82                     transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
83             if (symbol) {
84                 // Assign our style key the correct ID.
85                 // The ID may not exist.
86                 entry.key.id = symbol->id;
87 
88                 // Try to convert the value to a more specific, typed value based on the
89                 // attribute it is set to.
90                 entry.value = parseValueWithAttribute(std::move(entry.value),
91                                                       symbol->attribute.get());
92 
93                 // Link/resolve the final value (mostly if it's a reference).
94                 entry.value->accept(this);
95 
96                 // Now verify that the type of this item is compatible with the attribute it
97                 // is defined for. We pass `nullptr` as the DiagMessage so that this check is
98                 // fast and we avoid creating a DiagMessage when the match is successful.
99                 if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
100                     // The actual type of this item is incompatible with the attribute.
101                     DiagMessage msg(entry.key.getSource());
102 
103                     // Call the matches method again, this time with a DiagMessage so we fill
104                     // in the actual error message.
105                     symbol->attribute->matches(entry.value.get(), &msg);
106                     mContext->getDiagnostics()->error(msg);
107                     mError = true;
108                 }
109 
110             } else {
111                 DiagMessage msg(entry.key.getSource());
112                 msg << "style attribute '";
113                 ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
114                 msg << "' " << errStr;
115                 mContext->getDiagnostics()->error(msg);
116                 mError = true;
117             }
118         }
119     }
120 
hasError()121     bool hasError() {
122         return mError;
123     }
124 
125 private:
126     IAaptContext* mContext;
127     SymbolTable* mSymbols;
128     xml::IPackageDeclStack* mPackageDecls;
129     StringPool* mStringPool;
130     CallSite* mCallSite;
131     bool mError = false;
132 
133     /**
134      * Transform a RawString value into a more specific, appropriate value, based on the
135      * Attribute. If a non RawString value is passed in, this is an identity transform.
136      */
parseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)137     std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
138                                                   const Attribute* attr) {
139         if (RawString* rawString = valueCast<RawString>(value.get())) {
140             std::unique_ptr<Item> transformed =
141                     ResourceUtils::parseItemForAttribute(*rawString->value, attr);
142 
143             // If we could not parse as any specific type, try a basic STRING.
144             if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
145                 util::StringBuilder stringBuilder;
146                 stringBuilder.append(*rawString->value);
147                 if (stringBuilder) {
148                     transformed = util::make_unique<String>(
149                             mStringPool->makeRef(stringBuilder.str()));
150                 }
151             }
152 
153             if (transformed) {
154                 return transformed;
155             }
156         };
157         return value;
158     }
159 };
160 
161 } // 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  */
isSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callSite)167 bool ReferenceLinker::isSymbolVisible(const SymbolTable::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 && symbol.id) {
173             return ref.id.value().packageId() == symbol.id.value().packageId();
174         } else {
175             return false;
176         }
177     }
178     return true;
179 }
180 
resolveSymbol(const Reference & reference,NameMangler * mangler,SymbolTable * symbols)181 const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
182                                                           NameMangler* mangler,
183                                                           SymbolTable* 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 
resolveSymbolCheckVisibility(const Reference & reference,NameMangler * nameMangler,SymbolTable * symbols,CallSite * callSite,std::string * outError)194 const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
195         const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
196         CallSite* callSite, std::string* outError) {
197     const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
198     if (!symbol) {
199         if (outError) *outError = "not found";
200         return nullptr;
201     }
202 
203     if (!isSymbolVisible(*symbol, reference, *callSite)) {
204         if (outError) *outError = "is private";
205         return nullptr;
206     }
207     return symbol;
208 }
209 
resolveAttributeCheckVisibility(const Reference & reference,NameMangler * nameMangler,SymbolTable * symbols,CallSite * callSite,std::string * outError)210 const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
211         const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
212         CallSite* callSite, std::string* outError) {
213     const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
214                                                                      symbols, callSite,
215                                                                      outError);
216     if (!symbol) {
217         return nullptr;
218     }
219 
220     if (!symbol->attribute) {
221         if (outError) *outError = "is not an attribute";
222         return nullptr;
223     }
224     return symbol;
225 }
226 
compileXmlAttribute(const Reference & reference,NameMangler * nameMangler,SymbolTable * symbols,CallSite * callSite,std::string * outError)227 Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
228                                                                NameMangler* nameMangler,
229                                                                SymbolTable* symbols,
230                                                                CallSite* callSite,
231                                                                std::string* outError) {
232     const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
233     if (!symbol) {
234         return {};
235     }
236 
237     if (!symbol->attribute) {
238         if (outError) *outError = "is not an attribute";
239         return {};
240     }
241     return xml::AaptAttribute{ symbol->id, *symbol->attribute };
242 }
243 
writeResourceName(DiagMessage * outMsg,const Reference & orig,const Reference & transformed)244 void 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 
linkReference(Reference * reference,IAaptContext * context,SymbolTable * symbols,xml::IPackageDeclStack * decls,CallSite * callSite)258 bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
259                                     SymbolTable* 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 SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
270             transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
271     if (s) {
272         // The ID may not exist. This is fine because of the possibility of building against
273         // libraries without assigned IDs.
274         // Ex: Linking against own resources when building a static library.
275         reference->id = s->id;
276         return true;
277     }
278 
279     DiagMessage errorMsg(reference->getSource());
280     errorMsg << "resource ";
281     writeResourceName(&errorMsg, *reference, transformedReference);
282     errorMsg << " " << errStr;
283     context->getDiagnostics()->error(errorMsg);
284     return false;
285 }
286 
287 namespace {
288 
289 struct EmptyDeclStack : public xml::IPackageDeclStack {
transformPackageAliasaapt::__anon49fb99ee0211::EmptyDeclStack290     Maybe<xml::ExtractedPackage> transformPackageAlias(
291             const StringPiece16& alias, const StringPiece16& localPackage) const override {
292         if (alias.empty()) {
293             return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
294         }
295         return {};
296     }
297 };
298 
299 } // namespace
300 
consume(IAaptContext * context,ResourceTable * table)301 bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
302     EmptyDeclStack declStack;
303     bool error = false;
304     for (auto& package : table->packages) {
305         for (auto& type : package->types) {
306             for (auto& entry : type->entries) {
307                 // Symbol state information may be lost if there is no value for the resource.
308                 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
309                     context->getDiagnostics()->error(
310                             DiagMessage(entry->symbolStatus.source)
311                             << "no definition for declared symbol '"
312                             << ResourceNameRef(package->name, type->type, entry->name)
313                             << "'");
314                     error = true;
315                 }
316 
317                 CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
318                 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
319                                                &table->stringPool, &declStack, &callSite);
320 
321                 for (auto& configValue : entry->values) {
322                     configValue->value->accept(&visitor);
323                 }
324 
325                 if (visitor.hasError()) {
326                     error = true;
327                 }
328             }
329         }
330     }
331     return !error;
332 }
333 
334 } // namespace aapt
335