/******************************************************************************* * * Copyright (c) 2010, InfraDNA, Inc. * * 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: * * * * *******************************************************************************/ package hudson.util; import hudson.PluginManager.UberClassLoader; import hudson.model.Hudson; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import java.lang.reflect.Constructor; import static org.objectweb.asm.Opcodes.*; /** * Generates a new class that just defines constructors into the super types. * * @author Kohsuke Kawaguchi */ public class SubClassGenerator extends ClassLoader { public SubClassGenerator(ClassLoader parent) { super(parent); } public <T> Class<? extends T> generate(Class<T> base, String name) { ClassWriter cw = new ClassWriter(false, false);//? cw.visit(49, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(base), null); for (Constructor c : base.getDeclaredConstructors()) { Class[] et = c.getExceptionTypes(); String[] exceptions = new String[et.length]; for (int i = 0; i < et.length; i++) { exceptions[i] = Type.getInternalName(et[i]); } String methodDescriptor = getMethodDescriptor(c); MethodVisitor m = cw.visitMethod(c.getModifiers(), "<init>", methodDescriptor, null, exceptions); m.visitCode(); int index = 1; m.visitVarInsn(ALOAD, 0); for (Class param : c.getParameterTypes()) { Type t = Type.getType(param); m.visitVarInsn(t.getOpcode(ILOAD), index); index += t.getSize(); } m.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(base), "<init>", methodDescriptor); m.visitInsn(RETURN); m.visitMaxs(index, index); m.visitEnd(); } cw.visitEnd(); byte[] image = cw.toByteArray(); Class<? extends T> c = defineClass(name, image, 0, image.length).asSubclass(base); Hudson h = Hudson.getInstance(); if (h != null) // null only during tests. { ((UberClassLoader) h.pluginManager.uberClassLoader).addNamedClass(name, c); // can't change the field type as it breaks binary compatibility } return c; } private String getMethodDescriptor(Constructor c) { StringBuilder buf = new StringBuilder(); buf.append('('); for (Class p : c.getParameterTypes()) { buf.append(Type.getDescriptor(p)); } buf.append(")V"); return buf.toString(); } }