/* hard part: the numbering */
 private void number(HCode hc, boolean callSites) {
   if (hc != null)
     for (Iterator it = hc.getElementsI(); it.hasNext(); ) {
       Quad q = (Quad) it.next();
       if ((q instanceof ANEW) || (q instanceof NEW)) alloc2int.put(q, new Integer(alloc_count++));
       else if (callSites && (q instanceof CALL)) call2int.put(q, new Integer(call_count++));
     }
 }
 /** 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);
 }
 /** returns a map from hfields to classification objects */
 private Map doOne(HCode hc) {
   Map map = mf.makeMap();
   HMethod hm = hc.getMethod();
   assert isConstructor(hm);
   HClass thisClass = hm.getDeclaringClass();
   // first, get a SimpleConstMap
   ConstMap cm = new SimpleConstMap(hc);
   // also create a MustParamOracle
   MustParamOracle mpo = new MustParamOracle(hc);
   // look at every SET and CALL.
   for (Iterator it = hc.getElementsI(); it.hasNext(); ) {
     Quad q = (Quad) it.next();
     if (q instanceof SET) {
       SET qq = (SET) q;
       // field set.
       if (qq.field().getDeclaringClass().equals(thisClass)) {
         Classification oc = (Classification) map.get(qq.field());
         // okay, is the value we're setting a constant? or param?
         Classification nc = makeClassification(cm, mpo, qq, qq.src());
         // XXX: CAN MERGE const with param if the const is
         // the 'right' value (the one we're optimizing for)
         if (oc != null) // null means we haven't seen this field yet.
         nc.merge(oc);
         map.put(qq.field(), nc);
       }
     }
     if (q instanceof CALL) {
       // deal with call to 'this' constructor.
       CALL qq = (CALL) q;
       if (isThisConstructor(qq.method(), qq)) {
         // recursively invoke!
         Map m = classifyMethod(qq.method()); // this should cache.
         // construct parameter mapping.
         Classification pc[] = new Classification[qq.paramsLength()];
         for (int i = 0; i < pc.length; i++) pc[i] = makeClassification(cm, mpo, qq, qq.params(i));
         // filter method's classifications through the mapping and
         // merge with our previous classifications.
         for (Iterator it2 = m.entrySet().iterator(); it2.hasNext(); ) {
           Map.Entry me = (Map.Entry) it2.next();
           HField hf = (HField) me.getKey();
           Classification oc = (Classification) map.get(hf);
           Classification nc = (Classification) me.getValue();
           // clone nc so we don't modify value in 'm'!
           nc = (Classification) nc.clone();
           // map method's params to this' params.
           nc.map(pc);
           if (oc != null) nc.merge(oc); // merge if necessary.
           map.put(hf, nc);
         }
       }
     }
   }
   // collect statistics.
   if (STATISTICS) {
     boolean has_const = false, has_param = false;
     for (Iterator it = map.values().iterator(); it.hasNext(); ) {
       Classification c = (Classification) it.next();
       if (c.param != -1) has_param = true;
       if (c.isConstant) has_const = true;
     }
     if (has_param) one_param_field++;
     if (has_const) one_const_field++;
     one_constructor++;
   }
   // okay, we're done!
   return Collections.unmodifiableMap(map);
 }