private void checkCompoundIds(Class<?> javaClass) throws IOException { String javaClassName = javaClass.getCanonicalName(); PsiClass psiClass = myJavaPsiFacade.findClass( javaClassName, GlobalSearchScope.moduleWithLibrariesScope(myModule)); assertNotNull(psiClass); for (java.lang.reflect.Method javaMethod : javaClass.getDeclaredMethods()) { Method method = new Method( Type.getType(javaClass).getInternalName(), javaMethod.getName(), Type.getMethodDescriptor(javaMethod)); boolean noKey = javaMethod.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod psiMethod = psiClass.findMethodsByName(javaMethod.getName(), false)[0]; checkCompoundId(method, psiMethod, noKey); } for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) { Method method = new Method( Type.getType(javaClass).getInternalName(), "<init>", Type.getConstructorDescriptor(constructor)); boolean noKey = constructor.getAnnotation(ExpectNoPsiKey.class) != null; PsiMethod[] constructors = psiClass.getConstructors(); PsiMethod psiMethod = constructors[0]; checkCompoundId(method, psiMethod, noKey); } }
@NotNull public Type typeToType(PsiType type, boolean notNull) { Type result = typeToType(type); if (notNull) { result.convertedToNotNull(); } return result; }
private static String parseMethodViaDescription( final String desc, final PsiMethodStubImpl stub, final List<String> args) { final String returnType = getTypeText(Type.getReturnType(desc)); final Type[] argTypes = Type.getArgumentTypes(desc); for (Type argType : argTypes) { args.add(getTypeText(argType)); } new PsiTypeParameterListStubImpl(stub); return returnType; }
@NotNull private static TypeInfo fieldTypeViaDescription(@NotNull String desc) { Type type = Type.getType(desc); final int dim = type.getSort() == Type.ARRAY ? type.getDimensions() : 0; if (dim > 0) { type = type.getElementType(); } return new TypeInfo( getTypeText(type), (byte) dim, false, Collections.<PsiAnnotationStub>emptyList()); // todo read annos from .class file }
private static void checkLeakingParameters(Class<?> jClass) throws IOException { final HashMap<Method, boolean[]> map = new HashMap<Method, boolean[]>(); // collecting leakedParameters final ClassReader classReader = new ClassReader( new FileInputStream( jClass.getResource("/" + jClass.getName().replace('.', '/') + ".class").getFile())); classReader.accept( new ClassVisitor(Opcodes.ASM5) { @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { final MethodNode node = new MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions); final Method method = new Method(classReader.getClassName(), name, desc); return new MethodVisitor(Opcodes.ASM5, node) { @Override public void visitEnd() { super.visitEnd(); try { map.put( method, LeakingParameters.build(classReader.getClassName(), node, false).parameters); } catch (AnalyzerException ignore) { } } }; } }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); for (java.lang.reflect.Method jMethod : jClass.getDeclaredMethods()) { Method method = new Method( Type.getType(jClass).getInternalName(), jMethod.getName(), Type.getMethodDescriptor(jMethod)); Annotation[][] annotations = jMethod.getParameterAnnotations(); for (int i = 0; i < annotations.length; i++) { boolean isLeaking = false; Annotation[] parameterAnnotations = annotations[i]; for (Annotation parameterAnnotation : parameterAnnotations) { if (parameterAnnotation.annotationType() == ExpectLeaking.class) { isLeaking = true; } } assertEquals(method.toString() + " #" + i, isLeaking, map.get(method)[i]); } } }
public AnnotationTextCollector(@Nullable String desc, AnnotationResultCallback callback) { super(Opcodes.ASM4); myCallback = callback; myDesc = desc; if (desc != null) { myBuilder.append('@').append(getTypeText(Type.getType(desc))); } }
@NotNull private static String getTypeText(@NotNull Type type) { final String raw = type.getClassName(); // As the '$' char is a valid java identifier and is actively used by byte code generators, the // problem is // which occurrences of this char should be replaced and which should not. // Heuristic: replace only those $ occurrences that are surrounded non-"$" chars // (most likely generated by javac to separate inner or anonymous class name) // Leading and trailing $ chars should be left unchanged. return raw.indexOf('$') >= 0 ? REGEX_PATTERN.matcher(raw).replaceAll("\\.") : raw; }
private static String getClassName(final String name) { return getTypeText(Type.getObjectType(name)); }
@Override public void visitEnum(final String name, final String desc, final String value) { valuePairPrefix(name); myBuilder.append(getTypeText(Type.getType(desc))).append(".").append(value); }
@NotNull private List<Type> typesToNotNullableTypeList(@NotNull PsiType[] types) { List<Type> result = new LinkedList<Type>(typesToTypeList(types)); for (Type p : result) p.convertedToNotNull(); return result; }