/* * System.Reflection-like API for access to .NET assemblies (DLL & EXE) */ package ch.epfl.lamp.compiler.msil; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Arrays; /** * Represents type declarations: class types, interface types, array types, * value types, and enumeration types. * * @author Nikolay Mihaylov * @version 1.0 */ public abstract class Type extends MemberInfo { private java.util.List /* GenericParamAndConstraints */ tVars = new java.util.LinkedList(); private GenericParamAndConstraints[] sortedTVars = null; public void addTVar(GenericParamAndConstraints tvarAndConstraints) { sortedTVars = null; tVars.add(tvarAndConstraints); } public GenericParamAndConstraints[] getSortedTVars() { if(sortedTVars == null) { sortedTVars = new GenericParamAndConstraints[tVars.size()]; for (int i = 0; i < sortedTVars.length; i ++){ Iterator iter = tVars.iterator(); while(iter.hasNext()) { GenericParamAndConstraints tvC = (GenericParamAndConstraints)iter.next(); if(tvC.Number == i) { sortedTVars[i] = tvC; } } } } return sortedTVars; } //########################################################################## // public static members /** Empty array of type Type. */ public static final Type[] EmptyTypes = new Type[0]; /** Separates names in the namespace of the Type. */ public static final char Delimiter = '.'; //########################################################################## // public properties /** The fully qualified name of the Type. */ public final String FullName; /** The namespace of the Type. */ public final String Namespace; /** The type from which the current Type directly inherits. */ public final Type BaseType() { initBaseType(); return baseType; } protected Type baseType; /** The attributes associated with the Type. */ public final int Attributes; /** The sssembly that the type is declared in. */ public final Assembly Assembly() { return Module.Assembly; } /** The module (the EXE/DLL) in which the current Type is defined. */ public final Module Module; public final int MemberType() { return DeclaringType == null ? MemberTypes.TypeInfo : MemberTypes.NestedType; } //########################################################################## // internal members // Fields declared by this class protected FieldInfo[] fields; // Methods declared by this class protected MethodInfo[] methods; // Constructors of this class protected ConstructorInfo[] constructors; // Properties of the class protected PropertyInfo[] properties; // Events of the class protected EventInfo[] events; // Interfaces implemented by this class protected Type[] interfaces; // Nested types declared by this class protected Type[] nestedTypes; // holds the element type of array, pointer and byref types private final Type elemType; // the underlying type of an enumeration. null if the type is not enum. protected Type underlyingType; protected int auxAttr; //########################################################################## // Map with all the types known so far and operations on it private static final Map types = new HashMap(); protected static Type getType(String name) { return (Type) types.get(name); } protected static Type addType(Type t) { assert(!(t instanceof TMVarUsage)); assert(!(t instanceof ConstructedType)); Type oldType = (Type) types.put(t.FullName, t); // if (oldType != null) // throw new RuntimeException("The type: [" + t.Assembly + "]" + t // + " replaces the type: [" + // oldType.Assembly + "]" + oldType); return t; } //########################################################################## /** The main constructor. */ protected Type(Module module, int attr, String fullName, Type baseType, Type[] interfaces, Type declType, int auxAttr, Type elemType) { super(fullName.lastIndexOf(Delimiter) < 0 ? fullName : fullName.substring(fullName.lastIndexOf(Delimiter) + 1, fullName.length()), declType); Module = module; // null only for TMVarUsage and for PrimitiveType Attributes = attr; this.baseType = baseType; if (DeclaringType == null) { FullName = fullName; int i = FullName.lastIndexOf(Delimiter); Namespace = (i < 0) ? "" : FullName.substring(0,i); } else { FullName = declType.FullName + "+" + fullName; Namespace = DeclaringType.Namespace; } this.interfaces = interfaces; this.elemType = elemType; this.auxAttr = auxAttr; } public final boolean IsAbstract() { return (Attributes & TypeAttributes.Abstract) != 0; } public final boolean IsPublic() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public; } public final boolean IsNotPublic() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic; } public final boolean IsNestedPublic() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic; } public final boolean IsNestedPrivate() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate; } public final boolean IsNestedFamily() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily; } public final boolean IsNestedAssembly() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly; } public final boolean IsNestedFamORAssem() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem; } public final boolean IsNestedFamANDAssem() { return (Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem; } public final boolean IsSealed() { return (Attributes & TypeAttributes.Sealed) != 0; } public final boolean IsSpecialName() { return (Attributes & TypeAttributes.SpecialName) != 0; } public final boolean IsClass() { return (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class; } public final boolean IsInterface(){ return (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface; } public final boolean IsAutoLayout() { return (Attributes & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout; } public final boolean IsExplictitLayout() { return (Attributes & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout; } public final boolean IsLayoutSequential() { return (Attributes & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout; } public final boolean IsImport() { return (Attributes & TypeAttributes.Import) != 0; } public final boolean IsSerializable() { return (Attributes & TypeAttributes.Serializable) != 0; } public final boolean IsAnsiClass() { return (Attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass; } public final boolean IsUnicodeClass() { return (Attributes & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass; } public final boolean IsAutoClass() { return (Attributes & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass; } public final boolean IsArray() { return (auxAttr & AuxAttr.Array) != 0; } public final boolean IsByRef() { return (auxAttr & AuxAttr.ByRef) != 0; } public final boolean IsPointer() { return (auxAttr & AuxAttr.Pointer) != 0; } public final boolean IsPrimitive() { return (auxAttr & AuxAttr.Primitive) != 0; } public final boolean IsValueType() { return BaseType() == VALUE_TYPE() || IsEnum(); } public final boolean IsEnum() { return BaseType() == ENUM(); } public boolean CanBeTakenAddressOf() { /* TODO should be overridden in TMVarUsage, but there's currently no way to bind a TMVarUsage to its GenericParamAndConstraints definition. Why? Because of the way the msil library is organized (e.g., mkArray() returns the same !0[] representation for all !0[] usages, irrespective of the scope of the !0 type-param) This in turn is so because without generics there's no harm in using a type-def instance where a type-ref should go (e.g., the ParameterType of a ParameterInfo nowadays may point to a PEType). The net effect is that this method (CanBeTakenAddressOf) is conservative, it will answer "no" for example for !0 where !0 refers to a type-param with the isValuetype constraint set. The whole thing is ok at this point in time, where generics are not supported at the backend. */ return IsValueType() && (this != ENUM()); /* ENUM() is a singleton, i.e. System.Enum is not generic */ } /** IsGeneric, true for a PEType or TypeBuilder (i.e., a type definition) * containing one or more type params. Not to be called on a reference * to a constructed type. */ public final boolean IsGeneric() { return tVars.size() > 0; } public final boolean HasElementType() { return IsArray() || IsPointer() || IsByRef(); } public boolean IsTMVarUsage() { // overridden in TMVarUsage return false; } public boolean IsNestedType() { return DeclaringType != null; } public boolean IsDefinitelyInternal() { if(IsNestedType()) { return IsNestedPrivate(); } else { return IsNotPublic(); } } //public final boolean IsCOMObject; //public final boolean IsContextful; //public final boolean IsMarshalByRef; protected Type(Module module, int attr, String fullName, Type baseType, Type[] interfaces, Type declType, int auxAttr) { this(module, attr, fullName, baseType, interfaces, declType, auxAttr, null); } //########################################################################## public static final class TMVarUsage extends Type { public final int Number; public final boolean isTVar; /** Non-defining reference to either a TVar or an MVar. * An instance of GenericParamAndConstraints represents a TVar or an MVar definition. */ public TMVarUsage(int Number, boolean isTVar) { super(null, 0, ((isTVar ? "!" : "!!") + Number), null, null, null, AuxAttr.None, null); this.Number = Number; this.isTVar = isTVar; } public String toString() { return (isTVar ? "!" : "!!") + Number; } public final boolean IsTMVarUsage() { return true; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TMVarUsage that = (TMVarUsage) o; if (Number != that.Number) return false; if (isTVar != that.isTVar) return false; return true; } public int hashCode() { int result = Number; result = 31 * result + (isTVar ? 1 : 0); return result; } } protected static final class AuxAttr { public static final int None = 0x0000; public static final int Array = 0x0001; public static final int ByRef = 0x0002; public static final int Pointer = 0x0008; public static final int Primitive = 0x0010; } /***/ public static Type mkArray(Type elemType, int rank) { StringBuffer arrSig = new StringBuffer("["); for (int i = 0; i < rank; i++) { if (i > 0) arrSig.append(','); } arrSig.append(']'); Type array = getType(elemType.FullName + arrSig); if (array != null) return array; array = new PrimitiveType(elemType.Module, elemType.Attributes | TypeAttributes.Sealed | TypeAttributes.Serializable, elemType.FullName + arrSig, ARRAY(), EmptyTypes, null, AuxAttr.Array, elemType); return addType(array); } /***/ public static Type mkPtr(Type elemType) { String name = elemType.FullName + "*"; Type type = getType(name); if (type != null) return type; type = new PrimitiveType(elemType.Module, elemType.Attributes, name, null, EmptyTypes, null, AuxAttr.Pointer, elemType); return addType(type); } /***/ public static Type mkByRef(Type elemType) { String name = elemType.FullName + "&"; Type type = getType(name); if (type != null) return type; type = new PrimitiveType(elemType.Module, elemType.Attributes, name, null, EmptyTypes, null, AuxAttr.ByRef, elemType); return addType(type); } //########################################################################## // public methods /** * Return the type with the specified signature parameters. * For example, the fully qualified name for a class might look like this: * TopNamespace.SubNameSpace.ContainingClass+NestedClass,MyAssembly */ public static Type GetType(String fullName) { Type type = getType(fullName); if (type != null) return type; // check if it's an array type; TODO: make array type handling more robust int i = fullName.lastIndexOf('['); int j = fullName.lastIndexOf(']'); if (i >= 0) if (j > i && j == (fullName.length() - 1)) { String elementTypeName = fullName.substring(0, i); Type elementType = GetType(elementTypeName); if (elementType == null) throw new RuntimeException ("Unknown element type '" + elementTypeName + "' for the array type: " + fullName); int rank = j - i; for (int k = i + 1; k < j; k++) { if (fullName.charAt(k) != ',') throw new RuntimeException ("Malformed type name: " + fullName); } return mkArray(elementType, rank); } else throw new RuntimeException("Malformed type name: " + fullName); // check if it's a pointer type if (fullName.charAt(fullName.length() - 1) == '*') return addType (mkPtr(GetType(fullName.substring(0, fullName.length()-1)))); // check if it's a nested class i = fullName.lastIndexOf('+'); if (i > 0) { if (i == 0 || i == (fullName.length() - 1)) throw new RuntimeException("malformedTypeName"); Type enclosing = GetType(fullName.substring(0, i)); return enclosing == null ? null : enclosing.GetNestedType(fullName.substring(i + 1)); } //System.out.println("Looking for type: " + fullName + " (" + fullName.length() + ")"); // try in the assemblies Iterator assems = ch.epfl.lamp.compiler.msil.Assembly. assemblies.values().iterator(); while (type == null && assems.hasNext()) { Assembly assem = ((Assembly) assems.next()); type = assem.GetType(fullName); //System.out.println("\tin assemby " + assem + " -> " + type); } Type type2 = getType(fullName); if (type == type2) return type; return type == null ? null : addType(type); } /** * @return the type of the object encompassed or referenced to * by the current array, pointer or reference type. */ public Type GetElementType() { return elemType; } /** * @return the type underlying an enumeration type. */ public Type getUnderlyingType() { if (!IsEnum()) return null; // this would force the loading of the underlying type from the // the type of the value__ field of the enumeration initFields(); return underlyingType; } //########################################################################## // GetField/s/ /** Searches for the field with the specified name. */ public FieldInfo GetField(String name) { initFields(); for (int i = 0; i < fields.length; i++) if (fields[i].Name.equals(name) && !fields[i].IsPrivate()) return fields[i]; return null; } /** */ public FieldInfo GetField(String name, int bindingFlags) { FieldInfo[] fields = this.GetFields(bindingFlags); for (int i = 0; i < fields.length; i++) if (name.equals(fields[i].Name)) return fields[i]; return null; } /** Gets the fields of the current Type. */ public FieldInfo[] GetFields() { return GetFields(BindingFlags.Instance | BindingFlags.Public); } /** */ public FieldInfo[] GetFields(int bindingFlags) { initFields(); final FieldInfo[] fields = getAllFields((bindingFlags & BindingFlags.DeclaredOnly) != 0); final boolean getInstance = (bindingFlags & BindingFlags.Instance) != 0; final boolean getStatic = (bindingFlags & BindingFlags.Static) != 0; final boolean getPublic = (bindingFlags & BindingFlags.Public) != 0; final boolean getNonPublic = (bindingFlags & BindingFlags.NonPublic) != 0; int cnt = 0; for (int i = 0; i < fields.length; i++) { FieldInfo field = fields[i]; boolean accessible = (getPublic && field.IsPublic()) || (getNonPublic && !field.IsPublic()); if (accessible // strip off the private fields up the hierarchy && ((field.DeclaringType == this) || ((field.DeclaringType != this) && !field.IsPrivate())) && ((getInstance && !field.IsStatic()) || ((getStatic && field.IsStatic()) && (field.DeclaringType == this || (bindingFlags & BindingFlags.FlattenHierarchy) != 0)) ) ) fields[cnt++] = field; } FieldInfo [] resFields = new FieldInfo[cnt]; System.arraycopy(fields, 0, resFields, 0, cnt); return resFields; } protected FieldInfo[] getAllFields(boolean declaredOnly) { initFields(); FieldInfo [] inherited = BaseType() == null || declaredOnly ? FieldInfo.EMPTY_ARRAY : BaseType().getAllFields(declaredOnly); FieldInfo[] allFields = new FieldInfo[inherited.length + this.fields.length]; System.arraycopy(inherited, 0, allFields, 0, inherited.length); System.arraycopy(this.fields, 0, allFields, inherited.length, this.fields.length); return allFields; } //########################################################################## // GetConstructor/s/ /** Searches for a public instance constructor whose parameters * match the types in the specified array. */ public ConstructorInfo GetConstructor(Type[] paramTypes) { initMethods(); for (int i = 0; i < constructors.length; i++) { if (equalParameters(constructors[i].GetParameters(), paramTypes)) return constructors[i]; } return null; } /** Returns all public instance constructors defined for the current Type.*/ public ConstructorInfo[] GetConstructors() { return GetConstructors(BindingFlags.Instance | BindingFlags.Public); } /***/ public ConstructorInfo[] GetConstructors(int bindingFlags) { initMethods(); final boolean getInstance = (bindingFlags & BindingFlags.Instance) != 0; final boolean getStatic = (bindingFlags & BindingFlags.Static) != 0; final boolean getPublic = (bindingFlags & BindingFlags.Public) != 0; final boolean getNonPublic = (bindingFlags & BindingFlags.NonPublic) != 0; ConstructorInfo[] constrs = new ConstructorInfo[this.constructors.length]; int cnt = 0; for (int i = 0; i < this.constructors.length; i++) { ConstructorInfo constr = this.constructors[i]; boolean accessible = (getPublic && constr.IsPublic()) || (getNonPublic && !constr.IsPublic()); if (accessible && ((getInstance && !constr.IsStatic()) || (getStatic && constr.IsStatic()))) constrs[cnt++] = constr; } ConstructorInfo [] resConstrs = new ConstructorInfo[cnt]; System.arraycopy(constrs, 0, resConstrs, 0, cnt); return resConstrs; } //########################################################################## // GetMethod/s/ /** Searches for the specified public method whose parameters * match the specified argument types. */ public MethodInfo GetMethod(String name, Type[] paramTypes) { return GetMethod(name, paramTypes, null); } public MethodInfo GetMethod(String name, Type[] paramTypes, Type retType) { initMethods(); MethodInfo method = findMethod(methods, name, paramTypes, retType); if (method != null) return method; if (BaseType() != null) { method = BaseType().GetMethod(name, paramTypes, retType); if (method != null) return method; } // StringBuffer str = new StringBuffer(name); // str.append('('); // for (int i = 0; i < paramTypes.length; i++) { // if (i > 0) str.append(", "); // str.append(paramTypes[i]); // } // str.append(')'); // System.out.println("Cannot find method " + str + ":"); // System.out.println("Methods of class " + this); // for (int i = 0; i < methods.length; i++) // System.out.println("\t" + methods[i]); return null; } /** */ protected static MethodInfo findMethod(MethodInfo[] methods, String name, Type[] paramTypes, Type retType) { for (int i = 0; i < methods.length; i++) if (name.equals(methods[i].Name) && equalParameters(methods[i].GetParameters(), paramTypes) && (retType == null || methods[i].ReturnType == retType)) return methods[i]; return null; } /** */ protected static boolean equalParameters(ParameterInfo[] params, Type[] paramTypes) { if (params.length != paramTypes.length) return false; for (int i = 0; i < params.length; i++) { // System.out.println(params[i].ParameterType + " == " + paramTypes[i] // + " = " + (params[i].ParameterType == paramTypes[i])); if (params[i].ParameterType != paramTypes[i]) return false; } return true; } /** */ public MethodInfo GetMethod(String name, Type[] paramTypes, int bindingFlags) { MethodInfo[] methods = GetMethods(bindingFlags); MethodInfo method = findMethod(methods, name, paramTypes, null); if (method == null) { StringBuffer str = new StringBuffer(name); str.append('('); for (int i = 0; i < paramTypes.length; i++) { if (i > 0) str.append(", "); str.append(paramTypes[i]); } str.append(')'); System.out.println("Cannot find method " + str + ":"); System.out.println("Methods of class " + this); for (int i = 0; i < methods.length; i++) System.out.println("\t" + methods[i]); } return method; } /** Returns all public methods of the current Type. */ public MethodInfo[] GetMethods() { return GetMethods(BindingFlags.Instance | BindingFlags.Public); } /** */ public MethodInfo[] GetMethods(int bindingFlags) { initMethods(); final MethodInfo[] methods = getAllMethods((bindingFlags & BindingFlags.DeclaredOnly) != 0); //System.out.println("" + this + ".GetMethods(int) -> " + methods.length); final boolean getInstance = (bindingFlags & BindingFlags.Instance) != 0; final boolean getStatic = (bindingFlags & BindingFlags.Static) != 0; final boolean getPublic = (bindingFlags & BindingFlags.Public) != 0; final boolean getNonPublic = (bindingFlags & BindingFlags.NonPublic) != 0; int cnt = 0; for (int i = 0; i < methods.length; i++) { MethodInfo method = methods[i]; boolean accessible = (getPublic && method.IsPublic()) || (getNonPublic && !method.IsPublic()); if (accessible // strip off the private methods up the hierarchy && ((method.DeclaringType == this) || ((method.DeclaringType != this) && !method.IsPrivate())) && ((getInstance && !method.IsStatic()) || ((getStatic && method.IsStatic()) && (method.DeclaringType == this || (bindingFlags & BindingFlags.FlattenHierarchy) != 0)) ) ) methods[cnt++] = method; } MethodInfo [] resMethods = new MethodInfo[cnt]; System.arraycopy(methods, 0, resMethods, 0, cnt); return resMethods; } protected MethodInfo[] getAllMethods(boolean declaredOnly) { initMethods(); MethodInfo[] inherited = BaseType() == null || declaredOnly ? MethodInfo.EMPTY_ARRAY : BaseType().getAllMethods(declaredOnly); MethodInfo[] allMethods = new MethodInfo[inherited.length + this.methods.length]; System.arraycopy(inherited, 0, allMethods, 0, inherited.length); System.arraycopy(this.methods, 0, allMethods, inherited.length, this.methods.length); return allMethods; } //########################################################################## // GetProperty/ies/ /** Returns all public properties of the current Type. */ public PropertyInfo[] GetProperties() { initProperties(); return (PropertyInfo[]) properties.clone(); } /** Returns the properties of the current class * that satisfy the binding constrints. */ public PropertyInfo[] GetProperties(int bindingFlags) { initProperties(); return (PropertyInfo[]) properties.clone(); } /** Returns the public property with the given name. */ public PropertyInfo GetProperty(String name) { initProperties(); for (int i = 0; i < properties.length; i++) if (name.equals(properties[i].Name)) return properties[i]; return null; } /** Returns the property with the given name * that satisfies the binding constraints. */ public PropertyInfo GetProperty(String name, int bindingFlags) { throw new RuntimeException("Method not implemented yet"); } //########################################################################## // GetEvent(s) public EventInfo[] GetEvents() { initEvents(); return (EventInfo[]) events.clone(); } //########################################################################## // GetNestedType/s/ /** Searches for nested type with the specified name. */ public Type GetNestedType(String name) { initNestedTypes(); for (int i = 0; i < nestedTypes.length; i++) if (nestedTypes[i].Name.equals(name)) return nestedTypes[i]; return null; } /** Returns all types nested within the current Type. */ public Type[] GetNestedTypes() { initNestedTypes(); return (Type[]) nestedTypes.clone(); } //########################################################################## // GetInterface/s/ /** Searches for an Interface with the given name implemented by this type */ public Type GetInterface(String name) { return GetInterface(name, false); } /** Searches for the specified interface, * specifying whether to do a case-sensitive search. * @param name - the name of the interface to get * @param ignoreCase true to perform a case-insensitive search for name * false to perform a case-sensitive search for name * @return A Type object representing the interface with the specified name, * implemented or inherited by the current Type, if found; * otherwise, a null reference */ public Type GetInterface(String name, boolean ignoreCase) { initInterfaces(); for (int i = 0; i < interfaces.length; i++) { Type iface = interfaces[i]; if (ignoreCase) { if (name.equalsIgnoreCase(iface.Name)) return iface; if (name.equalsIgnoreCase(iface.FullName)) return iface; } else { if (name.equals(iface.Name)) return iface; if (name.equals(iface.FullName)) return iface; } } return BaseType() == null ? null : BaseType().GetInterface(name, ignoreCase); } /** Returns the interfaces implemented or inherited by the current Type. */ public Type[] GetInterfaces() { initInterfaces(); if (BaseType() == null) return interfaces; Type[] ifaces = interfaces; int count = 0; for (int i = 0; i < interfaces.length; i++) { if (BaseType().GetInterface(interfaces[i].FullName) == null) ifaces[count++] = ifaces[i]; } Type[] baseTypeIfaces = BaseType().GetInterfaces(); Type[] res = new Type[baseTypeIfaces.length + count]; System.arraycopy(baseTypeIfaces, 0, res, 0, baseTypeIfaces.length); System.arraycopy(ifaces, 0, res, baseTypeIfaces.length, count); return res; } public boolean isSubtypeOf(Type that) { if (this == that || BaseType() == that || that == OBJECT()) return true; initInterfaces(); for (int i = 0; i < interfaces.length; i++) if (interfaces[i].isSubtypeOf(that)) return true; boolean res = BaseType() == null ? false : BaseType().isSubtypeOf(that); // if (!res) { // System.out.println(dumpType(this) + " not a subtype of " + // dumpType(that)); // } return res; } private static String formatType(Type t) { if (t == null) return ""; String cname = t.getClass().getName(); int k = cname.lastIndexOf("."); if (k >= 0) cname = cname.substring(k + 1); return "[" + t.Assembly().GetName() + "]" + t + "(" + cname + "#" + Integer.toHexString(t.hashCode()) + ")"; } private static String dumpType(Type t) { StringBuffer str = new StringBuffer(); str.append(formatType(t) + " : "); str.append(formatType(t.BaseType())); Type[] ifaces = t.GetInterfaces(); for (int i = 0; i < ifaces.length; i++) str.append(", " + formatType(ifaces[i])); return str.toString(); } //########################################################################## // GetMember/s/ protected MemberInfo[] members; public MemberInfo[] GetMember(String name) { aggregateMembers(); List l = new ArrayList(); for (int i = 0; i < members.length; i++) { if (name.equals(members[i].Name)) l.add(members[i]); } return (MemberInfo[])l.toArray(MemberInfo.EMPTY_ARRAY); } protected void aggregateMembers() { if (members != null) return; initFields(); initMethods(); initProperties(); initNestedTypes(); // the List returned by Arrays.asList doesn't support the addAll method // so we have to wrap it in ArrayList List l = new ArrayList(Arrays.asList(fields)); l.addAll(Arrays.asList(constructors)); l.addAll(Arrays.asList(methods)); l.addAll(Arrays.asList(properties)); l.addAll(Arrays.asList(nestedTypes)); members = (MemberInfo[]) l.toArray(MemberInfo.EMPTY_ARRAY); } //########################################################################## // non-standard methods that return only members declared in this type /** * Return only the fields declared in this type. */ public FieldInfo[] getFields() { initFields(); FieldInfo[] fields = new FieldInfo[this.fields.length]; System.arraycopy(this.fields, 0, fields, 0, fields.length); return fields; } /** * Return only the conrtuctors declared in this type. */ public ConstructorInfo[] getConstructors() { initMethods(); ConstructorInfo[] ctors = new ConstructorInfo[constructors.length]; System.arraycopy(constructors, 0, ctors, 0, ctors.length); return ctors; } /** * Return only the methods declared in this type. */ public MethodInfo[] getMethods() { initMethods(); MethodInfo[] methods = new MethodInfo[this.methods.length]; System.arraycopy(this.methods, 0, methods, 0, methods.length); return methods; } /** * Return only the properties declared in this type. */ public PropertyInfo[] getProperties() { initProperties(); PropertyInfo[] props = new PropertyInfo[properties.length]; System.arraycopy(properties, 0, props, 0, props.length); return props; } /** * Return only the interfaces directly implemented by this type. */ public Type[] getInterfaces() { initInterfaces(); Type[] ifaces = new Type[interfaces.length]; System.arraycopy(interfaces, 0, ifaces, 0, ifaces.length); return ifaces; } /** * Return the types declared in this type. */ public Type[] getNestedTypes() { initNestedTypes(); Type[] nested = new Type[nestedTypes.length]; System.arraycopy(nestedTypes, 0, nested, 0, nested.length); return nested; } //########################################################################## public String toString() { return FullName; } //########################################################################## // lazy type construction members private boolean initBaseType = true; protected final void initBaseType() { if (initBaseType) { loadBaseType(); initBaseType = false; } } protected void loadBaseType() {} private boolean initInterfaces = true; protected void initInterfaces() { if (initInterfaces) { loadInterfaces(); initInterfaces = false; } assert interfaces != null : "In type " + this; } protected void loadInterfaces() {} private boolean initNestedTypes = true; protected void initNestedTypes() { if (initNestedTypes) { loadNestedTypes(); initNestedTypes = false; } assert nestedTypes != null : "In type " + this; } protected void loadNestedTypes() {} private boolean initFields = true; protected void initFields() { if (initFields) { loadFields(); initFields = false; } assert fields != null : "In type " + this; } protected void loadFields() {} private boolean initMethods = true; protected void initMethods() { if (initMethods) { loadMethods(); initMethods = false; } assert constructors != null : "In type " + this; assert methods != null : "In type " + this; } protected void loadMethods() {} private boolean initProperties = true; protected void initProperties() { if (initProperties) { initMethods(); loadProperties(); initProperties = false; } assert properties != null : "In type " + this; } protected void loadProperties() {} private boolean initEvents = true; protected void initEvents() { if (initEvents) { initMethods(); loadEvents(); initEvents = false; } assert events != null : "In type " + this; } protected void loadEvents() {} //########################################################################## //########################################################################## // static members private static Assembly MSCORLIB; private static Module MSCORLIB_DLL; public static Type OBJECT() { return __OBJECT; } public static Type STRING() { return __STRING; } public static Type ARRAY() { return __ARRAY; } public static Type VOID() { return __VOID; } public static Type ENUM() { return __ENUM; } public static Type VALUE_TYPE() { return __VALUE_TYPE; } private static Type __OBJECT; private static Type __STRING; private static Type __ARRAY; private static Type __VOID; private static Type __ENUM; private static Type __VALUE_TYPE; public static void initMSCORLIB(Assembly mscorlib) { if (MSCORLIB != null) throw new RuntimeException("mscorlib already initialized"); MSCORLIB = mscorlib; MSCORLIB_DLL = MSCORLIB.GetModules()[0]; __OBJECT = mscorlib.GetType("System.Object"); __STRING = mscorlib.GetType("System.String"); __ARRAY = mscorlib.GetType("System.Array"); __VOID = mscorlib.GetType("System.Void"); __ENUM = mscorlib.GetType("System.Enum"); __VALUE_TYPE = mscorlib.GetType("System.ValueType"); } //########################################################################## } // class Type