org/objectweb/proactive/core/mop/JavassistByteCodeStubBuilder.java

00001 /* 
00002  * ################################################################
00003  * 
00004  * ProActive: The Java(TM) library for Parallel, Distributed, 
00005  *            Concurrent computing with Security and Mobility
00006  * 
00007  * Copyright (C) 1997-2007 INRIA/University of Nice-Sophia Antipolis
00008  * Contact: proactive@objectweb.org
00009  * 
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or any later version.
00014  *  
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  * 
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with this library; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
00023  * USA
00024  *  
00025  *  Initial developer(s):               The ProActive Team
00026  *                        http://www.inria.fr/oasis/ProActive/contacts.html
00027  *  Contributor(s): 
00028  * 
00029  * ################################################################
00030  */ 
00031 package org.objectweb.proactive.core.mop;
00032 
00033 import java.io.Serializable;
00034 import java.lang.reflect.GenericDeclaration;
00035 import java.lang.reflect.TypeVariable;
00036 import java.util.HashMap;
00037 import java.util.Iterator;
00038 import java.util.List;
00039 import java.util.Map;
00040 import java.util.Vector;
00041 
00042 import javassist.CannotCompileException;
00043 import javassist.ClassClassPath;
00044 import javassist.ClassPool;
00045 import javassist.CtClass;
00046 import javassist.CtConstructor;
00047 import javassist.CtField;
00048 import javassist.CtMethod;
00049 import javassist.CtNewMethod;
00050 import javassist.Modifier;
00051 import javassist.NotFoundException;
00052 
00053 
00060 public class JavassistByteCodeStubBuilder {
00061     private static CtMethod proxyGetter;
00062     private static CtMethod proxySetter;
00063 
00073     public static byte[] create(String className, Class[] genericParameters) throws NoClassDefFoundError {
00074         CtClass generatedCtClass = null;
00075         if (genericParameters == null) {
00076                 genericParameters = new Class[0];
00077         }
00078         CtMethod[] reifiedMethodsWithoutGenerics;
00079         try {
00080             ClassPool pool = ClassPool.getDefault();
00081             generatedCtClass = pool.makeClass(Utils.convertClassNameToStubClassName(
00082                         className, genericParameters));
00083             
00084             CtClass superCtClass = null;
00085             try {
00086                 superCtClass = pool.get(className);
00087             } catch (NotFoundException e) {
00088                 // may happen in environments with multiple classloaders: className is not available
00089                 // in the initial classpath of javassist's class pool
00090                 // ==> try to append classpath of the class corresponding to className
00091                 pool.appendClassPath(new ClassClassPath(Class.forName(className)));
00092                 superCtClass = pool.get(className);
00093             }
00094             
00095             CtField outsideOfConstructorField = new CtField(pool.get(CtClass.booleanType.getName()), "outsideOfConstructor", generatedCtClass);
00096             
00097             generatedCtClass.addField(outsideOfConstructorField, (superCtClass.isInterface()? " false" : "true"));
00098             if (superCtClass.isInterface()) {
00099                 generatedCtClass.addInterface(superCtClass);
00100                 generatedCtClass.setSuperclass(pool.get(Object.class.getName()));
00101             } else {
00102                 generatedCtClass.setSuperclass(superCtClass);
00103             }
00104             generatedCtClass.addInterface(pool.get(Serializable.class.getName()));
00105             generatedCtClass.addInterface(pool.get(StubObject.class.getName()));
00106 
00107             createStubObjectMethods(generatedCtClass);
00108 
00109             
00110             CtField methodsField = new CtField(pool.get(
00111                         "java.lang.reflect.Method[]"), "overridenMethods",
00112                     generatedCtClass);
00113             
00114             methodsField.setModifiers(Modifier.STATIC);
00115             generatedCtClass.addField(methodsField);
00116 
00117             
00118             CtField genericTypesMappingField = new CtField(pool.get(
00119             "java.util.Map"), "genericTypesMapping",
00120             generatedCtClass);
00121 
00122             genericTypesMappingField.setModifiers(Modifier.STATIC);
00123             generatedCtClass.addField(genericTypesMappingField);
00124 
00125             //   This map is used for keeping track of the method signatures / methods that are to be reified
00126             java.util.Map<String, CtMethod> temp = new HashMap<String, CtMethod>();
00127 
00128             // Recursively calls getDeclaredMethods () on the target type
00129             // and each of its superclasses, all the way up to java.lang.Object
00130             // We have to be careful and only take into account overriden methods once
00131             CtClass currentCtClass = superCtClass;
00132 
00133             CtClass[] params;
00134             Object exists;
00135 
00136             List<String> classesIndexer = new Vector<String>();
00137             
00138             classesIndexer.add(superCtClass.getName());
00139 
00140             // If the target type is an interface, the only thing we have to do is to
00141             // get the list of all its public reifiedMethods.
00142             if (superCtClass.isInterface()) {
00143                 CtMethod[] allPublicMethods = superCtClass.getMethods();
00144                 for (int i = 0; i < allPublicMethods.length; i++) {
00145                     String key = "";
00146                     key = key + allPublicMethods[i].getName();
00147                     params = allPublicMethods[i].getParameterTypes();
00148                     for (int k = 0; k < params.length; k++) {
00149                         key = key + params[k].getName();
00150                     }
00151                     temp.put(key, allPublicMethods[i]);
00152                 }
00153                 classesIndexer.add("java.lang.Object");
00154             } else // If the target type is an actual class, we climb up the tree
00155              {
00156                 do {
00157                     if (!classesIndexer.contains(currentCtClass.getName())) {
00158                         classesIndexer.add(currentCtClass.getName());
00159                     }
00160 
00161                     // The declared reifiedMethods for the current class
00162                     CtMethod[] declaredCtMethods = currentCtClass.getDeclaredMethods();
00163                     
00164                     // For each method declared in this class
00165                     for (int i = 0; i < declaredCtMethods.length; i++) {
00166                         CtMethod currentMethod = declaredCtMethods[i];
00167 
00168                         // Build a key with the simple name of the method
00169                         // and the names of its parameters in the right order
00170                         String key = "";
00171                         key = key + currentMethod.getName();
00172                         params = currentMethod.getParameterTypes();
00173                         for (int k = 0; k < params.length; k++) {
00174                             key = key + params[k].getName();
00175                         }
00176                         // Tests if we already have met this method in a subclass
00177                         exists = temp.get(key);
00178                         if (exists == null) {
00179                             // The only method we ABSOLUTELY want to be called directly
00180                             // on the stub (and thus not reified) is
00181                             // the protected void finalize () throws Throwable
00182                             if ((key.equals("finalize")) &&
00183                                     (params.length == 0)) {
00184                                 // Do nothing, simply avoid adding this method to the list
00185                             } else {
00186                                 // If not, adds this method to the Vector that
00187                                 // holds all the reifiedMethods for this class
00188                                 //                                tempVector.addElement(currentMethod);
00189                                 temp.put(key, currentMethod);
00190                             }
00191                         } else {
00192                             // We already know this method because it is overriden
00193                             // in a subclass. Then do nothing
00194                         }
00195                     }
00196                     currentCtClass = currentCtClass.getSuperclass();
00197                 } while (currentCtClass != null); // Continue until we ask for the superclass of java.lang.Object
00198             }
00199 
00200             // now get the methods from implemented interfaces
00201             List<CtClass> superInterfaces = new Vector<CtClass>();
00202             addSuperInterfaces(superCtClass, superInterfaces);
00203             
00204             CtClass[] implementedInterfacesTable = (superInterfaces.toArray(new CtClass[superInterfaces.size()]));
00205             for (int i=0; i< implementedInterfacesTable.length; i++) {
00206                 
00207             }
00208             for (int itfsIndex = 0;
00209                     itfsIndex < implementedInterfacesTable.length;
00210                     itfsIndex++) {
00211                 if (!classesIndexer.contains(
00212                             implementedInterfacesTable[itfsIndex].getName())) {
00213                     classesIndexer.add(implementedInterfacesTable[itfsIndex].getName());
00214                 }
00215 
00216                 //              The declared methods for the current interface
00217                 CtMethod[] declaredMethods = implementedInterfacesTable[itfsIndex].getDeclaredMethods();
00218                 
00219 
00220                 // For each method declared in this class
00221                 for (int i = 0; i < declaredMethods.length; i++) {
00222                     CtMethod currentMethod = declaredMethods[i];
00223 
00224                     // Build a key with the simple name of the method
00225                     // and the names of its parameters in the right order
00226                     String key = "";
00227                     key = key + currentMethod.getName();
00228                     params = currentMethod.getParameterTypes();
00229                     for (int k = 0; k < params.length; k++) {
00230                         key = key + params[k].getName();
00231                     }
00232 
00233                     // replace with current one, because this gives the actual declaring Class of this method
00234                     temp.put(key, currentMethod);
00235                 }
00236             }
00237 
00238             reifiedMethodsWithoutGenerics = (CtMethod[]) (temp.values().toArray(new CtMethod[temp.size()]));
00239 
00240             // Determines which reifiedMethods are valid for reification
00241             // It is the responsibility of method checkMethod 
00242             // to decide if a method is valid for reification or not
00243             Vector<CtMethod> v = new Vector<CtMethod>();
00244             int initialNumberOfMethods = reifiedMethodsWithoutGenerics.length;
00245 
00246             for (int i = 0; i < initialNumberOfMethods; i++) {
00247                 if (checkMethod(reifiedMethodsWithoutGenerics[i])) {
00248                     v.addElement(reifiedMethodsWithoutGenerics[i]);
00249                 }
00250             }
00251             CtMethod[] validMethods = new CtMethod[v.size()];
00252             v.copyInto(validMethods);
00253 
00254             // Installs the list of valid reifiedMethods as an instance variable of this object
00255             reifiedMethodsWithoutGenerics = validMethods;
00256             
00257             
00258             Class realSuperClass = Class.forName(className);
00259             TypeVariable<GenericDeclaration>[] tv = realSuperClass.getTypeParameters();
00260             Map<TypeVariable, Class> genericTypesMapping = new HashMap<TypeVariable, Class>();
00261             if (genericParameters.length!=0) {
00262                 // only deal with cases where parameters have been specified
00263                 for (int i = 0; i < tv.length; i++) {
00264                         genericTypesMapping.put(tv[i], genericParameters[i]);
00265                 }
00266             }
00267 
00268             // create static block with method initializations
00269             createStaticInitializer(generatedCtClass, reifiedMethodsWithoutGenerics,
00270                 classesIndexer, className, genericParameters);
00271 
00272             createReifiedMethods(generatedCtClass, reifiedMethodsWithoutGenerics, superCtClass.isInterface());
00273 //                        generatedClass.writeFile();
00274 //                        System.out.println("[JAVASSIST] generated class : " + generatedClass.getName());
00275 
00276             // detach to fix  "frozen class" errors encountered in some large scale deployments 
00277             byte[] bytecode = generatedCtClass.toBytecode();
00278             generatedCtClass.detach();
00279             return bytecode;
00280         } catch (Exception e) {
00281             e.printStackTrace();
00282             throw new NoClassDefFoundError("Cannot generated stub for class " +
00283                 className + " with javassist : " + e.getMessage());
00284         }
00285     }
00286     
00287     
00288 
00295      private static void createReifiedMethods(CtClass generatedClass,
00296                 CtMethod[] reifiedMethods, boolean stubOnInterface)
00297                 throws NotFoundException, CannotCompileException {
00298                 for (int i = 0; i < reifiedMethods.length; i++) {
00299                     CtClass[] paramTypes = reifiedMethods[i].getParameterTypes();
00300                     String body = ("{\nObject[] parameters = new Object[" +
00301                         paramTypes.length + "];\n");
00302                     for (int j = 0; j < paramTypes.length; j++) {
00303                         if (paramTypes[j].isPrimitive()) {
00304                             body += ("  parameters[" + j + "]=" +
00305                             wrapPrimitiveParameter(paramTypes[j], "$" + (j + 1)) +
00306                             ";\n");
00307                         } else {
00308                             body += ("  parameters[" + j + "]=$" + (j + 1) + ";\n");
00309                         }
00310                     }
00311                     CtClass returnType = reifiedMethods[i].getReturnType();
00312                     String postWrap = null;
00313                     String preWrap = null;
00314 
00315                     if (returnType != CtClass.voidType) {
00316                         if (!returnType.isPrimitive()) {
00317                             preWrap = "(" + returnType.getName() + ")";
00318                         } else {
00319                             //boolean, byte, char, short, int, long, float, double
00320                             if (returnType.equals(CtClass.booleanType)) {
00321                                 preWrap = "((Boolean)";
00322                                 postWrap = ").booleanValue()";
00323                             }
00324                             if (returnType.equals(CtClass.byteType)) {
00325                                 preWrap = "((Byte)";
00326                                 postWrap = ").byteValue()";
00327                             }
00328                             if (returnType.equals(CtClass.charType)) {
00329                                 preWrap = "((Character)";
00330                                 postWrap = ").charValue()";
00331                             }
00332                             if (returnType.equals(CtClass.shortType)) {
00333                                 preWrap = "((Short)";
00334                                 postWrap = ").shortValue()";
00335                             }
00336                             if (returnType.equals(CtClass.intType)) {
00337                                 preWrap = "((Integer)";
00338                                 postWrap = ").intValue()";
00339                             }
00340                             if (returnType.equals(CtClass.longType)) {
00341                                 preWrap = "((Long)";
00342                                 postWrap = ").longValue()";
00343                             }
00344                             if (returnType.equals(CtClass.floatType)) {
00345                                 preWrap = "((Float)";
00346                                 postWrap = ").floatValue()";
00347                             }
00348                             if (returnType.equals(CtClass.doubleType)) {
00349                                 preWrap = "((Double)";
00350                                 postWrap = ").doubleValue()";
00351                             }
00352                         }
00353                         body += "return ";
00354                         if (preWrap != null) {
00355                             body += preWrap;
00356                         }
00357                     }
00358                     body += ("myProxy.reify(org.objectweb.proactive.core.mop.MethodCall.getMethodCall(" +
00359                             "(java.lang.reflect.Method)overridenMethods[" + i + "]" +
00360                             ", parameters, genericTypesMapping))");
00361                     
00362                     
00363 //                  body += ("myProxy.reify(org.objectweb.proactive.core.mop.MethodCall.getMethodCall(" +
00364 //                  "(java.lang.reflect.Method)overridenMethods[" + i + "]" +
00365 //                  ", parameters))");
00366                     if (postWrap != null) {
00367                         body += postWrap;
00368                     }
00369                     body += ";";
00370                     
00371                     // the following is for inserting conditional statement for method code executing
00372                     // within or outside the construction of the object
00373                     if (!stubOnInterface) {
00374                         String preReificationCode = "if (outsideOfConstructor) ";
00375                         // outside of constructor : object is already constructed
00376                         
00377                         String postReificationCode = "\n} else {\n";
00378                         // if inside constructor (i.e. in a method called by a
00379                         // constructor from a super class)
00380                         if (!reifiedMethods[i].getReturnType().equals(CtClass.voidType)) {
00381                             postReificationCode += "return ";
00382                         }
00383                         postReificationCode += "super." + reifiedMethods[i].getName() + "(";
00384                         for (int j = 0; j < paramTypes.length; j++) {
00385                             postReificationCode += "$" + (j + 1)
00386                                     + (((j + 1) < paramTypes.length) ? "," : "");
00387                         }
00388 
00389                         postReificationCode += ");";
00390                         body = preReificationCode + body + postReificationCode;
00391                     }
00392                     body += "\n}";
00393 //                  System.out.println("method : " + reifiedMethods[i].getName()
00394 //                          + " : \n" + body);
00395                     CtMethod methodToGenerate = null;
00396                     try {
00397                         methodToGenerate = CtNewMethod.make(reifiedMethods[i].getReturnType(),
00398                                 reifiedMethods[i].getName(),
00399                                 reifiedMethods[i].getParameterTypes(),
00400                                 reifiedMethods[i].getExceptionTypes(), body, generatedClass);
00401                     } catch (RuntimeException e) {
00402                         e.printStackTrace();
00403                     }
00404                     generatedClass.addMethod(methodToGenerate);
00405                 }
00406             }
00414     public static void createStaticInitializer(CtClass generatedClass,
00415         CtMethod[] reifiedMethods, List<String> classesIndexer, String superClassName, Class[] genericParameters)
00416         throws CannotCompileException, NotFoundException {
00417         if (genericParameters == null) {
00418                 genericParameters = new Class[0];
00419         }
00420         CtConstructor classInitializer = generatedClass.makeClassInitializer();
00421 
00422         String classInitializerBody = "{\n";
00423         classInitializerBody+="Class[] genericParameters = new Class[" + genericParameters.length+"];\n";
00424         for (int i=0; i<genericParameters.length; i++) {
00425                 classInitializerBody+="genericParameters["+i+"] = Class.forName(\""+genericParameters[i].getName()+"\");\n";
00426         }
00427         classInitializerBody+="Class realSuperClass = Class.forName(\"" + superClassName+"\");\n";
00428         classInitializerBody+="java.lang.reflect.TypeVariable[] tv = realSuperClass.getTypeParameters();\n";
00429         classInitializerBody+="genericTypesMapping = new java.util.HashMap();\n";
00430         
00431         // generic types mapping only occurs when parameters are specified
00432         if (genericParameters.length!=0) {
00433                 classInitializerBody+="for (int i = 0; i < tv.length; i++) {\n";
00434                 classInitializerBody+="     genericTypesMapping.put(tv[i], genericParameters[i]);\n";
00435                 classInitializerBody+="}\n";
00436         }
00437 
00438         classInitializerBody += ("overridenMethods = new java.lang.reflect.Method[" +
00439         reifiedMethods.length + "];\n");
00440         classInitializerBody += ("Class classes[] = new Class[" +
00441         (classesIndexer.size()) + "];\n");
00442         classInitializerBody += "Class[] temp;\n";
00443 
00444         int methodsIndex = 0;
00445         Iterator<String> it = classesIndexer.iterator();
00446         int index = 0;
00447         while (it.hasNext()) {
00448             classInitializerBody += ("classes[" + index +
00449             "] = Class.forName(\"" + it.next() + "\");\n");
00450             index++;
00451         }
00452         for (int i = 0; i < reifiedMethods.length; i++) {
00453             CtClass[] paramTypes = reifiedMethods[i].getParameterTypes();
00454             classInitializerBody += ("temp = new Class[" + paramTypes.length +
00455             "];\n");
00456             for (int n = 0; n < paramTypes.length; n++) {
00457                 if (paramTypes[n].isPrimitive()) {
00458                     classInitializerBody += ("temp[" + n + "] = " +
00459                     getClassTypeInitializer(paramTypes[n], false) + ";\n");
00460                 } else {
00461                     classInitializerBody += ("temp[" + n +
00462                     "] = Class.forName(\"" +
00463                     getClassTypeInitializer(paramTypes[n], false) + "\");\n");
00464                 }
00465             }
00466             classInitializerBody += ("overridenMethods[" + (methodsIndex) +
00467             "] = classes[" +
00468             classesIndexer.indexOf(reifiedMethods[i].getDeclaringClass()
00469                                                     .getName()) +
00470             "].getDeclaredMethod(\"" + reifiedMethods[i].getName() +
00471             "\", temp);\n");
00472             methodsIndex++;
00473         }
00474 
00475         classInitializerBody += "\n}";
00476 //        System.out.println(classInitializerBody);
00477         classInitializer.setBody(classInitializerBody);
00478     }
00479 
00485     public static void createStubObjectMethods(CtClass generatedClass)
00486         throws CannotCompileException, NotFoundException {
00487         CtField proxyField = new CtField(ClassPool.getDefault().get(Proxy.class.getName()),
00488                 "myProxy", generatedClass);
00489         generatedClass.addField(proxyField);
00490         proxyGetter = CtNewMethod.getter("getProxy", proxyField);
00491         generatedClass.addMethod(proxyGetter);
00492         proxySetter = CtNewMethod.setter("setProxy", proxyField);
00493         generatedClass.addMethod(proxySetter);
00494     }
00495 
00496     private static String getClassTypeInitializer(CtClass param,
00497         boolean elementInArray) throws NotFoundException {
00498         if (param.isArray()) {
00499             return "[" +
00500             getClassTypeInitializer(param.getComponentType(), true);
00501         } else if (param.equals(CtClass.byteType)) {
00502             return elementInArray ? "B" : "Byte.TYPE";
00503         } else if (param.equals(CtClass.charType)) {
00504             return elementInArray ? "C" : "Character.TYPE";
00505         } else if (param.equals(CtClass.doubleType)) {
00506             return elementInArray ? "D" : "Double.TYPE";
00507         } else if (param.equals(CtClass.floatType)) {
00508             return elementInArray ? "F" : "Float.TYPE";
00509         } else if (param.equals(CtClass.intType)) {
00510             return elementInArray ? "I" : "Integer.TYPE";
00511         } else if (param.equals(CtClass.longType)) {
00512             return elementInArray ? "L" : "Long.TYPE";
00513         } else if (param.equals(CtClass.shortType)) {
00514             return elementInArray ? "S" : "Short.TYPE";
00515         } else if (param.equals(CtClass.booleanType)) {
00516             return elementInArray ? "Z" : "Boolean.TYPE";
00517         } else if (param.equals(CtClass.voidType)) {
00518             return elementInArray ? "V" : "Void.TYPE";
00519         } else {
00520             return elementInArray ? ("L" + param.getName() + ";")
00521                                   : (param.getName());
00522         }
00523     }
00524 
00525     public static String wrapPrimitiveParameter(CtClass paramType,
00526         String paramString) {
00527         if (CtClass.booleanType.equals(paramType)) {
00528             return "new Boolean(" + paramString + ")";
00529         }
00530         if (CtClass.byteType.equals(paramType)) {
00531             return "new Byte(" + paramString + ")";
00532         }
00533         if (CtClass.charType.equals(paramType)) {
00534             return "new Character(" + paramString + ")";
00535         }
00536         if (CtClass.doubleType.equals(paramType)) {
00537             return "new Double(" + paramString + ")";
00538         }
00539         if (CtClass.floatType.equals(paramType)) {
00540             return "new Float(" + paramString + ")";
00541         }
00542         if (CtClass.intType.equals(paramType)) {
00543             return "new Integer(" + paramString + ")";
00544         }
00545         if (CtClass.longType.equals(paramType)) {
00546             return "new Long(" + paramString + ")";
00547         }
00548         if (CtClass.shortType.equals(paramType)) {
00549             return "new Short(" + paramString + ")";
00550         }
00551 
00552         // that should not happen
00553         return null;
00554     }
00555 
00556     static public boolean checkMethod(CtMethod met) throws NotFoundException {
00557         int modifiers = met.getModifiers();
00558 
00559         // Final reifiedMethods cannot be reified since we cannot redefine them
00560         // in a subclass
00561         if (Modifier.isFinal(modifiers)) {
00562             return false;
00563         }
00564 
00565         // Static reifiedMethods cannot be reified since they are not 'virtual'
00566         if (Modifier.isStatic(modifiers)) {
00567             return false;
00568         }
00569         if (!(Modifier.isPublic(modifiers))) {
00570             return false;
00571         }
00572 
00573         // If method is finalize (), don't reify it
00574         if ((met.getName().equals("finalize")) &&
00575                 (met.getParameterTypes().length == 0)) {
00576             return false;
00577         }
00578         
00579         if ((met.getSignature().equals(proxyGetter.getSignature()) || met.getSignature().equals(proxySetter.getSignature()))) {
00580             return false;
00581         }
00582 
00583         return true;
00584     }
00585     
00586     private static void addSuperInterfaces(CtClass cl, List<CtClass> superItfs) throws NotFoundException {
00587         if (!cl.isInterface() && !Modifier.isAbstract(cl.getModifiers())) {
00588                 // inspect interfaces AND abstract classes
00589             return;
00590         }
00591         CtClass[] super_interfaces = cl.getInterfaces();
00592         for (int i = 0; i < super_interfaces.length; i++) {
00593             superItfs.add(super_interfaces[i]);
00594             addSuperInterfaces(super_interfaces[i], superItfs);
00595         }
00596     }
00597     
00598 }

Generated on Mon Jan 22 15:16:08 2007 for ProActive by  doxygen 1.5.1