ResourceTable.cpp revision e4bb9eb5af5b0899dc0921d5580220b20e15bd5a
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 StringPiece16& rhs) { 39 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; 40} 41 42ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& 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 StringPiece16& 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 StringPiece16& 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 StringPiece16& 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 StringPiece16& 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 192/** 193 * The default handler for collisions. A return value of -1 means keep the 194 * existing value, 0 means fail, and +1 means take the incoming value. 195 */ 196int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) { 197 Attribute* existingAttr = valueCast<Attribute>(existing); 198 Attribute* incomingAttr = valueCast<Attribute>(incoming); 199 200 if (!incomingAttr) { 201 if (incoming->isWeak()) { 202 // We're trying to add a weak resource but a resource 203 // already exists. Keep the existing. 204 return -1; 205 } else if (existing->isWeak()) { 206 // Override the weak resource with the new strong resource. 207 return 1; 208 } 209 // The existing and incoming values are strong, this is an error 210 // if the values are not both attributes. 211 return 0; 212 } 213 214 if (!existingAttr) { 215 if (existing->isWeak()) { 216 // The existing value is not an attribute and it is weak, 217 // so take the incoming attribute value. 218 return 1; 219 } 220 // The existing value is not an attribute and it is strong, 221 // so the incoming attribute value is an error. 222 return 0; 223 } 224 225 assert(incomingAttr && existingAttr); 226 227 // 228 // Attribute specific handling. At this point we know both 229 // values are attributes. Since we can declare and define 230 // attributes all-over, we do special handling to see 231 // which definition sticks. 232 // 233 if (existingAttr->typeMask == incomingAttr->typeMask) { 234 // The two attributes are both DECLs, but they are plain attributes 235 // with the same formats. 236 // Keep the strongest one. 237 return existingAttr->isWeak() ? 1 : -1; 238 } 239 240 if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 241 // Any incoming attribute is better than this. 242 return 1; 243 } 244 245 if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) { 246 // The incoming attribute may be a USE instead of a DECL. 247 // Keep the existing attribute. 248 return -1; 249 } 250 return 0; 251} 252 253static constexpr const char16_t* kValidNameChars = u"._-"; 254static constexpr const char16_t* kValidNameMangledChars = u"._-$"; 255 256bool ResourceTable::addResource(const ResourceNameRef& name, 257 const ConfigDescription& config, 258 const StringPiece& product, 259 std::unique_ptr<Value> value, 260 IDiagnostics* diag) { 261 return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars, 262 resolveValueCollision, diag); 263} 264 265bool ResourceTable::addResource(const ResourceNameRef& name, 266 const ResourceId resId, 267 const ConfigDescription& config, 268 const StringPiece& product, 269 std::unique_ptr<Value> value, 270 IDiagnostics* diag) { 271 return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars, 272 resolveValueCollision, diag); 273} 274 275bool ResourceTable::addFileReference(const ResourceNameRef& name, 276 const ConfigDescription& config, 277 const Source& source, 278 const StringPiece16& path, 279 IDiagnostics* diag) { 280 return addFileReference(name, config, source, path, resolveValueCollision, diag); 281} 282 283bool ResourceTable::addFileReference(const ResourceNameRef& name, 284 const ConfigDescription& config, 285 const Source& source, 286 const StringPiece16& path, 287 std::function<int(Value*,Value*)> conflictResolver, 288 IDiagnostics* diag) { 289 std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( 290 stringPool.makeRef(path)); 291 fileRef->setSource(source); 292 return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef), 293 kValidNameChars, conflictResolver, diag); 294} 295 296bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 297 const ConfigDescription& config, 298 const StringPiece& product, 299 std::unique_ptr<Value> value, 300 IDiagnostics* diag) { 301 return addResourceImpl(name, ResourceId{}, config, product, std::move(value), 302 kValidNameMangledChars, resolveValueCollision, diag); 303} 304 305bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, 306 const ResourceId id, 307 const ConfigDescription& config, 308 const StringPiece& product, 309 std::unique_ptr<Value> value, 310 IDiagnostics* diag) { 311 return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars, 312 resolveValueCollision, diag); 313} 314 315bool ResourceTable::addResourceImpl(const ResourceNameRef& name, 316 const ResourceId resId, 317 const ConfigDescription& config, 318 const StringPiece& product, 319 std::unique_ptr<Value> value, 320 const char16_t* validChars, 321 std::function<int(Value*,Value*)> conflictResolver, 322 IDiagnostics* diag) { 323 assert(value && "value can't be nullptr"); 324 assert(diag && "diagnostics can't be nullptr"); 325 326 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 327 if (badCharIter != name.entry.end()) { 328 diag->error(DiagMessage(value->getSource()) 329 << "resource '" 330 << name 331 << "' has invalid entry name '" 332 << name.entry 333 << "'. Invalid character '" 334 << StringPiece16(badCharIter, 1) 335 << "'"); 336 return false; 337 } 338 339 ResourceTablePackage* package = findOrCreatePackage(name.package); 340 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 341 diag->error(DiagMessage(value->getSource()) 342 << "trying to add resource '" 343 << name 344 << "' with ID " 345 << resId 346 << " but package '" 347 << package->name 348 << "' already has ID " 349 << std::hex << (int) package->id.value() << std::dec); 350 return false; 351 } 352 353 ResourceTableType* type = package->findOrCreateType(name.type); 354 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 355 diag->error(DiagMessage(value->getSource()) 356 << "trying to add resource '" 357 << name 358 << "' with ID " 359 << resId 360 << " but type '" 361 << type->type 362 << "' already has ID " 363 << std::hex << (int) type->id.value() << std::dec); 364 return false; 365 } 366 367 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 368 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 369 diag->error(DiagMessage(value->getSource()) 370 << "trying to add resource '" 371 << name 372 << "' with ID " 373 << resId 374 << " but resource already has ID " 375 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 376 return false; 377 } 378 379 ResourceConfigValue* configValue = entry->findOrCreateValue(config, product); 380 if (!configValue->value) { 381 // Resource does not exist, add it now. 382 configValue->value = std::move(value); 383 384 } else { 385 int collisionResult = conflictResolver(configValue->value.get(), value.get()); 386 if (collisionResult > 0) { 387 // Take the incoming value. 388 configValue->value = std::move(value); 389 } else if (collisionResult == 0) { 390 diag->error(DiagMessage(value->getSource()) 391 << "duplicate value for resource '" << name << "' " 392 << "with config '" << config << "'"); 393 diag->error(DiagMessage(configValue->value->getSource()) 394 << "resource previously defined here"); 395 return false; 396 } 397 } 398 399 if (resId.isValid()) { 400 package->id = resId.packageId(); 401 type->id = resId.typeId(); 402 entry->id = resId.entryId(); 403 } 404 return true; 405} 406 407bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId resId, 408 const Symbol& symbol, IDiagnostics* diag) { 409 return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag); 410} 411 412bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name, 413 const ResourceId resId, 414 const Symbol& symbol, IDiagnostics* diag) { 415 return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag); 416} 417 418bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId, 419 const Symbol& symbol, const char16_t* validChars, 420 IDiagnostics* diag) { 421 assert(diag && "diagnostics can't be nullptr"); 422 423 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); 424 if (badCharIter != name.entry.end()) { 425 diag->error(DiagMessage(symbol.source) 426 << "resource '" 427 << name 428 << "' has invalid entry name '" 429 << name.entry 430 << "'. Invalid character '" 431 << StringPiece16(badCharIter, 1) 432 << "'"); 433 return false; 434 } 435 436 ResourceTablePackage* package = findOrCreatePackage(name.package); 437 if (resId.isValid() && package->id && package->id.value() != resId.packageId()) { 438 diag->error(DiagMessage(symbol.source) 439 << "trying to add resource '" 440 << name 441 << "' with ID " 442 << resId 443 << " but package '" 444 << package->name 445 << "' already has ID " 446 << std::hex << (int) package->id.value() << std::dec); 447 return false; 448 } 449 450 ResourceTableType* type = package->findOrCreateType(name.type); 451 if (resId.isValid() && type->id && type->id.value() != resId.typeId()) { 452 diag->error(DiagMessage(symbol.source) 453 << "trying to add resource '" 454 << name 455 << "' with ID " 456 << resId 457 << " but type '" 458 << type->type 459 << "' already has ID " 460 << std::hex << (int) type->id.value() << std::dec); 461 return false; 462 } 463 464 ResourceEntry* entry = type->findOrCreateEntry(name.entry); 465 if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) { 466 diag->error(DiagMessage(symbol.source) 467 << "trying to add resource '" 468 << name 469 << "' with ID " 470 << resId 471 << " but resource already has ID " 472 << ResourceId(package->id.value(), type->id.value(), entry->id.value())); 473 return false; 474 } 475 476 if (resId.isValid()) { 477 package->id = resId.packageId(); 478 type->id = resId.typeId(); 479 entry->id = resId.entryId(); 480 } 481 482 // Only mark the type state as public, it doesn't care about being private. 483 if (symbol.state == SymbolState::kPublic) { 484 type->symbolStatus.state = SymbolState::kPublic; 485 } 486 487 if (symbol.state == SymbolState::kUndefined && 488 entry->symbolStatus.state != SymbolState::kUndefined) { 489 // We can't undefine a symbol (remove its visibility). Ignore. 490 return true; 491 } 492 493 if (symbol.state == SymbolState::kPrivate && 494 entry->symbolStatus.state == SymbolState::kPublic) { 495 // We can't downgrade public to private. Ignore. 496 return true; 497 } 498 499 entry->symbolStatus = std::move(symbol); 500 return true; 501} 502 503Maybe<ResourceTable::SearchResult> 504ResourceTable::findResource(const ResourceNameRef& name) { 505 ResourceTablePackage* package = findPackage(name.package); 506 if (!package) { 507 return {}; 508 } 509 510 ResourceTableType* type = package->findType(name.type); 511 if (!type) { 512 return {}; 513 } 514 515 ResourceEntry* entry = type->findEntry(name.entry); 516 if (!entry) { 517 return {}; 518 } 519 return SearchResult{ package, type, entry }; 520} 521 522} // namespace aapt 523