blob: 810633f7d10fc9ae5cf4ee0cd3505d17d8623a98 [file] [log] [blame]
Andreas Huber1aec3972016-08-26 09:26:32 -07001/*
2 * Copyright (C) 2016 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
Andreas Huberc9410c72016-07-28 12:18:40 -070017#include "AST.h"
18
Andreas Huber5345ec22016-07-29 13:33:27 -070019#include "Coordinator.h"
Yifan Hongf24fa852016-09-23 11:03:15 -070020#include "EnumType.h"
Andreas Huber84f89de2016-07-28 15:39:51 -070021#include "FQName.h"
Andreas Hubereb1081f2016-07-28 13:13:24 -070022#include "HandleType.h"
Andreas Huberbfd76212016-08-09 11:12:16 -070023#include "Interface.h"
Andreas Huber4b2cf352016-08-31 13:58:19 -070024#include "PredefinedType.h"
Andreas Huberc9410c72016-07-28 12:18:40 -070025#include "Scope.h"
Andreas Huber8d3ac0c2016-08-04 14:49:23 -070026#include "TypeDef.h"
Andreas Huberc9410c72016-07-28 12:18:40 -070027
Iliyan Malcheva72e0d22016-09-09 11:03:08 -070028#include <hidl-util/Formatter.h>
Andreas Hubereb1081f2016-07-28 13:13:24 -070029#include <android-base/logging.h>
Andreas Huber39fa7182016-08-19 14:27:33 -070030#include <iostream>
Andreas Huberc9410c72016-07-28 12:18:40 -070031#include <stdlib.h>
32
Andreas Huberc9410c72016-07-28 12:18:40 -070033namespace android {
34
Andreas Huber0d0f9a22016-08-17 10:26:11 -070035AST::AST(Coordinator *coordinator, const std::string &path)
Andreas Huber5345ec22016-07-29 13:33:27 -070036 : mCoordinator(coordinator),
Andreas Huber0d0f9a22016-08-17 10:26:11 -070037 mPath(path),
Andreas Huber5345ec22016-07-29 13:33:27 -070038 mScanner(NULL),
Andreas Huber9ed827c2016-08-22 12:31:13 -070039 mRootScope(new Scope("" /* localName */)) {
Andreas Huberc9410c72016-07-28 12:18:40 -070040 enterScope(mRootScope);
41}
42
43AST::~AST() {
Andreas Huberc9410c72016-07-28 12:18:40 -070044 delete mRootScope;
Yifan Hong1977ea32016-10-05 12:49:08 -070045 mRootScope = nullptr;
Andreas Huberc9410c72016-07-28 12:18:40 -070046
Andreas Hubereb1081f2016-07-28 13:13:24 -070047 CHECK(mScanner == NULL);
Andreas Huberc9410c72016-07-28 12:18:40 -070048
Andreas Huber5345ec22016-07-29 13:33:27 -070049 // Ownership of "coordinator" was NOT transferred.
Andreas Huberc9410c72016-07-28 12:18:40 -070050}
51
52void *AST::scanner() {
53 return mScanner;
54}
55
56void AST::setScanner(void *scanner) {
57 mScanner = scanner;
58}
59
Andreas Huber0d0f9a22016-08-17 10:26:11 -070060const std::string &AST::getFilename() const {
61 return mPath;
62}
63
Andreas Huber84f89de2016-07-28 15:39:51 -070064bool AST::setPackage(const char *package) {
Andreas Huberda51b8e2016-07-28 16:00:57 -070065 mPackage.setTo(package);
66 CHECK(mPackage.isValid());
Andreas Huber84f89de2016-07-28 15:39:51 -070067
Andreas Huberda51b8e2016-07-28 16:00:57 -070068 if (mPackage.package().empty()
69 || mPackage.version().empty()
70 || !mPackage.name().empty()) {
Andreas Huber84f89de2016-07-28 15:39:51 -070071 return false;
72 }
73
Andreas Huber84f89de2016-07-28 15:39:51 -070074 return true;
Andreas Hubereb1081f2016-07-28 13:13:24 -070075}
76
Andreas Hubera2723d22016-07-29 15:36:07 -070077FQName AST::package() const {
78 return mPackage;
79}
80
81bool AST::isInterface(std::string *ifaceName) const {
82 return mRootScope->containsSingleInterface(ifaceName);
83}
84
Andreas Huber7c5ddfb2016-09-29 13:45:22 -070085bool AST::containsInterfaces() const {
86 return mRootScope->containsInterfaces();
87}
88
Andreas Huber5345ec22016-07-29 13:33:27 -070089bool AST::addImport(const char *import) {
90 FQName fqName(import);
91 CHECK(fqName.isValid());
Andreas Hubereb1081f2016-07-28 13:13:24 -070092
Andreas Huber5345ec22016-07-29 13:33:27 -070093 fqName.applyDefaults(mPackage.package(), mPackage.version());
94
Andreas Huber68f24592016-07-29 14:53:48 -070095 // LOG(INFO) << "importing " << fqName.string();
Andreas Huber5345ec22016-07-29 13:33:27 -070096
97 if (fqName.name().empty()) {
Yifan Hong1977ea32016-10-05 12:49:08 -070098 // import a package
Andreas Huberd2943e12016-08-05 11:59:31 -070099 std::vector<FQName> packageInterfaces;
Andreas Huber68f24592016-07-29 14:53:48 -0700100
Andreas Huberd2943e12016-08-05 11:59:31 -0700101 status_t err =
Steven Morelandaa186832016-09-26 13:51:43 -0700102 mCoordinator->appendPackageInterfacesToVector(fqName,
103 &packageInterfaces);
Andreas Huberd2943e12016-08-05 11:59:31 -0700104
105 if (err != OK) {
Andreas Huber68f24592016-07-29 14:53:48 -0700106 return false;
107 }
108
Andreas Huberd2943e12016-08-05 11:59:31 -0700109 for (const auto &subFQName : packageInterfaces) {
Andreas Huber39fa7182016-08-19 14:27:33 -0700110 AST *ast = mCoordinator->parse(subFQName, &mImportedASTs);
Yifan Hong1977ea32016-10-05 12:49:08 -0700111 if (ast == nullptr) {
Andreas Huber68f24592016-07-29 14:53:48 -0700112 return false;
113 }
Yifan Hong1977ea32016-10-05 12:49:08 -0700114 // all previous single type imports are ignored.
115 mImportedTypes.erase(ast);
Andreas Huber68f24592016-07-29 14:53:48 -0700116 }
117
118 return true;
Andreas Huber5345ec22016-07-29 13:33:27 -0700119 }
120
Yifan Hong1977ea32016-10-05 12:49:08 -0700121 AST *importAST;
Andreas Huber5345ec22016-07-29 13:33:27 -0700122
Yifan Hong1977ea32016-10-05 12:49:08 -0700123 // cases like android.hardware.foo@1.0::IFoo.Internal
124 // android.hardware.foo@1.0::Abc.Internal
125
126 // assume it is an interface, and try to import it.
127 const FQName interfaceName = fqName.getTopLevelType();
128 importAST = mCoordinator->parse(interfaceName, &mImportedASTs);
129
130 if (importAST != nullptr) {
131 // cases like android.hardware.foo@1.0::IFoo.Internal
132 // and android.hardware.foo@1.0::IFoo
133 if (fqName == interfaceName) {
134 // import a single file.
135 // all previous single type imports are ignored.
136 // cases like android.hardware.foo@1.0::IFoo
137 // and android.hardware.foo@1.0::types
138 mImportedTypes.erase(importAST);
139 return true;
140 }
141
142 // import a single type from this file
143 // cases like android.hardware.foo@1.0::IFoo.Internal
144 FQName matchingName;
145 Type *match = importAST->findDefinedType(fqName, &matchingName);
146 if (match == nullptr) {
147 return false;
148 }
149 // will automatically create a set if it does not exist
150 mImportedTypes[importAST].insert(match);
151 return true;
Andreas Huber68f24592016-07-29 14:53:48 -0700152 }
Andreas Huber84f89de2016-07-28 15:39:51 -0700153
Yifan Hong1977ea32016-10-05 12:49:08 -0700154 // probably a type in types.hal, like android.hardware.foo@1.0::Abc.Internal
155 FQName typesFQName = fqName.getTypesForPackage();
156 importAST = mCoordinator->parse(typesFQName, &mImportedASTs);
157
158 if (importAST != nullptr) {
159 // Attempt to find Abc.Internal in types.
160 FQName matchingName;
161 Type *match = importAST->findDefinedType(fqName, &matchingName);
162 if (match == nullptr) {
163 return false;
164 }
165 // will automatically create a set if not exist
166 mImportedTypes[importAST].insert(match);
167 return true;
168 }
169
170 // can't find an appropriate AST for fqName.
171 return false;
Andreas Hubereb1081f2016-07-28 13:13:24 -0700172}
173
Andreas Huber39fa7182016-08-19 14:27:33 -0700174void AST::addImportedAST(AST *ast) {
175 mImportedASTs.insert(ast);
176}
177
Andreas Huberc9410c72016-07-28 12:18:40 -0700178void AST::enterScope(Scope *container) {
179 mScopePath.push_back(container);
180}
181
182void AST::leaveScope() {
Steven Morelandd537ab02016-09-12 10:32:01 -0700183 mScopePath.pop_back();
Andreas Huberc9410c72016-07-28 12:18:40 -0700184}
185
186Scope *AST::scope() {
Andreas Hubereb1081f2016-07-28 13:13:24 -0700187 CHECK(!mScopePath.empty());
Steven Morelandd537ab02016-09-12 10:32:01 -0700188 return mScopePath.back();
Andreas Huberc9410c72016-07-28 12:18:40 -0700189}
190
Andreas Huber39fa7182016-08-19 14:27:33 -0700191bool AST::addTypeDef(
192 const char *localName, Type *type, std::string *errorMsg) {
193 // The reason we wrap the given type in a TypeDef is simply to suppress
194 // emitting any type definitions later on, since this is just an alias
195 // to a type defined elsewhere.
196 return addScopedTypeInternal(
Steven Morelandd537ab02016-09-12 10:32:01 -0700197 new TypeDef(localName, type), errorMsg);
Andreas Huber39fa7182016-08-19 14:27:33 -0700198}
199
Andreas Huber9ed827c2016-08-22 12:31:13 -0700200bool AST::addScopedType(NamedType *type, std::string *errorMsg) {
Andreas Huber39fa7182016-08-19 14:27:33 -0700201 return addScopedTypeInternal(
Steven Morelandd537ab02016-09-12 10:32:01 -0700202 type, errorMsg);
Andreas Huber39fa7182016-08-19 14:27:33 -0700203}
204
205bool AST::addScopedTypeInternal(
Steven Morelandd537ab02016-09-12 10:32:01 -0700206 NamedType *type,
207 std::string *errorMsg) {
Andreas Huber39fa7182016-08-19 14:27:33 -0700208
Steven Morelandd537ab02016-09-12 10:32:01 -0700209 bool success = scope()->addType(type, errorMsg);
Andreas Huber5a545442016-08-03 10:44:56 -0700210 if (!success) {
211 return false;
212 }
213
Andreas Huber31629bc2016-08-03 09:06:40 -0700214 std::string path;
215 for (size_t i = 1; i < mScopePath.size(); ++i) {
Andreas Huber0e00de42016-08-03 09:56:02 -0700216 path.append(mScopePath[i]->localName());
Andreas Huber31629bc2016-08-03 09:06:40 -0700217 path.append(".");
218 }
Steven Morelandd537ab02016-09-12 10:32:01 -0700219 path.append(type->localName());
Andreas Huber31629bc2016-08-03 09:06:40 -0700220
Andreas Huber31629bc2016-08-03 09:06:40 -0700221 FQName fqName(mPackage.package(), mPackage.version(), path);
Andreas Huber39fa7182016-08-19 14:27:33 -0700222
Steven Morelandd537ab02016-09-12 10:32:01 -0700223 type->setFullName(fqName);
Andreas Huber39fa7182016-08-19 14:27:33 -0700224
Steven Morelandd537ab02016-09-12 10:32:01 -0700225 mDefinedTypesByFullName[fqName] = type;
Andreas Huber31629bc2016-08-03 09:06:40 -0700226
Andreas Huber5a545442016-08-03 10:44:56 -0700227 return true;
Andreas Huber31629bc2016-08-03 09:06:40 -0700228}
229
Yifan Hongf24fa852016-09-23 11:03:15 -0700230EnumValue *AST::lookupEnumValue(const FQName &fqName, std::string *errorMsg) {
231
232 FQName enumTypeName = fqName.typeName();
233 std::string enumValueName = fqName.valueName();
234
235 CHECK(enumTypeName.isValid());
236 CHECK(!enumValueName.empty());
237
238 Type *type = lookupType(enumTypeName);
239 if(type == nullptr) {
240 *errorMsg = "Cannot find type " + enumTypeName.string();
241 return nullptr;
242 }
243 if(!type->isEnum()) {
244 *errorMsg = "Type " + enumTypeName.string() + " is not an enum type";
245 return nullptr;
246 }
247
248 EnumType *enumType = static_cast<EnumType *>(type);
249 EnumValue *v = static_cast<EnumValue *>(enumType->lookupIdentifier(enumValueName));
250 if(v == nullptr) {
251 *errorMsg = "Enum type " + enumTypeName.string() + " does not have " + enumValueName;
252 return nullptr;
253 }
254 return v;
255}
256
Yifan Hongae16eed2016-09-23 13:25:25 -0700257Type *AST::lookupType(const FQName &fqName) {
Andreas Huber84f89de2016-07-28 15:39:51 -0700258 CHECK(fqName.isValid());
259
Andreas Huberda51b8e2016-07-28 16:00:57 -0700260 if (fqName.name().empty()) {
261 // Given a package and version???
Yifan Hong1977ea32016-10-05 12:49:08 -0700262 return nullptr;
Andreas Huberda51b8e2016-07-28 16:00:57 -0700263 }
264
Andreas Huber84f89de2016-07-28 15:39:51 -0700265 if (fqName.package().empty() && fqName.version().empty()) {
266 // This is just a plain identifier, resolve locally first if possible.
267
268 for (size_t i = mScopePath.size(); i-- > 0;) {
Yifan Hongae16eed2016-09-23 13:25:25 -0700269 Type *type = mScopePath[i]->lookupType(fqName);
Andreas Huber84f89de2016-07-28 15:39:51 -0700270
Yifan Hong1977ea32016-10-05 12:49:08 -0700271 if (type != nullptr) {
Andreas Huber8d3ac0c2016-08-04 14:49:23 -0700272 // Resolve typeDefs to the target type.
273 while (type->isTypeDef()) {
274 type = static_cast<TypeDef *>(type)->referencedType();
275 }
276
Steven Morelandbb5c80b2016-10-05 11:07:36 -0700277 return type;
Andreas Huber84f89de2016-07-28 15:39:51 -0700278 }
Andreas Huberc9410c72016-07-28 12:18:40 -0700279 }
280 }
281
Andreas Huber39fa7182016-08-19 14:27:33 -0700282 Type *resolvedType = nullptr;
Iliyan Malchev800273d2016-09-02 15:25:07 -0700283 Type *returnedType = nullptr;
Andreas Huber39fa7182016-08-19 14:27:33 -0700284 FQName resolvedName;
Andreas Huber84f89de2016-07-28 15:39:51 -0700285
Andreas Huber39fa7182016-08-19 14:27:33 -0700286 for (const auto &importedAST : mImportedASTs) {
Yifan Hong1977ea32016-10-05 12:49:08 -0700287 if (mImportedTypes.find(importedAST) != mImportedTypes.end()) {
288 // ignore single type imports
289 continue;
290 }
Andreas Huber39fa7182016-08-19 14:27:33 -0700291 FQName matchingName;
292 Type *match = importedAST->findDefinedType(fqName, &matchingName);
Andreas Huber84f89de2016-07-28 15:39:51 -0700293
Andreas Huber39fa7182016-08-19 14:27:33 -0700294 if (match != nullptr) {
295 if (resolvedType != nullptr) {
296 std::cerr << "ERROR: Unable to resolve type name '"
297 << fqName.string()
298 << "', multiple matches found:\n";
Andreas Huber737080b2016-08-02 15:38:04 -0700299
Andreas Huber39fa7182016-08-19 14:27:33 -0700300 std::cerr << " " << resolvedName.string() << "\n";
301 std::cerr << " " << matchingName.string() << "\n";
302
Yifan Hong1977ea32016-10-05 12:49:08 -0700303 return nullptr;
304 }
305
306 resolvedType = match;
307 returnedType = resolvedType;
308 resolvedName = matchingName;
309
310 // Keep going even after finding a match.
311 }
312 }
313
314 for (const auto &pair : mImportedTypes) {
315 AST *importedAST = pair.first;
316 std::set<Type *> importedTypes = pair.second;
317
318 FQName matchingName;
319 Type *match = importedAST->findDefinedType(fqName, &matchingName);
320 if (match != nullptr &&
321 importedTypes.find(match) != importedTypes.end()) {
322 if (resolvedType != nullptr) {
323 std::cerr << "ERROR: Unable to resolve type name '"
324 << fqName.string()
325 << "', multiple matches found:\n";
326
327 std::cerr << " " << resolvedName.string() << "\n";
328 std::cerr << " " << matchingName.string() << "\n";
329
330 return nullptr;
Andreas Huber39fa7182016-08-19 14:27:33 -0700331 }
332
333 resolvedType = match;
Iliyan Malchev800273d2016-09-02 15:25:07 -0700334 returnedType = resolvedType;
Andreas Huber39fa7182016-08-19 14:27:33 -0700335 resolvedName = matchingName;
336
337 // Keep going even after finding a match.
338 }
339 }
340
Andreas Huber4bcf97d2016-08-30 11:27:49 -0700341 if (resolvedType == nullptr
342 && fqName.package().empty()
343 && fqName.version().empty()
Hridya Valsarajub77944a2016-09-22 09:34:20 -0700344 && fqName.name() == "MQDescriptorSync") {
345 return new PredefinedType(
346 "::android::hardware::MQDescriptorSync");
Andreas Huber4bcf97d2016-08-30 11:27:49 -0700347 }
348
Andreas Huber39fa7182016-08-19 14:27:33 -0700349 if (resolvedType) {
350#if 0
351 LOG(INFO) << "found '"
352 << resolvedName.string()
353 << "' after looking for '"
354 << fqName.string()
355 << "'.";
356#endif
357
358 // Resolve typeDefs to the target type.
359 while (resolvedType->isTypeDef()) {
360 resolvedType =
361 static_cast<TypeDef *>(resolvedType)->referencedType();
362 }
363
Iliyan Malchev800273d2016-09-02 15:25:07 -0700364 returnedType = resolvedType;
365
366 // If the resolved type is not an interface, we need to determine
367 // whether it is defined in types.hal, or in some other interface. In
368 // the latter case, we need to emit a dependency for the interface in
369 // which the type is defined.
370 //
371 // Consider the following:
372 // android.hardware.tests.foo@1.0::Record
373 // android.hardware.tests.foo@1.0::IFoo.Folder
374 // android.hardware.tests.foo@1.0::Folder
375 //
376 // If Record is an interface, then we keep track of it for the purpose
377 // of emitting dependencies in the target language (for example #include
378 // in C++). If Record is a UDT, then we assume it is defined in
379 // types.hal in android.hardware.tests.foo@1.0.
380 //
381 // In the case of IFoo.Folder, the same applies. If IFoo is an
382 // interface, we need to track this for the purpose of emitting
383 // dependencies. If not, then it must have been defined in types.hal.
384 //
385 // In the case of just specifying Folder, the resolved type is
386 // android.hardware.tests.foo@1.0::IFoo.Folder, and the same logic as
387 // above applies.
388
389 if (!resolvedType->isInterface()) {
390 FQName ifc(resolvedName.package(),
391 resolvedName.version(),
392 resolvedName.names().at(0));
393 for (const auto &importedAST : mImportedASTs) {
394 FQName matchingName;
395 Type *match = importedAST->findDefinedType(ifc, &matchingName);
396 if (match != nullptr && match->isInterface()) {
397 resolvedType = match;
398 }
399 }
400 }
401
Andreas Huber39fa7182016-08-19 14:27:33 -0700402 if (!resolvedType->isInterface()) {
Andreas Huber0e00de42016-08-03 09:56:02 -0700403 // Non-interface types are declared in the associated types header.
Andreas Huber39fa7182016-08-19 14:27:33 -0700404 FQName typesName(
405 resolvedName.package(), resolvedName.version(), "types");
406
Andreas Huber0e00de42016-08-03 09:56:02 -0700407 mImportedNames.insert(typesName);
Andreas Huber85eabdb2016-08-25 11:24:49 -0700408
Steven Morelandd537ab02016-09-12 10:32:01 -0700409 if (resolvedType->isNamedType() && !resolvedType->isTypeDef()) {
Andreas Huber85eabdb2016-08-25 11:24:49 -0700410 mImportedNamesForJava.insert(
411 static_cast<NamedType *>(resolvedType)->fqName());
412 }
Andreas Huber0e00de42016-08-03 09:56:02 -0700413 } else {
Andreas Huberbfd76212016-08-09 11:12:16 -0700414 // Do _not_ use fqName, i.e. the name we used to look up the type,
415 // but instead use the name of the interface we found.
416 // This is necessary because if fqName pointed to a typedef which
417 // in turn referenced the found interface we'd mistakenly use the
418 // name of the typedef instead of the proper name of the interface.
419
420 mImportedNames.insert(
Andreas Huber39fa7182016-08-19 14:27:33 -0700421 static_cast<Interface *>(resolvedType)->fqName());
Andreas Huber85eabdb2016-08-25 11:24:49 -0700422
423 mImportedNamesForJava.insert(
424 static_cast<Interface *>(resolvedType)->fqName());
Andreas Huber0e00de42016-08-03 09:56:02 -0700425 }
Andreas Huber737080b2016-08-02 15:38:04 -0700426 }
427
Steven Morelandbb5c80b2016-10-05 11:07:36 -0700428 return returnedType;
Andreas Huber5345ec22016-07-29 13:33:27 -0700429}
430
Andreas Huber39fa7182016-08-19 14:27:33 -0700431Type *AST::findDefinedType(const FQName &fqName, FQName *matchingName) const {
Steven Morelandd537ab02016-09-12 10:32:01 -0700432 for (const auto &pair : mDefinedTypesByFullName) {
433 const FQName &key = pair.first;
434 Type* type = pair.second;
Andreas Huber5345ec22016-07-29 13:33:27 -0700435
Andreas Huber39fa7182016-08-19 14:27:33 -0700436 if (key.endsWith(fqName)) {
437 *matchingName = key;
Steven Morelandd537ab02016-09-12 10:32:01 -0700438 return type;
Andreas Huber5345ec22016-07-29 13:33:27 -0700439 }
Andreas Huber5345ec22016-07-29 13:33:27 -0700440 }
Andreas Huber39fa7182016-08-19 14:27:33 -0700441
442 return nullptr;
Andreas Huberc9410c72016-07-28 12:18:40 -0700443}
444
Iliyan Malchev5bb14022016-08-09 15:04:39 -0700445void AST::getImportedPackages(std::set<FQName> *importSet) const {
Andreas Huberd2943e12016-08-05 11:59:31 -0700446 for (const auto &fqName : mImportedNames) {
447 FQName packageName(fqName.package(), fqName.version(), "");
448
449 if (packageName == mPackage) {
450 // We only care about external imports, not our own package.
451 continue;
452 }
453
454 importSet->insert(packageName);
455 }
456}
457
Andreas Huber0fa9e392016-08-31 09:05:44 -0700458bool AST::isJavaCompatible() const {
459 std::string ifaceName;
460 if (!AST::isInterface(&ifaceName)) {
Steven Morelandd537ab02016-09-12 10:32:01 -0700461 for (const auto *type : mRootScope->getSubTypes()) {
Andreas Huber0fa9e392016-08-31 09:05:44 -0700462 if (!type->isJavaCompatible()) {
463 return false;
464 }
465 }
466
467 return true;
468 }
469
470 const Interface *iface = mRootScope->getInterface();
471 return iface->isJavaCompatible();
472}
473
Andreas Huber019d21d2016-10-03 12:59:47 -0700474void AST::appendToExportedTypesVector(
475 std::vector<const Type *> *exportedTypes) const {
476 mRootScope->appendToExportedTypesVector(exportedTypes);
477}
478
Andreas Huberc9410c72016-07-28 12:18:40 -0700479} // namespace android;