Exemplo n.º 1
0
  private boolean hasTestMethods(JavaClass jClass) {
    boolean foundTest = false;
    Method[] methods = jClass.getMethods();
    for (Method m : methods) {
      if (m.isPublic() && m.getName().startsWith("test") && m.getSignature().equals("()V")) {
        return true;
      }
      if (m.getName().startsWith("runTest") && m.getSignature().endsWith("()V")) {
        return true;
      }
    }
    if (hasSuite(methods)) {
      return true;
    }

    try {
      JavaClass sClass = jClass.getSuperClass();
      if (sClass != null) {
        return hasTestMethods(sClass);
      }
    } catch (ClassNotFoundException e) {
      AnalysisContext.reportMissingClass(e);
    }

    return false;
  }
Exemplo n.º 2
0
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();

    // The class extends WebChromeClient
    boolean isWebChromeClient =
        InterfaceUtils.isSubtype(javaClass, "android.webkit.WebChromeClient");

    // Not the target of this detector
    if (!isWebChromeClient) return;

    Method[] methodList = javaClass.getMethods();

    for (Method m : methodList) {
      MethodGen methodGen = classContext.getMethodGen(m);

      if (DEBUG) System.out.println(">>> Method: " + m.getName());

      // The presence of onGeolocationPermissionsShowPrompt is not enforce for the moment
      if (!m.getName().equals("onGeolocationPermissionsShowPrompt")) {
        continue;
      }

      // Since the logic implemented need to be analyze by a human, all implementation will be
      // flagged.

      bugReporter.reportBug(
          new BugInstance(this, ANDROID_GEOLOCATION_TYPE, Priorities.NORMAL_PRIORITY) //
              .addClassAndMethod(javaClass, m));
    }
  }
Exemplo n.º 3
0
  @Override
  public String toString() {
    StringBuilder s = new StringBuilder();
    String n = String.format("%n");

    s.append("Equivalent methods:" + n);
    for (Method bzm : manifest.getBaseMethods()) {
      s.append("  " + bzm.getName() + " -> { ");
      for (Method zm : ELM.get(bzm))
        s.append(zm.getReturnType() + ":" + zm.getName() + " "); // TODO fix this info
      s.append("}" + n);
    }

    s.append("Equivalent types:" + n);
    for (JavaClass bzt : manifest.getBaseTypes()) {
      s.append("  " + bzt.getClassName() + " -> { ");
      for (JavaClass zt : ET.get(bzt)) s.append(zt.getClassName() + " ");
      s.append("}" + n);
    }

    s.append("Method costs:" + n);
    for (Method zm : CLM.keySet())
      s.append("  " + zm.getName() + " -> " + CLM.get(zm).getName() + n);

    s.append("Transformation costs:" + n);
    for (Method zs : CLS.keySet())
      s.append("  " + zs.getName() + " -> " + CLS.get(zs).getName() + n);

    return s.toString();
  }
  /**
   * overrides the visitor to find abstract methods that override concrete ones
   *
   * @param obj the context object of the currently parsed method
   */
  @Override
  public void visitMethod(Method obj) {
    if (!obj.isAbstract()) return;

    String methodName = obj.getName();
    String methodSig = obj.getSignature();
    outer:
    for (JavaClass cls : superClasses) {
      Method[] methods = cls.getMethods();
      for (Method m : methods) {
        if (m.isPrivate() || m.isAbstract()) continue;
        if (methodName.equals(m.getName()) && methodSig.equals(m.getSignature())) {
          BugInstance bug =
              new BugInstance(this, BugType.AOM_ABSTRACT_OVERRIDDEN_METHOD.name(), NORMAL_PRIORITY)
                  .addClass(this)
                  .addMethod(this);

          Code code = obj.getCode();
          if (code != null) bug.addSourceLineRange(clsContext, this, 0, code.getLength() - 1);
          bugReporter.reportBug(bug);

          break outer;
        }
      }
    }
  }
  /* (non-Javadoc)
   * @see edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs.classfile.IAnalysisCache, java.lang.Object)
   */
  public Method analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
      throws CheckedAnalysisException {
    JavaClass jclass =
        analysisCache.getClassAnalysis(JavaClass.class, descriptor.getClassDescriptor());
    Method[] methodList = jclass.getMethods();

    Method result = null;

    // As a side-effect, cache all of the Methods for this JavaClass
    for (Method method : methodList) {
      MethodDescriptor methodDescriptor =
          DescriptorFactory.instance()
              .getMethodDescriptor(
                  descriptor.getSlashedClassName(),
                  method.getName(),
                  method.getSignature(),
                  method.isStatic());

      // Put in cache eagerly
      analysisCache.eagerlyPutMethodAnalysis(Method.class, methodDescriptor, method);

      if (methodDescriptor.equals(descriptor)) {
        result = method;
      }
    }

    return result;
  }
 /**
  * Returns the name of the join point.
  *
  * @param method the method
  * @param methodSequence the method sequence
  * @return the name of the join point
  */
 private static String getJoinPointName(final Method method, final int methodSequence) {
   final StringBuffer joinPoint = new StringBuffer();
   joinPoint.append(TransformationUtil.MEMBER_METHOD_JOIN_POINT_PREFIX);
   joinPoint.append(method.getName());
   joinPoint.append(TransformationUtil.DELIMITER);
   joinPoint.append(methodSequence);
   return joinPoint.toString();
 }
Exemplo n.º 7
0
 /**
  * Construct a MethodDescriptor from JavaClass and method.
  *
  * @param jclass a JavaClass
  * @param method a Method belonging to the JavaClass
  * @return a MethodDescriptor identifying the method
  */
 public static MethodDescriptor getMethodDescriptor(JavaClass jclass, Method method) {
   return DescriptorFactory.instance()
       .getMethodDescriptor(
           jclass.getClassName().replace('.', '/'),
           method.getName(),
           method.getSignature(),
           method.isStatic());
 }
 /**
  * Returns the prefixed method name.
  *
  * @param method the method
  * @param methodSequence the method sequence
  * @param className the class name
  * @return the name of the join point
  */
 private String getPrefixedMethodName(
     final Method method, final int methodSequence, final String className) {
   final StringBuffer methodName = new StringBuffer();
   methodName.append(TransformationUtil.ORIGINAL_METHOD_PREFIX);
   methodName.append(method.getName());
   methodName.append(TransformationUtil.DELIMITER);
   methodName.append(methodSequence);
   methodName.append(TransformationUtil.DELIMITER);
   methodName.append(className.replace('.', '_'));
   return methodName.toString();
 }
Exemplo n.º 9
0
    public void visitMethod(Method method) {

      super.visitMethod(method);
      // now get the MethodInfo back from the ClassInfo for
      // additional work.
      String methodId = method.getName() + method.getSignature();
      OldMethodInfo mi = getITMethodInfo(methodId);
      if (JOPizer.dumpMgci) {
        // GCRT
        new GCRTMethodInfo(mi, method);
      }
    }
Exemplo n.º 10
0
  /**
   * Returns a string describing a method declaration. It contains the access flags (public,
   * private, static, etc), the return type, the method name, and the types of each of its
   * arguments.
   */
  public static String get_method_declaration(Method m) {

    StringBuilder sb = new StringBuilder();
    Formatter f = new Formatter(sb);

    f.format("%s %s %s (", get_access_flags(m), m.getReturnType(), m.getName());
    for (Type at : m.getArgumentTypes()) {
      f.format("%s, ", at);
    }
    f.format(")");
    return (sb.toString().replace(", )", ")"));
  }
  /**
   * Constructor.
   *
   * @param method an XMethod specifying a specific method in a specific class
   * @throws ClassNotFoundException
   */
  public JavaClassAndMethod(XMethod method) throws ClassNotFoundException {

    this.javaClass = Repository.lookupClass(method.getClassName());
    for (Method m : javaClass.getMethods())
      if (m.getName().equals(method.getName())
          && m.getSignature().equals(method.getSignature())
          && m.isStatic() == method.isStatic()) {
        this.method = m;
        return;
      }
    throw new IllegalArgumentException("Can't find " + method);
  }
  @Override
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();

    Method[] methodList = javaClass.getMethods();

    for (Method m : methodList) {
      MethodGen methodGen = classContext.getMethodGen(m);

      if (DEBUG) {
        System.out.println(">>> Method: " + m.getName());
      }

      // To suspect that an invalid String representation is being build,
      // we identify the construction of a MessageDigest and
      // the use of a function that trim leading 0.
      boolean invokeMessageDigest = false;
      boolean invokeToHexString = false;

      ConstantPoolGen cpg = classContext.getConstantPoolGen();
      if (methodGen == null || methodGen.getInstructionList() == null) {
        continue; // No instruction .. nothing to do
      }
      for (Iterator itIns = methodGen.getInstructionList().iterator(); itIns.hasNext(); ) {
        Instruction inst = ((InstructionHandle) itIns.next()).getInstruction();
        if (DEBUG) {
          ByteCode.printOpCode(inst, cpg);
        }

        if (inst instanceof INVOKEVIRTUAL) { // MessageDigest.digest is called
          INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) inst;
          if ("java.security.MessageDigest".equals(invoke.getClassName(cpg))
              && "digest".equals(invoke.getMethodName(cpg))) {
            invokeMessageDigest = true;
          }
        } else if (inst instanceof INVOKESTATIC
            && invokeMessageDigest) { // The conversion must occurs after the digest was created
          INVOKESTATIC invoke = (INVOKESTATIC) inst;
          if ("java.lang.Integer".equals(invoke.getClassName(cpg))
              && "toHexString".equals(invoke.getMethodName(cpg))) {
            invokeToHexString = true;
          }
        }
      }

      if (invokeMessageDigest && invokeToHexString) {
        bugReporter.reportBug(
            new BugInstance(this, BAD_HEXA_CONVERSION_TYPE, Priorities.NORMAL_PRIORITY) //
                .addClassAndMethod(javaClass, m));
      }
    }
  }
Exemplo n.º 13
0
 /** is there a JUnit3TestSuite */
 private boolean hasSuite(Method[] methods) {
   for (Method m : methods) {
     if (m.getName().equals("suite")
         && m.isPublic()
         && m.isStatic()
         // && m.getReturnType().equals(junit.framework.Test.class)
         // && m.getArgumentTypes().length == 0
         && m.getSignature().equals("()Ljunit/framework/Test;")) {
       return true;
     }
   }
   return false;
 }
Exemplo n.º 14
0
  /**
   * looks for methods that have it's parameters all follow the form arg0, arg1, arg2, or parm0,
   * parm1, parm2 etc, where the method actually has code in it
   *
   * @param cls the class to check
   */
  private void checkIDEGeneratedParmNames(JavaClass cls) {

    methods:
    for (Method m : cls.getMethods()) {
      if (!m.isPublic()) {
        continue;
      }

      String name = m.getName();
      if (Values.CONSTRUCTOR.equals(name) || Values.STATIC_INITIALIZER.equals(name)) {
        continue;
      }

      LocalVariableTable lvt = m.getLocalVariableTable();
      if (lvt == null) {
        continue;
      }

      if (m.getCode().getCode().length <= MAX_EMPTY_METHOD_SIZE) {
        continue;
      }

      int numArgs = m.getArgumentTypes().length;
      if (numArgs == 0) {
        continue;
      }

      int offset = m.isStatic() ? 0 : 1;

      for (int i = 0; i < numArgs; i++) {
        LocalVariable lv = lvt.getLocalVariable(offset + i, 0);
        if ((lv == null) || (lv.getName() == null)) {
          continue methods;
        }

        Matcher ma = ARG_PATTERN.matcher(lv.getName());
        if (!ma.matches()) {
          continue methods;
        }
      }

      bugReporter.reportBug(
          new BugInstance(
                  this,
                  BugType.IMC_IMMATURE_CLASS_IDE_GENERATED_PARAMETER_NAMES.name(),
                  NORMAL_PRIORITY)
              .addClass(cls)
              .addMethod(cls, m));
      return;
    }
  }
 /**
  * Filters the methods to be transformed.
  *
  * @param definition the definition
  * @param classMetaData the class meta-data
  * @param method the method to filter
  * @return boolean
  */
 private boolean methodFilter(
     final AspectWerkzDefinition definition,
     final ClassMetaData classMetaData,
     final MethodMetaData methodMetaData,
     final Method method) {
   if (method.isAbstract()
       || method.isNative()
       || method.getName().equals("<init>")
       || method.getName().equals("<clinit>")
       || method.getName().startsWith(TransformationUtil.ORIGINAL_METHOD_PREFIX)
       || method.getName().equals(TransformationUtil.GET_META_DATA_METHOD)
       || method.getName().equals(TransformationUtil.SET_META_DATA_METHOD)
       || method.getName().equals(TransformationUtil.CLASS_LOOKUP_METHOD)
       || method.getName().equals(TransformationUtil.GET_UUID_METHOD)) {
     return true;
   } else if (definition.hasExecutionPointcut(classMetaData, methodMetaData)) {
     return false;
   } else if (definition.hasThrowsPointcut(classMetaData, methodMetaData)) {
     return false;
   } else {
     return true;
   }
 }
  @Override
  public void visit(Method method) {
    super.visit(method);

    String methodName = method.getName();

    if ("foo".equals(methodName) || "bar".equals(methodName)) {
      reporter.reportBug(
          new BugInstance("LJCU_BUG_3", Priorities.HIGH_PRIORITY)
              .addClass(this)
              .addMethod(this)
              .addSourceLine(this));
    }
  }
Exemplo n.º 17
0
  /** Main entry for this transformer. */
  public void doTransformInterface(ClassEnhancer ce, ClassGen cg) {
    String class_name = cg.getClassName();
    ConstantPoolGen cpg = cg.getConstantPool();

    checkReadClassAttributes(ce, cg, class_name, cpg);

    generateFieldAccessForCallout(ce, cg, class_name, cpg);

    generateSuperAccessors(ce, cg, class_name, cpg);

    HashSet<String> calloutBindings = CallinBindingManager.getCalloutBindings(class_name);

    if (calloutBindings == null) {
      if (logging) printLogMessage("\nClass " + class_name + " requires no callout adjustment.");
      return;
    }

    if (logging) printLogMessage("\nCallout bindings might be changing class " + class_name + ":");

    HashSet<String> oldStyleBinding = new HashSet<String>();

    // try new style decapsulation first (since 1.2.8):
    for (String calloutBinding : calloutBindings) {
      DecapsulationDescriptor desc = new DecapsulationDescriptor();
      if (!desc.decode(calloutBinding, cg))
        oldStyleBinding.add(calloutBinding); // old style attribute
      else if (!desc.existsAlready) ce.addMethod(desc.generate(class_name, cpg), cg);
    }

    if (oldStyleBinding.isEmpty()) return;

    // --> follows: old style decapsulation for remaining bindings:
    int pos = class_name.lastIndexOf('.');
    String package_name = "NO_PACKAGE";
    if (pos != -1) package_name = class_name.substring(0, pos);

    Method[] methods = cg.getMethods();
    for (int i = 0; i < methods.length; i++) {
      Method m = methods[i];
      String method_name = m.getName();

      boolean requiresAdjustment =
          CallinBindingManager.requiresCalloutAdjustment(
              oldStyleBinding, method_name, m.getSignature());

      if (requiresAdjustment) {
        ce.decapsulateMethod(m, cg, package_name, cpg);
      }
    }
  }
  public void visitClassContext(ClassContext classContext) {
    JavaClass javaClass = classContext.getJavaClass();
    Method[] methodList = javaClass.getMethods();

    for (Method method : methodList) {
      if (method.getCode() == null) continue;

      try {
        analyzeMethod(classContext, method);
      } catch (MethodUnprofitableException e) {
        assert true; // move along; nothing to see
      } catch (CFGBuilderException e) {
        String msg =
            "Detector "
                + this.getClass().getName()
                + " caught exception while analyzing "
                + javaClass.getClassName()
                + "."
                + method.getName()
                + " : "
                + method.getSignature();
        bugReporter.logError(msg, e);
      } catch (DataflowAnalysisException e) {
        String msg =
            "Detector "
                + this.getClass().getName()
                + " caught exception while analyzing "
                + javaClass.getClassName()
                + "."
                + method.getName()
                + " : "
                + method.getSignature();
        bugReporter.logError(msg, e);
      }
    }
  }
Exemplo n.º 19
0
  /** Creates a new method info. */
  protected MethodInfo(Method m, ClassInfo c) {
    name = m.getName();
    signature = m.getSignature();
    ci = c;

    code = loadCode(m);
    exceptions = loadExceptions(m);
    thrownExceptionClassNames = loadThrownExceptionClassNames(m);
    lineNumbers = loadLineNumbers(m);
    maxLocals = getMaxLocals(m);
    maxStack = getMaxStack(m);
    localVariableNames = loadLocalVariableNames(m);
    localVariableTypes = loadLocalVariableTypes(m);

    modifiers = m.getModifiers();

    // clinits are automatically synchronized on the class object,
    // and they don't let unhandled exceptions through
    if (name.equals("<clinit>")) {
      modifiers |= Modifier.SYNCHRONIZED;
      attrs |= IS_CLINIT | FIREWALL;
    }

    if (name.equals("<init>")) {
      attrs |= IS_INIT;
    }

    if (c.isInterface()) { // all interface methods are public
      modifiers |= Modifier.PUBLIC;
    }

    // since that's used to store the method in the ClassInfo, and to
    // identify it in tne InvokeInstruction, we can set it here
    uniqueName = getUniqueName(name, signature);

    globalId = mthTable.size();
    mthTable.add(this);

    loadAnnotations(m.getAnnotationEntries());
    loadParameterAnnotations(m);
  }
Exemplo n.º 20
0
  /**
   * Find a method in given class.
   *
   * @param javaClass the class
   * @param methodName the name of the method
   * @param methodSig the signature of the method
   * @return the JavaClassAndMethod, or null if no such method exists in the class
   */
  @Deprecated
  public static @CheckForNull JavaClassAndMethod findConcreteMethod(
      JavaClass javaClass, String methodName, String methodSig) {

    if (DEBUG_METHOD_LOOKUP) {
      System.out.println("Check " + javaClass.getClassName());
    }
    Method[] methodList = javaClass.getMethods();
    for (Method method : methodList)
      if (method.getName().equals(methodName)
          && method.getSignature().equals(methodSig)
          && accessFlagsAreConcrete(method.getAccessFlags())) {
        JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method);

        return m;
      }
    if (DEBUG_METHOD_LOOKUP) {
      System.out.println("\t==> NOT FOUND");
    }
    return null;
  }
Exemplo n.º 21
0
 public static @CheckForNull JavaClassAndMethod findMethod(
     JavaClass javaClass, String methodName, String methodSig, JavaClassAndMethodChooser chooser) {
   if (DEBUG_METHOD_LOOKUP) {
     System.out.println("Check " + javaClass.getClassName());
   }
   Method[] methodList = javaClass.getMethods();
   for (Method method : methodList)
     if (method.getName().equals(methodName) && method.getSignature().equals(methodSig)) {
       JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method);
       if (chooser.choose(m)) {
         if (DEBUG_METHOD_LOOKUP) {
           System.out.println("\t==> FOUND: " + method);
         }
         return m;
       }
     }
   if (DEBUG_METHOD_LOOKUP) {
     System.out.println("\t==> NOT FOUND");
   }
   return null;
 }
  /**
   * overrides the visitor to reset the stack for the new method, then checks if the immutability
   * field is set to immutable and if so reports it
   *
   * @param obj the context object of the currently parsed method
   */
  @Override
  public void visitCode(Code obj) {
    try {
      String signature = Type.getReturnType(getMethod().getSignature()).getSignature();
      if (signature.startsWith("L")
          && CollectionUtils.isListSetMap(signature.substring(1, signature.length() - 1))) {
        stack.resetForMethodEntry(this);
        imType = ImmutabilityType.UNKNOWN;
        super.visitCode(obj);

        if ((imType == ImmutabilityType.IMMUTABLE)
            || (imType == ImmutabilityType.POSSIBLY_IMMUTABLE)) {
          Method m = getMethod();
          Statistics.getStatistics()
              .addImmutabilityStatus(clsName, m.getName(), m.getSignature(), imType);
        }
      }
    } catch (ClassNotFoundException cnfe) {
      bugReporter.reportMissingClass(cnfe);
    }
  }
  public void instrumentMethods() {
    if (initializationAgentClassnames.isEmpty()) {
      return;
    }

    Method[] methods = classfile.parsedType.getMethods();
    for (int methodIndex = 0; methodIndex < methods.length; methodIndex++) {
      Method method = methods[methodIndex];
      if (!method.getName().contains("<init>")) {
        continue;
      }

      try {
        Method newMethod =
            new InitializationMethodTransformer(this, method).instrumentInitializationAspects();
        classfile.setChanged();
        methods[methodIndex] = newMethod;
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  }
Exemplo n.º 24
0
 @Override
 public String toString() {
   return clazz.getClassName() + "." + method.getName();
 }
Exemplo n.º 25
0
 @Override
 public void visit(Method obj) {
   inConstructor = "<init>".equals(obj.getName());
 }
 /**
  * Get the MethodDescriptor that (hopefully) uniqely names this method.
  *
  * @return the MethodDescriptor uniquely naming this method
  */
 public MethodDescriptor toMethodDescriptor() {
   return DescriptorFactory.instance()
       .getMethodDescriptor(
           getSlashedClassName(), method.getName(), method.getSignature(), method.isStatic());
 }
Exemplo n.º 27
0
 /** returns whether or not the specified method is a class initializer */
 public static boolean is_clinit(Method m) {
   return (m.getName().equals("<clinit>"));
 }
Exemplo n.º 28
0
 /** returns whether or not the specified method is a constructor * */
 public static boolean is_constructor(Method m) {
   return (m.getName().equals("<init>") || m.getName().equals(""));
 }
  @Override
  public void visit(JavaClass obj) {
    String superClassname = obj.getSuperclassName();
    // System.out.println("superclass of " + getClassName() + " is " + superClassname);
    isEnum = superClassname.equals("java.lang.Enum");
    if (isEnum) return;
    int flags = obj.getAccessFlags();
    isAbstract = (flags & ACC_ABSTRACT) != 0 || (flags & ACC_INTERFACE) != 0;
    isAnonymousInnerClass = anonymousInnerClassNamePattern.matcher(getClassName()).matches();
    innerClassHasOuterInstance = false;
    for (Field f : obj.getFields()) {
      if (f.getName().equals("this$0")) {
        innerClassHasOuterInstance = true;
        break;
      }
    }

    sawSerialVersionUID = false;
    isSerializable = implementsSerializableDirectly = false;
    isExternalizable = false;
    directlyImplementsExternalizable = false;
    isGUIClass = false;
    isEjbImplClass = false;
    seenTransientField = false;
    // boolean isEnum = obj.getSuperclassName().equals("java.lang.Enum");
    fieldsThatMightBeAProblem.clear();
    transientFieldsUpdates.clear();
    transientFieldsSetInConstructor.clear();
    transientFieldsSetToDefaultValueInConstructor.clear();
    // isRemote = false;

    // Does this class directly implement Serializable?
    String[] interface_names = obj.getInterfaceNames();
    for (String interface_name : interface_names) {
      if (interface_name.equals("java.io.Externalizable")) {
        directlyImplementsExternalizable = true;
        isExternalizable = true;
        if (DEBUG) {
          System.out.println("Directly implements Externalizable: " + getClassName());
        }
      } else if (interface_name.equals("java.io.Serializable")) {
        implementsSerializableDirectly = true;
        isSerializable = true;
        if (DEBUG) {
          System.out.println("Directly implements Serializable: " + getClassName());
        }
        break;
      }
    }

    // Does this class indirectly implement Serializable?
    if (!isSerializable) {
      if (Subtypes2.instanceOf(obj, "java.io.Externalizable")) {
        isExternalizable = true;
        if (DEBUG) {
          System.out.println("Indirectly implements Externalizable: " + getClassName());
        }
      }
      if (Subtypes2.instanceOf(obj, "java.io.Serializable")) {
        isSerializable = true;
        if (DEBUG) {
          System.out.println("Indirectly implements Serializable: " + getClassName());
        }
      }
    }

    hasPublicVoidConstructor = false;
    superClassHasVoidConstructor = true;
    superClassHasReadObject = false;
    superClassImplementsSerializable = isSerializable && !implementsSerializableDirectly;
    ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor();
    if (superclassDescriptor != null)
      try {
        XClass superXClass =
            Global.getAnalysisCache().getClassAnalysis(XClass.class, superclassDescriptor);
        if (superXClass != null) {
          superClassImplementsSerializable =
              AnalysisContext.currentAnalysisContext()
                  .getSubtypes2()
                  .isSubtype(
                      superXClass.getClassDescriptor(),
                      DescriptorFactory.createClassDescriptor(java.io.Serializable.class));
          superClassHasVoidConstructor = false;
          for (XMethod m : superXClass.getXMethods()) {
            if (m.getName().equals("<init>") && m.getSignature().equals("()V") && !m.isPrivate()) {
              superClassHasVoidConstructor = true;
            }
            if (m.getName().equals("readObject")
                && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")
                && m.isPrivate()) superClassHasReadObject = true;
          }
        }
      } catch (ClassNotFoundException e) {
        bugReporter.reportMissingClass(e);
      } catch (CheckedAnalysisException e) {
        bugReporter.logError("huh", e);
      }

    // Is this a GUI  or other class that is rarely serialized?

    isGUIClass = false;
    isEjbImplClass = false;
    if (true || !directlyImplementsExternalizable && !implementsSerializableDirectly) {
      isEjbImplClass = Subtypes2.instanceOf(obj, "javax.ejb.SessionBean");
      isGUIClass =
          (Subtypes2.instanceOf(obj, "java.lang.Throwable")
              || Subtypes2.instanceOf(obj, "java.awt.Component")
              || Subtypes2.instanceOf(obj, "java.awt.Component$AccessibleAWTComponent")
              || Subtypes2.instanceOf(obj, "java.awt.event.ActionListener")
              || Subtypes2.instanceOf(obj, "java.util.EventListener"));
      if (!isGUIClass) {
        JavaClass o = obj;
        while (o != null) {
          if (o.getClassName().startsWith("java.awt")
              || o.getClassName().startsWith("javax.swing")) {
            isGUIClass = true;
            break;
          }
          try {
            o = o.getSuperClass();
          } catch (ClassNotFoundException e) {
            break;
          }
        }
      }
    }

    foundSynthetic = false;
    foundSynchronizedMethods = false;
    writeObjectIsSynchronized = false;

    sawReadExternal = sawWriteExternal = sawReadObject = sawReadResolve = sawWriteObject = false;
    if (isSerializable) {
      for (Method m : obj.getMethods()) {

        if (m.getName().equals("readObject")
            && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")) sawReadObject = true;
        else if (m.getName().equals("readResolve") && m.getSignature().startsWith("()"))
          sawReadResolve = true;
        else if (m.getName().equals("readObjectNoData") && m.getSignature().equals("()V"))
          sawReadObject = true;
        else if (m.getName().equals("writeObject")
            && m.getSignature().equals("(Ljava/io/ObjectOutputStream;)V")) sawWriteObject = true;
      }
      for (Field f : obj.getFields()) {
        if (f.isTransient()) seenTransientField = true;
      }
    }
  }