Search in sources :

Example 6 with BaseClassData

use of org.fakereplace.data.BaseClassData in project fakereplace by fakereplace.

the class ConstructorInvocationManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<ConstructorRewriteData>> constructorRewrites = new HashMap<>(data.getManipulationData(loader));
    Map<Integer, ConstructorRewriteData> methodCallLocations = new HashMap<>();
    // first we need to scan the constant pool looking for
    // CONSTANT_method_info_ref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a method call
        if (pool.getTag(i) == ConstPool.CONST_Methodref) {
            boolean handled = false;
            String className = pool.getMethodrefClassName(i);
            String methodDesc = pool.getMethodrefType(i);
            String methodName = pool.getMethodrefName(i);
            if (methodName.equals("<init>")) {
                if (constructorRewrites.containsKey(className)) {
                    for (ConstructorRewriteData data : constructorRewrites.get(className)) {
                        if (methodDesc.equals(data.getMethodDesc())) {
                            // store the location in the const pool of the method ref
                            methodCallLocations.put(i, data);
                            // we have found a method call
                            // now lets replace it
                            handled = true;
                            break;
                        }
                    }
                }
                if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
                    // may be an added field
                    // if the field does not actually exist yet we just assume it is about to come into existence
                    // and rewrite it anyway
                    BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                    if (data != null) {
                        MethodData method = data.getMethodOrConstructor("<init>", methodDesc);
                        if (method == null) {
                            // this is a new method
                            // lets deal with it
                            int methodNo = MethodIdentifierStore.instance().getMethodNumber("<init>", methodDesc);
                            methodCallLocations.put(i, new ConstructorRewriteData(className, methodDesc, methodNo, loader));
                        }
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!methodCallLocations.isEmpty()) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a method invocation
                    if (op == CodeIterator.INVOKESPECIAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.containsKey(val)) {
                            ConstructorRewriteData data = methodCallLocations.get(val);
                            // so we currently have all the arguments sitting on the
                            // stack, and we need to jigger them into
                            // an array and then call our method. First thing to do
                            // is scribble over the existing
                            // instructions:
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            Bytecode bc = new Bytecode(file.getConstPool());
                            ManipulationUtils.pushParametersIntoArray(bc, data.getMethodDesc());
                            // so now our stack looks like unconstructed instance : array
                            // we need unconstructed instance : int : array : null
                            bc.addIconst(data.getMethodNo());
                            bc.add(Opcode.SWAP);
                            bc.add(Opcode.ACONST_NULL);
                            bc.addInvokespecial(data.getClazz(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
                            // and we have our bytecode
                            it.insert(bc.get());
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodData(org.fakereplace.data.MethodData) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Example 7 with BaseClassData

use of org.fakereplace.data.BaseClassData in project fakereplace by fakereplace.

the class FieldManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<Data>> addedFieldData = data.getManipulationData(loader);
    if (addedFieldData.isEmpty()) {
        return false;
    }
    Map<Integer, Data> fieldAccessLocations = new HashMap<>();
    // first we need to scan the constant pool looking for
    // CONST_Fieldref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a field reference
        if (pool.getTag(i) == ConstPool.CONST_Fieldref) {
            String className = pool.getFieldrefClassName(i);
            String fieldName = pool.getFieldrefName(i);
            String descriptor = pool.getFieldrefType(i);
            boolean handled = false;
            if (addedFieldData.containsKey(className)) {
                for (Data data : addedFieldData.get(className)) {
                    if (fieldName.equals(data.getName())) {
                        // store the location in the const pool of the method ref
                        fieldAccessLocations.put(i, data);
                        handled = true;
                        break;
                    }
                }
            }
            if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
                // may be an added field
                // if the field does not actually exist yet we just assume it is about to come into existence
                // and rewrite it anyway
                BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                if (data != null) {
                    FieldData field = data.getField(fieldName);
                    if (field == null) {
                        // this is a new field
                        // lets deal with it
                        int fieldNo = FieldReferenceDataStore.instance().getFieldNo(fieldName, descriptor);
                        Data fieldData = new Data(fieldNo, fieldName, descriptor, className, loader);
                        fieldAccessLocations.put(i, fieldData);
                        Transformer.getManipulator().rewriteInstanceFieldAccess(fieldNo, fieldName, descriptor, className, loader);
                        addedFieldData = this.data.getManipulationData(loader);
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!fieldAccessLocations.isEmpty()) {
        List<MethodInfo> methods = file.getMethods();
        for (MethodInfo m : methods) {
            try {
                // ignore abstract methods
                if (m.getCodeAttribute() == null) {
                    continue;
                }
                CodeIterator it = m.getCodeAttribute().iterator();
                while (it.hasNext()) {
                    // loop through the bytecode
                    int index = it.next();
                    int op = it.byteAt(index);
                    // if the bytecode is a field access
                    if (op == Opcode.PUTFIELD || op == Opcode.GETFIELD || op == Opcode.GETSTATIC || op == Opcode.PUTSTATIC) {
                        int val = it.s16bitAt(index + 1);
                        // if the field access is for an added field
                        if (fieldAccessLocations.containsKey(val)) {
                            Data data = fieldAccessLocations.get(val);
                            int arrayPos = file.getConstPool().addIntegerInfo(data.getArrayIndex());
                            // write over the field access with nop
                            it.writeByte(Opcode.NOP, index);
                            it.writeByte(Opcode.NOP, index + 1);
                            it.writeByte(Opcode.NOP, index + 2);
                            if (op == Opcode.PUTFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            } else if (op == Opcode.PUTSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.add(Opcode.SWAP);
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            }
                            modifiedMethods.add(m);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
                e.printStackTrace();
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) FieldData(org.fakereplace.data.FieldData) FieldData(org.fakereplace.data.FieldData) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Aggregations

BaseClassData (org.fakereplace.data.BaseClassData)7 HashSet (java.util.HashSet)4 MethodInfo (javassist.bytecode.MethodInfo)4 IOException (java.io.IOException)3 HashMap (java.util.HashMap)3 Set (java.util.Set)3 Bytecode (javassist.bytecode.Bytecode)3 CodeIterator (javassist.bytecode.CodeIterator)3 ConstPool (javassist.bytecode.ConstPool)3 MethodData (org.fakereplace.data.MethodData)3 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)2 AnnotationsAttribute (javassist.bytecode.AnnotationsAttribute)2 DuplicateMemberException (javassist.bytecode.DuplicateMemberException)2 FieldData (org.fakereplace.data.FieldData)2 ByteArrayInputStream (java.io.ByteArrayInputStream)1 DataInputStream (java.io.DataInputStream)1 DataOutputStream (java.io.DataOutputStream)1 FileOutputStream (java.io.FileOutputStream)1 ClassDefinition (java.lang.instrument.ClassDefinition)1 UnmodifiableClassException (java.lang.instrument.UnmodifiableClassException)1