/**
   * 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;
        }
      }
    }
  }
 public static @CheckForNull LocalVariableAnnotation findUniqueBestMatchingParameter(
     ClassContext classContext, Method method, String name, String signature) {
   LocalVariableAnnotation match = null;
   int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
   int startIndex = 0;
   if (!method.isStatic()) startIndex = 1;
   SignatureParser parser = new SignatureParser(method.getSignature());
   Iterator<String> signatureIterator = parser.parameterSignatureIterator();
   int lowestCost = Integer.MAX_VALUE;
   for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) {
     String sig = signatureIterator.next();
     if (signature.equals(sig)) {
       LocalVariableAnnotation potentialMatch =
           LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0);
       if (!potentialMatch.isNamed()) continue;
       int distance = EditDistance.editDistance(name, potentialMatch.getName());
       if (distance < lowestCost) {
         match = potentialMatch;
         match.setDescription(DID_YOU_MEAN_ROLE);
         lowestCost = distance;
       } else if (distance == lowestCost) {
         // not unique best match
         match = null;
       }
       // signatures match
     }
   }
   if (lowestCost < 5) return match;
   return null;
 }
Example #3
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;
  }
Example #4
0
  @Override
  public void visit(Method obj) {
    if (getMethodName().equals("suite") && !obj.isStatic()) {
      bugReporter.reportBug(
          new BugInstance(this, "IJU_SUITE_NOT_STATIC", NORMAL_PRIORITY).addClassAndMethod(this));
    }

    if (getMethodName().equals("suite") && obj.getSignature().startsWith("()") && obj.isStatic()) {
      if ((!obj.getSignature().equals("()Ljunit/framework/Test;")
              && !obj.getSignature().equals("()Ljunit/framework/TestSuite;"))
          || !obj.isPublic()) {
        bugReporter.reportBug(
            new BugInstance(this, "IJU_BAD_SUITE_METHOD", NORMAL_PRIORITY).addClassAndMethod(this));
      }
    }
  }
  /* (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;
  }
  /**
   * implements the visitor to reset the stack and proceed for private methods
   *
   * @param obj the context object of the currently parsed code block
   */
  @Override
  public void visitCode(Code obj) {
    Method m = getMethod();
    int aFlags = m.getAccessFlags();
    if ((((aFlags & Constants.ACC_PRIVATE) != 0) || ((aFlags & Constants.ACC_STATIC) != 0))
        && ((aFlags & Constants.ACC_SYNTHETIC) == 0)
        && (!m.getSignature().endsWith(")Z"))) {
      stack.resetForMethodEntry(this);
      returnRegister = -1;
      returnConstant = null;
      registerConstants.clear();
      methodSuspect = true;
      returnPC = -1;
      super.visitCode(obj);
      if (methodSuspect && (returnConstant != null)) {
        BugInstance bi =
            new BugInstance(
                    this,
                    BugType.MRC_METHOD_RETURNS_CONSTANT.name(),
                    ((aFlags & Constants.ACC_PRIVATE) != 0) ? NORMAL_PRIORITY : LOW_PRIORITY)
                .addClass(this)
                .addMethod(this);
        if (returnPC >= 0) {
          bi.addSourceLine(this, returnPC);
        }

        bi.addString(returnConstant.toString());
        bugReporter.reportBug(bi);
      }
    }
  }
Example #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());
 }
  public static @CheckForNull LocalVariableAnnotation findMatchingIgnoredParameter(
      ClassContext classContext, Method method, String name, String signature) {
    try {
      Dataflow<BitSet, LiveLocalStoreAnalysis> llsaDataflow =
          classContext.getLiveLocalStoreDataflow(method);
      CFG cfg;

      cfg = classContext.getCFG(method);
      LocalVariableAnnotation match = null;
      int lowestCost = Integer.MAX_VALUE;
      BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
      int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
      int startIndex = 0;
      if (!method.isStatic()) startIndex = 1;
      SignatureParser parser = new SignatureParser(method.getSignature());
      Iterator<String> signatureIterator = parser.parameterSignatureIterator();
      for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) {
        String sig = signatureIterator.next();
        if (!liveStoreSetAtEntry.get(i) && signature.equals(sig)) {
          // parameter isn't live and signatures match
          LocalVariableAnnotation potentialMatch =
              LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0);
          potentialMatch.setDescription(DID_YOU_MEAN_ROLE);
          if (!potentialMatch.isNamed()) return potentialMatch;
          int distance = EditDistance.editDistance(name, potentialMatch.getName());
          if (distance < lowestCost) {
            match = potentialMatch;
            match.setDescription(DID_YOU_MEAN_ROLE);
            lowestCost = distance;
          } else if (distance == lowestCost) {
            // not unique best match
            match = null;
          }
        }
      }
      return match;
    } catch (DataflowAnalysisException e) {
      AnalysisContext.logError("", e);
    } catch (CFGBuilderException e) {
      AnalysisContext.logError("", e);
    }
    return null;
  }
  /**
   * 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);
  }
Example #10
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);
      }
    }
Example #11
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;
 }
Example #12
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);
      }
    }
  }
Example #14
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);
  }
  /**
   * 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;
  }
 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);
    }
  }
  @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;
      }
    }
  }
 /**
  * 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());
 }
  private void setup() throws Exception {
    // populate BT
    BT.addAll(manifest.getBaseTypes());

    // populate BLM
    BLM.addAll(manifest.getBaseMethods());

    // populate BLS
    LS.addAll(manifest.getTransformations());

    // populate ELM
    for (Method blm : BLM)
      for (Annotation an : AnnotationReader.getAnnotations(blm.getAttributes())) {
        if (an instanceof Equivalents) {
          Set<Method> lms = new HashSet<Method>();
          ELM.put(blm, lms);
          for (String lm_name : ((Equivalents) an).value().split(",")) lms.add(rlm(lm_name));
        }
      }

    // populate ET, ETinv
    for (JavaClass bt : BT)
      for (Annotation an : AnnotationReader.getAnnotations(bt.getAttributes()))
        if (an instanceof Equivalents) {
          Set<JavaClass> tset = new HashSet<JavaClass>();
          ET.put(bt, tset);
          for (String t_name : ((Equivalents) an).value().split(",")) tset.add(rlk(t_name));
          for (JavaClass t : tset) {
            if (ETinv.containsKey(t))
              throw new InvalidLibraryException(
                  "Bad type " + t + ": already equivalent to base method " + ETinv.get(t) + ".");
            ETinv.put(t, bt);
          }
        }

    // populate VLM
    for (Method blm : BLM) {
      VLM.put(blm, new HashMap<String, Method>());
      for (Method lm : ELM.get(blm)) {
        String vsig = lm.getSignature();
        VLM.get(blm).put(vsig, lm);
      }
    }

    // VLM type check
    for (Method blm : BLM) {
      Map<Integer, JavaClass> bv = method2v(blm);
      for (Method lm : ELM.get(blm)) {
        Map<Integer, JavaClass> v = method2v(lm);
        if (!v.keySet().equals(bv.keySet()))
          throw new InvalidLibraryException(
              "Bad method " + lm + ": number of parameters differ from base method.");
        for (int i : v.keySet())
          if (!ETinv.get(v.get(i)).equals(bv.get(i)))
            throw new InvalidLibraryException(
                "Bad method " + lm + ": argument " + i + " is incompatible with base method.");
      }
    }

    // populate FLS
    for (JavaClass t : ETinv.keySet()) FLS.put(t, new HashMap<JavaClass, Method>());
    for (Method ls : LS) {
      Map<Integer, JavaClass> v = method2v(ls);
      if (!v.containsKey(-1))
        throw new InvalidLibraryException("Bad transform " + ls + ": must return an object.");
      if (v.size() != 2)
        throw new InvalidLibraryException(
            "Bad transform " + ls + ": must take exactly one argument.");
      JavaClass tu = v.get(-1);
      JavaClass tv = v.get(0);
      if (!ETinv.containsKey(tu))
        throw new InvalidLibraryException(
            "Bad transform " + ls + ": invalid source type " + tu + ".");
      if (!ETinv.containsKey(tv))
        throw new InvalidLibraryException(
            "Bad transform " + ls + ": invalid dest type " + tu + ".");
      if (!ETinv.get(tu).equals(ETinv.get(tv)))
        throw new InvalidLibraryException(
            "Bad transform " + ls + ": source and dest types not compatible.");
      FLS.get(tu).put(tv, ls);
    }

    // populate CLM
    for (Set<Method> lms : ELM.values())
      for (Method lm : lms)
        for (Annotation an : AnnotationReader.getAnnotations(lm.getAttributes()))
          /*
          if (an instanceof Cost)
              czm.put(zm, ((Cost)an).value());
              */
          if (an instanceof CostFn) {
            String zc_name = ((CostFn) an).value();
            Method zc = rlm(zc_name);
            CLM.put(lm, zc);
          }

    // populate CLS
    for (Method ls : LS) // TODO check that types are equivalent?
    for (Annotation an : AnnotationReader.getAnnotations(ls.getAttributes()))
        /*
        if (an instanceof Cost)
            czs.put(zs, ((Cost)an).value());
            */
        if (an instanceof CostFn) {
          String zc_name = ((CostFn) an).value();
          Method zc = rlm(zc_name);
          CLS.put(ls, zc);
        }
  }