public void MethodIdsMatch() { var wellKnownTypes = WellKnownTypes.FromCompilation(this.compilation); foreach (var(type, typeSymbol) in GetTypeSymbolPairs(nameof(Grains))) { this.output.WriteLine($"Type: {RuntimeTypeNameFormatter.Format(type)}"); var methods = type.GetMethods(); var methodSymbols = methods.Select(m => typeSymbol.GetMembers(m.Name).SingleOrDefault()).OfType <IMethodSymbol>(); foreach (var(method, methodSymbol) in methods.Zip(methodSymbols, ValueTuple.Create)) { this.output.WriteLine($"IMethodSymbol: {methodSymbol}, MethodInfo: {method}"); Assert.NotNull(methodSymbol); { var expected = GrainInterfaceUtils.FormatMethodForIdComputation(method); var actual = OrleansLegacyCompat.FormatMethodForMethodIdComputation(methodSymbol); this.output.WriteLine($"Expected format: {expected}\nActual format: {actual}"); Assert.Equal(expected, actual); } { var expected = GrainInterfaceUtils.ComputeMethodId(method); var actual = wellKnownTypes.GetMethodId(methodSymbol); this.output.WriteLine($"Expected Id: 0x{expected:X}\nActual Id: 0x{actual:X}"); Assert.Equal(expected, actual); } } } }
/// <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); }
public void Override_MethodId_Test() { var methodId = GrainInterfaceUtils.ComputeMethodId( typeof(IMethodInterceptionGrain).GetMethod(nameof(IMethodInterceptionGrain.One))); Assert.Equal(14142, methodId); }
/// <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> /// 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); }