diff options
Diffstat (limited to 'tools/aidl/aidl.cpp')
-rw-r--r-- | tools/aidl/aidl.cpp | 1155 |
1 files changed, 1155 insertions, 0 deletions
diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp new file mode 100644 index 0000000..b8a4803 --- /dev/null +++ b/tools/aidl/aidl.cpp @@ -0,0 +1,1155 @@ + +#include "aidl_language.h" +#include "options.h" +#include "search_path.h" +#include "Type.h" +#include "generate_java.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <map> + +#ifdef HAVE_MS_C_RUNTIME +#include <io.h> +#include <sys/stat.h> +#endif + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +// The following are gotten as the offset from the allowable id's between +// android.os.IBinder.FIRST_CALL_TRANSACTION=1 and +// android.os.IBinder.LAST_CALL_TRANSACTION=16777215 +#define MIN_USER_SET_METHOD_ID 0 +#define MAX_USER_SET_METHOD_ID 16777214 + +using namespace std; + +static void +test_document(document_item_type* d) +{ + while (d) { + if (d->item_type == INTERFACE_TYPE_BINDER) { + interface_type* c = (interface_type*)d; + printf("interface %s %s {\n", c->package, c->name.data); + interface_item_type *q = (interface_item_type*)c->interface_items; + while (q) { + if (q->item_type == METHOD_TYPE) { + method_type *m = (method_type*)q; + printf(" %s %s(", m->type.type.data, m->name.data); + arg_type *p = m->args; + while (p) { + printf("%s %s",p->type.type.data,p->name.data); + if (p->next) printf(", "); + p=p->next; + } + printf(")"); + printf(";\n"); + } + q=q->next; + } + printf("}\n"); + } + else if (d->item_type == USER_DATA_TYPE) { + user_data_type* b = (user_data_type*)d; + if ((b->flattening_methods & PARCELABLE_DATA) != 0) { + printf("parcelable %s %s;\n", b->package, b->name.data); + } + if ((b->flattening_methods & RPC_DATA) != 0) { + printf("flattenable %s %s;\n", b->package, b->name.data); + } + } + else { + printf("UNKNOWN d=0x%08lx d->item_type=%d\n", (long)d, d->item_type); + } + d = d->next; + } +} + +// ========================================================== +int +convert_direction(const char* direction) +{ + if (direction == NULL) { + return IN_PARAMETER; + } + if (0 == strcmp(direction, "in")) { + return IN_PARAMETER; + } + if (0 == strcmp(direction, "out")) { + return OUT_PARAMETER; + } + return INOUT_PARAMETER; +} + +// ========================================================== +struct import_info { + const char* from; + const char* filename; + buffer_type statement; + const char* neededClass; + document_item_type* doc; + struct import_info* next; +}; + +document_item_type* g_document = NULL; +import_info* g_imports = NULL; + +static void +main_document_parsed(document_item_type* d) +{ + g_document = d; +} + +static void +main_import_parsed(buffer_type* statement) +{ + import_info* import = (import_info*)malloc(sizeof(import_info)); + memset(import, 0, sizeof(import_info)); + import->from = strdup(g_currentFilename); + import->statement.lineno = statement->lineno; + import->statement.data = strdup(statement->data); + import->statement.extra = NULL; + import->next = g_imports; + import->neededClass = parse_import_statement(statement->data); + g_imports = import; +} + +static ParserCallbacks g_mainCallbacks = { + &main_document_parsed, + &main_import_parsed +}; + +char* +parse_import_statement(const char* text) +{ + const char* end; + int len; + + while (isspace(*text)) { + text++; + } + while (!isspace(*text)) { + text++; + } + while (isspace(*text)) { + text++; + } + end = text; + while (!isspace(*end) && *end != ';') { + end++; + } + len = end-text; + + char* rv = (char*)malloc(len+1); + memcpy(rv, text, len); + rv[len] = '\0'; + + return rv; +} + +// ========================================================== +static void +import_import_parsed(buffer_type* statement) +{ +} + +static ParserCallbacks g_importCallbacks = { + &main_document_parsed, + &import_import_parsed +}; + +// ========================================================== +static int +check_filename(const char* filename, const char* package, buffer_type* name) +{ + const char* p; + string expected; + string fn; + size_t len; + char cwd[MAXPATHLEN]; + bool valid = false; + +#ifdef HAVE_WINDOWS_PATHS + if (isalpha(filename[0]) && filename[1] == ':' + && filename[2] == OS_PATH_SEPARATOR) { +#else + if (filename[0] == OS_PATH_SEPARATOR) { +#endif + fn = filename; + } else { + fn = getcwd(cwd, sizeof(cwd)); + len = fn.length(); + if (fn[len-1] != OS_PATH_SEPARATOR) { + fn += OS_PATH_SEPARATOR; + } + fn += filename; + } + + if (package) { + expected = package; + expected += '.'; + } + + len = expected.length(); + for (size_t i=0; i<len; i++) { + if (expected[i] == '.') { + expected[i] = OS_PATH_SEPARATOR; + } + } + + p = strchr(name->data, '.'); + len = p ? p-name->data : strlen(name->data); + expected.append(name->data, len); + + expected += ".aidl"; + + len = fn.length(); + valid = (len >= expected.length()); + + if (valid) { + p = fn.c_str() + (len - expected.length()); + +#ifdef HAVE_WINDOWS_PATHS + if (OS_PATH_SEPARATOR != '/') { + // Input filename under cygwin most likely has / separators + // whereas the expected string uses \\ separators. Adjust + // them accordingly. + for (char *c = const_cast<char *>(p); *c; ++c) { + if (*c == '/') *c = OS_PATH_SEPARATOR; + } + } +#endif + +#ifdef OS_CASE_SENSITIVE + valid = (expected == p); +#else + valid = !strcasecmp(expected.c_str(), p); +#endif + } + + if (!valid) { + fprintf(stderr, "%s:%d interface %s should be declared in a file" + " called %s.\n", + filename, name->lineno, name->data, expected.c_str()); + return 1; + } + + return 0; +} + +static int +check_filenames(const char* filename, document_item_type* items) +{ + int err = 0; + while (items) { + if (items->item_type == USER_DATA_TYPE) { + user_data_type* p = (user_data_type*)items; + err |= check_filename(filename, p->package, &p->name); + } + else if (items->item_type == INTERFACE_TYPE_BINDER + || items->item_type == INTERFACE_TYPE_RPC) { + interface_type* c = (interface_type*)items; + err |= check_filename(filename, c->package, &c->name); + } + else { + fprintf(stderr, "aidl: internal error unkown document type %d.\n", + items->item_type); + return 1; + } + items = items->next; + } + return err; +} + +// ========================================================== +static const char* +kind_to_string(int kind) +{ + switch (kind) + { + case Type::INTERFACE: + return "an interface"; + case Type::USERDATA: + return "a user data"; + default: + return "ERROR"; + } +} + +static char* +rfind(char* str, char c) +{ + char* p = str + strlen(str) - 1; + while (p >= str) { + if (*p == c) { + return p; + } + p--; + } + return NULL; +} + +static int +gather_types(const char* filename, document_item_type* items) +{ + int err = 0; + while (items) { + Type* type; + if (items->item_type == USER_DATA_TYPE) { + user_data_type* p = (user_data_type*)items; + type = new UserDataType(p->package ? p->package : "", p->name.data, + false, ((p->flattening_methods & PARCELABLE_DATA) != 0), + ((p->flattening_methods & RPC_DATA) != 0), filename, p->name.lineno); + } + else if (items->item_type == INTERFACE_TYPE_BINDER + || items->item_type == INTERFACE_TYPE_RPC) { + interface_type* c = (interface_type*)items; + type = new InterfaceType(c->package ? c->package : "", + c->name.data, false, c->oneway, + filename, c->name.lineno); + } + else { + fprintf(stderr, "aidl: internal error %s:%d\n", __FILE__, __LINE__); + return 1; + } + + Type* old = NAMES.Find(type->QualifiedName()); + if (old == NULL) { + NAMES.Add(type); + + if (items->item_type == INTERFACE_TYPE_BINDER) { + // for interfaces, also add the stub and proxy types, we don't + // bother checking these for duplicates, because the parser + // won't let us do it. + interface_type* c = (interface_type*)items; + + string name = c->name.data; + name += ".Stub"; + Type* stub = new Type(c->package ? c->package : "", + name, Type::GENERATED, false, false, false, + filename, c->name.lineno); + NAMES.Add(stub); + + name = c->name.data; + name += ".Stub.Proxy"; + Type* proxy = new Type(c->package ? c->package : "", + name, Type::GENERATED, false, false, false, + filename, c->name.lineno); + NAMES.Add(proxy); + } + else if (items->item_type == INTERFACE_TYPE_RPC) { + // for interfaces, also add the service base type, we don't + // bother checking these for duplicates, because the parser + // won't let us do it. + interface_type* c = (interface_type*)items; + + string name = c->name.data; + name += ".ServiceBase"; + Type* base = new Type(c->package ? c->package : "", + name, Type::GENERATED, false, false, false, + filename, c->name.lineno); + NAMES.Add(base); + } + } else { + if (old->Kind() == Type::BUILT_IN) { + fprintf(stderr, "%s:%d attempt to redefine built in class %s\n", + filename, type->DeclLine(), + type->QualifiedName().c_str()); + err = 1; + } + else if (type->Kind() != old->Kind()) { + const char* oldKind = kind_to_string(old->Kind()); + const char* newKind = kind_to_string(type->Kind()); + + fprintf(stderr, "%s:%d attempt to redefine %s as %s,\n", + filename, type->DeclLine(), + type->QualifiedName().c_str(), newKind); + fprintf(stderr, "%s:%d previously defined here as %s.\n", + old->DeclFile().c_str(), old->DeclLine(), oldKind); + err = 1; + } + } + + items = items->next; + } + return err; +} + +// ========================================================== +static bool +matches_keyword(const char* str) +{ + static const char* KEYWORDS[] = { "abstract", "assert", "boolean", "break", + "byte", "case", "catch", "char", "class", "const", "continue", + "default", "do", "double", "else", "enum", "extends", "final", + "finally", "float", "for", "goto", "if", "implements", "import", + "instanceof", "int", "interface", "long", "native", "new", "package", + "private", "protected", "public", "return", "short", "static", + "strictfp", "super", "switch", "synchronized", "this", "throw", + "throws", "transient", "try", "void", "volatile", "while", + "true", "false", "null", + NULL + }; + const char** k = KEYWORDS; + while (*k) { + if (0 == strcmp(str, *k)) { + return true; + } + k++; + } + return false; +} + +static int +check_method(const char* filename, int kind, method_type* m) +{ + int err = 0; + + // return type + Type* returnType = NAMES.Search(m->type.type.data); + if (returnType == NULL) { + fprintf(stderr, "%s:%d unknown return type %s\n", filename, + m->type.type.lineno, m->type.type.data); + err = 1; + return err; + } + + if (returnType == EVENT_FAKE_TYPE) { + if (kind != INTERFACE_TYPE_RPC) { + fprintf(stderr, "%s:%d event methods only supported for rpc interfaces\n", + filename, m->type.type.lineno); + err = 1; + } + } else { + if (!(kind == INTERFACE_TYPE_BINDER ? returnType->CanWriteToParcel() + : returnType->CanWriteToRpcData())) { + fprintf(stderr, "%s:%d return type %s can't be marshalled.\n", filename, + m->type.type.lineno, m->type.type.data); + err = 1; + } + } + + if (m->type.dimension > 0 && !returnType->CanBeArray()) { + fprintf(stderr, "%s:%d return type %s%s can't be an array.\n", filename, + m->type.array_token.lineno, m->type.type.data, + m->type.array_token.data); + err = 1; + } + + if (m->type.dimension > 1) { + fprintf(stderr, "%s:%d return type %s%s only one" + " dimensional arrays are supported\n", filename, + m->type.array_token.lineno, m->type.type.data, + m->type.array_token.data); + err = 1; + } + + int index = 1; + + arg_type* arg = m->args; + while (arg) { + Type* t = NAMES.Search(arg->type.type.data); + + // check the arg type + if (t == NULL) { + fprintf(stderr, "%s:%d parameter %s (%d) unknown type %s\n", + filename, m->type.type.lineno, arg->name.data, index, + arg->type.type.data); + err = 1; + goto next; + } + + if (t == EVENT_FAKE_TYPE) { + fprintf(stderr, "%s:%d parameter %s (%d) event can not be used as a parameter %s\n", + filename, m->type.type.lineno, arg->name.data, index, + arg->type.type.data); + err = 1; + goto next; + } + + if (!(kind == INTERFACE_TYPE_BINDER ? t->CanWriteToParcel() : t->CanWriteToRpcData())) { + fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n", + filename, m->type.type.lineno, index, + arg->type.type.data, arg->name.data); + err = 1; + } + + if (returnType == EVENT_FAKE_TYPE + && convert_direction(arg->direction.data) != IN_PARAMETER) { + fprintf(stderr, "%s:%d parameter %d: '%s %s' All paremeters on events must be 'in'.\n", + filename, m->type.type.lineno, index, + arg->type.type.data, arg->name.data); + err = 1; + goto next; + } + + if (arg->direction.data == NULL + && (arg->type.dimension != 0 || t->CanBeOutParameter())) { + fprintf(stderr, "%s:%d parameter %d: '%s %s' can be an out" + " parameter, so you must declare it as in," + " out or inout.\n", + filename, m->type.type.lineno, index, + arg->type.type.data, arg->name.data); + err = 1; + } + + if (convert_direction(arg->direction.data) != IN_PARAMETER + && !t->CanBeOutParameter() + && arg->type.dimension == 0) { + fprintf(stderr, "%s:%d parameter %d: '%s %s %s' can only be an in" + " parameter.\n", + filename, m->type.type.lineno, index, + arg->direction.data, arg->type.type.data, + arg->name.data); + err = 1; + } + + if (arg->type.dimension > 0 && !t->CanBeArray()) { + fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' can't be an" + " array.\n", filename, + m->type.array_token.lineno, index, arg->direction.data, + arg->type.type.data, arg->type.array_token.data, + arg->name.data); + err = 1; + } + + if (arg->type.dimension > 1) { + fprintf(stderr, "%s:%d parameter %d: '%s %s%s %s' only one" + " dimensional arrays are supported\n", filename, + m->type.array_token.lineno, index, arg->direction.data, + arg->type.type.data, arg->type.array_token.data, + arg->name.data); + err = 1; + } + + // check that the name doesn't match a keyword + if (matches_keyword(arg->name.data)) { + fprintf(stderr, "%s:%d parameter %d %s is named the same as a" + " Java or aidl keyword\n", + filename, m->name.lineno, index, arg->name.data); + err = 1; + } + +next: + index++; + arg = arg->next; + } + + return err; +} + +static int +check_types(const char* filename, document_item_type* items) +{ + int err = 0; + while (items) { + // (nothing to check for USER_DATA_TYPE) + if (items->item_type == INTERFACE_TYPE_BINDER + || items->item_type == INTERFACE_TYPE_RPC) { + map<string,method_type*> methodNames; + interface_type* c = (interface_type*)items; + + interface_item_type* member = c->interface_items; + while (member) { + if (member->item_type == METHOD_TYPE) { + method_type* m = (method_type*)member; + + err |= check_method(filename, items->item_type, m); + + // prevent duplicate methods + if (methodNames.find(m->name.data) == methodNames.end()) { + methodNames[m->name.data] = m; + } else { + fprintf(stderr,"%s:%d attempt to redefine method %s,\n", + filename, m->name.lineno, m->name.data); + method_type* old = methodNames[m->name.data]; + fprintf(stderr, "%s:%d previously defined here.\n", + filename, old->name.lineno); + err = 1; + } + } + member = member->next; + } + } + + items = items->next; + } + return err; +} + +// ========================================================== +static int +exactly_one_interface(const char* filename, const document_item_type* items, const Options& options, + bool* onlyParcelable) +{ + if (items == NULL) { + fprintf(stderr, "%s: file does not contain any interfaces\n", + filename); + return 1; + } + + const document_item_type* next = items->next; + // Allow parcelables to skip the "one-only" rule. + if (items->next != NULL && next->item_type != USER_DATA_TYPE) { + int lineno = -1; + if (next->item_type == INTERFACE_TYPE_BINDER) { + lineno = ((interface_type*)next)->interface_token.lineno; + } + else if (next->item_type == INTERFACE_TYPE_RPC) { + lineno = ((interface_type*)next)->interface_token.lineno; + } + fprintf(stderr, "%s:%d aidl can only handle one interface per file\n", + filename, lineno); + return 1; + } + + if (items->item_type == USER_DATA_TYPE) { + *onlyParcelable = true; + if (options.failOnParcelable) { + fprintf(stderr, "%s:%d aidl can only generate code for interfaces, not" + " parcelables or flattenables,\n", filename, + ((user_data_type*)items)->keyword_token.lineno); + fprintf(stderr, "%s:%d .aidl files that only declare parcelables or flattenables" + "may not go in the Makefile.\n", filename, + ((user_data_type*)items)->keyword_token.lineno); + return 1; + } + } else { + *onlyParcelable = false; + } + + return 0; +} + +// ========================================================== +void +generate_dep_file(const Options& options, const document_item_type* items) +{ + /* we open the file in binary mode to ensure that the same output is + * generated on all platforms !! + */ + FILE* to = NULL; + if (options.autoDepFile) { + string fileName = options.outputFileName + ".d"; + to = fopen(fileName.c_str(), "wb"); + } else { + to = fopen(options.depFileName.c_str(), "wb"); + } + + if (to == NULL) { + return; + } + + const char* slash = "\\"; + import_info* import = g_imports; + if (import == NULL) { + slash = ""; + } + + if (items->item_type == INTERFACE_TYPE_BINDER || items->item_type == INTERFACE_TYPE_RPC) { + fprintf(to, "%s: \\\n", options.outputFileName.c_str()); + } else { + // parcelable: there's no output file. + fprintf(to, " : \\\n"); + } + fprintf(to, " %s %s\n", options.inputFileName.c_str(), slash); + + while (import) { + if (import->next == NULL) { + slash = ""; + } + if (import->filename) { + fprintf(to, " %s %s\n", import->filename, slash); + } + import = import->next; + } + + fprintf(to, "\n"); + + // Output "<imported_file>: " so make won't fail if the imported file has + // been deleted, moved or renamed in incremental build. + import = g_imports; + while (import) { + if (import->filename) { + fprintf(to, "%s :\n", import->filename); + } + import = import->next; + } + + fclose(to); +} + +// ========================================================== +static string +generate_outputFileName2(const Options& options, const buffer_type& name, const char* package) +{ + string result; + + // create the path to the destination folder based on the + // interface package name + result = options.outputBaseFolder; + result += OS_PATH_SEPARATOR; + + string packageStr = package; + size_t len = packageStr.length(); + for (size_t i=0; i<len; i++) { + if (packageStr[i] == '.') { + packageStr[i] = OS_PATH_SEPARATOR; + } + } + + result += packageStr; + + // add the filename by replacing the .aidl extension to .java + const char* p = strchr(name.data, '.'); + len = p ? p-name.data : strlen(name.data); + + result += OS_PATH_SEPARATOR; + result.append(name.data, len); + result += ".java"; + + return result; +} + +// ========================================================== +static string +generate_outputFileName(const Options& options, const document_item_type* items) +{ + // items has already been checked to have only one interface. + if (items->item_type == INTERFACE_TYPE_BINDER || items->item_type == INTERFACE_TYPE_RPC) { + interface_type* type = (interface_type*)items; + + return generate_outputFileName2(options, type->name, type->package); + } else if (items->item_type == USER_DATA_TYPE) { + user_data_type* type = (user_data_type*)items; + return generate_outputFileName2(options, type->name, type->package); + } + + // I don't think we can come here, but safer than returning NULL. + string result; + return result; +} + + + +// ========================================================== +static void +check_outputFilePath(const string& path) { + size_t len = path.length(); + for (size_t i=0; i<len ; i++) { + if (path[i] == OS_PATH_SEPARATOR) { + string p = path.substr(0, i); + if (access(path.data(), F_OK) != 0) { +#ifdef HAVE_MS_C_RUNTIME + _mkdir(p.data()); +#else + mkdir(p.data(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); +#endif + } + } + } +} + + +// ========================================================== +static int +parse_preprocessed_file(const string& filename) +{ + int err; + + FILE* f = fopen(filename.c_str(), "rb"); + if (f == NULL) { + fprintf(stderr, "aidl: can't open preprocessed file: %s\n", + filename.c_str()); + return 1; + } + + int lineno = 1; + char line[1024]; + char type[1024]; + char fullname[1024]; + while (fgets(line, sizeof(line), f)) { + // skip comments and empty lines + if (!line[0] || strncmp(line, "//", 2) == 0) { + continue; + } + + sscanf(line, "%s %[^; \r\n\t];", type, fullname); + + char* packagename; + char* classname = rfind(fullname, '.'); + if (classname != NULL) { + *classname = '\0'; + classname++; + packagename = fullname; + } else { + classname = fullname; + packagename = NULL; + } + + //printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno, + // type, packagename, classname); + document_item_type* doc; + + if (0 == strcmp("parcelable", type)) { + user_data_type* parcl = (user_data_type*)malloc( + sizeof(user_data_type)); + memset(parcl, 0, sizeof(user_data_type)); + parcl->document_item.item_type = USER_DATA_TYPE; + parcl->keyword_token.lineno = lineno; + parcl->keyword_token.data = strdup(type); + parcl->package = packagename ? strdup(packagename) : NULL; + parcl->name.lineno = lineno; + parcl->name.data = strdup(classname); + parcl->semicolon_token.lineno = lineno; + parcl->semicolon_token.data = strdup(";"); + parcl->flattening_methods = PARCELABLE_DATA; + doc = (document_item_type*)parcl; + } + else if (0 == strcmp("flattenable", type)) { + user_data_type* parcl = (user_data_type*)malloc( + sizeof(user_data_type)); + memset(parcl, 0, sizeof(user_data_type)); + parcl->document_item.item_type = USER_DATA_TYPE; + parcl->keyword_token.lineno = lineno; + parcl->keyword_token.data = strdup(type); + parcl->package = packagename ? strdup(packagename) : NULL; + parcl->name.lineno = lineno; + parcl->name.data = strdup(classname); + parcl->semicolon_token.lineno = lineno; + parcl->semicolon_token.data = strdup(";"); + parcl->flattening_methods = RPC_DATA; + doc = (document_item_type*)parcl; + } + else if (0 == strcmp("interface", type)) { + interface_type* iface = (interface_type*)malloc( + sizeof(interface_type)); + memset(iface, 0, sizeof(interface_type)); + iface->document_item.item_type = INTERFACE_TYPE_BINDER; + iface->interface_token.lineno = lineno; + iface->interface_token.data = strdup(type); + iface->package = packagename ? strdup(packagename) : NULL; + iface->name.lineno = lineno; + iface->name.data = strdup(classname); + iface->open_brace_token.lineno = lineno; + iface->open_brace_token.data = strdup("{"); + iface->close_brace_token.lineno = lineno; + iface->close_brace_token.data = strdup("}"); + doc = (document_item_type*)iface; + } + else { + fprintf(stderr, "%s:%d: bad type in line: %s\n", + filename.c_str(), lineno, line); + return 1; + } + err = gather_types(filename.c_str(), doc); + lineno++; + } + + if (!feof(f)) { + fprintf(stderr, "%s:%d: error reading file, line to long.\n", + filename.c_str(), lineno); + return 1; + } + + fclose(f); + return 0; +} + +static int +check_and_assign_method_ids(const char * filename, interface_item_type* first_item) +{ + // Check whether there are any methods with manually assigned id's and any that are not. + // Either all method id's must be manually assigned or all of them must not. + // Also, check for duplicates of user set id's and that the id's are within the proper bounds. + set<int> usedIds; + interface_item_type* item = first_item; + bool hasUnassignedIds = false; + bool hasAssignedIds = false; + while (item != NULL) { + if (item->item_type == METHOD_TYPE) { + method_type* method_item = (method_type*)item; + if (method_item->hasId) { + hasAssignedIds = true; + method_item->assigned_id = atoi(method_item->id.data); + // Ensure that the user set id is not duplicated. + if (usedIds.find(method_item->assigned_id) != usedIds.end()) { + // We found a duplicate id, so throw an error. + fprintf(stderr, + "%s:%d Found duplicate method id (%d) for method: %s\n", + filename, method_item->id.lineno, + method_item->assigned_id, method_item->name.data); + return 1; + } + // Ensure that the user set id is within the appropriate limits + if (method_item->assigned_id < MIN_USER_SET_METHOD_ID || + method_item->assigned_id > MAX_USER_SET_METHOD_ID) { + fprintf(stderr, "%s:%d Found out of bounds id (%d) for method: %s\n", + filename, method_item->id.lineno, + method_item->assigned_id, method_item->name.data); + fprintf(stderr, " Value for id must be between %d and %d inclusive.\n", + MIN_USER_SET_METHOD_ID, MAX_USER_SET_METHOD_ID); + return 1; + } + usedIds.insert(method_item->assigned_id); + } else { + hasUnassignedIds = true; + } + if (hasAssignedIds && hasUnassignedIds) { + fprintf(stderr, + "%s: You must either assign id's to all methods or to none of them.\n", + filename); + return 1; + } + } + item = item->next; + } + + // In the case that all methods have unassigned id's, set a unique id for them. + if (hasUnassignedIds) { + int newId = 0; + item = first_item; + while (item != NULL) { + if (item->item_type == METHOD_TYPE) { + method_type* method_item = (method_type*)item; + method_item->assigned_id = newId++; + } + item = item->next; + } + } + + // success + return 0; +} + +// ========================================================== +static int +compile_aidl(Options& options) +{ + int err = 0, N; + + set_import_paths(options.importPaths); + + register_base_types(); + + // import the preprocessed file + N = options.preprocessedFiles.size(); + for (int i=0; i<N; i++) { + const string& s = options.preprocessedFiles[i]; + err |= parse_preprocessed_file(s); + } + if (err != 0) { + return err; + } + + // parse the main file + g_callbacks = &g_mainCallbacks; + err = parse_aidl(options.inputFileName.c_str()); + document_item_type* mainDoc = g_document; + g_document = NULL; + + // parse the imports + g_callbacks = &g_mainCallbacks; + import_info* import = g_imports; + while (import) { + if (NAMES.Find(import->neededClass) == NULL) { + import->filename = find_import_file(import->neededClass); + if (!import->filename) { + fprintf(stderr, "%s:%d: couldn't find import for class %s\n", + import->from, import->statement.lineno, + import->neededClass); + err |= 1; + } else { + err |= parse_aidl(import->filename); + import->doc = g_document; + if (import->doc == NULL) { + err |= 1; + } + } + } + import = import->next; + } + // bail out now if parsing wasn't successful + if (err != 0 || mainDoc == NULL) { + //fprintf(stderr, "aidl: parsing failed, stopping.\n"); + return 1; + } + + // complain about ones that aren't in the right files + err |= check_filenames(options.inputFileName.c_str(), mainDoc); + import = g_imports; + while (import) { + err |= check_filenames(import->filename, import->doc); + import = import->next; + } + + // gather the types that have been declared + err |= gather_types(options.inputFileName.c_str(), mainDoc); + import = g_imports; + while (import) { + err |= gather_types(import->filename, import->doc); + import = import->next; + } + +#if 0 + printf("---- main doc ----\n"); + test_document(mainDoc); + + import = g_imports; + while (import) { + printf("---- import doc ----\n"); + test_document(import->doc); + import = import->next; + } + NAMES.Dump(); +#endif + + // check the referenced types in mainDoc to make sure we've imported them + err |= check_types(options.inputFileName.c_str(), mainDoc); + + // finally, there really only needs to be one thing in mainDoc, and it + // needs to be an interface. + bool onlyParcelable = false; + err |= exactly_one_interface(options.inputFileName.c_str(), mainDoc, options, &onlyParcelable); + + // If this includes an interface definition, then assign method ids and validate. + if (!onlyParcelable) { + err |= check_and_assign_method_ids(options.inputFileName.c_str(), + ((interface_type*)mainDoc)->interface_items); + } + + // after this, there shouldn't be any more errors because of the + // input. + if (err != 0 || mainDoc == NULL) { + return 1; + } + + // if needed, generate the outputFileName from the outputBaseFolder + if (options.outputFileName.length() == 0 && + options.outputBaseFolder.length() > 0) { + options.outputFileName = generate_outputFileName(options, mainDoc); + } + + // if we were asked to, generate a make dependency file + // unless it's a parcelable *and* it's supposed to fail on parcelable + if ((options.autoDepFile || options.depFileName != "") && + !(onlyParcelable && options.failOnParcelable)) { + // make sure the folders of the output file all exists + check_outputFilePath(options.outputFileName); + generate_dep_file(options, mainDoc); + } + + // they didn't ask to fail on parcelables, so just exit quietly. + if (onlyParcelable && !options.failOnParcelable) { + return 0; + } + + // make sure the folders of the output file all exists + check_outputFilePath(options.outputFileName); + + err = generate_java(options.outputFileName, options.inputFileName.c_str(), + (interface_type*)mainDoc); + + return err; +} + +static int +preprocess_aidl(const Options& options) +{ + vector<string> lines; + int err; + + // read files + int N = options.filesToPreprocess.size(); + for (int i=0; i<N; i++) { + g_callbacks = &g_mainCallbacks; + err = parse_aidl(options.filesToPreprocess[i].c_str()); + if (err != 0) { + return err; + } + document_item_type* doc = g_document; + string line; + if (doc->item_type == USER_DATA_TYPE) { + user_data_type* parcelable = (user_data_type*)doc; + if ((parcelable->flattening_methods & PARCELABLE_DATA) != 0) { + line = "parcelable "; + } + if ((parcelable->flattening_methods & RPC_DATA) != 0) { + line = "flattenable "; + } + if (parcelable->package) { + line += parcelable->package; + line += '.'; + } + line += parcelable->name.data; + } else { + line = "interface "; + interface_type* iface = (interface_type*)doc; + if (iface->package) { + line += iface->package; + line += '.'; + } + line += iface->name.data; + } + line += ";\n"; + lines.push_back(line); + } + + // write preprocessed file + int fd = open( options.outputFileName.c_str(), + O_RDWR|O_CREAT|O_TRUNC|O_BINARY, +#ifdef HAVE_MS_C_RUNTIME + _S_IREAD|_S_IWRITE); +#else + S_IRUSR|S_IWUSR|S_IRGRP); +#endif + if (fd == -1) { + fprintf(stderr, "aidl: could not open file for write: %s\n", + options.outputFileName.c_str()); + return 1; + } + + N = lines.size(); + for (int i=0; i<N; i++) { + const string& s = lines[i]; + int len = s.length(); + if (len != write(fd, s.c_str(), len)) { + fprintf(stderr, "aidl: error writing to file %s\n", + options.outputFileName.c_str()); + close(fd); + unlink(options.outputFileName.c_str()); + return 1; + } + } + + close(fd); + return 0; +} + +// ========================================================== +int +main(int argc, const char **argv) +{ + Options options; + int result = parse_options(argc, argv, &options); + if (result) { + return result; + } + + switch (options.task) + { + case COMPILE_AIDL: + return compile_aidl(options); + case PREPROCESS_AIDL: + return preprocess_aidl(options); + } + fprintf(stderr, "aidl: internal error\n"); + return 1; +} |