/** * Determine if we are interested in this field on the basis of the target of the field. To wit, * if the field is static, then the target of the field must be the current class; if the field is * not static then the target must be "this". */ protected boolean isFieldsTargetAppropriate(Field f) { if (f.fieldInstance().flags().isStatic()) { ClassType containingClass = (ClassType) currCBI.currCodeDecl.codeInstance().container(); return containingClass.equals(f.fieldInstance().container()); } else { return (f.target() instanceof Special && Special.THIS.equals(((Special) f.target()).kind())); } }
/** Check that the assignment to a field is correct. */ protected void checkFieldAssign( FlowGraph graph, FieldAssign a, DataFlowItem dfIn, DataFlowItem dfOut) throws SemanticException { Field f = (Field) a.left(); FieldInstance fi = f.fieldInstance(); if (fi.flags().isFinal()) { if ((currCBI.currCodeDecl instanceof ConstructorDecl || currCBI.currCodeDecl instanceof Initializer) && isFieldsTargetAppropriate(f)) { // we are in a constructor or initializer block and // if the field is static then the target is the class // at hand, and if it is not static then the // target of the field is this. // So a final field in this situation can be // assigned to at most once. MinMaxInitCount initCount = (MinMaxInitCount) dfOut.initStatus.get(fi); if (InitCount.MANY.equals(initCount.getMax())) { throw new SemanticException( "field \"" + fi.name() + "\" might already have been assigned to", a.position()); } } else { // not in a constructor or intializer, or the target is // not appropriate. So we cannot assign // to a final field at all. throw new SemanticException( "Cannot assign a value " + "to final field \"" + fi.name() + "\"", a.position()); } } }
protected NodeVisitor enterCall(Node n) throws SemanticException { if (n instanceof FieldDecl) { FieldDecl fd = (FieldDecl) n; FwdReferenceChecker frc = (FwdReferenceChecker) this.copy(); frc.inInitialization = true; frc.inStaticInit = fd.flags().flags().isStatic(); frc.declaredFields = new HashSet<FieldDef>(declaredFields); declaredFields.add(fd.fieldDef()); return frc; } else if (n instanceof Initializer) { FwdReferenceChecker frc = (FwdReferenceChecker) this.copy(); frc.inInitialization = true; frc.inStaticInit = ((Initializer) n).flags().flags().isStatic(); return frc; } else if (n instanceof FieldAssign) { FwdReferenceChecker frc = (FwdReferenceChecker) this.copy(); return frc; } else if (n instanceof Field) { if (inInitialization) { // we need to check if this is an illegal fwd reference. Field f = (Field) n; // an illegal fwd reference if a usage of an instance // (resp. static) field occurs in an instance (resp. static) // initialization, and the innermost enclosing class or // interface of the usage is the same as the container of // the field, and we have not yet seen the field declaration. // // In addition, if a field is not accessed as a simple name, // then all is ok ClassType currentClass = context().currentClass(); StructType fContainer = f.fieldInstance().container(); if (inStaticInit == f.fieldInstance().flags().isStatic() && currentClass.typeEquals(fContainer, context) && !declaredFields.contains(f.fieldInstance().def()) && f.isTargetImplicit()) { throw new SemanticException("Illegal forward reference", f.position()); } } } return this; }
/** Perform the appropriate flow operations for assignment to a field */ protected Map flowFieldAssign( DataFlowItem inItem, FlowGraph graph, FieldAssign a, Set succEdgeKeys) { Field f = (Field) a.left(); FieldInstance fi = f.fieldInstance(); if (fi.flags().isFinal() && isFieldsTargetAppropriate(f)) { // this field is final and the target for this field is // appropriate for what we are interested in. Map m = new HashMap(inItem.initStatus); MinMaxInitCount initCount = (MinMaxInitCount) m.get(fi); // initCount may be null if the field is defined in an // outer class. if (initCount != null) { initCount = new MinMaxInitCount(initCount.getMin().increment(), initCount.getMax().increment()); m.put(fi, initCount); return itemToMap(new DataFlowItem(m), succEdgeKeys); } } return null; }