/*
* 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.lang.reflect.*;
import java.util.*;
import javax.annotation.*;
import mockit.external.asm.*;
import mockit.internal.util.*;
import static mockit.external.asm.ClassReader.*;
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
final class SuperConstructorCollector extends ClassVisitor
{
@Nonnull static final SuperConstructorCollector INSTANCE = new SuperConstructorCollector();
@Nonnull private final Map<String, String> cache = new HashMap<String, String>();
@Nullable private String constructorDesc;
private boolean samePackage;
private SuperConstructorCollector() {}
@Nonnull
synchronized String findConstructor(@Nonnull String classDesc, @Nonnull String superClassDesc)
{
constructorDesc = cache.get(superClassDesc);
if (constructorDesc != null) {
return constructorDesc;
}
findIfBothClassesAreInSamePackage(classDesc, superClassDesc);
ClassReader cr = ClassFile.readFromFile(superClassDesc);
try { cr.accept(this, SKIP_DEBUG + SKIP_FRAMES); } catch (VisitInterruptedException ignore) {}
cache.put(superClassDesc, constructorDesc);
return constructorDesc;
}
private void findIfBothClassesAreInSamePackage(@Nonnull String classDesc, @Nonnull String superClassDesc)
{
int p1 = classDesc.lastIndexOf('/');
int p2 = superClassDesc.lastIndexOf('/');
samePackage = p1 == p2 && (p1 < 0 || classDesc.substring(0, p1).equals(superClassDesc.substring(0, p2)));
}
@Override @Nullable
public MethodVisitor visitMethod(
int access, @Nonnull String name, @Nonnull String desc, @Nullable String signature, @Nullable String[] exceptions)
{
if (isAccessible(access) && "<init>".equals(name)) {
constructorDesc = desc;
throw VisitInterruptedException.INSTANCE;
}
return null;
}
private boolean isAccessible(int access) { return access != Modifier.PRIVATE && (access != 0 || samePackage); }
}