/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.capturing;
import javax.annotation.*;
import mockit.external.asm.*;
import mockit.internal.*;
import mockit.internal.startup.*;
import mockit.internal.state.*;
import static mockit.external.asm.ClassReader.*;
public abstract class CaptureOfImplementations<M>
{
protected CaptureOfImplementations() {}
public final void makeSureAllSubtypesAreModified(
@Nonnull Class<?> baseType, boolean registerCapturedClasses, @Nullable M typeMetadata)
{
CapturedType captureMetadata = new CapturedType(baseType);
redefineClassesAlreadyLoaded(captureMetadata, baseType, typeMetadata);
createCaptureTransformer(captureMetadata, registerCapturedClasses, typeMetadata);
}
private void redefineClassesAlreadyLoaded(
@Nonnull CapturedType captureMetadata, @Nonnull Class<?> baseType, @Nullable M typeMetadata)
{
Class<?>[] classesLoaded = Startup.instrumentation().getAllLoadedClasses();
for (Class<?> aClass : classesLoaded) {
if (captureMetadata.isToBeCaptured(aClass)) {
redefineClass(aClass, baseType, typeMetadata);
}
}
}
public void redefineClass(@Nonnull Class<?> realClass, @Nonnull Class<?> baseType, @Nullable M typeMetadata)
{
if (!TestRun.mockFixture().containsRedefinedClass(realClass)) {
ClassReader classReader;
try {
classReader = ClassFile.createReaderOrGetFromCache(realClass);
}
catch (ClassFile.NotFoundException ignore) {
return;
}
TestRun.ensureThatClassIsInitialized(realClass);
BaseClassModifier modifier = createModifier(realClass.getClassLoader(), classReader, baseType, typeMetadata);
classReader.accept(modifier, SKIP_FRAMES);
if (modifier.wasModified()) {
byte[] modifiedClass = modifier.toByteArray();
redefineClass(realClass, modifiedClass);
}
}
}
@Nonnull
protected abstract BaseClassModifier createModifier(
@Nullable ClassLoader cl, @Nonnull ClassReader cr, @Nonnull Class<?> baseType, @Nullable M typeMetadata);
protected abstract void redefineClass(@Nonnull Class<?> realClass, @Nonnull byte[] modifiedClass);
private void createCaptureTransformer(
@Nonnull CapturedType captureMetadata, boolean registerCapturedClasses, @Nullable M typeMetadata)
{
CaptureTransformer<M> transformer =
new CaptureTransformer<M>(captureMetadata, this, registerCapturedClasses, typeMetadata);
Startup.instrumentation().addTransformer(transformer, true);
TestRun.mockFixture().addCaptureTransformer(transformer);
}
}