#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 | 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, 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; }