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 "android-base/stringprintf.h"
21 #include "androidfw/ResourceTypes.h"
22
23 #include "Diagnostics.h"
24 #include "ResourceParser.h"
25 #include "ResourceTable.h"
26 #include "ResourceUtils.h"
27 #include "ResourceValues.h"
28 #include "ValueVisitor.h"
29 #include "link/Linkers.h"
30 #include "process/IResourceTableConsumer.h"
31 #include "process/SymbolTable.h"
32 #include "trace/TraceBuffer.h"
33 #include "util/Util.h"
34 #include "xml/XmlUtil.h"
35
36 using ::aapt::ResourceUtils::StringBuilder;
37 using ::android::StringPiece;
38 using ::android::base::StringPrintf;
39
40 namespace aapt {
41 namespace {
42 struct LoggingResourceName {
LoggingResourceNameaapt::__anon61d7ad890111::LoggingResourceName43 LoggingResourceName(const Reference& ref, const CallSite& callsite,
44 const xml::IPackageDeclStack* decls)
45 : ref_(ref), callsite_(callsite), decls_(decls) {
46 }
47
48 const Reference& ref_;
49 const CallSite& callsite_;
50 const xml::IPackageDeclStack* decls_;
51 };
52
operator <<(::std::ostream & out,const LoggingResourceName & name)53 inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
54 if (!name.ref_.name) {
55 out << name.ref_.id.value();
56 return out;
57 }
58
59 out << name.ref_.name.value();
60
61 Reference fully_qualified = name.ref_;
62 xml::ResolvePackage(name.decls_, &fully_qualified);
63
64 ResourceName& full_name = fully_qualified.name.value();
65 if (full_name.package.empty()) {
66 full_name.package = name.callsite_.package;
67 }
68
69 if (full_name != name.ref_.name.value()) {
70 out << " (aka " << full_name << ")";
71 }
72 return out;
73 }
74
75 } // namespace
76
TransformDerived(const Reference * value)77 std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
78 auto linked_item =
79 ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
80 if (linked_item) {
81 auto linked_item_ptr = linked_item.release();
82 if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
83 return std::unique_ptr<Reference>(ref);
84 }
85 context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
86 << "value of '"
87 << LoggingResourceName(*value, callsite_, package_decls_)
88 << "' must be a resource reference");
89 delete linked_item_ptr;
90 }
91
92 error_ = true;
93 return CloningValueTransformer::TransformDerived(value);
94 }
95
TransformDerived(const Style * style)96 std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
97 // We visit the Style specially because during this phase, values of attributes are either
98 // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
99 // the attributes to find out which types are allowed for the attributes' values.
100 auto new_style = CloningValueTransformer::TransformDerived(style);
101 if (new_style->parent) {
102 new_style->parent = *TransformDerived(&style->parent.value());
103 }
104
105 for (Style::Entry& entry : new_style->entries) {
106 std::string err_str;
107
108 // Transform the attribute reference so that it is using the fully qualified package
109 // name. This will also mark the reference as being able to see private resources if
110 // there was a '*' in the reference or if the package came from the private namespace.
111 Reference transformed_reference = entry.key;
112 ResolvePackage(package_decls_, &transformed_reference);
113
114 // Find the attribute in the symbol table and check if it is visible from this callsite.
115 const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
116 transformed_reference, callsite_, context_, symbols_, &err_str);
117 if (symbol) {
118 // Assign our style key the correct ID. The ID may not exist.
119 entry.key.id = symbol->id;
120
121 // Link/resolve the final value if it's a reference.
122 entry.value = entry.value->Transform(*this);
123
124 // Try to convert the value to a more specific, typed value based on the attribute it is
125 // set to.
126 entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
127
128 // Now verify that the type of this item is compatible with the
129 // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
130 // check is fast and we avoid creating a DiagMessage when the match is successful.
131 if (!symbol->attribute->Matches(*entry.value, nullptr)) {
132 // The actual type of this item is incompatible with the attribute.
133 DiagMessage msg(entry.key.GetSource());
134
135 // Call the matches method again, this time with a DiagMessage so we fill in the actual
136 // error message.
137 symbol->attribute->Matches(*entry.value, &msg);
138 context_->GetDiagnostics()->Error(msg);
139 error_ = true;
140 }
141 } else {
142 context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
143 << "style attribute '"
144 << LoggingResourceName(entry.key, callsite_, package_decls_)
145 << "' " << err_str);
146
147 error_ = true;
148 }
149 }
150 return new_style;
151 }
152
TransformItem(const Reference * value)153 std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
154 auto linked_value =
155 ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
156 if (linked_value) {
157 return linked_value;
158 }
159 error_ = true;
160 return CloningValueTransformer::TransformDerived(value);
161 }
162
163 // Transform a RawString value into a more specific, appropriate value, based on the
164 // Attribute. If a non RawString value is passed in, this is an identity transform.
ParseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)165 std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
166 std::unique_ptr<Item> value, const Attribute* attr) {
167 if (RawString* raw_string = ValueCast<RawString>(value.get())) {
168 std::unique_ptr<Item> transformed =
169 ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
170
171 // If we could not parse as any specific type, try a basic STRING.
172 if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
173 StringBuilder string_builder;
174 string_builder.AppendText(*raw_string->value);
175 if (string_builder) {
176 transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
177 }
178 }
179
180 if (transformed) {
181 return transformed;
182 }
183 }
184 return value;
185 }
186
187 namespace {
188
189 class EmptyDeclStack : public xml::IPackageDeclStack {
190 public:
191 EmptyDeclStack() = default;
192
TransformPackageAlias(const StringPiece & alias) const193 Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
194 if (alias.empty()) {
195 return xml::ExtractedPackage{{}, true /*private*/};
196 }
197 return {};
198 }
199
200 private:
201 DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
202 };
203
204 struct MacroDeclStack : public xml::IPackageDeclStack {
MacroDeclStackaapt::__anon61d7ad890211::MacroDeclStack205 explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
206 : alias_namespaces_(std::move(namespaces)) {
207 }
208
TransformPackageAliasaapt::__anon61d7ad890211::MacroDeclStack209 Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
210 if (alias.empty()) {
211 return xml::ExtractedPackage{{}, true /*private*/};
212 }
213 for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
214 if (alias == StringPiece(it->alias)) {
215 return xml::ExtractedPackage{it->package_name, it->is_private};
216 }
217 }
218 return {};
219 }
220
221 private:
222 std::vector<Macro::Namespace> alias_namespaces_;
223 };
224
225 // The symbol is visible if it is public, or if the reference to it is requesting private access
226 // or if the callsite comes from the same package.
IsSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callsite)227 bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
228 const CallSite& callsite) {
229 if (symbol.is_public || ref.private_reference) {
230 return true;
231 }
232
233 if (ref.name) {
234 const ResourceName& name = ref.name.value();
235 if (name.package.empty()) {
236 // If the symbol was found, and the package is empty, that means it was found in the local
237 // scope, which is always visible (private local).
238 return true;
239 }
240
241 // The symbol is visible if the reference is local to the same package it is defined in.
242 return callsite.package == name.package;
243 }
244
245 if (ref.id && symbol.id) {
246 return ref.id.value().package_id() == symbol.id.value().package_id();
247 }
248 return false;
249 }
250
251 } // namespace
252
ResolveSymbol(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols)253 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
254 const CallSite& callsite,
255 IAaptContext* context,
256 SymbolTable* symbols) {
257 if (reference.name) {
258 const ResourceName& name = reference.name.value();
259 if (name.package.empty()) {
260 // Use the callsite's package name if no package name was defined.
261 const SymbolTable::Symbol* symbol = symbols->FindByName(
262 ResourceName(callsite.package, name.type, name.entry));
263 if (symbol) {
264 return symbol;
265 }
266
267 // If the callsite package is the same as the current compilation package,
268 // check the feature split dependencies as well. Feature split resources
269 // can be referenced without a namespace, just like the base package.
270 if (callsite.package == context->GetCompilationPackage()) {
271 const auto& split_name_dependencies = context->GetSplitNameDependencies();
272 for (const std::string& split_name : split_name_dependencies) {
273 std::string split_package =
274 StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str());
275 symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry));
276 if (symbol) {
277 return symbol;
278 }
279 }
280 }
281 return nullptr;
282 }
283 return symbols->FindByName(name);
284 } else if (reference.id) {
285 return symbols->FindById(reference.id.value());
286 } else {
287 return nullptr;
288 }
289 }
290
ResolveSymbolCheckVisibility(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)291 const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
292 const CallSite& callsite,
293 IAaptContext* context,
294 SymbolTable* symbols,
295 std::string* out_error) {
296 const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols);
297 if (!symbol) {
298 if (out_error) *out_error = "not found";
299 return nullptr;
300 }
301
302 if (!IsSymbolVisible(*symbol, reference, callsite)) {
303 if (out_error) *out_error = "is private";
304 return nullptr;
305 }
306 return symbol;
307 }
308
ResolveAttributeCheckVisibility(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)309 const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
310 const Reference& reference, const CallSite& callsite, IAaptContext* context,
311 SymbolTable* symbols, std::string* out_error) {
312 const SymbolTable::Symbol* symbol =
313 ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error);
314 if (!symbol) {
315 return nullptr;
316 }
317
318 if (!symbol->attribute) {
319 if (out_error) *out_error = "is not an attribute";
320 return nullptr;
321 }
322 return symbol;
323 }
324
CompileXmlAttribute(const Reference & reference,const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,std::string * out_error)325 Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
326 const CallSite& callsite,
327 IAaptContext* context,
328 SymbolTable* symbols,
329 std::string* out_error) {
330 const SymbolTable::Symbol* symbol =
331 ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
332 if (!symbol) {
333 return {};
334 }
335
336 if (!symbol->attribute) {
337 if (out_error) *out_error = "is not an attribute";
338 return {};
339 }
340 return xml::AaptAttribute(*symbol->attribute, symbol->id);
341 }
342
WriteAttributeName(const Reference & ref,const CallSite & callsite,const xml::IPackageDeclStack * decls,DiagMessage * out_msg)343 void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
344 const xml::IPackageDeclStack* decls,
345 DiagMessage* out_msg) {
346 CHECK(out_msg != nullptr);
347 if (!ref.name) {
348 *out_msg << ref.id.value();
349 return;
350 }
351
352 const ResourceName& ref_name = ref.name.value();
353 CHECK_EQ(ref_name.type, ResourceType::kAttr);
354
355 if (!ref_name.package.empty()) {
356 *out_msg << ref_name.package << ":";
357 }
358 *out_msg << ref_name.entry;
359
360 Reference fully_qualified = ref;
361 xml::ResolvePackage(decls, &fully_qualified);
362
363 ResourceName& full_name = fully_qualified.name.value();
364 if (full_name.package.empty()) {
365 full_name.package = callsite.package;
366 }
367
368 if (full_name != ref.name.value()) {
369 *out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
370 }
371 }
372
LinkReference(const CallSite & callsite,const Reference & reference,IAaptContext * context,SymbolTable * symbols,ResourceTable * table,const xml::IPackageDeclStack * decls)373 std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
374 const Reference& reference,
375 IAaptContext* context, SymbolTable* symbols,
376 ResourceTable* table,
377 const xml::IPackageDeclStack* decls) {
378 if (!reference.name && !reference.id) {
379 // This is @null.
380 return std::make_unique<Reference>(reference);
381 }
382
383 Reference transformed_reference = reference;
384 xml::ResolvePackage(decls, &transformed_reference);
385
386 if (transformed_reference.name.value().type == ResourceType::kMacro) {
387 if (transformed_reference.name.value().package.empty()) {
388 transformed_reference.name.value().package = callsite.package;
389 }
390
391 auto result = table->FindResource(transformed_reference.name.value());
392 if (!result || result.value().entry->values.empty()) {
393 context->GetDiagnostics()->Error(
394 DiagMessage(reference.GetSource())
395 << "failed to find definition for "
396 << LoggingResourceName(transformed_reference, callsite, decls));
397 return {};
398 }
399
400 auto& macro_values = result.value().entry->values;
401 CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
402
403 auto macro = ValueCast<Macro>(macro_values[0]->value.get());
404 CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
405 << *macro_values[0]->value << ")";
406
407 // Re-create the state used to parse the macro tag to compile the macro contents as if it was
408 // defined inline
409 uint32_t type_flags = 0;
410 if (reference.type_flags.has_value()) {
411 type_flags = reference.type_flags.value();
412 }
413
414 MacroDeclStack namespace_stack(macro->alias_namespaces);
415 FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
416 .style_string = macro->style_string,
417 .untranslatable_sections = macro->untranslatable_sections,
418 .namespace_resolver = &namespace_stack,
419 .source = macro->GetSource()};
420
421 auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
422 macro_values[0]->config, *context->GetDiagnostics());
423 if (new_value == nullptr) {
424 context->GetDiagnostics()->Error(
425 DiagMessage(reference.GetSource())
426 << "failed to substitute macro "
427 << LoggingResourceName(transformed_reference, callsite, decls)
428 << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
429 return {};
430 }
431
432 if (auto ref = ValueCast<Reference>(new_value.get())) {
433 return LinkReference(callsite, *ref, context, symbols, table, decls);
434 }
435 return new_value;
436 }
437
438 std::string err_str;
439 const SymbolTable::Symbol* s =
440 ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
441 if (s) {
442 // The ID may not exist. This is fine because of the possibility of building
443 // against libraries without assigned IDs.
444 // Ex: Linking against own resources when building a static library.
445 auto new_ref = std::make_unique<Reference>(reference);
446 new_ref->id = s->id;
447 new_ref->is_dynamic = s->is_dynamic;
448 return std::move(new_ref);
449 }
450
451 context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
452 << "resource "
453 << LoggingResourceName(transformed_reference, callsite, decls)
454 << " " << err_str);
455 return {};
456 }
457
Consume(IAaptContext * context,ResourceTable * table)458 bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
459 TRACE_NAME("ReferenceLinker::Consume");
460 EmptyDeclStack decl_stack;
461 bool error = false;
462 for (auto& package : table->packages) {
463 // Since we're linking, each package must have a name.
464 CHECK(!package->name.empty()) << "all packages being linked must have a name";
465
466 for (auto& type : package->types) {
467 for (auto& entry : type->entries) {
468 // First, unmangle the name if necessary.
469 ResourceName name(package->name, type->type, entry->name);
470 NameMangler::Unmangle(&name.entry, &name.package);
471
472 // Symbol state information may be lost if there is no value for the resource.
473 if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
474 context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
475 << "no definition for declared symbol '" << name
476 << "'");
477 error = true;
478 }
479
480 // Ensure that definitions for values declared as overlayable exist
481 if (entry->overlayable_item && entry->values.empty()) {
482 context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_item.value().source)
483 << "no definition for overlayable symbol '"
484 << name << "'");
485 error = true;
486 }
487
488 // The context of this resource is the package in which it is defined.
489 const CallSite callsite{name.package};
490 ReferenceLinkerTransformer reference_transformer(callsite, context,
491 context->GetExternalSymbols(),
492 &table->string_pool, table, &decl_stack);
493
494 for (auto& config_value : entry->values) {
495 config_value->value = config_value->value->Transform(reference_transformer);
496 }
497
498 if (reference_transformer.HasError()) {
499 error = true;
500 }
501 }
502 }
503 }
504 return !error;
505 }
506
507 } // namespace aapt
508