001    /*
002      Copyright (C) 2002-2003 Fabrice Legond-Aubry, Renaud Pawlak, 
003      Lionel Seinturier, Laurent Martelli
004    
005      This program is free software; you can redistribute it and/or modify
006      it under the terms of the GNU Lesser General Public License as
007      published by the Free Software Foundation; either version 2 of the
008      License, or (at your option) any later version.
009    
010      This program is distributed in the hope that it will be useful, but
011      WITHOUT ANY WARRANTY; without even the implied warranty of
012      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013      Lesser General Public License for more details.
014    
015      You should have received a copy of the GNU Lesser General Public
016      License along with this program; if not, write to the Free Software
017      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
018      USA */
019    
020    package org.objectweb.jac.core.translators;
021    
022    import java.lang.reflect.Modifier;
023    import java.util.Arrays;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.LinkedList;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Vector;
031    import org.apache.bcel.Constants;
032    import org.apache.bcel.Repository;
033    import org.apache.bcel.classfile.*;
034    import org.apache.bcel.generic.*;
035    import org.apache.bcel.generic.BranchInstruction;
036    import org.apache.log4j.Logger;
037    import org.objectweb.jac.core.JacLoader;
038    import org.objectweb.jac.core.JacPropLoader;
039    import org.objectweb.jac.core.WrappeeTranslator;
040    import org.objectweb.jac.core.rtti.InvokeInfo;
041    import org.objectweb.jac.core.rtti.LoadtimeRTTI;
042    import org.objectweb.jac.util.ExtArrays;
043    import org.objectweb.jac.util.Stack;
044    
045    public  class WrappeeTranslator_BCEL implements WrappeeTranslator {
046        static Logger logger = Logger.getLogger("translator");
047        static Logger loggerRtti = Logger.getLogger("rtti.detect");
048        static Logger loggerBytecode = Logger.getLogger("translator.bytecode");
049    
050        LoadtimeRTTI rtti;
051    
052        /**
053         * Translator initializator. */
054    
055        public WrappeeTranslator_BCEL(LoadtimeRTTI rtti) {
056            this.rtti = rtti;
057        }
058    
059        private static final String nextWrapper_signature = 
060        "(Lorg/objectweb/jac/core/Interaction;)Ljava/lang/Object;";
061    
062        private static final String newInteraction_signature = 
063        "(Lorg/objectweb/jac/core/WrappingChain;Lorg/objectweb/jac/core/Wrappee;"+
064        "Lorg/objectweb/jac/core/rtti/AbstractMethodItem;"+
065        "[Ljava/lang/Object;)V";
066    
067        private static final String getWrappingChain_signature = 
068        "(Lorg/objectweb/jac/core/Wrappee;Lorg/objectweb/jac/core/rtti/AbstractMethodItem;)Lorg/objectweb/jac/core/WrappingChain;";
069    
070        private String primitiveTypeName(Type t)
071        {
072            if (t==Type.BOOLEAN)
073                return "boolean";
074            if (t==Type.BYTE)
075                return "byte";
076            if (t==Type.INT)
077                return "int";
078            if (t==Type.LONG)
079                return "long";
080            if (t==Type.SHORT)
081                return "short";
082            if (t==Type.FLOAT)
083                return "float";
084            if (t==Type.CHAR)
085                return "char";
086            if (t==Type.DOUBLE)
087                return "double";
088            return null;
089        }
090    
091        private String primitiveTypeAsObject(Type t)
092        {
093            if (t==Type.BOOLEAN)
094                return "java.lang.Boolean";
095            if (t==Type.BYTE)
096                return "java.lang.Byte";
097            if (t==Type.INT)
098                return "java.lang.Integer";
099            if (t==Type.LONG)
100                return "java.lang.Long";
101            if (t==Type.SHORT)
102                return "java.lang.Short";
103            if (t==Type.FLOAT)
104                return "java.lang.Float";
105            if (t==Type.CHAR)
106                return "java.lang.Character";
107            if (t==Type.DOUBLE)
108                return "java.lang.Double";
109            return null;
110        }
111       
112        private void generateStubMethod(ClassGen classGen, 
113                                        ConstantPoolGen constPool,
114                                        Method method, String gSNewName, 
115                                        int staticFieldIndex,
116                                        List staticFieldIndexes,
117                                        int wrappingChainIndex,
118                                        List wrappingChainIndexes, 
119                                        InstructionList callSuper)
120        {
121            logger.debug("Generating stub method "+
122                         method.getName()+method.getSignature());
123            InstructionList il = new InstructionList();
124            InstructionFactory ifactory = new InstructionFactory(classGen);
125            //create the stub method
126    
127            Type[] argumentTypes = Type.getArgumentTypes(method.getSignature());
128            Type returnType = Type.getReturnType(method.getSignature());
129            MethodGen stubMethod = 
130                new MethodGen(
131                    method.getAccessFlags(),
132                    returnType,argumentTypes,
133                    null,
134                    method.getName(),classGen.getClassName(), 
135                    il,constPool);
136            int lineNumber = 0;
137            // generate the super call when its a constructor
138            if (stubMethod.getName().equals("<init>")) {
139                logger.debug("Generating stub method "+
140                             classGen.getClassName()+"."+
141                             method.getName()+method.getSignature());
142                if (callSuper!=null) {
143                    logger.debug("   insert call to super");
144                    il.append(callSuper);
145                } else {
146                    logger.debug("   insert super()");
147                    il.append(new ALOAD(0));
148                    il.append(
149                        ifactory.createInvoke(classGen.getSuperclassName(), "<init>", 
150                                              Type.VOID, 
151                                              emptyTypeArray, 
152                                              Constants.INVOKESPECIAL));
153                }
154                // initialize the wrapping chains
155                for(int i=0;i<wrappingChainIndexes.size();i++) {
156                    if(wrappingChainIndexes.get(i)==null) continue;
157                    il.append(InstructionFactory.createThis());
158                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
159                    il.append(InstructionFactory.createThis());
160                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
161                    il.append(new GETSTATIC(
162                        ((Integer)staticFieldIndexes.get(i)).intValue()));
163                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
164                    il.append(ifactory.createInvoke(
165                        "org.objectweb.jac.core.Wrapping", "getWrappingChain", 
166                        Type.getReturnType(getWrappingChain_signature), 
167                        Type.getArgumentTypes(getWrappingChain_signature), 
168                        Constants.INVOKESTATIC));
169                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
170                    WCIndex chainIndex = (WCIndex)wrappingChainIndexes.get(i);
171                    if (chainIndex.isStatic) {
172                        il.append(new PUTSTATIC(chainIndex.index));
173                    } else {
174                        il.append(new PUTFIELD(chainIndex.index));
175                    }
176                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
177                }
178            }      
179    
180            ///////////////////////////////////
181            // LOOK AT THIS ! NEED TEST
182            ///////////////////////////////////////
183            stubMethod.removeExceptionHandlers();
184    
185            // create an Interaction object
186            il.append(ifactory.createNew("org.objectweb.jac.core.Interaction"));
187            stubMethod.addLineNumber(il.getEnd(),il.size()-1);
188            il.append(new DUP());
189            stubMethod.addLineNumber(il.getEnd(),il.size()-1);
190    
191            // get the wrapping chain
192            if (method.isStatic()) {
193                il.append(new GETSTATIC(wrappingChainIndex));
194                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
195            } else {
196                il.append(InstructionFactory.createThis());
197                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
198                il.append(new GETFIELD(wrappingChainIndex));
199                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
200            }
201          
202            // push the ref on this object in case of a non-static method on stack 
203            if( !stubMethod.isStatic() ) {
204                il.append(InstructionFactory.createThis());
205                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
206            } else {
207                il.append(new ACONST_NULL());
208                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
209            }
210    
211            // push the static field containing the AbstractMethodItem on the stack
212            il.append(new GETSTATIC(staticFieldIndex));
213            stubMethod.addLineNumber(il.getEnd(),il.size()-1);
214    
215            //create an array of Objects that are the parameters for the
216            //original method
217            if (argumentTypes.length==0) {
218                il.append(
219                    ifactory.createGetStatic(
220                        "org.objectweb.jac.core.Wrapping",
221                        "emptyArray",
222                        Type.getType("[Ljava.lang.Object;")));
223                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
224            } else {
225                il.append(new PUSH(constPool, argumentTypes.length));
226                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
227                //create a array and put its ref on the stack
228                il.append((Instruction)ifactory.createNewArray(Type.OBJECT,(short)1));
229                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
230                int j = (stubMethod.isStatic())?0:1; // index of argument on the stack
231                for (int i=0; i<argumentTypes.length; i++)
232                {
233                    //duplicate the ref on the array to keep it for next operation
234                    il.append(InstructionFactory.createDup(1));
235                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
236                    //get the index [in the array] in which we will store the ref 
237                    il.append(new PUSH(constPool, i));
238                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
239                    // is the parameter an object ?
240                    if (!Utils.isPrimitive(argumentTypes[i])) {
241                        //get the ref of the j st parameter of the local function
242                        il.append(InstructionFactory.createLoad(Type.OBJECT,j));
243                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
244                        //effectively store the ref in the array
245                        il.append(InstructionFactory.createArrayStore(Type.OBJECT));
246                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
247                        //increment the counter for the next object
248                        j++;
249                    } else {
250                        //create a new object similar to the primitive type
251                        String objectType = primitiveTypeAsObject(argumentTypes[i]);
252                        il.append (ifactory.createNew(objectType));
253                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
254                        //call the constructor of the new object with the primitive value
255                        il.append(InstructionFactory.createDup(1));
256                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
257                        il.append(InstructionFactory.createLoad(argumentTypes[i],j));
258                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
259                        il.append(ifactory.createInvoke(objectType, "<init>", Type.VOID, 
260                                                        new Type[] {argumentTypes[i]}, 
261                                                        Constants.INVOKESPECIAL));
262                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
263                        //store the new object in the array
264                        il.append(InstructionFactory.createArrayStore(Type.OBJECT));
265                        stubMethod.addLineNumber(il.getEnd(),il.size()-1);
266                        j++;
267                        if (argumentTypes[i]==Type.LONG ||
268                            argumentTypes[i]==Type.DOUBLE)
269                            // long and double take 2 slots on the stack
270                            j++;
271                    }
272                
273                }
274            }
275    
276            // <init> the Interaction
277            il.append(
278                ifactory.createInvoke(
279                    "org.objectweb.jac.core.Interaction", "<init>", 
280                    Type.getReturnType(newInteraction_signature), 
281                    Type.getArgumentTypes(newInteraction_signature), 
282                    Constants.INVOKESPECIAL));      
283            stubMethod.addLineNumber(il.getEnd(),il.size()-1);
284    
285            //make invocation of "nextWrapper"
286            if (method.isStatic()||method.getName().equals("<init>")) {
287                il.append(
288                    ifactory.createInvoke(
289                        "org.objectweb.jac.core.Wrapping", "nextWrapper", 
290                        Type.getReturnType(nextWrapper_signature), 
291                        Type.getArgumentTypes(nextWrapper_signature), 
292                        Constants.INVOKESTATIC));
293                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
294            } else {
295                il.append(
296                    ifactory.createInvoke(
297                        "org.objectweb.jac.core.Wrapping", "methodNextWrapper", 
298                        Type.getReturnType(nextWrapper_signature), 
299                        Type.getArgumentTypes(nextWrapper_signature), 
300                        Constants.INVOKESTATIC));
301                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
302            }
303    
304            //parse the return value if it is a primitive one
305            if (Utils.isPrimitive(returnType)) 
306            {
307                //ouch ! the return primitive types are also wrapped.
308                //test the cast for the object returned ....
309                //is this really usefull ?
310                                    
311                il.append(
312                    ifactory.createCheckCast(
313                        (ReferenceType)Type.getReturnType(
314                            "()L"+primitiveTypeAsObject(returnType).replace('.','/')+";")));
315                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
316                                    
317                //get the value wrapped in the object
318                il.append(
319                    ifactory.createInvoke(
320                        primitiveTypeAsObject(returnType), 
321                        primitiveTypeName(returnType)+"Value",
322                        stubMethod.getReturnType(), emptyTypeArray,
323                        Constants.INVOKEVIRTUAL));
324                stubMethod.addLineNumber(il.getEnd(),il.size()-1);
325            }
326            // else make a simple checkcast (avoid the checkcast on the VOID type)
327            //is this also really useful ?
328            else 
329                if (stubMethod.getReturnType()!=Type.VOID) {
330                    il.append (
331                        ifactory.createCheckCast((ReferenceType)returnType));
332                    stubMethod.addLineNumber(il.getEnd(),il.size()-1);
333                }
334            // finally return ! HOURRA !
335            il.append (InstructionFactory.createReturn(returnType));
336            stubMethod.addLineNumber(il.getEnd(),il.size()-1);
337            // compile all this stuff, generate the method
338            stubMethod.setMaxLocals();
339            stubMethod.setMaxStack();
340            //System.out.println ("il=\n"+il);
341            classGen.addMethod(stubMethod.getMethod());
342        }
343    
344        /**
345         * Generate a default constructor --which takes no argument-- which
346         * just calls super().
347         * 
348         * !!! IT SHOULD ALSO INITIALIZE FIELDS !!!
349         * We could use the "this" method generated by jikes-1.18
350         */
351        private void generateDefaultConstructor(ClassGen classGen)
352        {
353            logger.debug("class "+classGen.getClassName()+
354                      " ==> Generating default constructor");
355            ConstantPoolGen constPool = classGen.getConstantPool();
356            InstructionList instructions = new InstructionList();
357            InstructionFactory ifactory = new InstructionFactory(classGen);
358    
359            instructions.append(new ALOAD(0));
360            instructions.append(ifactory.createInvoke(classGen.getSuperclassName(), 
361                                                      "<init>", 
362                                                      Type.VOID, 
363                                                      emptyTypeArray, 
364                                                      Constants.INVOKESPECIAL));
365          
366            instructions.append(new RETURN());
367    
368            MethodGen constructor = new MethodGen(Modifier.PUBLIC, 
369                                                  Type.VOID,
370                                                  new Type[] {}, new String[] {},
371                                                  "<init>", classGen.getClassName(), 
372                                                  instructions, constPool);
373    
374            constructor.setMaxLocals();
375            constructor.setMaxStack();
376            classGen.addMethod(constructor.getMethod());
377        }
378    
379        /**
380         * Remove call to super()
381         * Replace collection attributes with the wrappable org.objectweb.jac.lib version
382         *
383         * @param classGen the class of the constructor
384         * @param origConstructor 
385         * @param constructor the constructor to translate
386         * @param callSuper store bytecodes that do the call to the super
387         * constructor in it 
388         * @param removeSuperCall wether to remove the call to super bytecodes
389         */
390        private void translateConstructor(ClassGen classGen, 
391                                          Method origConstructor,
392                                          MethodGen constructor,
393                                          InstructionList callSuper,
394                                          boolean removeSuperCall) {
395            logger.debug("constructor translation of "+
396                         constructor.getName()+constructor.getSignature());
397            InstructionList instructions = constructor.getInstructionList();
398            ConstantPoolGen constPool = classGen.getConstantPool();
399            InstructionFactory ifactory = new InstructionFactory(classGen);
400            instructions.setPositions();
401            int i=0;
402            // Represents the state of the JVM stack 
403            // (contains ThisPointer or DontCare)
404            boolean superRemoved = false;
405            VMStack stack = new VMStack(constPool,origConstructor.getCode(),
406                                        constructor.getArgumentTypes().length,false);
407            InstructionHandle first = null;
408            Iterator it = instructions.iterator();
409            while(it.hasNext()) {
410                InstructionHandle ih = (InstructionHandle)it.next();
411                InstructionHandle next = ih.getNext();
412                if (first==null)
413                    first = ih;
414                Instruction instruction = ih.getInstruction();
415                stack.preExecute(ih);
416                if (!superRemoved && !constructor.getName().equals("this")) {
417                    if (callSuper!=null) {
418                        if (instruction instanceof BranchInstruction) {
419                            callSuper.append((BranchInstruction)instruction);
420                        } else {
421                            callSuper.append(instruction);
422                        }
423                    }
424                    // remove call to super <init>
425                    if (instruction instanceof INVOKESPECIAL && 
426                        stack.getSubstance((INVOKESPECIAL)instruction)==VMStack.thisPointer) 
427                    {
428                        superRemoved = true;
429                        INVOKESPECIAL invoke = (INVOKESPECIAL)instruction;
430                        if (removeSuperCall) {
431                            if (!invoke.getClassName(constPool).equals(classGen.getClassName()))
432                            {
433                                try {
434                                    logger.debug("deleting call to super, callSuper = \n"+callSuper);
435                                    instructions.delete(first,ih);
436                                } catch (TargetLostException e) {
437                                    logger.debug("TargetLostException..., callSuper = \n"+callSuper);
438                                    InstructionHandle[] targets = e.getTargets();
439                                    for(int j=0; j<targets.length; j++) {
440                                        InstructionTargeter[] targeters = 
441                                            targets[j].getTargeters();
442                                        for(int k=0; k<targeters.length; k++)
443                                            targeters[k].updateTarget(targets[j], next);
444                                    }
445                                }
446                            } else {
447                                //if(classGen.containsMethod(
448                                //   invoke.getMethodName(constPool),
449                                //   invoke.getSignature(constPool)).isPublic())
450                                //{
451                                // if it calls another constructor of the same class,
452                                // replace it with a call to the renamed constructor
453                                ih.setInstruction(
454                                    ifactory.createInvoke(
455                                        classGen.getClassName(),
456                                        prefix+classGen.getClassName()
457                                        .substring(classGen.getClassName().lastIndexOf(".")+1),
458                                        Type.VOID,
459                                        invoke.getArgumentTypes(constPool),
460                                        Constants.INVOKEVIRTUAL
461                                    )
462                                );
463                                //}
464                            }
465                        }
466                    }
467                    stack.execute(instruction,ih);
468                } else if (JacPropLoader.translateFields(constructor.getClassName())) {
469    
470                    // Replace java.util. with org.objectweb.jac.lib.java.util
471                    // for collections
472    
473                    if (instruction instanceof PUTFIELD &&
474                        stack.peek(1) == VMStack.thisPointer &&
475                        stack.peek() instanceof VMStack.Instance &&
476                        isCollection(((VMStack.Instance)stack.peek()).type) ) {
477                        // this.putfield(...)
478                        PUTFIELD putfield = (PUTFIELD)instruction;
479                        if (!classGen.containsField(putfield.getFieldName(constPool)).isTransient()) {
480                            VMStack.Instance collection = (VMStack.Instance)stack.peek();
481                            /*
482                              System.out.println("collection: "+collection.type+"( new="+
483                              collection.newHandle+",init="+
484                              collection.initHandle+")");
485                            */
486                            collection.newHandle.setInstruction(
487                                ifactory.createNew("org.objectweb.jac.lib."+collection.type));
488                            collection.initHandle.setInstruction(
489                                ifactory.createInvoke(
490                                    "org.objectweb.jac.lib."+collection.type,
491                                    "<init>",
492                                    Type.VOID,
493                                    ((INVOKESPECIAL)collection.initHandle.getInstruction()).getArgumentTypes(constPool),
494                                    Constants.INVOKESPECIAL));
495                            logger.debug("Found collection field initialization: "+
496                                      putfield.getFieldName(constPool));
497                        }
498                    } else if (instruction instanceof INVOKESPECIAL) {
499                        INVOKESPECIAL invoke = (INVOKESPECIAL)instruction;
500                        Object substance = 
501                            stack.peek(VMStack.getConsumed(invoke,constPool)-1);
502                        if (substance instanceof VMStack.Instance) {
503                            logger.debug("Found collection <init> for "+substance);
504                            ((VMStack.Instance)substance).initHandle = ih;
505                        }
506                    } 
507                    stack.execute(instruction,ih);
508                }
509                i++;
510            }
511            logger.debug("callSuper = \n"+callSuper);
512            instructions.setPositions();
513            constructor.setInstructionList(instructions);
514            constructor.removeLineNumbers();
515        }
516    
517        /**
518         * Rename the method "xxx" in "_org_xxx". 
519         *
520         * <p>If the method is a constructor, it's translated.</p>
521         *
522         * @param classGen the class of the method to rename
523         * @param constPool 
524         * @param method the method to rename
525         * @param newProposedName new name for the translated method
526         * @param callSuper store bytecodes that do the call to the super
527         * constructor in this list
528         *
529         * @see #translateConstructor(ClassGen,Method,MethodGen,InstructionList)
530         */
531        private String renameMethod(ClassGen classGen, ConstantPoolGen constPool,
532                                    Method method, String newProposedName,
533                                    InstructionList callSuper)
534        {
535            logger.debug("Rename "+method.getName()+" -> "+newProposedName);
536            // handles constructors specific translations
537            if (method.getName().equals("<init>")) {
538                MethodGen newNamedMethod = 
539                    new MethodGen(method, classGen.getClassName(), constPool);
540                newNamedMethod.setName(newProposedName);
541                translateConstructor(classGen,method,newNamedMethod,callSuper,true);
542                //why the hell is all this stuff necessary ?!
543                newNamedMethod.removeLocalVariables();
544                //      newNamedMethod.setMaxLocals();
545                //      newNamedMethod.setMaxStack();
546                //move the original method into the new "_org_xxx" method
547                classGen.replaceMethod(method, newNamedMethod.getMethod());
548            } else {
549                Method newMethod = new Method(method);
550                newMethod.setNameIndex(constPool.addUtf8(newProposedName));
551                classGen.replaceMethod(method,newMethod);
552            }
553            return newProposedName;
554        }
555    
556        /**
557         * Generate RTTI information for a method.
558         *
559         * @param classGen the class of the method
560         * @param constPool the constant pool of the method
561         * @param method the method
562         * @param passive if true, do not perform any translation
563         */
564        Method fillRTTI(ClassGen classGen, ConstantPoolGen constPool, Method method,
565                        boolean passive) 
566        {
567            String className = classGen.getClassName(); 
568            String methodName = method.getName();
569            MethodGen methodGen = new MethodGen(method,className,constPool);
570            Iterator instructions = methodGen.getInstructionList().iterator();
571            String methodSign = null;
572            if (methodName.startsWith(prefix)) {
573                methodName = methodName.substring(prefix.length());
574            }
575            methodSign = className+"."+getMethodFullName(method);
576            VMStack stack = new VMStack(constPool,method.getCode(),
577                                        methodGen.getArgumentTypes().length,method.isStatic());
578            loggerRtti.debug("detecting RTTI for "+methodSign);
579            while (instructions.hasNext()) {
580                InstructionHandle ih=(InstructionHandle)instructions.next();
581                Instruction instruction = ih.getInstruction();
582                loggerBytecode.debug("offset: "+ih.getPosition());
583                stack.preExecute(ih);
584             
585                if (instruction instanceof PUTFIELD && 
586                    stack.peek(1)==VMStack.thisPointer) {
587                    // setters and modifiers
588                    PUTFIELD putfield = (PUTFIELD)instruction;
589                    String fieldName = putfield.getFieldName(constPool);
590                    if (!isSystemField(fieldName)) {
591                        if (stack.peek() instanceof VMStack.Argument) {
592                            loggerRtti.debug("  sets field "+fieldName);
593                            rtti.addltSetField(className,methodSign,fieldName);
594                            // TODO: check if the type is translated collection
595                        } 
596                        loggerRtti.debug("  modifies field "+fieldName);
597                        rtti.addltModifiedField(className,methodSign,fieldName);
598                    }
599                } else if (instruction instanceof PUTSTATIC && 
600                           ((PUTSTATIC)instruction).getClassName(constPool).equals(classGen.getClassName())) {
601                    // static setters and modifiers
602                    PUTSTATIC putfield = (PUTSTATIC)instruction;
603                    String fieldName = putfield.getFieldName(constPool);
604                    if (!isSystemField(fieldName)) {
605                        if (stack.peek() instanceof VMStack.Argument) {
606                            loggerRtti.debug(methodSign+" sets static field "+fieldName);
607                            rtti.addltSetField(className,methodSign,fieldName);
608                            // TODO: check if the type is translated collection
609                        } 
610                        loggerRtti.debug("  modifies static field "+fieldName);
611                        rtti.addltModifiedField(className,methodSign,fieldName);
612                    }
613                } else if (instruction instanceof ReturnInstruction && 
614                           !(instruction instanceof RETURN)) {
615                    if (stack.peek() instanceof VMStack.FieldValue) {
616                        // *the* getter
617                        VMStack.FieldValue fieldValue = (VMStack.FieldValue)stack.peek();
618                        if (!isSystemField(fieldValue.field)) {
619                            if (fieldValue.substance==VMStack.thisPointer) {
620                                loggerRtti.debug("  returns field "+fieldValue.field);
621                                rtti.addltReturnedField(className,methodSign,fieldValue.field);
622                            } else {
623                                loggerRtti.debug("  returns "+stack.peek());
624                                rtti.addltReturnedField(className,methodSign,null);
625                                rtti.setltIsGetter(className,methodSign,false);
626                            }
627                        }
628                    } else {
629                        loggerRtti.debug("  returns "+stack.peek());
630                        rtti.setltIsGetter(className,methodSign,false);               
631                    }
632                } else if (instruction instanceof GETFIELD &&
633                           stack.peek()==VMStack.thisPointer) {
634                    // getters
635                    String fieldName = ((GETFIELD)instruction).getFieldName(constPool);
636                    if (!isSystemField(fieldName)) {
637                        loggerRtti.debug("  accesses field "+fieldName);
638                        rtti.addltAccessedField(className,methodSign,fieldName);
639                    }
640                } else if (instruction instanceof GETSTATIC &&
641                           ((GETSTATIC)instruction).getClassName(constPool).equals(classGen.getClassName())) {
642                    // getters
643                    String fieldName = ((GETSTATIC)instruction).getFieldName(constPool);
644                    if (!isSystemField(fieldName)) {
645                        loggerRtti.debug("  accesses static field "+fieldName);
646                        rtti.addltAccessedField(className,methodSign,fieldName);
647                    }
648                } else if ((instruction instanceof INVOKEVIRTUAL || 
649                            instruction instanceof INVOKEINTERFACE) && 
650                           !className.startsWith("org.objectweb.jac.lib.java")) {
651                    // adders and removers
652                    InvokeInstruction invoke = (InvokeInstruction)instruction;
653                    String invokedClass = invoke.getClassName(constPool);
654                    String invokedMethodName = invoke.getMethodName(constPool);
655                    int numArgs = invoke.getArgumentTypes(constPool).length;
656                    Object substance = stack.invokedObject(invoke);
657                    loggerBytecode.info("substance="+substance);
658                    if (substance instanceof VMStack.FieldValue) {
659                        VMStack.FieldValue fieldValue = (VMStack.FieldValue)substance;
660                        loggerBytecode.info("detected INVOKE on field "+substance);
661                        ConstantFieldref fieldref = 
662                            (ConstantFieldref)constPool.getConstant(fieldValue.index);
663                        ConstantNameAndType nameAndType = 
664                            (ConstantNameAndType)constPool.getConstant(
665                                fieldref.getNameAndTypeIndex());
666                        String signature = 
667                            nameAndType.getSignature(constPool.getConstantPool());
668                        String fieldName = 
669                            nameAndType.getName(constPool.getConstantPool());
670                        loggerBytecode.debug("field signature: "+signature);
671                        loggerBytecode.debug("invoked class name: "+invokedClass);
672                        if (signature.startsWith("Lorg/objectweb/jac/lib/java") && 
673                            !invokedClass.startsWith("org.objectweb.jac.lib.java")) {
674                            loggerBytecode.info("FIXING INCOMPATIBLE TYPES "+
675                                      signature+" AND "+invokedClass+
676                                      " for "+nameAndType);
677                            loggerBytecode.info("  ==> "+
678                                      signature.substring(1,signature.length()-1));
679                            invoke.setIndex(
680                                constPool.addMethodref(
681                                    signature.substring(1,signature.length()-1),
682                                    invoke.getName(constPool),
683                                    invoke.getSignature(constPool)));
684                        }
685    
686                        // check that the field value belongs to "this"
687                        if (fieldValue.substance==VMStack.thisPointer && 
688                            (invoke.getClassType(constPool).isCastableTo(Type.getType(Collection.class)) ||
689                             invoke.getClassType(constPool).isCastableTo(Type.getType(Map.class)))) 
690                        {
691                            if (invokedMethodName.equals("add")) {
692                                if ((numArgs==1 && areArguments(stack,numArgs)) ||
693                                    (numArgs==2 && isArgument(stack,0))) {
694                                    loggerRtti.debug(methodSign+" is adder for "+
695                                              nameAndType.getName(constPool.getConstantPool()));
696                                    rtti.addltAddedCollection(className,methodSign,fieldName);
697                                } else {
698                                    loggerRtti.debug(methodSign+" is modifier for "+
699                                              nameAndType.getName(constPool.getConstantPool()));
700                                    rtti.addltModifiedCollection(className,methodSign,fieldName);
701                                }
702                                if (numArgs==2) {
703                                    VMStack.Argument arg = getArgument(stack,1);
704                                    int n;
705                                    if (arg!=null) {
706                                        n = arg.n - (method.isStatic() ? 0 : 1);
707                                        loggerRtti.debug("  has collectionIndexArgument "+n);
708                                        rtti.setCollectionIndexArgument(className,methodSign,n);
709                                    }
710                                    arg = getArgument(stack,0);
711                                    if (arg!=null) {
712                                        n = arg.n - (method.isStatic() ? 0 : 1);
713                                        loggerRtti.debug("  has collectionItemArgument "+n);
714                                        rtti.setCollectionItemArgument(className,methodSign,n);
715                                    }
716                                }
717                            } else if (invokedMethodName.equals("put")) {
718                                if (isArgument(stack,0)) {
719                                    loggerRtti.debug("  is putter for "+
720                                              nameAndType.getName(constPool.getConstantPool()));
721                                    rtti.addltAddedCollection(className,methodSign,fieldName);
722                                } else {
723                                    loggerRtti.debug("  is modifier for "+
724                                              nameAndType.getName(constPool.getConstantPool()));
725                                    rtti.addltModifiedCollection(className,methodSign,fieldName);
726                                }
727                            } else if (invokedMethodName.equals("remove")) {
728                                if (areArguments(stack,numArgs)) {
729                                    loggerRtti.debug("  is remover for "+
730                                              nameAndType.getName(constPool.getConstantPool()));
731                                    rtti.addltRemovedCollection(className,methodSign,fieldName);
732                                } else {
733                                    loggerRtti.debug("  is modifier for "+
734                                              nameAndType.getName(constPool.getConstantPool()));
735                                    rtti.addltModifiedCollection(className,methodSign,fieldName);
736                                }
737                            } else if (invokedMethodName.equals("set") ||
738                                       invokedMethodName.equals("clear") ||
739                                       invokedMethodName.equals("addAll") ||
740                                       invokedMethodName.equals("removeAll") ||
741                                       invokedMethodName.equals("retainAll")) {
742                                loggerRtti.debug("  is modifier for "+
743                                          nameAndType.getName(constPool.getConstantPool()));
744                                rtti.addltModifiedCollection(className,methodSign,fieldName);
745                                if (numArgs==2 && isArgument(stack,1)) {
746                                    loggerRtti.debug("  has collectionIndexArgument 0");
747                                    rtti.setCollectionIndexArgument(className,methodSign,0);
748                                }
749                            }
750                        }
751                    }
752    
753                    if (JacLoader.classIsToBeAdapted(invokedClass) && 
754                        !(invokedMethodName.equals(methodName) && 
755                          invokedClass.equals(className)))
756                        rtti.addInvokedMethod(className,methodSign,
757                                              new InvokeInfo(
758                                                  stack.getSubstance(invoke),
759                                                  invokedClass,invokedMethodName));
760                } else if (instruction instanceof INVOKESPECIAL) {
761                    INVOKESPECIAL invoke = (INVOKESPECIAL)instruction;
762                    Object substance = stack.getSubstance(invoke);
763                    loggerRtti.debug("  invokespecial "+
764                                     invoke.getMethodName(constPool)+" on "+substance);
765                    if (substance==VMStack.thisPointer &&
766                        invoke.getMethodName(constPool).equals(methodName) &&
767                        Arrays.equals(invoke.getArgumentTypes(constPool),method.getArgumentTypes())) 
768                    {
769                        loggerRtti.debug("  calls super");
770                        rtti.setCallSuper(className,methodSign);
771                    }
772                }
773                stack.execute(instruction,ih);
774            }
775            return methodGen.getMethod();
776        }
777    
778        static boolean isSystemField(String fieldName) {
779            return fieldName.indexOf('$')!=-1;
780        }
781    
782        /**
783         * Returns true if the n top elements of the stack are arguments
784         * @param stack a stack
785         * @param n number of elements to check
786         * @return true if the n top elements of the stack are arguments
787         */
788        static boolean areArguments(Stack stack, int n) {
789            for (;n>0; n--) {
790                if (!isArgument(stack,n-1))
791                    return false;
792            }
793            return true;
794        }
795    
796        /**
797         * Returns true if the nth top element of the stack is an argument
798         *
799         * @param stack a stack
800         * @param n index element to check
801         * @return true if stack.peek(n) is an argument
802         */
803        static boolean isArgument(Stack stack, int n) {
804            return !( !(stack.peek(n) instanceof VMStack.Argument) &&
805                      !(stack.peek(n) instanceof VMStack.PrimitiveValue &&
806                        ((VMStack.PrimitiveValue)stack.peek(n)).wrappedValue 
807                        instanceof VMStack.Argument) );
808        }
809    
810        static VMStack.Argument getArgument(Stack stack, int n) {
811            Object elt = stack.peek(n);
812            if (elt instanceof VMStack.Argument) {
813                return (VMStack.Argument)elt;
814            } else if (elt instanceof VMStack.PrimitiveValue &&
815                       ((VMStack.PrimitiveValue)elt).wrappedValue 
816                       instanceof VMStack.Argument) {
817                return (VMStack.Argument)((VMStack.PrimitiveValue)elt).wrappedValue;
818            }
819            return null;
820        }
821    
822        /**
823         * Change the type of collection fields to use org.objectweb.jac.lib types
824         * @param classGen the class holding the field
825         * @param field the field whose type to change
826         */
827        private void translateField(ClassGen classGen, Field field)
828        {
829            String type = Type.getType(field.getSignature()).toString();
830            String translatedType = (String)collectionTypes.get(type);
831            if (translatedType != null) {
832                logger.info("field "+
833                          field.getName()+" "+type+
834                          " -> "+translatedType);
835                ConstantPoolGen constPool = classGen.getConstantPool();
836                FieldGen newField = new FieldGen(field,constPool);
837                newField.setType(Type.getType(translatedType));
838                classGen.replaceField(field,newField.getField());
839                // replace the Fieldref entry in the constant pool
840                constPool.setConstant(
841                    constPool.lookupFieldref(classGen.getClassName(),
842                                             field.getName(),
843                                             field.getSignature()),
844                    new ConstantFieldref(
845                        classGen.getClassNameIndex(),
846                        constPool.addNameAndType(field.getName(),
847                                                 translatedType)));
848            }
849        }
850    
851        static final String prefix="_org_";
852    
853        /**
854         * Translate a method (rename it and generate a stub).
855         * 
856         * @param classGen the ClassGen holding the method
857         * @param constPool the constant pool 
858         * @param method the method to translate
859         * @param staticFieldIndex index of the static field holding the
860         * reference to the MethodItem of the method
861         * @param wrappingChainIndex index of the static field holding the
862         * wrapping chain for that method.
863         * @param wrappingChainIndexes 
864         */
865        private void translateMethod(ClassGen classGen, ConstantPoolGen constPool,
866                                     Method method, int staticFieldIndex,
867                                     List staticFieldIndexes, 
868                                     int wrappingChainIndex,
869                                     List wrappingChainIndexes)
870        {
871            logger.debug("Translating method "+
872                         method.getName()+"("+method.getNameIndex()+")");
873            // get all methods
874            String tmpname;
875                    
876            tmpname = method.getName();
877            //rename the method "xxx" in "_org_xxx" (except for
878            //constructors, where it's called _org_<class_name>)
879            String newName = null;
880            InstructionList callSuper;
881            String className = classGen.getClassName()
882                .substring(classGen.getClassName().lastIndexOf(".")+1);
883            if (tmpname.equals("<init>")) {
884                if (!method.isPublic()) {
885                    // We keep a copy of non public constructors because
886                    // they may be called from other constructors and we
887                    // won't generate a stub for them
888                    MethodGen copyMethod = 
889                        new MethodGen(method,classGen.getClassName(),constPool);
890                    translateConstructor(classGen,method,copyMethod,null,false);
891                    classGen.addMethod(copyMethod.getMethod());
892                }
893                callSuper = new InstructionList();
894                newName = renameMethod(
895                    classGen, constPool, method,
896                    prefix+className,
897                    callSuper);
898                logger.debug("callSuper = "+callSuper);
899                callSuper.setPositions();
900            } else {
901                callSuper = null;
902                classGen.removeMethod(method);
903                if (!classGen.getClassName().startsWith("org.objectweb.jac.lib.java")) {
904                    method = fillRTTI(classGen,constPool,method,false);
905                }
906                newName = renameMethod(classGen, constPool, method, 
907                                       prefix+tmpname+
908                                    // Renamed methods should not override each other
909                                       (method.isStatic()?"":"_"+className),
910                                       callSuper);
911            }
912            if (!tmpname.equals("<init>") || method.isPublic()) {
913                generateStubMethod(classGen,constPool,method,newName,
914                                   staticFieldIndex,staticFieldIndexes,
915                                   wrappingChainIndex,wrappingChainIndexes, 
916                                   callSuper);
917            } else {
918                logger.debug("skipping stub method generation for "+method);
919            }
920        }
921    
922        /**
923         * Returns true is the method should be translated
924         * @param className name of the class
925         * @param method the method
926         */
927        boolean isTranslatable(String className,Method method) {
928            String methodName = method.getName();
929            if ((!method.isPublic()) ||
930                method.isAbstract() || 
931                method.isInterface()) return false;
932            if (method.isStatic()
933                && (methodName.equals("get") ||
934                    methodName.equals("main") ||
935                    methodName.equals("<clinit>"))) 
936                return false;
937            if ( (methodName.equals("toString") && 
938                  method.getSignature().startsWith("()")) ||
939                 (methodName.equals("equals") && 
940                  method.getSignature().startsWith("(Ljava/lang/Object;)")) ||
941                 (methodName.equals("hashCode") && 
942                  method.getSignature().startsWith("()")) )
943                return false;
944            Map wrappableMethods = JacPropLoader.wrappableMethods;
945            if(!wrappableMethods.containsKey(className)) {
946                logger.debug(className+" has all methods wrappable");
947                return true;
948            }
949            // constructors should always be wrappable
950            if (methodName.equals("<init>"))
951                return true;
952            List methods = (List)wrappableMethods.get(className);
953            Iterator it = methods.iterator();
954            while(it.hasNext()) {
955                String cur = (String)it.next();
956                if (cur.equals(methodName)) {
957                    logger.debug(methodName+" is wrappable");
958                    return true;
959                }
960            }
961            logger.debug(methodName+" is not wrappable");
962             
963            return false;
964        }
965    
966        /**
967         * Create a static field containing a reference to an
968         * AbstractMethodItem for method.
969         * @return the index in the constant pool of created field 
970         */
971        int createMethodStaticField(ClassGen classGen, ConstantPoolGen cp, 
972                                    String fieldName) {
973            FieldGen fieldGen = 
974                new FieldGen(Constants.ACC_STATIC | 
975                             Constants.ACC_PUBLIC | 
976                             Constants.ACC_TRANSIENT |
977                             Constants.ACC_FINAL,
978                             Type.getType("Lorg/objectweb/jac/core/rtti/AbstractMethodItem;"),
979                             fieldName,
980                             cp);
981            Field field = fieldGen.getField();
982            //      int fieldIndex = field.getConstantValue().getConstantValueIndex();
983            classGen.addField(field);
984            return cp.addFieldref(classGen.getClassName(),
985                                  field.getName(),
986                                  field.getSignature());
987        }
988    
989        int createWrappingChainField(ClassGen classGen, 
990                                     ConstantPoolGen constPool,
991                                     String fieldName,
992                                     boolean isStatic) {
993            FieldGen fieldGen=new FieldGen(Constants.ACC_PUBLIC | 
994                                           Constants.ACC_TRANSIENT |
995                                           Constants.ACC_FINAL | 
996                                           (isStatic?Constants.ACC_STATIC:0),
997                                           Type.getType("Lorg.objectweb.jac.core.WrappingChain;"),
998                                           fieldName,
999                                           constPool);
1000            Field field = fieldGen.getField();
1001            classGen.addField(field);
1002            return constPool.addFieldref(
1003                classGen.getClassName(),
1004                field.getName(),field.getSignature());
1005        }
1006    
1007        /**
1008         * Setup class before really translating it.
1009         *
1010         * <p>The Wrappee interface is added, and the __JAC_TRANSLATED
1011         * field is added too. A default constructor is created if none
1012         * exists.</p>
1013         * @param classGen translated class */
1014        private void startingTranslation(ClassGen classGen)
1015        {
1016                    
1017            ConstantPoolGen constPool = classGen.getConstantPool();
1018            int objectIndexRef = constPool.lookupClass("java.lang.Object");
1019    
1020            // Add the __JAC_TRANSLATED field
1021            FieldGen fgJT = new FieldGen (
1022                Constants.ACC_STATIC | 
1023                Constants.ACC_PUBLIC | 
1024                (classGen.isInterface() ? Constants.ACC_FINAL : Constants.ACC_TRANSIENT), 
1025                Type.BOOLEAN, 
1026                "__JAC_TRANSLATED", 
1027                constPool);
1028            classGen.addField (fgJT.getField());
1029                    
1030            classGen.addInterface("org.objectweb.jac.core.Wrappee");
1031          
1032            if (!classGen.isInterface() && 
1033                classGen.containsMethod("<init>","()V")==null) {
1034                generateDefaultConstructor(classGen);
1035            }
1036        }
1037    
1038        /**
1039         * Returns true if the class is already translated (ie it contains
1040         * a field named __JAC_TRANSLATED)
1041         */
1042        protected boolean isTranslated(JavaClass javaClass) {
1043            Field[] fields = javaClass.getFields();
1044            for (int i=0; (i<fields.length); i++)
1045                if (fields[i].getName().equals("__JAC_TRANSLATED"))
1046                    return true;
1047            return false;
1048        }
1049    
1050        /**
1051         * Returns the full name of a method as required by RTTI
1052         * @param method the method
1053         * @return full name of the method (something like
1054         * myMethod(java.lang.String,int) */
1055        String getMethodFullName(Method method) {
1056            String fullName=method.getName()+"(";
1057            Type[] argumentTypes = Type.getArgumentTypes(method.getSignature());      
1058            for(int i=0;i<argumentTypes.length;i++) {
1059                fullName+=argumentTypes[i];
1060                if (i<argumentTypes.length-1) 
1061                    fullName+=",";
1062            }
1063            fullName+=")";
1064            return fullName;
1065        }
1066    
1067        /**
1068         * Create code to initialize static fields that hold MethodItems
1069         * and wrapping chains.
1070         *
1071         * @param classGen the class to build static fields initialization for
1072         * @param fields a list of StaticField
1073         * @param clinit the static class initialization method if one
1074         * already exists
1075         */
1076        void initStaticFields(ClassGen classGen, List fields, Method clinit) {
1077            //search for the static {} method
1078            Method[] methods = classGen.getMethods();
1079            ConstantPoolGen constPool = classGen.getConstantPool();
1080            MethodGen methodGen;
1081            InstructionFactory ifactory = new InstructionFactory (classGen);
1082            logger.info("initStaticFields for "+
1083                      classGen.getClassName());
1084            // If there is no static method 
1085            // then we will need to build one from scratch
1086            if (clinit==null)
1087            {
1088                methodGen = new MethodGen (Constants.ACC_STATIC, 
1089                                           Type.VOID, emptyTypeArray, 
1090                                           ExtArrays.emptyStringArray,
1091                                           Constants.STATIC_INITIALIZER_NAME,
1092                                           classGen.getClassName(), 
1093                                           new InstructionList(), 
1094                                           constPool);
1095            } else {
1096                //else we need to update the already existing one
1097                if (!clinit.getName().equals(Constants.STATIC_INITIALIZER_NAME)) {
1098                    logger.error("This is not a class initializer: "+clinit.getName());
1099                }
1100                methodGen = new MethodGen (clinit,
1101                                           classGen.getClassName(), 
1102                                           constPool);
1103            }
1104            InstructionList il = new InstructionList();
1105          
1106            // ClassRepository.get() ...
1107            il.append(
1108                ifactory.createInvoke("org.objectweb.jac.core.rtti.ClassRepository","get",
1109                                      Type.getType("Lorg/objectweb/jac/core/rtti/ClassRepository;"),
1110                                      emptyTypeArray,
1111                                      Constants.INVOKESTATIC));
1112    
1113            // ... .getClass(<className>)
1114            int classNameIndex = constPool.addString(classGen.getClassName());
1115            il.append(new LDC(classNameIndex));
1116            il.append(
1117                ifactory.createInvoke("org.objectweb.jac.core.rtti.ClassRepository","getClass",
1118                                      Type.getType("Lorg/objectweb/jac/core/rtti/ClassItem;"),
1119                                      new Type[] {Type.STRING},
1120                                      Constants.INVOKEVIRTUAL));
1121    
1122            // We now have the ClassItem on the stack
1123            // Let's call getMethod on it.
1124            Iterator it = fields.iterator();
1125            while (it.hasNext()) {
1126                StaticField field = (StaticField)it.next();
1127                logger.info("initStaticField("+
1128                            field.fieldName+","+field.methodName+")");
1129    
1130                il.append(new DUP());
1131                il.append(new LDC(constPool.addString(field.methodName)));
1132                il.append(
1133                    ifactory.createInvoke(
1134                        "org.objectweb.jac.core.rtti.ClassItem","getAbstractMethod",
1135                        Type.getType("Lorg/objectweb/jac/core/rtti/AbstractMethodItem;"),
1136                        new Type[] {Type.STRING},
1137                        Constants.INVOKEVIRTUAL));
1138                il.append(new PUTSTATIC(field.fieldIndex));
1139            }
1140            il.append(new POP());
1141            il.append (methodGen.getInstructionList());
1142            if (clinit==null) {
1143                il.append (InstructionFactory.createReturn(Type.VOID));
1144            }
1145    
1146            methodGen.setInstructionList(il);
1147            methodGen.removeNOPs();
1148            methodGen.removeLocalVariables();
1149            methodGen.setMaxLocals();
1150            methodGen.setMaxStack();
1151                    
1152            if (clinit!=null)
1153                classGen.removeMethod(clinit);
1154            classGen.addMethod (methodGen.getMethod());
1155                            
1156        }
1157    
1158        static class StaticField {
1159            public String fieldName;
1160            public String methodName;
1161            public int fieldIndex;
1162            public StaticField(String fieldName, String methodName, int fieldIndex) {
1163                this.fieldName = fieldName;
1164                this.methodName = methodName;
1165                this.fieldIndex = fieldIndex;
1166            }
1167        }
1168    
1169        /**
1170         * Translate a class
1171         * @param aClass name of the class to translate
1172         * @return bytecode of the translated class
1173         */
1174        public byte[] translateClass(String aClass) throws Exception 
1175        {
1176            JavaClass originalClass = Repository.lookupClass(aClass); 
1177            if (originalClass==null) {
1178                logger.info("CLASS NOT FOUND "+aClass);
1179                throw new ClassNotFoundException("Class not found: "+aClass);
1180            }
1181                    
1182            // test if it is public (for security resons, protected and
1183            // private classes are not translated).
1184            //if (!originalClass.isPublic()) {
1185            //   logger.info("skipping non public class "+aClass);
1186            //   return null;
1187            //}
1188    
1189            // test if the class is already translated/adapted.
1190            if (!isTranslated(originalClass))
1191            {
1192                /** None of the above mentionned cases are verified. Adapt the class. */
1193             
1194                logger.info("TRANSLATING "+aClass);
1195             
1196                // Mark it as translated
1197                ClassGen  newClassGen = new ClassGen (originalClass); 
1198                startingTranslation(newClassGen);
1199    
1200                // test if a real class (not an interface)
1201                if (originalClass.isInterface()) {
1202                    logger.info("skipping interface "+aClass);
1203                    return newClassGen.getJavaClass().getBytes();
1204                }
1205    
1206                Method clinit = newClassGen.containsMethod("<clinit>","()V");
1207                if (clinit!=null)
1208                    logger.debug("clinit = "+clinit+"("+clinit.getNameIndex()+")");
1209                else
1210                    logger.debug("no <clinit>");
1211             
1212                // translate fields 
1213                // (java.util.<collection> -> org.objectweb.jac.lib.java.util.<collection>)
1214                Field[] fields = newClassGen.getFields();
1215                if (JacPropLoader.translateFields(newClassGen.getClassName())) {
1216                    for (int i=0; i<fields.length; i++) {
1217                        if (!fields[i].isTransient() && !fields[i].isStatic())
1218                            translateField(newClassGen, fields[i]);
1219                    }
1220                }
1221    
1222                ConstantPoolGen constPool = newClassGen.getConstantPool();
1223             
1224                // get all methods and translate them
1225                Method[] methods = newClassGen.getMethods();
1226                LinkedList staticFields = new LinkedList();
1227                List staticFieldIndexes = new Vector();
1228                List wrappingChainIndexes = new Vector();
1229    
1230                // first pass to get the field indexes
1231                for (int i=0; i<methods.length; i++) {
1232                    if (isTranslatable(newClassGen.getClassName(),methods[i])) { 
1233                        // create a static field containing a ref to the
1234                        // AbstractMethodItem
1235                        staticFieldIndexes.add(new Integer(
1236                            createMethodStaticField(newClassGen,constPool,
1237                                                    "__JAC_method_"+i)));
1238                        wrappingChainIndexes.add(new WCIndex(
1239                            methods[i].isStatic(),
1240                            createWrappingChainField(newClassGen,
1241                                                     constPool,"__JAC_wc_"+i,
1242                                                     methods[i].isStatic())));
1243                    } else {
1244                        staticFieldIndexes.add(null);
1245                        wrappingChainIndexes.add(null);
1246                    }
1247                }
1248                   
1249                for (int i=0; i<methods.length; i++) {
1250                    if (isTranslatable(newClassGen.getClassName(),methods[i])) { 
1251                   
1252                        try {
1253                            translateMethod(
1254                                newClassGen,constPool,
1255                                methods[i],
1256                                ((Integer)staticFieldIndexes.get(i)).intValue(),
1257                                staticFieldIndexes,
1258                                ((WCIndex)wrappingChainIndexes.get(i)).index,
1259                                wrappingChainIndexes);
1260                            
1261                            staticFields.add(new StaticField(
1262                                "__JAC_method_"+i,
1263                                getMethodFullName(methods[i]),
1264                                ((Integer)staticFieldIndexes.get(i)).intValue()));
1265                        } catch(Exception e) {
1266                            logger.error("translateClass: failed to translate method "+
1267                                         newClassGen.getClassName()+"."+methods[i]+": "+e);
1268                            throw e;
1269                        }
1270    
1271                    } else {
1272    
1273                        // translate private or protected contructors (not
1274                        // stored in rtti)
1275                        if (methods[i].getName().equals("<init>") && 
1276                            !methods[i].isPublic())
1277                        {
1278                            translateMethod(
1279                                newClassGen,constPool,
1280                                methods[i],
1281                                0,null,0,null);
1282                        } else if (methods[i].getName().equals("this")) {
1283                            MethodGen methodGen = 
1284                                new MethodGen(methods[i], newClassGen.getClassName(), constPool);
1285                            translateConstructor(newClassGen,methods[i],methodGen,null,true);
1286                            newClassGen.replaceMethod(methods[i],methodGen.getMethod());
1287                        }
1288    
1289                        logger.debug("skipping "+methods[i]+
1290                                  "("+methods[i].getNameIndex()+")");
1291                        if (methods[i].getName().startsWith("<init>")) {
1292                            logger.warn("Constructor not translated: "+aClass+"."+methods[i]);
1293                        }
1294                    }
1295                }
1296    
1297                // initialize static fields
1298                initStaticFields(newClassGen,staticFields,clinit);
1299    
1300                return newClassGen.getJavaClass().getBytes();
1301                            
1302            } else {
1303    
1304                logger.info("ALREADY TRANSLATED "+aClass);
1305                ConstantPoolGen constPool = 
1306                    new ConstantPoolGen(originalClass.getConstantPool());
1307                Method[] methods = originalClass.getMethods();
1308                /*
1309                  for (int i=0; i<methods.length; i++) {
1310                  if (methods[i].getName().startsWith(prefix))
1311                  fillRTTI(originalClass.getClassName(), constPool, methods[i]);
1312                  }
1313                */         
1314                return originalClass.getBytes();
1315            }
1316    
1317        }
1318    
1319        /**
1320         * Computes RTTI info for a class and gets its bytecode
1321         * @param aClass name of the class analyze
1322         * @return bytecode of the class
1323         */
1324        public byte[] fillClassRTTI(String aClass) throws Exception 
1325        {
1326            JavaClass originalClass = Repository.lookupClass(aClass); 
1327            if (originalClass==null) {
1328                logger.info("CLASS NOT FOUND "+aClass);
1329                throw new ClassNotFoundException("Class not found: "+aClass);
1330            }
1331                    
1332            // test if a real class (not an interface)
1333            if (originalClass.isInterface()) {
1334                logger.info("skipping interface "+aClass);
1335                return null;
1336            }
1337    
1338            /** None of the above mentionned cases are verified. Load the class. */
1339             
1340            logger.info("LOADING "+aClass);
1341             
1342            // Mark it as translated
1343            ClassGen  newClassGen = new ClassGen(originalClass); 
1344            ConstantPoolGen constPool = newClassGen.getConstantPool();
1345             
1346            // get all methods and translate them
1347            Method[] methods = newClassGen.getMethods();
1348    
1349            for (int i=0; i<methods.length; i++) {
1350                logger.info("Analyzing "+methods[i]);
1351                fillRTTI(newClassGen,constPool,methods[i],true);
1352            }
1353    
1354            return newClassGen.getJavaClass().getBytes();
1355    
1356        }
1357    
1358        static public boolean isCollection(String type) {
1359            return collectionTypes.containsKey(type);
1360        }
1361    
1362        static class WCIndex {
1363            boolean isStatic;
1364            int index;
1365            public WCIndex(boolean isStatic, int index) {
1366                this.isStatic = isStatic;
1367                this.index = index;
1368            }
1369        }
1370    
1371        static HashMap collectionTypes = new HashMap();
1372        {
1373            collectionTypes.put("java.util.Vector","Lorg/objectweb/jac/lib/java/util/Vector;");
1374            collectionTypes.put("java.util.Hashtable","Lorg/objectweb/jac/lib/java/util/Hashtable;");
1375            collectionTypes.put("java.util.HashMap","Lorg/objectweb/jac/lib/java/util/HashMap;");
1376            collectionTypes.put("java.util.HashSet","Lorg/objectweb/jac/lib/java/util/HashSet;");
1377        }
1378    
1379        static final Type[] emptyTypeArray = new Type[0];
1380    }
1381