diff options
author | Adam Lesinski <adamlesinski@google.com> | 2014-01-23 18:17:42 -0800 |
---|---|---|
committer | Adam Lesinski <adamlesinski@google.com> | 2014-01-27 10:31:04 -0800 |
commit | 282e181b58cf72b6ca770dc7ca5f91f135444502 (patch) | |
tree | e313e7ab30ff4679562efa37bde29cfcb9e375d3 /tools/aidl | |
parent | 7023df08f14ec5dee76ac54c03e870f84e297636 (diff) | |
download | frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.zip frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.tar.gz frameworks_base-282e181b58cf72b6ca770dc7ca5f91f135444502.tar.bz2 |
Revert "Move frameworks/base/tools/ to frameworks/tools/"
This reverts commit 9f6a119c8aa276432ece4fe2118bd8a3c9b1067e.
Diffstat (limited to 'tools/aidl')
-rw-r--r-- | tools/aidl/AST.cpp | 912 | ||||
-rw-r--r-- | tools/aidl/AST.h | 371 | ||||
-rw-r--r-- | tools/aidl/Android.mk | 29 | ||||
-rw-r--r-- | tools/aidl/NOTICE | 190 | ||||
-rw-r--r-- | tools/aidl/Type.cpp | 1440 | ||||
-rw-r--r-- | tools/aidl/Type.h | 542 | ||||
-rw-r--r-- | tools/aidl/aidl.cpp | 1155 | ||||
-rw-r--r-- | tools/aidl/aidl_language.cpp | 20 | ||||
-rw-r--r-- | tools/aidl/aidl_language.h | 172 | ||||
-rw-r--r-- | tools/aidl/aidl_language_l.l | 214 | ||||
-rw-r--r-- | tools/aidl/aidl_language_y.y | 373 | ||||
-rw-r--r-- | tools/aidl/generate_java.cpp | 99 | ||||
-rw-r--r-- | tools/aidl/generate_java.h | 33 | ||||
-rw-r--r-- | tools/aidl/generate_java_binder.cpp | 560 | ||||
-rw-r--r-- | tools/aidl/generate_java_rpc.cpp | 1001 | ||||
-rw-r--r-- | tools/aidl/options.cpp | 154 | ||||
-rw-r--r-- | tools/aidl/options.h | 36 | ||||
-rw-r--r-- | tools/aidl/options_test.cpp | 291 | ||||
-rw-r--r-- | tools/aidl/search_path.cpp | 57 | ||||
-rw-r--r-- | tools/aidl/search_path.h | 23 |
20 files changed, 7672 insertions, 0 deletions
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp new file mode 100644 index 0000000..bfa6765 --- /dev/null +++ b/tools/aidl/AST.cpp @@ -0,0 +1,912 @@ +#include "AST.h" +#include "Type.h" + +void +WriteModifiers(FILE* to, int mod, int mask) +{ + int m = mod & mask; + + if (m & OVERRIDE) { + fprintf(to, "@Override "); + } + + if ((m & SCOPE_MASK) == PUBLIC) { + fprintf(to, "public "); + } + else if ((m & SCOPE_MASK) == PRIVATE) { + fprintf(to, "private "); + } + else if ((m & SCOPE_MASK) == PROTECTED) { + fprintf(to, "protected "); + } + + if (m & STATIC) { + fprintf(to, "static "); + } + + if (m & FINAL) { + fprintf(to, "final "); + } + + if (m & ABSTRACT) { + fprintf(to, "abstract "); + } +} + +void +WriteArgumentList(FILE* to, const vector<Expression*>& arguments) +{ + size_t N = arguments.size(); + for (size_t i=0; i<N; i++) { + arguments[i]->Write(to); + if (i != N-1) { + fprintf(to, ", "); + } + } +} + +ClassElement::ClassElement() +{ +} + +ClassElement::~ClassElement() +{ +} + +Field::Field() + :ClassElement(), + modifiers(0), + variable(NULL) +{ +} + +Field::Field(int m, Variable* v) + :ClassElement(), + modifiers(m), + variable(v) +{ +} + +Field::~Field() +{ +} + +void +Field::GatherTypes(set<Type*>* types) const +{ + types->insert(this->variable->type); +} + +void +Field::Write(FILE* to) +{ + if (this->comment.length() != 0) { + fprintf(to, "%s\n", this->comment.c_str()); + } + WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE); + fprintf(to, "%s %s", this->variable->type->QualifiedName().c_str(), + this->variable->name.c_str()); + if (this->value.length() != 0) { + fprintf(to, " = %s", this->value.c_str()); + } + fprintf(to, ";\n"); +} + +Expression::~Expression() +{ +} + +LiteralExpression::LiteralExpression(const string& v) + :value(v) +{ +} + +LiteralExpression::~LiteralExpression() +{ +} + +void +LiteralExpression::Write(FILE* to) +{ + fprintf(to, "%s", this->value.c_str()); +} + +StringLiteralExpression::StringLiteralExpression(const string& v) + :value(v) +{ +} + +StringLiteralExpression::~StringLiteralExpression() +{ +} + +void +StringLiteralExpression::Write(FILE* to) +{ + fprintf(to, "\"%s\"", this->value.c_str()); +} + +Variable::Variable() + :type(NULL), + name(), + dimension(0) +{ +} + +Variable::Variable(Type* t, const string& n) + :type(t), + name(n), + dimension(0) +{ +} + +Variable::Variable(Type* t, const string& n, int d) + :type(t), + name(n), + dimension(d) +{ +} + +Variable::~Variable() +{ +} + +void +Variable::GatherTypes(set<Type*>* types) const +{ + types->insert(this->type); +} + +void +Variable::WriteDeclaration(FILE* to) +{ + string dim; + for (int i=0; i<this->dimension; i++) { + dim += "[]"; + } + fprintf(to, "%s%s %s", this->type->QualifiedName().c_str(), dim.c_str(), + this->name.c_str()); +} + +void +Variable::Write(FILE* to) +{ + fprintf(to, "%s", name.c_str()); +} + +FieldVariable::FieldVariable(Expression* o, const string& n) + :object(o), + clazz(NULL), + name(n) +{ +} + +FieldVariable::FieldVariable(Type* c, const string& n) + :object(NULL), + clazz(c), + name(n) +{ +} + +FieldVariable::~FieldVariable() +{ +} + +void +FieldVariable::Write(FILE* to) +{ + if (this->object != NULL) { + this->object->Write(to); + } + else if (this->clazz != NULL) { + fprintf(to, "%s", this->clazz->QualifiedName().c_str()); + } + fprintf(to, ".%s", name.c_str()); +} + + +Statement::~Statement() +{ +} + +StatementBlock::StatementBlock() +{ +} + +StatementBlock::~StatementBlock() +{ +} + +void +StatementBlock::Write(FILE* to) +{ + fprintf(to, "{\n"); + int N = this->statements.size(); + for (int i=0; i<N; i++) { + this->statements[i]->Write(to); + } + fprintf(to, "}\n"); +} + +void +StatementBlock::Add(Statement* statement) +{ + this->statements.push_back(statement); +} + +void +StatementBlock::Add(Expression* expression) +{ + this->statements.push_back(new ExpressionStatement(expression)); +} + +ExpressionStatement::ExpressionStatement(Expression* e) + :expression(e) +{ +} + +ExpressionStatement::~ExpressionStatement() +{ +} + +void +ExpressionStatement::Write(FILE* to) +{ + this->expression->Write(to); + fprintf(to, ";\n"); +} + +Assignment::Assignment(Variable* l, Expression* r) + :lvalue(l), + rvalue(r), + cast(NULL) +{ +} + +Assignment::Assignment(Variable* l, Expression* r, Type* c) + :lvalue(l), + rvalue(r), + cast(c) +{ +} + +Assignment::~Assignment() +{ +} + +void +Assignment::Write(FILE* to) +{ + this->lvalue->Write(to); + fprintf(to, " = "); + if (this->cast != NULL) { + fprintf(to, "(%s)", this->cast->QualifiedName().c_str()); + } + this->rvalue->Write(to); +} + +MethodCall::MethodCall(const string& n) + :obj(NULL), + clazz(NULL), + name(n) +{ +} + +MethodCall::MethodCall(const string& n, int argc = 0, ...) + :obj(NULL), + clazz(NULL), + name(n) +{ + va_list args; + va_start(args, argc); + init(argc, args); + va_end(args); +} + +MethodCall::MethodCall(Expression* o, const string& n) + :obj(o), + clazz(NULL), + name(n) +{ +} + +MethodCall::MethodCall(Type* t, const string& n) + :obj(NULL), + clazz(t), + name(n) +{ +} + +MethodCall::MethodCall(Expression* o, const string& n, int argc = 0, ...) + :obj(o), + clazz(NULL), + name(n) +{ + va_list args; + va_start(args, argc); + init(argc, args); + va_end(args); +} + +MethodCall::MethodCall(Type* t, const string& n, int argc = 0, ...) + :obj(NULL), + clazz(t), + name(n) +{ + va_list args; + va_start(args, argc); + init(argc, args); + va_end(args); +} + +MethodCall::~MethodCall() +{ +} + +void +MethodCall::init(int n, va_list args) +{ + for (int i=0; i<n; i++) { + Expression* expression = (Expression*)va_arg(args, void*); + this->arguments.push_back(expression); + } +} + +void +MethodCall::Write(FILE* to) +{ + if (this->obj != NULL) { + this->obj->Write(to); + fprintf(to, "."); + } + else if (this->clazz != NULL) { + fprintf(to, "%s.", this->clazz->QualifiedName().c_str()); + } + fprintf(to, "%s(", this->name.c_str()); + WriteArgumentList(to, this->arguments); + fprintf(to, ")"); +} + +Comparison::Comparison(Expression* l, const string& o, Expression* r) + :lvalue(l), + op(o), + rvalue(r) +{ +} + +Comparison::~Comparison() +{ +} + +void +Comparison::Write(FILE* to) +{ + fprintf(to, "("); + this->lvalue->Write(to); + fprintf(to, "%s", this->op.c_str()); + this->rvalue->Write(to); + fprintf(to, ")"); +} + +NewExpression::NewExpression(Type* t) + :type(t) +{ +} + +NewExpression::NewExpression(Type* t, int argc = 0, ...) + :type(t) +{ + va_list args; + va_start(args, argc); + init(argc, args); + va_end(args); +} + +NewExpression::~NewExpression() +{ +} + +void +NewExpression::init(int n, va_list args) +{ + for (int i=0; i<n; i++) { + Expression* expression = (Expression*)va_arg(args, void*); + this->arguments.push_back(expression); + } +} + +void +NewExpression::Write(FILE* to) +{ + fprintf(to, "new %s(", this->type->InstantiableName().c_str()); + WriteArgumentList(to, this->arguments); + fprintf(to, ")"); +} + +NewArrayExpression::NewArrayExpression(Type* t, Expression* s) + :type(t), + size(s) +{ +} + +NewArrayExpression::~NewArrayExpression() +{ +} + +void +NewArrayExpression::Write(FILE* to) +{ + fprintf(to, "new %s[", this->type->QualifiedName().c_str()); + size->Write(to); + fprintf(to, "]"); +} + +Ternary::Ternary() + :condition(NULL), + ifpart(NULL), + elsepart(NULL) +{ +} + +Ternary::Ternary(Expression* a, Expression* b, Expression* c) + :condition(a), + ifpart(b), + elsepart(c) +{ +} + +Ternary::~Ternary() +{ +} + +void +Ternary::Write(FILE* to) +{ + fprintf(to, "(("); + this->condition->Write(to); + fprintf(to, ")?("); + this->ifpart->Write(to); + fprintf(to, "):("); + this->elsepart->Write(to); + fprintf(to, "))"); +} + +Cast::Cast() + :type(NULL), + expression(NULL) +{ +} + +Cast::Cast(Type* t, Expression* e) + :type(t), + expression(e) +{ +} + +Cast::~Cast() +{ +} + +void +Cast::Write(FILE* to) +{ + fprintf(to, "((%s)", this->type->QualifiedName().c_str()); + expression->Write(to); + fprintf(to, ")"); +} + +VariableDeclaration::VariableDeclaration(Variable* l, Expression* r, Type* c) + :lvalue(l), + cast(c), + rvalue(r) +{ +} + +VariableDeclaration::VariableDeclaration(Variable* l) + :lvalue(l), + cast(NULL), + rvalue(NULL) +{ +} + +VariableDeclaration::~VariableDeclaration() +{ +} + +void +VariableDeclaration::Write(FILE* to) +{ + this->lvalue->WriteDeclaration(to); + if (this->rvalue != NULL) { + fprintf(to, " = "); + if (this->cast != NULL) { + fprintf(to, "(%s)", this->cast->QualifiedName().c_str()); + } + this->rvalue->Write(to); + } + fprintf(to, ";\n"); +} + +IfStatement::IfStatement() + :expression(NULL), + statements(new StatementBlock), + elseif(NULL) +{ +} + +IfStatement::~IfStatement() +{ +} + +void +IfStatement::Write(FILE* to) +{ + if (this->expression != NULL) { + fprintf(to, "if ("); + this->expression->Write(to); + fprintf(to, ") "); + } + this->statements->Write(to); + if (this->elseif != NULL) { + fprintf(to, "else "); + this->elseif->Write(to); + } +} + +ReturnStatement::ReturnStatement(Expression* e) + :expression(e) +{ +} + +ReturnStatement::~ReturnStatement() +{ +} + +void +ReturnStatement::Write(FILE* to) +{ + fprintf(to, "return "); + this->expression->Write(to); + fprintf(to, ";\n"); +} + +TryStatement::TryStatement() + :statements(new StatementBlock) +{ +} + +TryStatement::~TryStatement() +{ +} + +void +TryStatement::Write(FILE* to) +{ + fprintf(to, "try "); + this->statements->Write(to); +} + +CatchStatement::CatchStatement(Variable* e) + :statements(new StatementBlock), + exception(e) +{ +} + +CatchStatement::~CatchStatement() +{ +} + +void +CatchStatement::Write(FILE* to) +{ + fprintf(to, "catch "); + if (this->exception != NULL) { + fprintf(to, "("); + this->exception->WriteDeclaration(to); + fprintf(to, ") "); + } + this->statements->Write(to); +} + +FinallyStatement::FinallyStatement() + :statements(new StatementBlock) +{ +} + +FinallyStatement::~FinallyStatement() +{ +} + +void +FinallyStatement::Write(FILE* to) +{ + fprintf(to, "finally "); + this->statements->Write(to); +} + +Case::Case() + :statements(new StatementBlock) +{ +} + +Case::Case(const string& c) + :statements(new StatementBlock) +{ + cases.push_back(c); +} + +Case::~Case() +{ +} + +void +Case::Write(FILE* to) +{ + int N = this->cases.size(); + if (N > 0) { + for (int i=0; i<N; i++) { + string s = this->cases[i]; + if (s.length() != 0) { + fprintf(to, "case %s:\n", s.c_str()); + } else { + fprintf(to, "default:\n"); + } + } + } else { + fprintf(to, "default:\n"); + } + statements->Write(to); +} + +SwitchStatement::SwitchStatement(Expression* e) + :expression(e) +{ +} + +SwitchStatement::~SwitchStatement() +{ +} + +void +SwitchStatement::Write(FILE* to) +{ + fprintf(to, "switch ("); + this->expression->Write(to); + fprintf(to, ")\n{\n"); + int N = this->cases.size(); + for (int i=0; i<N; i++) { + this->cases[i]->Write(to); + } + fprintf(to, "}\n"); +} + +Break::Break() +{ +} + +Break::~Break() +{ +} + +void +Break::Write(FILE* to) +{ + fprintf(to, "break;\n"); +} + +Method::Method() + :ClassElement(), + modifiers(0), + returnType(NULL), // (NULL means constructor) + returnTypeDimension(0), + statements(NULL) +{ +} + +Method::~Method() +{ +} + +void +Method::GatherTypes(set<Type*>* types) const +{ + size_t N, i; + + if (this->returnType) { + types->insert(this->returnType); + } + + N = this->parameters.size(); + for (i=0; i<N; i++) { + this->parameters[i]->GatherTypes(types); + } + + N = this->exceptions.size(); + for (i=0; i<N; i++) { + types->insert(this->exceptions[i]); + } +} + +void +Method::Write(FILE* to) +{ + size_t N, i; + + if (this->comment.length() != 0) { + fprintf(to, "%s\n", this->comment.c_str()); + } + + WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | ABSTRACT | FINAL | OVERRIDE); + + if (this->returnType != NULL) { + string dim; + for (i=0; i<this->returnTypeDimension; i++) { + dim += "[]"; + } + fprintf(to, "%s%s ", this->returnType->QualifiedName().c_str(), + dim.c_str()); + } + + fprintf(to, "%s(", this->name.c_str()); + + N = this->parameters.size(); + for (i=0; i<N; i++) { + this->parameters[i]->WriteDeclaration(to); + if (i != N-1) { + fprintf(to, ", "); + } + } + + fprintf(to, ")"); + + N = this->exceptions.size(); + for (i=0; i<N; i++) { + if (i == 0) { + fprintf(to, " throws "); + } else { + fprintf(to, ", "); + } + fprintf(to, "%s", this->exceptions[i]->QualifiedName().c_str()); + } + + if (this->statements == NULL) { + fprintf(to, ";\n"); + } else { + fprintf(to, "\n"); + this->statements->Write(to); + } +} + +Class::Class() + :modifiers(0), + what(CLASS), + type(NULL), + extends(NULL) +{ +} + +Class::~Class() +{ +} + +void +Class::GatherTypes(set<Type*>* types) const +{ + int N, i; + + types->insert(this->type); + if (this->extends != NULL) { + types->insert(this->extends); + } + + N = this->interfaces.size(); + for (i=0; i<N; i++) { + types->insert(this->interfaces[i]); + } + + N = this->elements.size(); + for (i=0; i<N; i++) { + this->elements[i]->GatherTypes(types); + } +} + +void +Class::Write(FILE* to) +{ + size_t N, i; + + if (this->comment.length() != 0) { + fprintf(to, "%s\n", this->comment.c_str()); + } + + WriteModifiers(to, this->modifiers, ALL_MODIFIERS); + + if (this->what == Class::CLASS) { + fprintf(to, "class "); + } else { + fprintf(to, "interface "); + } + + string name = this->type->Name(); + size_t pos = name.rfind('.'); + if (pos != string::npos) { + name = name.c_str() + pos + 1; + } + + fprintf(to, "%s", name.c_str()); + + if (this->extends != NULL) { + fprintf(to, " extends %s", this->extends->QualifiedName().c_str()); + } + + N = this->interfaces.size(); + if (N != 0) { + if (this->what == Class::CLASS) { + fprintf(to, " implements"); + } else { + fprintf(to, " extends"); + } + for (i=0; i<N; i++) { + fprintf(to, " %s", this->interfaces[i]->QualifiedName().c_str()); + } + } + + fprintf(to, "\n"); + fprintf(to, "{\n"); + + N = this->elements.size(); + for (i=0; i<N; i++) { + this->elements[i]->Write(to); + } + + fprintf(to, "}\n"); + +} + +Document::Document() +{ +} + +Document::~Document() +{ +} + +static string +escape_backslashes(const string& str) +{ + string result; + const size_t I=str.length(); + for (size_t i=0; i<I; i++) { + char c = str[i]; + if (c == '\\') { + result += "\\\\"; + } else { + result += c; + } + } + return result; +} + +void +Document::Write(FILE* to) +{ + size_t N, i; + + if (this->comment.length() != 0) { + fprintf(to, "%s\n", this->comment.c_str()); + } + fprintf(to, "/*\n" + " * This file is auto-generated. DO NOT MODIFY.\n" + " * Original file: %s\n" + " */\n", escape_backslashes(this->originalSrc).c_str()); + if (this->package.length() != 0) { + fprintf(to, "package %s;\n", this->package.c_str()); + } + + N = this->classes.size(); + for (i=0; i<N; i++) { + Class* c = this->classes[i]; + c->Write(to); + } +} + diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h new file mode 100644 index 0000000..ead5e7a --- /dev/null +++ b/tools/aidl/AST.h @@ -0,0 +1,371 @@ +#ifndef AIDL_AST_H +#define AIDL_AST_H + +#include <string> +#include <vector> +#include <set> +#include <stdarg.h> +#include <stdio.h> + +using namespace std; + +class Type; + +enum { + PACKAGE_PRIVATE = 0x00000000, + PUBLIC = 0x00000001, + PRIVATE = 0x00000002, + PROTECTED = 0x00000003, + SCOPE_MASK = 0x00000003, + + STATIC = 0x00000010, + FINAL = 0x00000020, + ABSTRACT = 0x00000040, + + OVERRIDE = 0x00000100, + + ALL_MODIFIERS = 0xffffffff +}; + +// Write the modifiers that are set in both mod and mask +void WriteModifiers(FILE* to, int mod, int mask); + +struct ClassElement +{ + ClassElement(); + virtual ~ClassElement(); + + virtual void GatherTypes(set<Type*>* types) const = 0; + virtual void Write(FILE* to) = 0; +}; + +struct Expression +{ + virtual ~Expression(); + virtual void Write(FILE* to) = 0; +}; + +struct LiteralExpression : public Expression +{ + string value; + + LiteralExpression(const string& value); + virtual ~LiteralExpression(); + virtual void Write(FILE* to); +}; + +// TODO: also escape the contents. not needed for now +struct StringLiteralExpression : public Expression +{ + string value; + + StringLiteralExpression(const string& value); + virtual ~StringLiteralExpression(); + virtual void Write(FILE* to); +}; + +struct Variable : public Expression +{ + Type* type; + string name; + int dimension; + + Variable(); + Variable(Type* type, const string& name); + Variable(Type* type, const string& name, int dimension); + virtual ~Variable(); + + virtual void GatherTypes(set<Type*>* types) const; + void WriteDeclaration(FILE* to); + void Write(FILE* to); +}; + +struct FieldVariable : public Expression +{ + Expression* object; + Type* clazz; + string name; + + FieldVariable(Expression* object, const string& name); + FieldVariable(Type* clazz, const string& name); + virtual ~FieldVariable(); + + void Write(FILE* to); +}; + +struct Field : public ClassElement +{ + string comment; + int modifiers; + Variable *variable; + string value; + + Field(); + Field(int modifiers, Variable* variable); + virtual ~Field(); + + virtual void GatherTypes(set<Type*>* types) const; + virtual void Write(FILE* to); +}; + +struct Statement +{ + virtual ~Statement(); + virtual void Write(FILE* to) = 0; +}; + +struct StatementBlock : public Statement +{ + vector<Statement*> statements; + + StatementBlock(); + virtual ~StatementBlock(); + virtual void Write(FILE* to); + + void Add(Statement* statement); + void Add(Expression* expression); +}; + +struct ExpressionStatement : public Statement +{ + Expression* expression; + + ExpressionStatement(Expression* expression); + virtual ~ExpressionStatement(); + virtual void Write(FILE* to); +}; + +struct Assignment : public Expression +{ + Variable* lvalue; + Expression* rvalue; + Type* cast; + + Assignment(Variable* lvalue, Expression* rvalue); + Assignment(Variable* lvalue, Expression* rvalue, Type* cast); + virtual ~Assignment(); + virtual void Write(FILE* to); +}; + +struct MethodCall : public Expression +{ + Expression* obj; + Type* clazz; + string name; + vector<Expression*> arguments; + vector<string> exceptions; + + MethodCall(const string& name); + MethodCall(const string& name, int argc, ...); + MethodCall(Expression* obj, const string& name); + MethodCall(Type* clazz, const string& name); + MethodCall(Expression* obj, const string& name, int argc, ...); + MethodCall(Type* clazz, const string& name, int argc, ...); + virtual ~MethodCall(); + virtual void Write(FILE* to); + +private: + void init(int n, va_list args); +}; + +struct Comparison : public Expression +{ + Expression* lvalue; + string op; + Expression* rvalue; + + Comparison(Expression* lvalue, const string& op, Expression* rvalue); + virtual ~Comparison(); + virtual void Write(FILE* to); +}; + +struct NewExpression : public Expression +{ + Type* type; + vector<Expression*> arguments; + + NewExpression(Type* type); + NewExpression(Type* type, int argc, ...); + virtual ~NewExpression(); + virtual void Write(FILE* to); + +private: + void init(int n, va_list args); +}; + +struct NewArrayExpression : public Expression +{ + Type* type; + Expression* size; + + NewArrayExpression(Type* type, Expression* size); + virtual ~NewArrayExpression(); + virtual void Write(FILE* to); +}; + +struct Ternary : public Expression +{ + Expression* condition; + Expression* ifpart; + Expression* elsepart; + + Ternary(); + Ternary(Expression* condition, Expression* ifpart, Expression* elsepart); + virtual ~Ternary(); + virtual void Write(FILE* to); +}; + +struct Cast : public Expression +{ + Type* type; + Expression* expression; + + Cast(); + Cast(Type* type, Expression* expression); + virtual ~Cast(); + virtual void Write(FILE* to); +}; + +struct VariableDeclaration : public Statement +{ + Variable* lvalue; + Type* cast; + Expression* rvalue; + + VariableDeclaration(Variable* lvalue); + VariableDeclaration(Variable* lvalue, Expression* rvalue, Type* cast = NULL); + virtual ~VariableDeclaration(); + virtual void Write(FILE* to); +}; + +struct IfStatement : public Statement +{ + Expression* expression; + StatementBlock* statements; + IfStatement* elseif; + + IfStatement(); + virtual ~IfStatement(); + virtual void Write(FILE* to); +}; + +struct ReturnStatement : public Statement +{ + Expression* expression; + + ReturnStatement(Expression* expression); + virtual ~ReturnStatement(); + virtual void Write(FILE* to); +}; + +struct TryStatement : public Statement +{ + StatementBlock* statements; + + TryStatement(); + virtual ~TryStatement(); + virtual void Write(FILE* to); +}; + +struct CatchStatement : public Statement +{ + StatementBlock* statements; + Variable* exception; + + CatchStatement(Variable* exception); + virtual ~CatchStatement(); + virtual void Write(FILE* to); +}; + +struct FinallyStatement : public Statement +{ + StatementBlock* statements; + + FinallyStatement(); + virtual ~FinallyStatement(); + virtual void Write(FILE* to); +}; + +struct Case +{ + vector<string> cases; + StatementBlock* statements; + + Case(); + Case(const string& c); + virtual ~Case(); + virtual void Write(FILE* to); +}; + +struct SwitchStatement : public Statement +{ + Expression* expression; + vector<Case*> cases; + + SwitchStatement(Expression* expression); + virtual ~SwitchStatement(); + virtual void Write(FILE* to); +}; + +struct Break : public Statement +{ + Break(); + virtual ~Break(); + virtual void Write(FILE* to); +}; + +struct Method : public ClassElement +{ + string comment; + int modifiers; + Type* returnType; + size_t returnTypeDimension; + string name; + vector<Variable*> parameters; + vector<Type*> exceptions; + StatementBlock* statements; + + Method(); + virtual ~Method(); + + virtual void GatherTypes(set<Type*>* types) const; + virtual void Write(FILE* to); +}; + +struct Class : public ClassElement +{ + enum { + CLASS, + INTERFACE + }; + + string comment; + int modifiers; + int what; // CLASS or INTERFACE + Type* type; + Type* extends; + vector<Type*> interfaces; + vector<ClassElement*> elements; + + Class(); + virtual ~Class(); + + virtual void GatherTypes(set<Type*>* types) const; + virtual void Write(FILE* to); +}; + +struct Document +{ + string comment; + string package; + string originalSrc; + set<Type*> imports; + vector<Class*> classes; + + Document(); + virtual ~Document(); + + virtual void Write(FILE* to); +}; + +#endif // AIDL_AST_H diff --git a/tools/aidl/Android.mk b/tools/aidl/Android.mk new file mode 100644 index 0000000..77d46ab --- /dev/null +++ b/tools/aidl/Android.mk @@ -0,0 +1,29 @@ +# Copyright 2007 The Android Open Source Project +# +# Copies files into the directory structure described by a manifest + +# This tool is prebuilt if we're doing an app-only build. +ifeq ($(TARGET_BUILD_APPS),) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + aidl_language_l.l \ + aidl_language_y.y \ + aidl.cpp \ + aidl_language.cpp \ + options.cpp \ + search_path.cpp \ + AST.cpp \ + Type.cpp \ + generate_java.cpp \ + generate_java_binder.cpp \ + generate_java_rpc.cpp + +LOCAL_CFLAGS := -g +LOCAL_MODULE := aidl + +include $(BUILD_HOST_EXECUTABLE) + +endif # TARGET_BUILD_APPS diff --git a/tools/aidl/NOTICE b/tools/aidl/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/tools/aidl/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp new file mode 100644 index 0000000..d572af6 --- /dev/null +++ b/tools/aidl/Type.cpp @@ -0,0 +1,1440 @@ +#include "Type.h" + +Namespace NAMES; + +Type* VOID_TYPE; +Type* BOOLEAN_TYPE; +Type* BYTE_TYPE; +Type* CHAR_TYPE; +Type* INT_TYPE; +Type* LONG_TYPE; +Type* FLOAT_TYPE; +Type* DOUBLE_TYPE; +Type* STRING_TYPE; +Type* OBJECT_TYPE; +Type* CHAR_SEQUENCE_TYPE; +Type* TEXT_UTILS_TYPE; +Type* REMOTE_EXCEPTION_TYPE; +Type* RUNTIME_EXCEPTION_TYPE; +Type* IBINDER_TYPE; +Type* IINTERFACE_TYPE; +Type* BINDER_NATIVE_TYPE; +Type* BINDER_PROXY_TYPE; +Type* PARCEL_TYPE; +Type* PARCELABLE_INTERFACE_TYPE; +Type* CONTEXT_TYPE; +Type* MAP_TYPE; +Type* LIST_TYPE; +Type* CLASSLOADER_TYPE; +Type* RPC_DATA_TYPE; +Type* RPC_ERROR_TYPE; +Type* EVENT_FAKE_TYPE; + +Expression* NULL_VALUE; +Expression* THIS_VALUE; +Expression* SUPER_VALUE; +Expression* TRUE_VALUE; +Expression* FALSE_VALUE; + +void +register_base_types() +{ + VOID_TYPE = new BasicType("void", + "XXX", "XXX", "XXX", "XXX", "XXX", + "XXX", "XXX", "XXX", "XXX", "XXX"); + NAMES.Add(VOID_TYPE); + + BOOLEAN_TYPE = new BooleanType(); + NAMES.Add(BOOLEAN_TYPE); + + BYTE_TYPE = new BasicType("byte", + "writeByte", "readByte", "writeByteArray", "createByteArray", "readByteArray", + "putByte", "getByte", "putByteArray", "createByteArray", "getByteArray"); + NAMES.Add(BYTE_TYPE); + + CHAR_TYPE = new CharType(); + NAMES.Add(CHAR_TYPE); + + INT_TYPE = new BasicType("int", + "writeInt", "readInt", "writeIntArray", "createIntArray", "readIntArray", + "putInteger", "getInteger", "putIntegerArray", "createIntegerArray", "getIntegerArray"); + NAMES.Add(INT_TYPE); + + LONG_TYPE = new BasicType("long", + "writeLong", "readLong", "writeLongArray", "createLongArray", "readLongArray", + "putLong", "getLong", "putLongArray", "createLongArray", "getLongArray"); + NAMES.Add(LONG_TYPE); + + FLOAT_TYPE = new BasicType("float", + "writeFloat", "readFloat", "writeFloatArray", "createFloatArray", "readFloatArray", + "putFloat", "getFloat", "putFloatArray", "createFloatArray", "getFloatArray"); + NAMES.Add(FLOAT_TYPE); + + DOUBLE_TYPE = new BasicType("double", + "writeDouble", "readDouble", "writeDoubleArray", "createDoubleArray", "readDoubleArray", + "putDouble", "getDouble", "putDoubleArray", "createDoubleArray", "getDoubleArray"); + NAMES.Add(DOUBLE_TYPE); + + STRING_TYPE = new StringType(); + NAMES.Add(STRING_TYPE); + + OBJECT_TYPE = new Type("java.lang", "Object", Type::BUILT_IN, false, false, false); + NAMES.Add(OBJECT_TYPE); + + CHAR_SEQUENCE_TYPE = new CharSequenceType(); + NAMES.Add(CHAR_SEQUENCE_TYPE); + + MAP_TYPE = new MapType(); + NAMES.Add(MAP_TYPE); + + LIST_TYPE = new ListType(); + NAMES.Add(LIST_TYPE); + + TEXT_UTILS_TYPE = new Type("android.text", "TextUtils", Type::BUILT_IN, false, false, false); + NAMES.Add(TEXT_UTILS_TYPE); + + REMOTE_EXCEPTION_TYPE = new RemoteExceptionType(); + NAMES.Add(REMOTE_EXCEPTION_TYPE); + + RUNTIME_EXCEPTION_TYPE = new RuntimeExceptionType(); + NAMES.Add(RUNTIME_EXCEPTION_TYPE); + + IBINDER_TYPE = new IBinderType(); + NAMES.Add(IBINDER_TYPE); + + IINTERFACE_TYPE = new IInterfaceType(); + NAMES.Add(IINTERFACE_TYPE); + + BINDER_NATIVE_TYPE = new BinderType(); + NAMES.Add(BINDER_NATIVE_TYPE); + + BINDER_PROXY_TYPE = new BinderProxyType(); + NAMES.Add(BINDER_PROXY_TYPE); + + PARCEL_TYPE = new ParcelType(); + NAMES.Add(PARCEL_TYPE); + + PARCELABLE_INTERFACE_TYPE = new ParcelableInterfaceType(); + NAMES.Add(PARCELABLE_INTERFACE_TYPE); + + CONTEXT_TYPE = new Type("android.content", "Context", Type::BUILT_IN, false, false, false); + NAMES.Add(CONTEXT_TYPE); + + RPC_DATA_TYPE = new RpcDataType(); + NAMES.Add(RPC_DATA_TYPE); + + RPC_ERROR_TYPE = new UserDataType("android.support.place.rpc", "RpcError", + true, __FILE__, __LINE__); + NAMES.Add(RPC_ERROR_TYPE); + + EVENT_FAKE_TYPE = new Type("event", Type::BUILT_IN, false, false, false); + NAMES.Add(EVENT_FAKE_TYPE); + + CLASSLOADER_TYPE = new ClassLoaderType(); + NAMES.Add(CLASSLOADER_TYPE); + + NULL_VALUE = new LiteralExpression("null"); + THIS_VALUE = new LiteralExpression("this"); + SUPER_VALUE = new LiteralExpression("super"); + TRUE_VALUE = new LiteralExpression("true"); + FALSE_VALUE = new LiteralExpression("false"); + + NAMES.AddGenericType("java.util", "List", 1); + NAMES.AddGenericType("java.util", "Map", 2); +} + +static Type* +make_generic_type(const string& package, const string& name, + const vector<Type*>& args) +{ + if (package == "java.util" && name == "List") { + return new GenericListType("java.util", "List", args); + } + return NULL; + //return new GenericType(package, name, args); +} + +// ================================================================ + +Type::Type(const string& name, int kind, bool canWriteToParcel, bool canWriteToRpcData, + bool canBeOut) + :m_package(), + m_name(name), + m_declFile(""), + m_declLine(-1), + m_kind(kind), + m_canWriteToParcel(canWriteToParcel), + m_canWriteToRpcData(canWriteToRpcData), + m_canBeOut(canBeOut) +{ + m_qualifiedName = name; +} + +Type::Type(const string& package, const string& name, + int kind, bool canWriteToParcel, bool canWriteToRpcData, + bool canBeOut, const string& declFile, int declLine) + :m_package(package), + m_name(name), + m_declFile(declFile), + m_declLine(declLine), + m_kind(kind), + m_canWriteToParcel(canWriteToParcel), + m_canWriteToRpcData(canWriteToRpcData), + m_canBeOut(canBeOut) +{ + if (package.length() > 0) { + m_qualifiedName = package; + m_qualifiedName += '.'; + } + m_qualifiedName += name; +} + +Type::~Type() +{ +} + +bool +Type::CanBeArray() const +{ + return false; +} + +string +Type::ImportType() const +{ + return m_qualifiedName; +} + +string +Type::CreatorName() const +{ + return ""; +} + +string +Type::RpcCreatorName() const +{ + return ""; +} + +string +Type::InstantiableName() const +{ + return QualifiedName(); +} + + +void +Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%sn", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* WriteToParcel error " + + m_qualifiedName + " */")); +} + +void +Type::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* CreateFromParcel error " + + m_qualifiedName + " */")); +} + +void +Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* ReadFromParcel error " + + m_qualifiedName + " */")); +} + +void +Type::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* WriteArrayToParcel error " + + m_qualifiedName + " */")); +} + +void +Type::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* CreateArrayFromParcel error " + + m_qualifiedName + " */")); +} + +void +Type::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* ReadArrayFromParcel error " + + m_qualifiedName + " */")); +} + +void +Type::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* WriteToRpcData error " + + m_qualifiedName + " */")); +} + +void +Type::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", + __FILE__, __LINE__, m_qualifiedName.c_str()); + addTo->Add(new LiteralExpression("/* ReadFromRpcData error " + + m_qualifiedName + " */")); +} + +void +Type::SetQualifiedName(const string& qualified) +{ + m_qualifiedName = qualified; +} + +Expression* +Type::BuildWriteToParcelFlags(int flags) +{ + if (flags == 0) { + return new LiteralExpression("0"); + } + if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0) { + return new FieldVariable(PARCELABLE_INTERFACE_TYPE, + "PARCELABLE_WRITE_RETURN_VALUE"); + } + return new LiteralExpression("0"); +} + +// ================================================================ + +BasicType::BasicType(const string& name, const string& marshallParcel, + const string& unmarshallParcel, const string& writeArrayParcel, + const string& createArrayParcel, const string& readArrayParcel, + const string& marshallRpc, const string& unmarshallRpc, + const string& writeArrayRpc, const string& createArrayRpc, const string& readArrayRpc) + :Type(name, BUILT_IN, true, true, false), + m_marshallParcel(marshallParcel), + m_unmarshallParcel(unmarshallParcel), + m_writeArrayParcel(writeArrayParcel), + m_createArrayParcel(createArrayParcel), + m_readArrayParcel(readArrayParcel), + m_marshallRpc(marshallRpc), + m_unmarshallRpc(unmarshallRpc), + m_writeArrayRpc(writeArrayRpc), + m_createArrayRpc(createArrayRpc), + m_readArrayRpc(readArrayRpc) +{ +} + +void +BasicType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, m_marshallParcel, 1, v)); +} + +void +BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallParcel))); +} + +bool +BasicType::CanBeArray() const +{ + return true; +} + +void +BasicType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, m_writeArrayParcel, 1, v)); +} + +void +BasicType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayParcel))); +} + +void +BasicType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new MethodCall(parcel, m_readArrayParcel, 1, v)); +} + +void +BasicType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, m_marshallRpc, 2, k, v)); +} + +void +BasicType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + addTo->Add(new Assignment(v, new MethodCall(data, m_unmarshallRpc, 1, k))); +} + +// ================================================================ + +BooleanType::BooleanType() + :Type("boolean", BUILT_IN, true, true, false) +{ +} + +void +BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeInt", 1, + new Ternary(v, new LiteralExpression("1"), + new LiteralExpression("0")))); +} + +void +BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new Comparison(new LiteralExpression("0"), + "!=", new MethodCall(parcel, "readInt")))); +} + +bool +BooleanType::CanBeArray() const +{ + return true; +} + +void +BooleanType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeBooleanArray", 1, v)); +} + +void +BooleanType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray"))); +} + +void +BooleanType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v)); +} + +void +BooleanType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, "putBoolean", 2, k, v)); +} + +void +BooleanType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + addTo->Add(new Assignment(v, new MethodCall(data, "getBoolean", 1, k))); +} + +// ================================================================ + +CharType::CharType() + :Type("char", BUILT_IN, true, true, false) +{ +} + +void +CharType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeInt", 1, + new Cast(INT_TYPE, v))); +} + +void +CharType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this)); +} + +bool +CharType::CanBeArray() const +{ + return true; +} + +void +CharType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeCharArray", 1, v)); +} + +void +CharType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray"))); +} + +void +CharType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new MethodCall(parcel, "readCharArray", 1, v)); +} + +void +CharType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, "putChar", 2, k, v)); +} + +void +CharType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + addTo->Add(new Assignment(v, new MethodCall(data, "getChar", 1, k))); +} + +// ================================================================ + +StringType::StringType() + :Type("java.lang", "String", BUILT_IN, true, true, false) +{ +} + +string +StringType::CreatorName() const +{ + return "android.os.Parcel.STRING_CREATOR"; +} + +void +StringType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeString", 1, v)); +} + +void +StringType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "readString"))); +} + +bool +StringType::CanBeArray() const +{ + return true; +} + +void +StringType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeStringArray", 1, v)); +} + +void +StringType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray"))); +} + +void +StringType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new MethodCall(parcel, "readStringArray", 1, v)); +} + +void +StringType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, "putString", 2, k, v)); +} + +void +StringType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(data, "getString", 1, k))); +} + +// ================================================================ + +CharSequenceType::CharSequenceType() + :Type("java.lang", "CharSequence", BUILT_IN, true, true, false) +{ +} + +string +CharSequenceType::CreatorName() const +{ + return "android.os.Parcel.STRING_CREATOR"; +} + +void +CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + // if (v != null) { + // parcel.writeInt(1); + // v.writeToParcel(parcel); + // } else { + // parcel.writeInt(0); + // } + IfStatement* elsepart = new IfStatement(); + elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1, + new LiteralExpression("0"))); + IfStatement* ifpart = new IfStatement; + ifpart->expression = new Comparison(v, "!=", NULL_VALUE); + ifpart->elseif = elsepart; + ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1, + new LiteralExpression("1"))); + ifpart->statements->Add(new MethodCall(TEXT_UTILS_TYPE, "writeToParcel", + 3, v, parcel, BuildWriteToParcelFlags(flags))); + + addTo->Add(ifpart); +} + +void +CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + // if (0 != parcel.readInt()) { + // v = TextUtils.createFromParcel(parcel) + // } else { + // v = null; + // } + IfStatement* elsepart = new IfStatement(); + elsepart->statements->Add(new Assignment(v, NULL_VALUE)); + + IfStatement* ifpart = new IfStatement(); + ifpart->expression = new Comparison(new LiteralExpression("0"), "!=", + new MethodCall(parcel, "readInt")); + ifpart->elseif = elsepart; + ifpart->statements->Add(new Assignment(v, + new MethodCall(TEXT_UTILS_TYPE, + "CHAR_SEQUENCE_CREATOR.createFromParcel", 1, parcel))); + + addTo->Add(ifpart); +} + + +// ================================================================ + +RemoteExceptionType::RemoteExceptionType() + :Type("android.os", "RemoteException", BUILT_IN, false, false, false) +{ +} + +void +RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +// ================================================================ + +RuntimeExceptionType::RuntimeExceptionType() + :Type("java.lang", "RuntimeException", BUILT_IN, false, false, false) +{ +} + +void +RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + + +// ================================================================ + +IBinderType::IBinderType() + :Type("android.os", "IBinder", BUILT_IN, true, false, false) +{ +} + +void +IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, v)); +} + +void +IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder"))); +} + +void +IBinderType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeBinderArray", 1, v)); +} + +void +IBinderType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray"))); +} + +void +IBinderType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v)); +} + + +// ================================================================ + +IInterfaceType::IInterfaceType() + :Type("android.os", "IInterface", BUILT_IN, false, false, false) +{ +} + +void +IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + + +// ================================================================ + +BinderType::BinderType() + :Type("android.os", "Binder", BUILT_IN, false, false, false) +{ +} + +void +BinderType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + + +// ================================================================ + +BinderProxyType::BinderProxyType() + :Type("android.os", "BinderProxy", BUILT_IN, false, false, false) +{ +} + +void +BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + + +// ================================================================ + +ParcelType::ParcelType() + :Type("android.os", "Parcel", BUILT_IN, false, false, false) +{ +} + +void +ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +// ================================================================ + +ParcelableInterfaceType::ParcelableInterfaceType() + :Type("android.os", "Parcelable", BUILT_IN, false, false, false) +{ +} + +void +ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +void +ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__); +} + +// ================================================================ + +MapType::MapType() + :Type("java.util", "Map", BUILT_IN, true, false, true) +{ +} + +void +MapType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeMap", 1, v)); +} + +static void EnsureClassLoader(StatementBlock* addTo, Variable** cl) +{ + // We don't want to look up the class loader once for every + // collection argument, so ensure we do it at most once per method. + if (*cl == NULL) { + *cl = new Variable(CLASSLOADER_TYPE, "cl"); + addTo->Add(new VariableDeclaration(*cl, + new LiteralExpression("this.getClass().getClassLoader()"), + CLASSLOADER_TYPE)); + } +} + +void +MapType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl) +{ + EnsureClassLoader(addTo, cl); + addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, *cl))); +} + +void +MapType::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl) +{ + EnsureClassLoader(addTo, cl); + addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl)); +} + + +// ================================================================ + +ListType::ListType() + :Type("java.util", "List", BUILT_IN, true, true, true) +{ +} + +string +ListType::InstantiableName() const +{ + return "java.util.ArrayList"; +} + +void +ListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeList", 1, v)); +} + +void +ListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable** cl) +{ + EnsureClassLoader(addTo, cl); + addTo->Add(new Assignment(v, new MethodCall(parcel, "readArrayList", 1, *cl))); +} + +void +ListType::ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl) +{ + EnsureClassLoader(addTo, cl); + addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl)); +} + +void +ListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, "putList", 2, k, v)); +} + +void +ListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + addTo->Add(new Assignment(v, new MethodCall(data, "getList", 1, k))); +} + +// ================================================================ + +UserDataType::UserDataType(const string& package, const string& name, + bool builtIn, bool canWriteToParcel, bool canWriteToRpcData, + const string& declFile, int declLine) + :Type(package, name, builtIn ? BUILT_IN : USERDATA, canWriteToParcel, canWriteToRpcData, + true, declFile, declLine) +{ +} + +string +UserDataType::CreatorName() const +{ + return QualifiedName() + ".CREATOR"; +} + +string +UserDataType::RpcCreatorName() const +{ + return QualifiedName() + ".RPC_CREATOR"; +} + +void +UserDataType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + // if (v != null) { + // parcel.writeInt(1); + // v.writeToParcel(parcel); + // } else { + // parcel.writeInt(0); + // } + IfStatement* elsepart = new IfStatement(); + elsepart->statements->Add(new MethodCall(parcel, "writeInt", 1, + new LiteralExpression("0"))); + IfStatement* ifpart = new IfStatement; + ifpart->expression = new Comparison(v, "!=", NULL_VALUE); + ifpart->elseif = elsepart; + ifpart->statements->Add(new MethodCall(parcel, "writeInt", 1, + new LiteralExpression("1"))); + ifpart->statements->Add(new MethodCall(v, "writeToParcel", 2, + parcel, BuildWriteToParcelFlags(flags))); + + addTo->Add(ifpart); +} + +void +UserDataType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + // if (0 != parcel.readInt()) { + // v = CLASS.CREATOR.createFromParcel(parcel) + // } else { + // v = null; + // } + IfStatement* elsepart = new IfStatement(); + elsepart->statements->Add(new Assignment(v, NULL_VALUE)); + + IfStatement* ifpart = new IfStatement(); + ifpart->expression = new Comparison(new LiteralExpression("0"), "!=", + new MethodCall(parcel, "readInt")); + ifpart->elseif = elsepart; + ifpart->statements->Add(new Assignment(v, + new MethodCall(v->type, "CREATOR.createFromParcel", 1, parcel))); + + addTo->Add(ifpart); +} + +void +UserDataType::ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + // TODO: really, we don't need to have this extra check, but we + // don't have two separate marshalling code paths + // if (0 != parcel.readInt()) { + // v.readFromParcel(parcel) + // } + IfStatement* ifpart = new IfStatement(); + ifpart->expression = new Comparison(new LiteralExpression("0"), "!=", + new MethodCall(parcel, "readInt")); + ifpart->statements->Add(new MethodCall(v, "readFromParcel", 1, parcel)); + addTo->Add(ifpart); +} + +bool +UserDataType::CanBeArray() const +{ + return true; +} + +void +UserDataType::WriteArrayToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v, + BuildWriteToParcelFlags(flags))); +} + +void +UserDataType::CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + string creator = v->type->QualifiedName() + ".CREATOR"; + addTo->Add(new Assignment(v, new MethodCall(parcel, + "createTypedArray", 1, new LiteralExpression(creator)))); +} + +void +UserDataType::ReadArrayFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + string creator = v->type->QualifiedName() + ".CREATOR"; + addTo->Add(new MethodCall(parcel, "readTypedArray", 2, + v, new LiteralExpression(creator))); +} + +void +UserDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + // data.putFlattenable(k, v); + addTo->Add(new MethodCall(data, "putFlattenable", 2, k, v)); +} + +void +UserDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl) +{ + // data.getFlattenable(k, CLASS.RPC_CREATOR); + addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenable", 2, k, + new FieldVariable(v->type, "RPC_CREATOR")))); +} + +// ================================================================ + +InterfaceType::InterfaceType(const string& package, const string& name, + bool builtIn, bool oneway, + const string& declFile, int declLine) + :Type(package, name, builtIn ? BUILT_IN : INTERFACE, true, false, false, + declFile, declLine) + ,m_oneway(oneway) +{ +} + +bool +InterfaceType::OneWay() const +{ + return m_oneway; +} + +void +InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + // parcel.writeStrongBinder(v != null ? v.asBinder() : null); + addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, + new Ternary( + new Comparison(v, "!=", NULL_VALUE), + new MethodCall(v, "asBinder"), + NULL_VALUE))); +} + +void +InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + // v = Interface.asInterface(parcel.readStrongBinder()); + string type = v->type->QualifiedName(); + type += ".Stub"; + addTo->Add(new Assignment(v, + new MethodCall( NAMES.Find(type), "asInterface", 1, + new MethodCall(parcel, "readStrongBinder")))); +} + + +// ================================================================ + +GenericType::GenericType(const string& package, const string& name, + const vector<Type*>& args) + :Type(package, name, BUILT_IN, true, true, true) +{ + m_args = args; + + m_importName = package + '.' + name; + + string gen = "<"; + int N = args.size(); + for (int i=0; i<N; i++) { + Type* t = args[i]; + gen += t->QualifiedName(); + if (i != N-1) { + gen += ','; + } + } + gen += '>'; + m_genericArguments = gen; + SetQualifiedName(m_importName + gen); +} + +const vector<Type*>& +GenericType::GenericArgumentTypes() const +{ + return m_args; +} + +string +GenericType::GenericArguments() const +{ + return m_genericArguments; +} + +string +GenericType::ImportType() const +{ + return m_importName; +} + +void +GenericType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + fprintf(stderr, "implement GenericType::WriteToParcel\n"); +} + +void +GenericType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + fprintf(stderr, "implement GenericType::CreateFromParcel\n"); +} + +void +GenericType::ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + fprintf(stderr, "implement GenericType::ReadFromParcel\n"); +} + + +// ================================================================ + +GenericListType::GenericListType(const string& package, const string& name, + const vector<Type*>& args) + :GenericType(package, name, args), + m_creator(args[0]->CreatorName()) +{ +} + +string +GenericListType::CreatorName() const +{ + return "android.os.Parcel.arrayListCreator"; +} + +string +GenericListType::InstantiableName() const +{ + return "java.util.ArrayList" + GenericArguments(); +} + +void +GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel, int flags) +{ + if (m_creator == STRING_TYPE->CreatorName()) { + addTo->Add(new MethodCall(parcel, "writeStringList", 1, v)); + } else if (m_creator == IBINDER_TYPE->CreatorName()) { + addTo->Add(new MethodCall(parcel, "writeBinderList", 1, v)); + } else { + // parcel.writeTypedListXX(arg); + addTo->Add(new MethodCall(parcel, "writeTypedList", 1, v)); + } +} + +void +GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel, Variable**) +{ + if (m_creator == STRING_TYPE->CreatorName()) { + addTo->Add(new Assignment(v, + new MethodCall(parcel, "createStringArrayList", 0))); + } else if (m_creator == IBINDER_TYPE->CreatorName()) { + addTo->Add(new Assignment(v, + new MethodCall(parcel, "createBinderArrayList", 0))); + } else { + // v = _data.readTypedArrayList(XXX.creator); + addTo->Add(new Assignment(v, + new MethodCall(parcel, "createTypedArrayList", 1, + new LiteralExpression(m_creator)))); + } +} + +void +GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable**) +{ + if (m_creator == STRING_TYPE->CreatorName()) { + addTo->Add(new MethodCall(parcel, "readStringList", 1, v)); + } else if (m_creator == IBINDER_TYPE->CreatorName()) { + addTo->Add(new MethodCall(parcel, "readBinderList", 1, v)); + } else { + // v = _data.readTypedList(v, XXX.creator); + addTo->Add(new MethodCall(parcel, "readTypedList", 2, + v, + new LiteralExpression(m_creator))); + } +} + +void +GenericListType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + Type* generic = GenericArgumentTypes()[0]; + if (generic == RPC_DATA_TYPE) { + addTo->Add(new MethodCall(data, "putRpcDataList", 2, k, v)); + } else if (generic->RpcCreatorName() != "") { + addTo->Add(new MethodCall(data, "putFlattenableList", 2, k, v)); + } else { + addTo->Add(new MethodCall(data, "putList", 2, k, v)); + } +} + +void +GenericListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl) +{ + Type* generic = GenericArgumentTypes()[0]; + if (generic == RPC_DATA_TYPE) { + addTo->Add(new Assignment(v, new MethodCall(data, "getRpcDataList", 2, k))); + } else if (generic->RpcCreatorName() != "") { + addTo->Add(new Assignment(v, new MethodCall(data, "getFlattenableList", 2, k, + new LiteralExpression(generic->RpcCreatorName())))); + } else { + string classArg = GenericArgumentTypes()[0]->QualifiedName(); + classArg += ".class"; + addTo->Add(new Assignment(v, new MethodCall(data, "getList", 2, k, + new LiteralExpression(classArg)))); + } +} + + +// ================================================================ + +RpcDataType::RpcDataType() + :UserDataType("android.support.place.rpc", "RpcData", true, true, true) +{ +} + +void +RpcDataType::WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags) +{ + addTo->Add(new MethodCall(data, "putRpcData", 2, k, v)); +} + +void +RpcDataType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, Variable* data, + Variable** cl) +{ + addTo->Add(new Assignment(v, new MethodCall(data, "getRpcData", 1, k))); +} + + +// ================================================================ + +ClassLoaderType::ClassLoaderType() + :Type("java.lang", "ClassLoader", BUILT_IN, false, false, false) +{ +} + + +// ================================================================ + +Namespace::Namespace() +{ +} + +Namespace::~Namespace() +{ + int N = m_types.size(); + for (int i=0; i<N; i++) { + delete m_types[i]; + } +} + +void +Namespace::Add(Type* type) +{ + Type* t = Find(type->QualifiedName()); + if (t == NULL) { + m_types.push_back(type); + } +} + +void +Namespace::AddGenericType(const string& package, const string& name, int args) +{ + Generic g; + g.package = package; + g.name = name; + g.qualified = package + '.' + name; + g.args = args; + m_generics.push_back(g); +} + +Type* +Namespace::Find(const string& name) const +{ + int N = m_types.size(); + for (int i=0; i<N; i++) { + if (m_types[i]->QualifiedName() == name) { + return m_types[i]; + } + } + return NULL; +} + +Type* +Namespace::Find(const char* package, const char* name) const +{ + string s; + if (package != NULL) { + s += package; + s += '.'; + } + s += name; + return Find(s); +} + +static string +normalize_generic(const string& s) +{ + string r; + int N = s.size(); + for (int i=0; i<N; i++) { + char c = s[i]; + if (!isspace(c)) { + r += c; + } + } + return r; +} + +Type* +Namespace::Search(const string& name) +{ + // an exact match wins + Type* result = Find(name); + if (result != NULL) { + return result; + } + + // try the class names + // our language doesn't allow you to not specify outer classes + // when referencing an inner class. that could be changed, and this + // would be the place to do it, but I don't think the complexity in + // scoping rules is worth it. + int N = m_types.size(); + for (int i=0; i<N; i++) { + if (m_types[i]->Name() == name) { + return m_types[i]; + } + } + + // we got to here and it's not a generic, give up + if (name.find('<') == name.npos) { + return NULL; + } + + // remove any whitespace + string normalized = normalize_generic(name); + + // find the part before the '<', find a generic for it + ssize_t baseIndex = normalized.find('<'); + string base(normalized.c_str(), baseIndex); + const Generic* g = search_generic(base); + if (g == NULL) { + return NULL; + } + + // For each of the args, do a recursive search on it. We don't allow + // generics within generics like Java does, because we're really limiting + // them to just built-in container classes, at least for now. Our syntax + // ensures this right now as well. + vector<Type*> args; + size_t start = baseIndex + 1; + size_t end = start; + while (normalized[start] != '\0') { + end = normalized.find(',', start); + if (end == normalized.npos) { + end = normalized.find('>', start); + } + string s(normalized.c_str()+start, end-start); + Type* t = this->Search(s); + if (t == NULL) { + // maybe we should print a warning here? + return NULL; + } + args.push_back(t); + start = end+1; + } + + // construct a GenericType, add it to our name set so they always get + // the same object, and return it. + result = make_generic_type(g->package, g->name, args); + if (result == NULL) { + return NULL; + } + + this->Add(result); + return this->Find(result->QualifiedName()); +} + +const Namespace::Generic* +Namespace::search_generic(const string& name) const +{ + int N = m_generics.size(); + + // first exact match + for (int i=0; i<N; i++) { + const Generic& g = m_generics[i]; + if (g.qualified == name) { + return &g; + } + } + + // then name match + for (int i=0; i<N; i++) { + const Generic& g = m_generics[i]; + if (g.name == name) { + return &g; + } + } + + return NULL; +} + +void +Namespace::Dump() const +{ + int n = m_types.size(); + for (int i=0; i<n; i++) { + Type* t = m_types[i]; + printf("type: package=%s name=%s qualifiedName=%s\n", + t->Package().c_str(), t->Name().c_str(), + t->QualifiedName().c_str()); + } +} diff --git a/tools/aidl/Type.h b/tools/aidl/Type.h new file mode 100644 index 0000000..ae12720 --- /dev/null +++ b/tools/aidl/Type.h @@ -0,0 +1,542 @@ +#ifndef AIDL_TYPE_H +#define AIDL_TYPE_H + +#include "AST.h" +#include <string> +#include <vector> + +using namespace std; + +class Type +{ +public: + // kinds + enum { + BUILT_IN, + USERDATA, + INTERFACE, + GENERATED + }; + + // WriteToParcel flags + enum { + PARCELABLE_WRITE_RETURN_VALUE = 0x0001 + }; + + Type(const string& name, int kind, bool canWriteToParcel, + bool canWriteToRpcData, bool canBeOut); + Type(const string& package, const string& name, + int kind, bool canWriteToParcel, bool canWriteToRpcData, bool canBeOut, + const string& declFile = "", int declLine = -1); + virtual ~Type(); + + inline string Package() const { return m_package; } + inline string Name() const { return m_name; } + inline string QualifiedName() const { return m_qualifiedName; } + inline int Kind() const { return m_kind; } + inline string DeclFile() const { return m_declFile; } + inline int DeclLine() const { return m_declLine; } + inline bool CanWriteToParcel() const { return m_canWriteToParcel; } + inline bool CanWriteToRpcData() const { return m_canWriteToRpcData; } + inline bool CanBeOutParameter() const { return m_canBeOut; } + + virtual string ImportType() const; + virtual string CreatorName() const; + virtual string RpcCreatorName() const; + virtual string InstantiableName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); + +protected: + void SetQualifiedName(const string& qualified); + Expression* BuildWriteToParcelFlags(int flags); + +private: + Type(); + Type(const Type&); + + string m_package; + string m_name; + string m_qualifiedName; + string m_declFile; + int m_declLine; + int m_kind; + bool m_canWriteToParcel; + bool m_canWriteToRpcData; + bool m_canBeOut; +}; + +class BasicType : public Type +{ +public: + BasicType(const string& name, + const string& marshallParcel, + const string& unmarshallParcel, + const string& writeArrayParcel, + const string& createArrayParcel, + const string& readArrayParcel, + const string& marshallRpc, + const string& unmarshallRpc, + const string& writeArrayRpc, + const string& createArrayRpc, + const string& readArrayRpc); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); + +private: + string m_marshallParcel; + string m_unmarshallParcel; + string m_writeArrayParcel; + string m_createArrayParcel; + string m_readArrayParcel; + string m_marshallRpc; + string m_unmarshallRpc; + string m_writeArrayRpc; + string m_createArrayRpc; + string m_readArrayRpc; +}; + +class BooleanType : public Type +{ +public: + BooleanType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + +class CharType : public Type +{ +public: + CharType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + + +class StringType : public Type +{ +public: + StringType(); + + virtual string CreatorName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + +class CharSequenceType : public Type +{ +public: + CharSequenceType(); + + virtual string CreatorName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class RemoteExceptionType : public Type +{ +public: + RemoteExceptionType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class RuntimeExceptionType : public Type +{ +public: + RuntimeExceptionType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class IBinderType : public Type +{ +public: + IBinderType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class IInterfaceType : public Type +{ +public: + IInterfaceType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class BinderType : public Type +{ +public: + BinderType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class BinderProxyType : public Type +{ +public: + BinderProxyType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class ParcelType : public Type +{ +public: + ParcelType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class ParcelableInterfaceType : public Type +{ +public: + ParcelableInterfaceType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class MapType : public Type +{ +public: + MapType(); + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); +}; + +class ListType : public Type +{ +public: + ListType(); + + virtual string InstantiableName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + +class UserDataType : public Type +{ +public: + UserDataType(const string& package, const string& name, + bool builtIn, bool canWriteToParcel, bool canWriteToRpcData, + const string& declFile = "", int declLine = -1); + + virtual string CreatorName() const; + virtual string RpcCreatorName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual bool CanBeArray() const; + + virtual void WriteArrayToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadArrayFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + +class InterfaceType : public Type +{ +public: + InterfaceType(const string& package, const string& name, + bool builtIn, bool oneway, + const string& declFile, int declLine); + + bool OneWay() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + +private: + bool m_oneway; +}; + + +class GenericType : public Type +{ +public: + GenericType(const string& package, const string& name, + const vector<Type*>& args); + + const vector<Type*>& GenericArgumentTypes() const; + string GenericArguments() const; + + virtual string ImportType() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + +private: + string m_genericArguments; + string m_importName; + vector<Type*> m_args; +}; + +class RpcDataType : public UserDataType +{ +public: + RpcDataType(); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); +}; + +class ClassLoaderType : public Type +{ +public: + ClassLoaderType(); +}; + +class GenericListType : public GenericType +{ +public: + GenericListType(const string& package, const string& name, + const vector<Type*>& args); + + virtual string CreatorName() const; + virtual string InstantiableName() const; + + virtual void WriteToParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, int flags); + virtual void CreateFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + virtual void ReadFromParcel(StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl); + + virtual void WriteToRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, int flags); + virtual void CreateFromRpcData(StatementBlock* addTo, Expression* k, Variable* v, + Variable* data, Variable** cl); + +private: + string m_creator; +}; + +class Namespace +{ +public: + Namespace(); + ~Namespace(); + void Add(Type* type); + + // args is the number of template types (what is this called?) + void AddGenericType(const string& package, const string& name, int args); + + // lookup a specific class name + Type* Find(const string& name) const; + Type* Find(const char* package, const char* name) const; + + // try to search by either a full name or a partial name + Type* Search(const string& name); + + void Dump() const; + +private: + struct Generic { + string package; + string name; + string qualified; + int args; + }; + + const Generic* search_generic(const string& name) const; + + vector<Type*> m_types; + vector<Generic> m_generics; +}; + +extern Namespace NAMES; + +extern Type* VOID_TYPE; +extern Type* BOOLEAN_TYPE; +extern Type* BYTE_TYPE; +extern Type* CHAR_TYPE; +extern Type* INT_TYPE; +extern Type* LONG_TYPE; +extern Type* FLOAT_TYPE; +extern Type* DOUBLE_TYPE; +extern Type* OBJECT_TYPE; +extern Type* STRING_TYPE; +extern Type* CHAR_SEQUENCE_TYPE; +extern Type* TEXT_UTILS_TYPE; +extern Type* REMOTE_EXCEPTION_TYPE; +extern Type* RUNTIME_EXCEPTION_TYPE; +extern Type* IBINDER_TYPE; +extern Type* IINTERFACE_TYPE; +extern Type* BINDER_NATIVE_TYPE; +extern Type* BINDER_PROXY_TYPE; +extern Type* PARCEL_TYPE; +extern Type* PARCELABLE_INTERFACE_TYPE; + +extern Type* CONTEXT_TYPE; + +extern Type* RPC_DATA_TYPE; +extern Type* RPC_ERROR_TYPE; +extern Type* RPC_CONTEXT_TYPE; +extern Type* EVENT_FAKE_TYPE; + +extern Expression* NULL_VALUE; +extern Expression* THIS_VALUE; +extern Expression* SUPER_VALUE; +extern Expression* TRUE_VALUE; +extern Expression* FALSE_VALUE; + +void register_base_types(); + +#endif // AIDL_TYPE_H 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; +} diff --git a/tools/aidl/aidl_language.cpp b/tools/aidl/aidl_language.cpp new file mode 100644 index 0000000..cd6a3bd --- /dev/null +++ b/tools/aidl/aidl_language.cpp @@ -0,0 +1,20 @@ +#include "aidl_language.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_MS_C_RUNTIME +int isatty(int fd) +{ + return (fd == 0); +} +#endif + +#if 0 +ParserCallbacks k_parserCallbacks = { + NULL +}; +#endif + +ParserCallbacks* g_callbacks = NULL; // &k_parserCallbacks; + diff --git a/tools/aidl/aidl_language.h b/tools/aidl/aidl_language.h new file mode 100644 index 0000000..de1370c --- /dev/null +++ b/tools/aidl/aidl_language.h @@ -0,0 +1,172 @@ +#ifndef DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H +#define DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H + + +typedef enum { + NO_EXTRA_TEXT = 0, + SHORT_COMMENT, + LONG_COMMENT, + COPY_TEXT, + WHITESPACE +} which_extra_text; + +typedef struct extra_text_type { + unsigned lineno; + which_extra_text which; + char* data; + unsigned len; + struct extra_text_type* next; +} extra_text_type; + +typedef struct buffer_type { + unsigned lineno; + unsigned token; + char *data; + extra_text_type* extra; +} buffer_type; + +typedef struct type_type { + buffer_type type; + buffer_type array_token; + int dimension; +} type_type; + +typedef struct arg_type { + buffer_type comma_token; // empty in the first one in the list + buffer_type direction; + type_type type; + buffer_type name; + struct arg_type *next; +} arg_type; + +enum { + METHOD_TYPE +}; + +typedef struct interface_item_type { + unsigned item_type; + struct interface_item_type* next; +} interface_item_type; + +typedef struct method_type { + interface_item_type interface_item; + type_type type; + bool oneway; + buffer_type oneway_token; + buffer_type name; + buffer_type open_paren_token; + arg_type* args; + buffer_type close_paren_token; + bool hasId; + buffer_type equals_token; + buffer_type id; + // XXX missing comments/copy text here + buffer_type semicolon_token; + buffer_type* comments_token; // points into this structure, DO NOT DELETE + int assigned_id; +} method_type; + +enum { + USER_DATA_TYPE = 12, + INTERFACE_TYPE_BINDER, + INTERFACE_TYPE_RPC +}; + +typedef struct document_item_type { + unsigned item_type; + struct document_item_type* next; +} document_item_type; + + +// for user_data_type.flattening_methods +enum { + PARCELABLE_DATA = 0x1, + RPC_DATA = 0x2 +}; + +typedef struct user_data_type { + document_item_type document_item; + buffer_type keyword_token; // only the first one + char* package; + buffer_type name; + buffer_type semicolon_token; + int flattening_methods; +} user_data_type; + +typedef struct interface_type { + document_item_type document_item; + buffer_type interface_token; + bool oneway; + buffer_type oneway_token; + char* package; + buffer_type name; + buffer_type open_brace_token; + interface_item_type* interface_items; + buffer_type close_brace_token; + buffer_type* comments_token; // points into this structure, DO NOT DELETE +} interface_type; + +typedef union lexer_type { + buffer_type buffer; + type_type type; + arg_type *arg; + method_type* method; + interface_item_type* interface_item; + interface_type* interface_obj; + user_data_type* user_data; + document_item_type* document_item; +} lexer_type; + + +#define YYSTYPE lexer_type + +#if __cplusplus +extern "C" { +#endif + +int parse_aidl(char const *); + +// strips off the leading whitespace, the "import" text +// also returns whether it's a local or system import +// we rely on the input matching the import regex from below +char* parse_import_statement(const char* text); + +// in, out or inout +enum { + IN_PARAMETER = 1, + OUT_PARAMETER = 2, + INOUT_PARAMETER = 3 +}; +int convert_direction(const char* direction); + +// callbacks from within the parser +// these functions all take ownership of the strings +typedef struct ParserCallbacks { + void (*document)(document_item_type* items); + void (*import)(buffer_type* statement); +} ParserCallbacks; + +extern ParserCallbacks* g_callbacks; + +// true if there was an error parsing, false otherwise +extern int g_error; + +// the name of the file we're currently parsing +extern char const* g_currentFilename; + +// the package name for our current file +extern char const* g_currentPackage; + +typedef enum { + STATEMENT_INSIDE_INTERFACE +} error_type; + +void init_buffer_type(buffer_type* buf, int lineno); + + +#if __cplusplus +} +#endif + + +#endif // DEVICE_TOOLS_AIDL_AIDL_LANGUAGE_H diff --git a/tools/aidl/aidl_language_l.l b/tools/aidl/aidl_language_l.l new file mode 100644 index 0000000..3d33e7a --- /dev/null +++ b/tools/aidl/aidl_language_l.l @@ -0,0 +1,214 @@ +%{ +#include "aidl_language.h" +#include "aidl_language_y.h" +#include "search_path.h" +#include <string.h> +#include <stdlib.h> + +extern YYSTYPE yylval; + +// comment and whitespace handling +// these functions save a copy of the buffer +static void begin_extra_text(unsigned lineno, which_extra_text which); +static void append_extra_text(char* text); +static extra_text_type* get_extra_text(void); // you now own the object + // this returns +static void drop_extra_text(void); + +// package handling +static void do_package_statement(const char* importText); + +#define SET_BUFFER(t) \ + do { \ + yylval.buffer.lineno = yylineno; \ + yylval.buffer.token = (t); \ + yylval.buffer.data = strdup(yytext); \ + yylval.buffer.extra = get_extra_text(); \ + } while(0) + +%} + +%option yylineno +%option noyywrap + +%x COPYING LONG_COMMENT + +identifier [_a-zA-Z][_a-zA-Z0-9\.]* +whitespace ([ \t\n\r]+) +brackets \[{whitespace}?\] +idvalue (0|[1-9][0-9]*) + +%% + + +\%\%\{ { begin_extra_text(yylineno, COPY_TEXT); BEGIN(COPYING); } +<COPYING>\}\%\% { BEGIN(INITIAL); } +<COPYING>.*\n { append_extra_text(yytext); } +<COPYING>.* { append_extra_text(yytext); } +<COPYING>\n+ { append_extra_text(yytext); } + + +\/\* { begin_extra_text(yylineno, (which_extra_text)LONG_COMMENT); + BEGIN(LONG_COMMENT); } +<LONG_COMMENT>[^*]* { append_extra_text(yytext); } +<LONG_COMMENT>\*+[^/] { append_extra_text(yytext); } +<LONG_COMMENT>\n { append_extra_text(yytext); } +<LONG_COMMENT>\**\/ { BEGIN(INITIAL); } + +^{whitespace}?import{whitespace}[^ \t\r\n]+{whitespace}?; { + SET_BUFFER(IMPORT); + return IMPORT; + } +^{whitespace}?package{whitespace}[^ \t\r\n]+{whitespace}?; { + do_package_statement(yytext); + SET_BUFFER(PACKAGE); + return PACKAGE; + } +<<EOF>> { yyterminate(); } + +\/\/.*\n { begin_extra_text(yylineno, SHORT_COMMENT); + append_extra_text(yytext); } + +{whitespace} { /* begin_extra_text(yylineno, WHITESPACE); + append_extra_text(yytext); */ } + +; { SET_BUFFER(';'); return ';'; } +\{ { SET_BUFFER('{'); return '{'; } +\} { SET_BUFFER('}'); return '}'; } +\( { SET_BUFFER('('); return '('; } +\) { SET_BUFFER(')'); return ')'; } +, { SET_BUFFER(','); return ','; } += { SET_BUFFER('='); return '='; } + + /* keywords */ +parcelable { SET_BUFFER(PARCELABLE); return PARCELABLE; } +interface { SET_BUFFER(INTERFACE); return INTERFACE; } +flattenable { SET_BUFFER(FLATTENABLE); return FLATTENABLE; } +rpc { SET_BUFFER(INTERFACE); return RPC; } +in { SET_BUFFER(IN); return IN; } +out { SET_BUFFER(OUT); return OUT; } +inout { SET_BUFFER(INOUT); return INOUT; } +oneway { SET_BUFFER(ONEWAY); return ONEWAY; } + +{brackets}+ { SET_BUFFER(ARRAY); return ARRAY; } +{idvalue} { SET_BUFFER(IDVALUE); return IDVALUE; } +{identifier} { SET_BUFFER(IDENTIFIER); return IDENTIFIER; } +{identifier}\<{whitespace}*{identifier}({whitespace}*,{whitespace}*{identifier})*{whitespace}*\> { + SET_BUFFER(GENERIC); return GENERIC; } + + /* syntax error! */ +. { printf("UNKNOWN(%s)", yytext); + yylval.buffer.lineno = yylineno; + yylval.buffer.token = IDENTIFIER; + yylval.buffer.data = strdup(yytext); + return IDENTIFIER; + } + +%% + +// comment and whitespace handling +// ================================================ +extra_text_type* g_extraText = NULL; +extra_text_type* g_nextExtraText = NULL; + +void begin_extra_text(unsigned lineno, which_extra_text which) +{ + extra_text_type* text = (extra_text_type*)malloc(sizeof(extra_text_type)); + text->lineno = lineno; + text->which = which; + text->data = NULL; + text->len = 0; + text->next = NULL; + if (g_nextExtraText == NULL) { + g_extraText = text; + } else { + g_nextExtraText->next = text; + } + g_nextExtraText = text; +} + +void append_extra_text(char* text) +{ + if (g_nextExtraText->data == NULL) { + g_nextExtraText->data = strdup(text); + g_nextExtraText->len = strlen(text); + } else { + char* orig = g_nextExtraText->data; + unsigned oldLen = g_nextExtraText->len; + unsigned len = strlen(text); + g_nextExtraText->len += len; + g_nextExtraText->data = (char*)malloc(g_nextExtraText->len+1); + memcpy(g_nextExtraText->data, orig, oldLen); + memcpy(g_nextExtraText->data+oldLen, text, len); + g_nextExtraText->data[g_nextExtraText->len] = '\0'; + free(orig); + } +} + +extra_text_type* +get_extra_text(void) +{ + extra_text_type* result = g_extraText; + g_extraText = NULL; + g_nextExtraText = NULL; + return result; +} + +void drop_extra_text(void) +{ + extra_text_type* p = g_extraText; + while (p) { + extra_text_type* next = p->next; + free(p->data); + free(p); + free(next); + } + g_extraText = NULL; + g_nextExtraText = NULL; +} + + +// package handling +// ================================================ +void do_package_statement(const char* importText) +{ + if (g_currentPackage) free((void*)g_currentPackage); + g_currentPackage = parse_import_statement(importText); +} + + +// main parse function +// ================================================ +char const* g_currentFilename = NULL; +char const* g_currentPackage = NULL; + +int yyparse(void); + +int parse_aidl(char const *filename) +{ + yyin = fopen(filename, "r"); + if (yyin) { + char const* oldFilename = g_currentFilename; + char const* oldPackage = g_currentPackage; + g_currentFilename = strdup(filename); + + g_error = 0; + yylineno = 1; + int rv = yyparse(); + if (g_error != 0) { + rv = g_error; + } + + free((void*)g_currentFilename); + g_currentFilename = oldFilename; + + if (g_currentPackage) free((void*)g_currentPackage); + g_currentPackage = oldPackage; + + return rv; + } else { + fprintf(stderr, "aidl: unable to open file for read: %s\n", filename); + return 1; + } +} + diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y new file mode 100644 index 0000000..9b40d28 --- /dev/null +++ b/tools/aidl/aidl_language_y.y @@ -0,0 +1,373 @@ +%{ +#include "aidl_language.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int yyerror(char* errstr); +int yylex(void); +extern int yylineno; + +static int count_brackets(const char*); + +%} + +%token IMPORT +%token PACKAGE +%token IDENTIFIER +%token IDVALUE +%token GENERIC +%token ARRAY +%token PARCELABLE +%token INTERFACE +%token FLATTENABLE +%token RPC +%token IN +%token OUT +%token INOUT +%token ONEWAY + +%% +document: + document_items { g_callbacks->document($1.document_item); } + | headers document_items { g_callbacks->document($2.document_item); } + ; + +headers: + package { } + | imports { } + | package imports { } + ; + +package: + PACKAGE { } + ; + +imports: + IMPORT { g_callbacks->import(&($1.buffer)); } + | IMPORT imports { g_callbacks->import(&($1.buffer)); } + ; + +document_items: + { $$.document_item = NULL; } + | document_items declaration { + if ($2.document_item == NULL) { + // error cases only + $$ = $1; + } else { + document_item_type* p = $1.document_item; + while (p && p->next) { + p=p->next; + } + if (p) { + p->next = (document_item_type*)$2.document_item; + $$ = $1; + } else { + $$.document_item = (document_item_type*)$2.document_item; + } + } + } + | document_items error { + fprintf(stderr, "%s:%d: syntax error don't know what to do with \"%s\"\n", g_currentFilename, + $2.buffer.lineno, $2.buffer.data); + $$ = $1; + } + ; + +declaration: + parcelable_decl { $$.document_item = (document_item_type*)$1.user_data; } + | interface_decl { $$.document_item = (document_item_type*)$1.interface_item; } + ; + +parcelable_decl: + PARCELABLE IDENTIFIER ';' { + user_data_type* b = (user_data_type*)malloc(sizeof(user_data_type)); + b->document_item.item_type = USER_DATA_TYPE; + b->document_item.next = NULL; + b->keyword_token = $1.buffer; + b->name = $2.buffer; + b->package = g_currentPackage ? strdup(g_currentPackage) : NULL; + b->semicolon_token = $3.buffer; + b->flattening_methods = PARCELABLE_DATA; + $$.user_data = b; + } + | PARCELABLE ';' { + fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name.\n", + g_currentFilename, $1.buffer.lineno); + $$.user_data = NULL; + } + | PARCELABLE error ';' { + fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name, saw \"%s\".\n", + g_currentFilename, $2.buffer.lineno, $2.buffer.data); + $$.user_data = NULL; + } + | FLATTENABLE IDENTIFIER ';' { + user_data_type* b = (user_data_type*)malloc(sizeof(user_data_type)); + b->document_item.item_type = USER_DATA_TYPE; + b->document_item.next = NULL; + b->keyword_token = $1.buffer; + b->name = $2.buffer; + b->package = g_currentPackage ? strdup(g_currentPackage) : NULL; + b->semicolon_token = $3.buffer; + b->flattening_methods = PARCELABLE_DATA | RPC_DATA; + $$.user_data = b; + } + | FLATTENABLE ';' { + fprintf(stderr, "%s:%d syntax error in flattenable declaration. Expected type name.\n", + g_currentFilename, $1.buffer.lineno); + $$.user_data = NULL; + } + | FLATTENABLE error ';' { + fprintf(stderr, "%s:%d syntax error in flattenable declaration. Expected type name, saw \"%s\".\n", + g_currentFilename, $2.buffer.lineno, $2.buffer.data); + $$.user_data = NULL; + } + + ; + +interface_header: + INTERFACE { + interface_type* c = (interface_type*)malloc(sizeof(interface_type)); + c->document_item.item_type = INTERFACE_TYPE_BINDER; + c->document_item.next = NULL; + c->interface_token = $1.buffer; + c->oneway = false; + memset(&c->oneway_token, 0, sizeof(buffer_type)); + c->comments_token = &c->interface_token; + $$.interface_obj = c; + } + | ONEWAY INTERFACE { + interface_type* c = (interface_type*)malloc(sizeof(interface_type)); + c->document_item.item_type = INTERFACE_TYPE_BINDER; + c->document_item.next = NULL; + c->interface_token = $2.buffer; + c->oneway = true; + c->oneway_token = $1.buffer; + c->comments_token = &c->oneway_token; + $$.interface_obj = c; + } + | RPC { + interface_type* c = (interface_type*)malloc(sizeof(interface_type)); + c->document_item.item_type = INTERFACE_TYPE_RPC; + c->document_item.next = NULL; + c->interface_token = $1.buffer; + c->oneway = false; + memset(&c->oneway_token, 0, sizeof(buffer_type)); + c->comments_token = &c->interface_token; + $$.interface_obj = c; + } + ; + +interface_keywords: + INTERFACE + | RPC + ; + +interface_decl: + interface_header IDENTIFIER '{' interface_items '}' { + interface_type* c = $1.interface_obj; + c->name = $2.buffer; + c->package = g_currentPackage ? strdup(g_currentPackage) : NULL; + c->open_brace_token = $3.buffer; + c->interface_items = $4.interface_item; + c->close_brace_token = $5.buffer; + $$.interface_obj = c; + } + | interface_keywords error '{' interface_items '}' { + fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected type name, saw \"%s\"\n", + g_currentFilename, $2.buffer.lineno, $2.buffer.data); + $$.document_item = NULL; + } + | interface_keywords error '}' { + fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected type name, saw \"%s\"\n", + g_currentFilename, $2.buffer.lineno, $2.buffer.data); + $$.document_item = NULL; + } + + ; + +interface_items: + { $$.interface_item = NULL; } + | interface_items method_decl { + interface_item_type* p=$1.interface_item; + while (p && p->next) { + p=p->next; + } + if (p) { + p->next = (interface_item_type*)$2.method; + $$ = $1; + } else { + $$.interface_item = (interface_item_type*)$2.method; + } + } + | interface_items error ';' { + fprintf(stderr, "%s:%d: syntax error before ';' (expected method declaration)\n", + g_currentFilename, $3.buffer.lineno); + $$ = $1; + } + ; + +method_decl: + type IDENTIFIER '(' arg_list ')' ';' { + method_type *method = (method_type*)malloc(sizeof(method_type)); + method->interface_item.item_type = METHOD_TYPE; + method->interface_item.next = NULL; + method->oneway = false; + method->type = $1.type; + memset(&method->oneway_token, 0, sizeof(buffer_type)); + method->name = $2.buffer; + method->open_paren_token = $3.buffer; + method->args = $4.arg; + method->close_paren_token = $5.buffer; + method->hasId = false; + memset(&method->equals_token, 0, sizeof(buffer_type)); + memset(&method->id, 0, sizeof(buffer_type)); + method->semicolon_token = $6.buffer; + method->comments_token = &method->type.type; + $$.method = method; + } + | ONEWAY type IDENTIFIER '(' arg_list ')' ';' { + method_type *method = (method_type*)malloc(sizeof(method_type)); + method->interface_item.item_type = METHOD_TYPE; + method->interface_item.next = NULL; + method->oneway = true; + method->oneway_token = $1.buffer; + method->type = $2.type; + method->name = $3.buffer; + method->open_paren_token = $4.buffer; + method->args = $5.arg; + method->close_paren_token = $6.buffer; + method->hasId = false; + memset(&method->equals_token, 0, sizeof(buffer_type)); + memset(&method->id, 0, sizeof(buffer_type)); + method->semicolon_token = $7.buffer; + method->comments_token = &method->oneway_token; + $$.method = method; + } + | type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';' { + method_type *method = (method_type*)malloc(sizeof(method_type)); + method->interface_item.item_type = METHOD_TYPE; + method->interface_item.next = NULL; + method->oneway = false; + memset(&method->oneway_token, 0, sizeof(buffer_type)); + method->type = $1.type; + method->name = $2.buffer; + method->open_paren_token = $3.buffer; + method->args = $4.arg; + method->close_paren_token = $5.buffer; + method->hasId = true; + method->equals_token = $6.buffer; + method->id = $7.buffer; + method->semicolon_token = $8.buffer; + method->comments_token = &method->type.type; + $$.method = method; + } + | ONEWAY type IDENTIFIER '(' arg_list ')' '=' IDVALUE ';' { + method_type *method = (method_type*)malloc(sizeof(method_type)); + method->interface_item.item_type = METHOD_TYPE; + method->interface_item.next = NULL; + method->oneway = true; + method->oneway_token = $1.buffer; + method->type = $2.type; + method->name = $3.buffer; + method->open_paren_token = $4.buffer; + method->args = $5.arg; + method->close_paren_token = $6.buffer; + method->hasId = true; + method->equals_token = $7.buffer; + method->id = $8.buffer; + method->semicolon_token = $9.buffer; + method->comments_token = &method->oneway_token; + $$.method = method; + } + ; + +arg_list: + { $$.arg = NULL; } + | arg { $$ = $1; } + | arg_list ',' arg { + if ($$.arg != NULL) { + // only NULL on error + $$ = $1; + arg_type *p = $1.arg; + while (p && p->next) { + p=p->next; + } + $3.arg->comma_token = $2.buffer; + p->next = $3.arg; + } + } + | error { + fprintf(stderr, "%s:%d: syntax error in parameter list\n", g_currentFilename, $1.buffer.lineno); + $$.arg = NULL; + } + ; + +arg: + direction type IDENTIFIER { + arg_type* arg = (arg_type*)malloc(sizeof(arg_type)); + memset(&arg->comma_token, 0, sizeof(buffer_type)); + arg->direction = $1.buffer; + arg->type = $2.type; + arg->name = $3.buffer; + arg->next = NULL; + $$.arg = arg; + } + ; + +type: + IDENTIFIER { + $$.type.type = $1.buffer; + init_buffer_type(&$$.type.array_token, yylineno); + $$.type.dimension = 0; + } + | IDENTIFIER ARRAY { + $$.type.type = $1.buffer; + $$.type.array_token = $2.buffer; + $$.type.dimension = count_brackets($2.buffer.data); + } + | GENERIC { + $$.type.type = $1.buffer; + init_buffer_type(&$$.type.array_token, yylineno); + $$.type.dimension = 0; + } + ; + +direction: + { init_buffer_type(&$$.buffer, yylineno); } + | IN { $$.buffer = $1.buffer; } + | OUT { $$.buffer = $1.buffer; } + | INOUT { $$.buffer = $1.buffer; } + ; + +%% + +#include <ctype.h> +#include <stdio.h> + +int g_error = 0; + +int yyerror(char* errstr) +{ + fprintf(stderr, "%s:%d: %s\n", g_currentFilename, yylineno, errstr); + g_error = 1; + return 1; +} + +void init_buffer_type(buffer_type* buf, int lineno) +{ + buf->lineno = lineno; + buf->token = 0; + buf->data = NULL; + buf->extra = NULL; +} + +static int count_brackets(const char* s) +{ + int n=0; + while (*s) { + if (*s == '[') n++; + s++; + } + return n; +} diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp new file mode 100644 index 0000000..9e57407 --- /dev/null +++ b/tools/aidl/generate_java.cpp @@ -0,0 +1,99 @@ +#include "generate_java.h" +#include "Type.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// ================================================= +VariableFactory::VariableFactory(const string& base) + :m_base(base), + m_index(0) +{ +} + +Variable* +VariableFactory::Get(Type* type) +{ + char name[100]; + sprintf(name, "%s%d", m_base.c_str(), m_index); + m_index++; + Variable* v = new Variable(type, name); + m_vars.push_back(v); + return v; +} + +Variable* +VariableFactory::Get(int index) +{ + return m_vars[index]; +} + +// ================================================= +string +gather_comments(extra_text_type* extra) +{ + string s; + while (extra) { + if (extra->which == SHORT_COMMENT) { + s += extra->data; + } + else if (extra->which == LONG_COMMENT) { + s += "/*"; + s += extra->data; + s += "*/"; + } + extra = extra->next; + } + return s; +} + +string +append(const char* a, const char* b) +{ + string s = a; + s += b; + return s; +} + +// ================================================= +int +generate_java(const string& filename, const string& originalSrc, + interface_type* iface) +{ + Class* cl; + + if (iface->document_item.item_type == INTERFACE_TYPE_BINDER) { + cl = generate_binder_interface_class(iface); + } + else if (iface->document_item.item_type == INTERFACE_TYPE_RPC) { + cl = generate_rpc_interface_class(iface); + } + + Document* document = new Document; + document->comment = ""; + if (iface->package) document->package = iface->package; + document->originalSrc = originalSrc; + document->classes.push_back(cl); + +// printf("outputting... filename=%s\n", filename.c_str()); + FILE* to; + if (filename == "-") { + to = stdout; + } else { + /* open file in binary mode to ensure that the tool produces the + * same output on all platforms !! + */ + to = fopen(filename.c_str(), "wb"); + if (to == NULL) { + fprintf(stderr, "unable to open %s for write\n", filename.c_str()); + return 1; + } + } + + document->Write(to); + + fclose(to); + return 0; +} + diff --git a/tools/aidl/generate_java.h b/tools/aidl/generate_java.h new file mode 100644 index 0000000..4bfcfeb --- /dev/null +++ b/tools/aidl/generate_java.h @@ -0,0 +1,33 @@ +#ifndef GENERATE_JAVA_H +#define GENERATE_JAVA_H + +#include "aidl_language.h" +#include "AST.h" + +#include <string> + +using namespace std; + +int generate_java(const string& filename, const string& originalSrc, + interface_type* iface); + +Class* generate_binder_interface_class(const interface_type* iface); +Class* generate_rpc_interface_class(const interface_type* iface); + +string gather_comments(extra_text_type* extra); +string append(const char* a, const char* b); + +class VariableFactory +{ +public: + VariableFactory(const string& base); // base must be short + Variable* Get(Type* type); + Variable* Get(int index); +private: + vector<Variable*> m_vars; + string m_base; + int m_index; +}; + +#endif // GENERATE_JAVA_H + diff --git a/tools/aidl/generate_java_binder.cpp b/tools/aidl/generate_java_binder.cpp new file mode 100644 index 0000000..f291ceb --- /dev/null +++ b/tools/aidl/generate_java_binder.cpp @@ -0,0 +1,560 @@ +#include "generate_java.h" +#include "Type.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// ================================================= +class StubClass : public Class +{ +public: + StubClass(Type* type, Type* interfaceType); + virtual ~StubClass(); + + Variable* transact_code; + Variable* transact_data; + Variable* transact_reply; + Variable* transact_flags; + SwitchStatement* transact_switch; +private: + void make_as_interface(Type* interfaceType); +}; + +StubClass::StubClass(Type* type, Type* interfaceType) + :Class() +{ + this->comment = "/** Local-side IPC implementation stub class. */"; + this->modifiers = PUBLIC | ABSTRACT | STATIC; + this->what = Class::CLASS; + this->type = type; + this->extends = BINDER_NATIVE_TYPE; + this->interfaces.push_back(interfaceType); + + // descriptor + Field* descriptor = new Field(STATIC | FINAL | PRIVATE, + new Variable(STRING_TYPE, "DESCRIPTOR")); + descriptor->value = "\"" + interfaceType->QualifiedName() + "\""; + this->elements.push_back(descriptor); + + // ctor + Method* ctor = new Method; + ctor->modifiers = PUBLIC; + ctor->comment = "/** Construct the stub at attach it to the " + "interface. */"; + ctor->name = "Stub"; + ctor->statements = new StatementBlock; + MethodCall* attach = new MethodCall(THIS_VALUE, "attachInterface", + 2, THIS_VALUE, new LiteralExpression("DESCRIPTOR")); + ctor->statements->Add(attach); + this->elements.push_back(ctor); + + // asInterface + make_as_interface(interfaceType); + + // asBinder + Method* asBinder = new Method; + asBinder->modifiers = PUBLIC | OVERRIDE; + asBinder->returnType = IBINDER_TYPE; + asBinder->name = "asBinder"; + asBinder->statements = new StatementBlock; + asBinder->statements->Add(new ReturnStatement(THIS_VALUE)); + this->elements.push_back(asBinder); + + // onTransact + this->transact_code = new Variable(INT_TYPE, "code"); + this->transact_data = new Variable(PARCEL_TYPE, "data"); + this->transact_reply = new Variable(PARCEL_TYPE, "reply"); + this->transact_flags = new Variable(INT_TYPE, "flags"); + Method* onTransact = new Method; + onTransact->modifiers = PUBLIC | OVERRIDE; + onTransact->returnType = BOOLEAN_TYPE; + onTransact->name = "onTransact"; + onTransact->parameters.push_back(this->transact_code); + onTransact->parameters.push_back(this->transact_data); + onTransact->parameters.push_back(this->transact_reply); + onTransact->parameters.push_back(this->transact_flags); + onTransact->statements = new StatementBlock; + onTransact->exceptions.push_back(REMOTE_EXCEPTION_TYPE); + this->elements.push_back(onTransact); + this->transact_switch = new SwitchStatement(this->transact_code); + + onTransact->statements->Add(this->transact_switch); + MethodCall* superCall = new MethodCall(SUPER_VALUE, "onTransact", 4, + this->transact_code, this->transact_data, + this->transact_reply, this->transact_flags); + onTransact->statements->Add(new ReturnStatement(superCall)); +} + +StubClass::~StubClass() +{ +} + +void +StubClass::make_as_interface(Type *interfaceType) +{ + Variable* obj = new Variable(IBINDER_TYPE, "obj"); + + Method* m = new Method; + m->comment = "/**\n * Cast an IBinder object into an "; + m->comment += interfaceType->QualifiedName(); + m->comment += " interface,\n"; + m->comment += " * generating a proxy if needed.\n */"; + m->modifiers = PUBLIC | STATIC; + m->returnType = interfaceType; + m->name = "asInterface"; + m->parameters.push_back(obj); + m->statements = new StatementBlock; + + IfStatement* ifstatement = new IfStatement(); + ifstatement->expression = new Comparison(obj, "==", NULL_VALUE); + ifstatement->statements = new StatementBlock; + ifstatement->statements->Add(new ReturnStatement(NULL_VALUE)); + m->statements->Add(ifstatement); + + // IInterface iin = obj.queryLocalInterface(DESCRIPTOR) + MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface"); + queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR")); + IInterfaceType* iinType = new IInterfaceType(); + Variable *iin = new Variable(iinType, "iin"); + VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface, NULL); + m->statements->Add(iinVd); + + // Ensure the instance type of the local object is as expected. + // One scenario where this is needed is if another package (with a + // different class loader) runs in the same process as the service. + + // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>) iin; + Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE); + Comparison* instOfCheck = new Comparison(iin, " instanceof ", + new LiteralExpression(interfaceType->QualifiedName())); + IfStatement* instOfStatement = new IfStatement(); + instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck); + instOfStatement->statements = new StatementBlock; + instOfStatement->statements->Add(new ReturnStatement(new Cast(interfaceType, iin))); + m->statements->Add(instOfStatement); + + string proxyType = interfaceType->QualifiedName(); + proxyType += ".Stub.Proxy"; + NewExpression* ne = new NewExpression(NAMES.Find(proxyType)); + ne->arguments.push_back(obj); + m->statements->Add(new ReturnStatement(ne)); + + this->elements.push_back(m); +} + + + +// ================================================= +class ProxyClass : public Class +{ +public: + ProxyClass(Type* type, InterfaceType* interfaceType); + virtual ~ProxyClass(); + + Variable* mRemote; + bool mOneWay; +}; + +ProxyClass::ProxyClass(Type* type, InterfaceType* interfaceType) + :Class() +{ + this->modifiers = PRIVATE | STATIC; + this->what = Class::CLASS; + this->type = type; + this->interfaces.push_back(interfaceType); + + mOneWay = interfaceType->OneWay(); + + // IBinder mRemote + mRemote = new Variable(IBINDER_TYPE, "mRemote"); + this->elements.push_back(new Field(PRIVATE, mRemote)); + + // Proxy() + Variable* remote = new Variable(IBINDER_TYPE, "remote"); + Method* ctor = new Method; + ctor->name = "Proxy"; + ctor->statements = new StatementBlock; + ctor->parameters.push_back(remote); + ctor->statements->Add(new Assignment(mRemote, remote)); + this->elements.push_back(ctor); + + // IBinder asBinder() + Method* asBinder = new Method; + asBinder->modifiers = PUBLIC | OVERRIDE; + asBinder->returnType = IBINDER_TYPE; + asBinder->name = "asBinder"; + asBinder->statements = new StatementBlock; + asBinder->statements->Add(new ReturnStatement(mRemote)); + this->elements.push_back(asBinder); +} + +ProxyClass::~ProxyClass() +{ +} + +// ================================================= +static void +generate_new_array(Type* t, StatementBlock* addTo, Variable* v, + Variable* parcel) +{ + Variable* len = new Variable(INT_TYPE, v->name + "_length"); + addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt"))); + IfStatement* lencheck = new IfStatement(); + lencheck->expression = new Comparison(len, "<", new LiteralExpression("0")); + lencheck->statements->Add(new Assignment(v, NULL_VALUE)); + lencheck->elseif = new IfStatement(); + lencheck->elseif->statements->Add(new Assignment(v, + new NewArrayExpression(t, len))); + addTo->Add(lencheck); +} + +static void +generate_write_to_parcel(Type* t, StatementBlock* addTo, Variable* v, + Variable* parcel, int flags) +{ + if (v->dimension == 0) { + t->WriteToParcel(addTo, v, parcel, flags); + } + if (v->dimension == 1) { + t->WriteArrayToParcel(addTo, v, parcel, flags); + } +} + +static void +generate_create_from_parcel(Type* t, StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl) +{ + if (v->dimension == 0) { + t->CreateFromParcel(addTo, v, parcel, cl); + } + if (v->dimension == 1) { + t->CreateArrayFromParcel(addTo, v, parcel, cl); + } +} + +static void +generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v, + Variable* parcel, Variable** cl) +{ + if (v->dimension == 0) { + t->ReadFromParcel(addTo, v, parcel, cl); + } + if (v->dimension == 1) { + t->ReadArrayFromParcel(addTo, v, parcel, cl); + } +} + + +static void +generate_method(const method_type* method, Class* interface, + StubClass* stubClass, ProxyClass* proxyClass, int index) +{ + arg_type* arg; + int i; + bool hasOutParams = false; + + const bool oneway = proxyClass->mOneWay || method->oneway; + + // == the TRANSACT_ constant ============================================= + string transactCodeName = "TRANSACTION_"; + transactCodeName += method->name.data; + + char transactCodeValue[60]; + sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index); + + Field* transactCode = new Field(STATIC | FINAL, + new Variable(INT_TYPE, transactCodeName)); + transactCode->value = transactCodeValue; + stubClass->elements.push_back(transactCode); + + // == the declaration in the interface =================================== + Method* decl = new Method; + decl->comment = gather_comments(method->comments_token->extra); + decl->modifiers = PUBLIC; + decl->returnType = NAMES.Search(method->type.type.data); + decl->returnTypeDimension = method->type.dimension; + decl->name = method->name.data; + + arg = method->args; + while (arg != NULL) { + decl->parameters.push_back(new Variable( + NAMES.Search(arg->type.type.data), arg->name.data, + arg->type.dimension)); + arg = arg->next; + } + + decl->exceptions.push_back(REMOTE_EXCEPTION_TYPE); + + interface->elements.push_back(decl); + + // == the stub method ==================================================== + + Case* c = new Case(transactCodeName); + + MethodCall* realCall = new MethodCall(THIS_VALUE, method->name.data); + + // interface token validation is the very first thing we do + c->statements->Add(new MethodCall(stubClass->transact_data, + "enforceInterface", 1, new LiteralExpression("DESCRIPTOR"))); + + // args + Variable* cl = NULL; + VariableFactory stubArgs("_arg"); + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = stubArgs.Get(t); + v->dimension = arg->type.dimension; + + c->statements->Add(new VariableDeclaration(v)); + + if (convert_direction(arg->direction.data) & IN_PARAMETER) { + generate_create_from_parcel(t, c->statements, v, + stubClass->transact_data, &cl); + } else { + if (arg->type.dimension == 0) { + c->statements->Add(new Assignment(v, new NewExpression(v->type))); + } + else if (arg->type.dimension == 1) { + generate_new_array(v->type, c->statements, v, + stubClass->transact_data); + } + else { + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, + __LINE__); + } + } + + realCall->arguments.push_back(v); + + arg = arg->next; + } + + // the real call + Variable* _result = NULL; + if (0 == strcmp(method->type.type.data, "void")) { + c->statements->Add(realCall); + + if (!oneway) { + // report that there were no exceptions + MethodCall* ex = new MethodCall(stubClass->transact_reply, + "writeNoException", 0); + c->statements->Add(ex); + } + } else { + _result = new Variable(decl->returnType, "_result", + decl->returnTypeDimension); + c->statements->Add(new VariableDeclaration(_result, realCall)); + + if (!oneway) { + // report that there were no exceptions + MethodCall* ex = new MethodCall(stubClass->transact_reply, + "writeNoException", 0); + c->statements->Add(ex); + } + + // marshall the return value + generate_write_to_parcel(decl->returnType, c->statements, _result, + stubClass->transact_reply, + Type::PARCELABLE_WRITE_RETURN_VALUE); + } + + // out parameters + i = 0; + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = stubArgs.Get(i++); + + if (convert_direction(arg->direction.data) & OUT_PARAMETER) { + generate_write_to_parcel(t, c->statements, v, + stubClass->transact_reply, + Type::PARCELABLE_WRITE_RETURN_VALUE); + hasOutParams = true; + } + + arg = arg->next; + } + + // return true + c->statements->Add(new ReturnStatement(TRUE_VALUE)); + stubClass->transact_switch->cases.push_back(c); + + // == the proxy method =================================================== + Method* proxy = new Method; + proxy->comment = gather_comments(method->comments_token->extra); + proxy->modifiers = PUBLIC | OVERRIDE; + proxy->returnType = NAMES.Search(method->type.type.data); + proxy->returnTypeDimension = method->type.dimension; + proxy->name = method->name.data; + proxy->statements = new StatementBlock; + arg = method->args; + while (arg != NULL) { + proxy->parameters.push_back(new Variable( + NAMES.Search(arg->type.type.data), arg->name.data, + arg->type.dimension)); + arg = arg->next; + } + proxy->exceptions.push_back(REMOTE_EXCEPTION_TYPE); + proxyClass->elements.push_back(proxy); + + // the parcels + Variable* _data = new Variable(PARCEL_TYPE, "_data"); + proxy->statements->Add(new VariableDeclaration(_data, + new MethodCall(PARCEL_TYPE, "obtain"))); + Variable* _reply = NULL; + if (!oneway) { + _reply = new Variable(PARCEL_TYPE, "_reply"); + proxy->statements->Add(new VariableDeclaration(_reply, + new MethodCall(PARCEL_TYPE, "obtain"))); + } + + // the return value + _result = NULL; + if (0 != strcmp(method->type.type.data, "void")) { + _result = new Variable(proxy->returnType, "_result", + method->type.dimension); + proxy->statements->Add(new VariableDeclaration(_result)); + } + + // try and finally + TryStatement* tryStatement = new TryStatement(); + proxy->statements->Add(tryStatement); + FinallyStatement* finallyStatement = new FinallyStatement(); + proxy->statements->Add(finallyStatement); + + // the interface identifier token: the DESCRIPTOR constant, marshalled as a string + tryStatement->statements->Add(new MethodCall(_data, "writeInterfaceToken", + 1, new LiteralExpression("DESCRIPTOR"))); + + // the parameters + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = new Variable(t, arg->name.data, arg->type.dimension); + int dir = convert_direction(arg->direction.data); + if (dir == OUT_PARAMETER && arg->type.dimension != 0) { + IfStatement* checklen = new IfStatement(); + checklen->expression = new Comparison(v, "==", NULL_VALUE); + checklen->statements->Add(new MethodCall(_data, "writeInt", 1, + new LiteralExpression("-1"))); + checklen->elseif = new IfStatement(); + checklen->elseif->statements->Add(new MethodCall(_data, "writeInt", + 1, new FieldVariable(v, "length"))); + tryStatement->statements->Add(checklen); + } + else if (dir & IN_PARAMETER) { + generate_write_to_parcel(t, tryStatement->statements, v, _data, 0); + } + arg = arg->next; + } + + // the transact call + MethodCall* call = new MethodCall(proxyClass->mRemote, "transact", 4, + new LiteralExpression("Stub." + transactCodeName), + _data, _reply ? _reply : NULL_VALUE, + new LiteralExpression( + oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0")); + tryStatement->statements->Add(call); + + // throw back exceptions. + if (_reply) { + MethodCall* ex = new MethodCall(_reply, "readException", 0); + tryStatement->statements->Add(ex); + } + + // returning and cleanup + if (_reply != NULL) { + if (_result != NULL) { + generate_create_from_parcel(proxy->returnType, + tryStatement->statements, _result, _reply, &cl); + } + + // the out/inout parameters + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = new Variable(t, arg->name.data, arg->type.dimension); + if (convert_direction(arg->direction.data) & OUT_PARAMETER) { + generate_read_from_parcel(t, tryStatement->statements, + v, _reply, &cl); + } + arg = arg->next; + } + + finallyStatement->statements->Add(new MethodCall(_reply, "recycle")); + } + finallyStatement->statements->Add(new MethodCall(_data, "recycle")); + + if (_result != NULL) { + proxy->statements->Add(new ReturnStatement(_result)); + } +} + +static void +generate_interface_descriptors(StubClass* stub, ProxyClass* proxy) +{ + // the interface descriptor transaction handler + Case* c = new Case("INTERFACE_TRANSACTION"); + c->statements->Add(new MethodCall(stub->transact_reply, "writeString", + 1, new LiteralExpression("DESCRIPTOR"))); + c->statements->Add(new ReturnStatement(TRUE_VALUE)); + stub->transact_switch->cases.push_back(c); + + // and the proxy-side method returning the descriptor directly + Method* getDesc = new Method; + getDesc->modifiers = PUBLIC; + getDesc->returnType = STRING_TYPE; + getDesc->returnTypeDimension = 0; + getDesc->name = "getInterfaceDescriptor"; + getDesc->statements = new StatementBlock; + getDesc->statements->Add(new ReturnStatement(new LiteralExpression("DESCRIPTOR"))); + proxy->elements.push_back(getDesc); +} + +Class* +generate_binder_interface_class(const interface_type* iface) +{ + InterfaceType* interfaceType = static_cast<InterfaceType*>( + NAMES.Find(iface->package, iface->name.data)); + + // the interface class + Class* interface = new Class; + interface->comment = gather_comments(iface->comments_token->extra); + interface->modifiers = PUBLIC; + interface->what = Class::INTERFACE; + interface->type = interfaceType; + interface->interfaces.push_back(IINTERFACE_TYPE); + + // the stub inner class + StubClass* stub = new StubClass( + NAMES.Find(iface->package, append(iface->name.data, ".Stub").c_str()), + interfaceType); + interface->elements.push_back(stub); + + // the proxy inner class + ProxyClass* proxy = new ProxyClass( + NAMES.Find(iface->package, + append(iface->name.data, ".Stub.Proxy").c_str()), + interfaceType); + stub->elements.push_back(proxy); + + // stub and proxy support for getInterfaceDescriptor() + generate_interface_descriptors(stub, proxy); + + // all the declared methods of the interface + int index = 0; + interface_item_type* item = iface->interface_items; + while (item != NULL) { + if (item->item_type == METHOD_TYPE) { + method_type * method_item = (method_type*) item; + generate_method(method_item, interface, stub, proxy, method_item->assigned_id); + } + item = item->next; + index++; + } + + return interface; +} + diff --git a/tools/aidl/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp new file mode 100644 index 0000000..5e4dacc --- /dev/null +++ b/tools/aidl/generate_java_rpc.cpp @@ -0,0 +1,1001 @@ +#include "generate_java.h" +#include "Type.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +Type* SERVICE_CONTEXT_TYPE = new Type("android.content", + "Context", Type::BUILT_IN, false, false, false); +Type* PRESENTER_BASE_TYPE = new Type("android.support.place.connector", + "EventListener", Type::BUILT_IN, false, false, false); +Type* PRESENTER_LISTENER_BASE_TYPE = new Type("android.support.place.connector", + "EventListener.Listener", Type::BUILT_IN, false, false, false); +Type* RPC_BROKER_TYPE = new Type("android.support.place.connector", "Broker", + Type::BUILT_IN, false, false, false); +Type* RPC_CONTAINER_TYPE = new Type("com.android.athome.connector", "ConnectorContainer", + Type::BUILT_IN, false, false, false); +Type* PLACE_INFO_TYPE = new Type("android.support.place.connector", "PlaceInfo", + Type::BUILT_IN, false, false, false); +// TODO: Just use Endpoint, so this works for all endpoints. +Type* RPC_CONNECTOR_TYPE = new Type("android.support.place.connector", "Connector", + Type::BUILT_IN, false, false, false); +Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("android.support.place.rpc", + "EndpointInfo", true, __FILE__, __LINE__); +Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("android.support.place.rpc", "RpcResultHandler", + true, __FILE__, __LINE__); +Type* RPC_ERROR_LISTENER_TYPE = new Type("android.support.place.rpc", "RpcErrorHandler", + Type::BUILT_IN, false, false, false); +Type* RPC_CONTEXT_TYPE = new UserDataType("android.support.place.rpc", "RpcContext", true, + __FILE__, __LINE__); + +static void generate_create_from_data(Type* t, StatementBlock* addTo, const string& key, + Variable* v, Variable* data, Variable** cl); +static void generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from); +static void generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v, + Variable* data); + +static string +format_int(int n) +{ + char str[20]; + sprintf(str, "%d", n); + return string(str); +} + +static string +class_name_leaf(const string& str) +{ + string::size_type pos = str.rfind('.'); + if (pos == string::npos) { + return str; + } else { + return string(str, pos+1); + } +} + +static string +results_class_name(const string& n) +{ + string str = n; + str[0] = toupper(str[0]); + str.insert(0, "On"); + return str; +} + +static string +results_method_name(const string& n) +{ + string str = n; + str[0] = toupper(str[0]); + str.insert(0, "on"); + return str; +} + +static string +push_method_name(const string& n) +{ + string str = n; + str[0] = toupper(str[0]); + str.insert(0, "push"); + return str; +} + +// ================================================= +class DispatcherClass : public Class +{ +public: + DispatcherClass(const interface_type* iface, Expression* target); + virtual ~DispatcherClass(); + + void AddMethod(const method_type* method); + void DoneWithMethods(); + + Method* processMethod; + Variable* actionParam; + Variable* requestParam; + Variable* rpcContextParam; + Variable* errorParam; + Variable* requestData; + Variable* resultData; + IfStatement* dispatchIfStatement; + Expression* targetExpression; + +private: + void generate_process(); +}; + +DispatcherClass::DispatcherClass(const interface_type* iface, Expression* target) + :Class(), + dispatchIfStatement(NULL), + targetExpression(target) +{ + generate_process(); +} + +DispatcherClass::~DispatcherClass() +{ +} + +void +DispatcherClass::generate_process() +{ + // byte[] process(String action, byte[] params, RpcContext context, RpcError status) + this->processMethod = new Method; + this->processMethod->modifiers = PUBLIC; + this->processMethod->returnType = BYTE_TYPE; + this->processMethod->returnTypeDimension = 1; + this->processMethod->name = "process"; + this->processMethod->statements = new StatementBlock; + + this->actionParam = new Variable(STRING_TYPE, "action"); + this->processMethod->parameters.push_back(this->actionParam); + + this->requestParam = new Variable(BYTE_TYPE, "requestParam", 1); + this->processMethod->parameters.push_back(this->requestParam); + + this->rpcContextParam = new Variable(RPC_CONTEXT_TYPE, "context", 0); + this->processMethod->parameters.push_back(this->rpcContextParam); + + this->errorParam = new Variable(RPC_ERROR_TYPE, "errorParam", 0); + this->processMethod->parameters.push_back(this->errorParam); + + this->requestData = new Variable(RPC_DATA_TYPE, "request"); + this->processMethod->statements->Add(new VariableDeclaration(requestData, + new NewExpression(RPC_DATA_TYPE, 1, this->requestParam))); + + this->resultData = new Variable(RPC_DATA_TYPE, "resultData"); + this->processMethod->statements->Add(new VariableDeclaration(this->resultData, + NULL_VALUE)); +} + +void +DispatcherClass::AddMethod(const method_type* method) +{ + arg_type* arg; + + // The if/switch statement + IfStatement* ifs = new IfStatement(); + ifs->expression = new MethodCall(new StringLiteralExpression(method->name.data), "equals", + 1, this->actionParam); + StatementBlock* block = ifs->statements = new StatementBlock; + if (this->dispatchIfStatement == NULL) { + this->dispatchIfStatement = ifs; + this->processMethod->statements->Add(dispatchIfStatement); + } else { + this->dispatchIfStatement->elseif = ifs; + this->dispatchIfStatement = ifs; + } + + // The call to decl (from above) + MethodCall* realCall = new MethodCall(this->targetExpression, method->name.data); + + // args + Variable* classLoader = NULL; + VariableFactory stubArgs("_arg"); + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = stubArgs.Get(t); + v->dimension = arg->type.dimension; + + // Unmarshall the parameter + block->Add(new VariableDeclaration(v)); + if (convert_direction(arg->direction.data) & IN_PARAMETER) { + generate_create_from_data(t, block, arg->name.data, v, + this->requestData, &classLoader); + } else { + if (arg->type.dimension == 0) { + block->Add(new Assignment(v, new NewExpression(v->type))); + } + else if (arg->type.dimension == 1) { + generate_new_array(v->type, block, v, this->requestData); + } + else { + fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, + __LINE__); + } + } + + // Add that parameter to the method call + realCall->arguments.push_back(v); + + arg = arg->next; + } + + // Add a final parameter: RpcContext. Contains data about + // incoming request (e.g., certificate) + realCall->arguments.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0)); + + Type* returnType = NAMES.Search(method->type.type.data); + if (returnType == EVENT_FAKE_TYPE) { + returnType = VOID_TYPE; + } + + // the real call + bool first = true; + Variable* _result = NULL; + if (returnType == VOID_TYPE) { + block->Add(realCall); + } else { + _result = new Variable(returnType, "_result", + method->type.dimension); + block->Add(new VariableDeclaration(_result, realCall)); + + // need the result RpcData + if (first) { + block->Add(new Assignment(this->resultData, + new NewExpression(RPC_DATA_TYPE))); + first = false; + } + + // marshall the return value + generate_write_to_data(returnType, block, + new StringLiteralExpression("_result"), _result, this->resultData); + } + + // out parameters + int i = 0; + arg = method->args; + while (arg != NULL) { + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = stubArgs.Get(i++); + + if (convert_direction(arg->direction.data) & OUT_PARAMETER) { + // need the result RpcData + if (first) { + block->Add(new Assignment(this->resultData, new NewExpression(RPC_DATA_TYPE))); + first = false; + } + + generate_write_to_data(t, block, new StringLiteralExpression(arg->name.data), + v, this->resultData); + } + + arg = arg->next; + } +} + +void +DispatcherClass::DoneWithMethods() +{ + if (this->dispatchIfStatement == NULL) { + return; + } + + this->elements.push_back(this->processMethod); + + IfStatement* fallthrough = new IfStatement(); + fallthrough->statements = new StatementBlock; + fallthrough->statements->Add(new ReturnStatement( + new MethodCall(SUPER_VALUE, "process", 4, + this->actionParam, this->requestParam, + this->rpcContextParam, + this->errorParam))); + this->dispatchIfStatement->elseif = fallthrough; + IfStatement* s = new IfStatement; + s->statements = new StatementBlock; + this->processMethod->statements->Add(s); + s->expression = new Comparison(this->resultData, "!=", NULL_VALUE); + s->statements->Add(new ReturnStatement(new MethodCall(this->resultData, "serialize"))); + s->elseif = new IfStatement; + s = s->elseif; + s->statements->Add(new ReturnStatement(NULL_VALUE)); +} + +// ================================================= +class RpcProxyClass : public Class +{ +public: + RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType); + virtual ~RpcProxyClass(); + + Variable* endpoint; + Variable* broker; + +private: + void generate_ctor(); + void generate_get_endpoint_info(); +}; + +RpcProxyClass::RpcProxyClass(const interface_type* iface, InterfaceType* interfaceType) + :Class() +{ + this->comment = gather_comments(iface->comments_token->extra); + this->modifiers = PUBLIC; + this->what = Class::CLASS; + this->type = interfaceType; + + // broker + this->broker = new Variable(RPC_BROKER_TYPE, "_broker"); + this->elements.push_back(new Field(PRIVATE, this->broker)); + // endpoint + this->endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "_endpoint"); + this->elements.push_back(new Field(PRIVATE, this->endpoint)); + + // methods + generate_ctor(); + generate_get_endpoint_info(); +} + +RpcProxyClass::~RpcProxyClass() +{ +} + +void +RpcProxyClass::generate_ctor() +{ + Variable* broker = new Variable(RPC_BROKER_TYPE, "broker"); + Variable* endpoint = new Variable(RPC_ENDPOINT_INFO_TYPE, "endpoint"); + Method* ctor = new Method; + ctor->modifiers = PUBLIC; + ctor->name = class_name_leaf(this->type->Name()); + ctor->statements = new StatementBlock; + ctor->parameters.push_back(broker); + ctor->parameters.push_back(endpoint); + this->elements.push_back(ctor); + + ctor->statements->Add(new Assignment(this->broker, broker)); + ctor->statements->Add(new Assignment(this->endpoint, endpoint)); +} + +void +RpcProxyClass::generate_get_endpoint_info() +{ + Method* get = new Method; + get->modifiers = PUBLIC; + get->returnType = RPC_ENDPOINT_INFO_TYPE; + get->name = "getEndpointInfo"; + get->statements = new StatementBlock; + this->elements.push_back(get); + + get->statements->Add(new ReturnStatement(this->endpoint)); +} + +// ================================================= +class EventListenerClass : public DispatcherClass +{ +public: + EventListenerClass(const interface_type* iface, Type* listenerType); + virtual ~EventListenerClass(); + + Variable* _listener; + +private: + void generate_ctor(); +}; + +Expression* +generate_get_listener_expression(Type* cast) +{ + return new Cast(cast, new MethodCall(THIS_VALUE, "getView")); +} + +EventListenerClass::EventListenerClass(const interface_type* iface, Type* listenerType) + :DispatcherClass(iface, new FieldVariable(THIS_VALUE, "_listener")) +{ + this->modifiers = PRIVATE; + this->what = Class::CLASS; + this->type = new Type(iface->package ? iface->package : "", + append(iface->name.data, ".Presenter"), + Type::GENERATED, false, false, false); + this->extends = PRESENTER_BASE_TYPE; + + this->_listener = new Variable(listenerType, "_listener"); + this->elements.push_back(new Field(PRIVATE, this->_listener)); + + // methods + generate_ctor(); +} + +EventListenerClass::~EventListenerClass() +{ +} + +void +EventListenerClass::generate_ctor() +{ + Variable* broker = new Variable(RPC_BROKER_TYPE, "broker"); + Variable* listener = new Variable(this->_listener->type, "listener"); + Method* ctor = new Method; + ctor->modifiers = PUBLIC; + ctor->name = class_name_leaf(this->type->Name()); + ctor->statements = new StatementBlock; + ctor->parameters.push_back(broker); + ctor->parameters.push_back(listener); + this->elements.push_back(ctor); + + ctor->statements->Add(new MethodCall("super", 2, broker, listener)); + ctor->statements->Add(new Assignment(this->_listener, listener)); +} + +// ================================================= +class ListenerClass : public Class +{ +public: + ListenerClass(const interface_type* iface); + virtual ~ListenerClass(); + + bool needed; + +private: + void generate_ctor(); +}; + +ListenerClass::ListenerClass(const interface_type* iface) + :Class(), + needed(false) +{ + this->comment = "/** Extend this to listen to the events from this class. */"; + this->modifiers = STATIC | PUBLIC ; + this->what = Class::CLASS; + this->type = new Type(iface->package ? iface->package : "", + append(iface->name.data, ".Listener"), + Type::GENERATED, false, false, false); + this->extends = PRESENTER_LISTENER_BASE_TYPE; +} + +ListenerClass::~ListenerClass() +{ +} + +// ================================================= +class EndpointBaseClass : public DispatcherClass +{ +public: + EndpointBaseClass(const interface_type* iface); + virtual ~EndpointBaseClass(); + + bool needed; + +private: + void generate_ctor(); +}; + +EndpointBaseClass::EndpointBaseClass(const interface_type* iface) + :DispatcherClass(iface, THIS_VALUE), + needed(false) +{ + this->comment = "/** Extend this to implement a link service. */"; + this->modifiers = STATIC | PUBLIC | ABSTRACT; + this->what = Class::CLASS; + this->type = new Type(iface->package ? iface->package : "", + append(iface->name.data, ".EndpointBase"), + Type::GENERATED, false, false, false); + this->extends = RPC_CONNECTOR_TYPE; + + // methods + generate_ctor(); +} + +EndpointBaseClass::~EndpointBaseClass() +{ +} + +void +EndpointBaseClass::generate_ctor() +{ + Variable* container = new Variable(RPC_CONTAINER_TYPE, "container"); + Variable* broker = new Variable(RPC_BROKER_TYPE, "broker"); + Variable* place = new Variable(PLACE_INFO_TYPE, "placeInfo"); + Method* ctor = new Method; + ctor->modifiers = PUBLIC; + ctor->name = class_name_leaf(this->type->Name()); + ctor->statements = new StatementBlock; + ctor->parameters.push_back(container); + ctor->parameters.push_back(broker); + ctor->parameters.push_back(place); + this->elements.push_back(ctor); + + ctor->statements->Add(new MethodCall("super", 3, container, broker, place)); +} + +// ================================================= +class ResultDispatcherClass : public Class +{ +public: + ResultDispatcherClass(); + virtual ~ResultDispatcherClass(); + + void AddMethod(int index, const string& name, Method** method, Variable** param); + + bool needed; + Variable* methodId; + Variable* callback; + Method* onResultMethod; + Variable* resultParam; + SwitchStatement* methodSwitch; + +private: + void generate_ctor(); + void generate_onResult(); +}; + +ResultDispatcherClass::ResultDispatcherClass() + :Class(), + needed(false) +{ + this->modifiers = PRIVATE | FINAL; + this->what = Class::CLASS; + this->type = new Type("_ResultDispatcher", Type::GENERATED, false, false, false); + this->interfaces.push_back(RPC_RESULT_HANDLER_TYPE); + + // methodId + this->methodId = new Variable(INT_TYPE, "methodId"); + this->elements.push_back(new Field(PRIVATE, this->methodId)); + this->callback = new Variable(OBJECT_TYPE, "callback"); + this->elements.push_back(new Field(PRIVATE, this->callback)); + + // methods + generate_ctor(); + generate_onResult(); +} + +ResultDispatcherClass::~ResultDispatcherClass() +{ +} + +void +ResultDispatcherClass::generate_ctor() +{ + Variable* methodIdParam = new Variable(INT_TYPE, "methId"); + Variable* callbackParam = new Variable(OBJECT_TYPE, "cbObj"); + Method* ctor = new Method; + ctor->modifiers = PUBLIC; + ctor->name = class_name_leaf(this->type->Name()); + ctor->statements = new StatementBlock; + ctor->parameters.push_back(methodIdParam); + ctor->parameters.push_back(callbackParam); + this->elements.push_back(ctor); + + ctor->statements->Add(new Assignment(this->methodId, methodIdParam)); + ctor->statements->Add(new Assignment(this->callback, callbackParam)); +} + +void +ResultDispatcherClass::generate_onResult() +{ + this->onResultMethod = new Method; + this->onResultMethod->modifiers = PUBLIC; + this->onResultMethod->returnType = VOID_TYPE; + this->onResultMethod->returnTypeDimension = 0; + this->onResultMethod->name = "onResult"; + this->onResultMethod->statements = new StatementBlock; + this->elements.push_back(this->onResultMethod); + + this->resultParam = new Variable(BYTE_TYPE, "result", 1); + this->onResultMethod->parameters.push_back(this->resultParam); + + this->methodSwitch = new SwitchStatement(this->methodId); + this->onResultMethod->statements->Add(this->methodSwitch); +} + +void +ResultDispatcherClass::AddMethod(int index, const string& name, Method** method, Variable** param) +{ + Method* m = new Method; + m->modifiers = PUBLIC; + m->returnType = VOID_TYPE; + m->returnTypeDimension = 0; + m->name = name; + m->statements = new StatementBlock; + *param = new Variable(BYTE_TYPE, "result", 1); + m->parameters.push_back(*param); + this->elements.push_back(m); + *method = m; + + Case* c = new Case(format_int(index)); + c->statements->Add(new MethodCall(new LiteralExpression("this"), name, 1, this->resultParam)); + c->statements->Add(new Break()); + + this->methodSwitch->cases.push_back(c); +} + +// ================================================= +static void +generate_new_array(Type* t, StatementBlock* addTo, Variable* v, Variable* from) +{ + fprintf(stderr, "aidl: implement generate_new_array %s:%d\n", __FILE__, __LINE__); + exit(1); +} + +static void +generate_create_from_data(Type* t, StatementBlock* addTo, const string& key, Variable* v, + Variable* data, Variable** cl) +{ + Expression* k = new StringLiteralExpression(key); + if (v->dimension == 0) { + t->CreateFromRpcData(addTo, k, v, data, cl); + } + if (v->dimension == 1) { + //t->ReadArrayFromRpcData(addTo, v, data, cl); + fprintf(stderr, "aidl: implement generate_create_from_data for arrays%s:%d\n", + __FILE__, __LINE__); + } +} + +static void +generate_write_to_data(Type* t, StatementBlock* addTo, Expression* k, Variable* v, Variable* data) +{ + if (v->dimension == 0) { + t->WriteToRpcData(addTo, k, v, data, 0); + } + if (v->dimension == 1) { + //t->WriteArrayToParcel(addTo, v, data); + fprintf(stderr, "aidl: implement generate_write_to_data for arrays%s:%d\n", + __FILE__, __LINE__); + } +} + +// ================================================= +static Type* +generate_results_method(const method_type* method, RpcProxyClass* proxyClass) +{ + arg_type* arg; + + string resultsMethodName = results_method_name(method->name.data); + Type* resultsInterfaceType = new Type(results_class_name(method->name.data), + Type::GENERATED, false, false, false); + + if (!method->oneway) { + Class* resultsClass = new Class; + resultsClass->modifiers = STATIC | PUBLIC; + resultsClass->what = Class::INTERFACE; + resultsClass->type = resultsInterfaceType; + + Method* resultMethod = new Method; + resultMethod->comment = gather_comments(method->comments_token->extra); + resultMethod->modifiers = PUBLIC; + resultMethod->returnType = VOID_TYPE; + resultMethod->returnTypeDimension = 0; + resultMethod->name = resultsMethodName; + if (0 != strcmp("void", method->type.type.data)) { + resultMethod->parameters.push_back(new Variable(NAMES.Search(method->type.type.data), + "_result", method->type.dimension)); + } + arg = method->args; + while (arg != NULL) { + if (convert_direction(arg->direction.data) & OUT_PARAMETER) { + resultMethod->parameters.push_back(new Variable( + NAMES.Search(arg->type.type.data), arg->name.data, + arg->type.dimension)); + } + arg = arg->next; + } + resultsClass->elements.push_back(resultMethod); + + if (resultMethod->parameters.size() > 0) { + proxyClass->elements.push_back(resultsClass); + return resultsInterfaceType; + } + } + //delete resultsInterfaceType; + return NULL; +} + +static void +generate_proxy_method(const method_type* method, RpcProxyClass* proxyClass, + ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index) +{ + arg_type* arg; + Method* proxyMethod = new Method; + proxyMethod->comment = gather_comments(method->comments_token->extra); + proxyMethod->modifiers = PUBLIC; + proxyMethod->returnType = VOID_TYPE; + proxyMethod->returnTypeDimension = 0; + proxyMethod->name = method->name.data; + proxyMethod->statements = new StatementBlock; + proxyClass->elements.push_back(proxyMethod); + + // The local variables + Variable* _data = new Variable(RPC_DATA_TYPE, "_data"); + proxyMethod->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE))); + + // Add the arguments + arg = method->args; + while (arg != NULL) { + if (convert_direction(arg->direction.data) & IN_PARAMETER) { + // Function signature + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = new Variable(t, arg->name.data, arg->type.dimension); + proxyMethod->parameters.push_back(v); + + // Input parameter marshalling + generate_write_to_data(t, proxyMethod->statements, + new StringLiteralExpression(arg->name.data), v, _data); + } + arg = arg->next; + } + + // If there is a results interface for this class + Expression* resultParameter; + if (resultsInterfaceType != NULL) { + // Result interface parameter + Variable* resultListener = new Variable(resultsInterfaceType, "_result"); + proxyMethod->parameters.push_back(resultListener); + + // Add the results dispatcher callback + resultsDispatcherClass->needed = true; + resultParameter = new NewExpression(resultsDispatcherClass->type, 2, + new LiteralExpression(format_int(index)), resultListener); + } else { + resultParameter = NULL_VALUE; + } + + // All proxy methods take an error parameter + Variable* errorListener = new Variable(RPC_ERROR_LISTENER_TYPE, "_errors"); + proxyMethod->parameters.push_back(errorListener); + + // Call the broker + proxyMethod->statements->Add(new MethodCall(new FieldVariable(THIS_VALUE, "_broker"), + "sendRpc", 5, + proxyClass->endpoint, + new StringLiteralExpression(method->name.data), + new MethodCall(_data, "serialize"), + resultParameter, + errorListener)); +} + +static void +generate_result_dispatcher_method(const method_type* method, + ResultDispatcherClass* resultsDispatcherClass, Type* resultsInterfaceType, int index) +{ + arg_type* arg; + Method* dispatchMethod; + Variable* dispatchParam; + resultsDispatcherClass->AddMethod(index, method->name.data, &dispatchMethod, &dispatchParam); + + Variable* classLoader = NULL; + Variable* resultData = new Variable(RPC_DATA_TYPE, "resultData"); + dispatchMethod->statements->Add(new VariableDeclaration(resultData, + new NewExpression(RPC_DATA_TYPE, 1, dispatchParam))); + + // The callback method itself + MethodCall* realCall = new MethodCall( + new Cast(resultsInterfaceType, new FieldVariable(THIS_VALUE, "callback")), + results_method_name(method->name.data)); + + // The return value + { + Type* t = NAMES.Search(method->type.type.data); + if (t != VOID_TYPE) { + Variable* rv = new Variable(t, "rv"); + dispatchMethod->statements->Add(new VariableDeclaration(rv)); + generate_create_from_data(t, dispatchMethod->statements, "_result", rv, + resultData, &classLoader); + realCall->arguments.push_back(rv); + } + } + + VariableFactory stubArgs("arg"); + arg = method->args; + while (arg != NULL) { + if (convert_direction(arg->direction.data) & OUT_PARAMETER) { + // Unmarshall the results + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = stubArgs.Get(t); + dispatchMethod->statements->Add(new VariableDeclaration(v)); + + generate_create_from_data(t, dispatchMethod->statements, arg->name.data, v, + resultData, &classLoader); + + // Add the argument to the callback + realCall->arguments.push_back(v); + } + arg = arg->next; + } + + // Call the callback method + IfStatement* ifst = new IfStatement; + ifst->expression = new Comparison(new FieldVariable(THIS_VALUE, "callback"), "!=", NULL_VALUE); + dispatchMethod->statements->Add(ifst); + ifst->statements->Add(realCall); +} + +static void +generate_regular_method(const method_type* method, RpcProxyClass* proxyClass, + EndpointBaseClass* serviceBaseClass, ResultDispatcherClass* resultsDispatcherClass, + int index) +{ + arg_type* arg; + + // == the callback interface for results ================================ + // the service base class + Type* resultsInterfaceType = generate_results_method(method, proxyClass); + + // == the method in the proxy class ===================================== + generate_proxy_method(method, proxyClass, resultsDispatcherClass, resultsInterfaceType, index); + + // == the method in the result dispatcher class ========================= + if (resultsInterfaceType != NULL) { + generate_result_dispatcher_method(method, resultsDispatcherClass, resultsInterfaceType, + index); + } + + // == The abstract method that the service developers implement ========== + Method* decl = new Method; + decl->comment = gather_comments(method->comments_token->extra); + decl->modifiers = PUBLIC | ABSTRACT; + decl->returnType = NAMES.Search(method->type.type.data); + decl->returnTypeDimension = method->type.dimension; + decl->name = method->name.data; + arg = method->args; + while (arg != NULL) { + decl->parameters.push_back(new Variable( + NAMES.Search(arg->type.type.data), arg->name.data, + arg->type.dimension)); + arg = arg->next; + } + + // Add the default RpcContext param to all methods + decl->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0)); + + serviceBaseClass->elements.push_back(decl); + + + // == the dispatch method in the service base class ====================== + serviceBaseClass->AddMethod(method); +} + +static void +generate_event_method(const method_type* method, RpcProxyClass* proxyClass, + EndpointBaseClass* serviceBaseClass, ListenerClass* listenerClass, + EventListenerClass* presenterClass, int index) +{ + arg_type* arg; + listenerClass->needed = true; + + // == the push method in the service base class ========================= + Method* push = new Method; + push->modifiers = PUBLIC; + push->name = push_method_name(method->name.data); + push->statements = new StatementBlock; + push->returnType = VOID_TYPE; + serviceBaseClass->elements.push_back(push); + + // The local variables + Variable* _data = new Variable(RPC_DATA_TYPE, "_data"); + push->statements->Add(new VariableDeclaration(_data, new NewExpression(RPC_DATA_TYPE))); + + // Add the arguments + arg = method->args; + while (arg != NULL) { + // Function signature + Type* t = NAMES.Search(arg->type.type.data); + Variable* v = new Variable(t, arg->name.data, arg->type.dimension); + push->parameters.push_back(v); + + // Input parameter marshalling + generate_write_to_data(t, push->statements, + new StringLiteralExpression(arg->name.data), v, _data); + + arg = arg->next; + } + + // Send the notifications + push->statements->Add(new MethodCall("pushEvent", 2, + new StringLiteralExpression(method->name.data), + new MethodCall(_data, "serialize"))); + + // == the event callback dispatcher method ==================================== + presenterClass->AddMethod(method); + + // == the event method in the listener base class ===================== + Method* event = new Method; + event->modifiers = PUBLIC; + event->name = method->name.data; + event->statements = new StatementBlock; + event->returnType = VOID_TYPE; + listenerClass->elements.push_back(event); + arg = method->args; + while (arg != NULL) { + event->parameters.push_back(new Variable( + NAMES.Search(arg->type.type.data), arg->name.data, + arg->type.dimension)); + arg = arg->next; + } + + // Add a final parameter: RpcContext. Contains data about + // incoming request (e.g., certificate) + event->parameters.push_back(new Variable(RPC_CONTEXT_TYPE, "context", 0)); +} + +static void +generate_listener_methods(RpcProxyClass* proxyClass, Type* presenterType, Type* listenerType) +{ + // AndroidAtHomePresenter _presenter; + // void startListening(Listener listener) { + // stopListening(); + // _presenter = new Presenter(_broker, listener); + // _presenter.startListening(_endpoint); + // } + // void stopListening() { + // if (_presenter != null) { + // _presenter.stopListening(); + // } + // } + + Variable* _presenter = new Variable(presenterType, "_presenter"); + proxyClass->elements.push_back(new Field(PRIVATE, _presenter)); + + Variable* listener = new Variable(listenerType, "listener"); + + Method* startListeningMethod = new Method; + startListeningMethod->modifiers = PUBLIC; + startListeningMethod->returnType = VOID_TYPE; + startListeningMethod->name = "startListening"; + startListeningMethod->statements = new StatementBlock; + startListeningMethod->parameters.push_back(listener); + proxyClass->elements.push_back(startListeningMethod); + + startListeningMethod->statements->Add(new MethodCall(THIS_VALUE, "stopListening")); + startListeningMethod->statements->Add(new Assignment(_presenter, + new NewExpression(presenterType, 2, proxyClass->broker, listener))); + startListeningMethod->statements->Add(new MethodCall(_presenter, + "startListening", 1, proxyClass->endpoint)); + + Method* stopListeningMethod = new Method; + stopListeningMethod->modifiers = PUBLIC; + stopListeningMethod->returnType = VOID_TYPE; + stopListeningMethod->name = "stopListening"; + stopListeningMethod->statements = new StatementBlock; + proxyClass->elements.push_back(stopListeningMethod); + + IfStatement* ifst = new IfStatement; + ifst->expression = new Comparison(_presenter, "!=", NULL_VALUE); + stopListeningMethod->statements->Add(ifst); + + ifst->statements->Add(new MethodCall(_presenter, "stopListening")); + ifst->statements->Add(new Assignment(_presenter, NULL_VALUE)); +} + +Class* +generate_rpc_interface_class(const interface_type* iface) +{ + // the proxy class + InterfaceType* interfaceType = static_cast<InterfaceType*>( + NAMES.Find(iface->package, iface->name.data)); + RpcProxyClass* proxy = new RpcProxyClass(iface, interfaceType); + + // the listener class + ListenerClass* listener = new ListenerClass(iface); + + // the presenter class + EventListenerClass* presenter = new EventListenerClass(iface, listener->type); + + // the service base class + EndpointBaseClass* base = new EndpointBaseClass(iface); + proxy->elements.push_back(base); + + // the result dispatcher + ResultDispatcherClass* results = new ResultDispatcherClass(); + + // all the declared methods of the proxy + int index = 0; + interface_item_type* item = iface->interface_items; + while (item != NULL) { + if (item->item_type == METHOD_TYPE) { + if (NAMES.Search(((method_type*)item)->type.type.data) == EVENT_FAKE_TYPE) { + generate_event_method((method_type*)item, proxy, base, listener, presenter, index); + } else { + generate_regular_method((method_type*)item, proxy, base, results, index); + } + } + item = item->next; + index++; + } + presenter->DoneWithMethods(); + base->DoneWithMethods(); + + // only add this if there are methods with results / out parameters + if (results->needed) { + proxy->elements.push_back(results); + } + if (listener->needed) { + proxy->elements.push_back(listener); + proxy->elements.push_back(presenter); + generate_listener_methods(proxy, presenter->type, listener->type); + } + + return proxy; +} diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp new file mode 100644 index 0000000..7b2daeb --- /dev/null +++ b/tools/aidl/options.cpp @@ -0,0 +1,154 @@ + +#include "options.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int +usage() +{ + fprintf(stderr, + "usage: aidl OPTIONS INPUT [OUTPUT]\n" + " aidl --preprocess OUTPUT INPUT...\n" + "\n" + "OPTIONS:\n" + " -I<DIR> search path for import statements.\n" + " -d<FILE> generate dependency file.\n" + " -a generate dependency file next to the output file with the name based on the input file.\n" + " -p<FILE> file created by --preprocess to import.\n" + " -o<FOLDER> base output folder for generated files.\n" + " -b fail when trying to compile a parcelable.\n" + "\n" + "INPUT:\n" + " An aidl interface file.\n" + "\n" + "OUTPUT:\n" + " The generated interface files.\n" + " If omitted and the -o option is not used, the input filename is used, with the .aidl extension changed to a .java extension.\n" + " If the -o option is used, the generated files will be placed in the base output folder, under their package folder\n" + ); + return 1; +} + +int +parse_options(int argc, const char* const* argv, Options *options) +{ + int i = 1; + + if (argc >= 2 && 0 == strcmp(argv[1], "--preprocess")) { + if (argc < 4) { + return usage(); + } + options->outputFileName = argv[2]; + for (int i=3; i<argc; i++) { + options->filesToPreprocess.push_back(argv[i]); + } + options->task = PREPROCESS_AIDL; + return 0; + } + + options->task = COMPILE_AIDL; + options->failOnParcelable = false; + options->autoDepFile = false; + + // OPTIONS + while (i < argc) { + const char* s = argv[i]; + int len = strlen(s); + if (s[0] == '-') { + if (len > 1) { + // -I<system-import-path> + if (s[1] == 'I') { + if (len > 2) { + options->importPaths.push_back(s+2); + } else { + fprintf(stderr, "-I option (%d) requires a path.\n", i); + return usage(); + } + } + else if (s[1] == 'd') { + if (len > 2) { + options->depFileName = s+2; + } else { + fprintf(stderr, "-d option (%d) requires a file.\n", i); + return usage(); + } + } + else if (s[1] == 'a') { + options->autoDepFile = true; + } + else if (s[1] == 'p') { + if (len > 2) { + options->preprocessedFiles.push_back(s+2); + } else { + fprintf(stderr, "-p option (%d) requires a file.\n", i); + return usage(); + } + } + else if (s[1] == 'o') { + if (len > 2) { + options->outputBaseFolder = s+2; + } else { + fprintf(stderr, "-o option (%d) requires a path.\n", i); + return usage(); + } + } + else if (len == 2 && s[1] == 'b') { + options->failOnParcelable = true; + } + else { + // s[1] is not known + fprintf(stderr, "unknown option (%d): %s\n", i, s); + return usage(); + } + } else { + // len <= 1 + fprintf(stderr, "unknown option (%d): %s\n", i, s); + return usage(); + } + } else { + // s[0] != '-' + break; + } + i++; + } + + // INPUT + if (i < argc) { + options->inputFileName = argv[i]; + i++; + } else { + fprintf(stderr, "INPUT required\n"); + return usage(); + } + + // OUTPUT + if (i < argc) { + options->outputFileName = argv[i]; + i++; + } else if (options->outputBaseFolder.length() == 0) { + // copy input into output and change the extension from .aidl to .java + options->outputFileName = options->inputFileName; + string::size_type pos = options->outputFileName.size()-5; + if (options->outputFileName.compare(pos, 5, ".aidl") == 0) { // 5 = strlen(".aidl") + options->outputFileName.replace(pos, 5, ".java"); // 5 = strlen(".aidl") + } else { + fprintf(stderr, "INPUT is not an .aidl file.\n"); + return usage(); + } + } + + // anything remaining? + if (i != argc) { + fprintf(stderr, "unknown option%s:", (i==argc-1?(const char*)"":(const char*)"s")); + for (; i<argc-1; i++) { + fprintf(stderr, " %s", argv[i]); + } + fprintf(stderr, "\n"); + return usage(); + } + + return 0; +} + diff --git a/tools/aidl/options.h b/tools/aidl/options.h new file mode 100644 index 0000000..387e37d --- /dev/null +++ b/tools/aidl/options.h @@ -0,0 +1,36 @@ +#ifndef DEVICE_TOOLS_AIDL_H +#define DEVICE_TOOLS_AIDL_H + +#include <string.h> +#include <string> +#include <vector> + +using namespace std; + +enum { + COMPILE_AIDL, + PREPROCESS_AIDL +}; + +// This struct is the parsed version of the command line options +struct Options +{ + int task; + bool failOnParcelable; + vector<string> importPaths; + vector<string> preprocessedFiles; + string inputFileName; + string outputFileName; + string outputBaseFolder; + string depFileName; + bool autoDepFile; + + vector<string> filesToPreprocess; +}; + +// takes the inputs from the command line and fills in the Options struct +// Returns 0 on success, and nonzero on failure. +// It also prints the usage statement on failure. +int parse_options(int argc, const char* const* argv, Options *options); + +#endif // DEVICE_TOOLS_AIDL_H diff --git a/tools/aidl/options_test.cpp b/tools/aidl/options_test.cpp new file mode 100644 index 0000000..bd106ce --- /dev/null +++ b/tools/aidl/options_test.cpp @@ -0,0 +1,291 @@ +#include <iostream> +#include "options.h" + +const bool VERBOSE = false; + +using namespace std; + +struct Answer { + const char* argv[8]; + int result; + const char* systemSearchPath[8]; + const char* localSearchPath[8]; + const char* inputFileName; + language_t nativeLanguage; + const char* outputH; + const char* outputCPP; + const char* outputJava; +}; + +bool +match_arrays(const char* const*expected, const vector<string> &got) +{ + int count = 0; + while (expected[count] != NULL) { + count++; + } + if (got.size() != count) { + return false; + } + for (int i=0; i<count; i++) { + if (got[i] != expected[i]) { + return false; + } + } + return true; +} + +void +print_array(const char* prefix, const char* const*expected) +{ + while (*expected) { + cout << prefix << *expected << endl; + expected++; + } +} + +void +print_array(const char* prefix, const vector<string> &got) +{ + size_t count = got.size(); + for (size_t i=0; i<count; i++) { + cout << prefix << got[i] << endl; + } +} + +static int +test(const Answer& answer) +{ + int argc = 0; + while (answer.argv[argc]) { + argc++; + } + + int err = 0; + + Options options; + int result = parse_options(argc, answer.argv, &options); + + // result + if (((bool)result) != ((bool)answer.result)) { + cout << "mismatch: result: got " << result << " expected " << + answer.result << endl; + err = 1; + } + + if (result != 0) { + // if it failed, everything is invalid + return err; + } + + // systemSearchPath + if (!match_arrays(answer.systemSearchPath, options.systemSearchPath)) { + cout << "mismatch: systemSearchPath: got" << endl; + print_array(" ", options.systemSearchPath); + cout << " expected" << endl; + print_array(" ", answer.systemSearchPath); + err = 1; + } + + // localSearchPath + if (!match_arrays(answer.localSearchPath, options.localSearchPath)) { + cout << "mismatch: localSearchPath: got" << endl; + print_array(" ", options.localSearchPath); + cout << " expected" << endl; + print_array(" ", answer.localSearchPath); + err = 1; + } + + // inputFileName + if (answer.inputFileName != options.inputFileName) { + cout << "mismatch: inputFileName: got " << options.inputFileName + << " expected " << answer.inputFileName << endl; + err = 1; + } + + // nativeLanguage + if (answer.nativeLanguage != options.nativeLanguage) { + cout << "mismatch: nativeLanguage: got " << options.nativeLanguage + << " expected " << answer.nativeLanguage << endl; + err = 1; + } + + // outputH + if (answer.outputH != options.outputH) { + cout << "mismatch: outputH: got " << options.outputH + << " expected " << answer.outputH << endl; + err = 1; + } + + // outputCPP + if (answer.outputCPP != options.outputCPP) { + cout << "mismatch: outputCPP: got " << options.outputCPP + << " expected " << answer.outputCPP << endl; + err = 1; + } + + // outputJava + if (answer.outputJava != options.outputJava) { + cout << "mismatch: outputJava: got " << options.outputJava + << " expected " << answer.outputJava << endl; + err = 1; + } + + return err; +} + +const Answer g_tests[] = { + + { + /* argv */ { "test", "-i/moof", "-I/blah", "-Ibleh", "-imoo", "inputFileName.aidl_cpp", NULL, NULL }, + /* result */ 0, + /* systemSearchPath */ { "/blah", "bleh", NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { "/moof", "moo", NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "inputFileName.aidl_cpp", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "", + /* outputJava */ "" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", NULL, NULL, NULL, NULL }, + /* result */ 0, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "inputFileName.aidl_cpp", + /* nativeLanguage */ CPP, + /* outputH */ "outputH", + /* outputCPP */ "", + /* outputJava */ "" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", NULL, NULL, NULL, NULL }, + /* result */ 0, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "inputFileName.aidl_cpp", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "outputCPP", + /* outputJava */ "" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", NULL, NULL, NULL, NULL }, + /* result */ 0, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "inputFileName.aidl_cpp", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "", + /* outputJava */ "outputJava" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-ocpp", "outputCPP", "-ojava", "outputJava" }, + /* result */ 0, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "inputFileName.aidl_cpp", + /* nativeLanguage */ CPP, + /* outputH */ "outputH", + /* outputCPP */ "outputCPP", + /* outputJava */ "outputJava" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-oh", "outputH", "-oh", "outputH1", NULL, NULL }, + /* result */ 1, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "", + /* outputJava */ "" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-ocpp", "outputCPP", "-ocpp", "outputCPP1", NULL, NULL }, + /* result */ 1, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "", + /* outputJava */ "" + }, + + { + /* argv */ { "test", "inputFileName.aidl_cpp", "-ojava", "outputJava", "-ojava", "outputJava1", NULL, NULL }, + /* result */ 1, + /* systemSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* localSearchPath */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, + /* inputFileName */ "", + /* nativeLanguage */ CPP, + /* outputH */ "", + /* outputCPP */ "", + /* outputJava */ "" + }, + +}; + +int +main(int argc, const char** argv) +{ + const int count = sizeof(g_tests)/sizeof(g_tests[0]); + int matches[count]; + + int result = 0; + for (int i=0; i<count; i++) { + if (VERBOSE) { + cout << endl; + cout << "---------------------------------------------" << endl; + const char* const* p = g_tests[i].argv; + while (*p) { + cout << " " << *p; + p++; + } + cout << endl; + cout << "---------------------------------------------" << endl; + } + matches[i] = test(g_tests[i]); + if (VERBOSE) { + if (0 == matches[i]) { + cout << "passed" << endl; + } else { + cout << "failed" << endl; + } + result |= matches[i]; + } + } + + cout << endl; + cout << "=============================================" << endl; + cout << "options_test summary" << endl; + cout << "=============================================" << endl; + + if (!result) { + cout << "passed" << endl; + } else { + cout << "failed the following tests:" << endl; + for (int i=0; i<count; i++) { + if (matches[i]) { + cout << " "; + const char* const* p = g_tests[i].argv; + while (*p) { + cout << " " << *p; + p++; + } + cout << endl; + } + } + } + + return result; +} + diff --git a/tools/aidl/search_path.cpp b/tools/aidl/search_path.cpp new file mode 100644 index 0000000..ffb6cb2 --- /dev/null +++ b/tools/aidl/search_path.cpp @@ -0,0 +1,57 @@ +#include <unistd.h> +#include "search_path.h" +#include "options.h" +#include <string.h> + +#ifdef HAVE_MS_C_RUNTIME +#include <io.h> +#endif + +static vector<string> g_importPaths; + +void +set_import_paths(const vector<string>& importPaths) +{ + g_importPaths = importPaths; +} + +char* +find_import_file(const char* given) +{ + string expected = given; + + int N = expected.length(); + for (int i=0; i<N; i++) { + char c = expected[i]; + if (c == '.') { + expected[i] = OS_PATH_SEPARATOR; + } + } + expected += ".aidl"; + + vector<string>& paths = g_importPaths; + for (vector<string>::iterator it=paths.begin(); it!=paths.end(); it++) { + string f = *it; + if (f.size() == 0) { + f = "."; + f += OS_PATH_SEPARATOR; + } + else if (f[f.size()-1] != OS_PATH_SEPARATOR) { + f += OS_PATH_SEPARATOR; + } + f.append(expected); + +#ifdef HAVE_MS_C_RUNTIME + /* check that the file exists and is not write-only */ + if (0 == _access(f.c_str(), 0) && /* mode 0=exist */ + 0 == _access(f.c_str(), 4) ) { /* mode 4=readable */ +#else + if (0 == access(f.c_str(), R_OK)) { +#endif + return strdup(f.c_str()); + } + } + + return NULL; +} + diff --git a/tools/aidl/search_path.h b/tools/aidl/search_path.h new file mode 100644 index 0000000..2bf94b1 --- /dev/null +++ b/tools/aidl/search_path.h @@ -0,0 +1,23 @@ +#ifndef DEVICE_TOOLS_AIDL_SEARCH_PATH_H +#define DEVICE_TOOLS_AIDL_SEARCH_PATH_H + +#include <stdio.h> + +#if __cplusplus +#include <vector> +#include <string> +using namespace std; +extern "C" { +#endif + +// returns a FILE* and the char* for the file that it found +// given is the class name we're looking for +char* find_import_file(const char* given); + +#if __cplusplus +}; // extern "C" +void set_import_paths(const vector<string>& importPaths); +#endif + +#endif // DEVICE_TOOLS_AIDL_SEARCH_PATH_H + |