380 lines
9.6 KiB
Plaintext
380 lines
9.6 KiB
Plaintext
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<String> result = new List<String>();
|
|
|
|
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.
|
|
*
|
|
* <p/>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.
|
|
*
|
|
* <p/>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");
|
|
}
|
|
}
|