private static MemberDeclarationSyntax GenerateIsCompatibleMethod(Type grainType) { var method = TypeUtils.Method((GrainReference _) => _.IsCompatible(default(int))); var methodDeclaration = method.GetDeclarationSyntax(); var interfaceIdParameter = method.GetParameters()[0].Name.ToIdentifierName(); var interfaceIds = new HashSet <int>( new[] { GrainInterfaceUtils.GetGrainInterfaceId(grainType) }.Concat( GrainInterfaceUtils.GetRemoteInterfaces(grainType).Keys)); var returnValue = default(BinaryExpressionSyntax); foreach (var interfaceId in interfaceIds) { var check = SF.BinaryExpression( SyntaxKind.EqualsExpression, interfaceIdParameter, SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId))); // If this is the first check, assign it, otherwise OR this check with the previous checks. returnValue = returnValue == null ? check : SF.BinaryExpression(SyntaxKind.LogicalOrExpression, returnValue, check); } return (methodDeclaration.AddBodyStatements(SF.ReturnStatement(returnValue)) .AddModifiers(SF.Token(SyntaxKind.OverrideKeyword))); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> /// <param name="grainType"> /// The grain interface type. /// </param> /// <returns> /// The generated class. /// </returns> internal static TypeDeclarationSyntax GenerateClass(Type grainType) { var baseTypes = new List <BaseTypeSyntax> { SF.SimpleBaseType(typeof(IGrainMethodInvoker).GetTypeSyntax()) }; var grainTypeInfo = grainType.GetTypeInfo(); var genericTypes = grainTypeInfo.IsGenericTypeDefinition ? grainTypeInfo.GetGenericArguments() .Select(_ => SF.TypeParameter(_.ToString())) .ToArray() : new TypeParameterSyntax[0]; // Create the special method invoker marker attribute. var interfaceId = GrainInterfaceUtils.GetGrainInterfaceId(grainType); var interfaceIdArgument = SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId)); var grainTypeArgument = SF.TypeOfExpression(grainType.GetTypeSyntax(includeGenericParameters: false)); var attributes = new List <AttributeSyntax> { CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), SF.Attribute(typeof(MethodInvokerAttribute).GetNameSyntax()) .AddArgumentListArguments( SF.AttributeArgument(grainType.GetParseableName().GetLiteralExpression()), SF.AttributeArgument(interfaceIdArgument), SF.AttributeArgument(grainTypeArgument)), #if !NETSTANDARD SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()) #endif }; var members = new List <MemberDeclarationSyntax> { GenerateInvokeMethod(grainType), GenerateInterfaceIdProperty(grainType) }; // If this is an IGrainExtension, make the generated class implement IGrainExtensionMethodInvoker. if (typeof(IGrainExtension).IsAssignableFrom(grainType)) { baseTypes.Add(SF.SimpleBaseType(typeof(IGrainExtensionMethodInvoker).GetTypeSyntax())); members.Add(GenerateExtensionInvokeMethod(grainType)); } var classDeclaration = SF.ClassDeclaration( CodeGeneratorCommon.ClassPrefix + TypeUtils.GetSuitableClassName(grainType) + ClassSuffix) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddBaseListTypes(baseTypes.ToArray()) .AddConstraintClauses(grainType.GetTypeConstraintSyntax()) .AddMembers(members.ToArray()) .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray())); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } return(classDeclaration); }
private static MemberDeclarationSyntax GenerateInterfaceIdProperty(Type grainType) { var property = TypeUtils.Member((IGrainMethodInvoker _) => _.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.ProtectedKeyword), SF.Token(SyntaxKind.OverrideKeyword))); }
/// <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 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())); } }