blob: 8c924b5b0f99bf95932a6108856ceb85eafcd958 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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 "ResourceTable.h"
19#include "ResourceUtils.h"
20#include "ResourceValues.h"
21#include "util/Util.h"
22#include "ValueVisitor.h"
23
24#include "link/Linkers.h"
25#include "link/ReferenceLinkerVisitor.h"
26#include "process/IResourceTableConsumer.h"
27#include "process/SymbolTable.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 StyleAndReferenceLinkerVisitor : public ValueVisitor {
45private:
46 ReferenceLinkerVisitor mReferenceVisitor;
47 IAaptContext* mContext;
48 ISymbolTable* mSymbols;
49 IPackageDeclStack* mPackageDecls;
50 StringPool* mStringPool;
51 bool mError = false;
52
53 const ISymbolTable::Symbol* findAttributeSymbol(Reference* reference) {
54 assert(reference);
55 assert(reference->name || reference->id);
56
57 if (reference->name) {
58 // Transform the package name if it is an alias.
59 Maybe<ResourceName> realName = mPackageDecls->transformPackage(
60 reference->name.value(), mContext->getCompilationPackage());
61
62 // Mangle the reference name if it should be mangled.
63 Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
64 realName ? realName.value() : reference->name.value());
65
66 const ISymbolTable::Symbol* s = nullptr;
67 if (mangledName) {
68 s = mSymbols->findByName(mangledName.value());
69 } else if (realName) {
70 s = mSymbols->findByName(realName.value());
71 } else {
72 s = mSymbols->findByName(reference->name.value());
73 }
74
75 if (s && s->attribute) {
76 return s;
77 }
78 }
79
80 if (reference->id) {
81 if (const ISymbolTable::Symbol* s = mSymbols->findById(reference->id.value())) {
82 if (s->attribute) {
83 return s;
84 }
85 }
86 }
87 return nullptr;
88 }
89
90 /**
91 * Transform a RawString value into a more specific, appropriate value, based on the
92 * Attribute. If a non RawString value is passed in, this is an identity transform.
93 */
94 std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
95 const Attribute* attr) {
96 if (RawString* rawString = valueCast<RawString>(value.get())) {
97 std::unique_ptr<Item> transformed = ResourceUtils::parseItemForAttribute(
98 *rawString->value, attr);
99
100 // If we could not parse as any specific type, try a basic STRING.
101 if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
102 util::StringBuilder stringBuilder;
103 stringBuilder.append(*rawString->value);
104 if (stringBuilder) {
105 transformed = util::make_unique<String>(
106 mStringPool->makeRef(stringBuilder.str()));
107 }
108 }
109
110 if (transformed) {
111 return transformed;
112 }
113 };
114 return value;
115 }
116
117 void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
118 const Item* value) {
119 *msg << "expected";
120 if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
121 *msg << " boolean";
122 }
123
124 if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
125 *msg << " color";
126 }
127
128 if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
129 *msg << " dimension";
130 }
131
132 if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
133 *msg << " enum";
134 }
135
136 if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
137 *msg << " flags";
138 }
139
140 if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
141 *msg << " float";
142 }
143
144 if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
145 *msg << " fraction";
146 }
147
148 if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
149 *msg << " integer";
150 }
151
152 if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
153 *msg << " reference";
154 }
155
156 if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
157 *msg << " string";
158 }
159
160 *msg << " but got " << *value;
161 }
162
163public:
164 using ValueVisitor::visit;
165
166 StyleAndReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols,
167 StringPool* stringPool, IPackageDeclStack* decl) :
168 mReferenceVisitor(context, symbols, decl), mContext(context), mSymbols(symbols),
169 mPackageDecls(decl), mStringPool(stringPool) {
170 }
171
172 void visit(Reference* reference) override {
173 mReferenceVisitor.visit(reference);
174 }
175
176 /**
177 * We visit the Style specially because during this phase, values of attributes are
178 * all RawString values. Now that we are expected to resolve all symbols, we can
179 * lookup the attributes to find out which types are allowed for the attributes' values.
180 */
181 void visit(Style* style) override {
182 if (style->parent) {
183 visit(&style->parent.value());
184 }
185
186 for (Style::Entry& entry : style->entries) {
187 if (const ISymbolTable::Symbol* s = findAttributeSymbol(&entry.key)) {
188 // Assign our style key the correct ID.
189 entry.key.id = s->id;
190
191 // Try to convert the value to a more specific, typed value based on the
192 // attribute it is set to.
193 entry.value = parseValueWithAttribute(std::move(entry.value), s->attribute.get());
194
195 // Link/resolve the final value (mostly if it's a reference).
196 entry.value->accept(this);
197
198 // Now verify that the type of this item is compatible with the attribute it
199 // is defined for.
200 android::Res_value val = {};
201 entry.value->flatten(&val);
202
203 // Always allow references.
204 const uint32_t typeMask = s->attribute->typeMask |
205 android::ResTable_map::TYPE_REFERENCE;
206
207 if (!(typeMask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
208 // The actual type of this item is incompatible with the attribute.
Adam Lesinskie78fd612015-10-22 12:48:43 -0700209 DiagMessage msg(style->getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700210 buildAttributeMismatchMessage(&msg, s->attribute.get(), entry.value.get());
211 mContext->getDiagnostics()->error(msg);
212 mError = true;
213 }
214 } else {
Adam Lesinskie78fd612015-10-22 12:48:43 -0700215 DiagMessage msg(style->getSource());
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700216 msg << "style attribute '";
217 if (entry.key.name) {
218 msg << entry.key.name.value().package << ":" << entry.key.name.value().entry;
219 } else {
220 msg << entry.key.id.value();
221 }
222 msg << "' not found";
223 mContext->getDiagnostics()->error(msg);
224 mError = true;
225 }
226 }
227 }
228
229 inline bool hasError() {
230 return mError || mReferenceVisitor.hasError();
231 }
232};
233
234struct EmptyDeclStack : public IPackageDeclStack {
235 Maybe<ResourceName> transformPackage(const ResourceName& name,
236 const StringPiece16& localPackage) const override {
237 if (name.package.empty()) {
238 return ResourceName{ localPackage.toString(), name.type, name.entry };
239 }
240 return {};
241 }
242};
243
244} // namespace
245
246bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
247 EmptyDeclStack declStack;
248 bool error = false;
249 for (auto& package : table->packages) {
250 for (auto& type : package->types) {
251 for (auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700252 // Symbol state information may be lost if there is no value for the resource.
253 if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
254 context->getDiagnostics()->error(
255 DiagMessage(entry->symbolStatus.source)
256 << "no definition for declared symbol '"
257 << ResourceNameRef(package->name, type->type, entry->name)
258 << "'");
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700259 error = true;
260 }
261
262 for (auto& configValue : entry->values) {
263 StyleAndReferenceLinkerVisitor visitor(context,
264 context->getExternalSymbols(),
265 &table->stringPool, &declStack);
266 configValue.value->accept(&visitor);
267 if (visitor.hasError()) {
268 error = true;
269 }
270 }
271 }
272 }
273 }
274 return !error;
275}
276
277} // namespace aapt