/** 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); }