/// <summary> /// Creates and returns a map from interface type to map of method id to method info for the provided constructed generic type. /// </summary> /// <param name="implementationType">The implementation type.</param> /// <returns>A map from interface type to map of method id to method info for the provided type.</returns> private static Dictionary <Type, Dictionary <int, Entry> > CreateMapForConstructedGeneric(Type implementationType) { // It is important to note that the interfaceId and methodId are computed based upon the non-concrete // version of the implementation type. During code generation, the concrete type would not be available // and therefore the generic type definition is used. if (!implementationType.IsConstructedGenericType) { throw new InvalidOperationException( $"Type {implementationType} passed to {nameof(CreateMapForConstructedGeneric)} is not a constructed generic type"); } var genericClass = implementationType.GetGenericTypeDefinition(); var genericInterfaces = genericClass.GetInterfaces(); var concreteInterfaces = implementationType.GetInterfaces(); // Create an invoker for every interface on the provided type. var result = new Dictionary <Type, Dictionary <int, Entry> >(genericInterfaces.Length); for (var i = 0; i < genericInterfaces.Length; i++) { // Because these methods are identical except for type parameters, their methods should also be identical except // for type parameters, including identical ordering. That is the assumption. var genericMethods = GrainInterfaceUtils.GetMethods(genericInterfaces[i]); var concreteInterfaceMethods = GrainInterfaceUtils.GetMethods(concreteInterfaces[i]); // Map every method on this interface from the definition interface onto the implementation class. var methodMap = new Dictionary <int, Entry>(genericMethods.Length); var genericMap = default(InterfaceMapping); var concreteMap = default(InterfaceMapping); for (var j = 0; j < genericMethods.Length; j++) { // If this method is not from the expected interface (eg, because it's from a parent interface), then // get the mapping for the interface which it does belong to. var genericInterfaceMethod = genericMethods[j]; if (genericMap.InterfaceType != genericInterfaceMethod.DeclaringType) { genericMap = genericClass.GetTypeInfo().GetRuntimeInterfaceMap(genericInterfaceMethod.DeclaringType); concreteMap = implementationType.GetTypeInfo().GetRuntimeInterfaceMap(concreteInterfaceMethods[j].DeclaringType); } // Determine the position in the definition's map which the target method belongs to and take the implementation // from the same position on the implementation's map. for (var k = 0; k < genericMap.InterfaceMethods.Length; k++) { if (genericMap.InterfaceMethods[k] != genericInterfaceMethod) { continue; } methodMap[GrainInterfaceUtils.ComputeMethodId(genericInterfaceMethod)] = new Entry(concreteMap.TargetMethods[k], concreteMap.InterfaceMethods[k]); break; } } // Add the resulting map of methodId -> method to the interface map. result[concreteInterfaces[i]] = methodMap; } return(result); }
/// <summary> /// Creates and returns a map from interface id to map of method id to method info for the provided non-generic type. /// </summary> /// <param name="implementationType">The implementation type.</param> /// <returns>A map from interface id to map of method id to method info for the provided type.</returns> private static Dictionary <int, Dictionary <int, Entry> > CreateMapForNonGeneric(Type implementationType) { if (implementationType.IsConstructedGenericType) { throw new InvalidOperationException( $"Type {implementationType} passed to {nameof(CreateMapForNonGeneric)} is a constructed generic type."); } var implementationTypeInfo = implementationType.GetTypeInfo(); var interfaces = implementationType.GetInterfaces(); // Create an invoker for every interface on the provided type. var result = new Dictionary <int, Dictionary <int, Entry> >(interfaces.Length); foreach (var iface in interfaces) { var methods = GrainInterfaceUtils.GetMethods(iface); // Map every method on this interface from the definition interface onto the implementation class. var methodMap = new Dictionary <int, Entry>(methods.Length); var mapping = default(InterfaceMapping); foreach (var method in methods) { // If this method is not from the expected interface (eg, because it's from a parent interface), then // get the mapping for the interface which it does belong to. if (mapping.InterfaceType != method.DeclaringType) { mapping = implementationTypeInfo.GetRuntimeInterfaceMap(method.DeclaringType); } // Find the index of the interface method and then get the implementation method at that position. for (var k = 0; k < mapping.InterfaceMethods.Length; k++) { if (mapping.InterfaceMethods[k] != method) { continue; } methodMap[GrainInterfaceUtils.ComputeMethodId(method)] = new Entry(mapping.TargetMethods[k], method); break; } } // Add the resulting map of methodId -> method to the interface map. var interfaceId = GrainInterfaceUtils.GetGrainInterfaceId(iface); result[interfaceId] = methodMap; } return(result); }
/// <summary> /// Maps the provided <paramref name="interfaceId"/> to the provided <paramref name="implementationType"/>. /// </summary> /// <param name="interfaceType">The interface type.</param> /// <param name="implementationType">The implementation type.</param> /// <returns>The mapped interface.</returns> private static IReadOnlyDictionary <int, MethodInfo> GetInterfaceToImplementationMap( int interfaceId, Type implementationType) { // Get the interface mapping for the current implementation. var interfaceTypes = GrainInterfaceUtils.GetRemoteInterfaces(implementationType); var interfaceType = interfaceTypes[interfaceId]; var interfaceMapping = implementationType.GetInterfaceMap(interfaceType); // Map the interface methods to implementation methods. var interfaceMethods = GrainInterfaceUtils.GetMethods(interfaceType); return(interfaceMethods.ToDictionary( GrainInterfaceUtils.ComputeMethodId, interfaceMethod => GetImplementingMethod(interfaceMethod, interfaceMapping))); }
/// <summary> /// Maps the interfaces of the provided <paramref name="implementationType"/>. /// </summary> /// <param name="implementationType">The implementation type.</param> /// <returns>The mapped interface.</returns> private static Dictionary <Type, Dictionary <MethodInfo, Entry> > CreateInterfaceToImplementationMap(Type implementationType) { var interfaces = implementationType.GetInterfaces(); // Create an invoker for every interface on the provided type. var result = new Dictionary <Type, Dictionary <MethodInfo, Entry> >(interfaces.Length); var implementationTypeInfo = implementationType.GetTypeInfo(); foreach (var iface in interfaces) { var methods = GrainInterfaceUtils.GetMethods(iface); // Map every method on this interface from the definition interface onto the implementation class. var methodMap = new Dictionary <MethodInfo, Entry>(methods.Length); var mapping = default(InterfaceMapping); for (var i = 0; i < methods.Length; i++) { var method = methods[i]; // If this method is not from the expected interface (eg, because it's from a parent interface), then // get the mapping for the interface which it does belong to. if (mapping.InterfaceType != method.DeclaringType) { mapping = implementationTypeInfo.GetRuntimeInterfaceMap(method.DeclaringType); } // Find the index of the interface method and then get the implementation method at that position. for (var k = 0; k < mapping.InterfaceMethods.Length; k++) { if (mapping.InterfaceMethods[k] != method) { continue; } methodMap[method] = new Entry(mapping.TargetMethods[k], method); break; } } // Add the resulting map of methodId -> method to the interface map. result[iface] = methodMap; } return(result); }
/// <summary> /// Maps the provided <paramref name="interfaceId"/> to the provided <paramref name="implementationType"/>. /// </summary> /// <param name="interfaceId">The interface id.</param> /// <param name="implementationType">The implementation type.</param> /// <returns>The mapped interface.</returns> private static Dictionary <int, MethodInfo> GetInterfaceToImplementationMap( int interfaceId, Type implementationType) { var interfaceTypes = GrainInterfaceUtils.GetRemoteInterfaces(implementationType); // Get all interface mappings of all interfaces. var interfaceMapping = implementationType .GetInterfaces() .Select(i => implementationType.GetTypeInfo().GetRuntimeInterfaceMap(i)) .SelectMany(map => map.InterfaceMethods .Zip(map.TargetMethods, (interfaceMethod, targetMethod) => new { interfaceMethod, targetMethod })) .ToArray(); // Map the grain interface methods to the implementation methods. return(GrainInterfaceUtils.GetMethods(interfaceTypes[interfaceId]) .ToDictionary(GrainInterfaceUtils.ComputeMethodId, m => interfaceMapping.SingleOrDefault(pair => pair.interfaceMethod == m)?.targetMethod)); }
/// <summary> /// Generates switch cases for the provided grain type. /// </summary> /// <param name="grainType"> /// The grain type. /// </param> /// <param name="methodIdArgument"> /// The method id argument, which is used to select the correct switch label. /// </param> /// <param name="generateMethodHandler"> /// The function used to generate switch block statements for each method. /// </param> /// <returns> /// The switch cases for the provided grain type. /// </returns> public static SwitchSectionSyntax[] GenerateGrainInterfaceAndMethodSwitch( Type grainType, ExpressionSyntax methodIdArgument, Func <MethodInfo, StatementSyntax[]> generateMethodHandler) { var interfaces = GrainInterfaceUtils.GetRemoteInterfaces(grainType); interfaces[GrainInterfaceUtils.GetGrainInterfaceId(grainType)] = grainType; // Switch on interface id. var interfaceCases = new List <SwitchSectionSyntax>(); foreach (var @interface in interfaces) { var interfaceType = @interface.Value; var interfaceId = @interface.Key; var methods = GrainInterfaceUtils.GetMethods(interfaceType); var methodCases = new List <SwitchSectionSyntax>(); // Switch on method id. foreach (var method in methods) { // Generate switch case. var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodType = method; // Generate the switch label for this interface id. var methodIdSwitchLabel = SF.CaseSwitchLabel( SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Generate the switch body. var methodInvokeStatement = generateMethodHandler(methodType); methodCases.Add( SF.SwitchSection().AddLabels(methodIdSwitchLabel).AddStatements(methodInvokeStatement)); } // Generate the switch label for this interface id. var interfaceIdSwitchLabel = SF.CaseSwitchLabel( SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId))); // Generate the default case, which will throw a NotImplementedException. var errorMessage = SF.BinaryExpression( SyntaxKind.AddExpression, "interfaceId=".GetLiteralExpression(), SF.BinaryExpression( SyntaxKind.AddExpression, SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId)), SF.BinaryExpression( SyntaxKind.AddExpression, ",methodId=".GetLiteralExpression(), methodIdArgument))); var throwStatement = SF.ThrowStatement( SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(errorMessage))); var defaultCase = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement); // Generate switch statements for the methods in this interface. var methodSwitchStatements = SF.SwitchStatement(methodIdArgument).AddSections(methodCases.ToArray()).AddSections(defaultCase); // Generate the switch section for this interface. interfaceCases.Add( SF.SwitchSection().AddLabels(interfaceIdSwitchLabel).AddStatements(methodSwitchStatements)); } return(interfaceCases.ToArray()); }
/// <summary> /// Creates and returns a map from interface type to map of method id to method info for the provided non-generic type. /// </summary> /// <param name="implementationType">The implementation type.</param> /// <returns>A map from interface type to map of method id to method info for the provided type.</returns> private static Dictionary <Type, Dictionary <int, Entry> > CreateMapForNonGeneric(Type implementationType) { if (implementationType.IsConstructedGenericType) { throw new InvalidOperationException( $"Type {implementationType} passed to {nameof(CreateMapForNonGeneric)} is a constructed generic type."); } var concreteInterfaces = implementationType.GetInterfaces(); var interfaces = new List <(Type Concrete, Type Generic)>(concreteInterfaces.Length); foreach (var iface in concreteInterfaces) { if (iface.IsConstructedGenericType) { interfaces.Add((iface, iface.GetGenericTypeDefinition())); } else { interfaces.Add((iface, null)); } } // Create an invoker for every interface on the provided type. var result = new Dictionary <Type, Dictionary <int, Entry> >(interfaces.Count); var implementationTypeInfo = implementationType.GetTypeInfo(); foreach (var(iface, genericIface) in interfaces) { var methods = GrainInterfaceUtils.GetMethods(iface); var genericIfaceMethods = genericIface is object?GrainInterfaceUtils.GetMethods(genericIface) : null; // Map every method on this interface from the definition interface onto the implementation class. var methodMap = new Dictionary <int, Entry>(methods.Length); var genericInterfaceMethodMap = genericIface is object?new Dictionary <int, Entry>(genericIfaceMethods.Length) : null; var mapping = default(InterfaceMapping); for (var i = 0; i < methods.Length; i++) { var method = methods[i]; // If this method is not from the expected interface (eg, because it's from a parent interface), then // get the mapping for the interface which it does belong to. if (mapping.InterfaceType != method.DeclaringType) { mapping = implementationTypeInfo.GetRuntimeInterfaceMap(method.DeclaringType); } // Find the index of the interface method and then get the implementation method at that position. for (var k = 0; k < mapping.InterfaceMethods.Length; k++) { if (mapping.InterfaceMethods[k] != method) { continue; } methodMap[GrainInterfaceUtils.ComputeMethodId(method)] = new Entry(mapping.TargetMethods[k], method); if (genericIface is object) { var id = GrainInterfaceUtils.ComputeMethodId(genericIfaceMethods[i]); genericInterfaceMethodMap[id] = new Entry(mapping.TargetMethods[k], genericIfaceMethods[i]); methodMap[id] = new Entry(mapping.TargetMethods[k], method); } break; } } // Add the resulting map of methodId -> method to the interface map. result[iface] = methodMap; if (genericIface is object) { result[genericIface] = genericInterfaceMethodMap; } } return(result); }