slcom/slangc/model/expressions/SubreferenceExpression.sauce

224 lines
8.6 KiB
Plaintext
Raw Permalink Normal View History

package slangc.model.expressions;
import slangc.api.BytecodeInstructionWriter;
import slangc.api.Reporter;
import slangc.bytecode.TypeSignature;
import slangc.model.ArrayTypeModel;
import slangc.model.ExpressionModel;
import slangc.model.ExpressionOwner;
import slangc.model.ExpressionResult;
import slangc.model.FieldModel;
import slangc.model.LocalStorageModel;
import slangc.model.Named;
import slangc.model.PackageModel;
import slangc.model.StorageSlot;
import slangc.model.SystemModel;
import slangc.model.TypeModel;
import slangc.model.UserTypeModel;
import slangc.parser.Branch;
import slangc.parser.ErrorType;
import slangc.parser.Node;
import slangc.parser.WarningType;
public class SubreferenceExpression extends ExpressionModel implements ExpressionOwner {
private ExpressionModel leftHandSide;
private String name;
private SpecialFieldKind specialFieldKind = null;
public static enum SpecialFieldKind {
NOT_SPECIAL,
ARRAY_LENGTH
}
public SubreferenceExpression(ExpressionOwner owner, Node source) {
super(owner, source);
leftHandSide = ExpressionModel.construct(this, ((Branch)source).getSubnode(0));
name = UserTypeModel.plainName(((Branch)source).getSubnode(2));
//sourceFile.annotate(ErrorType.INTERNAL_ERROR, "Unrecognised expression (Internal error/TODO)");
}
@Override
public void dump(Reporter reporter, String indent, String incr) {
reporter.note("DUMP", indent + "> Subreference expression '" + name + "' of:");
dumpResolved(reporter, indent + incr, incr);
leftHandSide.dump(reporter, indent + incr, incr);
}
@Override
public ExpressionModel[] getSubexpressions() {
return new ExpressionModel[] {leftHandSide};
}
public String getName() {
return name;
}
@Override
public TypeModel getExpectedResult(ExpressionModel e) {
// TODO Auto-generated method stub
return null;
}
public ExpressionModel getLeftHandSide() {
return leftHandSide;
}
@Override
protected ExpressionResult resolveResult() {
/* When resolving, we initially set the special field kind to NOT_SPECIAL and later
* set it to ARRAY_LENGTH if necessary.
*/
specialFieldKind = SpecialFieldKind.NOT_SPECIAL;
/* If the left-hand-side resolves to the start of a name (e.g. "x.y") rather than
* something like a variable or field (e.g. if "x" was declared in the scope) then
* we expect that at some point along the chain (e.g. at "x.y.Z") it will resolve to
* a type (in this case a type named "Z" in the package "x.y").
*
* This requires some special handling between Reference/Subreference and ExpressionResult.
*/
if (leftHandSide.getResult().getKind() == ExpressionResult.Kind.START_OF_NAME) {
ExpressionResult.StartOfName start = (ExpressionResult.StartOfName) leftHandSide.getResult();
SystemModel system = getOwner().getMethodOrField().getOwner().getPackage().getSystem();// TODO: Make easier way of finding this
/* If the name so far (without adding this subreference) can resolve to a package name, then
* we check if a type with our subreference's name exists within that package. If no type is
* found, we fall back to the default behaviour (in case an enclosing Subreference gives us
* more information and it ends up resolving to a type in a subpackage instead).
*/
if (system.hasPackage(start.getNameSoFar())) {
PackageModel pkg = system.getPackage(start.getNameSoFar());
if (pkg.hasType(name)) {
return new ExpressionResult.PointsToType(pkg.getType(name));
}
}
/* If we didn't resolve to a type here, the default behaviour is to create a new StartOfName
* with this subreference name added.
* This allows an outer Subreference to easily determine it's full name for further lookup.
*/
String nameSoFar = start.getNameSoFar() + "." + name;
if (!(getOwner() instanceof SubreferenceExpression)) {
getSource().annotate(ErrorType.INTERNAL_ERROR, "Failed to resolve '" + nameSoFar + "'");
}
return new ExpressionResult.StartOfName(nameSoFar);
}
/* If the left-hand-side points to a type, rather than producing some kind of runtime typeName, then
* we should only look up subtypes or static fields.
*/
if (leftHandSide.getResult().getKind() == ExpressionResult.Kind.POINTS_TO_TYPE) {
TypeModel lhsType = ((ExpressionResult.PointsToType) leftHandSide.getResult()).getType();
if (lhsType.hasInnerType(name)) {
return new ExpressionResult.PointsToType(lhsType.getInnerType(name));
} else if (lhsType.hasField(name)) {
FieldModel m = lhsType.getField(name);
if (!m.isStatic()) {
getSource().annotate(ErrorType.INTERNAL_ERROR, "The field '" + name + "' in type " + lhsType + " cannot be used in a static context");
return ExpressionResult.INVALID;
}
return new ExpressionResult.PointsToStorageSlot(lhsType.getField(name));
} else {
getSource().annotate(ErrorType.INTERNAL_ERROR, "Can't find static field or inner type '" + name + "' in type " + lhsType + " of " + leftHandSide.getResult());
return ExpressionResult.INVALID;
}
}
/* Otherwise we're dealing with a typeName, and should ideally be looking for an instance field
* (aside from a few edge cases...)
*/
if (leftHandSide.getResult().resolvesToValueOrNull()) {
if (leftHandSide.getResult().getKind() == ExpressionResult.Kind.NULL) {
getSource().annotate(ErrorType.INTERNAL_ERROR, "Can't find field or inner type '" + name + "' within, uh, null");
return ExpressionResult.INVALID;
}
// TODO: Handling of duck types and/or unresolved lambdas
TypeModel valueType = leftHandSide.getResult().getValueType();
// Special case of array length results in an integer
if (valueType instanceof ArrayTypeModel && getSystem().isArrayLengthName(name)) {//name.equals("length")) {
specialFieldKind = SpecialFieldKind.ARRAY_LENGTH;
return new ExpressionResult.TypedValue((TypeModel) lookupSimpleName("int"));
}
if (valueType.hasInnerType(name)) {
return new ExpressionResult.PointsToType(valueType.getInnerType(name));
} else if (valueType.hasField(name)) {
FieldModel m = valueType.getField(name);
// If a static field is referred to in a non-static context we should at least add a warning...
if (m.isStatic()) {
getSource().annotate(WarningType.INTERNAL_WARNING, "The field '" + name + "' in type " + valueType + " is static and shouldn't be used from an instance");
}
return new ExpressionResult.PointsToStorageSlot(valueType.getField(name));
} else {
getSource().annotate(ErrorType.INTERNAL_ERROR, "Can't find instance field or inner type '" + name + "' in type " + valueType + " of " + leftHandSide.getResult());
return ExpressionResult.INVALID;
}
}
return null;
}
public SpecialFieldKind getSpecialFieldKind() {
return specialFieldKind;
}
@Override
public TypeSignature innerGenerate(BytecodeInstructionWriter w) {
switch (getSpecialFieldKind()) {
case SpecialFieldKind.ARRAY_LENGTH:
leftHandSide.generate(w);
w.genArrayLength();
return getSystem().getDefaultInt32Type().getTypeSignature();
case SpecialFieldKind.NOT_SPECIAL:{
ExpressionResult.PointsToStorageSlot s = (ExpressionResult.PointsToStorageSlot)getResult();
StorageSlot slot = s.getSlot();
if (slot instanceof FieldModel) {
FieldModel f = (FieldModel)slot;
if (!f.isStatic()) {
leftHandSide.generate(w);
}
w.genLoad(f.getFieldSignature());
return f.getStorageType().getTypeSignature();
} else {
w.genError("TODO: innerGenerate for " + Type.of(this) + " with slot " + slot);
return null;
}
}
default:
w.genError("TODO: in innerGenerate for " + Type.of(this));
return null;
}
}
@Override
public void generateStore(BytecodeInstructionWriter w, TypeSignature storing) {
switch (getSpecialFieldKind()) {
case SpecialFieldKind.ARRAY_LENGTH:
w.genError("Can't assign array length");
break;
case SpecialFieldKind.NOT_SPECIAL:{
ExpressionResult.PointsToStorageSlot s = (ExpressionResult.PointsToStorageSlot)getResult();
StorageSlot slot = s.getSlot();
if (slot instanceof FieldModel) {
FieldModel f = (FieldModel)slot;
if (!f.isStatic()) {
leftHandSide.generate(w);
}
w.genStore(f.getFieldSignature());
} else {
w.genError("TODO: generateStore for " + Type.of(this) + " with slot " + slot);
}
break;
}
default:
w.genError("TODO: in generateStore for " + Type.of(this));
}
}
public TypeModel resolveType(Node n) {
return super.resolveType(n); // TODO: Better support for interfaces
}
}