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