224 lines
8.6 KiB
Plaintext
224 lines
8.6 KiB
Plaintext
|
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
|
||
|
}
|
||
|
}
|