/* * Copyright (c) 2006 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal; import java.io.*; import java.util.*; import java.util.concurrent.*; import javax.annotation.*; import mockit.external.asm.*; import mockit.internal.state.*; import static mockit.external.asm.ClassReader.*; public final class ClassFile { private static final Map<String, ClassReader> CLASS_FILES = new ConcurrentHashMap<String, ClassReader>(); private ClassFile() {} public static final class NotFoundException extends RuntimeException { private NotFoundException(@Nonnull String classNameOrDesc) { super("Unable to find class file for " + classNameOrDesc.replace('/', '.')); } } private static void verifyClassFileFound(@Nullable InputStream classFile, @Nonnull String classNameOrDesc) { if (classFile == null) { throw new NotFoundException(classNameOrDesc); } } @Nonnull public static ClassReader createClassFileReader(@Nonnull Class<?> aClass) { byte[] cachedClassfile = CachedClassfiles.getClassfile(aClass); if (cachedClassfile != null) { return new ClassReader(cachedClassfile); } String className = aClass.getName(); InputStream classFile = aClass.getResourceAsStream('/' + className.replace('.', '/') + ".class"); verifyClassFileFound(classFile, className); try { return new ClassReader(classFile); } catch (IOException e) { throw new RuntimeException("Failed to read class file for " + className, e); } } @Nonnull public static ClassReader createReaderOrGetFromCache(@Nonnull Class<?> aClass) { byte[] cachedClassfile = CachedClassfiles.getClassfile(aClass); if (cachedClassfile != null) { return new ClassReader(cachedClassfile); } String classDesc = aClass.getName().replace('.', '/'); ClassReader reader = CLASS_FILES.get(classDesc); if (reader == null) { reader = readFromFile(classDesc); CLASS_FILES.put(classDesc, reader); } return reader; } @Nonnull @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") private static InputStream readClassFromClasspath(@Nonnull String classDesc) { String classFileName = classDesc + ".class"; ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); InputStream inputStream = null; if (contextClassLoader != null) { inputStream = contextClassLoader.getResourceAsStream(classFileName); } if (inputStream == null) { ClassLoader thisClassLoader = ClassFile.class.getClassLoader(); if (thisClassLoader != contextClassLoader) { inputStream = thisClassLoader.getResourceAsStream(classFileName); if (inputStream == null) { Class<?> testClass = TestRun.getCurrentTestClass(); if (testClass != null) { inputStream = testClass.getClassLoader().getResourceAsStream(classFileName); } } } } verifyClassFileFound(inputStream, classDesc); //noinspection ConstantConditions return inputStream; } @Nonnull public static ClassReader createReaderFromLastRedefinitionIfAny(@Nonnull Class<?> aClass) { byte[] classfile = TestRun.mockFixture().getRedefinedClassfile(aClass); if (classfile == null) { classfile = CachedClassfiles.getClassfile(aClass); } if (classfile != null) { return new ClassReader(classfile); } String classDesc = aClass.getName().replace('.', '/'); ClassReader reader = readFromFile(classDesc); CLASS_FILES.put(classDesc, reader); return reader; } @Nonnull public static ClassReader createClassFileReader(@Nullable ClassLoader loader, @Nonnull String internalClassName) { byte[] cachedClassfile = CachedClassfiles.getClassfile(loader, internalClassName); if (cachedClassfile != null) { return new ClassReader(cachedClassfile); } return readFromFile(internalClassName); } @Nonnull public static ClassReader readFromFile(@Nonnull Class<?> aClass) { String classDesc = aClass.getName().replace('.', '/'); return readFromFile(classDesc); } @Nonnull public static ClassReader readFromFile(@Nonnull String classDesc) { if (classDesc.startsWith("java/") || classDesc.startsWith("javax/")) { byte[] classfile = CachedClassfiles.getClassfile(classDesc); if (classfile != null) { return new ClassReader(classfile); } } InputStream classFile = readClassFromClasspath(classDesc); try { return new ClassReader(classFile); } catch (IOException e) { throw new RuntimeException("Failed to read class file for " + classDesc.replace('/', '.'), e); } } public static void visitClass(@Nonnull String classDesc, @Nonnull ClassVisitor visitor) { byte[] classfile = CachedClassfiles.getClassfile(classDesc); ClassReader cr = classfile != null ? new ClassReader(classfile) : readFromFile(classDesc); cr.accept(visitor, SKIP_DEBUG + SKIP_FRAMES); } }