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