/** caching infrastructure around 'doOne' */
 Map classifyMethod(HMethod hm) {
   assert isConstructor(hm);
   if (!cache.containsKey(hm)) {
     HCode hc = hcf.convert(hm);
     assert hc != null;
     cache.put(hm, doOne(hc));
   }
   return (Map) cache.get(hm);
 }
 /** Find fields which are *not* subclass-final. */
 private Set findBadFields(HCodeFactory hcf, ClassHierarchy ch) {
   Set badFields = new HashSet();
   // for each callable method...
   for (Iterator it = ch.callableMethods().iterator(); it.hasNext(); ) {
     HMethod hm = (HMethod) it.next();
     HCode hc = hcf.convert(hm);
     if (hc == null) continue; // xxx: native methods may write fields!
     // construct a must-param oracle for constructors.
     MustParamOracle mpo = isConstructor(hm) ? new MustParamOracle(hc) : null;
     // examine this method for writes
     HClass thisClass = hc.getMethod().getDeclaringClass();
     for (Iterator it2 = hc.getElementsI(); it2.hasNext(); ) {
       Quad q = (Quad) it2.next();
       if (q instanceof SET) {
         SET qq = (SET) q;
         // ignore writes of static fields.
         if (qq.isStatic()) continue;
         // if this is a constructor, than it may write only
         // to fields of 'this'
         if (isConstructor(hm)
             && mpo.isMustParam(qq.objectref())
             && mpo.whichMustParam(qq.objectref()) == 0) continue; // this is a permitted write.
         // writes by subclass methods to superclass fields are
         // okay. (but not writes by 'this' methods to 'this'
         // fields, unless the method is a constructor)
         if (qq.field().getDeclaringClass().isInstanceOf(thisClass)
             &&
             // XXX i think the presence of the 'isConstructor'
             // clause here is a bug. constructor writes
             // should be taken care of by clause above.
             (isConstructor(hm) || !thisClass.equals(qq.field().getDeclaringClass())))
           continue; // subclass writes are permitted.
         // non-permitted write!
         badFields.add(qq.field());
       }
     }
     // on to the next!
   }
   // done!  we have set of all bad (not subclass-final) fields.
   return Collections.unmodifiableSet(badFields);
 }
  /**
   * Code factory for applying <code>DynamicWBTreePass</code> to a canonical tree. Clones the tree
   * before doing transformation in-place.
   */
  public static HCodeFactory codeFactory(
      final HCodeFactory parent, final Frame frame, final Linker linker) {
    final HMethod clearHM =
        linker
            .forName("harpoon.Runtime.PreciseGC.WriteBarrier")
            .getMethod("clearBit", new HClass[] {linker.forName("java.lang.Object")});
    assert parent.getCodeName().equals(CanonicalTreeCode.codename);
    return Canonicalize.codeFactory(
        new HCodeFactory() {
          public HCode convert(HMethod m) {
            HCode hc = parent.convert(m);
            if (hc != null) {
              harpoon.IR.Tree.Code code = (harpoon.IR.Tree.Code) hc;
              // clone code...
              code = (harpoon.IR.Tree.Code) code.clone(m).hcode();
              DerivationGenerator dg = null;
              try {
                dg = (DerivationGenerator) code.getTreeDerivation();
              } catch (ClassCastException ex) {
                /* i guess not */
              }
              // ...do analysis and modify cloned code in-place.
              simplify((Stm) code.getRootElement(), dg, HCE_RULES(frame, clearHM));
              hc = code;
            }
            return hc;
          }

          public String getCodeName() {
            return parent.getCodeName();
          }

          public void clear(HMethod m) {
            parent.clear(m);
          }
        });
  }