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