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 "ResourceUtils.h"
     19 #include "SdkConstants.h"
     20 #include "link/Linkers.h"
     21 #include "link/ReferenceLinker.h"
     22 #include "process/IResourceTableConsumer.h"
     23 #include "process/SymbolTable.h"
     24 #include "util/Util.h"
     25 #include "xml/XmlDom.h"
     26 
     27 namespace aapt {
     28 
     29 namespace {
     30 
     31 /**
     32  * Visits all references (including parents of styles, references in styles, arrays, etc) and
     33  * links their symbolic name to their Resource ID, performing mangling and package aliasing
     34  * as needed.
     35  */
     36 class ReferenceVisitor : public ValueVisitor {
     37 public:
     38     using ValueVisitor::visit;
     39 
     40     ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
     41                      CallSite* callSite) :
     42              mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
     43              mError(false) {
     44     }
     45 
     46     void visit(Reference* ref) override {
     47         if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
     48             mError = true;
     49         }
     50     }
     51 
     52     bool hasError() const {
     53         return mError;
     54     }
     55 
     56 private:
     57     IAaptContext* mContext;
     58     SymbolTable* mSymbols;
     59     xml::IPackageDeclStack* mDecls;
     60     CallSite* mCallSite;
     61     bool mError;
     62 };
     63 
     64 /**
     65  * Visits each xml Element and compiles the attributes within.
     66  */
     67 class XmlVisitor : public xml::PackageAwareVisitor {
     68 public:
     69     using xml::PackageAwareVisitor::visit;
     70 
     71     XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
     72                std::set<int>* sdkLevelsFound, CallSite* callSite) :
     73             mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
     74             mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
     75     }
     76 
     77     void visit(xml::Element* el) override {
     78         const Source source = mSource.withLine(el->lineNumber);
     79         for (xml::Attribute& attr : el->attributes) {
     80             Maybe<xml::ExtractedPackage> maybePackage =
     81                     xml::extractPackageFromNamespace(attr.namespaceUri);
     82             if (maybePackage) {
     83                 // There is a valid package name for this attribute. We will look this up.
     84                 StringPiece16 package = maybePackage.value().package;
     85                 if (package.empty()) {
     86                     // Empty package means the 'current' or 'local' package.
     87                     package = mContext->getCompilationPackage();
     88                 }
     89 
     90                 Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
     91                 attrRef.privateReference = maybePackage.value().privateNamespace;
     92 
     93                 std::string errStr;
     94                 attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
     95                         attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
     96 
     97                 // Convert the string value into a compiled Value if this is a valid attribute.
     98                 if (attr.compiledAttribute) {
     99                     if (attr.compiledAttribute.value().id) {
    100                         // Record all SDK levels from which the attributes were defined.
    101                         const size_t sdkLevel = findAttributeSdkLevel(
    102                                 attr.compiledAttribute.value().id.value());
    103                         if (sdkLevel > 1) {
    104                             mSdkLevelsFound->insert(sdkLevel);
    105                         }
    106                     }
    107 
    108                     const Attribute* attribute = &attr.compiledAttribute.value().attribute;
    109                     attr.compiledValue = ResourceUtils::parseItemForAttribute(attr.value,
    110                                                                               attribute);
    111                     if (!attr.compiledValue &&
    112                             !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
    113                         // We won't be able to encode this as a string.
    114                         mContext->getDiagnostics()->error(
    115                                 DiagMessage(source) << "'" << attr.value << "' "
    116                                                     << "is incompatible with attribute "
    117                                                     << package << ":" << attr.name << " "
    118                                                     << *attribute);
    119                         mError = true;
    120                     }
    121 
    122                 } else {
    123                     mContext->getDiagnostics()->error(DiagMessage(source)
    124                                                       << "attribute '" << package << ":"
    125                                                       << attr.name << "' " << errStr);
    126                     mError = true;
    127 
    128                 }
    129             } else {
    130                 // We still encode references.
    131                 attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
    132             }
    133 
    134             if (attr.compiledValue) {
    135                 // With a compiledValue, we must resolve the reference and assign it an ID.
    136                 attr.compiledValue->setSource(source);
    137                 attr.compiledValue->accept(&mReferenceVisitor);
    138             }
    139         }
    140 
    141         // Call the super implementation.
    142         xml::PackageAwareVisitor::visit(el);
    143     }
    144 
    145     bool hasError() {
    146         return mError || mReferenceVisitor.hasError();
    147     }
    148 
    149 private:
    150     IAaptContext* mContext;
    151     SymbolTable* mSymbols;
    152     Source mSource;
    153     std::set<int>* mSdkLevelsFound;
    154     CallSite* mCallSite;
    155     ReferenceVisitor mReferenceVisitor;
    156     bool mError = false;
    157 };
    158 
    159 } // namespace
    160 
    161 bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
    162     mSdkLevelsFound.clear();
    163     CallSite callSite = { resource->file.name };
    164     XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
    165                        &mSdkLevelsFound, &callSite);
    166     if (resource->root) {
    167         resource->root->accept(&visitor);
    168         return !visitor.hasError();
    169     }
    170     return false;
    171 }
    172 
    173 } // namespace aapt
    174