package slangc.model; import slang.data.ComparisonOp; import slang.data.List; import slang.data.Mappable; import slangc.api.Reporter; import slangc.bytecode.TypeSignature; import slangc.model.clauses.Arguments; import slangc.model.statements.CaseLabelStatement; import slangc.parser.Branch; public abstract class TypeModel extends InnerTypeScope implements Named, AttributeOwner, Mappable { PackageModel pkg; String name; TypeLevel level; private int nextUntitledNumber = 0; private ArrayTypeModel arrayOfThis = null; AttributeSet attributes = null; public TypeModel(PackageModel packageModel, String name, TypeLevel level, InnerTypeScope owner) { super(owner); this.pkg = packageModel; this.name = name; this.level = level; this.owner = owner; if (level != TypeLevel.OUTER) { if (owner.innerTypes.hasKey(name)) { throw new Error("Internal error/TODO: Type '" + fullName() + "' includes inner type '" + name + "' twice"); } if (level == TypeLevel.INNER_INNER) { // The public name is given using a # but for the purpose of scoped lookup we keep it simple owner.innerTypes.set(name.sub(0, name.searchFirst("#")), this); } else { owner.innerTypes.set(name, this); } } } public String fullName() { return (pkg.getName() == "" ? "" : (pkg.getName() + ".")) + getOuterName(); } public String baseName() { return (pkg.getName() == "" ? "" : (pkg.getName() + ".")) + getOuterBaseName(); } @Override public PackageModel getPackage() { return pkg; } public TypeLevel getTypeLevel() { return level; } /** Gets the outer-level equivalent of this name. E.g. For the class foo.Bar it would be bar, but for foo.Bar.Baz it might be "Bar:Baz". */ @Override public String getOuterName() { if (level == TypeLevel.OUTER) { return name; } else { return owner.getOuterName() + ":" + name; } } public String getStartOfName() { if (level == TypeLevel.OUTER) { return ""; } else { return owner.getOuterName() + ":"; } } public String getOuterBaseName() { if (level == TypeLevel.OUTER) { return getInnerBaseName(); } else { return owner.getOuterName() + ":" + getInnerBaseName(); } } public String getInnerName() { return name; } /** Returns the name minus any template arguments or placeholders. */ public String getInnerBaseName() { if (name.search("<")) { return name.sub(0, name.searchLast("<")); } else { return name; } } public int nextUntitledNumber() { int result = nextUntitledNumber; nextUntitledNumber++; return result; } public abstract boolean isSynthetic(); public abstract TypeType getTypeType(); public abstract int expand(); public void dump(Reporter reporter, String indent, String incr) { reporter.note("DUMP", indent + "> Type '" + getOuterName() + "' (" + getTypeType() + ", " + getTypeLevel() + ")" + (existsAtRuntime() ? "" : " [doesn't exist at runtime]")); } @Override public TypeModel resolveTypeName(String name) { TypeModel simpleLookup = super.resolveTypeName(name); if (simpleLookup != null) { return simpleLookup; } if (innerTypes.hasKey(name)) { return innerTypes.get(name); } else { TypeModel[] models = innerTypes.values().arrayCopy(); for (int mi = 0; mi < models.length; mi++) { TypeModel m = models[mi]; if (m.matchesName(name)) { return m; } } if (owner != null) { return owner.resolveTypeName(name); } else if (getPackage().hasType(name)) { return getPackage().getType(name); } else { // TODO: Handle super-types here? return null; } } } public abstract int resolveTypes(); public TypeModel getOrCreateTemplateInstance(TemplateArguments targs) { return null; // This error is handled by the (main) caller //throw new Error("Internal error/TODO: This isn't a template type or instantiation hasn't been implemented here"); } public boolean hasInnerType(String innerName, boolean includeBase) { return innerTypes.hasKey(innerName); } public boolean hasInnerType(String innerName) { return hasInnerType(innerName, true); } public TypeModel getInnerType(String innerName, boolean includeBase) { return innerTypes.get(innerName); } public TypeModel getInnerType(String innerName) { return getInnerType(innerName, true); } public String[] getInnerTypeNames() { List result = new List(); result.appendList(innerTypes.keys()); result.sort(String.getDefaultComparisonOp()); return result.arrayCopy(); } public boolean existsAtRuntime() { if (getTypeLevel() == TypeLevel.OUTER) { return true; } else { return getOwner().getNearestType().existsAtRuntime(); } } public boolean isReferenceOnly() { return false; } @Override public String toString() { return fullName(); } /** * For an alias type (or similar), the simplify method should return the actual target type. * This means that if an alias points to another alias (which eventually points to a concrete type) * this method must traverse every alias. * * @return */ public TypeModel simplify() { return this; } public TypeModel getOrCreateArrayInstance() { if (simplify() != this && simplify() != null) { return simplify().getOrCreateArrayInstance(); } if (arrayOfThis == null) { arrayOfThis = new ArrayTypeModel(this); getPackage().addSyntheticType(arrayOfThis); } return arrayOfThis; } //@Override public String getName() { return getInnerName(); } public boolean isAssignableFrom(TypeModel otherType) { if (otherType == null) { return false; } else if (this.simplify() == otherType.simplify()) { return true; } else { return otherType.inherits(this); } } /** * Should return true if and only if this type inherits from the other type. * *

Note, for the purposes of being specific, "this" type shouldn't be considered * to inherit itself. This test should only match if a base class or interface is the * other type or inherits it indirectly. * *

If this is a class definition and "otherType" is the root class then this should * always return true (provided the type model is actually set up correctly!). If this * is an interface and "otherType" is a class then this should always return false (in * particular, an interface type is not considered to inherit from the root class). * TODO: Check interface behaviour is correct. */ public boolean inherits(TypeModel otherType) { return false; } public abstract int resolveExpressions(); public boolean hasField(String name, boolean includeBase) { return false; } public FieldModel getField(String name, boolean includeBase) { return null; } public final boolean hasField(String name) { return hasField(name, true); } public final FieldModel getField(String name) { return getField(name, true); } public boolean hasMethods(String name, boolean includeBase) { return countMethods(name, includeBase) > 0; } public int countMethods(String name, boolean includeBase) { return getMethods(name, includeBase).length; } public MethodModel[] getMethods(String name, boolean includeBase) { return new MethodModel[0]; } public Named lookupSimpleName(String name) { return lookupSimpleName(name, true); } public Named lookupSimpleName(String name, boolean includeOuterScopes) { throw new Error("TODO"); } public MethodModel[] lookupSimpleMethod(String name) { return lookupSimpleMethod(name, true); } public MethodModel[] lookupSimpleMethod(String name, boolean includeOuterScopes) { throw new Error("TODO"); } public MethodModel[] getInstanceConstructors() { return new MethodModel[0]; } public MethodModel getDefaultInstanceConstructor() { MethodModel[] constrs = getInstanceConstructors(); for (int i = 0; i < constrs.length; i++) { if (constrs[i].countParameters() == 0) { return constrs[i]; } } return null; } public abstract boolean isObjectType(); public int countInterfaces() { return 0; } public TypeModel getInterface(int n) { throw new Error("This type has no interfaces"); } public int countFieldMembers() { return 0; } public FieldModel getFieldMember(int i) { throw new Error("This type has no fields"); } public int countMethodMembers() { return 0; } public MethodModel getMethodMember(int i) { throw new Error("This type has no methods"); } public TypeSignature getTypeSignature() { return new TypeSignature(getPackage().getName(), getOuterName()); } public AttributeSet getAttributes() { if (attributes == null) { attributes = new AttributeSet(this, null); } return attributes; } public int getFlags() { int f = getAttributes().getFlags(); switch (getTypeType()) { case TypeType.CLASS: case TypeType.CLASS_TEMPLATE: f |= Flags.MASK_CLASS; break; case TypeType.INTERFACE: case TypeType.INTERFACE_TEMPLATE: f |= Flags.MASK_INTERFACE; break; case TypeType.ENUM: f |= Flags.MASK_ENUM; break; } if (isSynthetic()) { f |= Flags.MASK_SYNTHETIC; } return f; } protected void setAttributes(AttributeSet attributes) { this.attributes = attributes; } public boolean matchesName(String n) { return this.name.equals(n); } public boolean mappableEquals(Mappable o) { throw new Error("TODO"); } public int mappableHash() { throw new Error("TODO"); } }