• 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 "link/ReferenceLinker.h"
18 
19 #include "android-base/logging.h"
20 #include "androidfw/ResourceTypes.h"
21 
22 #include "Diagnostics.h"
23 #include "ResourceTable.h"
24 #include "ResourceUtils.h"
25 #include "ResourceValues.h"
26 #include "ValueVisitor.h"
27 #include "link/Linkers.h"
28 #include "process/IResourceTableConsumer.h"
29 #include "process/SymbolTable.h"
30 #include "util/Util.h"
31 #include "xml/XmlUtil.h"
32 
33 using ::aapt::ResourceUtils::StringBuilder;
34 using ::android::StringPiece;
35 
36 namespace aapt {
37 
38 namespace {
39 
40 // The ReferenceLinkerVisitor will follow all references and make sure they point
41 // to resources that actually exist, either in the local resource table, or as external
42 // symbols. Once the target resource has been found, the ID of the resource will be assigned
43 // to the reference object.
44 //
45 // NOTE: All of the entries in the ResourceTable must be assigned IDs.
46 class ReferenceLinkerVisitor : public DescendingValueVisitor {
47  public:
48   using DescendingValueVisitor::Visit;
49 
ReferenceLinkerVisitor(const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,StringPool * string_pool,xml::IPackageDeclStack * decl)50   ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
51                          StringPool* string_pool, xml::IPackageDeclStack* decl)
52       : callsite_(callsite),
53         context_(context),
54         symbols_(symbols),
55         package_decls_(decl),
56         string_pool_(string_pool) {}
57 
Visit(Reference * ref)58   void Visit(Reference* ref) override {
59     if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
60       error_ = true;
61     }
62   }
63 
64   // We visit the Style specially because during this phase, values of attributes are
65   // all RawString values. Now that we are expected to resolve all symbols, we can
66   // lookup the attributes to find out which types are allowed for the attributes' values.
Visit(Style * style)67   void Visit(Style* style) override {
68     if (style->parent) {
69       Visit(&style->parent.value());
70     }
71 
72     for (Style::Entry& entry : style->entries) {
73       std::string err_str;
74 
75       // Transform the attribute reference so that it is using the fully qualified package
76       // name. This will also mark the reference as being able to see private resources if
77       // there was a '*' in the reference or if the package came from the private namespace.
78       Reference transformed_reference = entry.key;
79       ResolvePackage(package_decls_, &transformed_reference);
80 
81       // Find the attribute in the symbol table and check if it is visible from this callsite.
82       const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
83           transformed_reference, callsite_, symbols_, &err_str);
84       if (symbol) {
85         // Assign our style key the correct ID. 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 attribute it is
89         // set to.
90         entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
91 
92         // Link/resolve the final value (mostly if it's a reference).
93         entry.value->Accept(this);
94 
95         // Now verify that the type of this item is compatible with the
96         // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
97         // check is fast and we avoid creating a DiagMessage when the match is successful.
98         if (!symbol->attribute->Matches(*entry.value, nullptr)) {
99           // The actual type of this item is incompatible with the attribute.
100           DiagMessage msg(entry.key.GetSource());
101 
102           // Call the matches method again, this time with a DiagMessage so we fill in the actual
103           // error message.
104           symbol->attribute->Matches(*entry.value, &msg);
105           context_->GetDiagnostics()->Error(msg);
106           error_ = true;
107         }
108 
109       } else {
110         DiagMessage msg(entry.key.GetSource());
111         msg << "style attribute '";
112         ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
113         msg << "' " << err_str;
114         context_->GetDiagnostics()->Error(msg);
115         error_ = true;
116       }
117     }
118   }
119 
HasError()120   bool HasError() {
121     return error_;
122   }
123 
124  private:
125   DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
126 
127   // Transform a RawString value into a more specific, appropriate value, based on the
128   // Attribute. If a non RawString value is passed in, this is an identity transform.
ParseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)129   std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
130                                                 const Attribute* attr) {
131     if (RawString* raw_string = ValueCast<RawString>(value.get())) {
132       std::unique_ptr<Item> transformed =
133           ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
134 
135       // If we could not parse as any specific type, try a basic STRING.
136       if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
137         StringBuilder string_builder;
138         string_builder.AppendText(*raw_string->value);
139         if (string_builder) {
140           transformed =
141               util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
142         }
143       }
144 
145       if (transformed) {
146         return transformed;
147       }
148     }
149     return value;
150   }
151 
152   const CallSite& callsite_;
153   IAaptContext* context_;
154   SymbolTable* symbols_;
155   xml::IPackageDeclStack* package_decls_;
156   StringPool* string_pool_;
157   bool error_ = false;
158 };
159 
160 class EmptyDeclStack : public xml::IPackageDeclStack {
161  public:
162   EmptyDeclStack() = default;
163 
TransformPackageAlias(const StringPiece & alias) const164   Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
165     if (alias.empty()) {
166       return xml::ExtractedPackage{{}, true /*private*/};
167     }
168     return {};
169   }
170 
171  private:
172   DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
173 };
174 
175 // The symbol is visible if it is public, or if the reference to it is requesting private access
176 // or if the callsite comes from the same package.
IsSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callsite)177 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
178                      const CallSite& callsite) {
179   if (symbol.is_public || ref.private_reference) {
180     return true;
181   }
182 
183   if (ref.name) {
184     const ResourceName& name = ref.name.value();
185     if (name.package.empty()) {
186       // If the symbol was found, and the package is empty, that means it was found in the local
187       // scope, which is always visible (private local).
188       return true;
189     }
190 
191     // The symbol is visible if the reference is local to the same package it is defined in.
192     return callsite.package == name.package;
193   }
194 
195   if (ref.id && symbol.id) {
196     return ref.id.value().package_id() == symbol.id.value().package_id();
197   }
198   return false;
199 }
200 
201 }  // namespace
202 
ResolveSymbol(const Reference & reference,const CallSite & callsite,SymbolTable * symbols)203 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
204                                                           const CallSite& callsite,
205                                                           SymbolTable* symbols) {
206   if (reference.name) {
207     const ResourceName& name = reference.name.value();
208     if (name.package.empty()) {
209       // Use the callsite's package name if no package name was defined.
210       return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
211     }
212     return symbols->FindByName(name);
213   } else if (reference.id) {
214     return symbols->FindById(reference.id.value());
215   } else {
216     return nullptr;
217   }
218 }
219 
ResolveSymbolCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)220 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
221                                                                          const CallSite& callsite,
222                                                                          SymbolTable* symbols,
223                                                                          std::string* out_error) {
224   const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
225   if (!symbol) {
226     if (out_error) *out_error = "not found";
227     return nullptr;
228   }
229 
230   if (!IsSymbolVisible(*symbol, reference, callsite)) {
231     if (out_error) *out_error = "is private";
232     return nullptr;
233   }
234   return symbol;
235 }
236 
ResolveAttributeCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)237 const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
238     const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
239     std::string* out_error) {
240   const SymbolTable::Symbol* symbol =
241       ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
242   if (!symbol) {
243     return nullptr;
244   }
245 
246   if (!symbol->attribute) {
247     if (out_error) *out_error = "is not an attribute";
248     return nullptr;
249   }
250   return symbol;
251 }
252 
CompileXmlAttribute(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)253 Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
254                                                                const CallSite& callsite,
255                                                                SymbolTable* symbols,
256                                                                std::string* out_error) {
257   const SymbolTable::Symbol* symbol =
258       ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
259   if (!symbol) {
260     return {};
261   }
262 
263   if (!symbol->attribute) {
264     if (out_error) *out_error = "is not an attribute";
265     return {};
266   }
267   return xml::AaptAttribute(*symbol->attribute, symbol->id);
268 }
269 
WriteResourceName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)270 void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
271                                         const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
272   CHECK(out_msg != nullptr);
273   if (!ref.name) {
274     *out_msg << ref.id.value();
275     return;
276   }
277 
278   *out_msg << ref.name.value();
279 
280   Reference fully_qualified = ref;
281   xml::ResolvePackage(decls, &fully_qualified);
282 
283   ResourceName& full_name = fully_qualified.name.value();
284   if (full_name.package.empty()) {
285     full_name.package = callsite.package;
286   }
287 
288   if (full_name != ref.name.value()) {
289     *out_msg << " (aka " << full_name << ")";
290   }
291 }
292 
WriteAttributeName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)293 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
294                                          const xml::IPackageDeclStack* decls,
295                                          DiagMessage* out_msg) {
296   CHECK(out_msg != nullptr);
297   if (!ref.name) {
298     *out_msg << ref.id.value();
299     return;
300   }
301 
302   const ResourceName& ref_name = ref.name.value();
303   CHECK_EQ(ref_name.type, ResourceType::kAttr);
304 
305   if (!ref_name.package.empty()) {
306     *out_msg << ref_name.package << ":";
307   }
308   *out_msg << ref_name.entry;
309 
310   Reference fully_qualified = ref;
311   xml::ResolvePackage(decls, &fully_qualified);
312 
313   ResourceName& full_name = fully_qualified.name.value();
314   if (full_name.package.empty()) {
315     full_name.package = callsite.package;
316   }
317 
318   if (full_name != ref.name.value()) {
319     *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
320   }
321 }
322 
LinkReference(const CallSite & callsite,Reference * reference,IAaptContext * context,SymbolTable * symbols,const xml::IPackageDeclStack * decls)323 bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
324                                     IAaptContext* context, SymbolTable* symbols,
325                                     const xml::IPackageDeclStack* decls) {
326   CHECK(reference != nullptr);
327   if (!reference->name && !reference->id) {
328     // This is @null.
329     return true;
330   }
331 
332   Reference transformed_reference = *reference;
333   xml::ResolvePackage(decls, &transformed_reference);
334 
335   std::string err_str;
336   const SymbolTable::Symbol* s =
337       ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
338   if (s) {
339     // The ID may not exist. This is fine because of the possibility of building
340     // against libraries without assigned IDs.
341     // Ex: Linking against own resources when building a static library.
342     reference->id = s->id;
343     reference->is_dynamic = s->is_dynamic;
344     return true;
345   }
346 
347   DiagMessage error_msg(reference->GetSource());
348   error_msg << "resource ";
349   WriteResourceName(*reference, callsite, decls, &error_msg);
350   error_msg << " " << err_str;
351   context->GetDiagnostics()->Error(error_msg);
352   return false;
353 }
354 
Consume(IAaptContext * context,ResourceTable * table)355 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
356   EmptyDeclStack decl_stack;
357   bool error = false;
358   for (auto& package : table->packages) {
359     // Since we're linking, each package must have a name.
360     CHECK(!package->name.empty()) << "all packages being linked must have a name";
361 
362     for (auto& type : package->types) {
363       for (auto& entry : type->entries) {
364         // First, unmangle the name if necessary.
365         ResourceName name(package->name, type->type, entry->name);
366         NameMangler::Unmangle(&name.entry, &name.package);
367 
368         // Symbol state information may be lost if there is no value for the resource.
369         if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
370           context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
371                                            << "no definition for declared symbol '" << name << "'");
372           error = true;
373         }
374 
375         // The context of this resource is the package in which it is defined.
376         const CallSite callsite{name.package};
377         ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
378                                        &table->string_pool, &decl_stack);
379 
380         for (auto& config_value : entry->values) {
381           config_value->value->Accept(&visitor);
382         }
383 
384         if (visitor.HasError()) {
385           error = true;
386         }
387       }
388     }
389   }
390   return !error;
391 }
392 
393 }  // namespace aapt
394