package polyglot.ext.jl.ast; import java.util.Collections; import java.util.List; import polyglot.ast.AmbReceiver; import polyglot.ast.Expr; import polyglot.ast.Field; import polyglot.ast.Node; import polyglot.ast.Precedence; import polyglot.ast.Receiver; import polyglot.ast.Special; import polyglot.ast.Term; import polyglot.ast.TypeNode; import polyglot.types.Context; import polyglot.types.FieldInstance; import polyglot.types.Flags; import polyglot.types.SemanticException; import polyglot.types.Type; import polyglot.types.TypeSystem; import polyglot.types.VarInstance; import polyglot.util.CodeWriter; import polyglot.util.InternalCompilerError; import polyglot.util.Position; import polyglot.visit.AscriptionVisitor; import polyglot.visit.CFGBuilder; import polyglot.visit.NodeVisitor; import polyglot.visit.PrettyPrinter; import polyglot.visit.Translator; import polyglot.visit.TypeBuilder; import polyglot.visit.TypeChecker; import polyglot.main.Options; /** * A <code>Field</code> is an immutable representation of a Java field * access. It consists of field name and may also have either a * <code>Type</code> or an <code>Expr</code> containing the field being * accessed. */ public class Field_c extends Expr_c implements Field { protected Receiver target; protected String name; protected FieldInstance fi; protected boolean targetImplicit; public Field_c(Position pos, Receiver target, String name) { super(pos); this.target = target; this.name = name; this.targetImplicit = false; if (target == null) { throw new InternalCompilerError("Cannot create a field with a null " + "target. Use AmbExpr or prefix " + "with the appropriate type node or " + "this."); } } /** Get the precedence of the field. */ public Precedence precedence() { return Precedence.LITERAL; } /** Get the target of the field. */ public Receiver target() { return this.target; } /** Set the target of the field. */ public Field target(Receiver target) { Field_c n = (Field_c) copy(); n.target = target; return n; } /** Get the name of the field. */ public String name() { return this.name; } /** Set the name of the field. */ public Field name(String name) { Field_c n = (Field_c) copy(); n.name = name; return n; } /** Return the access flags of the variable. */ public Flags flags() { return fi.flags(); } /** Get the field instance of the field. */ public FieldInstance fieldInstance() { return fi; } /** Set the field instance of the field. */ public Field fieldInstance(FieldInstance fi) { /* if (! fi.type().isCanonical()) { throw new InternalCompilerError("Type of " + fi + " in " + fi.container() + " is not canonical."); } */ Field_c n = (Field_c) copy(); n.fi = fi; return n; } public boolean isTargetImplicit() { return this.targetImplicit; } public Field targetImplicit(boolean implicit) { Field_c n = (Field_c) copy(); n.targetImplicit = implicit; return n; } /** Reconstruct the field. */ protected Field_c reconstruct(Receiver target) { if (target != this.target) { Field_c n = (Field_c) copy(); n.target = target; return n; } return this; } /** Visit the children of the field. */ public Node visitChildren(NodeVisitor v) { Receiver target = (Receiver) visitChild(this.target, v); return reconstruct(target); } public Node buildTypes(TypeBuilder tb) throws SemanticException { Field_c n = (Field_c) super.buildTypes(tb); TypeSystem ts = tb.typeSystem(); FieldInstance fi = ts.fieldInstance(position(), ts.Object(), Flags.NONE, ts.unknownType(position()), name); return n.fieldInstance(fi); } /** Type check the field. */ public Node typeCheck(TypeChecker tc) throws SemanticException { Context c = tc.context(); TypeSystem ts = tc.typeSystem(); if (! target.type().isReference()) { throw new SemanticException("Cannot access field \"" + name + "\" " + (target instanceof Expr ? "on an expression " : "") + "of non-reference type \"" + target.type() + "\".", target.position()); } FieldInstance fi = ts.findField(target.type().toReference(), name, c.currentClass()); if (fi == null) { throw new InternalCompilerError("Cannot access field on node of type " + target.getClass().getName() + "."); } Field_c f = (Field_c)fieldInstance(fi).type(fi.type()); f.checkConsistency(c); return f; } public Type childExpectedType(Expr child, AscriptionVisitor av) { if (child == target) { return fi.container(); } return child.type(); } /** Write the field to an output file. */ public void prettyPrint(CodeWriter w, PrettyPrinter tr) { if (!targetImplicit) { // explicit target. if (target instanceof Expr) { printSubExpr((Expr) target, w, tr); } else if (target instanceof TypeNode || target instanceof AmbReceiver) { print(target, w, tr); } w.write("."); } w.write(name); } public void dump(CodeWriter w) { super.dump(w); w.allowBreak(4, " "); w.begin(0); w.write("(name \"" + name + "\")"); w.end(); } public Term entry() { if (target instanceof Expr) { return ((Expr) target).entry(); } return this; } public List acceptCFG(CFGBuilder v, List succs) { if (target instanceof Expr) { v.visitCFG((Expr) target, this); } return succs; } public String toString() { return ((target != null && !targetImplicit)? target + "." : "") + name; } public List throwTypes(TypeSystem ts) { if (target instanceof Expr && ! (target instanceof Special)) { return Collections.singletonList(ts.NullPointerException()); } return Collections.EMPTY_LIST; } public boolean isConstant() { if (fi != null && (target instanceof TypeNode || (target instanceof Special && targetImplicit))) { return fi.isConstant(); } return false; } public Object constantValue() { if (isConstant()) { return fi.constantValue(); } return null; } // check that the implicit target setting is correct. protected void checkConsistency(Context c) { if (targetImplicit) { VarInstance vi = c.findVariableSilent(name); if (vi instanceof FieldInstance) { FieldInstance rfi = (FieldInstance) vi; if (c.typeSystem().equals(rfi, fi)) { // all is OK. return; } } throw new InternalCompilerError("Field " + this + " has an " + "implicit target, but the name " + name + " resolves to " + vi + " instead of " + target, position()); } } }