Java tutorial
/******************************************************************************* * Copyright (c) 2007, 2011 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.binding.reflection; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.simantics.databoard.binding.error.BindingConstructionException; public class AsmBindingClassLoader extends ClassLoader implements Opcodes { Map<String, Class<?>> map = new HashMap<String, Class<?>>(); public AsmBindingClassLoader() { super(Thread.currentThread().getContextClassLoader()); } public AsmBindingClassLoader(ClassLoader parent) { super(parent); } public String toBindingClassName(String targetClassName) { if (targetClassName.startsWith("java")) { return "x" + targetClassName + ".Binding"; } return targetClassName + ".Binding"; } public String toTargetClassName(String bindingClassName) { if (!bindingClassName.endsWith(".Binding")) return null; if (bindingClassName.substring(1, 5).equals("java")) { return bindingClassName.substring(1, bindingClassName.length() - 8); } else { return bindingClassName.substring(0, bindingClassName.length() - 8); } } @Override protected synchronized Class<?> findClass(String bindingClassName) throws ClassNotFoundException { Class<?> c = map.get(bindingClassName); if (c != null) return c; try { String targetClassName = toTargetClassName(bindingClassName); if (targetClassName == null) { // try { return super.findClass(bindingClassName); // } catch( ClassNotFoundException e ) { // e.printStackTrace(); // throw e; // } } ClassLoader cl = getParent(); if (cl == null) { cl = Thread.currentThread().getContextClassLoader(); } Class<?> targetClass = cl.loadClass(targetClassName); ClassInfo ci = ClassInfo.getInfo(targetClass); byte[] data = createBindingClass(ci, bindingClassName); Class<?> bindingClass = defineClass(bindingClassName, data, 0, data.length); map.put(bindingClassName, bindingClass); return bindingClass; } catch (BindingConstructionException e) { throw new ClassNotFoundException(e.getMessage(), e.getCause()); } } public synchronized Class<?> getBindingClass(Class<?> targetClass) throws ClassNotFoundException { String targetClassName = targetClass.getName(); String bindingClassName = toBindingClassName(targetClassName); Class<?> c = map.get(bindingClassName); if (c != null) return c; try { ClassInfo ci = ClassInfo.getInfo(targetClass); byte[] data = createBindingClass(ci, bindingClassName); Class<?> bindingClass = defineClass(bindingClassName, data, 0, data.length); map.put(bindingClassName, bindingClass); return bindingClass; } catch (BindingConstructionException e) { throw new ClassNotFoundException(e.getMessage(), e.getCause()); } } public byte[] createBindingClass(ClassInfo ci, String bindingClassName) { //System.out.println("BindingFactory: "+bindingClassName+" (for "+ci.clazz.getClassLoader()+")"); int count = ci.fields.length; ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); ClassVisitor cv = cw;//new CheckClassAdapter(cw); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; String className = ci.clazz.getName().replaceAll("\\.", "/"); bindingClassName = bindingClassName.replaceAll("\\.", "/"); Object[] classNameX = new Object[] { className }; String signature = "L" + bindingClassName + ";"; // Constructor String superClass = "org/simantics/databoard/binding/reflection/ClassBinding"; cv.visit(V1_6, ACC_PUBLIC + ACC_SUPER, bindingClassName, null, superClass, null); // Constructor { mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(Lorg/simantics/databoard/type/RecordType;)V", null, new String[] { "org/simantics/databoard/binding/error/BindingConstructionException" }); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitLdcInsn(Type.getType("L" + className + ";")); mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "(Ljava/lang/Class;)V"); Label l1 = new Label(); mv.visitLabel(l1); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, bindingClassName, "type", "Lorg/simantics/databoard/type/Datatype;"); Label l2 = new Label(); mv.visitLabel(l2); mv.visitInsn(RETURN); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLocalVariable("this", signature, null, l0, l3, 0); mv.visitLocalVariable("type", "Lorg/simantics/databoard/type/RecordType;", null, l0, l3, 1); mv.visitMaxs(2, 2); mv.visitEnd(); } // getComponent { mv = cv.visitMethod(ACC_PUBLIC, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 3); Label l1 = new Label(); mv.visitLabel(l1); Label caseLabels[] = createFieldLabels(ci); Label elseLabel = new Label(); if (count > 0) { // Switch mv.visitVarInsn(ILOAD, 2); mv.visitTableSwitchInsn(0, count - 1, elseLabel, caseLabels); // case i: x.field = value[i] for (int i = 0; i < count; i++) { Label label = caseLabels[i]; Field field = ci.fields[i]; String fieldName = field.getName(); Class<?> fieldClass = ci.fields[i].getType(); String typeDescriptor = toTypeDescriptor(fieldClass); Method getter = ci.getters[i]; boolean useGetter = ((field.getModifiers() & Modifier.PUBLIC) == 0) && getter != null; mv.visitLabel(label); if (i == 0) { mv.visitFrame(Opcodes.F_APPEND, 1, classNameX, 0, null); } else { mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } // Read instance argument mv.visitVarInsn(ALOAD, 3); if (useGetter) { // call getField mv.visitMethodInsn(INVOKEVIRTUAL, className, getter.getName(), "()" + typeDescriptor); } else { // Read field mv.visitFieldInsn(GETFIELD, className, fieldName, typeDescriptor); } // Box box(mv, fieldClass); mv.visitInsn(ARETURN); } } mv.visitLabel(elseLabel); if (count > 0) { mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } mv.visitTypeInsn(NEW, "org/simantics/databoard/binding/error/BindingException"); mv.visitInsn(DUP); mv.visitLdcInsn("Illegal field index"); mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/binding/error/BindingException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); // End Label l19 = new Label(); mv.visitLabel(l19); mv.visitLocalVariable("this", "L" + className + ";", null, l0, l19, 0); mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, l0, l19, 1); mv.visitLocalVariable("index", "I", null, l0, l19, 2); //mv.visitLocalVariable("x", "Lorg/simantics/databoard/binding/reflection/MyClass;", null, l1, l19, 3); mv.visitMaxs(3, 4); mv.visitEnd(); } // Create { mv = cv.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); if (ci.beanConstructor != null) { mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Lorg/simantics/databoard/binding/Binding;)V"); mv.visitVarInsn(ASTORE, 2); Label l1 = new Label(); mv.visitLabel(l1); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "setComponents", "(Ljava/lang/Object;[Ljava/lang/Object;)V"); Label l2 = new Label(); mv.visitLabel(l2); mv.visitVarInsn(ALOAD, 2); mv.visitInsn(ARETURN); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l3, 0); mv.visitLocalVariable("values", "[Ljava/lang/Object;", null, l0, l3, 1); //mv.visitLocalVariable("x", "L"+className+";", null, l1, l3, 2); mv.visitMaxs(3, 3); mv.visitEnd(); } else if (ci.argsConstructor != null) { mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); String constArgsDescriptor = "("; Class<?>[] args = ci.argsConstructor.getParameterTypes(); for (int i = 0; i < count; i++) { Label label = new Label(); Class<?> field = args[i]; String fieldName = field.getName(); Method getter = ci.getters[i]; Class<?> fieldClass = ci.fields[i].getType(); Class<?> boxClass = getBoxClass(fieldClass); String typeDescriptor = toTypeDescriptor(fieldClass); String boxTypeDescriptor = toTypeDescriptor(boxClass); constArgsDescriptor += typeDescriptor; mv.visitLabel(label); mv.visitVarInsn(ALOAD, 1); if (i < 6) { mv.visitInsn(ICONST_0 + i); } else { mv.visitIntInsn(BIPUSH, i); } mv.visitInsn(AALOAD); mv.visitTypeInsn(CHECKCAST, toClassCanonicalName(boxClass)); unbox(mv, fieldClass); } Label l17 = new Label(); mv.visitLabel(l17); constArgsDescriptor += ")V"; mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", constArgsDescriptor); mv.visitInsn(ARETURN); Label l18 = new Label(); mv.visitLabel(l18); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l18, 0); mv.visitLocalVariable("values", "[Ljava/lang/Object;", null, l0, l18, 1); mv.visitMaxs(21, 2); mv.visitEnd(); } else { mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V"); mv.visitVarInsn(ASTORE, 2); Label l1 = new Label(); mv.visitLabel(l1); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "setComponents", "(Ljava/lang/Object;[Ljava/lang/Object;)V"); Label l2 = new Label(); mv.visitLabel(l2); mv.visitVarInsn(ALOAD, 2); mv.visitInsn(ARETURN); Label l3 = new Label(); mv.visitLabel(l3); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l3, 0); mv.visitLocalVariable("values", "[Ljava/lang/Object;", null, l0, l3, 1); //mv.visitLocalVariable("x", "L"+className+";", null, l1, l3, 2); mv.visitMaxs(3, 3); mv.visitEnd(); } } // CreatePartial mv = cv.visitMethod(ACC_PUBLIC, "createPartial", "()Ljava/lang/Object;", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); if (ci.beanConstructor != null) { mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Lorg/simantics/databoard/binding/Binding;)V"); mv.visitInsn(ARETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l1, 0); mv.visitMaxs(3, 1); mv.visitEnd(); } else if (ci.noArgsConstructor != null) { // return new MyClass(); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitTypeInsn(NEW, className); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V"); mv.visitInsn(ARETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l1, 0); mv.visitMaxs(2, 1); mv.visitEnd(); } else { mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitIntInsn(BIPUSH, count); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(ASTORE, 1); Label l1 = new Label(); mv.visitLabel(l1); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 2); Label l2 = new Label(); mv.visitLabel(l2); Label l3 = new Label(); mv.visitJumpInsn(GOTO, l3); Label l4 = new Label(); mv.visitLabel(l4); mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] { "[Ljava/lang/Object;", Opcodes.INTEGER }, 0, null); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, bindingClassName, "componentBindings", "[Lorg/simantics/databoard/binding/Binding;"); mv.visitVarInsn(ILOAD, 2); mv.visitInsn(AALOAD); mv.visitVarInsn(ASTORE, 3); Label l5 = new Label(); mv.visitLabel(l5); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/Binding", "createDefault", "()Ljava/lang/Object;"); mv.visitInsn(AASTORE); Label l6 = new Label(); mv.visitLabel(l6); mv.visitIincInsn(2, 1); mv.visitLabel(l3); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(ILOAD, 2); mv.visitVarInsn(ALOAD, 1); mv.visitInsn(ARRAYLENGTH); mv.visitJumpInsn(IF_ICMPLT, l4); Label l7 = new Label(); mv.visitLabel(l7); mv.visitLineNumber(109, l7); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "create", "([Ljava/lang/Object;)Ljava/lang/Object;"); mv.visitInsn(ARETURN); Label l8 = new Label(); mv.visitLabel(l8); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, l8, 0); mv.visitLocalVariable("values", "[Ljava/lang/Object;", null, l1, l8, 1); mv.visitLocalVariable("i", "I", null, l2, l7, 2); mv.visitLocalVariable("fb", "Lorg/simantics/databoard/binding/Binding;", null, l5, l6, 3); mv.visitMaxs(3, 4); mv.visitEnd(); } // setComponent { mv = cv.visitMethod(ACC_PUBLIC, "setComponent", "(Ljava/lang/Object;ILjava/lang/Object;)V", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 4); Label endLabel = new Label(); Label l1 = new Label(); mv.visitLabel(l1); Label elseLabel = new Label(); Label labels[] = new Label[count]; for (int i = 0; i < count; i++) labels[i] = new Label(); if (count > 0) { mv.visitVarInsn(ILOAD, 2); mv.visitTableSwitchInsn(0, count - 1, elseLabel, labels); for (int i = 0; i < count; i++) { Label label = labels[i]; mv.visitLabel(label); Field field = ci.fields[i]; String fieldName = field.getName(); Class<?> fieldClass = ci.fields[i].getType(); Class<?> boxClass = getBoxClass(fieldClass); String typeDescriptor = toTypeDescriptor(fieldClass); String boxTypeDescriptor = toTypeDescriptor(boxClass); Method setter = ci.setters[i]; Class<?> setterClass = setter != null ? setter.getParameterTypes()[0] : null; boolean useSetter = ((field.getModifiers() & Modifier.PUBLIC) == 0) && setter != null; if (i == 0) { mv.visitFrame(Opcodes.F_APPEND, 1, classNameX, 0, null); } else { mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } mv.visitVarInsn(ALOAD, 4); mv.visitVarInsn(ALOAD, 3); mv.visitTypeInsn(CHECKCAST, toClassCanonicalName(boxClass)); if (useSetter) { unbox(mv, setterClass); mv.visitMethodInsn(INVOKEVIRTUAL, className, setter.getName(), "(" + typeDescriptor + ")V"); } else { unbox(mv, fieldClass); mv.visitFieldInsn(PUTFIELD, className, field.getName(), typeDescriptor); } mv.visitInsn(RETURN); } } mv.visitLabel(elseLabel); mv.visitLineNumber(178, elseLabel); if (count > 0) { mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); } mv.visitTypeInsn(NEW, "org/simantics/databoard/binding/error/BindingException"); mv.visitInsn(DUP); mv.visitLdcInsn("Illegal field index"); mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/binding/error/BindingException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); mv.visitLabel(endLabel); mv.visitLocalVariable("this", "L" + bindingClassName + ";", null, l0, endLabel, 0); mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, l0, endLabel, 1); mv.visitLocalVariable("index", "I", null, l0, endLabel, 2); mv.visitLocalVariable("value", "Ljava/lang/Object;", null, l0, endLabel, 3); //mv.visitLocalVariable("x", "L"+className+";", null, l1, endLabel, 4); mv.visitMaxs(3, 5); mv.visitEnd(); } // IsImmutable { mv = cv.visitMethod(ACC_PUBLIC, "isImmutable", "()Z", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitInsn(ICONST_0); mv.visitInsn(IRETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L" + className + ";", null, l0, l1, 0); mv.visitMaxs(1, 1); mv.visitEnd(); } // IsInstance { mv = cv.visitMethod(ACC_PUBLIC, "isInstance", "(Ljava/lang/Object;)Z", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(INSTANCEOF, className); mv.visitInsn(IRETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitLocalVariable("this", "L" + className + ";", null, l0, l1, 0); mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, l0, l1, 1); mv.visitMaxs(1, 2); mv.visitEnd(); } // SetComponents { mv = cv.visitMethod(ACC_PUBLIC + ACC_VARARGS, "setComponents", "(Ljava/lang/Object;[Ljava/lang/Object;)V", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 3); Label firstLabel = l0; for (int i = 0; i < count; i++) { Label label = new Label(); if (firstLabel == l0) firstLabel = label; Field field = ci.fields[i]; String fieldName = field.getName(); Class<?> fieldClass = ci.fields[i].getType(); Class<?> boxClass = getBoxClass(fieldClass); String typeDescriptor = toTypeDescriptor(fieldClass); String boxTypeDescriptor = toTypeDescriptor(boxClass); Method setter = ci.setters[i]; Class<?> setterClass = setter != null ? setter.getParameterTypes()[0] : null; boolean useSetter = ((field.getModifiers() & Modifier.PUBLIC) == 0) && setter != null; mv.visitLabel(label); mv.visitVarInsn(ALOAD, 3); mv.visitVarInsn(ALOAD, 2); if (i < 6) { mv.visitInsn(ICONST_0 + i); } else { mv.visitIntInsn(BIPUSH, i); } mv.visitInsn(AALOAD); mv.visitTypeInsn(CHECKCAST, toClassCanonicalName(boxClass)); if (useSetter) { unbox(mv, setterClass); mv.visitMethodInsn(INVOKEVIRTUAL, className, setter.getName(), "(" + typeDescriptor + ")V"); } else { unbox(mv, fieldClass); mv.visitFieldInsn(PUTFIELD, className, field.getName(), typeDescriptor); } } Label l17 = new Label(); mv.visitLabel(l17); mv.visitInsn(RETURN); Label endLabel = new Label(); mv.visitLabel(endLabel); mv.visitLocalVariable("this", "L" + className + ";", null, l0, endLabel, 0); mv.visitLocalVariable("obj", "Ljava/lang/Object;", null, l0, endLabel, 1); mv.visitLocalVariable("value", "[Ljava/lang/Object;", null, l0, endLabel, 2); //mv.visitLocalVariable("x", "Lorg/simantics/databoard/binding/reflection/MyClass;", null, firstLabel, endLabel, 3); mv.visitMaxs(3, 4); mv.visitEnd(); } // Add primitive setters { addGetSetPrimitive(ci, cv, "Boolean", "Z", bindingClassName); addGetSetPrimitive(ci, cv, "Byte", "B", bindingClassName); addGetSetPrimitive(ci, cv, "Int", "I", bindingClassName); addGetSetPrimitive(ci, cv, "Long", "J", bindingClassName); addGetSetPrimitive(ci, cv, "Float", "F", bindingClassName); addGetSetPrimitive(ci, cv, "Double", "D", bindingClassName); } cv.visitEnd(); return cw.toByteArray(); } private void addGetSetPrimitive(ClassInfo ci, ClassVisitor cv, String setterName, String signature, String bindingClassName) { String className = ci.clazz.getName().replaceAll("\\.", "/"); Label firstLabel = new Label(); Label secondLabel = new Label(); Label errorLabel = new Label(); Label exitLabel = new Label(); Label lastLabel = new Label(); boolean oneByte = !signature.equals("J") && !signature.equals("D"); int c = 0; for (Field f : ci.fields) { if (toTypeDescriptor(getPrimitiveClass(f.getType())).equals(signature)) c++; } int[] indices = new int[c]; Label[] caseLabel = new Label[c]; c = 0; for (int i = 0; i < ci.fields.length; i++) { Class<?> fieldClass = ci.fields[i].getType(); fieldClass = getPrimitiveClass(fieldClass); String s = toTypeDescriptor(fieldClass); if (!s.equals(signature)) continue; indices[c] = i; caseLabel[c] = new Label(); c++; } ////////////////// /// Setter /// MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "set" + setterName, "(Ljava/lang/Object;I" + signature + ")V", null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); mv.visitCode(); mv.visitLabel(firstLabel); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, oneByte ? 4 : 5); mv.visitLabel(secondLabel); mv.visitVarInsn(ILOAD, 2); // switch mv.visitLookupSwitchInsn(errorLabel, indices, caseLabel); // case 1: if (c > 0) { for (int i = 0; i < c; i++) { int index = indices[i]; Method setter = ci.setters[index]; Field field = ci.fields[index]; Class<?> fieldClass = field.getType(); Class<?> setterClass = setter != null ? setter.getParameterTypes()[0] : null; boolean useSetter = ((field.getModifiers() & Modifier.PUBLIC) == 0) && setter != null; String typeDescriptor = toTypeDescriptor(useSetter ? setterClass : fieldClass); mv.visitLabel(caseLabel[i]); if (i == 0) { mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { className }, 0, null); } else { mv.visitFrame(Opcodes.F_SAME, 0, new Object[] { className }, 0, null); } if (signature.equals("F")) { mv.visitVarInsn(ALOAD, 4); mv.visitVarInsn(FLOAD, 3); } else if (signature.equals("D")) { mv.visitVarInsn(ALOAD, 5); mv.visitVarInsn(DLOAD, 3); } else if (signature.equals("J")) { mv.visitVarInsn(ALOAD, 5); mv.visitVarInsn(LLOAD, 3); } else { mv.visitVarInsn(ALOAD, 4); mv.visitVarInsn(ILOAD, 3); } if (useSetter) { boxTo(mv, setterClass); mv.visitMethodInsn(INVOKEVIRTUAL, className, setter.getName(), "(" + typeDescriptor + ")V"); } else { boxTo(mv, fieldClass); mv.visitFieldInsn(PUTFIELD, className, field.getName(), typeDescriptor); } mv.visitJumpInsn(GOTO, exitLabel); } } else { mv.visitInsn(POP); } // default: (error) /* mv.visitLabel(errorLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitTypeInsn(NEW, "org/simantics/databoard/binding/error/BindingException"); mv.visitInsn(DUP); mv.visitLdcInsn("Field is not "+setterName); mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/binding/error/BindingException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); */ // default: setComponent mv.visitLabel(errorLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, bindingClassName, "componentBindings", "[Lorg/simantics/databoard/binding/Binding;"); mv.visitVarInsn(ILOAD, 2); mv.visitInsn(AALOAD); if (signature.equals("F")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/FloatBinding"); mv.visitVarInsn(FLOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/FloatBinding", "create", "(F)Ljava/lang/Object;"); } else if (signature.equals("D")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/DoubleBinding"); mv.visitVarInsn(DLOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/DoubleBinding", "create", "(D)Ljava/lang/Object;"); } else if (signature.equals("J")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/LongBinding"); mv.visitVarInsn(LLOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/LongBinding", "create", "(J)Ljava/lang/Object;"); } else if (signature.equals("Z")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/BooleanBinding"); mv.visitVarInsn(ILOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/BooleanBinding", "create", "(Z)Ljava/lang/Object;"); } else if (signature.equals("I")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/IntBinding"); mv.visitVarInsn(ILOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/IntBinding", "create", "(I)Ljava/lang/Object;"); } else if (signature.equals("B")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/ByteBinding"); mv.visitVarInsn(ILOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/ByteBinding", "create", "(B)Ljava/lang/Object;"); } mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "setComponent", "(Ljava/lang/Object;ILjava/lang/Object;)V"); // return mv.visitLabel(exitLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitInsn(RETURN); // Something at the end mv.visitLabel(lastLabel); // mv.visitLocalVariable("this", "L"+bindingClassName+";", null, firstLabel, lastLabel, 0); // mv.visitLocalVariable("r", "Ljava/lang/Object;", null, firstLabel, lastLabel, 1); // mv.visitLocalVariable("index", "I", null, firstLabel, lastLabel, 2); // mv.visitLocalVariable("z", signature, null, firstLabel, lastLabel, 3); // mv.visitLocalVariable("x", "L"+className+";", null, secondLabel, lastLabel, 4); mv.visitMaxs(oneByte ? 5 : 6, oneByte ? 5 : 6); mv.visitEnd(); ////////////////// /// Getter /// firstLabel = new Label(); secondLabel = new Label(); errorLabel = new Label(); exitLabel = new Label(); lastLabel = new Label(); for (int i = 0; i < c; i++) caseLabel[i] = new Label(); mv = cv.visitMethod(ACC_PUBLIC, "get" + setterName, "(Ljava/lang/Object;I)" + signature, null, new String[] { "org/simantics/databoard/binding/error/BindingException" }); mv.visitCode(); mv.visitLabel(firstLabel); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, className); mv.visitVarInsn(ASTORE, 3); mv.visitLabel(secondLabel); mv.visitVarInsn(ILOAD, 2); // switch if (c > 0) { mv.visitLookupSwitchInsn(errorLabel, indices, caseLabel); // case i: for (int i = 0; i < c; i++) { int index = indices[i]; Method getter = ci.getters[index]; Field field = ci.fields[index]; Class<?> fieldClass = field.getType(); Class<?> getterClass = getter != null ? getter.getReturnType() : null; boolean useGetter = ((field.getModifiers() & Modifier.PUBLIC) == 0) && getter != null; String typeDescriptor = toTypeDescriptor(useGetter ? getterClass : fieldClass); mv.visitLabel(caseLabel[i]); if (i == 0) { mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { className }, 0, null); } else { mv.visitFrame(Opcodes.F_SAME, 0, new Object[] { className }, 0, null); } mv.visitVarInsn(ALOAD, 3); if (useGetter) { mv.visitMethodInsn(INVOKEVIRTUAL, className, getter.getName(), "()" + typeDescriptor); unboxFrom(mv, getterClass); } else { mv.visitFieldInsn(GETFIELD, className, field.getName(), typeDescriptor); unboxFrom(mv, fieldClass); } if (signature.equals("F")) { mv.visitInsn(FRETURN); } else if (signature.equals("D")) { mv.visitInsn(DRETURN); } else if (signature.equals("J")) { mv.visitInsn(LRETURN); } else { mv.visitInsn(IRETURN); } } } else { mv.visitInsn(POP); } // default: (error) /* mv.visitLabel(errorLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitTypeInsn(NEW, "org/simantics/databoard/binding/error/BindingException"); mv.visitInsn(DUP); mv.visitLdcInsn("Field is not "+setterName); mv.visitMethodInsn(INVOKESPECIAL, "org/simantics/databoard/binding/error/BindingException", "<init>", "(Ljava/lang/String;)V"); mv.visitInsn(ATHROW); */ // default: mv.visitLabel(errorLabel); mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, bindingClassName, "componentBindings", "[Lorg/simantics/databoard/binding/Binding;"); mv.visitVarInsn(ILOAD, 2); mv.visitInsn(AALOAD); if (signature.equals("Z")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/BooleanBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/BooleanBinding", "getValue_", "(Ljava/lang/Object;)Z"); mv.visitInsn(IRETURN); } else if (signature.equals("B")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/ByteBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/ByteBinding", "getValue_", "(Ljava/lang/Object;)B"); mv.visitInsn(IRETURN); } else if (signature.equals("I")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/IntBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/IntBinding", "getValue_", "(Ljava/lang/Object;)I"); mv.visitInsn(IRETURN); } else if (signature.equals("J")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/LongBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/LongBinding", "getValue_", "(Ljava/lang/Object;)J"); mv.visitInsn(LRETURN); } else if (signature.equals("F")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/FloatBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/FloatBinding", "getValue_", "(Ljava/lang/Object;)F"); mv.visitInsn(FRETURN); } else if (signature.equals("D")) { mv.visitTypeInsn(CHECKCAST, "org/simantics/databoard/binding/DoubleBinding"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ILOAD, 2); mv.visitMethodInsn(INVOKEVIRTUAL, bindingClassName, "getComponent", "(Ljava/lang/Object;I)Ljava/lang/Object;"); mv.visitMethodInsn(INVOKEVIRTUAL, "org/simantics/databoard/binding/DoubleBinding", "getValue_", "(Ljava/lang/Object;)D"); mv.visitInsn(DRETURN); } // Something at the end mv.visitLabel(lastLabel); // mv.visitLocalVariable("this", "Lorg/simantics/databoard/binding/reflection/MyBinding;", null, firstLabel, lastLabel, 0); // mv.visitLocalVariable("r", "Ljava/lang/Object;", null, firstLabel, lastLabel, 1); // mv.visitLocalVariable("index", "I", null, firstLabel, lastLabel, 2); // mv.visitLocalVariable("x", "Lorg/simantics/databoard/binding/reflection/MyClass;", null, secondLabel, lastLabel, 3); mv.visitMaxs(4, 4); mv.visitEnd(); } public static Label[] createFieldLabels(ClassInfo ci) { Label caseLabels[] = new Label[ci.fields.length]; for (int i = 0; i < ci.fields.length; i++) caseLabels[i] = new Label(); return caseLabels; } public static String toTypeDescriptor(Class<?> clazz) { if (clazz == void.class) return "V"; if (clazz == boolean.class) return "Z"; if (clazz == char.class) return "C"; if (clazz == byte.class) return "B"; if (clazz == short.class) return "S"; if (clazz == int.class) return "I"; if (clazz == float.class) return "F"; if (clazz == long.class) return "J"; if (clazz == double.class) return "D"; if (clazz.isArray()) return clazz.getName().replaceAll("\\.", "/"); return "L" + clazz.getName().replaceAll("\\.", "/") + ";"; } /** * Get respective boxed class * @param clazz * @return box-class */ public static Class<?> getBoxClass(Class<?> clazz) { if (clazz == void.class) return null; if (clazz == boolean.class) return Boolean.class; if (clazz == char.class) return Character.class; if (clazz == byte.class) return Byte.class; if (clazz == short.class) return Short.class; if (clazz == int.class) return Integer.class; if (clazz == float.class) return Float.class; if (clazz == long.class) return Long.class; if (clazz == double.class) return Double.class; return clazz; } /** * Get respective primitive class * @param clazz * @return primitive-class */ public static Class<?> getPrimitiveClass(Class<?> clazz) { if (clazz == Boolean.class) return boolean.class; if (clazz == Character.class) return char.class; if (clazz == Byte.class) return byte.class; if (clazz == Short.class) return short.class; if (clazz == Integer.class) return int.class; if (clazz == Float.class) return float.class; if (clazz == Long.class) return long.class; if (clazz == Double.class) return double.class; return clazz; } public static boolean isPrimitive(Class<?> clazz) { return clazz == boolean.class || clazz == char.class || clazz == byte.class || clazz == short.class || clazz == int.class || clazz == float.class || clazz == long.class || clazz == double.class; } public static void unbox(MethodVisitor mv, Class<?> clazz) { if (clazz == boolean.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); } else if (clazz == char.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"); // C? } else if (clazz == byte.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); } else if (clazz == short.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); } else if (clazz == int.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); } else if (clazz == float.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); } else if (clazz == long.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); } else if (clazz == double.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); } } public static void box(MethodVisitor mv, Class<?> clazz) { if (clazz == boolean.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); } else if (clazz == char.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); } else if (clazz == byte.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); } else if (clazz == short.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); } else if (clazz == int.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } else if (clazz == float.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); } else if (clazz == long.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); } else if (clazz == double.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); } } public static void boxTo(MethodVisitor mv, Class<?> clazz) { if (clazz == Boolean.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); } else if (clazz == Character.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); } else if (clazz == Byte.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); } else if (clazz == Short.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); } else if (clazz == Integer.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } else if (clazz == Float.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); } else if (clazz == Long.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); } else if (clazz == Double.class) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); } } public static void unboxFrom(MethodVisitor mv, Class<?> clazz) { if (clazz == Boolean.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); } else if (clazz == Character.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"); // C? } else if (clazz == Byte.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); } else if (clazz == Short.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); } else if (clazz == Integer.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); } else if (clazz == Float.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); } else if (clazz == Long.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); } else if (clazz == Double.class) { mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); } } public static String toClassCanonicalName(Class<?> clazz) { return clazz.getName().replaceAll("\\.", "/"); } }