android_frameworks_base/tools/aidl/generate_java.cpp
Adrian Taylor 754270601a Removing "import" statements from AIDL-generated .java files.
AIDL generates unnecessary "import" statements. These cause warnings
within Eclipse when the default Eclipse warnings settings are used.
This is inconvenient since the generated .java files are not
editable. Some pesky organisations have a zero-warnings policy
too, so there's no option but to fiddle with the Eclipse settings.

This patch ensures that all usages of class names within the
generated code are fully-qualified. In practice, they were nearly
all fully-qualified already. And this patch also removes the
generation of the import statements, since they are redundant
if we're using fully-qualified names everywhere.

This should fix issue 43 in the Google Code Android issues tracker.
http://code.google.com/p/android/issues/detail?id=43

I would appreciate if somebody who knows exactly how 'aidl'
works could confirm that there's no reason 'import' statements
would have been necessary except for the bits I've fixed. (I think
unqualified names were used much more frequently in early versions
of aidl, which might explain why import statements are generated
so eagerly).
2009-02-13 09:53:11 +00:00

654 lines
22 KiB
C++

#include "generate_java.h"
#include "AST.h"
#include "Type.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// =================================================
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;
};
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];
}
// =================================================
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;
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;
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, iinType);
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;
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 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;
}
static string
append(const char* a, const char* b)
{
string s = a;
s += b;
return s;
}
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)
{
if (v->dimension == 0) {
t->CreateFromParcel(addTo, v, parcel);
}
if (v->dimension == 1) {
t->CreateArrayFromParcel(addTo, v, parcel);
}
}
static void
generate_read_from_parcel(Type* t, StatementBlock* addTo, Variable* v,
Variable* parcel)
{
if (v->dimension == 0) {
t->ReadFromParcel(addTo, v, parcel);
}
if (v->dimension == 1) {
t->ReadArrayFromParcel(addTo, v, parcel);
}
}
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[50];
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
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);
} 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;
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);
}
// 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);
}
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);
}
static Class*
generate_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) {
generate_method((method_type*)item, interface, stub, proxy, index);
}
item = item->next;
index++;
}
return interface;
}
int
generate_java(const string& filename, const string& originalSrc,
interface_type* iface)
{
Document* document = new Document;
document->comment = "";
if (iface->package) document->package = iface->package;
document->originalSrc = originalSrc;
document->classes.push_back(generate_interface_class(iface));
// 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;
}