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 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 
     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 
    180 class 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  */
    204 bool 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 
    219 const 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 
    230 const 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 
    247 const 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 
    263 Maybe<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 
    280 void 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 
    295 bool 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 
    326 bool 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