ResourceTable.cpp revision 5eeaaddffd23d8d85aeb321e3ceea626e42cf9de
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 "ConfigDescription.h" 18#include "NameMangler.h" 19#include "ResourceTable.h" 20#include "ResourceValues.h" 21#include "ValueVisitor.h" 22#include "util/Util.h" 23 24#include <algorithm> 25#include <androidfw/ResourceTypes.h> 26#include <memory> 27#include <string> 28#include <tuple> 29 30namespace aapt { 31 32static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) { 33 return lhs->type < rhs; 34} 35 36template <typename T> 37static bool lessThanStructWithName(const std::unique_ptr<T>& lhs, 38 const StringPiece& rhs) { 39 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; 40} 41 42ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) { 43 const auto last = packages.end(); 44 auto iter = std::lower_bound(packages.begin(), last, name, 45 lessThanStructWithName<ResourceTablePackage>); 46 if (iter != last && name == (*iter)->name) { 47 return iter->get(); 48 } 49 return nullptr; 50} 51 52ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) { 53 for (auto& package : packages) { 54 if (package->id && package->id.value() == id) { 55 return package.get(); 56 } 57 } 58 return nullptr; 59} 60 61ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name, Maybe<uint8_t> id) { 62 ResourceTablePackage* package = findOrCreatePackage(name); 63 if (id && !package->id) { 64 package->id = id; 65 return package; 66 } 67 68 if (id && package->id && package->id.value() != id.value()) { 69 return nullptr; 70 } 71 return package; 72} 73 74ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece& name) { 75 const auto last = packages.end(); 76 auto iter = std::lower_bound(packages.begin(), last, name, 77 lessThanStructWithName<ResourceTablePackage>); 78 if (iter != last && name == (*iter)->name) { 79 return iter->get(); 80 } 81 82 std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>(); 83 newPackage->name = name.toString(); 84 return packages.emplace(iter, std::move(newPackage))->get(); 85} 86 87ResourceTableType* ResourceTablePackage::findType(ResourceType type) { 88 const auto last = types.end(); 89 auto iter = std::lower_bound(types.begin(), last, type, lessThanType); 90 if (iter != last && (*iter)->type == type) { 91 return iter->get(); 92 } 93 return nullptr; 94} 95 96ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) { 97 const auto last = types.end(); 98 auto iter = std::lower_bound(types.begin(), last, type, lessThanType); 99 if (iter != last && (*iter)->type == type) { 100 return iter->get(); 101 } 102 return types.emplace(iter, new ResourceTableType(type))->get(); 103} 104 105ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) { 106 const auto last = entries.end(); 107 auto iter = std::lower_bound(entries.begin(), last, name, 108 lessThanStructWithName<ResourceEntry>); 109 if (iter != last && name == (*iter)->name) { 110 return iter->get(); 111 } 112 return nullptr; 113} 114 115ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) { 116 auto last = entries.end(); 117 auto iter = std::lower_bound(entries.begin(), last, name, 118 lessThanStructWithName<ResourceEntry>); 119 if (iter != last && name == (*iter)->name) { 120 return iter->get(); 121 } 122 return entries.emplace(iter, new ResourceEntry(name))->get(); 123} 124 125ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) { 126 return findValue(config, StringPiece()); 127} 128 129struct ConfigKey { 130 const ConfigDescription* config; 131 const StringPiece& product; 132}; 133 134bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) { 135 int cmp = lhs->config.compare(*rhs.config); 136 if (cmp == 0) { 137 cmp = StringPiece(lhs->product).compare(rhs.product); 138 } 139 return cmp < 0; 140} 141 142ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config, 143 const StringPiece& product) { 144 auto iter = std::lower_bound(values.begin(), values.end(), 145 ConfigKey{ &config, product }, ltConfigKeyRef); 146 if (iter != values.end()) { 147 ResourceConfigValue* value = iter->get(); 148 if (value->config == config && StringPiece(value->product) == product) { 149 return value; 150 } 151 } 152 return nullptr; 153} 154 155ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config, 156 const StringPiece& product) { 157 auto iter = std::lower_bound(values.begin(), values.end(), 158 ConfigKey{ &config, product }, ltConfigKeyRef); 159 if (iter != values.end()) { 160 ResourceConfigValue* value = iter->get(); 161 if (value->config == config && StringPiece(value->product) == product) { 162 return value; 163 } 164 } 165 ResourceConfigValue* newValue = values.insert( 166 iter, util::make_unique<ResourceConfigValue>(config, product))->get(); 167 return newValue; 168} 169 170std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) { 171 std::vector<ResourceConfigValue*> results; 172 173 auto iter = values.begin(); 174 for (; iter != values.end(); ++iter) { 175 ResourceConfigValue* value = iter->get(); 176 if (value->config == config) { 177 results.push_back(value); 178 ++iter; 179 break; 180 } 181 } 182 183 for (; iter != values.end(); ++iter) { 184 ResourceConfigValue* value = iter->get(); 185 if (value->config == config) { 186 results.push_back(value); 187 } 188 } 189 return results; 190} 191 192std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf( 193 const std::function<bool(ResourceConfigValue*)>& f) { 194 std::vector<ResourceConfigValue*> results; 195 for (auto& configValue : values) { 196 if (f(configValue.get())) { 197 results.push_back(configValue.get()); 198 } 199 } 200 return results; 201} 202 203/** 204 * The default handler for collisions. 205 * 206 * Typically, a weak value will be overridden by a strong value. An existing weak 207 * value will not be overridden by an incoming weak value. 208 * 209 * There are some exceptions: 210 * 211 * Attributes: There are two types of Attribute values: USE and DECL. 212 * 213 * USE is anywhere an Attribute is declared without a format, and in a place that would 214 * be legal to declare if the Attribute already existed. This is typically in a 215 * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak. 216 * 217 * DECL is an absolute declaration of an Attribute and specifies an explicit format. 218 * 219 * A DECL will override a USE without error. Two DECLs must match in their format for there to be 220 * no error. 221 */ 222ResourceTable::CollisionResult ResourceTable::resolveValueCollision( 223 Value* existing, Value* incoming) { 224 Attribute* existingAttr = valueCast<Attribute>(existing); 225 Attribute* incomingAttr = valueCast<Attribute>(incoming); 226 if (!incomingAttr) { 227 if (incoming->isWeak()) { 228 // We're trying to add a weak resource but a resource 229 // already exists. Keep the existing. 230 return CollisionResult::kKeepOriginal; 231 } else if (existing->isWeak()) { 232 // Override the weak resource with the new strong resource. 233 return CollisionResult::kTakeNew; 234 } 235 // The existing and incoming values are strong, this is an error 236 // if the values are not both attributes. 237 return CollisionResult::kConflict; 238 } 239 240 if (!existingAttr) { 241 if (existing->isWeak()) { 242 // The existing value is not an attribute and it is weak, 243 // so take the incoming attribute value. 244 return CollisionResult::kTakeNew; 245 } 246 // The existing value is not an attribute and it is strong, 247 // so the incoming attribute value is an error. 248 return CollisionResult::kConflict; 249 } 250 251 assert(incomingAttr && existingAttr); 252 253 // 254 // Attribute specific handling. At this point we know both 255 // values are attributes. Since we can declare and define 256 // attributes all-over, we do special handling to see 257 // which definition sticks. 258 // 259 if (existingAttr->typeMask == incomingAttr->typeMask) { 260 // The two attributes are both DECLs, but they are plain attributes 261 // with the same formats. 262 // Keep the strongest one. 263 return existingAttr->isWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal; 264 } 265 266 if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 267 // Any incoming attribute is better than this. 268 return CollisionResult::kTakeNew; 269 } 270 271 if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 272 // The incoming attribute may be a USE instead of a DECL. 273 // Keep the existing attribute. 274 return CollisionResult::kKeepOriginal; 275 } 276 return CollisionResult::kConflict; 277} 278 279static constexpr const char* kValidNameChars = "._-"; 280static constexpr const char* kValidNameMangledChars = "._-$"; 281 282bool ResourceTable::addResource(const ResourceNameRef& name, 283 const ConfigDescription& config, 284 const StringPiece& product, 285 std::unique_ptr<Value> value, 286 IDiagnostics* diag) { 287 return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars, 288 resolveValueCollision, diag); 289} 290 291bool ResourceTable::addResource(const ResourceNameRef& name, 292 const ResourceId& resId, 293 const ConfigDescription& config, 294 const StringPiece& product, 295 std::unique_ptr<Value> value, 296 IDiagnostics* diag) { 297 return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars, 298 resolveValueCollision, diag); 299} 300 301bool ResourceTable::addFileReference(const ResourceNameRef& name, 302 const ConfigDescription& config, 303 const Source& source, 304 const StringPiece& path, 305 IDiagnostics* diag) { 306 return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag); 307} 308 309bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name, 310 const ConfigDescription& config, 311 const Source& source, 312 const StringPiece& path, 313 io::IFile* file, 314 IDiagnostics* diag) { 315 return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag); 316} 317 318bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name, 319 const ConfigDescription& config, 320 const Source& source, 321 const StringPiece& path, 322 io::IFile* file, 323 const char* validChars, 324 IDiagnostics* diag) { 325 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( 326 stringPool.makeRef(path)); 327 fileRef->setSource(source); 328 fileRef->file = file; 329 return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef), 330 validChars, resolveValueCollision, diag); 331} 332 333bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 334 const ConfigDescription& config, 335 const StringPiece& product, 336 std::unique_ptr<Value> value, 337 IDiagnostics* diag) { 338 return addResourceImpl(name, ResourceId{}, config, product, std::move(value), 339 kValidNameMangledChars, resolveValueCollision, diag); 340} 341 342bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 343 const ResourceId& id, 344 const ConfigDescription& config, 345 const StringPiece& product, 346 std::unique_ptr<Value> value, 347 IDiagnostics* diag) { 348 return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars, 349 resolveValueCollision, diag); 350} 351 352bool ResourceTable::addResourceImpl(const ResourceNameRef& name, 353 const ResourceId& resId, 354 const ConfigDescription& config, 355 const StringPiece& product, 356 std::unique_ptr<Value> value, 357 const char* validChars, 358 const CollisionResolverFunc& conflictResolver, 359 IDiagnostics* diag) { 360 assert(value && "value can't be nullptr"); 361 assert(diag && "diagnostics can't be nullptr"); 362 363 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 364 if (badCharIter != name.entry.end()) { 365 diag->error(DiagMessage(value->getSource()) 366 << "resource '" 367 << name 368 << "' has invalid entry name '" 369 << name.entry 370 << "'. Invalid character '" 371 << StringPiece(badCharIter, 1) 372 << "'"); 373 return false; 374 } 375 376 ResourceTablePackage* package = findOrCreatePackage(name.package); 377 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 378 diag->error(DiagMessage(value->getSource()) 379 << "trying to add resource '" 380 << name 381 << "' with ID " 382 << resId 383 << " but package '" 384 << package->name 385 << "' already has ID " 386 << std::hex << (int) package->id.value() << std::dec); 387 return false; 388 } 389 390 ResourceTableType* type = package->findOrCreateType(name.type); 391 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 392 diag->error(DiagMessage(value->getSource()) 393 << "trying to add resource '" 394 << name 395 << "' with ID " 396 << resId 397 << " but type '" 398 << type->type 399 << "' already has ID " 400 << std::hex << (int) type->id.value() << std::dec); 401 return false; 402 } 403 404 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 405 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 406 diag->error(DiagMessage(value->getSource()) 407 << "trying to add resource '" 408 << name 409 << "' with ID " 410 << resId 411 << " but resource already has ID " 412 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 413 return false; 414 } 415 416 ResourceConfigValue* configValue = entry->findOrCreateValue(config, product); 417 if (!configValue->value) { 418 // Resource does not exist, add it now. 419 configValue->value = std::move(value); 420 421 } else { 422 switch (conflictResolver(configValue->value.get(), value.get())) { 423 case CollisionResult::kTakeNew: 424 // Take the incoming value. 425 configValue->value = std::move(value); 426 break; 427 428 case CollisionResult::kConflict: 429 diag->error(DiagMessage(value->getSource()) 430 << "duplicate value for resource '" << name << "' " 431 << "with config '" << config << "'"); 432 diag->error(DiagMessage(configValue->value->getSource()) 433 << "resource previously defined here"); 434 return false; 435 436 case CollisionResult::kKeepOriginal: 437 break; 438 } 439 } 440 441 if (resId.isValid()) { 442 package->id = resId.packageId(); 443 type->id = resId.typeId(); 444 entry->id = resId.entryId(); 445 } 446 return true; 447} 448 449bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId, 450 const Symbol& symbol, IDiagnostics* diag) { 451 return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag); 452} 453 454bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name, 455 const ResourceId& resId, 456 const Symbol& symbol, IDiagnostics* diag) { 457 return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag); 458} 459 460bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId, 461 const Symbol& symbol, const char* validChars, 462 IDiagnostics* diag) { 463 assert(diag && "diagnostics can't be nullptr"); 464 465 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 466 if (badCharIter != name.entry.end()) { 467 diag->error(DiagMessage(symbol.source) 468 << "resource '" 469 << name 470 << "' has invalid entry name '" 471 << name.entry 472 << "'. Invalid character '" 473 << StringPiece(badCharIter, 1) 474 << "'"); 475 return false; 476 } 477 478 ResourceTablePackage* package = findOrCreatePackage(name.package); 479 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 480 diag->error(DiagMessage(symbol.source) 481 << "trying to add resource '" 482 << name 483 << "' with ID " 484 << resId 485 << " but package '" 486 << package->name 487 << "' already has ID " 488 << std::hex << (int) package->id.value() << std::dec); 489 return false; 490 } 491 492 ResourceTableType* type = package->findOrCreateType(name.type); 493 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 494 diag->error(DiagMessage(symbol.source) 495 << "trying to add resource '" 496 << name 497 << "' with ID " 498 << resId 499 << " but type '" 500 << type->type 501 << "' already has ID " 502 << std::hex << (int) type->id.value() << std::dec); 503 return false; 504 } 505 506 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 507 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 508 diag->error(DiagMessage(symbol.source) 509 << "trying to add resource '" 510 << name 511 << "' with ID " 512 << resId 513 << " but resource already has ID " 514 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 515 return false; 516 } 517 518 if (resId.isValid()) { 519 package->id = resId.packageId(); 520 type->id = resId.typeId(); 521 entry->id = resId.entryId(); 522 } 523 524 // Only mark the type state as public, it doesn't care about being private. 525 if (symbol.state == SymbolState::kPublic) { 526 type->symbolStatus.state = SymbolState::kPublic; 527 } 528 529 if (symbol.state == SymbolState::kUndefined && 530 entry->symbolStatus.state != SymbolState::kUndefined) { 531 // We can't undefine a symbol (remove its visibility). Ignore. 532 return true; 533 } 534 535 if (symbol.state == SymbolState::kPrivate && 536 entry->symbolStatus.state == SymbolState::kPublic) { 537 // We can't downgrade public to private. Ignore. 538 return true; 539 } 540 541 entry->symbolStatus = std::move(symbol); 542 return true; 543} 544 545Maybe<ResourceTable::SearchResult> 546ResourceTable::findResource(const ResourceNameRef& name) { 547 ResourceTablePackage* package = findPackage(name.package); 548 if (!package) { 549 return {}; 550 } 551 552 ResourceTableType* type = package->findType(name.type); 553 if (!type) { 554 return {}; 555 } 556 557 ResourceEntry* entry = type->findEntry(name.entry); 558 if (!entry) { 559 return {}; 560 } 561 return SearchResult{ package, type, entry }; 562} 563 564} // namespace aapt 565