public FieldGranularityTransformer( ClassLoader classLoader, ClassNode classNode, MetadataRepository metadataRepository) { if (classNode == null) { throw new RuntimeException(); } this.metadataRepository = metadataRepository; this.classLoader = classLoader; this.classNode = classNode; this.classMetadata = metadataRepository.loadClassMetadata(classLoader, classNode.name); }
private InsnList fixInstructions(MethodNode originalMethod, CloneMap cloneMap) { InsnList instructions = new InsnList(); for (int k = 0; k < originalMethod.instructions.size(); k++) { AbstractInsnNode originalInsn = originalMethod.instructions.get(k); switch (originalInsn.getOpcode()) { // the put on the field granular field is transformed to a fieldref.set case PUTFIELD: { FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn; ClassMetadata ownerMetadata = metadataRepository.loadClassMetadata(classLoader, fieldInsn.owner); FieldMetadata fieldMetadata = ownerMetadata.getFieldMetadata(fieldInsn.name); Type originalFieldType = Type.getType(fieldMetadata.getDesc()); if (fieldMetadata.hasFieldGranularity()) { boolean fieldIsCategory2 = isCategory2(fieldMetadata.getDesc()); if (fieldIsCategory2) { // value(category2), owner,.. instructions.add(new InsnNode(DUP2_X1)); // [value(category2), owner, value(category2),...] instructions.add(new InsnNode(POP2)); // [owner, value(category2), ...] } else { // [value(category1), owner, instructions.add(new InsnNode(SWAP)); // [owner, value(category1),.. } String referenceDesc = findReferenceDesc(fieldMetadata.getDesc()); String referenceName = Type.getType(referenceDesc).getInternalName(); instructions.add( new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc)); if (fieldIsCategory2) { // [owner, value(category2),.. instructions.add(new InsnNode(DUP_X2)); // [owner, value(category2), owner instructions.add(new InsnNode(POP)); // [value(category2), owner } else { // [owner, value(category1) instructions.add(new InsnNode(SWAP)); // [value(category1), owner.. } // call the set if (originalFieldType.getSort() == Type.ARRAY || originalFieldType.getSort() == Type.OBJECT) { String objectDesc = Type.getDescriptor(Object.class); MethodInsnNode methodInsn = new MethodInsnNode( INVOKEVIRTUAL, referenceName, "set", format("(%s)%s", objectDesc, objectDesc)); instructions.add(methodInsn); } else { MethodInsnNode methodInsn = new MethodInsnNode( INVOKEVIRTUAL, referenceName, "set", format("(%s)%s", fieldMetadata.getDesc(), fieldMetadata.getDesc())); instructions.add(methodInsn); } // pop the unused return value of the set. if (fieldIsCategory2) { instructions.add(new InsnNode(POP2)); } else { instructions.add(new InsnNode(POP)); } } else { instructions.add(originalInsn.clone(cloneMap)); } } break; // the get on the field granular field is transformed to a fieldref.get case GETFIELD: { FieldInsnNode fieldInsn = (FieldInsnNode) originalInsn; FieldMetadata fieldMetadata = metadataRepository .loadClassMetadata(classLoader, fieldInsn.owner) .getFieldMetadata(fieldInsn.name); if (!fieldMetadata.hasFieldGranularity()) { // if it is not getter on a field granular field instructions.add(originalInsn.clone(cloneMap)); } else { // it is a getter on a field granular field. String referenceDesc = findReferenceDesc(fieldMetadata.getDesc()); String referenceName = Type.getType(referenceDesc).getInternalName(); // place the fieldref on the stack. instructions.add( new FieldInsnNode(GETFIELD, fieldInsn.owner, fieldInsn.name, referenceDesc)); Type originalFieldType = Type.getType(fieldMetadata.getDesc()); if (originalFieldType.getSort() == Type.ARRAY || originalFieldType.getSort() == Type.OBJECT) { instructions.add( new MethodInsnNode( INVOKEVIRTUAL, referenceName, "get", format("()%s", getDescriptor(Object.class)))); if (!originalFieldType.equals(Type.getType(Object.class))) { instructions.add( new TypeInsnNode(CHECKCAST, originalFieldType.getInternalName())); } } else { instructions.add( new MethodInsnNode( INVOKEVIRTUAL, referenceName, "get", format("()%s", fieldMetadata.getDesc()))); } } } break; default: instructions.add(originalInsn.clone(cloneMap)); break; } } return instructions; }