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 32namespace aapt { 33 34namespace { 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 */ 44class ReferenceLinkerVisitor : public ValueVisitor { 45public: 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 125private: 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 */ 167bool 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 181const 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 194const 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 210const 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 227Maybe<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 244void 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 258bool 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 287namespace { 288 289struct 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 301bool 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