public IEnumerable <SyntaxNode> BuildPort(IPortModel portModel, PortSemantic portSemantic = PortSemantic.Read) { var buildPortInner = BuildPortInner(portModel, out var builtNode).ToList(); if (portSemantic == PortSemantic.Read && (Options & UnityEngine.Modifier.VisualScripting.CompilationOptions.Tracing) != 0 && builtNode != null && buildPortInner.Count == 1 && buildPortInner.First() is ExpressionSyntax exp) { return(Enumerable.Repeat(InstrumentForInEditorDebugging.RecordValue(GetRecorderName(), exp, null, (NodeModel)builtNode), 1)); } return(buildPortInner); }
internal IEnumerable <StatementSyntax> Instrument(StatementSyntax syntaxNode, INodeModel nodeModel, CompilationOptions options) { // TODO: RecordValue codegen counter instead of counting them after the fact if ((options & CompilationOptions.Tracing) != 0) { int recordedValuesCount = syntaxNode.GetAnnotatedNodes(Annotations.RecordValueKind).Count(); yield return(InstrumentForInEditorDebugging.BuildLastCallFrameExpression(recordedValuesCount, nodeModel.Guid, GetRecorderName(), nodeModel is INodeModelProgress && nodeModel is CoroutineNodeModel coroutineNodeModel ? MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(CoroutineParameterName), IdentifierName(coroutineNodeModel.VariableName)) : null)); } yield return(syntaxNode); }
public virtual void BuildStack(IStackModel stack, ref BlockSyntax blockNode, StackExitStrategy exitStrategy = StackExitStrategy.Return) { if (stack == null || stack.State == ModelState.Disabled) { return; } RegisterBuiltStack(stack); // JUST in case... until we validate the previous failsafe if (m_BuiltStackCounter++ > 10000) { throw new InvalidOperationException("Infinite loop while building the script, aborting"); } StackExitStrategy origStackExitStrategy = m_StackExitStrategy; if (exitStrategy != StackExitStrategy.Inherit) { m_StackExitStrategy = exitStrategy; } // Debug.Log($"Build stack {stack}"); // m_EndStack might get changed by recursive BuildNode() calls StackBaseModel origEndStack = EndStack; var statements = new List <StatementSyntax>(); foreach (var statement in stack.NodeModels) { if (statement.State == ModelState.Disabled) { continue; } var syntaxNodes = BuildNode(statement); foreach (var syntaxNode in syntaxNodes) { StatementSyntax resultingStatement; switch (syntaxNode) { case StatementSyntax statementNode: resultingStatement = statementNode; break; case ExpressionSyntax expressionNode: resultingStatement = ExpressionStatement(expressionNode); break; default: throw new InvalidOperationException($"Expected a statement or expression node, found a {syntaxNode.GetType()} when building {statement}"); } // TODO: RecordValue codegen counter instead of counting them after the fact if ((Options & UnityEngine.Modifier.VisualScripting.CompilationOptions.Tracing) != 0) { var recordValueCount = syntaxNode.GetAnnotations(Annotations.RecordValueCountKind).FirstOrDefault(); int recordedValuesCount = recordValueCount == null ? syntaxNode.GetAnnotatedNodes(Annotations.RecordValueKind).Count() : int.Parse(recordValueCount.Data); statements.Add(InstrumentForInEditorDebugging.BuildLastCallFrameExpression(recordedValuesCount, statement.Guid, this.GetRecorderName())); } statements.Add(resultingStatement); } } blockNode = blockNode.AddStatements(statements.ToArray()); if (stack.DelegatesOutputsToNode(out _)) { var nextStack = EndStack; m_StackExitStrategy = origStackExitStrategy; // Debug.Log($"Stack {stack} delegates ports. nextStack {nextStack} origEndStack {origEndStack}"); // if a nested node changed the end stack, but found the same common descendant, // let the parent call handle it if (EndStack == origEndStack) { return; } EndStack = origEndStack; BuildStack(nextStack, ref blockNode, exitStrategy); return; } bool anyConnection = false; foreach (var outputPort in stack.OutputPorts) { foreach (var connectedStack in outputPort.ConnectionPortModels) { if (connectedStack.NodeModel is IStackModel nextStack) { anyConnection = true; if (!ReferenceEquals(nextStack, EndStack)) { BuildStack(nextStack, ref blockNode, StackExitStrategy.Inherit); } } } } // TODO use function default return value StatementSyntax lastStatementSyntax = blockNode.Statements.LastOrDefault(); if (!anyConnection && !(lastStatementSyntax is ReturnStatementSyntax) && !(lastStatementSyntax is ContinueStatementSyntax)) { //TODO we had that for a reason // lastStatementSyntax = StatementFromExitStrategy(m_StackExitStrategy, null); // if(lastStatementSyntax != null) // blockNode = blockNode.AddStatements(lastStatementSyntax); } m_StackExitStrategy = origStackExitStrategy; }
StatementSyntax BuildInner() { const string eventBufferName = "eventBuffer"; Type eventType = IterationContext.UpdateMode == UpdateMode.OnEvent ? ((OnEventNodeModel)IterationContext.Query).EventTypeHandle.Resolve(IterationContext.Stencil) : null; var parameters = new List <ParameterSyntax> { Parameter( Identifier(EntityName)) .WithType(typeof(Entity).ToTypeSyntax()) }; var block = Block(); if (!string.IsNullOrEmpty(m_CoroutineComponentName)) { parameters.Add(Parameter( Identifier(CoroutineContext.BuildCoroutineParameterName(m_CoroutineComponentName))) .WithModifiers(TokenList(Token(SyntaxKind.RefKeyword))) .WithType(IdentifierName(m_CoroutineComponentName))); } var stateComponentName = IterationContext.UpdateMode == UpdateMode.OnUpdate || IterationContext.UpdateMode == UpdateMode.OnEvent ? null : IncludeTrackingSystemStateComponent(IterationContext.Query.ComponentQueryDeclarationModel, IterationContext.UpdateMode == UpdateMode.OnEnd); var stateComponentParameterName = stateComponentName?.ToLowerInvariant(); switch (IterationContext.UpdateMode) { case UpdateMode.OnUpdate: break; case UpdateMode.OnEvent: // Add it at the end break; case UpdateMode.OnStart: m_UpdateStatements.AddRange(GetEntityManipulationTranslator().AddComponent( this, IdentifierName(EntityName), DefaultExpression(IdentifierName(stateComponentName)), IdentifierName(stateComponentName), false)); break; case UpdateMode.OnEnd: parameters.Add(MakeLambdaParameter( stateComponentParameterName, IdentifierName(stateComponentName), false, false)); m_UpdateStatements.AddRange(GetEntityManipulationTranslator().RemoveComponent( this, IdentifierName(EntityName), IdentifierName(stateComponentName))); break; default: throw new ArgumentOutOfRangeException(); } // SharedComponents should be inserted first in ForEach var sortedComponents = IterationContext.FlattenedComponentDefinitions() .OrderBy(d => !typeof(ISharedComponentData).IsAssignableFrom(d.TypeHandle.Resolve(IterationContext.Stencil))) .ToList(); foreach (ComponentDefinition definition in sortedComponents) { if (!RoslynEcsTranslatorExtensions.ShouldGenerateComponentAccess( definition.TypeHandle, true, out Type componentType, IterationContext.Stencil, out bool _, out bool isGameObjectComponent)) { continue; } if (!m_WrittenComponents.TryGetValue(definition.TypeHandle, out RoslynEcsTranslator.AccessMode mode)) { mode = RoslynEcsTranslator.AccessMode.None; } if (mode == RoslynEcsTranslator.AccessMode.None) { continue; } string componentDataName = IterationContext.GetComponentDataName(componentType); parameters.Add(MakeLambdaParameter( componentDataName, componentType.ToTypeSyntax(), isGameObjectComponent, typeof(ISharedComponentData).IsAssignableFrom(componentType))); } switch (IterationContext.UpdateMode) { case UpdateMode.OnEvent: parameters.Add(MakeLambdaParameter( eventBufferName, GenericName("DynamicBuffer").WithTypeArgumentList(TypeArgumentList(SingletonSeparatedList(eventType.ToTypeSyntax()))), false, true)); break; } ExpressionSyntax baseQuery = InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Entities"), IdentifierName("With"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( IdentifierName(IterationContext.UpdateMode == UpdateMode.OnEvent ? SendEventTranslator.MakeQueryIncludingEventName(IterationContext, eventType) : IterationContext.GroupName))))); if (IterationContext.UpdateMode == UpdateMode.OnEnd) { // REPLACE query baseQuery = IdentifierName("Entities"); m_UpdateStatements.Insert(0, IfStatement( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(stateComponentParameterName), IdentifierName("Processed")), ReturnStatement())); } BlockSyntax innerLambdaBody; if (IterationContext.UpdateMode == UpdateMode.OnEvent) { //for (int event_index = 0; event_index < eventBuffer.Length; event_index++) { T ev = eventBuffer[event_index]; ... } const string eventIndexName = "event_index"; m_UpdateStatements.Insert(0, RoslynBuilder.DeclareLocalVariable(eventType, "ev", ElementAccessExpression( IdentifierName(eventBufferName)) .WithArgumentList( BracketedArgumentList( SingletonSeparatedList( Argument( IdentifierName(eventIndexName))))) .NormalizeWhitespace())); innerLambdaBody = Block(ForStatement( Block(m_UpdateStatements)) .WithDeclaration( VariableDeclaration( PredefinedType( Token(SyntaxKind.IntKeyword))) .WithVariables( SingletonSeparatedList( VariableDeclarator( Identifier(eventIndexName)) .WithInitializer( EqualsValueClause( LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(0))))))) .WithCondition( BinaryExpression( SyntaxKind.LessThanExpression, IdentifierName(eventIndexName), MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(eventBufferName), IdentifierName("Length")))) .WithIncrementors( SingletonSeparatedList <ExpressionSyntax>( PostfixUnaryExpression( SyntaxKind.PostIncrementExpression, IdentifierName(eventIndexName)))) .NormalizeWhitespace()); } else { innerLambdaBody = Block(m_UpdateStatements); } if ((TranslationOptions & RoslynEcsTranslator.TranslationOptions.Tracing) != 0) { var recorderDeclaration = RoslynBuilder.DeclareLocalVariable(typeof(DebuggerTracer.EntityFrameTrace), DebugInstrumentationHandler.RecorderVariableName, MakeInitRecorderExpression(), RoslynBuilder.VariableDeclarationType.InferredType); var stackDebugInstrumentation = InstrumentForInEditorDebugging.BuildLastCallFrameExpression(0, IterationContext.Query.Guid, IdentifierName(k_RecorderWriterName)); innerLambdaBody = innerLambdaBody.WithStatements(innerLambdaBody.Statements.Insert(0, recorderDeclaration).Insert(1, stackDebugInstrumentation)); } var parenthesizedLambdaExpressionSyntax = ParenthesizedLambdaExpression( innerLambdaBody) .WithParameterList( ParameterList().AddParameters(parameters.ToArray())); var updateCall = ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, baseQuery, IdentifierName("ForEach"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( parenthesizedLambdaExpressionSyntax))))); if (IterationContext.UpdateMode == UpdateMode.OnEnd) { var addMissingStateComponents = ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("EntityManager"), IdentifierName("AddComponent"))) .WithArgumentList( ArgumentList( SeparatedList( new[] { Argument( IdentifierName(RootContext.GetQueryAddStateName(IterationContext.GroupName))), Argument( TypeOfExpression( IdentifierName(stateComponentName))) })))); var foreachClearProcessed = ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Entities"), IdentifierName("ForEach"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( ParenthesizedLambdaExpression( MakeProcessed(false)) .WithParameterList( ParameterList( SingletonSeparatedList( Parameter( Identifier(stateComponentParameterName)) .WithModifiers( TokenList( Token(SyntaxKind.RefKeyword))) .WithType( IdentifierName(stateComponentName)))))))))); var foreachQueryMarkProcessed = ExpressionStatement( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Entities"), IdentifierName("With"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( IdentifierName(IterationContext.GroupName))))), IdentifierName("ForEach"))) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument( ParenthesizedLambdaExpression(MakeProcessed(true)) .WithParameterList( ParameterList( SeparatedList(parameters)))))))); return(block.AddStatements( addMissingStateComponents, foreachClearProcessed, foreachQueryMarkProcessed, updateCall)); } return(block.AddStatements(updateCall)); ParameterSyntax MakeLambdaParameter(string componentDataName, TypeSyntax typeSyntax, bool isGameObjectComponent, bool isSharedComponent) { var modifiers = new List <SyntaxToken>(); if (!isSharedComponent) { modifiers.Add(Token(SyntaxKind.RefKeyword)); } var parameterSyntax = Parameter( Identifier(componentDataName)) .WithType( typeSyntax); if (!isGameObjectComponent) { parameterSyntax = parameterSyntax .WithModifiers(TokenList(modifiers)); } return(parameterSyntax); } AssignmentExpressionSyntax MakeProcessed(bool processed) { return(AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, IdentifierName(stateComponentParameterName), IdentifierName("Processed")), LiteralExpression(processed ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression))); } }
public void Apply(ref Microsoft.CodeAnalysis.SyntaxTree syntaxTree, CompilationOptions options) { syntaxTree = new InstrumentForInEditorDebugging().Visit(syntaxTree.GetRoot()).SyntaxTree; // TODO handle exceptions again: syntaxTree = new ExceptionHandlingInjection().Visit(syntaxTree.GetRoot()).SyntaxTree; }