private static void TransformOriginalConsolidatedTypeFormalsIntoMethodFormals(Method dupMethod, Method closureMethod, Method closureInstanceMethod, out TypeNodeList actuals) { // make sure that if we copy it from a generic context, the method becomes generic in the target context // if we are copying from the same declaring type (not instantiated), then don't add enclosing type parameters. var originals = closureInstanceMethod.DeclaringType.ConsolidatedTemplateArguments == null ? null : closureMethod.DeclaringType.ConsolidatedTemplateParameters; if (originals != null) { originals = originals.Clone(); } if (closureMethod.TemplateParameters != null && closureMethod.TemplateParameters.Count > 0) { if (originals == null) { originals = closureMethod.TemplateParameters.Clone(); } else { foreach (var tp in closureMethod.TemplateParameters) { originals.Add(tp); } } } if (originals == null) { actuals = null; return; } actuals = closureInstanceMethod.DeclaringType.ConsolidatedTemplateArguments == null ? null : closureInstanceMethod.DeclaringType.ConsolidatedTemplateArguments.Clone(); if (closureInstanceMethod.TemplateArguments != null && closureInstanceMethod.TemplateArguments.Count > 0) { if (actuals == null) { actuals = closureInstanceMethod.TemplateArguments.Clone(); } else { foreach (var tp in closureInstanceMethod.TemplateArguments) { actuals.Add(tp); } } } var declaringModule = dupMethod.DeclaringType.DeclaringModule; // new method formals var tparams = originals.Clone(); for (int i = 0; i < originals.Count; i++) { // setup forwarding of tparams to method params ITypeParameter tp = originals[i] as ITypeParameter; TypeNode mtp = NewEqualMethodTypeParameter(tp, dupMethod, i); tparams[i] = mtp; } var specializer = new Specializer(declaringModule, originals, tparams); // System.Console.WriteLine("Made {0} a generic method", dupMethod.FullName); dupMethod.TemplateParameters = tparams; dupMethod.IsGeneric = true; specializer.VisitMethod(dupMethod); var bodySpecializer = new MethodBodySpecializer(declaringModule, originals, tparams); bodySpecializer.CurrentType = dupMethod.DeclaringType; bodySpecializer.CurrentMethod = dupMethod; bodySpecializer.VisitBlock(dupMethod.Body); }
public virtual Method/*!*/ GetTemplateInstance(TypeNode referringType, TypeNodeList typeArguments) { if(referringType == null || this.DeclaringType == null) { Debug.Assert(false); return this; } if(this.IsGeneric) referringType = this.DeclaringType; if(referringType != this.DeclaringType && referringType.DeclaringModule == this.DeclaringType.DeclaringModule) return this.GetTemplateInstance(this.DeclaringType, typeArguments); if(referringType.structurallyEquivalentMethod == null) referringType.structurallyEquivalentMethod = new TrivialHashtableUsingWeakReferences(); Module module = referringType.DeclaringModule; if(module == null) return this; int n = typeArguments == null ? 0 : typeArguments.Count; if(n == 0 || typeArguments == null) return this; StringBuilder sb = new StringBuilder(this.Name.ToString()); sb.Append('<'); for(int i = 0; i < n; i++) { TypeNode ta = typeArguments[i]; if(ta == null) continue; sb.Append(ta.FullName); if(i < n - 1) sb.Append(','); } sb.Append('>'); Identifier mangledName = Identifier.For(sb.ToString()); lock(this) { Method m = (Method)referringType.structurallyEquivalentMethod[mangledName.UniqueIdKey]; int counter = 1; while(m != null) { if(m.template == this && Method.TypeListsAreEquivalent(m.TemplateArguments, typeArguments)) return m; mangledName = Identifier.For(mangledName.ToString() + counter++); m = (Method)referringType.structurallyEquivalentMethod[mangledName.UniqueIdKey]; } Duplicator duplicator = new Duplicator(referringType.DeclaringModule, referringType); duplicator.RecordOriginalAsTemplate = true; duplicator.SkipBodies = true; Method result = duplicator.VisitMethod(this); //^ assume result != null; result.Attributes = this.Attributes; //These do not get specialized, but may need to get normalized result.Name = mangledName; result.fullName = null; result.template = this; result.TemplateArguments = typeArguments; TypeNodeList templateParameters = result.TemplateParameters; result.TemplateParameters = null; result.IsNormalized = true; if(!this.IsGeneric) { ParameterList pars = this.Parameters; ParameterList rpars = result.Parameters; if(pars != null && rpars != null && rpars.Count >= pars.Count) for(int i = 0, count = pars.Count; i < count; i++) { Parameter p = pars[i]; Parameter rp = rpars[i]; if(p == null || rp == null) continue; rp.Attributes = p.Attributes; //These do not get specialized, but may need to get normalized } } if(!this.IsGeneric && !(result.IsStatic) && this.DeclaringType != referringType) { result.Flags &= ~(MethodFlags.Virtual | MethodFlags.NewSlot); result.Flags |= MethodFlags.Static; result.CallingConvention &= ~CallingConventionFlags.HasThis; result.CallingConvention |= CallingConventionFlags.ExplicitThis; ParameterList pars = result.Parameters; if(pars == null) result.Parameters = pars = new ParameterList(); Parameter thisPar = new Parameter(StandardIds.This, this.DeclaringType); pars.Add(thisPar); for(int i = pars.Count - 1; i > 0; i--) pars[i] = pars[i - 1]; pars[0] = thisPar; } referringType.structurallyEquivalentMethod[mangledName.UniqueIdKey] = result; Specializer specializer = new Specializer(module, templateParameters, typeArguments); specializer.VisitMethod(result); if(this.IsGeneric) { result.DeclaringType = this.DeclaringType; return result; } if(this.IsAbstract) return result; referringType.Members.Add(result); return result; } }
private static void DuplicateMember(DuplicatorForContractsAndClosures dup, FindClosurePartsToDuplicate fmd, int memindex, TypeNode targetType) { Member mem = fmd.MembersToDuplicate[memindex]; TypeNode nestedType = mem as TypeNode; Method closureInstanceMethod = mem as Method; Method closureMethodTemplate = closureInstanceMethod; while (closureMethodTemplate != null && closureMethodTemplate.Template != null) { closureMethodTemplate = closureMethodTemplate.Template; } if (nestedType != null) { // if nested type is nested inside another type to be duplicated, skip it if (nestedType.DeclaringType != null && fmd.MembersToDuplicate.Contains(nestedType.DeclaringType)) return; // For call-site wrappers, we end up having multiple methods from the same original contract method // thus we have to avoid duplicating the same closure type multiple times. var duplicatedNestedType = FindExistingClosureType(targetType, nestedType); if (duplicatedNestedType == null) { dup.FindTypesToBeDuplicated(new TypeNodeList(nestedType)); // if parent type is generic and different from the target type, then we may have to include all the // consolidated type parameters excluding the ones from the target type. TypeNodeList originalTemplateParameters = FindNonStandardTypeParametersToBeDuplicated(fmd, nestedType, targetType); duplicatedNestedType = dup.Visit(mem) as TypeNode; if (originalTemplateParameters != null) { int parentParameters = (targetType.ConsolidatedTemplateParameters == null) ? 0 : targetType.ConsolidatedTemplateParameters.Count; if (parentParameters > 0 && (nestedType.DeclaringType.ConsolidatedTemplateParameters == null || nestedType.DeclaringType.ConsolidatedTemplateParameters.Count == 0)) { // type is turning from non-generic to generic Debug.Assert(false); } TypeNodeList dupTPs = DuplicateTypeParameterList(originalTemplateParameters, parentParameters, duplicatedNestedType); duplicatedNestedType.TemplateParameters = dupTPs; dup.SafeAddMember(targetType, duplicatedNestedType, mem); // populate the self specialization forwarding // OriginalDecl<X,Y,Z>.NestedType<A,B,C> -> WrapperType<U,V,W>.NewNestedType<X,Y,Z,A,B,C> //var oldSelfInstanceType = nestedType.GetGenericTemplateInstance(targetModule, nestedType.ConsolidatedTemplateParameters); //var newSelfInstanceType = duplicatedNestedType.GetGenericTemplateInstance(targetModule, duplicatedNestedType.ConsolidatedTemplateParameters); //dup.DuplicateFor[oldSelfInstanceType.UniqueKey] = newSelfInstanceType; // specialize duplicated type var specializer = new Specializer(targetType.DeclaringModule, originalTemplateParameters, dupTPs); specializer.VisitTypeParameterList(dupTPs); // for constraints etc. specializer.VisitTypeNode(duplicatedNestedType); var bodySpecializer = new MethodBodySpecializer(targetType.DeclaringModule, originalTemplateParameters, dupTPs); bodySpecializer.Visit(duplicatedNestedType); // after copying the closure class, clear the self specialization forwarding // OriginalDecl<X,Y,Z>.NestedType<A,B,C> -> WrapperType<U,V,W>.NewNestedType<X,Y,Z,A,B,C> //dup.DuplicateFor[oldSelfInstanceType.UniqueKey] = null; } else { dup.SafeAddMember(targetType, duplicatedNestedType, mem); } } else { // already copied type previously dup.DuplicateFor[nestedType.UniqueKey] = duplicatedNestedType; #if false if (nestedType.ConsolidatedTemplateArguments != null) { // populate the self specialization forwarding // NestedType<Self1,Self2> -> NewNestedType<NewSelf1,NewSelf2> var origSelfInstantiation = nestedType.DeclaringType.GetTemplateInstance(nestedType, nestedType.DeclaringType.TemplateParameters).GetNestedType(nestedType.Name); var newSelfInstantiation = duplicatedNestedType.GetGenericTemplateInstance(targetModule, duplicatedNestedType.ConsolidatedTemplateParameters); dup.DuplicateFor[origSelfInstantiation.UniqueKey] = newSelfInstantiation; // Also forward ContractType<A,B>.NestedType instantiated at target ContractType<X,Y>.NestedType to // TargetType<X,Y,Z>.NewNestedType<X,Y>, since this reference may appear in the contract itself. var consolidatedContractTemplateArguments = sourceMethod.DeclaringType.ConsolidatedTemplateArguments; var instantiatedNestedOriginal = nestedType.DeclaringType.GetGenericTemplateInstance(targetModule, consolidatedContractTemplateArguments).GetNestedType(nestedType.Name); dup.DuplicateFor[instantiatedNestedOriginal.UniqueKey] = duplicatedNestedType.GetTemplateInstance(targetType, consolidatedContractTemplateArguments); } else { Debugger.Break(); } #endif } } else if (closureInstanceMethod != null && closureMethodTemplate != null && !fmd.MembersToDuplicate.Contains(closureMethodTemplate.DeclaringType)) { Method closureMethod = closureMethodTemplate; Debug.Assert(closureMethod.Template == null); //var m = FindExistingClosureMethod(targetType, closureMethod); Method m = null; // why did we ever try to find an existing one? This can capture a completely unrelated closure that happens to match by name. if (m == null) { Method dupMethod = dup.Visit(closureMethod) as Method; TypeNodeList actuals; TransformOriginalConsolidatedTypeFormalsIntoMethodFormals(dupMethod, closureMethod, closureInstanceMethod, out actuals); // now setup a forwarding from the closureInstanceMethod to the new instance Method newInstance = dupMethod.GetTemplateInstance(dupMethod.DeclaringType, actuals); newInstance.Name = dupMethod.Name; dup.DuplicateFor[closureInstanceMethod.UniqueKey] = newInstance; dup.SafeAddMember(targetType, dupMethod, closureMethod); // special case when resulting method is generic and instance, then we need to make "this" a parameter // and the method static, otherwise, there's a type mismatch between the "this" and the explicitly generic parameter types used // in arguments. var originalParentTemplateParameters = closureMethod.DeclaringType.ConsolidatedTemplateParameters; if (!dupMethod.IsStatic && originalParentTemplateParameters != null && originalParentTemplateParameters.Count > 0) { var oldThis = dupMethod.ThisParameter; oldThis.Type = dup.PossiblyRemapContractClassToInterface(oldThis.Type); dupMethod.Flags |= MethodFlags.Static; dupMethod.CallingConvention &= ~CallingConventionFlags.HasThis; var oldParameters = dupMethod.Parameters; dupMethod.Parameters = new ParameterList(oldParameters.Count + 1); dupMethod.Parameters.Add(oldThis); // make explicit for (int i = 0; i < oldParameters.Count; i++) { dupMethod.Parameters.Add(oldParameters[i]); } // now need to specialize transforming original parameters into first n method template parameters var targetTypeParameters = new TypeNodeList(originalParentTemplateParameters.Count); for (int i = 0; i < originalParentTemplateParameters.Count; i++) { targetTypeParameters.Add(dupMethod.TemplateParameters[i]); } var specializer = new Specializer(targetType.DeclaringModule, originalParentTemplateParameters, targetTypeParameters); specializer.VisitMethod(dupMethod); var bodySpecializer = new MethodBodySpecializer(targetType.DeclaringModule, originalParentTemplateParameters, targetTypeParameters); bodySpecializer.VisitMethod(dupMethod); } } } else if (closureInstanceMethod != null) { var m = FindExistingClosureMethod(targetType, closureInstanceMethod); if (m == null) { Member duplicatedMember = dup.Visit(mem) as Member; dup.SafeAddMember(targetType, duplicatedMember, mem); } } else { Member duplicatedMember = dup.Visit(mem) as Member; dup.SafeAddMember(targetType, duplicatedMember, mem); } }
internal static MethodContract DuplicateContractAndClosureParts( DuplicatorForContractsAndClosures dup, Method targetMethod, Method sourceMethod, ContractNodes contractNodes, bool copyValidations ) { //System.Console.WriteLine(">>>" + sourceMethod.FullName); // materialize source contract: var sourceContract = sourceMethod.Contract; ForceSideEffect(sourceContract.ContractInitializer); TypeNode targetType = targetMethod.DeclaringType; TypeNode sourceType = sourceMethod.DeclaringType; // need to null out ProvideNestedTypes so the NestedTypes property doesn't use // metadata to fill in the list of nested types. Because maybe sourceType has // had nested types added to it in memory and those nested types don't exist // in the assembly that sourceType is defined in. // The source type itself shouldn't be duplicated, because any references to its // members (e.g., method calls) in a contract should remain as references to that // member. I.e., a contract on virtual method B.M might contain a call to "this.P()". When copying // the contract from B.M to an override C.M, "this" of type B should become "this" of type C (which // is why the self parameter is mapped below), but the member B.P() should remain B.P(). // However, any nested types within the source type, such as any closure classes, *should* be // duplicated. sourceType.ProvideNestedTypes = null; targetType.ProvideNestedTypes = null; #region HACK // For some reason, it is important to materialize the name of the targetType here!!! // Otherwise the duplicator will fail. ForceSideEffect(targetType.Name.Name); #endregion Local closureLocal; FindClosureInitialization(sourceMethod, sourceContract.ContractInitializer, out closureLocal); #region Duplicate anonymous delegates that turn into static methods (and their caching fields) FindClosurePartsToDuplicate fmd = new FindClosurePartsToDuplicate(sourceType, sourceMethod); fmd.VisitMethodContract(sourceContract); // special handling of closures that are not used. if (closureLocal != null) { var closureType = Unspecialize(closureLocal.Type); if (!fmd.MembersToDuplicate.Contains(closureType)) { // contracts do not depend on closure and won't copy it, so remove the initialization, otherwise debugger gets confused DeleteClosureInitialization(sourceMethod, sourceContract, closureLocal); } } for (var memindex = fmd.MembersToDuplicate.Count - 1; memindex >= 0; memindex--) { Member mem = fmd.MembersToDuplicate[memindex]; TypeNode nestedType = mem as TypeNode; Method closureInstanceMethod = mem as Method; Method closureMethodTemplate = closureInstanceMethod; while (closureMethodTemplate != null && closureMethodTemplate.Template != null) { closureMethodTemplate = closureMethodTemplate.Template; } if (nestedType != null) { // if nested type is nested inside another type to be duplicated, skip it if (nestedType.DeclaringType != null && fmd.MembersToDuplicate.Contains(nestedType.DeclaringType)) continue; // For call-site wrappers, we end up having multiple methods from the same original contract method // thus we have to avoid duplicating the same closure type multiple times. var duplicatedNestedType = FindExistingClosureType(targetType, nestedType); if (duplicatedNestedType == null) { dup.FindTypesToBeDuplicated(new TypeNodeList(nestedType)); // if parent type is generic and different from the target type, then we may have to include all the // consolidated type parameters excluding the ones from the target type. TypeNodeList originalTemplateParameters = FindNonStandardTypeParametersToBeDuplicated(fmd, nestedType, targetType); duplicatedNestedType = dup.Visit(mem) as TypeNode; if (originalTemplateParameters != null) { int parentParameters = (targetType.ConsolidatedTemplateParameters == null) ? 0 : targetType.ConsolidatedTemplateParameters.Count; if (parentParameters > 0 && (nestedType.DeclaringType.ConsolidatedTemplateParameters == null || nestedType.DeclaringType.ConsolidatedTemplateParameters.Count == 0)) { // type is turning from non-generic to generic Debug.Assert(false); } TypeNodeList dupTPs = DuplicateTypeParameterList(originalTemplateParameters, parentParameters, duplicatedNestedType); duplicatedNestedType.TemplateParameters = dupTPs; dup.SafeAddMember(targetType, duplicatedNestedType, mem); // populate the self specialization forwarding // OriginalDecl<X,Y,Z>.NestedType<A,B,C> -> WrapperType<U,V,W>.NewNestedType<X,Y,Z,A,B,C> //var oldSelfInstanceType = nestedType.GetGenericTemplateInstance(targetModule, nestedType.ConsolidatedTemplateParameters); //var newSelfInstanceType = duplicatedNestedType.GetGenericTemplateInstance(targetModule, duplicatedNestedType.ConsolidatedTemplateParameters); //dup.DuplicateFor[oldSelfInstanceType.UniqueKey] = newSelfInstanceType; // specialize duplicated type var specializer = new Specializer(targetType.DeclaringModule, originalTemplateParameters, dupTPs); specializer.VisitTypeParameterList(dupTPs); // for constraints etc. specializer.VisitTypeNode(duplicatedNestedType); var bodySpecializer = new MethodBodySpecializer(targetType.DeclaringModule, originalTemplateParameters, dupTPs); bodySpecializer.Visit(duplicatedNestedType); // after copying the closure class, clear the self specialization forwarding // OriginalDecl<X,Y,Z>.NestedType<A,B,C> -> WrapperType<U,V,W>.NewNestedType<X,Y,Z,A,B,C> //dup.DuplicateFor[oldSelfInstanceType.UniqueKey] = null; } else { dup.SafeAddMember(targetType, duplicatedNestedType, mem); } } else { // already copied type previously dup.DuplicateFor[nestedType.UniqueKey] = duplicatedNestedType; #if false if (nestedType.ConsolidatedTemplateArguments != null) { // populate the self specialization forwarding // NestedType<Self1,Self2> -> NewNestedType<NewSelf1,NewSelf2> var origSelfInstantiation = nestedType.DeclaringType.GetTemplateInstance(nestedType, nestedType.DeclaringType.TemplateParameters).GetNestedType(nestedType.Name); var newSelfInstantiation = duplicatedNestedType.GetGenericTemplateInstance(targetModule, duplicatedNestedType.ConsolidatedTemplateParameters); dup.DuplicateFor[origSelfInstantiation.UniqueKey] = newSelfInstantiation; // Also forward ContractType<A,B>.NestedType instantiated at target ContractType<X,Y>.NestedType to // TargetType<X,Y,Z>.NewNestedType<X,Y>, since this reference may appear in the contract itself. var consolidatedContractTemplateArguments = sourceMethod.DeclaringType.ConsolidatedTemplateArguments; var instantiatedNestedOriginal = nestedType.DeclaringType.GetGenericTemplateInstance(targetModule, consolidatedContractTemplateArguments).GetNestedType(nestedType.Name); dup.DuplicateFor[instantiatedNestedOriginal.UniqueKey] = duplicatedNestedType.GetTemplateInstance(targetType, consolidatedContractTemplateArguments); } else { Debugger.Break(); } #endif } } else if (closureInstanceMethod != null && closureMethodTemplate != null && !fmd.MembersToDuplicate.Contains(closureMethodTemplate.DeclaringType)) { Method closureMethod = closureMethodTemplate; Debug.Assert(closureMethod.Template == null); //var m = FindExistingClosureMethod(targetType, closureMethod); Method m = null; // why did we ever try to find an existing one? This can capture a completely unrelated closure that happens to match by name. if (m == null) { Method dupMethod = dup.Visit(closureMethod) as Method; TypeNodeList actuals; TransformOriginalConsolidatedTypeFormalsIntoMethodFormals(dupMethod, closureMethod, closureInstanceMethod, out actuals); // now setup a forwarding from the closureInstanceMethod to the new instance Method newInstance = dupMethod.GetTemplateInstance(dupMethod.DeclaringType, actuals); newInstance.Name = dupMethod.Name; dup.DuplicateFor[closureInstanceMethod.UniqueKey] = newInstance; dup.SafeAddMember(targetType, dupMethod, closureMethod); // special case when resulting method is generic and instance, then we need to make "this" a parameter // and the method static, otherwise, there's a type mismatch between the "this" and the explicitly generic parameter types used // in arguments. var originalParentTemplateParameters = closureMethod.DeclaringType.ConsolidatedTemplateParameters; if (!dupMethod.IsStatic && originalParentTemplateParameters != null && originalParentTemplateParameters.Count > 0) { var oldThis = dupMethod.ThisParameter; oldThis.Type = dup.PossiblyRemapContractClassToInterface(oldThis.Type); dupMethod.Flags |= MethodFlags.Static; dupMethod.CallingConvention &= ~CallingConventionFlags.HasThis; var oldParameters = dupMethod.Parameters; dupMethod.Parameters = new ParameterList(oldParameters.Count + 1); dupMethod.Parameters.Add(oldThis); // make explicit for (int i = 0; i < oldParameters.Count; i++) { dupMethod.Parameters.Add(oldParameters[i]); } // now need to specialize transforming original parameters into first n method template parameters var targetTypeParameters = new TypeNodeList(originalParentTemplateParameters.Count); for (int i = 0; i < originalParentTemplateParameters.Count; i++) { targetTypeParameters.Add(dupMethod.TemplateParameters[i]); } var specializer = new Specializer(targetType.DeclaringModule, originalParentTemplateParameters, targetTypeParameters); specializer.VisitMethod(dupMethod); var bodySpecializer = new MethodBodySpecializer(targetType.DeclaringModule, originalParentTemplateParameters, targetTypeParameters); bodySpecializer.VisitMethod(dupMethod); } } } else if (closureInstanceMethod != null) { var m = FindExistingClosureMethod(targetType, closureInstanceMethod); if (m == null) { Member duplicatedMember = dup.Visit(mem) as Member; dup.SafeAddMember(targetType, duplicatedMember, mem); } } else { Member duplicatedMember = dup.Visit(mem) as Member; dup.SafeAddMember(targetType, duplicatedMember, mem); } } #endregion Duplicate anonymous delegates that turn into static methods (and their caching fields) var duplicateContract = dup.VisitMethodContract(sourceContract); if (copyValidations) { duplicateContract.Validations = dup.VisitRequiresList(sourceContract.Validations); } foreach (Member mem in sourceType.Members) { if (mem == null) continue; Member newMember = (Member)dup.DuplicateFor[mem.UniqueKey]; if (newMember != null && mem != (Member)targetType && newMember.DeclaringType == targetType && !targetType.Members.Contains(newMember)) { TypeNode nestedType = mem as TypeNode; if (nestedType != null && nestedType.Template != null) { // don't add instantiations continue; } Method nestedMethod = mem as Method; if (nestedMethod != null && nestedMethod.Template != null) { // don't add instantiations continue; } // second conjunct is to make sure we don't recursively add a nested type to itself // e.g., ArrayList+IListWrapper extends ArrayList and so inherits contracts from it // so this method could be called with sourceMethod "ArrayList.M" and targetMethod "ArrayList+IListWrapper.M" // But need to make sure that there isn't already a type with the same name!!! TypeNode possibleClash = targetType.GetNestedType(newMember.Name); if (possibleClash != null) { newMember.Name = Identifier.For(newMember.Name.Name + "_1"); } // System.Console.WriteLine("found nested member that wasn't there before closure and contract duplication: {0}", newMember.FullName); dup.SafeAddMember(targetType, newMember, mem); //targetType.Members.Add(newMember); } } return duplicateContract; }