890 lines
27 KiB
Plaintext
890 lines
27 KiB
Plaintext
package slangc.model;
|
|
|
|
import slangc.api.Reporter;
|
|
import slangc.bytecode.TypeSignature;
|
|
import slangc.parser.Branch;
|
|
import slangc.parser.Node;
|
|
import slangc.parser.NodeType;
|
|
import slangc.parser.Token;
|
|
|
|
public class UserTypeModel extends TypeModel {
|
|
SourceModel sourceFile;
|
|
Branch parsed;
|
|
TypeModel baseClass;
|
|
TypeModel[] interfaces;
|
|
/** This is only assigned by a subclass of StatementOrExpression which defines a type,
|
|
* specifically in the case of a new-class expression or an inner class declaration
|
|
* in a block/switch scope, and is used for looking up variables in the outer lexical
|
|
* scope (this happens separately from type lookup).
|
|
*/
|
|
public StatementOrExpression statementOrExpression;
|
|
|
|
private TemplateModel templateModel = null;
|
|
|
|
boolean isTemplateCopy = false;
|
|
//TemplateArguments directTemplateArguments = null;
|
|
UserTypeModel templateOuter = null;
|
|
UserTypeModel templateOriginal = null;
|
|
|
|
InnerTypeSet innerTypeMembers = null;
|
|
FieldSet fieldMembers = null;
|
|
MethodSet methodMembers = null;
|
|
|
|
public UserTypeModel(PackageModel packageModel, String name, SourceModel source, Branch parsed, TypeLevel level, InnerTypeScope owner) {
|
|
super(packageModel, name, level, owner);
|
|
this.sourceFile = source;
|
|
this.parsed = parsed;
|
|
}
|
|
|
|
public void setupTranslations() {
|
|
for (int i = 0; i < getAttributes().countTranslations(); i++) {
|
|
AttributeSet.Translation tr = getAttributes().getTranslation(i);
|
|
AliasTypeModel a = getPackage().getSystem().addAliasType(getPackage().getName(), getStartOfName() + tr.name, getPackage().getName(), getOuterName());
|
|
a.simplify();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean matchesName(String name) {
|
|
return super.matchesName(name) || getAttributes().matchesTranslation(null, name);
|
|
}
|
|
|
|
private UserTypeModel(UserTypeModel original, TemplateArguments targs) {
|
|
super(original.pkg, original.getSpecialisedInnerName(targs), original.level, original.owner);
|
|
sourceFile = original.sourceFile;
|
|
parsed = original.parsed;
|
|
templateModel = original.templateModel.specialised(this, targs);
|
|
while (templateModel.expand() != 0) {}
|
|
|
|
isTemplateCopy = true;
|
|
templateOriginal = original;
|
|
templateOuter = this;
|
|
|
|
String[] names = original.getInnerTypeNames();
|
|
for(int i = 0; i < names.length; i++) {
|
|
TypeModel m = original.getInnerType(names[i]);
|
|
if (m instanceof UserTypeModel) {
|
|
((UserTypeModel)m).replicateForTemplateInstance(this, this);
|
|
}
|
|
}
|
|
//directTemplateArguments = targs;
|
|
}
|
|
|
|
@Override
|
|
public boolean isReferenceOnly() {
|
|
if (isTemplateCopy) {
|
|
// Always include template instances (even if the template is defined in referenced code)
|
|
return false;
|
|
} else if (getTypeLevel() == TypeLevel.OUTER) {
|
|
return !getSource().isIncluded();
|
|
} else {
|
|
return getOwner().getNearestType().isReferenceOnly();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the type would exist at runtime and false otherwise (i.e. checks that it isn't just template that exists at compile time, but may be an *instance* of a template)
|
|
*/
|
|
@Override
|
|
public boolean existsAtRuntime() {
|
|
boolean might = false;
|
|
switch (getTypeType()) {
|
|
case TypeType.CLASS_TEMPLATE:
|
|
case TypeType.INTERFACE_TEMPLATE:
|
|
might = templateOuter == this; // A template type only exists if it's been instantiated, meaning it's templateOuter would point to itself
|
|
break;
|
|
default:
|
|
might = true;
|
|
}
|
|
//System.out.println("AAA");
|
|
if (might && getTypeLevel() == TypeLevel.OUTER) {
|
|
//System.out.println("AAAB");
|
|
return true;
|
|
} else if (might) {
|
|
//System.out.println("AAAC");
|
|
return getOwner().getNearestType().existsAtRuntime();
|
|
} else {
|
|
//System.out.println("AAAD");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private UserTypeModel(UserTypeModel original, UserTypeModel template, UserTypeModel outer) {
|
|
super(original.pkg, original.getInnerName(), original.level, outer);
|
|
sourceFile = original.sourceFile;
|
|
parsed = original.parsed;
|
|
templateModel = original.templateModel;
|
|
while (templateModel != null && templateModel.expand() != 0) {}
|
|
|
|
isTemplateCopy = true;
|
|
templateOriginal = original;
|
|
templateOuter = outer;
|
|
|
|
String[] names = original.getInnerTypeNames();
|
|
for(int i = 0; i < names.length; i++) {
|
|
TypeModel m = original.getInnerType(names[i]);
|
|
if (m instanceof UserTypeModel) {
|
|
((UserTypeModel)m).replicateForTemplateInstance(template, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private UserTypeModel replicateForTemplateInstance(UserTypeModel template, UserTypeModel outer) {
|
|
UserTypeModel t = new UserTypeModel(this, template, outer);
|
|
getPackage().addInstantiatedType(t);
|
|
return t;
|
|
}
|
|
|
|
public String getSpecialisedInnerName(TemplateArguments targs) {
|
|
return getInnerBaseName() + targs.toString();
|
|
}
|
|
|
|
public String getSpecialisedOuterName(TemplateArguments targs) {
|
|
return getOuterBaseName() + targs.toString();
|
|
}
|
|
|
|
@Override
|
|
public TypeModel getOrCreateTemplateInstance(TemplateArguments targs) {
|
|
if (getPackage().hasType(getSpecialisedOuterName(targs))) {
|
|
return getPackage().getType(getSpecialisedOuterName(targs));
|
|
}
|
|
UserTypeModel t = new UserTypeModel(this, targs);
|
|
getPackage().addInstantiatedType(t);
|
|
return t;
|
|
// TODO Auto-generated method stub
|
|
//return super.getOrCreateTemplateInstance(targs);
|
|
}
|
|
|
|
public SourceModel getSource() {
|
|
return sourceFile;
|
|
}
|
|
|
|
public Branch getParsed() {
|
|
return parsed;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSynthetic() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public TypeType getTypeType() {
|
|
switch(getParsed().getNodeType()) {
|
|
case NodeType.CLASS_DECLARATION:
|
|
if (getParsed().getSubnode(3).getNodeType() == NodeType.GENERIC_DECLARATIONS) {
|
|
return TypeType.CLASS_TEMPLATE;
|
|
} else {
|
|
return TypeType.CLASS;
|
|
}
|
|
case NodeType.NEW_CLASS_EXPRESSION:
|
|
return TypeType.CLASS;
|
|
case NodeType.INTERFACE_DECLARATION:
|
|
if (getParsed().getSubnode(3).getNodeType() == NodeType.GENERIC_DECLARATIONS) {
|
|
return TypeType.INTERFACE_TEMPLATE;
|
|
} else {
|
|
return TypeType.INTERFACE;
|
|
}
|
|
case NodeType.ATTRIBUTE_DECLARATION:
|
|
return TypeType.ATTRIBUTE;
|
|
case NodeType.ENUM_DECLARATION:
|
|
return TypeType.ENUM;
|
|
//case NodeType.ATTRIBUTE_DECLARATION:
|
|
// return TypeType.ATTRIBUTE;
|
|
default:
|
|
throw new Error("Internal error/TODO: Unrecognised node type in TypeModel: " + getParsed().getNodeType());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int countInterfaces() {
|
|
if (interfaces == null) {
|
|
return 0;
|
|
} else {
|
|
return interfaces.length;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public TypeModel getInterface(int n) {
|
|
return interfaces[n];
|
|
}
|
|
|
|
@Override
|
|
public int countFieldMembers() {
|
|
if (fieldMembers == null) {
|
|
return 0;
|
|
} else {
|
|
return fieldMembers.countMembers();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public FieldModel getFieldMember(int n) {
|
|
return fieldMembers.getMember(n);
|
|
}
|
|
|
|
@Override
|
|
public int countMethodMembers() {
|
|
if (methodMembers == null) {
|
|
return 0;
|
|
} else {
|
|
return methodMembers.countMembers();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public MethodModel getMethodMember(int n) {
|
|
return methodMembers.getMember(n);
|
|
}
|
|
|
|
@Override
|
|
public int expand() {
|
|
switch (getTypeType()) {
|
|
case TypeType.CLASS_TEMPLATE:
|
|
case TypeType.INTERFACE_TEMPLATE:
|
|
if (templateModel == null) {
|
|
templateModel = new TemplateModel(this, getParsed().getSubnode(3));
|
|
return 1;
|
|
} else {
|
|
return templateModel.expand();
|
|
}
|
|
default:
|
|
}
|
|
// TODO Auto-generated method stub
|
|
return 0;
|
|
}
|
|
|
|
public TypeModel getBaseClass() {
|
|
return baseClass;
|
|
}
|
|
|
|
public TypeModel resolveBaseClass() {
|
|
if (getParsed().getNodeType() == NodeType.NEW_CLASS_EXPRESSION) {
|
|
TypeModel t = resolveTypeReference(getParsed().getSubnode(1));
|
|
/* We need to do a bit of a fixup if it references an interface instead of a base class. */
|
|
if (t != null && (t.getTypeType() == TypeType.INTERFACE || t.getTypeType() == TypeType.INTERFACE_TEMPLATE)) {
|
|
/* In this case we assign the default base class, but we also initialise the interfaces here. */
|
|
interfaces = new TypeModel[] { t };
|
|
return getPackage().getSystem().getDefaultBaseType();
|
|
} else {
|
|
return t;
|
|
}
|
|
} else if (getTypeType() == TypeType.ENUM || getTypeType() == TypeType.CLASS || getTypeType() == TypeType.CLASS_TEMPLATE) {
|
|
Node baseNode = getParsed().getSubnode(4);
|
|
if (baseNode.getNodeType() == NodeType.NO_CLASS_BASE) {
|
|
if (getTypeType() == TypeType.ENUM) {
|
|
return getPackage().getSystem().getDefaultEnumBaseType();
|
|
} else {
|
|
return defaultBaseClass();
|
|
}
|
|
} else if (baseNode.getNodeType() == NodeType.CLASS_BASE) {
|
|
//return resolveTypeReference(baseNode);
|
|
return resolveTypeReference(((Branch)baseNode).getSubnode(1));
|
|
} else {
|
|
throw new Error("Internal error/TODO: Unrecognised base class node " + baseNode.getNodeType());
|
|
}
|
|
} else {
|
|
return defaultBaseClass();
|
|
}
|
|
}
|
|
|
|
public TypeModel defaultBaseClass() {
|
|
TypeModel def = getPackage().getSystem().getDefaultBaseType();
|
|
if (def == this) {
|
|
return null; // Avoid adding the base type as it's own base type!
|
|
} else {
|
|
return def;
|
|
}
|
|
}
|
|
|
|
public TypeModel[] resolveInterfaces() {
|
|
if (getParsed().getNodeType() == NodeType.NEW_CLASS_EXPRESSION) {
|
|
return new TypeModel[0];
|
|
} else if (getTypeType() == TypeType.CLASS || getTypeType() == TypeType.CLASS_TEMPLATE) {
|
|
Node baseNode = getParsed().getSubnode(5);
|
|
if (baseNode.getNodeType() == NodeType.NO_CLASS_IMPLEMENTS) {
|
|
return new TypeModel[0];
|
|
} else {
|
|
return resolveClassReferences(getParsed().getSubnode(5));
|
|
}
|
|
} else if (getTypeType() == TypeType.INTERFACE || getTypeType() == TypeType.INTERFACE_TEMPLATE) {
|
|
Node baseNode = getParsed().getSubnode(4);
|
|
if (baseNode.getNodeType() == NodeType.NO_INTERFACE_BASES) {
|
|
return new TypeModel[0];
|
|
} else {
|
|
return resolveClassReferences(getParsed().getSubnode(4));
|
|
}
|
|
} else {
|
|
return new TypeModel[0];
|
|
}
|
|
}
|
|
|
|
|
|
private TypeModel resolveSimpleTypeName(String name) {
|
|
TypeModel simpleLookup = super.resolveTypeName(name);
|
|
if (simpleLookup != null) {
|
|
return simpleLookup;
|
|
}
|
|
if (innerTypes.hasKey(name)) {
|
|
return innerTypes.get(name);
|
|
} else {
|
|
Object[] models = innerTypes.values().arrayCopy();
|
|
for (int mi = 0; mi < models.length; mi++) {
|
|
TypeModel m = (TypeModel)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;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public TypeModel resolveTypeName(String name) {
|
|
/*Named m = lookupSimpleName(name, true);
|
|
if (m instanceof TypeModel) {
|
|
return (TypeModel) m;
|
|
} else {
|
|
return null;
|
|
}*/
|
|
TypeModel result = super.resolveTypeName(name);
|
|
if (result == null && baseClass != null) {
|
|
result = baseClass.resolveTypeName(name);
|
|
}
|
|
if (result == null) {
|
|
result = getSource().getImports().lookupExact(name);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
result = getSource().getImports().lookupLoose(name);
|
|
if (result == null && getSource().getPackage().hasType(name)) {
|
|
return getSource().getPackage().getType(name);
|
|
}
|
|
return result;
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private void addField(Branch b, Node name) {
|
|
fieldMembers.addMember(new FieldModel(this, plainName(((Branch)name).getSubnode(0)), b, (Branch) name));
|
|
}
|
|
|
|
private void addEnumField(Branch b, Node name) {
|
|
Token t = (Token) name;
|
|
fieldMembers.addMember(new FieldModel(this, t.getSnippet().getSource(), b));
|
|
}
|
|
|
|
private void addFields(Branch b) {
|
|
Branch slots = (Branch)b.getSubnode(2);
|
|
for (int i = 0; i < slots.countSubnodes(); i++) {
|
|
Node n = slots.getSubnode(i);
|
|
if (n.getNodeType() != NodeType.COMMA) {
|
|
addField(b, n);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addMethodOrConstructor(Branch b) {
|
|
String n = null;
|
|
if (b.getNodeType() == NodeType.CONSTRUCTOR_DECLARATION) {
|
|
/* Constructors are given an implicit name of "this", i.e. corresponding with a call
|
|
* to the "this" constructor. The name doesn't matter too much (at least not until
|
|
* the code generation stage when we have to adhere to an ABI), it just needs to be
|
|
* a name that won't conflict with a normal method ("this" is fine because it's a keyword)
|
|
*/
|
|
n = "this";
|
|
} else if (b.getNodeType() == NodeType.STATIC_CONSTRUCTOR_DECLARATION) {
|
|
n = "static-init";
|
|
} else {
|
|
n = plainName(b.getSubnode(3));
|
|
}
|
|
methodMembers.addMember(new MethodModel(this, n, b));
|
|
}
|
|
|
|
private void unpackMember(Branch b) {
|
|
switch (b.getNodeType()) {
|
|
case NodeType.CLASS_DECLARATION:
|
|
case NodeType.INTERFACE_DECLARATION:
|
|
case NodeType.ENUM_DECLARATION:
|
|
innerTypeMembers.addMember(new InnerTypeModel(this, plainName(b.getSubnode(2)), b, getInnerType(plainName(b.getSubnode(2)))));
|
|
break;
|
|
case NodeType.FIELD_DECLARATION:
|
|
addFields(b);
|
|
break;
|
|
case NodeType.CONSTRUCTOR_DECLARATION:
|
|
case NodeType.STATIC_CONSTRUCTOR_DECLARATION:
|
|
case NodeType.METHOD_DECLARATION:
|
|
addMethodOrConstructor(b);
|
|
break;
|
|
case NodeType.SEMICOLON: // We allow stray semicolons around type members.
|
|
break;
|
|
case NodeType.CLASS_MEMBERS: // Used as an internal division in enums
|
|
{
|
|
for (int i = 0; i < b.countSubnodes(); i++) {
|
|
Node s = b.getSubnode(i);
|
|
//System.out.println("GOT POSSIBLE ENTRY: " + s.getNodeType());
|
|
unpackMember((Branch) s);
|
|
}
|
|
} break;
|
|
case NodeType.ENUM_MEMBER:
|
|
addEnumField(b, b.getSubnode(1));
|
|
break;
|
|
case NodeType.COMMA:
|
|
break;
|
|
default:
|
|
throw new Error("Internal error/TODO: Unable to process " + b.getNodeType());
|
|
}
|
|
}
|
|
|
|
/** This is only designed to be called when registering alias types for generic arguments. */
|
|
void registerInnerType(TypeModel t) {
|
|
if (innerTypeMembers == null) {
|
|
innerTypeMembers = new InnerTypeSet(this, MemberCategory.INNER_TYPE);
|
|
}
|
|
// TODO: Should check that it's a sane argument? Or should simplify it early?
|
|
innerTypeMembers.addMember(new InnerTypeModel(this, t.getInnerName(), null, t));
|
|
//if (innerTypes.hasKey(t.getInnerName())) {
|
|
// throw new Error("TODO/internal error: Name collision of " + t.getName());
|
|
//}
|
|
//innerTypes.put(t.getInnerName(), t);
|
|
}
|
|
|
|
private int unpackMembers() {
|
|
int count = 0;
|
|
if (innerTypeMembers == null) {
|
|
innerTypeMembers = new InnerTypeSet(this, MemberCategory.INNER_TYPE);
|
|
}
|
|
fieldMembers = new FieldSet(this, MemberCategory.FIELD);
|
|
methodMembers = new MethodSet(this, MemberCategory.METHOD);
|
|
|
|
/* If a type doesn't exist at runtime (i.e. if it's just a template which exists at compile-time) then
|
|
* it is just left as though it exists in the type tree (for the sake of later lookup and instantiation,
|
|
* error-handling, etc.) but as though it has no members (since it wouldn't make sense to look up members
|
|
* of something which isn't instantiated, especially because their type signatures wouldn't exist).
|
|
*/
|
|
if (!existsAtRuntime()) {
|
|
return 0;
|
|
}
|
|
|
|
Node n = getParsed().getSubnode(getParsed().countSubnodes() - 2);
|
|
if (n instanceof Branch) {
|
|
Branch b = (Branch) n;
|
|
|
|
for (int i = 0; i < b.countSubnodes(); i++) {
|
|
Node s = b.getSubnode(i);
|
|
//System.out.println("GOT POSSIBLE ENTRY: " + s.getNodeType());
|
|
unpackMember((Branch) s);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (getTypeType() == TypeType.ENUM) {
|
|
n = getParsed().getSubnode(getParsed().countSubnodes() - 3);
|
|
if (n instanceof Branch) {
|
|
Branch b = (Branch) n;
|
|
|
|
for (int i = 0; i < b.countSubnodes(); i++) {
|
|
Node s = b.getSubnode(i);
|
|
//System.out.println("GOT POSSIBLE ENUM ENTRY: " + s.getNodeType());
|
|
unpackMember((Branch) s);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
@Override
|
|
public AttributeSet getAttributes() {
|
|
if (attributes == null) {
|
|
if (parsed == null || getTypeLevel() == TypeLevel.UNTITLED) {
|
|
attributes = new AttributeSet(this, null);
|
|
} else {
|
|
attributes = new AttributeSet(this, (Branch) parsed.getSubnode(0)); // TODO: Consider how this may impact inner classes (attributes will be decoded here and also with the type, may need to combine both sets)
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
@Override
|
|
public int resolveTypes() {
|
|
if (attributes == null) {
|
|
getAttributes();
|
|
return 1;
|
|
} else if (interfaces == null && existsAtRuntime()) {
|
|
baseClass = resolveBaseClass();
|
|
if (interfaces == null) {
|
|
interfaces = resolveInterfaces();
|
|
return (baseClass == null ? 0 : 1) + interfaces.length;
|
|
} else {
|
|
return baseClass == null ? 0 : 1;
|
|
}
|
|
} else if (fieldMembers == null) {
|
|
return 1 + unpackMembers();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int resolveExpressions() {
|
|
int nresolved = 0;
|
|
try {
|
|
if (fieldMembers != null) {
|
|
for (int i = 0; i < fieldMembers.countMembers(); i++) {
|
|
FieldModel f = fieldMembers.getMember(i);
|
|
nresolved += f.resolveExpressions();
|
|
}
|
|
}
|
|
if (methodMembers != null) {
|
|
for (int i = 0; i < methodMembers.countMembers(); i++) {
|
|
MethodModel m = methodMembers.getMember(i);
|
|
nresolved += m.resolveExpressions();
|
|
}
|
|
}
|
|
} catch (Error e) {
|
|
throw new Error("Something failed while resolving expressions in type " + toString(), e);
|
|
}
|
|
return nresolved;
|
|
}
|
|
|
|
@Override
|
|
public void dump(Reporter reporter, String indent, String incr) {
|
|
super.dump(reporter, indent, incr);
|
|
if (statementOrExpression != null) {
|
|
reporter.note("DUMP", indent + incr + "> This type is defined within " + statementOrExpression);
|
|
}
|
|
reporter.note("DUMP", indent + incr + "> Base class is " + baseClass);
|
|
if (interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
TypeModel m = interfaces[i];
|
|
reporter.note("DUMP", indent + incr + "> Implements interface " + m);
|
|
}
|
|
}
|
|
if (fieldMembers == null) {
|
|
reporter.note("DUMP", indent + incr + "[no members yet]");
|
|
} else if (!existsAtRuntime()) {
|
|
reporter.note("DUMP", indent + incr + "[doesn't exist at runtime so no members are modelled]");
|
|
} else {
|
|
for (int i = 0; i < innerTypeMembers.countMembers(); i++) {
|
|
innerTypeMembers.getMember(i).dump(reporter, indent + incr, incr);
|
|
}
|
|
for (int i = 0; i < fieldMembers.countMembers(); i++) {
|
|
fieldMembers.getMember(i).dump(reporter, indent + incr, incr);
|
|
}
|
|
for (int i = 0; i < methodMembers.countMembers(); i++) {
|
|
methodMembers.getMember(i).dump(reporter, indent + incr, incr);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Named lookupSimpleName(String name, boolean includeOuterScopes) {
|
|
if (hasImmediatelyDefinedField(name)) {
|
|
return getImmediatelyDefinedField(name);
|
|
}
|
|
if (hasInnerType(name)) {
|
|
return getInnerType(name);
|
|
}
|
|
if (baseClass != null) {
|
|
Named result = baseClass.lookupSimpleName(name, false);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
if (interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
Named result = interfaces[i].lookupSimpleName(name, false);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
if (includeOuterScopes) {
|
|
if (statementOrExpression != null) {
|
|
Named result = statementOrExpression.lookupSimpleName(name);
|
|
if (result instanceof LocalStorageModel) {
|
|
return createUpvalueReference((LocalStorageModel)result);
|
|
} else {
|
|
return result;
|
|
}
|
|
} else if (getOwner() instanceof TypeModel) {
|
|
return ((TypeModel)getOwner()).lookupSimpleName(name, true);
|
|
}
|
|
Named imported = sourceFile.getImports().lookupExact(name);
|
|
if (imported != null) {
|
|
return imported;
|
|
}
|
|
if (getPackage().hasType(name)) {
|
|
return getPackage().getType(name);
|
|
}
|
|
return sourceFile.getImports().lookupLoose(name);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private Named createUpvalueReference(LocalStorageModel target) {
|
|
FieldModel upf = new FieldModel(this, target.getName(), target);
|
|
fieldMembers.addMember(upf);
|
|
return upf;
|
|
}
|
|
|
|
public int countUpvalues() {
|
|
int n = 0;
|
|
if (getBaseClass() instanceof UserTypeModel) {
|
|
n += ((UserTypeModel) getBaseClass()).countUpvalues();
|
|
}
|
|
for (int i = 0; i < fieldMembers.countMembers(); i++) {
|
|
if (fieldMembers.getMember(i).isUpvalue()) {
|
|
n++;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
public FieldModel[] getUpvalues() {
|
|
FieldModel[] result = new FieldModel[countUpvalues()];
|
|
int i = 0;
|
|
if (getBaseClass() instanceof UserTypeModel) {
|
|
FieldModel[] base = ((UserTypeModel) getBaseClass()).getUpvalues();
|
|
for (i = 0; i < base.length; i++) {
|
|
result[i] = base[i];
|
|
}
|
|
}
|
|
for (int j = 0; j < fieldMembers.countMembers(); j++) {
|
|
if (fieldMembers.getMember(j).isUpvalue()) {
|
|
result[i] = fieldMembers.getMember(j);
|
|
i++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public MethodModel[] lookupSimpleMethod(String name, boolean includeOuterScopes) {
|
|
MethodModel[] result = this.getMethods(name, true);
|
|
if (includeOuterScopes) {
|
|
if (statementOrExpression != null) {
|
|
result = MethodModel.addArrays(result, statementOrExpression.lookupSimpleMethod(name));
|
|
} else if (getOwner() instanceof TypeModel) {
|
|
result = MethodModel.addArrays(result, ((TypeModel)getOwner()).lookupSimpleMethod(name, true));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public boolean hasImmediatelyDefinedField(String name) {
|
|
return fieldMembers != null && fieldMembers.hasMemberNamed(name);
|
|
}
|
|
|
|
public FieldModel getImmediatelyDefinedField(String name) {
|
|
if (!hasImmediatelyDefinedField(name)) {
|
|
return null;
|
|
}
|
|
return fieldMembers.getMemberNamed(name, 0);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasField(String name, boolean includeBase) {
|
|
if (hasImmediatelyDefinedField(name)) {
|
|
return true;
|
|
}
|
|
if (includeBase && baseClass != null && baseClass.hasField(name, true)) {
|
|
return true;
|
|
}
|
|
if (includeBase && interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].hasField(name, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public FieldModel getField(String name, boolean includeBase) {
|
|
if (hasImmediatelyDefinedField(name)) {
|
|
return getImmediatelyDefinedField(name);
|
|
}
|
|
if (includeBase && baseClass != null && baseClass.hasField(name, true)) {
|
|
return baseClass.getField(name, true);
|
|
}
|
|
|
|
if (includeBase && interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].hasField(name, true)) {
|
|
return interfaces[i].getField(name, true);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public MethodModel[] getMethods(String name, boolean includeBase) {
|
|
// TODO: Remove overriden methods?
|
|
MethodModel[] result = new MethodModel[0];
|
|
if (methodMembers != null) {
|
|
MethodModel[] inner = new MethodModel[methodMembers.countMembersNamed(name)];
|
|
for (int i = 0; i < inner.length; i++) {
|
|
inner[i] = methodMembers.getMemberNamed(name, i);
|
|
}
|
|
result = MethodModel.addArrays(result, inner);
|
|
}
|
|
if (includeBase && baseClass != null && baseClass.hasMethods(name, true)) {
|
|
result = MethodModel.addArrays(result, baseClass.getMethods(name, true));
|
|
}
|
|
|
|
if (includeBase && interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].hasMethods(name, true)) {
|
|
result = MethodModel.addArrays(result, interfaces[i].getMethods(name, true));
|
|
}
|
|
}
|
|
}
|
|
// This will avoid adding any methods which are overridden or duplicated by others on the list
|
|
return MethodModel.findRelevantMethods(result, true);
|
|
}
|
|
|
|
private MethodModel createSyntheticConstructor(MethodModel baseConstructor) {
|
|
MethodModel result = new MethodModel(this, baseConstructor.getName(), baseConstructor);
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public MethodModel[] getInstanceConstructors() {
|
|
MethodModel[] localConstructors = getMethods("this", false);
|
|
if (localConstructors.length > 0) {
|
|
return localConstructors;
|
|
} else if ((getTypeType() == TypeType.CLASS || getTypeType() == TypeType.ENUM || (getTypeType() == TypeType.CLASS_TEMPLATE && existsAtRuntime())) && baseClass != null) {
|
|
MethodModel[] baseConstructors = baseClass.getInstanceConstructors();
|
|
for (int i = 0; i < baseConstructors.length; i++) {
|
|
MethodModel b = baseConstructors[i];
|
|
if (b.countParameters() == 0 || ((getTypeType() == TypeType.ENUM || getTypeLevel()==TypeLevel.UNTITLED) && !b.isPrivate())) {
|
|
// TODO: Generate anonymous constructors properly
|
|
methodMembers.addMember(createSyntheticConstructor(b));
|
|
}
|
|
/*if (!b.isPrivate()) {
|
|
methodMembers.addMember(createSyntheticConstructor(b));
|
|
}*/
|
|
}
|
|
return getMethods("this", false);
|
|
} else {
|
|
return new MethodModel[0];
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasInnerType(String name, boolean includeBase) {
|
|
if (super.hasInnerType(name, includeBase)) {
|
|
return true;
|
|
}
|
|
if (includeBase && baseClass != null && baseClass.hasInnerType(name, true)) {
|
|
return true;
|
|
}
|
|
if (includeBase && interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].hasInnerType(name, true)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public TypeModel getInnerType(String name, boolean includeBase) {
|
|
if (super.hasInnerType(name, false)) {
|
|
return super.getInnerType(name, false);
|
|
}
|
|
if (includeBase && baseClass != null && baseClass.hasInnerType(name, true)) {
|
|
return baseClass.getInnerType(name, true);
|
|
}
|
|
|
|
if (includeBase && interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].hasInnerType(name, true)) {
|
|
return interfaces[i].getInnerType(name, true);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean inherits(TypeModel otherType) {
|
|
otherType = otherType.simplify();
|
|
|
|
//System.err.println("Checking if " + this + " inherits from " + otherType);
|
|
if (baseClass == otherType) {
|
|
//System.err.println("Hit this");
|
|
return true;
|
|
} else if (baseClass != null && baseClass.inherits(otherType)) {
|
|
//System.err.println("Hit base");
|
|
return true;
|
|
} else if (interfaces != null) {
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i] == otherType || interfaces[i].inherits(otherType)) {
|
|
//System.err.println("Hit interface");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//System.err.println("Check failed");
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isObjectType() {
|
|
return true;
|
|
}
|
|
|
|
public boolean isInnerType() {
|
|
// TODO: Handle static declaration
|
|
return getTypeLevel() == TypeLevel.INNER
|
|
|| getTypeLevel() == TypeLevel.INNER_INNER
|
|
|| getTypeLevel() == TypeLevel.UNTITLED;
|
|
}
|
|
|
|
public boolean isNonStaticInnerType() {
|
|
return isInnerType() && !isStatic();
|
|
}
|
|
|
|
public boolean isStatic() {
|
|
return getTypeType() == TypeType.INTERFACE
|
|
|| getTypeType() == TypeType.INTERFACE_TEMPLATE
|
|
|| getTypeType() == TypeType.ENUM
|
|
|| getTypeLevel() == TypeLevel.OUTER
|
|
|| getAttributes().isStatic();
|
|
}
|
|
|
|
@Override
|
|
public int getFlags() {
|
|
int f = super.getFlags();
|
|
|
|
if (isStatic()) {
|
|
f |= Flags.MASK_STATIC;
|
|
}
|
|
|
|
if (isInnerType()) {
|
|
f |= Flags.MASK_INNER;
|
|
}
|
|
|
|
return f;
|
|
}
|
|
}
|