internal static MethodContract DuplicateContractAndClosureParts( DuplicatorForContractsAndClosures dup, Method targetMethod, Method sourceMethod, ContractNodes contractNodes, bool copyValidations) { Contract.Ensures(Contract.Result<MethodContract>() != null); //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; // HACK // For some reason, it is important to materialize the name of the targetType here!!! // Otherwise the duplicator will fail. ForceSideEffect(targetType.Name.Name); Local closureLocal; FindClosureInitialization(sourceMethod, sourceContract.ContractInitializer, out closureLocal); // 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); } } // !Very important! // To get appropriate types mapping for duplicated members we have to have two steps approach. // First we need to copy all static closures (they're exists only for roslyn-based compiler) // Second we need to copy all other members (including closures). // The reason why the members are traversed backwards is following: // Nested scope can introduced nested closures. In this case, top level closures can reference // low level closures. Traversing them in backward order will allow to fill type mapping appropriately. for (var memindex = fmd.MembersToDuplicate.Count - 1; memindex >= 0; memindex--) { Member member = fmd.MembersToDuplicate[memindex]; if (IsRoslynBasedStaticClosure(member)) { DuplicateMember(dup, fmd, memindex, targetType); } } for (var memindex = fmd.MembersToDuplicate.Count - 1; memindex >= 0; memindex--) { var member = fmd.MembersToDuplicate[memindex]; if (IsRoslynBasedStaticClosure(member)) { continue; } DuplicateMember(dup, fmd, memindex, targetType); } 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; }
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; }