package polyglot.visit; import java.util.*; import java.util.HashMap; import java.util.Map; import java.util.Set; import polyglot.ast.*; import polyglot.ast.ConstructorCall; import polyglot.ast.Node; import polyglot.ast.NodeFactory; import polyglot.frontend.Job; import polyglot.types.ConstructorInstance; import polyglot.types.Context; import polyglot.types.SemanticException; import polyglot.types.TypeSystem; import polyglot.util.InternalCompilerError; /** Visitor which ensures that field intializers and initializers do not * make illegal forward references to fields. * This is an implementation of the rules of the Java Language Spec, 2nd * Edition, Section 8.3.2.3 */ public class FwdReferenceChecker extends ContextVisitor { public FwdReferenceChecker(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); } private boolean inInitialization = false; private boolean inStaticInit = false; private Field fieldAssignLHS = null; private Set declaredFields = new HashSet(); protected NodeVisitor enterCall(Node n) throws SemanticException { if (n instanceof FieldDecl) { FieldDecl fd = (FieldDecl)n; declaredFields.add(fd.fieldInstance()); FwdReferenceChecker frc = (FwdReferenceChecker)this.copy(); frc.inInitialization = true; frc.inStaticInit = fd.flags().isStatic(); return frc; } else if (n instanceof Initializer) { FwdReferenceChecker frc = (FwdReferenceChecker)this.copy(); frc.inInitialization = true; frc.inStaticInit = ((Initializer)n).flags().isStatic(); return frc; } else if (n instanceof FieldAssign) { FwdReferenceChecker frc = (FwdReferenceChecker)this.copy(); frc.fieldAssignLHS = (Field)((FieldAssign)n).left(); return frc; } else if (n instanceof Field) { if (fieldAssignLHS == n) { // the field is on the left hand side of an assignment. // we can ignore it fieldAssignLHS = null; } else 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 if (inStaticInit == f.fieldInstance().flags().isStatic() && context().currentClass().equals(f.fieldInstance().container()) && !declaredFields.contains(f.fieldInstance()) && f.isTargetImplicit()) { throw new SemanticException("Illegal forward reference", f.position()); } } } return this; } }