#include "generate_java.h"
#include "AST.h"
#include "Type.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 | 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->Name();
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, "(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 ? "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;
}