Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2010 the original author or authors.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.callbackparams.support;
 
 import java.util.List;
 import java.util.Map;
 import  org.apache.bcel.Constants;
 import  org.apache.bcel.classfile.Attribute;
 import  org.apache.bcel.classfile.Code;
 import  org.apache.bcel.classfile.Method;
 import  org.apache.bcel.classfile.Unknown;
 import  org.apache.bcel.generic.ALOAD;
 import  org.apache.bcel.generic.ClassGen;
 import  org.apache.bcel.generic.ConstantPoolGen;
 import  org.apache.bcel.generic.DUP;
 import  org.apache.bcel.generic.DUP2;
 import  org.apache.bcel.generic.DUP2_X1;
 import  org.apache.bcel.generic.DUP2_X2;
 import  org.apache.bcel.generic.DUP_X1;
 import  org.apache.bcel.generic.DUP_X2;
 import  org.apache.bcel.generic.EmptyVisitor;
 import  org.apache.bcel.generic.INVOKESPECIAL;
 import  org.apache.bcel.generic.Instruction;
 import  org.apache.bcel.generic.InstructionConstants;
 import  org.apache.bcel.generic.InstructionFactory;
 import  org.apache.bcel.generic.InstructionHandle;
 import  org.apache.bcel.generic.InstructionList;
 import  org.apache.bcel.generic.MethodGen;
 import  org.apache.bcel.generic.SWAP;
 import  org.apache.bcel.generic.Type;
 import  org.apache.bcel.generic.Visitor;
 import  org.apache.bcel.util.ClassLoaderRepository;

A builder class for modifying class byte-code by staringt-off with an exising template class. It comes with a few convience methods that can make some common generical byte-code modifications. One of the main features is to enable constant-time lookup of methods through hashing - which might not be that important for most cases but could prove useful once someone comes along with some generated class containing thousands of methods :)

Author(s):
Henrik Kaipe
 
 public class ClassBytecodeBuilder {

    
Is used for sending a "complete" exit-code from inside a visitor.
 
     private static class VisitorTaskPerformed extends RuntimeException {
         private final static VisitorTaskPerformed instance =
                 new VisitorTaskPerformed();
     }
 
     private final ClassGen cg;

    
Maps MethodHashKey instances to their respective bcel-Method instances.
 
     private final Map methodMap = new HashMap();
 
     private ClassBytecodeBuilder(final ClassGen cg) {
         this. = cg;
         final Method[] methods = cg.getMethods();
         for (int i = 0 ; i < methods.length ; ++i) {
             final Method m = methods[i];
             .put(MethodHashKey.getHashKey(cgm), m);
         }
     }
 
     public static ClassBytecodeBuilder newInstance(Class templateClass) {
         ClassLoaderRepository loaderRepo =
                 new ClassLoaderRepository(templateClass.getClassLoader());
         try {
             return new ClassBytecodeBuilder(new ClassGen(
                     loaderRepo.loadClass(templateClass)));
         } catch (ClassNotFoundException ex) {
             throw new Error(ex);
         }
     }
 
    public InstructionFactory getInstructionFactory() {
        return new InstructionFactory();
    }
    private static void filterUnknownAttributes(Code codeAttribute) {
        final Attribute[] subAttrs = codeAttribute.getAttributes();
        List list = new ArrayList(subAttrs.length);
        for (int i = 0; i < subAttrs.length; ++i) {
            if (false == subAttrs[iinstanceof Unknown) {
                list.add(subAttrs[i]);
            }
        }
        if (subAttrs.length != list.size()) {
            codeAttribute.setAttributes(
                    (Attribute[]) list.toArray(new Attribute[list.size()]));
        }
    }
    private static Method filterUnknownCodeAttributes(Method m) {
        Attribute[] attributes = m.getAttributes();
        for (int i = 0; i < attributes.length; ++i) {
            if (attributes[iinstanceof Code) {
                filterUnknownAttributes((Code) attributes[i]);
            }
        }
        return m;
    }
    public void prependMethod(Method m, InstructionList instructionList) {
        MethodGen mg = new MethodGen(
                m.getClassName(), .getConstantPool());
        mg.getInstructionList().insert(instructionList);
        mg.setMaxStack();
        replaceMethod(filterUnknownCodeAttributes(mg.getMethod()));
    }
    public Method getMethod(java.lang.reflect.Method m) {
        return (Method) .get(MethodHashKey.getHashKey(m));
    }
    public ClassBytecodeBuilder replaceMethod(Method newMethod) {
        MethodHashKey hashkey = MethodHashKey.getHashKey(newMethod);
        Method oldMethod = (Method) .get(hashkey);
        if (null == oldMethod) {
            throw new NullPointerException(
                    "There is no equivalent method to replace - " + newMethod);
        }
        .replaceMethod(oldMethodnewMethod);
        .put(hashkeynewMethod);
        return this;
    }
    public ClassBytecodeBuilder addMethod(Method m) {
        .addMethod(m);
        .put(MethodHashKey.getHashKey(m), m);
        return this;
    }
    public byte[] getByteCode() {
        return .getJavaClass().getBytes();
    }
    public void setPublic() {
        .setModifiers(.getModifiers() | Constants.ACC_PUBLIC
                & ~Constants.ACC_PRIVATE & ~Constants.ACC_PROTECTED);
    }
    public void setNewSuperClass(String newSuperClassName) {
        for (Iterator i = .values().iterator() ; i.hasNext() ;) {
            final Method m = (Method) i.next();
            if ("<init>".equals(m.getName())) {
                modifyConstructor(mnewSuperClassName);
            }
        }
        .setSuperclassName(newSuperClassName);
    }
    public void setupTransparentConstructors(
            List constructorParamTypesString newSuperClassName) {
        Method defaultConstructor = findDefaultConstructor();
        List instructionsToCopy = listInstructionsToKeep(defaultConstructor);
        /* Ditch the construction - it will be replaced by new ones. */
        removeMethod(defaultConstructor);
        for (Iterator i = constructorParamTypes.iterator() ; i.hasNext() ;) {
            final Type[] paramTypes = (Type[]) i.next();
            addNewTransparentConstructor(
                    paramTypesnewSuperClassNameinstructionsToCopy);
        }
        .setSuperclassName(newSuperClassName);
    }
    private void addNewTransparentConstructor(final Type[] paramTypes,
            String newSuperClassNameList instructionsToCopy) {
        InstructionList il = new InstructionList();
        MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, Type.VOID,
                paramTypesnull"<init>".getClassName(), il,
                .getConstantPool());
        il.append(InstructionConstants.ALOAD_0);
        for (int i = 0 ; i < paramTypes.length ; ++i) {
            il.append(InstructionFactory.createLoad(paramTypes[i], i + 1));
        }
        il.append(getInstructionFactory().createInvoke(newSuperClassName,
                "<init>", Type.VOID, paramTypes, Constants.INVOKESPECIAL));
        for (Iterator i = instructionsToCopy.iterator() ; i.hasNext() ;) {
            il.append((Instruction) i.next());
        }
        mg.setMaxStack();
        addMethod(mg.getMethod());
    }

    
Assumes there is only one constructor for this class.
    private Method findDefaultConstructor() {
        for (Iterator i = .entrySet().iterator() ; i.hasNext() ;) {
            final Map.Entry entry = (Entryi.next();
            final MethodHashKey key = (MethodHashKeyentry.getKey();
            if ("<init>".equals(key.methodName)) {
                return (Method) entry.getValue();
            }
        }
        throw new Error("No constructor was found");
    }
    private void modifyConstructor(
            Method methodfinal String newSuperClassName) {
        final String className = .getClassName();
        final Type thisType = Type.getType('L' + className + ';');
        final ConstantPoolGen cpg = .getConstantPool();
        final MethodGen mg = new MethodGen(methodclassNamecpg);
        new org.apache.bcel.generic.EmptyVisitor() {
            int stackDistanceToA0 = ./2; // Initial dummy-value
            InstructionHandle ih;
            boolean pending = true;
//            @Override
            public void visitALOAD(ALOAD obj) {
                if (0 == obj.getIndex()) {
                     = 0;
                    throw .;
                }
            }

            
Used for handling the duplicating stack-operations DUP...
            void donotModifyStackDistanceLessThan(int sizeOfUnchangedStackTop) {
                if ( < sizeOfUnchangedStackTop) {
                    throw .;
                }
            }
//            @Override
            public void visitDUP(DUP obj) {
                donotModifyStackDistanceLessThan(1);
            }
//            @Override
            public void visitDUP2(DUP2 obj) {
                donotModifyStackDistanceLessThan(2);
            }
//            @Override
            public void visitDUP2_X1(DUP2_X1 obj) {
                donotModifyStackDistanceLessThan(3);
            }
//            @Override
            public void visitDUP2_X2(DUP2_X2 obj) {
                donotModifyStackDistanceLessThan(4);
            }
//            @Override
            public void visitDUP_X1(DUP_X1 obj) {
                donotModifyStackDistanceLessThan(2);
            }
//            @Override
            public void visitDUP_X2(DUP_X2 obj) {
                donotModifyStackDistanceLessThan(3);
            }
//            @Override
            public void visitSWAP(SWAP obj) {
                if ( < 2) {
                     = 1 - ;
                }
                throw .;
            }
//            @Override
            public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
//                System.out.println("Invoke special on " + className);
//                System.out.println("Distance to A0 :" + stackDistanceToA0);
//                System.out.println("Stack to consume: " + obj.consumeStack(cpg));
//                System.out.println("Instruction name: " + obj.getName(cpg));
                if ( < obj.consumeStack(cpg)) {
                    assert "<init>".equals(obj.getName(cpg));
                    if (false == thisType.equals(obj.getReferenceType(cpg))) {
                        .setInstruction(getInstructionFactory().createInvoke(
                                newSuperClassName,
                                "<init>",
                                Type.VOID,
                                obj.getArgumentTypes(cpg),
                                Constants.INVOKESPECIAL));
                        mg.setMaxStack();
                        replaceMethod(mg.getMethod());
                    }
                     = false;
                    throw .;
                }
            }
            void replaceSuperConstructorInvocation() {
                for ( = mg.getInstructionList().getStart()
                        ;  ;  = .getNext()) {
                    try {
                        .accept(this);
                        Instruction instr = .getInstruction();
                        int produceStack = instr.produceStack(cpg);
                        if (0 < produceStack) {
                             += produceStack;
                        } else {
                            int consumeStackinstr.consumeStack(cpg);
                            if (0 < consumeStack) {
                                 -= consumeStack;
                            }
                        }
                    } catch (VisitorTaskPerformed x) {
                        continue;
                    }
                }
            }

            
Initiator (An anonymous class cannot have a constructor)
            {replaceSuperConstructorInvocation();}
        };
    }
    private List listInstructionsToKeep(Method defaultConstructor) {
        List instructionsToKeepInNewConstructors = new ArrayList();
        InstructionList il = new MethodGen(
                defaultConstructor.getClassName(), .getConstantPool()).getInstructionList();
        Visitor superConstructorInvocationDetector = new EmptyVisitor() {
//            @Override
            public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
                throw .;
            }
        };
        Iterator i = il.iterator();
        try {
            while (i.hasNext()) {
                final InstructionHandle ih = (InstructionHandle) i.next();
                ih.accept(superConstructorInvocationDetector);
            }
            throw new Error("Found no super() invocation in instruction list: " + il);
        } catch (VisitorTaskPerformed x) {
            while (i.hasNext()) {
                final InstructionHandle ih = (InstructionHandle) i.next();
                instructionsToKeepInNewConstructors.add(ih.getInstruction());
            }
        }
        return instructionsToKeepInNewConstructors;
    }
    private void removeMethod(Method methodToRemove) {
        .removeMethod(methodToRemove);
        for (Iterator i = .entrySet().iterator() ; i.hasNext() ;) {
            Map.Entry entry = (Entryi.next();
            if (methodToRemove == entry.getValue()) {
                i.remove();
                break;
            }
        }
    }
New to GrepCode? Check out our FAQ X