/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.ast;
import java.util.Collections;
import java.util.List;
import polyglot.types.*;
import polyglot.util.*;
import polyglot.visit.*;
import x10.types.constants.ConstantValue;
/**
* 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 abstract class Field_c extends Expr_c implements Field
{
protected Receiver target;
protected Id name;
protected FieldInstance fi;
protected boolean targetImplicit;
public Field_c(Position pos, Receiver target, Id name) {
super(pos);
assert(target != null && name != null);
this.target = target;
this.name = name;
this.targetImplicit = false;
e = new Exception();
}
Exception e;
/** 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 Id name() {
return this.name;
}
/** Set the name of the field. */
public Field name(Id 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 varInstance() {
return fi;
}
/** 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 == this.fi) return this;
Field_c n = (Field_c) copy();
assert fi != null;
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, Id name) {
if (target != this.target || name != this.name) {
Field_c n = (Field_c) copy();
n.target = target;
n.name = name;
return n;
}
return this;
}
/** Visit the children of the field. */
public Node visitChildren(NodeVisitor v) {
Receiver target = (Receiver) visitChild(this.target, v);
Id name = (Id) visitChild(this.name, v);
return reconstruct(target, name);
}
public Node buildTypes(TypeBuilder tb) {
Field_c n = (Field_c) super.buildTypes(tb);
TypeSystem ts = tb.typeSystem();
FieldInstance fi = ts.createFieldInstance(position(),
new ErrorRef_c<FieldDef>(ts, position(), "Cannot get FieldDef before type-checking field access.") {
private static final long serialVersionUID = 7823901508583320051L;
public String toString() { e.printStackTrace(); return super.toString(); } });
return n.fieldInstance(fi);
}
/** Type check the field. */
public abstract Node typeCheck(ContextVisitor tc);
public Node checkConstants(ContextVisitor tc) {
// Just check if the field is constant to force a dependency to be
// created.
isConstant();
return this;
}
/** Write the field to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
w.begin(0);
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.allowBreak(2, 3, "", 0);
}
tr.print(this, name, w);
w.end();
}
public void dump(CodeWriter w) {
super.dump(w);
if (fi != null) {
w.allowBreak(4, " ");
w.begin(0);
w.write("(instance " + fi + ")");
w.end();
}
w.allowBreak(4, " ");
w.begin(0);
w.write("(name \"" + name + "\")");
w.end();
}
public Term firstChild() {
if (target instanceof Term) {
return (Term) target;
}
return null;
}
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
if (target instanceof Term) {
v.visitCFG((Term) target, this, EXIT);
}
return succs;
}
public String toString() {
return (target != null ? target + "." : "") + name;
}
public List<Type> throwTypes(TypeSystem ts) {
if (target instanceof Expr && ! (target instanceof Special)) {
return Collections.<Type>singletonList(ts.NullPointerException());
}
return Collections.<Type>emptyList();
}
public boolean isConstant() {
if (fi != null &&
(target instanceof TypeNode ||
(target instanceof Special && targetImplicit))) {
return fi.isConstant();
}
return false;
}
public ConstantValue constantValue() {
if (isConstant()) {
return fi.constantValue();
}
return null;
}
/**
* Check the consistency of the implicit target inserted by the compiler by
* asserting that the FieldInstance in the Context for this field's name is
* the same as the FieldInstance we assigned to this field.
*/
protected void checkConsistency(Context c) {
if (targetImplicit) {
VarInstance<?> vi = c.findVariableSilent(name.id());
if (vi instanceof FieldInstance) {
FieldInstance rfi = (FieldInstance) vi;
// Compare the original (declaration) fis, not the actuals.
// We do this because some extensions that do type substitutions
// perform the substitution
// on the fi after lookup and some extensions modify lookup
// itself to do the substitution.
if (c.typeSystem().equals(rfi.def(), fi.def())) {
// all is OK.
return;
}
System.out.println("(found) rfi is " + rfi.def());
System.out.println("(actual) fi is " + fi.def());
}
throw new InternalCompilerError("Field " + this + " has an " +
"implicit target, but the name " + name.id() + " resolves to " +
vi + " instead of " + target, position());
}
}
}