public override TypeNode AddNonNullWrapperIfNeeded(TypeNode typeNode) { if (typeNode == null || typeNode is ITypeParameter) { return(typeNode); } SpecSharpCompilerOptions options = this.currentOptions as SpecSharpCompilerOptions; if (options != null && !options.Compatibility && options.ReferenceTypesAreNonNullByDefault && !typeNode.IsValueType) { if (this.typeSystem != null && !this.typeSystem.IsNonNullType(typeNode)) { typeNode = OptionalModifier.For(SystemTypes.NonNullType, typeNode); } } return(typeNode); }
public override TypeNode VisitTypeReference(TypeNode type) { //TODO: break up this method if (type == null) { return(null); } TypeNodeList pars = this.pars; TypeNodeList args = this.args; switch (type.NodeType) { case NodeType.ArrayType: ArrayType arrType = (ArrayType)type; TypeNode elemType = this.VisitTypeReference(arrType.ElementType); if (elemType == arrType.ElementType || elemType == null) { return(arrType); } if (arrType.IsSzArray()) { return(elemType.GetArrayType(1)); } return(elemType.GetArrayType(arrType.Rank, arrType.Sizes, arrType.LowerBounds)); case NodeType.DelegateNode: { FunctionType ftype = type as FunctionType; if (ftype == null) { goto default; } TypeNode referringType = ftype.DeclaringType == null ? this.CurrentType : this.VisitTypeReference(ftype.DeclaringType); return(FunctionType.For(this.VisitTypeReference(ftype.ReturnType), this.VisitParameterList(ftype.Parameters), referringType)); } case NodeType.Pointer: Pointer pType = (Pointer)type; elemType = this.VisitTypeReference(pType.ElementType); if (elemType == pType.ElementType || elemType == null) { return(pType); } return(elemType.GetPointerType()); case NodeType.Reference: Reference rType = (Reference)type; elemType = this.VisitTypeReference(rType.ElementType); if (elemType == rType.ElementType || elemType == null) { return(rType); } return(elemType.GetReferenceType()); case NodeType.ArrayTypeExpression: ArrayTypeExpression aExpr = (ArrayTypeExpression)type; aExpr.ElementType = this.VisitTypeReference(aExpr.ElementType); return(aExpr); case NodeType.BoxedTypeExpression: BoxedTypeExpression bExpr = (BoxedTypeExpression)type; bExpr.ElementType = this.VisitTypeReference(bExpr.ElementType); return(bExpr); case NodeType.ClassExpression: { ClassExpression cExpr = (ClassExpression)type; cExpr.Expression = this.VisitTypeExpression(cExpr.Expression); //Could happen if the expression is a template parameter if (cExpr.Expression is Literal lit) { return(lit.Value as TypeNode); } cExpr.TemplateArguments = this.VisitTypeReferenceList(cExpr.TemplateArguments); return(cExpr); } case NodeType.ClassParameter: case NodeType.TypeParameter: int key = type.UniqueKey; for (int i = 0, n = pars == null ? 0 : pars.Count, m = args == null ? 0 : args.Count; i < n && i < m; i++) { //^ assert pars != null && args != null; TypeNode tp = pars[i]; if (tp == null) { continue; } if (tp.UniqueKey == key) { return(args[i]); } if (tp.Name.UniqueIdKey == type.Name.UniqueIdKey && (tp is ClassParameter && type is TypeParameter)) { //This shouldn't really happen, but in practice it does. Hack past it. return(args[i]); } } return(type); case NodeType.FlexArrayTypeExpression: FlexArrayTypeExpression flExpr = (FlexArrayTypeExpression)type; flExpr.ElementType = this.VisitTypeReference(flExpr.ElementType); return(flExpr); case NodeType.FunctionTypeExpression: FunctionTypeExpression ftExpr = (FunctionTypeExpression)type; ftExpr.Parameters = this.VisitParameterList(ftExpr.Parameters); ftExpr.ReturnType = this.VisitTypeReference(ftExpr.ReturnType); return(ftExpr); case NodeType.InvariantTypeExpression: InvariantTypeExpression invExpr = (InvariantTypeExpression)type; invExpr.ElementType = this.VisitTypeReference(invExpr.ElementType); return(invExpr); case NodeType.InterfaceExpression: InterfaceExpression iExpr = (InterfaceExpression)type; if (iExpr.Expression == null) { goto default; } iExpr.Expression = this.VisitTypeExpression(iExpr.Expression); iExpr.TemplateArguments = this.VisitTypeReferenceList(iExpr.TemplateArguments); return(iExpr); case NodeType.NonEmptyStreamTypeExpression: NonEmptyStreamTypeExpression neExpr = (NonEmptyStreamTypeExpression)type; neExpr.ElementType = this.VisitTypeReference(neExpr.ElementType); return(neExpr); case NodeType.NonNullTypeExpression: NonNullTypeExpression nnExpr = (NonNullTypeExpression)type; nnExpr.ElementType = this.VisitTypeReference(nnExpr.ElementType); return(nnExpr); case NodeType.NonNullableTypeExpression: NonNullableTypeExpression nbExpr = (NonNullableTypeExpression)type; nbExpr.ElementType = this.VisitTypeReference(nbExpr.ElementType); return(nbExpr); case NodeType.NullableTypeExpression: NullableTypeExpression nuExpr = (NullableTypeExpression)type; nuExpr.ElementType = this.VisitTypeReference(nuExpr.ElementType); return(nuExpr); case NodeType.OptionalModifier: { TypeModifier modType = (TypeModifier)type; TypeNode modifiedType = this.VisitTypeReference(modType.ModifiedType); TypeNode modifierType = this.VisitTypeReference(modType.Modifier); if (modifiedType == null || modifierType == null) { return(type); } return(OptionalModifier.For(modifierType, modifiedType)); } case NodeType.RequiredModifier: { TypeModifier modType = (TypeModifier)type; TypeNode modifiedType = this.VisitTypeReference(modType.ModifiedType); TypeNode modifierType = this.VisitTypeReference(modType.Modifier); if (modifiedType == null || modifierType == null) { Debug.Fail(""); return(type); } return(RequiredModifier.For(modifierType, modifiedType)); } case NodeType.OptionalModifierTypeExpression: OptionalModifierTypeExpression optmodType = (OptionalModifierTypeExpression)type; optmodType.ModifiedType = this.VisitTypeReference(optmodType.ModifiedType); optmodType.Modifier = this.VisitTypeReference(optmodType.Modifier); return(optmodType); case NodeType.RequiredModifierTypeExpression: RequiredModifierTypeExpression reqmodType = (RequiredModifierTypeExpression)type; reqmodType.ModifiedType = this.VisitTypeReference(reqmodType.ModifiedType); reqmodType.Modifier = this.VisitTypeReference(reqmodType.Modifier); return(reqmodType); case NodeType.PointerTypeExpression: PointerTypeExpression pExpr = (PointerTypeExpression)type; pExpr.ElementType = this.VisitTypeReference(pExpr.ElementType); return(pExpr); case NodeType.ReferenceTypeExpression: ReferenceTypeExpression rExpr = (ReferenceTypeExpression)type; rExpr.ElementType = this.VisitTypeReference(rExpr.ElementType); return(rExpr); case NodeType.StreamTypeExpression: StreamTypeExpression sExpr = (StreamTypeExpression)type; sExpr.ElementType = this.VisitTypeReference(sExpr.ElementType); return(sExpr); case NodeType.TupleTypeExpression: TupleTypeExpression tuExpr = (TupleTypeExpression)type; tuExpr.Domains = this.VisitFieldList(tuExpr.Domains); return(tuExpr); case NodeType.TypeExpression: { TypeExpression tExpr = (TypeExpression)type; tExpr.Expression = this.VisitTypeExpression(tExpr.Expression); if (tExpr.Expression is Literal) { return(type); } tExpr.TemplateArguments = this.VisitTypeReferenceList(tExpr.TemplateArguments); return(tExpr); } case NodeType.TypeIntersectionExpression: TypeIntersectionExpression tiExpr = (TypeIntersectionExpression)type; tiExpr.Types = this.VisitTypeReferenceList(tiExpr.Types); return(tiExpr); case NodeType.TypeUnionExpression: TypeUnionExpression tyuExpr = (TypeUnionExpression)type; tyuExpr.Types = this.VisitTypeReferenceList(tyuExpr.Types); return(tyuExpr); default: TypeNode declaringType = this.VisitTypeReference(type.DeclaringType); if (declaringType != null) { Identifier tname = type.Name; if (type.Template != null && type.IsGeneric) { tname = type.Template.Name; } TypeNode nt = declaringType.GetNestedType(tname); if (nt != null) { TypeNodeList arguments = type.TemplateArguments; type = nt; if (TargetPlatform.UseGenerics) { if (arguments != null && arguments.Count > 0 && nt.ConsolidatedTemplateParameters != null && nt.ConsolidatedTemplateParameters.Count > 0) { type = nt.GetTemplateInstance(this.TargetModule, this.CurrentType, declaringType, arguments); } } } } if (type.Template != null && (type.ConsolidatedTemplateParameters == null || type.ConsolidatedTemplateParameters.Count == 0)) { if (!type.IsNotFullySpecialized && (!type.IsNormalized || (this.CurrentType != null && type.DeclaringModule == this.CurrentType.DeclaringModule))) { return(type); } // Type is a template instance, but some of its arguments were themselves parameters. // See if any of these parameters are to be specialized by this specializer. bool mustSpecializeFurther = false; TypeNodeList targs = type.TemplateArguments; int numArgs = targs == null ? 0 : targs.Count; if (targs != null) { targs = new TypeNodeList(targs); for (int i = 0; i < numArgs; i++) { TypeNode targ = targs[i]; if (targ is ITypeParameter tparg) { for (int j = 0, np = pars == null ? 0 : pars.Count, m = args == null ? 0 : args.Count; j < np && j < m; j++) { //^ assert pars != null && args != null; if (TargetPlatform.UseGenerics) { if (!(pars[j] is ITypeParameter par)) { continue; } if (tparg == par || (tparg.ParameterListIndex == par.ParameterListIndex && tparg.DeclaringMember == par.DeclaringMember)) { targ = this.args[j]; break; } } else { if (targ == pars[j]) { targ = this.args[j]; break; } } } } else { if (targ != type) { targ = this.VisitTypeReference(targ); } if (targ == type) { continue; } } mustSpecializeFurther |= targs[i] != targ; targs[i] = targ; } } if (targs == null || !mustSpecializeFurther) { return(type); } return(type.Template.GetTemplateInstance(this.TargetModule, this.CurrentType, declaringType, targs)); } TypeNodeList tPars = type.TemplateParameters; if (tPars == null || tPars.Count == 0) { return(type); //Not a parameterized type. No need to get an instance. } TypeNodeList tArgs = new TypeNodeList(); for (int i = 0, n = tPars.Count; i < n; i++) { TypeNode tPar = tPars[i]; tArgs.Add(tPar); //Leave parameter in place if there is no match if (tPar == null || tPar.Name == null) { continue; } int idKey = tPar.Name.UniqueIdKey; for (int j = 0, m = pars == null ? 0 : pars.Count, k = args == null ? 0 : args.Count; j < m && j < k; j++) { //^ assert pars != null && args != null; TypeNode par = pars[j]; if (par == null || par.Name == null) { continue; } if (par.Name.UniqueIdKey == idKey) { tArgs[i] = args[j]; break; } } } return(type.GetTemplateInstance(this.TargetModule, this.CurrentType, this.VisitTypeReference(type.DeclaringType), tArgs)); } }
public static Expression BindPseudoMember(Expression qualifier, Identifier identifier) { if (qualifier != null) { SourceContext fullSourceContext = identifier.SourceContext; if (qualifier.SourceContext.Document != null) { fullSourceContext.StartPos = qualifier.SourceContext.StartPos; } TypeNode qualifierType = qualifier.Type; if (identifier.UniqueIdKey == Cci.Runtime.IsConsistentId.UniqueIdKey) { if (qualifier is Base) { if (qualifierType != null) { qualifierType = TypeNode.StripModifiers(qualifierType); return(new MethodCall(new MemberBinding(null, SystemTypes.Guard.GetMethod(Identifier.For("FrameIsExposable"), SystemTypes.Object, SystemTypes.Type)), new ExpressionList(qualifier, new UnaryExpression(new Literal(qualifierType, SystemTypes.Type), NodeType.Typeof, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Type))), NodeType.Call, SystemTypes.Boolean, fullSourceContext)); } } else { return(new MethodCall( new MemberBinding(null, SystemTypes.Guard.GetMethod(Cci.Runtime.IsConsistentId, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Object))), new ExpressionList(qualifier), NodeType.Call, SystemTypes.Boolean, fullSourceContext)); } } if (identifier.UniqueIdKey == Cci.Runtime.IsPeerConsistentId.UniqueIdKey) { return(new MethodCall( new MemberBinding(null, SystemTypes.Guard.GetMethod(Cci.Runtime.IsPeerConsistentId, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Object))), new ExpressionList(qualifier), NodeType.Call, SystemTypes.Boolean, fullSourceContext)); } if (qualifierType != null) { qualifierType = TypeNode.StripModifiers(qualifierType); if (identifier.UniqueIdKey == Cci.Runtime.IsVirtualConsistentId.UniqueIdKey) { if (qualifier is This || qualifier is ImplicitThis) { SourceContext sc = identifier.SourceContext; identifier = Cci.Runtime.IsExposableId; identifier.SourceContext = sc; } } TypeNode guard = SystemTypes.Guard; if (guard != null) { Property property = guard.GetProperty(identifier); if (property != null && property.IsPublic && !property.IsStatic) { Method method = guard.GetMethod(Identifier.For("Frame" + identifier.Name), SystemTypes.Object, SystemTypes.Type); if (method != null && method.IsPublic && method.IsStatic) { return (new MethodCall( new MemberBinding(null, method), new ExpressionList(qualifier, new UnaryExpression(new Literal(qualifierType, SystemTypes.Type), NodeType.Typeof, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Type))), NodeType.Call, method.ReturnType, fullSourceContext)); } } } } { Method method = SystemTypes.Guard.GetMethod(identifier, SystemTypes.Object); if (method != null && method.IsPublic && method.IsStatic && method.ReturnType != SystemTypes.Void) { return(new MethodCall(new MemberBinding(null, method), new ExpressionList(qualifier), NodeType.Call, method.ReturnType, fullSourceContext)); } } } return(null); }
private void PrepareGuardedClass(TypeNode typeNode) { SpecSharpCompilerOptions options = this.currentOptions as SpecSharpCompilerOptions; if (!(options != null && (options.DisableGuardedClassesChecks || options.Compatibility))) { if (typeNode is Class && typeNode.Contract != null && (typeNode.Contract.InvariantCount > 0 || typeNode.Contract.ModelfieldContractCount > 0) || typeNode is Class && this.currentPreprocessorDefinedSymbols != null && this.currentPreprocessorDefinedSymbols.ContainsKey("GuardAllClasses")) { if (typeNode.Interfaces == null) { typeNode.Interfaces = new InterfaceList(); } if (typeNode.Template == null) //we have to be careful when we are passed a typeNode of a specialized generic type as it shares the contract of the 'real' generic typeNode { #region Add the field "frame" to the class. Field frameField = new Field(typeNode, null, FieldFlags.Public, Identifier.For("SpecSharp::frameGuard"), SystemTypes.Guard, null); frameField.CciKind = CciMemberKind.Auxiliary; typeNode.Contract.FrameField = frameField; typeNode.Members.Add(frameField); This thisParameter = new This(typeNode); Method frameGetter = new Method(typeNode, NoDefaultExpose(), Identifier.For("get_SpecSharp::FrameGuard"), null, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Guard), new Block(new StatementList(new Return(new BinaryExpression(new MemberBinding(thisParameter, frameField), new Literal(SystemTypes.NonNullType, SystemTypes.Type), System.Compiler.NodeType.ExplicitCoercion, OptionalModifier.For(SystemTypes.NonNullType, SystemTypes.Guard)))))); // Pretend this method is [Delayed] so that we can call it from a delayed constructor. frameGetter.Attributes.Add(new AttributeNode(new Literal(ExtendedRuntimeTypes.DelayedAttribute, SystemTypes.Type), null, AttributeTargets.Method)); frameGetter.CciKind = CciMemberKind.FrameGuardGetter; frameGetter.Attributes.Add(new AttributeNode(new Literal(SystemTypes.PureAttribute, SystemTypes.Type), null, AttributeTargets.Method)); frameGetter.Flags = MethodFlags.Public | MethodFlags.HideBySig | MethodFlags.SpecialName; frameGetter.CallingConvention = CallingConventionFlags.HasThis; frameGetter.ThisParameter = thisParameter; typeNode.Contract.FramePropertyGetter = frameGetter; typeNode.Members.Add(frameGetter); Property frameProperty = new Property(typeNode, null, PropertyFlags.None, Identifier.For("SpecSharp::FrameGuard"), frameGetter, null); typeNode.Members.Add(frameProperty); typeNode.Contract.FrameProperty = frameProperty; #endregion typeNode.Contract.InitFrameSetsMethod = new Method(typeNode, NoDefaultExpose(), Identifier.For("SpecSharp::InitGuardSets"), null, SystemTypes.Void, null); typeNode.Contract.InitFrameSetsMethod.CciKind = CciMemberKind.Auxiliary; typeNode.Contract.InitFrameSetsMethod.Flags = MethodFlags.Public | MethodFlags.HideBySig | MethodFlags.SpecialName; } } } }
private static string GetStringForTypeNode(TypeNode type, List <TypeNode> typeTemplateParameters, List <TypeNode> memberTemplateParameters) { StringBuilder sb = new StringBuilder(); switch (type.NodeType) { /* Ordinary types */ case NodeType.Class: case NodeType.Interface: case NodeType.Struct: case NodeType.EnumNode: case NodeType.DelegateNode: if (type.DeclaringType == null) { if (type.Namespace.Name.Length != 0) { sb.Append(type.Namespace.Name); sb.Append('.'); } } else { sb.Append(GetStringForTypeNode(type.DeclaringType, typeTemplateParameters, memberTemplateParameters)); sb.Append('.'); } if (type.IsGeneric) { String templateName = type.Template.Name.Name.Replace('+', '.'); int pos = templateName.LastIndexOf('`'); if (pos != -1) { sb.Append(templateName.Substring(0, pos)); } else { sb.Append(templateName); } } else { sb.Append(type.Name.Name.Replace('+', '.')); } break; /* Simple pointer / reference types */ case NodeType.Reference: sb.Append(GetStringForTypeNode(((Reference)type).ElementType, typeTemplateParameters, memberTemplateParameters)); sb.Append('@'); break; case NodeType.Pointer: sb.Append(GetStringForTypeNode(((Pointer)type).ElementType, typeTemplateParameters, memberTemplateParameters)); sb.Append('*'); break; /* Generic parameters */ case NodeType.ClassParameter: case NodeType.TypeParameter: int index; if ((index = typeTemplateParameters.IndexOf(type)) != -1) { sb.AppendFormat(CultureInfo.InvariantCulture, "`{0}", index); } else if ((index = memberTemplateParameters.IndexOf(type)) != -1) { // Undocumented: based on output from MS compilers. sb.AppendFormat(CultureInfo.InvariantCulture, "``{0}", index); } else { throw new InvalidOperationException("Unable to resolve TypeParameter to a type argument."); } break; /* Arrays */ case NodeType.ArrayType: ArrayType array = ((ArrayType)type); sb.Append(GetStringForTypeNode(array.ElementType, typeTemplateParameters, memberTemplateParameters)); if (array.IsSzArray()) { sb.Append("[]"); } else { // This case handles true multidimensional arrays. // For example, in C#: string[,] myArray sb.Append('['); for (int i = 0; i < array.Rank; i++) { if (i != 0) { sb.Append(','); } // The following appears to be consistent with MS C# compiler output. sb.AppendFormat(CultureInfo.InvariantCulture, "{0}:", array.GetLowerBound(i)); if (array.GetSize(i) != 0) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}", array.GetSize(i)); } } sb.Append(']'); } break; /* Strange types (typically from C++/CLI) */ case NodeType.FunctionPointer: FunctionPointer funcPointer = (FunctionPointer)type; sb.Append("=FUNC:"); sb.Append(GetStringForTypeNode(funcPointer.ReturnType, typeTemplateParameters, memberTemplateParameters)); if (funcPointer.ParameterTypes.Count != 0) { bool comma = false; sb.Append('('); foreach (TypeNode parameterType in funcPointer.ParameterTypes) { if (comma) { sb.Append(','); } sb.Append(GetStringForTypeNode(parameterType, typeTemplateParameters, memberTemplateParameters)); comma = true; } sb.Append(')'); } else { // Inconsistent with documentation: based on MS C++ compiler output. sb.Append("(System.Void)"); } break; case NodeType.RequiredModifier: RequiredModifier reqModifier = (RequiredModifier)type; sb.Append(GetStringForTypeNode(reqModifier.ModifiedType, typeTemplateParameters, memberTemplateParameters)); sb.Append("|"); sb.Append(GetStringForTypeNode(reqModifier.Modifier, typeTemplateParameters, memberTemplateParameters)); break; case NodeType.OptionalModifier: OptionalModifier optModifier = (OptionalModifier)type; sb.Append(GetStringForTypeNode(optModifier.ModifiedType, typeTemplateParameters, memberTemplateParameters)); sb.Append("!"); sb.Append(GetStringForTypeNode(optModifier.Modifier, typeTemplateParameters, memberTemplateParameters)); break; default: throw new ArgumentException("Unsupported NodeType.", "type"); } if (type.IsGeneric && type.TemplateArguments.Count != 0) { // Undocumented: based on output from MS compilers. sb.Append('{'); bool comma = false; foreach (TypeNode templateArgumentType in type.TemplateArguments) { if (comma) { sb.Append(','); } sb.Append(GetStringForTypeNode(templateArgumentType, typeTemplateParameters, memberTemplateParameters)); comma = true; } sb.Append('}'); } return(sb.ToString()); }
private void AddType(TypeNode tn) { if (this._string == null) { this._string = new StringBuilder(); } if (tn == null) { return; } OptionalModifier tnm = tn as OptionalModifier; if (tnm != null) { this._string.Append("optional("); this.AddType(tnm.Modifier); this._string.Append(","); AddType(tnm.ModifiedType); this._string.Append(")"); return; } if (tn == SystemTypes.Object) { this._string.Append("object"); } else if (tn == SystemTypes.String) { this._string.Append("string"); } else if (tn == SystemTypes.Char) { this._string.Append("char"); } else if (tn == SystemTypes.Void) { this._string.Append("void"); } else if (tn == SystemTypes.Boolean) { this._string.Append("bool"); } else if (tn == SystemTypes.Int8) { this._string.Append("i8"); } else if (tn == SystemTypes.Int16) { this._string.Append("i16"); } else if (tn == SystemTypes.Int32) { this._string.Append("i32"); } else if (tn == SystemTypes.Int64) { this._string.Append("i64"); } else if (tn == SystemTypes.UInt8) { this._string.Append("u8"); } else if (tn == SystemTypes.UInt16) { this._string.Append("u16"); } else if (tn == SystemTypes.UInt32) { this._string.Append("u32"); } else if (tn == SystemTypes.UInt64) { this._string.Append("u64"); } else if (tn == SystemTypes.Single) { this._string.Append("single"); } else if (tn == SystemTypes.Double) { this._string.Append("double"); } else if (tn.IsTemplateParameter) { if (tn is MethodTypeParameter || tn is MethodClassParameter) { this._string.Append("$methodTypeParam("); } else { this._string.Append("$typeParam("); } this._string.Append(tn.Name); this._string.Append(")"); } else if (tn is ArrayType) { ArrayType arrayType = (ArrayType)tn; this.AddType(arrayType.ElementType); this._string.Append("[]"); } else if (tn is Reference) { Reference reference = (Reference)tn; this._string.Append("&"); this.AddType(reference.ElementType); } else if (tn is Pointer) { Pointer pointer = (Pointer)tn; this.AddType(pointer.ElementType); this._string.Append("*"); } else { if (this.currentModule != tn.DeclaringModule && tn.DeclaringModule != null && tn.DeclaringModule.ContainingAssembly != null && tn.DeclaringModule.ContainingAssembly.Name != null) { this._string.AppendFormat("[{0}]", tn.DeclaringModule.ContainingAssembly.Name); } TypeNode template = tn.Template == null ? tn : tn.Template; if (template.FullName != null) { this._string.Append(template.FullName); } if (tn.Template != null) { this._string.Append("<"); TypeNodeList args = tn.ConsolidatedTemplateArguments; for (int i = 0; i < args.Count; i++) { if (0 < i) { this._string.Append(","); } this.AddType(args[i]); } this._string.Append(">"); } } }