Search in sources :

Example 6 with FieldData

use of org.fakereplace.data.FieldData 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

FieldData (org.fakereplace.data.FieldData)6 ClassData (org.fakereplace.data.ClassData)4 Field (java.lang.reflect.Field)3 ArrayList (java.util.ArrayList)2 BaseClassData (org.fakereplace.data.BaseClassData)2 IOException (java.io.IOException)1 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 LinkedHashSet (java.util.LinkedHashSet)1 Set (java.util.Set)1 Bytecode (javassist.bytecode.Bytecode)1 CodeIterator (javassist.bytecode.CodeIterator)1 ConstPool (javassist.bytecode.ConstPool)1 DuplicateMemberException (javassist.bytecode.DuplicateMemberException)1 FieldInfo (javassist.bytecode.FieldInfo)1 MethodInfo (javassist.bytecode.MethodInfo)1