private static MemberDeclarationSyntax GenerateInterfaceIdProperty(Type grainType) { var property = TypeUtils.Member((GrainReference _) => _.InterfaceId); var returnValue = SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal(GrainInterfaceUtils.GetGrainInterfaceId(grainType))); return (SF.PropertyDeclaration(typeof(int).GetTypeSyntax(), property.Name) .AddAccessorListAccessors( SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .AddBodyStatements(SF.ReturnStatement(returnValue))) .AddModifiers(SF.Token(SyntaxKind.PublicKeyword), SF.Token(SyntaxKind.OverrideKeyword))); }
private static MemberDeclarationSyntax GenerateInterfaceVersionProperty(Type grainType) { var property = TypeUtils.Member((IGrainMethodInvoker _) => _.InterfaceVersion); var returnValue = SF.LiteralExpression( SyntaxKind.NumericLiteralExpression, SF.Literal(GrainInterfaceUtils.GetGrainInterfaceVersion(grainType))); return (SF.PropertyDeclaration(typeof(ushort).GetTypeSyntax(), property.Name) .AddAccessorListAccessors( SF.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .AddBodyStatements(SF.ReturnStatement(returnValue))) .AddModifiers(SF.Token(SyntaxKind.ProtectedKeyword), SF.Token(SyntaxKind.OverrideKeyword))); }
/// <summary> /// Returns syntax for the options argument to <see cref="GrainReference.InvokeMethodAsync{T}"/> and <see cref="GrainReference.InvokeOneWayMethod"/>. /// </summary> /// <param name="method">The method which an invoke call is being generated for.</param> /// <returns> /// Argument syntax for the options argument to <see cref="GrainReference.InvokeMethodAsync{T}"/> and /// <see cref="GrainReference.InvokeOneWayMethod"/>, or <see langword="null"/> if no options are to be specified. /// </returns> private static ArgumentSyntax GetInvokeOptions(MethodInfo method) { var options = new List <ExpressionSyntax>(); if (GrainInterfaceUtils.IsReadOnly(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.ReadOnly.ToString())); } if (GrainInterfaceUtils.IsUnordered(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.Unordered.ToString())); } if (GrainInterfaceUtils.IsAlwaysInterleave(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.AlwaysInterleave.ToString())); } if (GrainInterfaceUtils.IsNewTransactionRequired(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionRequiresNew.ToString())); } if (GrainInterfaceUtils.IsTransactionRequired(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionRequired.ToString())); } ExpressionSyntax allOptions; if (options.Count <= 1) { allOptions = options.FirstOrDefault(); } else { allOptions = options.Aggregate((a, b) => SF.BinaryExpression(SyntaxKind.BitwiseOrExpression, a, b)); } if (allOptions == null) { return(null); } return(SF.Argument(SF.NameColon("options"), SF.Token(SyntaxKind.None), allOptions)); }
/// <summary> /// Generates <see cref="GenericMethodInvoker"/> fields for the generic methods in <paramref name="grainType"/>. /// </summary> /// <param name="grainType">The grain type.</param> /// <returns>The generated fields.</returns> private static MemberDeclarationSyntax[] GenerateGenericInvokerFields(Type grainType) { var methods = GrainInterfaceUtils.GetMethods(grainType); var result = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { if (!method.IsGenericMethodDefinition) { continue; } result.Add(GenerateGenericInvokerField(method)); } return(result.ToArray()); }
internal static IAddressable MakeGrainReference_FromType( Func <GrainClassData, GrainId> getGrainId, Type interfaceType, string grainClassNamePrefix = null) { CheckRuntimeEnvironmentSetup(); if (!GrainInterfaceUtils.IsGrainType(interfaceType)) { throw new ArgumentException("Cannot fabricate grain-reference for non-grain type: " + interfaceType.FullName); } var implementation = TypeCodeMapper.GetImplementation(interfaceType, grainClassNamePrefix); GrainId grainId = getGrainId(implementation); var typeInfo = interfaceType.GetTypeInfo(); return(GrainReference.FromGrainId(grainId, typeInfo.IsGenericType ? TypeUtils.GenericTypeArgsString(typeInfo.UnderlyingSystemType.FullName) : null)); }
private void ConsiderType( Type type, bool runtime, Assembly targetAssembly, ISet <Type> includedTypes, bool considerForSerialization = false) { // The module containing the serializer. var typeInfo = type.GetTypeInfo(); // If a type was encountered which can be accessed and is marked as [Serializable], process it for serialization. if (considerForSerialization) { this.RecordType(type, targetAssembly, includedTypes); } // Consider generic arguments to base types and implemented interfaces for code generation. this.ConsiderGenericBaseTypeArguments(typeInfo, targetAssembly, includedTypes); this.ConsiderGenericInterfacesArguments(typeInfo, targetAssembly, includedTypes); // Include grain interface types. if (GrainInterfaceUtils.IsGrainInterface(type)) { // If code generation is being performed at runtime, the interface must be accessible to the generated code. if (!runtime || TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly)) { if (Logger.IsVerbose2) { Logger.Verbose2("Will generate code for: {0}", type.GetParseableName()); } includedTypes.Add(type); } } if (TypeUtils.IsConcreteGrainClass(type)) { includedTypes.Add(type); } }
/// <summary> /// Generates invoker methods. /// </summary> /// <param name="grainType">The grain type.</param> /// <param name="onEncounteredType"> /// The callback which is invoked when a type is encountered. /// </param> /// <returns>Invoker methods for the provided grain type.</returns> private static MemberDeclarationSyntax[] GenerateInvokeMethods(Type grainType, Action <Type> onEncounteredType) { var baseReference = SF.BaseExpression(); var methods = GrainInterfaceUtils.GetMethods(grainType); var members = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { onEncounteredType(method.ReturnType); var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodIdArgument = SF.Argument(SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Construct a new object array from all method arguments. var parameters = method.GetParameters(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { onEncounteredType(parameter.ParameterType); if (typeof(IGrainObserver).GetTypeInfo().IsAssignableFrom(parameter.ParameterType)) { body.Add( SF.ExpressionStatement( CheckGrainObserverParamInternalExpression.Invoke() .AddArgumentListArguments(SF.Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. ExpressionSyntax args; if (parameters.Length == 0) { args = SF.LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select(GetParameterForInvocation).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. if (method.ReturnType == typeof(void)) { var invocation = SF.InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ExpressionStatement(invocation)); } else { var returnType = method.ReturnType == typeof(Task) ? typeof(object) : method.ReturnType.GenericTypeArguments[0]; var invocation = SF.InvocationExpression(baseReference.Member("InvokeMethodAsync", returnType)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ReturnStatement(invocation)); } members.Add(method.GetDeclarationSyntax().AddBodyStatements(body.ToArray())); } return(members.ToArray()); }
/// <summary> /// Returns syntax for the options argument to <see cref="GrainReference.InvokeMethodAsync{T}"/> and <see cref="GrainReference.InvokeOneWayMethod"/>. /// </summary> /// <param name="method">The method which an invoke call is being generated for.</param> /// <returns> /// Argument syntax for the options argument to <see cref="GrainReference.InvokeMethodAsync{T}"/> and /// <see cref="GrainReference.InvokeOneWayMethod"/>, or <see langword="null"/> if no options are to be specified. /// </returns> private static ArgumentSyntax GetInvokeOptions(MethodInfo method) { var options = new List <ExpressionSyntax>(); if (GrainInterfaceUtils.IsReadOnly(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.ReadOnly.ToString())); } if (GrainInterfaceUtils.IsUnordered(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.Unordered.ToString())); } if (GrainInterfaceUtils.IsAlwaysInterleave(method)) { options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.AlwaysInterleave.ToString())); } if (GrainInterfaceUtils.TryGetTransactionOption(method, out TransactionOption option)) { switch (option) { case TransactionOption.Suppress: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionSuppress.ToString())); break; case TransactionOption.CreateOrJoin: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionCreateOrJoin.ToString())); break; case TransactionOption.Create: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionCreate.ToString())); break; case TransactionOption.Join: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionJoin.ToString())); break; case TransactionOption.Supported: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionSupported.ToString())); break; case TransactionOption.NotAllowed: options.Add(typeof(InvokeMethodOptions).GetNameSyntax().Member(InvokeMethodOptions.TransactionNotAllowed.ToString())); break; default: throw new NotSupportedException($"Transaction option {options} is not supported."); } } ExpressionSyntax allOptions; if (options.Count <= 1) { allOptions = options.FirstOrDefault(); } else { allOptions = options.Aggregate((a, b) => SF.BinaryExpression(SyntaxKind.BitwiseOrExpression, a, b)); } if (allOptions == null) { return(null); } return(SF.Argument(SF.NameColon("options"), SF.Token(SyntaxKind.None), allOptions)); }
/// <summary> /// Generates invoker methods. /// </summary> /// <param name="grainType">The grain type.</param> /// <param name="onEncounteredType"> /// The callback which is invoked when a type is encountered. /// </param> /// <returns>Invoker methods for the provided grain type.</returns> private static MemberDeclarationSyntax[] GenerateInvokeMethods(Type grainType, Action <Type> onEncounteredType) { var baseReference = SF.BaseExpression(); var methods = GrainInterfaceUtils.GetMethods(grainType); var members = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { onEncounteredType(method.ReturnType); var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodIdArgument = SF.Argument(SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Construct a new object array from all method arguments. var parameters = method.GetParameters(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { onEncounteredType(parameter.ParameterType); if (typeof(IGrainObserver).IsAssignableFrom(parameter.ParameterType)) { body.Add( SF.ExpressionStatement( CheckGrainObserverParamInternalExpression.Invoke() .AddArgumentListArguments(SF.Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. ExpressionSyntax args; if (method.IsGenericMethodDefinition) { // Create an arguments array which includes the method's type parameters followed by the method's parameter list. var allParameters = new List <ExpressionSyntax>(); foreach (var typeParameter in method.GetGenericArguments()) { allParameters.Add(SF.TypeOfExpression(typeParameter.GetTypeSyntax())); } allParameters.AddRange(parameters.Select(GetParameterForInvocation)); args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(allParameters.ToArray())); } else if (parameters.Length == 0) { args = SF.LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select(GetParameterForInvocation).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. var isOneWayTask = method.GetCustomAttribute <OneWayAttribute>() != null; if (method.ReturnType == typeof(void) || isOneWayTask) { var invocation = SF.InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ExpressionStatement(invocation)); if (isOneWayTask) { if (method.ReturnType != typeof(Task)) { throw new CodeGenerationException( $"Method {grainType.GetParseableName()}.{method.Name} is marked with [{nameof(OneWayAttribute)}], " + $"but has a return type which is not assignable from {typeof(Task)}"); } var done = typeof(Task).GetNameSyntax(true).Member((object _) => Task.CompletedTask); body.Add(SF.ReturnStatement(done)); } } else { var returnType = method.ReturnType == typeof(Task) ? typeof(object) : method.ReturnType.GenericTypeArguments[0]; var invocation = SF.InvocationExpression(baseReference.Member("InvokeMethodAsync", returnType)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } ExpressionSyntax returnContent = invocation; if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition().FullName == "System.Threading.Tasks.ValueTask`1") { // Wrapping invocation expression with initialization of ValueTask (e.g. new ValueTask<int>(base.InvokeMethod())) returnContent = SF.ObjectCreationExpression(method.ReturnType.GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(SF.ExpressionStatement(invocation).Expression)); } body.Add(SF.ReturnStatement(returnContent)); } members.Add(method.GetDeclarationSyntax().AddBodyStatements(body.ToArray())); } return(members.ToArray()); }
/// <summary> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="targetAssembly">The assemblies used for accessiblity checks, or <see langword="null"/> during runtime code generation.</param> /// <param name="assemblies">The assemblies to generate code for.</param> /// <returns>The generated syntax tree.</returns> private GeneratedSyntax GenerateCode(Assembly targetAssembly, List <Assembly> assemblies) { var features = new FeatureDescriptions(); var members = new List <MemberDeclarationSyntax>(); // Expand the list of included assemblies and types. var knownAssemblies = new Dictionary <Assembly, KnownAssemblyAttribute>( assemblies.ToDictionary(k => k, k => default(KnownAssemblyAttribute))); foreach (var attribute in assemblies.SelectMany(asm => asm.GetCustomAttributes <KnownAssemblyAttribute>())) { knownAssemblies[attribute.Assembly] = attribute; } if (logger.IsEnabled(LogLevel.Information)) { logger.Info($"Generating code for assemblies: {string.Join(", ", knownAssemblies.Keys.Select(a => a.FullName))}"); } // Get types from assemblies which reference Orleans and are not generated assemblies. var grainClasses = new HashSet <Type>(); var grainInterfaces = new HashSet <Type>(); foreach (var pair in knownAssemblies) { var assembly = pair.Key; var treatTypesAsSerializable = pair.Value?.TreatTypesAsSerializable ?? false; foreach (var type in TypeUtils.GetDefinedTypes(assembly, this.logger)) { if (treatTypesAsSerializable || type.IsSerializable || TypeHasKnownBase(type)) { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { if (treatTypesAsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where 'TreatTypesAsSerializable' = true"; } else if (type.IsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where type is [Serializable]"; } else if (type.IsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where type has known base type."; } } serializableTypes.RecordType(type, targetAssembly, logContext); } // Include grain interfaces and classes. var isGrainInterface = GrainInterfaceUtils.IsGrainInterface(type); var isGrainClass = TypeUtils.IsConcreteGrainClass(type); if (isGrainInterface || isGrainClass) { // If code generation is being performed at runtime, the interface must be accessible to the generated code. if (!TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping inaccessible grain type, {0}", type.GetParseableName()); } continue; } // Attempt to generate serializers for grain state classes, i.e, T in Grain<T>. var baseType = type.BaseType; if (baseType != null && baseType.IsConstructedGenericType) { foreach (var arg in baseType.GetGenericArguments()) { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { logContext = "generic base type of " + type.GetLogFormat(); } this.serializableTypes.RecordType(arg, targetAssembly, logContext); } } // Skip classes generated by this generator. if (IsOrleansGeneratedCode(type)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping generated grain type, {0}", type.GetParseableName()); } continue; } if (this.knownGrainTypes.Contains(type)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping grain type {0} since it already has generated code.", type.GetParseableName()); } continue; } if (isGrainClass) { if (this.logger.IsEnabled(LogLevel.Information)) { this.logger.Info("Found grain implementation class: {0}", type.GetParseableName()); } grainClasses.Add(type); } if (isGrainInterface) { if (this.logger.IsEnabled(LogLevel.Information)) { this.logger.Info("Found grain interface: {0}", type.GetParseableName()); } GrainInterfaceUtils.ValidateInterfaceRules(type); grainInterfaces.Add(type); } } } } // Group the types by namespace and generate the required code in each namespace. foreach (var groupedGrainInterfaces in grainInterfaces.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceName = groupedGrainInterfaces.Key; var namespaceMembers = new List <MemberDeclarationSyntax>(); foreach (var grainInterface in groupedGrainInterfaces) { var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(grainInterface); var invokerTypeName = GrainMethodInvokerGenerator.GetGeneratedClassName(grainInterface); namespaceMembers.Add( GrainReferenceGenerator.GenerateClass( grainInterface, referenceTypeName, encounteredType => { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { logContext = "used by grain type " + grainInterface.GetLogFormat(); } this.serializableTypes.RecordType(encounteredType, targetAssembly, logContext); })); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(grainInterface, invokerTypeName)); var genericTypeSuffix = GetGenericTypeSuffix(grainInterface.GetGenericArguments().Length); features.GrainInterfaces.Add( new GrainInterfaceDescription { Interface = grainInterface.GetTypeSyntax(includeGenericParameters: false), Reference = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix), Invoker = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix), InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(grainInterface) }); } members.Add(CreateNamespace(namespaceName, namespaceMembers)); } foreach (var type in grainClasses) { features.GrainClasses.Add( new GrainClassDescription { ClassType = type.GetTypeSyntax(includeGenericParameters: false) }); } // Generate serializers into their own namespace. var serializerNamespace = this.GenerateSerializers(targetAssembly, features); members.Add(serializerNamespace); // Add serialization metadata for the types which were encountered. this.AddSerializationTypes(features.Serializers, targetAssembly, knownAssemblies.Keys.ToList()); foreach (var attribute in knownAssemblies.Keys.SelectMany(asm => asm.GetCustomAttributes <ConsiderForCodeGenerationAttribute>())) { this.serializableTypes.RecordType(attribute.Type, targetAssembly, "[ConsiderForCodeGeneration]"); if (attribute.ThrowOnFailure && !this.serializableTypes.IsTypeRecorded(attribute.Type) && !this.serializableTypes.IsTypeIgnored(attribute.Type)) { throw new CodeGenerationException( $"Found {attribute.GetType().Name} for type {attribute.Type.GetParseableName()}, but code" + " could not be generated. Ensure that the type is accessible."); } } // Generate metadata directives for all of the relevant types. var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, features); members.AddRange(memberDeclarations); var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray()); return(new GeneratedSyntax { SourceAssemblies = knownAssemblies.Keys.ToList(), Syntax = compilationUnit }); }
private static GrainReferenceCaster MakeCaster(Type interfaceType) { var typeInfo = interfaceType.GetTypeInfo(); CodeGeneratorManager.GenerateAndCacheCodeForAssembly(typeInfo.Assembly); var genericInterfaceType = interfaceType.IsConstructedGenericType ? typeInfo.GetGenericTypeDefinition() : interfaceType; // Try to find the correct GrainReference type for this interface. Type grainReferenceType; if (!GrainToReferenceMapping.TryGetValue(genericInterfaceType, out grainReferenceType)) { throw new InvalidOperationException( string.Format("Cannot find generated GrainReference class for interface '{0}'", interfaceType)); } if (interfaceType.IsConstructedGenericType) { grainReferenceType = grainReferenceType.MakeGenericType(typeInfo.GenericTypeArguments); } // Get the grain reference constructor. var constructor = grainReferenceType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .Where( _ => { var parameters = _.GetParameters(); return(parameters.Length == 1 && parameters[0].ParameterType == typeof(GrainReference)); }).FirstOrDefault(); if (constructor == null) { throw new InvalidOperationException( string.Format( "Cannot find suitable constructor on generated reference type for interface '{0}'", interfaceType)); } // Construct an expression to construct a new instance of this grain reference when given another grain // reference. var createLambdaParameter = Expression.Parameter(typeof(GrainReference), "gr"); var createLambda = Expression.Lambda <Func <GrainReference, IAddressable> >( Expression.New(constructor, createLambdaParameter), createLambdaParameter); var grainRefParameter = Expression.Parameter(typeof(IAddressable), "grainRef"); var body = Expression.Call( GrainReferenceCastInternalMethodInfo, Expression.Constant(interfaceType), createLambda, grainRefParameter, Expression.Constant(GrainInterfaceUtils.GetGrainInterfaceId(interfaceType))); // Compile and return the reference casting lambda. var lambda = Expression.Lambda <GrainReferenceCaster>(body, grainRefParameter); return(lambda.Compile()); }
/// <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> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="assemblies">The assemblies to generate code for.</param> /// <param name="runtime">Whether or not runtime code generation is being performed.</param> /// <returns>The generated syntax tree.</returns> private static GeneratedSyntax GenerateForAssemblies(List <Assembly> assemblies, bool runtime) { if (Logger.IsVerbose) { Logger.Verbose( "Generating code for assemblies: {0}", string.Join(", ", assemblies.Select(_ => _.FullName))); } Assembly targetAssembly; HashSet <Type> ignoredTypes; if (runtime) { // Ignore types which have already been accounted for. ignoredTypes = GetTypesWithGeneratedSupportClasses(); targetAssembly = null; } else { ignoredTypes = new HashSet <Type>(); targetAssembly = assemblies.FirstOrDefault(); } var members = new List <MemberDeclarationSyntax>(); // If any KnownAssemblies have been specified, include them during code generation. var knownAssemblies = assemblies.SelectMany(_ => _.GetCustomAttributes <KnownAssemblyAttribute>()) .Select(_ => _.Assembly) .Distinct() .ToSet(); if (knownAssemblies.Count > 0) { knownAssemblies.UnionWith(assemblies); assemblies = knownAssemblies.ToList(); } // Get types from assemblies which reference Orleans and are not generated assemblies. var includedTypes = new HashSet <Type>(); for (var i = 0; i < assemblies.Count; i++) { var assembly = assemblies[i]; foreach (var attribute in assembly.GetCustomAttributes <KnownTypeAttribute>()) { ConsiderType(attribute.Type, runtime, targetAssembly, includedTypes); } foreach (var type in assembly.DefinedTypes) { ConsiderType(type, runtime, targetAssembly, includedTypes); } } includedTypes.RemoveWhere(_ => ignoredTypes.Contains(_)); // Group the types by namespace and generate the required code in each namespace. foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceMembers = new List <MemberDeclarationSyntax>(); foreach (var type in group) { // The module containing the serializer. var module = runtime ? null : type.Module; // Every type which is encountered must be considered for serialization. Action <Type> onEncounteredType = encounteredType => { // If a type was encountered which can be accessed, process it for serialization. SerializerGenerationManager.RecordTypeToGenerate(encounteredType, module, targetAssembly); }; if (Logger.IsVerbose2) { Logger.Verbose2("Generating code for: {0}", type.GetParseableName()); } if (GrainInterfaceUtils.IsGrainInterface(type)) { if (Logger.IsVerbose2) { Logger.Verbose2( "Generating GrainReference and MethodInvoker for {0}", type.GetParseableName()); } GrainInterfaceUtils.ValidateInterfaceRules(type); namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, onEncounteredType)); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type)); } // Generate serializers. var first = true; Type toGen; while (SerializerGenerationManager.GetNextTypeToProcess(out toGen)) { if (!runtime) { if (first) { ConsoleText.WriteStatus("ClientGenerator - Generating serializer classes for types:"); first = false; } ConsoleText.WriteStatus( "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace + " defined in Assembly " + toGen.Assembly.GetName()); } if (Logger.IsVerbose2) { Logger.Verbose2( "Generating & Registering Serializer for Type {0}", toGen.GetParseableName()); } namespaceMembers.AddRange(SerializerGenerator.GenerateClass(toGen, onEncounteredType)); } } if (namespaceMembers.Count == 0) { if (Logger.IsVerbose) { Logger.Verbose2("Skipping namespace: {0}", group.Key); } continue; } members.Add( SF.NamespaceDeclaration(SF.ParseName(group.Key)) .AddUsings( TypeUtils.GetNamespaces(typeof(TaskUtility), typeof(GrainExtensions), typeof(IntrospectionExtensions)) .Select(_ => SF.UsingDirective(SF.ParseName(_))) .ToArray()) .AddMembers(namespaceMembers.ToArray())); } return(new GeneratedSyntax { SourceAssemblies = assemblies, Syntax = members.Count > 0 ? SF.CompilationUnit().AddMembers(members.ToArray()) : null }); }
/// <summary> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="assemblies">The assemblies to generate code for.</param> /// <param name="runtime">Whether or not runtime code generation is being performed.</param> /// <returns>The generated syntax tree.</returns> private GeneratedSyntax GenerateForAssemblies(Assembly targetAssembly, bool runtime) { var grainInterfaces = new List <GrainInterfaceDescription>(); var grainClasses = new List <GrainClassDescription>(); var serializationTypes = new SerializationTypeDescriptions(); var members = new List <MemberDeclarationSyntax>(); // Expand the list of included assemblies and types. var(includedTypes, assemblies) = this.GetIncludedTypes(targetAssembly, runtime); if (Logger.IsVerbose) { Logger.Verbose( "Generating code for assemblies: {0}", string.Join(", ", assemblies.Select(_ => _.FullName))); } var serializerNamespaceMembers = new List <MemberDeclarationSyntax>(); var serializerNamespaceName = $"{SerializerNamespacePrefix}{targetAssembly?.GetName().Name.GetHashCode():X}"; // Group the types by namespace and generate the required code in each namespace. foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceMembers = new List <MemberDeclarationSyntax>(); var namespaceName = group.Key; foreach (var type in group) { // Skip generated classes. if (type.GetCustomAttribute <GeneratedCodeAttribute>() != null) { continue; } // Every type which is encountered must be considered for serialization. void OnEncounteredType(Type encounteredType) { // If a type was encountered which can be accessed, process it for serialization. this.typeCollector.RecordEncounteredType(type); this.serializerGenerationManager.RecordTypeToGenerate(encounteredType, targetAssembly); } if (Logger.IsVerbose2) { Logger.Verbose2("Generating code for: {0}", type.GetParseableName()); } if (GrainInterfaceUtils.IsGrainInterface(type)) { if (Logger.IsVerbose2) { Logger.Verbose2( "Generating GrainReference and MethodInvoker for {0}", type.GetParseableName()); } GrainInterfaceUtils.ValidateInterfaceRules(type); var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(type); var invokerTypeName = GrainMethodInvokerGenerator.GetGeneratedClassName(type); namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, referenceTypeName, OnEncounteredType)); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type, invokerTypeName)); var genericTypeSuffix = GetGenericTypeSuffix(type.GetGenericArguments().Length); grainInterfaces.Add( new GrainInterfaceDescription { Interface = type.GetTypeSyntax(includeGenericParameters: false), Reference = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix), Invoker = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix), InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(type) }); } if (TypeUtils.IsConcreteGrainClass(type)) { grainClasses.Add( new GrainClassDescription { ClassType = type.GetTypeSyntax(includeGenericParameters: false) }); } // Generate serializers. var first = true; while (this.serializerGenerationManager.GetNextTypeToProcess(out var toGen)) { if (first) { Logger.Info("ClientGenerator - Generating serializer classes for types:"); first = false; } Logger.Info( "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace + " defined in Assembly " + toGen.GetTypeInfo().Assembly.GetName()); if (Logger.IsVerbose2) { Logger.Verbose2( "Generating serializer for type {0}", toGen.GetParseableName()); } var generatedSerializerName = SerializerGenerator.GetGeneratedClassName(toGen); serializerNamespaceMembers.Add(SerializerGenerator.GenerateClass(generatedSerializerName, toGen, OnEncounteredType)); var qualifiedSerializerName = serializerNamespaceName + '.' + generatedSerializerName + GetGenericTypeSuffix(toGen.GetGenericArguments().Length); serializationTypes.SerializerTypes.Add( new SerializerTypeDescription { Serializer = SF.ParseTypeName(qualifiedSerializerName), Target = toGen.GetTypeSyntax(includeGenericParameters: false) }); } } if (namespaceMembers.Count == 0) { if (Logger.IsVerbose) { Logger.Verbose2("Skipping namespace: {0}", namespaceName); } continue; } members.Add(CreateNamespace(namespaceName, namespaceMembers)); } // Add all generated serializers to their own namespace. members.Add(CreateNamespace(serializerNamespaceName, serializerNamespaceMembers)); // Add serialization metadata for the types which were encountered. this.AddSerializationTypes(serializationTypes, targetAssembly); // Generate metadata directives for all of the relevant types. var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, grainInterfaces, grainClasses, serializationTypes); members.AddRange(memberDeclarations); var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray()); return(new GeneratedSyntax { SourceAssemblies = assemblies, Syntax = compilationUnit }); string GetGenericTypeSuffix(int numParams) { if (numParams == 0) { return(string.Empty); } return('<' + new string(',', numParams - 1) + '>'); } NamespaceDeclarationSyntax CreateNamespace(string namespaceName, IEnumerable <MemberDeclarationSyntax> namespaceMembers) { return (SF.NamespaceDeclaration(SF.ParseName(namespaceName)) .AddUsings( TypeUtils.GetNamespaces(typeof(GrainExtensions), typeof(IntrospectionExtensions)) .Select(_ => SF.UsingDirective(SF.ParseName(_))) .ToArray()) .AddMembers(namespaceMembers.ToArray())); } }