/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.classGeneration; import java.lang.reflect.Type; import javax.annotation.*; import mockit.external.asm.*; import mockit.internal.*; import mockit.internal.util.*; import static mockit.external.asm.ClassReader.*; /** * Allows the creation of new implementation classes for interfaces and abstract classes. */ public abstract class ImplementationClass<T> { @Nonnull protected final Class<?> sourceClass; @Nonnull protected String generatedClassName; @Nullable private byte[] generatedBytecode; protected ImplementationClass(@Nonnull Type mockedType) { this(Utilities.getClassType(mockedType)); } protected ImplementationClass(@Nonnull Class<?> mockedClass) { this(mockedClass, GeneratedClasses.getNameForGeneratedClass(mockedClass, null)); } protected ImplementationClass(@Nonnull Class<?> sourceClass, @Nonnull String desiredClassName) { this.sourceClass = sourceClass; generatedClassName = desiredClassName; } @Nonnull public final Class<T> generateClass() { ClassReader classReader = ClassFile.createReaderOrGetFromCache(sourceClass); ClassVisitor modifier = createMethodBodyGenerator(classReader); classReader.accept(modifier, SKIP_FRAMES); return defineNewClass(modifier); } @Nonnull protected abstract ClassVisitor createMethodBodyGenerator(@Nonnull ClassReader typeReader); @Nonnull private Class<T> defineNewClass(@Nonnull ClassVisitor modifier) { final ClassLoader parentLoader = ClassLoad.getClassLoaderWithAccess(sourceClass); final byte[] modifiedClassfile = modifier.toByteArray(); try { @SuppressWarnings("unchecked") Class<T> generatedClass = (Class<T>) new ClassLoader(parentLoader) { @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (!name.equals(generatedClassName)) { return parentLoader.loadClass(name); } return defineClass(name, modifiedClassfile, 0, modifiedClassfile.length); } }.findClass(generatedClassName); generatedBytecode = modifiedClassfile; return generatedClass; } catch (ClassNotFoundException e) { throw new RuntimeException("Unable to define class: " + generatedClassName, e); } } @Nullable public final byte[] getGeneratedBytecode() { return generatedBytecode; } }