@Override public void visit(Method obj) { int accessFlags = obj.getAccessFlags(); boolean isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0; if (getMethodName().equals("<init>") && getMethodSig().equals("()V") && (accessFlags & ACC_PUBLIC) != 0) hasPublicVoidConstructor = true; if (!getMethodName().equals("<init>") && isSynthetic(obj)) foundSynthetic = true; // System.out.println(methodName + isSynchronized); if (getMethodName().equals("readExternal") && getMethodSig().equals("(Ljava/io/ObjectInput;)V")) { sawReadExternal = true; if (DEBUG && !obj.isPrivate()) System.out.println("Non-private readExternal method in: " + getDottedClassName()); } else if (getMethodName().equals("writeExternal") && getMethodSig().equals("(Ljava/io/Objectoutput;)V")) { sawWriteExternal = true; if (DEBUG && !obj.isPrivate()) System.out.println("Non-private writeExternal method in: " + getDottedClassName()); } else if (getMethodName().equals("readResolve") && getMethodSig().startsWith("()") && isSerializable) { sawReadResolve = true; if (!getMethodSig().equals("()Ljava/lang/Object;")) bugReporter.reportBug( new BugInstance(this, "SE_READ_RESOLVE_MUST_RETURN_OBJECT", HIGH_PRIORITY) .addClassAndMethod(this)); else if (obj.isStatic()) bugReporter.reportBug( new BugInstance(this, "SE_READ_RESOLVE_IS_STATIC", HIGH_PRIORITY) .addClassAndMethod(this)); else if (obj.isPrivate()) try { Set<ClassDescriptor> subtypes = AnalysisContext.currentAnalysisContext() .getSubtypes2() .getSubtypes(getClassDescriptor()); if (subtypes.size() > 1) { BugInstance bug = new BugInstance(this, "SE_PRIVATE_READ_RESOLVE_NOT_INHERITED", NORMAL_PRIORITY) .addClassAndMethod(this); boolean nasty = false; for (ClassDescriptor subclass : subtypes) if (!subclass.equals(getClassDescriptor())) { XClass xSub = AnalysisContext.currentXFactory().getXClass(subclass); if (xSub != null && xSub.findMethod("readResolve", "()Ljava/lang/Object;", false) == null && xSub.findMethod("writeReplace", "()Ljava/lang/Object;", false) == null) { bug.addClass(subclass).describe(ClassAnnotation.SUBCLASS_ROLE); nasty = true; } } if (nasty) bug.setPriority(HIGH_PRIORITY); else if (!getThisClass().isPublic()) bug.setPriority(LOW_PRIORITY); bugReporter.reportBug(bug); } } catch (ClassNotFoundException e) { bugReporter.reportMissingClass(e); } } else if (getMethodName().equals("readObject") && getMethodSig().equals("(Ljava/io/ObjectInputStream;)V") && isSerializable) { sawReadObject = true; if (!obj.isPrivate()) bugReporter.reportBug( new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", HIGH_PRIORITY) .addClassAndMethod(this)); } else if (getMethodName().equals("readObjectNoData") && getMethodSig().equals("()V") && isSerializable) { if (!obj.isPrivate()) bugReporter.reportBug( new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", HIGH_PRIORITY) .addClassAndMethod(this)); } else if (getMethodName().equals("writeObject") && getMethodSig().equals("(Ljava/io/ObjectOutputStream;)V") && isSerializable) { sawWriteObject = true; if (!obj.isPrivate()) bugReporter.reportBug( new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", HIGH_PRIORITY) .addClassAndMethod(this)); } if (isSynchronized) { if (getMethodName().equals("readObject") && getMethodSig().equals("(Ljava/io/ObjectInputStream;)V") && isSerializable) bugReporter.reportBug( new BugInstance(this, "RS_READOBJECT_SYNC", NORMAL_PRIORITY).addClass(this)); else if (getMethodName().equals("writeObject") && getMethodSig().equals("(Ljava/io/ObjectOutputStream;)V") && isSerializable) writeObjectIsSynchronized = true; else foundSynchronizedMethods = true; } super.visit(obj); }
private TypeQualifierValue(ClassDescriptor typeQualifier, @CheckForNull Object value) { this.typeQualifier = typeQualifier; this.value = value; boolean isStrict = false; // will be set to true if this is a strict // type qualifier value boolean isExclusive = false; // will be set to true if this is an // exclusive type qualifier value boolean isExhaustive = false; // will be set to true if this is an // exhaustive type qualifier value TypeQualifierValidator<A> validator = null; Class<A> qualifierClass = null; A proxy = null; try { XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, typeQualifier); // Annotation elements appear as abstract methods in the annotation // class (interface). // So, if the type qualifier annotation has specified a default When // value, // it will appear as an abstract method called "when". XMethod whenMethod = xclass.findMethod("when", "()Ljavax/annotation/meta/When;", false); if (whenMethod == null) { isStrict = true; } for (XMethod xmethod : xclass.getXMethods()) { if (xmethod.getName().equals("value") && xmethod.getSignature().startsWith("()")) { isExhaustive = xmethod.getAnnotation(EXHAUSTIVE_ANNOTATION) != null; if (isExhaustive) { // exhaustive qualifiers are automatically exclusive isExclusive = true; } else { // see if there is an explicit @Exclusive annotation isExclusive = xmethod.getAnnotation(EXCLUSIVE_ANNOTATION) != null; } break; } } } catch (MissingClassException e) { AnalysisContext.currentAnalysisContext() .getLookupFailureCallback() .reportMissingClass(e.getClassNotFoundException()); } catch (CheckedAnalysisException e) { AnalysisContext.logError( "Error looking up annotation class " + typeQualifier.toDottedClassName(), e); } this.isStrict = isStrict; this.isExclusive = isExclusive; this.isExhaustive = isExhaustive; ClassDescriptor checkerName = DescriptorFactory.createClassDescriptor(typeQualifier.getClassName() + "$Checker"); try { Global.getAnalysisCache().getClassAnalysis(ClassData.class, checkerName); // found it. // System.out.println(checkerName); SecurityManager m = System.getSecurityManager(); if (m == null) System.setSecurityManager(new ValidationSecurityManager()); Class<?> c = validatorLoader.loadClass(checkerName.getDottedClassName()); if (TypeQualifierValidator.class.isAssignableFrom(c)) { Class<? extends TypeQualifierValidator> checkerClass = c.asSubclass(TypeQualifierValidator.class); validator = getValidator(checkerClass); qualifierClass = getQualifierClass(typeQualifier); InvocationHandler handler = new InvocationHandler() { public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { if (arg1.getName() == "value") return TypeQualifierValue.this.value; throw new UnsupportedOperationException("Can't handle " + arg1); } }; proxy = qualifierClass.cast( Proxy.newProxyInstance(validatorLoader, new Class[] {qualifierClass}, handler)); } } catch (ClassNotFoundException e) { assert true; // ignore } catch (CheckedAnalysisException e) { assert true; // ignore } catch (Exception e) { AnalysisContext.logError("Unable to construct type qualifier checker " + checkerName, e); } catch (Throwable e) { AnalysisContext.logError( "Unable to construct type qualifier checker " + checkerName + " due to " + e.getClass().getSimpleName() + ":" + e.getMessage()); } this.validator = validator; this.typeQualifierClass = qualifierClass; this.proxy = proxy; }