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
33using android::StringPiece;
34
35namespace aapt {
36
37namespace {
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 */
50class ReferenceLinkerVisitor : public ValueVisitor {
51 public:
52  using ValueVisitor::Visit;
53
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
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   */
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
113        // is defined for. We pass `nullptr` as the DiagMessage so that this
114        // check is
115        // fast and we avoid creating a DiagMessage when the match is
116        // successful.
117        if (!symbol->attribute->Matches(entry.value.get(), nullptr)) {
118          // The actual type of this item is incompatible with the attribute.
119          DiagMessage msg(entry.key.GetSource());
120
121          // Call the matches method again, this time with a DiagMessage so we
122          // fill in the actual error message.
123          symbol->attribute->Matches(entry.value.get(), &msg);
124          context_->GetDiagnostics()->Error(msg);
125          error_ = true;
126        }
127
128      } else {
129        DiagMessage msg(entry.key.GetSource());
130        msg << "style attribute '";
131        ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
132        msg << "' " << err_str;
133        context_->GetDiagnostics()->Error(msg);
134        error_ = true;
135      }
136    }
137  }
138
139  bool HasError() { return error_; }
140
141 private:
142  DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
143
144  /**
145   * Transform a RawString value into a more specific, appropriate value, based
146   * on the
147   * Attribute. If a non RawString value is passed in, this is an identity
148   * transform.
149   */
150  std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
151                                                const Attribute* attr) {
152    if (RawString* raw_string = ValueCast<RawString>(value.get())) {
153      std::unique_ptr<Item> transformed =
154          ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
155
156      // If we could not parse as any specific type, try a basic STRING.
157      if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
158        util::StringBuilder string_builder;
159        string_builder.Append(*raw_string->value);
160        if (string_builder) {
161          transformed = util::make_unique<String>(string_pool_->MakeRef(string_builder.ToString()));
162        }
163      }
164
165      if (transformed) {
166        return transformed;
167      }
168    }
169    return value;
170  }
171
172  const CallSite& callsite_;
173  IAaptContext* context_;
174  SymbolTable* symbols_;
175  xml::IPackageDeclStack* package_decls_;
176  StringPool* string_pool_;
177  bool error_ = false;
178};
179
180class EmptyDeclStack : public xml::IPackageDeclStack {
181 public:
182  EmptyDeclStack() = default;
183
184  Maybe<xml::ExtractedPackage> TransformPackageAlias(
185      const StringPiece& alias,
186      const StringPiece& local_package) const override {
187    if (alias.empty()) {
188      return xml::ExtractedPackage{local_package.to_string(), true /* private */};
189    }
190    return {};
191  }
192
193 private:
194  DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
195};
196
197}  // namespace
198
199/**
200 * The symbol is visible if it is public, or if the reference to it is
201 * requesting private access
202 * or if the callsite comes from the same package.
203 */
204bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
205                                      const Reference& ref,
206                                      const CallSite& callsite) {
207  if (!symbol.is_public && !ref.private_reference) {
208    if (ref.name) {
209      return callsite.resource.package == ref.name.value().package;
210    } else if (ref.id && symbol.id) {
211      return ref.id.value().package_id() == symbol.id.value().package_id();
212    } else {
213      return false;
214    }
215  }
216  return true;
217}
218
219const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
220                                                          SymbolTable* symbols) {
221  if (reference.name) {
222    return symbols->FindByName(reference.name.value());
223  } else if (reference.id) {
224    return symbols->FindById(reference.id.value());
225  } else {
226    return nullptr;
227  }
228}
229
230const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
231                                                                         const CallSite& callsite,
232                                                                         SymbolTable* symbols,
233                                                                         std::string* out_error) {
234  const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
235  if (!symbol) {
236    if (out_error) *out_error = "not found";
237    return nullptr;
238  }
239
240  if (!IsSymbolVisible(*symbol, reference, callsite)) {
241    if (out_error) *out_error = "is private";
242    return nullptr;
243  }
244  return symbol;
245}
246
247const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
248    const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
249    std::string* out_error) {
250  const SymbolTable::Symbol* symbol =
251      ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
252  if (!symbol) {
253    return nullptr;
254  }
255
256  if (!symbol->attribute) {
257    if (out_error) *out_error = "is not an attribute";
258    return nullptr;
259  }
260  return symbol;
261}
262
263Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
264                                                               const CallSite& callsite,
265                                                               SymbolTable* symbols,
266                                                               std::string* out_error) {
267  const SymbolTable::Symbol* symbol =
268      ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
269  if (!symbol) {
270    return {};
271  }
272
273  if (!symbol->attribute) {
274    if (out_error) *out_error = "is not an attribute";
275    return {};
276  }
277  return xml::AaptAttribute(*symbol->attribute, symbol->id);
278}
279
280void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
281                                        const Reference& orig,
282                                        const Reference& transformed) {
283  CHECK(out_msg != nullptr);
284
285  if (orig.name) {
286    *out_msg << orig.name.value();
287    if (transformed.name.value() != orig.name.value()) {
288      *out_msg << " (aka " << transformed.name.value() << ")";
289    }
290  } else {
291    *out_msg << orig.id.value();
292  }
293}
294
295bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
296                                    IAaptContext* context, SymbolTable* symbols,
297                                    xml::IPackageDeclStack* decls) {
298  CHECK(reference != nullptr);
299  if (!reference->name && !reference->id) {
300    // This is @null.
301    return true;
302  }
303
304  Reference transformed_reference = *reference;
305  TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
306
307  std::string err_str;
308  const SymbolTable::Symbol* s =
309      ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
310  if (s) {
311    // The ID may not exist. This is fine because of the possibility of building
312    // against libraries without assigned IDs.
313    // Ex: Linking against own resources when building a static library.
314    reference->id = s->id;
315    return true;
316  }
317
318  DiagMessage error_msg(reference->GetSource());
319  error_msg << "resource ";
320  WriteResourceName(&error_msg, *reference, transformed_reference);
321  error_msg << " " << err_str;
322  context->GetDiagnostics()->Error(error_msg);
323  return false;
324}
325
326bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
327  EmptyDeclStack decl_stack;
328  bool error = false;
329  for (auto& package : table->packages) {
330    for (auto& type : package->types) {
331      for (auto& entry : type->entries) {
332        // Symbol state information may be lost if there is no value for the
333        // resource.
334        if (entry->symbol_status.state != SymbolState::kUndefined &&
335            entry->values.empty()) {
336          context->GetDiagnostics()->Error(
337              DiagMessage(entry->symbol_status.source)
338              << "no definition for declared symbol '"
339              << ResourceNameRef(package->name, type->type, entry->name)
340              << "'");
341          error = true;
342        }
343
344        CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
345        ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
346                                       &table->string_pool, &decl_stack);
347
348        for (auto& config_value : entry->values) {
349          config_value->value->Accept(&visitor);
350        }
351
352        if (visitor.HasError()) {
353          error = true;
354        }
355      }
356    }
357  }
358  return !error;
359}
360
361}  // namespace aapt
362