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