/** * Visit every class/interface this proxy should implement, and generate the appropriate bytecode * for delegation if available. * * @param clazz an class for which to generate bytecode */ private void visitClass(final Class clazz) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Class<?>[] exceptionTypes = method.getExceptionTypes(); String[] exceptions = new String[exceptionTypes.length]; for (int i = 0; i < exceptions.length; i++) { exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]); } // for each method defined in the class, generate the appropriate delegation bytecode visitMethod( method.getModifiers(), method.getName(), BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes()), null, exceptions); } Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor method : constructors) { Class<?>[] exceptionTypes = method.getExceptionTypes(); String[] exceptions = new String[exceptionTypes.length]; for (int i = 0; i < exceptions.length; i++) { exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]); } // for each method defined in the class, generate the appropriate delegation bytecode visitMethod( method.getModifiers(), "<init>", BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()), null, exceptions); } for (Class intf : clazz.getInterfaces()) { visitClass(intf); } Class superclass = clazz.getSuperclass(); if (superclass != null) visitClass(superclass); // Ultimately, methods can be available in the closure map which are not defined by the // superclass // nor the interfaces for (Map.Entry<String, Boolean> entry : delegatedClosures.entrySet()) { Boolean visited = entry.getValue(); if (!visited) { String name = entry.getKey(); if (!"*".equals(name)) { // generate a new method visitMethod(ACC_PUBLIC, name, "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); } } } }
private static List<Method> getInheritedMethods(Class baseClass, List<Method> methods) { Collections.addAll(methods, baseClass.getMethods()); Class currentClass = baseClass; while (currentClass != null) { Method[] protectedMethods = currentClass.getDeclaredMethods(); for (Method method : protectedMethods) { if (method.getName().indexOf('$') != -1) continue; if (Modifier.isProtected(method.getModifiers()) && !containsEquivalentMethod(methods, method)) methods.add(method); } currentClass = currentClass.getSuperclass(); } return methods; }
@Override public FieldAccessor build(ClassLoader classLoader) { String targetClassType = getInternalName(this.getTarget().getName()); ClassWriter classWriter = this.getClassWriter(); injectFieldTable(classWriter); injectConstructor(classWriter); injectGetTargetClass(classWriter, this.getTarget()); List<Field> fields = new ArrayList<>(); Class<?> current = this.getTarget(); while (current != Object.class) { for (Field field : current.getDeclaredFields()) { fields.add(field); } current = current.getSuperclass(); } injectGetByIndex(classWriter, targetClassType, fields); injectSetByIndex(classWriter, targetClassType, fields); injectGetByName(classWriter); injectSetByName(classWriter); injectGetFieldTable(classWriter); injectGetIndex(classWriter); classWriter.visitEnd(); Class<?> result = CompilerService.create(classLoader) .defineClass( getExternalName( getAccessorNameInternal( this.getTarget(), this .getAccessorType())), // this is somewhat redundant but maybe in the // future the class-name-format changes this.getClassWriter().toByteArray()); return (FieldAccessor) instantiate(result); }