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 } }