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
ReferenceLinkerVisitor(const CallSite & callsite,IAaptContext * context,SymbolTable * symbols,StringPool * string_pool,xml::IPackageDeclStack * decl)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
Visit(Reference * ref)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 */
Visit(Style * style)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
HasError()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 */
ParseValueWithAttribute(std::unique_ptr<Item> value,const Attribute * attr)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
TransformPackageAlias(const StringPiece & alias,const StringPiece & local_package) const184 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 */
IsSymbolVisible(const SymbolTable::Symbol & symbol,const Reference & ref,const CallSite & callsite)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
ResolveSymbol(const Reference & reference,SymbolTable * symbols)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
ResolveSymbolCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)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
ResolveAttributeCheckVisibility(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)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
CompileXmlAttribute(const Reference & reference,const CallSite & callsite,SymbolTable * symbols,std::string * out_error)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
WriteResourceName(DiagMessage * out_msg,const Reference & orig,const Reference & transformed)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
LinkReference(const CallSite & callsite,Reference * reference,IAaptContext * context,SymbolTable * symbols,xml::IPackageDeclStack * decls)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
Consume(IAaptContext * context,ResourceTable * table)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