Home | History | Annotate | Download | only in link
      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 
     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 
     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.
     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 
    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.
    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 
    164   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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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