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