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);
        }
Exemple #3
0
        public void Override_MethodId_Test()
        {
            var methodId = GrainInterfaceUtils.ComputeMethodId(
                typeof(IMethodInterceptionGrain).GetMethod(nameof(IMethodInterceptionGrain.One)));

            Assert.Equal(14142, methodId);
        }
Exemple #4
0
        /// <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);
        }