package nebula.data.impl; import static org.objectweb.asm.Opcodes.ACC_BRIDGE; import static org.objectweb.asm.Opcodes.ACC_FINAL; import static org.objectweb.asm.Opcodes.ACC_NATIVE; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PROTECTED; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ARETURN; import static org.objectweb.asm.Opcodes.CHECKCAST; import static org.objectweb.asm.Opcodes.DLOAD; import static org.objectweb.asm.Opcodes.DRETURN; import static org.objectweb.asm.Opcodes.FLOAD; import static org.objectweb.asm.Opcodes.FRETURN; import static org.objectweb.asm.Opcodes.GETFIELD; import static org.objectweb.asm.Opcodes.ILOAD; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.IRETURN; import static org.objectweb.asm.Opcodes.LLOAD; import static org.objectweb.asm.Opcodes.LRETURN; import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.RETURN; import java.io.IOException; import nebula.data.Broker; import nebula.data.BrokerHandler; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.Method; class BrokerForClassClassVisitor extends ClassVisitor { final static String ActualInstanceFieldName = "_actualValue"; String clzInternalName; String targetTypeName; String targetTypeDescriptor; private boolean hasDefaultConstract; final static String brokerTypeName = Type.getInternalName(BrokerHandler.class); public BrokerForClassClassVisitor(int api) { super(api); } public BrokerForClassClassVisitor(int api, ClassVisitor cv, String name) { super(api, cv); this.clzInternalName = name; } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.targetTypeName = name; this.targetTypeDescriptor = "L" + this.targetTypeName + ";"; FieldVisitor fv; MethodVisitor mv; cv.visit(version, ACC_PUBLIC + ACC_SUPER, this.clzInternalName, "L" + this.targetTypeName + ";L" + brokerTypeName + "<L" + this.targetTypeName + ";>;", this.targetTypeName, new String[] { brokerTypeName }); { fv = cv.visitField(ACC_PROTECTED, "_actualValue", this.targetTypeDescriptor, null, null); fv.visitEnd(); } { fv = cv.visitField(ACC_PROTECTED, "_listeners", "Ljava/util/List;", "Ljava/util/List<Lnebula/data/DataWatcher<" + this.targetTypeDescriptor + ">;>;", null); fv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, this.targetTypeName, "<init>", "()V"); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC, "get", "()" + this.targetTypeDescriptor, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } ClassReader cr2; try { cr2 = new ClassReader(Broker.class.getName()); } catch (IOException e) { throw new RuntimeException(e); } BrokerInClassVisitor bw2 = new BrokerInClassVisitor(Opcodes.ASM4, cv, name); cr2.accept(bw2, 0); { mv = cv.visitMethod(ACC_PUBLIC, "getActualValue", "()" + this.targetTypeDescriptor, null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_actualValue", this.targetTypeDescriptor); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC, "addWatcher", "(Lnebula/data/DataWatcher;)V", "(Lnebula/data/DataWatcher<" + this.targetTypeDescriptor + ">;)V", null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_listeners", "Ljava/util/List;"); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_actualValue", this.targetTypeDescriptor); mv.visitMethodInsn(INVOKESPECIAL, this.clzInternalName, "_addWatcher", "(Ljava/util/List;Lnebula/data/DataWatcher;Ljava/lang/Object;)Ljava/util/List;"); mv.visitFieldInsn(PUTFIELD, this.clzInternalName, "_listeners", "Ljava/util/List;"); mv.visitInsn(RETURN); mv.visitMaxs(5, 2); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC, "setNewValue", "(" + this.targetTypeDescriptor + ")V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, this.clzInternalName, "_actualValue", this.targetTypeDescriptor); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_listeners", "Ljava/util/List;"); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_actualValue", this.targetTypeDescriptor); mv.visitMethodInsn(INVOKESPECIAL, this.clzInternalName, "_notify", "(Ljava/util/List;Ljava/lang/Object;Ljava/lang/Object;)V"); mv.visitInsn(RETURN); mv.visitMaxs(4, 2); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "get", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, this.clzInternalName, "get", "()" + this.targetTypeDescriptor); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "getActualValue", "()Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, this.clzInternalName, "getActualValue", "()" + this.targetTypeDescriptor); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cv.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "setNewValue", "(Ljava/lang/Object;)V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, this.targetTypeName); mv.visitMethodInsn(INVOKEVIRTUAL, this.clzInternalName, "setNewValue", "(" + this.targetTypeDescriptor + ")V"); mv.visitInsn(RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); } } @Override public void visitSource(String source, String debug) { String newSourceName = this.clzInternalName + ".java"; cv.visitSource(newSourceName, debug); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = null; { if (name.equals("<init>")) { if ("()V".equals(desc)) { this.hasDefaultConstract = true; } } else if (!name.equals("<clinit>") && (access & (ACC_STATIC | ACC_PRIVATE | ACC_SYNTHETIC | ACC_NATIVE | ACC_BRIDGE)) == 0) { if ((access & ACC_FINAL) != 0) throw new RuntimeException("new FinalModifierException(superToCopy, name)"); mv = cv.visitMethod(ACC_PUBLIC, name, desc, signature, exceptions); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, this.clzInternalName, "_actualValue", this.targetTypeDescriptor); mv.visitTypeInsn(CHECKCAST, this.targetTypeName); Method currentTransformMethod = new Method(name, desc); delegateCall(mv, currentTransformMethod, this.targetTypeName); mv.visitMaxs(2, 2); mv.visitEnd(); } } return null; } /** * This method loads this, any args, then invokes the super version of this */ private final void delegateCall(MethodVisitor mv, Method currentTransformMethod, String typeName) { int nargs = currentTransformMethod.getArgumentTypes().length; for (int i = 1; i <= nargs; i++) { switch (currentTransformMethod.getArgumentTypes()[i - 1].getSort()) { case (Type.BOOLEAN): case (Type.BYTE): case (Type.CHAR): case (Type.SHORT): case (Type.INT): mv.visitVarInsn(ILOAD, i); break; case (Type.FLOAT): mv.visitVarInsn(FLOAD, i); break; case (Type.DOUBLE): mv.visitVarInsn(DLOAD, i); break; case (Type.LONG): mv.visitVarInsn(LLOAD, i); break; default: mv.visitVarInsn(ALOAD, i); } } mv.visitMethodInsn(INVOKEVIRTUAL, typeName, currentTransformMethod.getName(), currentTransformMethod.getDescriptor()); switch (currentTransformMethod.getReturnType().getSort()) { case (Type.BOOLEAN): case (Type.BYTE): case (Type.CHAR): case (Type.SHORT): case (Type.INT): mv.visitInsn(IRETURN); break; case (Type.VOID): mv.visitInsn(RETURN); break; case (Type.FLOAT): mv.visitInsn(FRETURN); break; case (Type.DOUBLE): mv.visitInsn(DRETURN); break; case (Type.LONG): mv.visitInsn(LRETURN); break; default: mv.visitInsn(ARETURN); } } @Override public void visitEnd() { if (!this.hasDefaultConstract) { throw new RuntimeException("cannot support class with default(no arguement) constructor"); } cv.visitEnd(); } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { // return cv.visitField(access, name, desc, signature, value); return null; } static class BrokerInClassVisitor extends ClassVisitor { public BrokerInClassVisitor(int api) { super(api); } public BrokerInClassVisitor(int api, ClassVisitor cv, String name) { super(api, cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (!name.equals("<init>") && !name.equals("<clinit>") && (access & ACC_PRIVATE) != 0 && (access & (ACC_STATIC | ACC_SYNTHETIC | ACC_NATIVE | ACC_BRIDGE)) == 0) { return cv.visitMethod(access, name, desc, signature, exceptions); } else { return null; } } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { } @Override public void visitSource(String source, String debug) { } @Override public void visitOuterClass(String owner, String name, String desc) { } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } @Override public void visitAttribute(Attribute attr) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } @Override public void visitEnd() { } } }