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 "Diagnostics.h"
     18 #include "ReferenceLinker.h"
     19 #include "ResourceTable.h"
     20 #include "ResourceUtils.h"
     21 #include "ResourceValues.h"
     22 #include "ValueVisitor.h"
     23 #include "link/Linkers.h"
     24 #include "process/IResourceTableConsumer.h"
     25 #include "process/SymbolTable.h"
     26 #include "util/Util.h"
     27 #include "xml/XmlUtil.h"
     28 
     29 #include <androidfw/ResourceTypes.h>
     30 #include <cassert>
     31 
     32 namespace aapt {
     33 
     34 namespace {
     35 
     36 /**
     37  * The ReferenceLinkerVisitor will follow all references and make sure they point
     38  * to resources that actually exist, either in the local resource table, or as external
     39  * symbols. Once the target resource has been found, the ID of the resource will be assigned
     40  * to the reference object.
     41  *
     42  * NOTE: All of the entries in the ResourceTable must be assigned IDs.
     43  */
     44 class ReferenceLinkerVisitor : public ValueVisitor {
     45 public:
     46     using ValueVisitor::visit;
     47 
     48     ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
     49                            xml::IPackageDeclStack* decl,CallSite* callSite) :
     50             mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
     51             mCallSite(callSite) {
     52     }
     53 
     54     void visit(Reference* ref) override {
     55         if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
     56             mError = true;
     57         }
     58     }
     59 
     60     /**
     61      * We visit the Style specially because during this phase, values of attributes are
     62      * all RawString values. Now that we are expected to resolve all symbols, we can
     63      * lookup the attributes to find out which types are allowed for the attributes' values.
     64      */
     65     void visit(Style* style) override {
     66         if (style->parent) {
     67             visit(&style->parent.value());
     68         }
     69 
     70         for (Style::Entry& entry : style->entries) {
     71             std::string errStr;
     72 
     73             // Transform the attribute reference so that it is using the fully qualified package
     74             // name. This will also mark the reference as being able to see private resources if
     75             // there was a '*' in the reference or if the package came from the private namespace.
     76             Reference transformedReference = entry.key;
     77             transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
     78                                             &transformedReference);
     79 
     80             // Find the attribute in the symbol table and check if it is visible from this callsite.
     81             const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
     82                     transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
     83             if (symbol) {
     84                 // Assign our style key the correct ID.
     85                 // 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
     89                 // attribute it is set to.
     90                 entry.value = parseValueWithAttribute(std::move(entry.value),
     91                                                       symbol->attribute.get());
     92 
     93                 // Link/resolve the final value (mostly if it's a reference).
     94                 entry.value->accept(this);
     95 
     96                 // Now verify that the type of this item is compatible with the attribute it
     97                 // is defined for. We pass `nullptr` as the DiagMessage so that this check is
     98                 // fast and we avoid creating a DiagMessage when the match is successful.
     99                 if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
    100                     // The actual type of this item is incompatible with the attribute.
    101                     DiagMessage msg(entry.key.getSource());
    102 
    103                     // Call the matches method again, this time with a DiagMessage so we fill
    104                     // in the actual error message.
    105                     symbol->attribute->matches(entry.value.get(), &msg);
    106                     mContext->getDiagnostics()->error(msg);
    107                     mError = true;
    108                 }
    109 
    110             } else {
    111                 DiagMessage msg(entry.key.getSource());
    112                 msg << "style attribute '";
    113                 ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
    114                 msg << "' " << errStr;
    115                 mContext->getDiagnostics()->error(msg);
    116                 mError = true;
    117             }
    118         }
    119     }
    120 
    121     bool hasError() {
    122         return mError;
    123     }
    124 
    125 private:
    126     IAaptContext* mContext;
    127     SymbolTable* mSymbols;
    128     xml::IPackageDeclStack* mPackageDecls;
    129     StringPool* mStringPool;
    130     CallSite* mCallSite;
    131     bool mError = false;
    132 
    133     /**
    134      * Transform a RawString value into a more specific, appropriate value, based on the
    135      * Attribute. If a non RawString value is passed in, this is an identity transform.
    136      */
    137     std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
    138                                                   const Attribute* attr) {
    139         if (RawString* rawString = valueCast<RawString>(value.get())) {
    140             std::unique_ptr<Item> transformed =
    141                     ResourceUtils::parseItemForAttribute(*rawString->value, attr);
    142 
    143             // If we could not parse as any specific type, try a basic STRING.
    144             if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
    145                 util::StringBuilder stringBuilder;
    146                 stringBuilder.append(*rawString->value);
    147                 if (stringBuilder) {
    148                     transformed = util::make_unique<String>(
    149                             mStringPool->makeRef(stringBuilder.str()));
    150                 }
    151             }
    152 
    153             if (transformed) {
    154                 return transformed;
    155             }
    156         };
    157         return value;
    158     }
    159 };
    160 
    161 } // namespace
    162 
    163 /**
    164  * The symbol is visible if it is public, or if the reference to it is requesting private access
    165  * or if the callsite comes from the same package.
    166  */
    167 bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
    168                                       const CallSite& callSite) {
    169     if (!symbol.isPublic && !ref.privateReference) {
    170         if (ref.name) {
    171             return callSite.resource.package == ref.name.value().package;
    172         } else if (ref.id && symbol.id) {
    173             return ref.id.value().packageId() == symbol.id.value().packageId();
    174         } else {
    175             return false;
    176         }
    177     }
    178     return true;
    179 }
    180 
    181 const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
    182                                                           NameMangler* mangler,
    183                                                           SymbolTable* symbols) {
    184     if (reference.name) {
    185         Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
    186         return symbols->findByName(mangled ? mangled.value() : reference.name.value());
    187     } else if (reference.id) {
    188         return symbols->findById(reference.id.value());
    189     } else {
    190         return nullptr;
    191     }
    192 }
    193 
    194 const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
    195         const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
    196         CallSite* callSite, std::string* outError) {
    197     const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
    198     if (!symbol) {
    199         if (outError) *outError = "not found";
    200         return nullptr;
    201     }
    202 
    203     if (!isSymbolVisible(*symbol, reference, *callSite)) {
    204         if (outError) *outError = "is private";
    205         return nullptr;
    206     }
    207     return symbol;
    208 }
    209 
    210 const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
    211         const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
    212         CallSite* callSite, std::string* outError) {
    213     const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
    214                                                                      symbols, callSite,
    215                                                                      outError);
    216     if (!symbol) {
    217         return nullptr;
    218     }
    219 
    220     if (!symbol->attribute) {
    221         if (outError) *outError = "is not an attribute";
    222         return nullptr;
    223     }
    224     return symbol;
    225 }
    226 
    227 Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
    228                                                                NameMangler* nameMangler,
    229                                                                SymbolTable* symbols,
    230                                                                CallSite* callSite,
    231                                                                std::string* outError) {
    232     const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
    233     if (!symbol) {
    234         return {};
    235     }
    236 
    237     if (!symbol->attribute) {
    238         if (outError) *outError = "is not an attribute";
    239         return {};
    240     }
    241     return xml::AaptAttribute{ symbol->id, *symbol->attribute };
    242 }
    243 
    244 void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
    245                                         const Reference& transformed) {
    246     assert(outMsg);
    247 
    248     if (orig.name) {
    249         *outMsg << orig.name.value();
    250         if (transformed.name.value() != orig.name.value()) {
    251             *outMsg << " (aka " << transformed.name.value() << ")";
    252         }
    253     } else {
    254         *outMsg << orig.id.value();
    255     }
    256 }
    257 
    258 bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
    259                                     SymbolTable* symbols, xml::IPackageDeclStack* decls,
    260                                     CallSite* callSite) {
    261     assert(reference);
    262     assert(reference->name || reference->id);
    263 
    264     Reference transformedReference = *reference;
    265     transformReferenceFromNamespace(decls, context->getCompilationPackage(),
    266                                     &transformedReference);
    267 
    268     std::string errStr;
    269     const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
    270             transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
    271     if (s) {
    272         // The ID may not exist. This is fine because of the possibility of building against
    273         // libraries without assigned IDs.
    274         // Ex: Linking against own resources when building a static library.
    275         reference->id = s->id;
    276         return true;
    277     }
    278 
    279     DiagMessage errorMsg(reference->getSource());
    280     errorMsg << "resource ";
    281     writeResourceName(&errorMsg, *reference, transformedReference);
    282     errorMsg << " " << errStr;
    283     context->getDiagnostics()->error(errorMsg);
    284     return false;
    285 }
    286 
    287 namespace {
    288 
    289 struct EmptyDeclStack : public xml::IPackageDeclStack {
    290     Maybe<xml::ExtractedPackage> transformPackageAlias(
    291             const StringPiece16& alias, const StringPiece16& localPackage) const override {
    292         if (alias.empty()) {
    293             return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
    294         }
    295         return {};
    296     }
    297 };
    298 
    299 } // namespace
    300 
    301 bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
    302     EmptyDeclStack declStack;
    303     bool error = false;
    304     for (auto& package : table->packages) {
    305         for (auto& type : package->types) {
    306             for (auto& entry : type->entries) {
    307                 // Symbol state information may be lost if there is no value for the resource.
    308                 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
    309                     context->getDiagnostics()->error(
    310                             DiagMessage(entry->symbolStatus.source)
    311                             << "no definition for declared symbol '"
    312                             << ResourceNameRef(package->name, type->type, entry->name)
    313                             << "'");
    314                     error = true;
    315                 }
    316 
    317                 CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
    318                 ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
    319                                                &table->stringPool, &declStack, &callSite);
    320 
    321                 for (auto& configValue : entry->values) {
    322                     configValue->value->accept(&visitor);
    323                 }
    324 
    325                 if (visitor.hasError()) {
    326                     error = true;
    327                 }
    328             }
    329         }
    330     }
    331     return !error;
    332 }
    333 
    334 } // namespace aapt
    335