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 "Diagnostics.h"
18#include "ReferenceLinker.h"
19#include "ResourceTable.h"
20#include "ResourceUtils.h"
21#include "ResourceValues.h"
22#include "ValueVisitor.h"
23#include "link/Linkers.h"
24#include "process/IResourceTableConsumer.h"
25#include "process/SymbolTable.h"
26#include "util/Util.h"
27#include "xml/XmlUtil.h"
28
29#include <androidfw/ResourceTypes.h>
30#include <cassert>
31
32namespace aapt {
33
34namespace {
35
36/**
37 * The ReferenceLinkerVisitor will follow all references and make sure they point
38 * to resources that actually exist, either in the local resource table, or as external
39 * symbols. Once the target resource has been found, the ID of the resource will be assigned
40 * to the reference object.
41 *
42 * NOTE: All of the entries in the ResourceTable must be assigned IDs.
43 */
44class ReferenceLinkerVisitor : public ValueVisitor {
45public:
46    using ValueVisitor::visit;
47
48    ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
49                           xml::IPackageDeclStack* decl,CallSite* callSite) :
50            mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
51            mCallSite(callSite) {
52    }
53
54    void visit(Reference* ref) override {
55        if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
56            mError = true;
57        }
58    }
59
60    /**
61     * We visit the Style specially because during this phase, values of attributes are
62     * all RawString values. Now that we are expected to resolve all symbols, we can
63     * lookup the attributes to find out which types are allowed for the attributes' values.
64     */
65    void visit(Style* style) override {
66        if (style->parent) {
67            visit(&style->parent.value());
68        }
69
70        for (Style::Entry& entry : style->entries) {
71            std::string errStr;
72
73            // Transform the attribute reference so that it is using the fully qualified package
74            // name. This will also mark the reference as being able to see private resources if
75            // there was a '*' in the reference or if the package came from the private namespace.
76            Reference transformedReference = entry.key;
77            transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
78                                            &transformedReference);
79
80            // Find the attribute in the symbol table and check if it is visible from this callsite.
81            const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
82                    transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
83            if (symbol) {
84                // Assign our style key the correct ID.
85                // The ID may not exist.
86                entry.key.id = symbol->id;
87
88                // Try to convert the value to a more specific, typed value based on the
89                // attribute it is set to.
90                entry.value = parseValueWithAttribute(std::move(entry.value),
91                                                      symbol->attribute.get());
92
93                // Link/resolve the final value (mostly if it's a reference).
94                entry.value->accept(this);
95
96                // Now verify that the type of this item is compatible with the attribute it
97                // is defined for. We pass `nullptr` as the DiagMessage so that this check is
98                // fast and we avoid creating a DiagMessage when the match is successful.
99                if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
100                    // The actual type of this item is incompatible with the attribute.
101                    DiagMessage msg(entry.key.getSource());
102
103                    // Call the matches method again, this time with a DiagMessage so we fill
104                    // in the actual error message.
105                    symbol->attribute->matches(entry.value.get(), &msg);
106                    mContext->getDiagnostics()->error(msg);
107                    mError = true;
108                }
109
110            } else {
111                DiagMessage msg(entry.key.getSource());
112                msg << "style attribute '";
113                ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
114                msg << "' " << errStr;
115                mContext->getDiagnostics()->error(msg);
116                mError = true;
117            }
118        }
119    }
120
121    bool hasError() {
122        return mError;
123    }
124
125private:
126    IAaptContext* mContext;
127    SymbolTable* mSymbols;
128    xml::IPackageDeclStack* mPackageDecls;
129    StringPool* mStringPool;
130    CallSite* mCallSite;
131    bool mError = false;
132
133    /**
134     * Transform a RawString value into a more specific, appropriate value, based on the
135     * Attribute. If a non RawString value is passed in, this is an identity transform.
136     */
137    std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
138                                                  const Attribute* attr) {
139        if (RawString* rawString = valueCast<RawString>(value.get())) {
140            std::unique_ptr<Item> transformed =
141                    ResourceUtils::parseItemForAttribute(*rawString->value, attr);
142
143            // If we could not parse as any specific type, try a basic STRING.
144            if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
145                util::StringBuilder stringBuilder;
146                stringBuilder.append(*rawString->value);
147                if (stringBuilder) {
148                    transformed = util::make_unique<String>(
149                            mStringPool->makeRef(stringBuilder.str()));
150                }
151            }
152
153            if (transformed) {
154                return transformed;
155            }
156        };
157        return value;
158    }
159};
160
161} // namespace
162
163/**
164 * The symbol is visible if it is public, or if the reference to it is requesting private access
165 * or if the callsite comes from the same package.
166 */
167bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
168                                      const CallSite& callSite) {
169    if (!symbol.isPublic && !ref.privateReference) {
170        if (ref.name) {
171            return callSite.resource.package == ref.name.value().package;
172        } else if (ref.id && symbol.id) {
173            return ref.id.value().packageId() == symbol.id.value().packageId();
174        } else {
175            return false;
176        }
177    }
178    return true;
179}
180
181const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
182                                                          NameMangler* mangler,
183                                                          SymbolTable* symbols) {
184    if (reference.name) {
185        Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
186        return symbols->findByName(mangled ? mangled.value() : reference.name.value());
187    } else if (reference.id) {
188        return symbols->findById(reference.id.value());
189    } else {
190        return nullptr;
191    }
192}
193
194const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
195        const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
196        CallSite* callSite, std::string* outError) {
197    const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
198    if (!symbol) {
199        if (outError) *outError = "not found";
200        return nullptr;
201    }
202
203    if (!isSymbolVisible(*symbol, reference, *callSite)) {
204        if (outError) *outError = "is private";
205        return nullptr;
206    }
207    return symbol;
208}
209
210const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
211        const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
212        CallSite* callSite, std::string* outError) {
213    const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
214                                                                     symbols, callSite,
215                                                                     outError);
216    if (!symbol) {
217        return nullptr;
218    }
219
220    if (!symbol->attribute) {
221        if (outError) *outError = "is not an attribute";
222        return nullptr;
223    }
224    return symbol;
225}
226
227Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
228                                                               NameMangler* nameMangler,
229                                                               SymbolTable* symbols,
230                                                               CallSite* callSite,
231                                                               std::string* outError) {
232    const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
233    if (!symbol) {
234        return {};
235    }
236
237    if (!symbol->attribute) {
238        if (outError) *outError = "is not an attribute";
239        return {};
240    }
241    return xml::AaptAttribute{ symbol->id, *symbol->attribute };
242}
243
244void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
245                                        const Reference& transformed) {
246    assert(outMsg);
247
248    if (orig.name) {
249        *outMsg << orig.name.value();
250        if (transformed.name.value() != orig.name.value()) {
251            *outMsg << " (aka " << transformed.name.value() << ")";
252        }
253    } else {
254        *outMsg << orig.id.value();
255    }
256}
257
258bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
259                                    SymbolTable* symbols, xml::IPackageDeclStack* decls,
260                                    CallSite* callSite) {
261    assert(reference);
262    assert(reference->name || reference->id);
263
264    Reference transformedReference = *reference;
265    transformReferenceFromNamespace(decls, context->getCompilationPackage(),
266                                    &transformedReference);
267
268    std::string errStr;
269    const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
270            transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
271    if (s) {
272        // The ID may not exist. This is fine because of the possibility of building against
273        // libraries without assigned IDs.
274        // Ex: Linking against own resources when building a static library.
275        reference->id = s->id;
276        return true;
277    }
278
279    DiagMessage errorMsg(reference->getSource());
280    errorMsg << "resource ";
281    writeResourceName(&errorMsg, *reference, transformedReference);
282    errorMsg << " " << errStr;
283    context->getDiagnostics()->error(errorMsg);
284    return false;
285}
286
287namespace {
288
289struct EmptyDeclStack : public xml::IPackageDeclStack {
290    Maybe<xml::ExtractedPackage> transformPackageAlias(
291            const StringPiece16& alias, const StringPiece16& localPackage) const override {
292        if (alias.empty()) {
293            return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
294        }
295        return {};
296    }
297};
298
299} // namespace
300
301bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
302    EmptyDeclStack declStack;
303    bool error = false;
304    for (auto& package : table->packages) {
305        for (auto& type : package->types) {
306            for (auto& entry : type->entries) {
307                // Symbol state information may be lost if there is no value for the resource.
308                if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
309                    context->getDiagnostics()->error(
310                            DiagMessage(entry->symbolStatus.source)
311                            << "no definition for declared symbol '"
312                            << ResourceNameRef(package->name, type->type, entry->name)
313                            << "'");
314                    error = true;
315                }
316
317                CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
318                ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
319                                               &table->stringPool, &declStack, &callSite);
320
321                for (auto& configValue : entry->values) {
322                    configValue->value->accept(&visitor);
323                }
324
325                if (visitor.hasError()) {
326                    error = true;
327                }
328            }
329        }
330    }
331    return !error;
332}
333
334} // namespace aapt
335