Java tutorial
/* * Copyright (C) 2013 uebb.tu-berlin.de. * * This file is part of JBOP (Java Bytecode OPtimizer). * * JBOP is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JBOP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JBOP. If not, see <http://www.gnu.org/licenses/>. */ package de.tuberlin.uebb.jbop.optimizer; import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; import static org.objectweb.asm.Opcodes.ACC_INTERFACE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARRAYLENGTH; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.ATHROW; import static org.objectweb.asm.Opcodes.BIPUSH; import static org.objectweb.asm.Opcodes.DCMPG; import static org.objectweb.asm.Opcodes.DCONST_1; import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.IALOAD; import static org.objectweb.asm.Opcodes.IASTORE; import static org.objectweb.asm.Opcodes.IFEQ; import static org.objectweb.asm.Opcodes.IINC; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.IRETURN; import static org.objectweb.asm.Opcodes.ISTORE; import static org.objectweb.asm.Opcodes.JSR; import static org.objectweb.asm.Opcodes.LDC; import static org.objectweb.asm.Opcodes.NEWARRAY; import static org.objectweb.asm.Opcodes.NOP; import static org.objectweb.asm.Opcodes.POP; import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.SALOAD; import static org.objectweb.asm.Opcodes.SASTORE; import static org.objectweb.asm.Opcodes.SIPUSH; import java.util.ArrayList; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.ConstructorUtils; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MultiANewArrayInsnNode; import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.VarInsnNode; import de.tuberlin.uebb.jbop.access.ClassAccessor; import de.tuberlin.uebb.jbop.access.ClassDescriptor; import de.tuberlin.uebb.jbop.access.ConstructorBuilder; import de.tuberlin.uebb.jbop.optimizer.utils.NodeHelper; /** * Base class for {@link IOptimizer}-Tests. * * This is a fluent-api for creating ClassNodes. * * @author Christopher Ewest */ public final class ClassNodeBuilder { /** The class node. */ private final ClassNode classNode; private MethodNode lastMethod; private MethodNode lastConstructor; private FieldNode lastField; private Object lastElement; /** The lastConstructor. */ private Class<?> buildedClass; private int constructorVarIndex = 1; private int lastConstructorVarIndex = 1; private int methodVarIndex = 1; private int lastMethodVarIndex; private Type lastVarElementType; private final boolean isInterface; private ClassNodeBuilder(final String className, final String superClass, final String constructorDesc, final String superConstructorDesc, final boolean isInterface) { this.isInterface = isInterface; classNode = new ClassNode(Opcodes.ASM5); classNode.access = ACC_PUBLIC; classNode.name = className.replace(".", "/"); if (superClass != null) { classNode.superName = superClass.replace(".", "/"); } classNode.version = Opcodes.V1_7; if (!isInterface) { addConstructor(constructorDesc, superConstructorDesc); } else { classNode.access |= ACC_INTERFACE; classNode.access |= ACC_ABSTRACT; } lastElement = null; } /** * Creates a new ClassNode with the given name and a default Constructor. * * @param className * the class name * @return the abstract optimizer test */ public static ClassNodeBuilder createClass(final String className) { return createClass(className, "()V", Type.getInternalName(Object.class), "()V"); } /** * Creates a new ClassNode with the given name and a default Constructor. * * @param className * the class name * @return the abstract optimizer test */ public static ClassNodeBuilder createInterface(final String className) { return new ClassNodeBuilder(className, Type.getInternalName(Object.class), null, null, true); } /** * Creates a new ClassNode with the given name , superClass and Constructor. * * @param className * the class name * @param constructorDesc * the constructor desc * @param superClass * the super class * @param superConstructorDesc * the super constructor desc * @return the abstract optimizer test */ public static ClassNodeBuilder createClass(final String className, final String constructorDesc, final String superClass, final String superConstructorDesc) { final ClassNodeBuilder builder = new ClassNodeBuilder(className, superClass, constructorDesc, superConstructorDesc, false); return builder; } /** * Appends a Constructor with the given descriptors. * * @param constructorDesc * the constructor desc * @param superConstructorDesc * the super constructor desc * @return the class node builder */ public ClassNodeBuilder addConstructor(final String constructorDesc, final String superConstructorDesc) { if (isInterface) { return this; } addMethod("<init>", constructorDesc);// lastConstructorVarIndex = 1; final InsnList list = new InsnList(); list.add(new VarInsnNode(Opcodes.ALOAD, 0)); final Type methodType = Type.getMethodType(superConstructorDesc); for (final Type parameterType : methodType.getArgumentTypes()) { list.add(new VarInsnNode(parameterType.getOpcode(ILOAD), lastConstructorVarIndex)); lastConstructorVarIndex += parameterType.getSize(); } list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, classNode.superName, "<init>", superConstructorDesc)); list.add(new InsnNode(Opcodes.RETURN)); addToConstructor(list); return this; } /** * Creates a getter for the last added field. * * @return the class node builder */ public ClassNodeBuilder withGetter() { if (isInterface) { return this; } final String name = lastField.name; final String desc = lastField.desc; addMethod("get" + Character.toUpperCase(name.charAt(0)) + name.substring(1), "()" + desc); final Type type = Type.getType(desc); addInsn(new VarInsnNode(Opcodes.ALOAD, 0)); addInsn(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, name, desc)); addInsn(new InsnNode(type.getOpcode(Opcodes.IRETURN))); return this; } /** * Creates a setter for the last added field. * * @return the class node builder */ public ClassNodeBuilder withSetter() { if (isInterface) { return this; } final String name = lastField.name; final String desc = lastField.desc; addMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), "(" + desc + ")V"); final Type type = Type.getType(desc); addInsn(new VarInsnNode(Opcodes.ALOAD, 0)); addInsn(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), 1)); addInsn(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, name, desc)); addInsn(new InsnNode(Opcodes.RETURN)); return this; } /** * Creates a getter and setter for the last added field. * * @return the class node builder */ public ClassNodeBuilder withGetterAndSetter() { if (isInterface) { return this; } withGetter().withSetter(); return this; } /** * adds a new empty void-method containing only the returnstatement. * * @param methodName * the method name * @return the class node builder */ public ClassNodeBuilder addEmptyMethod(final String methodName) { if ("<init>".equals(methodName)) { return this; } addMethod(methodName, "()V"); if (isInterface) { return this; } addInsn(new InsnNode(Opcodes.RETURN)); return this; } /** * adds a new method (public accessor) with the given name and descriptor to the classNode. * * @param methodName * the method name * @param descriptor * the descriptor * @param modifiers * the modifiers * @return the abstract optimizer test */ public ClassNodeBuilder addMethod(final String methodName, final String descriptor, final int... modifiers) { methodVarIndex = 1; final int modifier = getEffectiveModifier(modifiers); final MethodNode method = new MethodNode(modifier, methodName, descriptor, null, new String[] {}); method.exceptions = new ArrayList<>(); method.invisibleAnnotations = new ArrayList<>(); method.visibleAnnotations = new ArrayList<>(); classNode.methods.add(method); setLastMethod(method); if (isInterface) { method.access |= ACC_ABSTRACT; } return this; } private int getEffectiveModifier(final int... modifiers) { int modifier = 0; if (modifiers == null || modifiers.length == 0) { modifier = Opcodes.ACC_PUBLIC; } else { for (int i = 0; i < modifiers.length; ++i) { modifier |= modifiers[i]; } } return modifier; } /** * Adds the given annotation to the last created element. * The values have to be key-value pairs * * @param annotationClass * the annotation class * @param values * the values * @return the class node builder */ public ClassNodeBuilder withAnnotation(final Class<?> annotationClass, final Object... values) { final AnnotationNode annotationNode = new AnnotationNode(Type.getDescriptor(annotationClass)); if (values != null && values.length > 0) { annotationNode.values = Arrays.asList(values); } if (lastElement instanceof MethodNode) { lastMethod.visibleAnnotations.add(annotationNode); } else if (lastElement instanceof FieldNode) { lastField.visibleAnnotations.add(annotationNode); } else { classNode.visibleAnnotations.add(annotationNode); } return this; } /** * Adds a new Field with the given name and descriptor to the classNode. * for primitive Types and String a default value can be given via value. * * @param fieldName * the field name * @param descriptor * the descriptor * @param value * the value * @return the abstract optimizer test */ public ClassNodeBuilder addField(final String fieldName, final String descriptor, final Object... value) { if (isInterface) { return this; } final FieldNode fieldNode = new FieldNode(Opcodes.ACC_PRIVATE, fieldName, descriptor, null, value == null ? null : value.length > 0 ? value[0] : null); classNode.fields.add(fieldNode); lastField = fieldNode; lastElement = fieldNode; fieldNode.invisibleAnnotations = new ArrayList<>(); fieldNode.visibleAnnotations = new ArrayList<>(); return this; } /** * Sets the access modifier of the last added element. * * @param modifiers * the modifiers * @return the class node builder */ public ClassNodeBuilder withModifiers(final int... modifiers) { final int effectiveModifier = getEffectiveModifier(modifiers); if (lastElement instanceof MethodNode) { lastMethod.access = effectiveModifier; } else if (lastElement instanceof FieldNode) { lastField.access = effectiveModifier; } else { classNode.access = effectiveModifier; } return this; } /** * Initializes a classField in the default lastConstructor * (eg: if fieldType of field "field" is "TestObject", field = new TestObject() is called). * * Numbers and Strings are used to assign "field", every other value leads to new Object. * * @param object * the object to Use. * @return the abstract optimizer test */ public ClassNodeBuilder initWith(final Object object) { if (isInterface) { return this; } final InsnList list = new InsnList(); if (object instanceof String || object instanceof Number || object instanceof Boolean || object instanceof Character) { list.add(new VarInsnNode(Opcodes.ALOAD, 0)); final AbstractInsnNode numberNode = NodeHelper.getInsnNodeFor(object); list.add(numberNode); if (lastField.desc.startsWith("L") && object instanceof Number) { list.add(ConstructorBuilder.getBoxingNode(lastField)); } list.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, lastField.name, lastField.desc)); } else { list.add(new VarInsnNode(ALOAD, 0)); list.add(newObject(lastField.desc)); list.add(new MethodInsnNode(INVOKESPECIAL, StringUtils.removeEnd(StringUtils.removeStart(lastField.desc, "L"), ";"), "<init>", "()V")); list.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, lastField.name, lastField.desc)); } addToConstructor(list); return this; } /** * Creates Instruction to instantiate a new Object of type "desc". * * @param desc * the desc * @return the insn list */ private InsnList newObject(final String desc) { final InsnList list = new InsnList(); final String fieldDesc = StringUtils.removeEnd(StringUtils.removeStart(desc, "L"), ";"); final TypeInsnNode node = new TypeInsnNode(Opcodes.NEW, fieldDesc); list.add(node); list.add(new InsnNode(DUP)); return list; } /** * Creates Instruction to instantiate a new Object of the given type. * * @param builder * the builder * @return this ClassNodeBuilder */ public ClassNodeBuilder addNewObject(final ClassNodeBuilder builder) { if (isInterface) { return this; } return addNewObject(builder.getDesc()); } /** * Creates Instruction to instantiate a new Object of type "desc". * * @param desc * the desc * @return the insn list */ public ClassNodeBuilder addNewObject(final String desc) { if (isInterface) { return this; } final InsnList newObject = newObject(desc); return addInsn(newObject); } /** * Initializes a classField of type Array in the default Constructor. * The array is initialized with the given Numbers. * * @param values * the values * @return the abstract optimizer test * @see AbstractOptimizerTest#initArrayInConstructor(String, int) */ public ClassNodeBuilder initArrayWith(final Number... values) { if (isInterface) { return this; } final Type elementType = Type.getType(lastField.desc).getElementType(); if (elementType.getDescriptor().startsWith("L")) { initArrayInternal(Opcodes.AASTORE, (Object[]) values); return this; } final Type type = toPrimitive(elementType); initArrayInternal(type.getOpcode(Opcodes.IASTORE), (Object[]) values); return this; } /** * Inits the multi array with. * * @param value * the value * @param indexes * the indexes * @return the class node builder */ public ClassNodeBuilder initMultiArrayWith(final Object value, final int... indexes) { if (isInterface) { return this; } final InsnList list = new InsnList(); list.add(new VarInsnNode(Opcodes.ALOAD, 0)); list.add(new FieldInsnNode(Opcodes.GETFIELD, classNode.name, lastField.name, lastField.desc)); for (int i = 0; i < indexes.length - 1; ++i) { list.add(NodeHelper.getInsnNodeFor(indexes[i])); list.add(new InsnNode(Opcodes.AALOAD)); } list.add(NodeHelper.getInsnNodeFor(indexes[indexes.length - 1])); list.add(NodeHelper.getInsnNodeFor(value)); final Type elementType = Type.getType(lastField.desc).getElementType(); if (elementType.getDescriptor().startsWith("L")) { list.add(new InsnNode(Opcodes.AASTORE)); } else { list.add(new InsnNode(toPrimitive(Type.getType(value.getClass())).getOpcode(Opcodes.IASTORE))); } addToConstructor(list); return this; } private Type toPrimitive(final Type type) { if (Type.getType(Double.class).equals(type)) { return Type.DOUBLE_TYPE; } if (Type.getType(Float.class).equals(type)) { return Type.FLOAT_TYPE; } if (Type.getType(Long.class).equals(type)) { return Type.LONG_TYPE; } if (Type.getType(Integer.class).equals(type)) { return Type.INT_TYPE; } if (Type.getType(Short.class).equals(type)) { return Type.SHORT_TYPE; } if (Type.getType(Byte.class).equals(type)) { return Type.BYTE_TYPE; } if (Type.getType(Character.class).equals(type)) { return Type.CHAR_TYPE; } if (Type.getType(Boolean.class).equals(type)) { return Type.BOOLEAN_TYPE; } return type; } /** * Initializes a classField of type Array in the default lastConstructor. * The array is initialized with the given Strings. * * @param values * the values * @return the abstract optimizer test * @see AbstractOptimizerTest#initArrayInConstructor(String, int) */ public ClassNodeBuilder initArrayWith(final String... values) { if (isInterface) { return this; } initArrayInternal(Opcodes.AASTORE, (Object[]) values); return this; } private void initArrayInternal(final int opcode, final Object... values) { final InsnList list = new InsnList(); final int length = values.length; initArray(length); int index = 0; for (final Object number : values) { list.add(new VarInsnNode(Opcodes.ALOAD, lastConstructorVarIndex)); final AbstractInsnNode indexNode = NodeHelper.getInsnNodeFor(index++); list.add(indexNode); final AbstractInsnNode numberNode = NodeHelper.getInsnNodeFor(number); list.add(numberNode); if (number instanceof Number && opcode == Opcodes.ASTORE) { list.add(ConstructorBuilder.getBoxingNode(lastField)); } list.add(new InsnNode(opcode)); } addToConstructor(list); } /** * Initializes a classField of type Array in the default lastConstructor with the given length. * (eg: if fieldType of field "field" is "double[]", field = new double[length] is called). * * @param length * the length * @return the abstract optimizer test */ public ClassNodeBuilder initArray(final int... length) { if (isInterface) { return this; } final InsnList list = new InsnList(); list.add(new VarInsnNode(Opcodes.ALOAD, 0)); final AbstractInsnNode node; if (length.length == 1) { final Type elementType = Type.getType(lastField.desc).getElementType(); list.add(NodeHelper.getInsnNodeFor(Integer.valueOf(length[0]))); if (elementType.getDescriptor().startsWith("L")) { node = new TypeInsnNode(Opcodes.ANEWARRAY, elementType.getInternalName()); } else { node = new IntInsnNode(Opcodes.NEWARRAY, getSort(elementType)); } } else { for (final int currentLength : length) { list.add(NodeHelper.getInsnNodeFor(Integer.valueOf(currentLength))); } node = new MultiANewArrayInsnNode(lastField.desc, length.length); } list.add(node); list.add(new VarInsnNode(Opcodes.ASTORE, constructorVarIndex)); list.add(new VarInsnNode(Opcodes.ALOAD, constructorVarIndex)); lastConstructorVarIndex = constructorVarIndex; constructorVarIndex++; list.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, lastField.name, lastField.desc)); addToConstructor(list); return this; } /** * Adds the array to the last created method. * * @param desc * the desc * @param length * the length * @return the class node builder */ public ClassNodeBuilder addArray(final String desc, final int... length) { if (isInterface) { return this; } final Type elementType = Type.getType(desc).getElementType(); if (length.length == 1) { addInsn(NodeHelper.getInsnNodeFor(Integer.valueOf(length[0]))); if (elementType.getDescriptor().startsWith("L")) { addInsn(new TypeInsnNode(Opcodes.ANEWARRAY, elementType.getInternalName())); } else { addInsn(new IntInsnNode(Opcodes.NEWARRAY, getSort(elementType))); } } else { for (final int currentLength : length) { addInsn(NodeHelper.getInsnNodeFor(Integer.valueOf(currentLength))); } addInsn(new MultiANewArrayInsnNode(desc, length.length)); } addInsn(new VarInsnNode(Opcodes.ASTORE, methodVarIndex)); lastMethodVarIndex = methodVarIndex; lastVarElementType = elementType; methodVarIndex++; return this; } /** * Stores the given value of type Number or String in the array created before at index indexes... * * @param value * the value * @param indexes * the indexes * @return the class node builder */ public ClassNodeBuilder withValue(final Object value, final int... indexes) { if (isInterface) { return this; } addInsn(new VarInsnNode(Opcodes.ALOAD, lastMethodVarIndex)); for (int i = 0; i < indexes.length - 1; ++i) { addInsn(NodeHelper.getInsnNodeFor(indexes[i])); addInsn(new InsnNode(Opcodes.AALOAD)); } addInsn(NodeHelper.getInsnNodeFor(indexes[indexes.length - 1])); addInsn(NodeHelper.getInsnNodeFor(value)); if (lastVarElementType.getDescriptor().startsWith("L")) { addInsn(new InsnNode(Opcodes.AASTORE)); } else { addInsn(new InsnNode(toPrimitive(Type.getType(value.getClass())).getOpcode(Opcodes.IASTORE))); } return this; } private int getSort(final Type type) { final int sort = type.getSort(); switch (sort) { case Type.INT: return Opcodes.T_INT; case Type.FLOAT: return Opcodes.T_FLOAT; case Type.LONG: return Opcodes.T_LONG; case Type.DOUBLE: return Opcodes.T_DOUBLE; case Type.SHORT: return Opcodes.T_SHORT; case Type.CHAR: return Opcodes.T_CHAR; case Type.BOOLEAN: return Opcodes.T_BOOLEAN; case Type.BYTE: return Opcodes.T_BYTE; default: return -1; } } /** * Adds the given node to the lastConstructor. * * @param node * the node * @return the class node builder */ public ClassNodeBuilder addToConstructor(final AbstractInsnNode node) { if (isInterface) { return this; } final AbstractInsnNode returnNode = lastConstructor.instructions.getLast(); if (returnNode == null) { lastConstructor.instructions.add(node); } else { lastConstructor.instructions.insertBefore(returnNode, node); } return this; } /** * Adds the given nodes to the lastConstructor. * * @param nodes * the nodes * @return the class node builder */ public ClassNodeBuilder addToConstructor(final InsnList nodes) { if (isInterface) { return this; } final AbstractInsnNode returnNode = lastConstructor.instructions.getLast(); if (returnNode == null) { lastConstructor.instructions.add(nodes); } else { lastConstructor.instructions.insertBefore(returnNode, nodes); } return this; } /** * adds a the given instruction to the method that was created last via {@link #addMethod(String, String)}. * * @param node * the node * @return the abstract optimizer test */ public ClassNodeBuilder addInsn(final AbstractInsnNode node) { if (isInterface) { return this; } if ("<init>".equals(lastMethod.name)) { addToConstructor(node); } else { lastMethod.instructions.add(node); } return this; } /** * adds a the given instructions to the method that was created last via {@link #addMethod(String, String)}. * * @param nodes * the nodes * @return this ClassNodeBuilder */ public ClassNodeBuilder addInsn(final InsnList nodes) { if (isInterface) { return this; } if (lastMethod == null) { addToConstructor(nodes); } else { lastMethod.instructions.add(nodes); } return this; } /** * adds a FieldInsnNode for the given Field. * * @param field * the field * @return the abstract optimizer test */ public ClassNodeBuilder addGetClassField(final String field) { addInsn(new VarInsnNode(Opcodes.ALOAD, 0)); return addGetField(this, field); } /** * adds a FieldInsnNode for the given Field. * * @param builder * the builder * @param field * the field * @return the abstract optimizer test */ public ClassNodeBuilder addGetField(final ClassNodeBuilder builder, final String field) { final FieldNode fieldNode = builder.getField(field); return addGetField(builder.getClassNode().name, fieldNode.name, fieldNode.desc); } /** * adds a FieldInsnNode for the given Field. * * @param builder * the builder * @param field * the field * @return the abstract optimizer test */ public ClassNodeBuilder addGetField(final String owner, final String field, final String desc) { final FieldInsnNode node = new FieldInsnNode(Opcodes.GETFIELD, owner, field, desc); return addInsn(node); } /** * adds a FieldInsnNode for the given Field. * * @param field * the field * @return the abstract optimizer test */ public ClassNodeBuilder addPutClassField(final String field) { return addPutField(this, field); } /** * adds a FieldInsnNode for the given Field. * * @param builder * the builder * @param field * the field * @return this ClassNodeBuilder */ public ClassNodeBuilder addPutField(final ClassNodeBuilder builder, final String field) { final FieldNode fieldNode = builder.getField(field); return addPutField(builder.classNode.name, field, fieldNode.desc); } /** * Adds the put field. * * @param owner * the owner * @param name * the name * @param desc * the desc * @return the class node builder */ public ClassNodeBuilder addPutField(final String owner, final String name, final String desc) { return addInsn(new FieldInsnNode(PUTFIELD, owner, name, desc)); } /** * Load field array value by getting the field and than loading the value. * * @param field * the field * @param index * the index * @return the class node builder */ public ClassNodeBuilder loadFieldArrayValue(final String field, final int index) { addGetClassField(field); return addArrayLoad(field, index); } /** * Adds the array load for the specific index. * * @param field * the field * @param index * the index * @return the class node builder */ public ClassNodeBuilder addArrayLoad(final String field, final int index) { final FieldNode fieldNode = getField(field); addInsn(NodeHelper.getInsnNodeFor(index)); Type elementType = Type.getType(fieldNode.desc).getElementType(); if (fieldNode.desc.startsWith("[[")) { elementType = Type.getType(Object.class); } return addInsn(new InsnNode(elementType.getOpcode(Opcodes.IALOAD))); } /** * get the first MethodNode with the given Name. * * @param name * the name * @return the method */ public MethodNode getMethod(final String name) { return getMethod(name, null); } /** * gets the lastConstructor with the given descriptor. * * @param desc * the desc * @return the constructor */ public MethodNode getConstructor(final String desc) { return getMethod("<init>", desc); } /** * get the MethodNode with the given Name and descriptor. * * @param name * the name * @param desc * the methoddescriptor * @return the method */ public MethodNode getMethod(final String name, final String desc) { for (final MethodNode method : classNode.methods) { if (name.equals(method.name)) { if (StringUtils.isBlank(desc)) { return method; } if (method.desc.equals(desc)) { return method; } } } return null; } /** * get the FieldNode with the given name. * * @param name * the name * @return the field */ public FieldNode getField(final String name) { if (isInterface) { return null; } for (final FieldNode field : classNode.fields) { if (name.equals(field.name)) { return field; } } return null; } /** * Create the ClassObject. * * @return the abstract optimizer test * @throws Exception * the exception */ public ClassNodeBuilder toClass() throws Exception { final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); classNode.accept(writer); final ClassDescriptor descriptor = new ClassDescriptor(classNode.name.replace("/", "."), writer.toByteArray(), "NoFile"); ClassAccessor.store(descriptor); buildedClass = ClassAccessor.getClassloader().loadClass(descriptor.getName()); return this; } /** * Gets the builded class. * * @return the builded class */ public Class<?> getBuildedClass() throws Exception { if (buildedClass == null) { toClass(); } return buildedClass; } /** * Instantiate the classObject. * * @param params * the params * @return the object * @throws Exception * the exception */ public Object instance(final Object... params) throws Exception { if (buildedClass == null) { toClass(); } return ConstructorUtils.invokeConstructor(buildedClass, params); } /** * Gets the class node. * * @return the class node */ public ClassNode getClassNode() { return classNode; } /** * Select the Method as being "active". * * @param name * the name * @param desc * the desc * @return the class node builder */ public ClassNodeBuilder selectMethod(final String name, final String desc) { setLastMethod(getMethod(name, desc)); return this; } /** * Select the given lastConstructor as being "active". * * @param desc * the desc * @return the class node builder */ public ClassNodeBuilder selectConstructor(final String desc) { setLastMethod(getMethod("<init>", desc)); return this; } private void setLastMethod(final MethodNode method) { if ("<init>".equals(method.name)) { lastConstructor = method; lastMethod = method; } else { lastMethod = method; } lastElement = method; } /** * Loads the given method-local variable. * * @param index * the index * @return the class node builder */ public ClassNodeBuilder load(final int index) { if (index == 0 && (lastMethod.access & ACC_STATIC) == 0) { addInsn(new VarInsnNode(ALOAD, 0)); return this; } addInsn(new VarInsnNode(getType(index).getOpcode(ILOAD), getVarIndex(index))); return this; } /** * Load the variable index of given type. * * @param type * the type * @param index * the index * @return the class node builder */ public ClassNodeBuilder load(final Type type, final int index) { addInsn(new VarInsnNode(type.getOpcode(ILOAD), index)); return this; } /** * Stores the given method-local variable. * * @param index * the index * @return the class node builder */ public ClassNodeBuilder store(final int index) { final Type type = getType(index); return store(type, index); } /** * Stores the given variable index of type type. * * @param type * the type * @param index * the index * @return the class node builder */ public ClassNodeBuilder store(final Type type, final int index) { addInsn(new VarInsnNode(type.getOpcode(ISTORE), getVarIndex(index))); return this; } /** * Adds the return statement. * * @return the class node builder */ public ClassNodeBuilder addReturn() { addInsn(new InsnNode(Type.getReturnType(lastMethod.desc).getOpcode(IRETURN))); return this; } /** * Adds the given operation. * * @param opcode * the opcode * @param args * the args * @return the class node builder */ public ClassNodeBuilder add(final int opcode, final Object... args) { if (opcode == IINC) { return addInsn(new IincInsnNode((Integer) args[0], (Integer) args[1])); } if (opcode >= NOP && opcode <= DCONST_1 || opcode >= POP && opcode <= DCMPG || opcode >= IALOAD && opcode <= SALOAD || opcode >= IASTORE && opcode <= SASTORE || opcode == ARRAYLENGTH || opcode == ATHROW) { return addInsn(new InsnNode(opcode)); } if (opcode >= BIPUSH && opcode <= SIPUSH || opcode == NEWARRAY) { return addInsn(new IntInsnNode(opcode, (Integer) args[0])); } if (opcode == LDC) { return loadConstant(args[0]); } if (opcode >= ILOAD && opcode <= ALOAD) { return addInsn(new VarInsnNode(opcode, (Integer) args[0])); } if (opcode >= ISTORE && opcode <= ASTORE) { return addInsn(new VarInsnNode(opcode, (Integer) args[0])); } if (opcode >= IFEQ && opcode <= JSR) { return jump(opcode, (LabelNode) args[0]); } return this; } /** * Load constant. * * @param arg * the arg * @return the class node builder */ public ClassNodeBuilder loadConstant(final Object arg) { return addInsn(new LdcInsnNode(arg)); } /** * Jump. * * @param opcode * the opcode * @param labelNode * the label node * @return the class node builder */ public ClassNodeBuilder jump(final int opcode, final LabelNode labelNode) { return addInsn(new JumpInsnNode(opcode, labelNode)); } private int getVarIndex(final int index) { int varIndex = shiftStaticIndex(index); final Type[] parameterTypes = Type.getArgumentTypes(lastMethod.desc); if (parameterTypes.length <= index) { return index; } for (int i = 0; i < index; ++i) { final Type pType = parameterTypes[i]; varIndex += pType.getSize(); } return varIndex; } private int shiftStaticIndex(final int index) { int varIndex = index; if ((lastMethod.access & ACC_STATIC) == 0) { varIndex--; } return varIndex; } private Type getType(final int index) { final int varIndex = shiftStaticIndex(index); final Type[] parameterTypes = Type.getMethodType(lastMethod.desc).getArgumentTypes(); if (parameterTypes.length <= varIndex) { return Type.INT_TYPE; } final Type type = parameterTypes[varIndex]; return type; } /** * Invoke. * * @param opcode * the opcode * @param method * the method * @param args * the args * @return the class node builder */ public ClassNodeBuilder invoke(final int opcode, final String method, final Number... args) { add(ALOAD, 0); return invoke(opcode, this, method, args); } /** * Invoke. * * @param opcode * the opcode * @param classBuilder * the class builder * @param method * the method * @param args * the args * @return the class node builder */ public ClassNodeBuilder invoke(final int opcode, final ClassNodeBuilder classBuilder, final String method, final Number... args) { return invoke(opcode, classBuilder.classNode.name, method, classBuilder.getMethod(method).desc, args); } /** * Invoke. * * @param opcode * the opcode * @param owner * the owner * @param method * the method * @param desc * the desc * @param args * the args * @return the class node builder */ public ClassNodeBuilder invoke(final int opcode, final String owner, final String method, final String desc, final Number... args) { if (args != null) { for (final Object arg : args) { add(((Number) arg).intValue()); } } return addInsn(new MethodInsnNode(opcode, owner, method, desc)); } /** * Gets the desc. * * @return the desc */ public String getDesc() { return classNode.name; } /** * Implement interface. * * @param builder * the builder * @return the class node builder */ public ClassNodeBuilder implementInterface(final ClassNodeBuilder builder) { return implementInterface(builder.getDesc()); } /** * Implement interface. * * @param desc * the desc * @return the class node builder */ public ClassNodeBuilder implementInterface(final String desc) { classNode.interfaces.add(desc); return this; } /** * With signature. * * @param signature * the signature * @return the class node builder */ public ClassNodeBuilder withSignature(final String signature) { classNode.signature = signature; return this; } /** * Removes the method from this class. */ public ClassNodeBuilder removeMethod(final String name, final String desc) { final MethodNode method = getMethod(name, desc); classNode.methods.remove(method); return this; } }