public void AddConstants(SyntaxNode attribute, SemanticModel semanticModel, EventSourceTypeInfo eventSourceTypeInfo) { if (attribute == null) throw new ArgumentNullException("attribute", "attribute is null."); AttributeSyntax attributeSyntax; AttributeListSyntax attributeListSyntax = attribute as AttributeListSyntax; if (attributeListSyntax != null) { if (attributeListSyntax.Attributes.Count != 1) throw new GenerationException(attribute.GetLocation(), "Expected a single attribute in attribute list, but either none or more than one were found."); attributeSyntax = attributeListSyntax.Attributes.Single(); } else { attributeSyntax = attribute as AttributeSyntax; if (attributeSyntax == null) throw new GenerationException($"SyntaxNode was not of expected type {typeof(AttributeSyntax).FullName}"); } AddToDictionary(attributeSyntax, "Keywords", m_keywords, semanticModel, eventSourceTypeInfo.EventKeywordsType); AddToDictionary(attributeSyntax, "Opcode", m_opcodes, semanticModel, eventSourceTypeInfo.EventOpcodeType); AddToDictionary(attributeSyntax, "Task", m_tasks, semanticModel, eventSourceTypeInfo.EventTaskType); }
public CollectedGenerationInfo(EventSourceTypeInfo eventSourceTypeInfo) { if (eventSourceTypeInfo == null) throw new ArgumentNullException("eventSourceTypeInfo", "eventSourceTypeInfo is null."); m_eventSourceTypeInfo = eventSourceTypeInfo; }
public CollectedGenerationInfo(EventSourceTypeInfo eventSourceTypeInfo) { if (eventSourceTypeInfo == null) { throw new ArgumentNullException("eventSourceTypeInfo", "eventSourceTypeInfo is null."); } m_eventSourceTypeInfo = eventSourceTypeInfo; }
private TemplateEventMethodInfo TranslateMethodAttributes(IMethodSymbol sourceMethod, EventSourceTypeInfo eventSourceTypeInfo, CollectedGenerationInfo overloads) { List<SyntaxNode> attributes = new List<SyntaxNode>(); SyntaxNode eventAttribute = null; int? eventId = null; foreach (AttributeData attributeData in sourceMethod.GetAttributes()) { var attributeClass = attributeData.AttributeClass; if (attributeClass.Name.Equals(TemplateEventAttributeName) || attributeClass.Equals(eventSourceTypeInfo.EventAttributeType)) { SyntaxNode attributeSyntax = attributeData.ApplicationSyntaxReference?.GetSyntax(); if (attributeSyntax == null) throw new CodeGeneratorException(sourceMethod, $"Cannot find the source file containing the method {sourceMethod.Name}. The source code must be available for any Template EventSource class to participate in generation. Is the project unloaded?"); Document attributeDocument = m_document.Project.Solution.GetDocument(attributeSyntax.SyntaxTree); if (attributeDocument == null) throw new CodeGeneratorException(sourceMethod, $"Cannot find the document containing the method {sourceMethod.Name}."); overloads.AddConstants(attributeSyntax, attributeDocument.GetSemanticModelAsync().Result, eventSourceTypeInfo); attributeSyntax = m_generator.Attribute(eventSourceTypeInfo.EventAttributeType.GetFullName(), m_generator.GetAttributeArguments(attributeSyntax)); attributes.Add(attributeSyntax); TypedConstant eventIdArgument = attributeData.ConstructorArguments.FirstOrDefault(); if (attributeData.ConstructorArguments.Length == 0) throw new CodeGeneratorException(sourceMethod, $"The {attributeData.AttributeClass.Name} attribute must have an event ID as its first argument."); if (!(eventIdArgument.Value is int)) throw new CodeGeneratorException(sourceMethod, $"The first argument to the {attributeData.AttributeClass.Name} attribute must be of type Int32."); eventId = (int)eventIdArgument.Value; eventAttribute = attributeSyntax; } else { attributes.Add(CreateAttribute(attributeData)); } } if (eventAttribute == null) throw new CodeGeneratorException(sourceMethod, $"Internal error; Unable to find EventAttribute or TemplateEventAttribute on method {sourceMethod.Name}"); if (eventId == null) throw new CodeGeneratorException(sourceMethod, $"Unable to determine EventId for method {sourceMethod.Name}"); return new TemplateEventMethodInfo(attributes, eventId.Value); }
/// <summary> /// Gets all the attributes from the specified <paramref name="sourceClass"/>, with special handling and translation /// of a TemplateEventSourceAttribute which will be translated into an EventSourceAttribute with additional arguments stripped away. /// </summary> /// <param name="sourceClass">The clas from which to retrieve and translate attribtues.</param> /// <returns>A list of all attributes that should be applied to the target class.</returns> private IEnumerable<SyntaxNode> GetClassAttributesWithTranslation(INamedTypeSymbol sourceClass, EventSourceTypeInfo baseTypeInfo) { foreach (var attributeData in sourceClass.GetAttributes()) { if (attributeData.AttributeClass.Name.Equals(TemplateEventSourceAttributeName)) { yield return m_generator.Attribute(baseTypeInfo.EventSourceAttributeType.GetFullName(), attributeData.ConstructorArguments.Select(arg => m_generator.AttributeArgument(m_generator.LiteralExpression(arg.Value))) .Concat( attributeData.NamedArguments.Where(arg => !typeof(GenerationOptions).GetProperties().Any(p => p.Name.Equals(arg.Key))) .Select(arg => m_generator.AttributeArgument(arg.Key, m_generator.LiteralExpression(arg.Value.Value))) ) ); } else { yield return m_generator.Attribute(attributeData); } } }
/// <summary>Gets the event source template methods in this collection.</summary> /// <param name="sourceClass">The class from which to retrieve methods.</param> /// <param name="eventSourceBase">The event source base of the <paramref name="sourceClass"/>.</param> /// <returns> /// All methods that are candidates to be used for EventSource generation. /// </returns> private IEnumerable<IMethodSymbol> GetEventSourceTemplateMethods(INamedTypeSymbol sourceClass, EventSourceTypeInfo eventSourceBase) { System.Diagnostics.Debug.Assert(sourceClass.IsType); foreach (IMethodSymbol method in sourceClass.GetAllMembers().OfType<IMethodSymbol>()) { if (method.GetAttributes().Any(attribute => attribute.AttributeClass.Equals(eventSourceBase.EventAttributeType)) && method.IsAbstract) { yield return method; } else if (method.GetAttributes().Any(attribute => attribute.AttributeClass.Name.Equals(TemplateEventAttributeName))) { if (!method.IsAbstract) throw new CodeGeneratorException(method, $"The method {sourceClass.Name}.{method.Name} must be abstract to participate in EventSource generation."); yield return method; } } }
private IEnumerable<SyntaxNode> GenerateEventSourceMethods(INamedTypeSymbol sourceClass, EventSourceTypeInfo eventSourceTypeInfo, CollectedGenerationInfo overloads, GenerationOptions options) { var templateMethods = GetEventSourceTemplateMethods(sourceClass, eventSourceTypeInfo); foreach (var sourceMethodEntry in templateMethods.AsSmartEnumerable()) { IMethodSymbol sourceMethod = sourceMethodEntry.Value; TemplateEventMethodInfo eventAttributeInfo = TranslateMethodAttributes(sourceMethod, eventSourceTypeInfo, overloads); WriteEventOverloadInfo overloadInfo = new WriteEventOverloadInfo(sourceMethodEntry.Value, options, m_parameterConverters); if (overloadInfo.Parameters.Any(p => p.IsSupported == false)) { throw new CodeGeneratorException(sourceMethod, $"The parameter(s) {StringUtils.Join(overloadInfo.Parameters.Where(p => !p.IsSupported).Select(p => $"{p.Parameter.Name} ({p.Parameter.Type.Name})"), ", ", " and ")} are not supported."); } overloads.TryAdd(overloadInfo); // Check if this method has needs a wrapper method to perform parameter conversion into a natively supported parameter type. if (overloadInfo.NeedsConverter) { // Create the wrapper method SyntaxNode wrapperMethod = m_generator.MethodDeclaration(sourceMethod); // This method should only have the [NonEvent] attribute. wrapperMethod = m_generator.AddAttributes(m_generator.RemoveAllAttributes(wrapperMethod), m_generator.Attribute(eventSourceTypeInfo.EventSourceNamespace.GetFullName() + ".NonEvent")); wrapperMethod = m_generator.WithAccessibility(wrapperMethod, sourceMethod.DeclaredAccessibility); wrapperMethod = m_generator.WithModifiers(wrapperMethod, DeclarationModifiers.Override); // And it should call the overload of the same method that is generated below (the actual event method) wrapperMethod = m_generator.WithStatements(wrapperMethod, new[] { m_generator.IfStatement( // Condition m_generator.InvocationExpression(m_generator.IdentifierName("IsEnabled")), // True-Statements new[] { m_generator.ExpressionStatement( m_generator.InvocationExpression(m_generator.IdentifierName(sourceMethod.Name), overloadInfo.Parameters.Select(parameter => m_generator.Argument( parameter.HasConverter ? parameter.Converter.GetConversionExpression(m_generator.IdentifierName(parameter.Parameter.Name)) : m_generator.IdentifierName(parameter.Parameter.Name) ) ) ) ) } ) } ); // And let's add some warning comments about this being generated code. wrapperMethod = wrapperMethod.WithLeadingTrivia(wrapperMethod.GetLeadingTrivia().AddRange(CreateWarningComment())); yield return wrapperMethod; } // Generate the actual event method. SyntaxNode eventMethod = m_generator.MethodDeclaration(sourceMethod.Name); if (overloadInfo.NeedsConverter) { // If we have a wrapper method converting parameters, this will be a private method. eventMethod = m_generator.WithAccessibility(eventMethod, Accessibility.Private); } else { // Otherwise it has the same accessibility as the base class method, except this is an override of course. eventMethod = m_generator.WithAccessibility(eventMethod, sourceMethod.DeclaredAccessibility); eventMethod = m_generator.WithModifiers(eventMethod, DeclarationModifiers.Override); } // The parameter list may be modified from the source method to account for any conversions performed by the wrapper method. eventMethod = m_generator.AddParameters(eventMethod, overloadInfo.Parameters.Select(pi => m_generator.ParameterDeclaration(pi.Parameter.Name, m_generator.TypeExpression(pi.TargetType))) ); // Statement to call the WriteEvent() method. SyntaxNode writeEventStatement = m_generator.ExpressionStatement( m_generator.InvocationExpression(m_generator.IdentifierName("WriteEvent"), new[] { m_generator.Argument(m_generator.LiteralExpression(eventAttributeInfo.EventId)), }.Concat(overloadInfo.Parameters.Select(parameter => m_generator.Argument( m_generator.IdentifierName(parameter.Parameter.Name)) ) ) ) ); if (overloadInfo.NeedsConverter) { // If this method has a wrapper method, then the IsEnabled() check has already been made, so we skip that here // and just call the WriteEvent() method. eventMethod = m_generator.WithStatements(eventMethod, new[] { writeEventStatement }); } else { // Otherwise we want to check the IsEnabled() flag first. eventMethod = m_generator.WithStatements(eventMethod, new[] { m_generator.IfStatement( // Condition m_generator.InvocationExpression(m_generator.IdentifierName("IsEnabled")), // True-Statements new[] { writeEventStatement } ) } ); } // Add all attributes from the source method. (Well, with translation of the TemplateEventAttribute). eventMethod = m_generator.AddAttributes(eventMethod, eventAttributeInfo.Attributes); // And some warning comments as usual. eventMethod = eventMethod.WithLeadingTrivia(eventMethod.GetLeadingTrivia().AddRange(CreateWarningComment())); yield return eventMethod; } }
private IEnumerable<SyntaxNode> GenerateWriteEventOverloads(CollectedGenerationInfo overloads, GenerationOptions options, EventSourceTypeInfo eventSourceTypeInfo) { foreach (WriteEventOverloadInfo overload in overloads.Overloads) { SyntaxNode method = m_generator.MethodDeclaration("WriteEvent", new[] { m_generator.ParameterDeclaration("eventId", m_generator.TypeExpression(SpecialType.System_Int32)) } .Concat(overload.Parameters.Select((pi, idx) => m_generator.ParameterDeclaration($"arg{idx}", m_generator.TypeExpression(pi.TargetType)))), accessibility: Accessibility.Private); method = WithUnsafeModifier(method); method = m_generator.AddAttributes(method, m_generator.Attribute( m_generator.TypeExpression( m_semanticModel.Compilation.GetTypeByMetadataName(eventSourceTypeInfo.EventSourceNamespace.GetFullName() + ".NonEventAttribute") ))); List<SyntaxNode> statements = new List<SyntaxNode>(); for (int i = 0; i < overload.Parameters.Length; i++) { if (overload.Parameters[i].TargetType.SpecialType == SpecialType.System_String) { statements.Add(m_generator.IfStatement(m_generator.ReferenceEqualsExpression(m_generator.IdentifierName($"arg{i}"), m_generator.NullLiteralExpression()), new[] { m_generator.AssignmentStatement(m_generator.IdentifierName($"arg{i}"), m_generator.MemberAccessExpression(m_generator.TypeExpression(SpecialType.System_String), "Empty") ) } )); } else if (overload.Parameters[i].TargetType.TypeKind == TypeKind.Array && ((IArrayTypeSymbol)overload.Parameters[i].TargetType).ElementType.SpecialType == SpecialType.System_Byte) { statements.Add(m_generator.IfStatement(m_generator.ReferenceEqualsExpression(m_generator.IdentifierName($"arg{i}"), m_generator.NullLiteralExpression()), new[] { m_generator.AssignmentStatement(m_generator.IdentifierName($"arg{i}"), m_generator.ArrayCreationExpression( m_generator.TypeExpression(SpecialType.System_Byte), m_generator.LiteralExpression(0) ) ) } )); } else if (overload.Parameters[i].TargetType.SpecialType == SpecialType.System_DateTime) { statements.Add( m_generator.LocalDeclarationStatement( m_generator.TypeExpression(SpecialType.System_Int64), $"fileTime{i}", m_generator.InvocationExpression( m_generator.MemberAccessExpression( m_generator.IdentifierName($"arg{i}"), m_generator.IdentifierName("ToFileTimeUtc") ) ) ) ); } else if (overload.Parameters[i].TargetType.TypeKind == TypeKind.Enum) { INamedTypeSymbol namedTypeSymbol = (INamedTypeSymbol)overload.Parameters[i].TargetType; statements.Add( m_generator.LocalDeclarationStatement( m_generator.TypeExpression(namedTypeSymbol.EnumUnderlyingType), $"enumValue{i}", m_generator.CastExpression( m_generator.TypeExpression(namedTypeSymbol.EnumUnderlyingType), m_generator.IdentifierName($"arg{i}") ) ) ); } } // Descriptor length. A byte-array actually takes up two descriptor-slots (length + data), so we calculate the total size here."); int descrLength = overload.Parameters.Select(pi => pi.TargetType.IsByteArray() ? 2 : 1).Sum(); string eventDataTypeFullName = eventSourceTypeInfo.EventSourceClass.GetFullName() + "+EventData"; INamedTypeSymbol eventDataType = m_compilation.GetTypeByMetadataName(eventDataTypeFullName); if (eventDataType == null) throw new CodeGeneratorException($"Failed to lookup type {eventDataTypeFullName}."); TypeSyntax eventDataTypeSyntax = (TypeSyntax)m_generator.TypeExpression(eventDataType); //EventData* descrs = stackalloc EventData[{descrLength}]; statements.Add( SF.LocalDeclarationStatement( SF.VariableDeclaration( SF.PointerType( eventDataTypeSyntax ), SF.SingletonSeparatedList( SF.VariableDeclarator( SF.Identifier("descrs"), null, SF.EqualsValueClause( SF.Token(SyntaxKind.EqualsToken), SF.StackAllocArrayCreationExpression( SF.ArrayType( eventDataTypeSyntax, SF.SingletonList( SF.ArrayRankSpecifier( SF.SingletonSeparatedList( m_generator.LiteralExpression(descrLength) as ExpressionSyntax ) ) ) ) ) ) ) ) ) ) ); List<SyntaxNode> innerStatements = new List<SyntaxNode>(); int descrPos = 0; for (int i = 0; i < overload.Parameters.Length; i++, descrPos++) { if (overload.Parameters[i].TargetType.IsByteArray()) { // ==> int length{i} = arg{i}.Length; innerStatements.Add( m_generator.LocalDeclarationStatement( m_generator.TypeExpression(SpecialType.System_Int32), $"length{i}", m_generator.MemberAccessExpression(m_generator.IdentifierName($"arg{i}"), "Length") ) ); // ==> descrs[{descrPos}].DataPointer = (IntPtr)(&length{i}); innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", AddressOfVariableAsIntPtrSyntax($"length{i}")) ); // ==> descrs[{descrPos}].Size = 4; innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", m_generator.LiteralExpression(4)) ); descrPos++; // ==> descrs[{descrPos}].DataPointer = (IntPtr)bin{i} innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", m_generator.CastExpression(IntPtrType, m_generator.IdentifierName($"bin{i}"))) ); // ==> descrs[{descrPos}].Size = length{i} innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", m_generator.IdentifierName($"length{i}")) ); } else if (overload.Parameters[i].TargetType.SpecialType == SpecialType.System_String) { // ==> descrs[{descrPos}].DataPointer = (IntPtr)str{i} innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", m_generator.CastExpression(IntPtrType, m_generator.IdentifierName($"str{i}"))) ); // ==> descrs[{descrPos}].Size = (arg{i}.Length + 1) * 2; innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", m_generator.MultiplyExpression( m_generator.AddExpression( m_generator.MemberAccessExpression( m_generator.IdentifierName($"arg{i}"), m_generator.IdentifierName($"Length") ), m_generator.LiteralExpression(1) ), m_generator.LiteralExpression(2) ) ) ); } else if (overload.Parameters[i].TargetType.SpecialType == SpecialType.System_DateTime) { // ==> descrs[{descrPos}].DataPointer = (IntPtr)(&fileTime{i}); innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", AddressOfVariableAsIntPtrSyntax($"fileTime{i}")) ); // ==> descrs[{descrPos}].Size = 8; innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", m_generator.LiteralExpression(8)) ); } else if (overload.Parameters[i].TargetType.TypeKind == TypeKind.Enum) { INamedTypeSymbol namedTypeSymbol = (INamedTypeSymbol)overload.Parameters[i].TargetType; // ==> descrs[{descrPos}].DataPointer = (IntPtr)(&enumValue{i}); innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", AddressOfVariableAsIntPtrSyntax($"enumValue{i}")) ); // ==> descrs[{descrPos}].Size = 8; innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", GetTypeSize(overload.Parameters[i].TargetType)) ); } else { // ==> descrs[{descrPos}].DataPointer = (IntPtr)(&arg{i}); innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "DataPointer", AddressOfVariableAsIntPtrSyntax($"arg{i}")) ); // ==> descrs[{descrPos}].Size = 8; innerStatements.Add( AssignDescrSyntax("descrs", descrPos, "Size", GetTypeSize(overload.Parameters[i].TargetType)) ); } } innerStatements.Add( m_generator.ExpressionStatement( m_generator.InvocationExpression( m_generator.IdentifierName("WriteEventCore"), m_generator.IdentifierName("eventId"), m_generator.LiteralExpression(descrLength), m_generator.IdentifierName("descrs") ) ) ); // Create fixed statements BlockSyntax fixedContent = SF.Block(innerStatements.Cast<StatementSyntax>()); FixedStatementSyntax fixedStatementSyntax = null; for (int i = 0; i < overload.Parameters.Length; i++) { FixedStatementSyntax current = null; if (overload.Parameters[i].TargetType.SpecialType == SpecialType.System_String) { current = GetFixedStatement(SyntaxKind.CharKeyword, fixedContent, $"str{i}", $"arg{i}"); } else if (overload.Parameters[i].TargetType.IsByteArray()) { current = GetFixedStatement(SyntaxKind.ByteKeyword, fixedContent, $"bin{i}", $"arg{i}"); } if (current != null) { if (fixedStatementSyntax == null) { fixedStatementSyntax = current; } else { fixedStatementSyntax = current.WithStatement(SF.Block(fixedStatementSyntax)); } } } if (fixedStatementSyntax != null) { statements.Add(fixedStatementSyntax); } else { statements.Add(fixedContent); } method = m_generator.WithStatements(method, statements); method = method.PrependLeadingTrivia(CreateWarningComment()); yield return method; } }
public void AddConstants(SyntaxNode attribute, SemanticModel semanticModel, EventSourceTypeInfo eventSourceTypeInfo) { if (attribute == null) { throw new ArgumentNullException("attribute", "attribute is null."); } AttributeSyntax attributeSyntax; AttributeListSyntax attributeListSyntax = attribute as AttributeListSyntax; if (attributeListSyntax != null) { if (attributeListSyntax.Attributes.Count != 1) { throw new CodeGeneratorException(attribute, "Expected a single attribute in attribute list, but either none or more than one were found."); } attributeSyntax = attributeListSyntax.Attributes.Single(); } else { attributeSyntax = attribute as AttributeSyntax; if (attributeSyntax == null) { throw new CodeGeneratorException(attribute, $"SyntaxNode was not of expected type {typeof(AttributeSyntax).FullName}"); } } AddToDictionary(attributeSyntax, "Keywords", m_keywords, semanticModel, eventSourceTypeInfo.EventKeywordsType); AddToDictionary(attributeSyntax, "Opcode", m_opcodes, semanticModel, eventSourceTypeInfo.EventOpcodeType); AddToDictionary(attributeSyntax, "Task", m_tasks, semanticModel, eventSourceTypeInfo.EventTaskType); }