internal static SyntaxNode SchedulingInvocationFor(LambdaJobDescription description) { string ExecuteMethodArgs() { var argStrings = description.VariablesCaptured.Where(variable => !variable.IsThis) .Select(variable => (description.ScheduleMode == ScheduleMode.Run && variable.IsWritable) ? $"ref {variable.OriginalVariableName}" : variable.OriginalVariableName).ToList(); if (description.DependencyArgument != null) { argStrings.Add($@"{description.DependencyArgument.ToString()}"); } if (description.WithFilter_EntityArray != null) { argStrings.Add($@"{description.WithFilter_EntityArray.ToString()}"); } foreach (var argumentSyntax in description.WithSharedComponentFilterArgumentSyntaxes) { if (argumentSyntax.Expression is IdentifierNameSyntax && description.VariablesCaptured.All(variable => variable.OriginalVariableName != argumentSyntax.ToString())) { argStrings.Add(argumentSyntax.ToString()); } } return(argStrings.Distinct().SeparateByComma()); } var template = $@"{description.ExecuteInSystemMethodName}{GenericArguments(description)}({ExecuteMethodArgs()}));"; return(SyntaxFactory.ParseStatement(template).DescendantNodes().OfType <InvocationExpressionSyntax>().FirstOrDefault()); }
static string RunWithoutJobSystemMethod(LambdaJobDescription description) { if (description.LambdaJobKind == LambdaJobKind.Entities) { var type = description.NeedsEntityInQueryIndex ? "Unity.Entities.JobEntityBatchIndexExtensions" : "Unity.Entities.JobEntityBatchExtensions"; if (description.WithFilter_EntityArray != null) { return($@" {BurstCompileAttribute(description)} public static unsafe void RunWithoutJobSystem(EntityQuery* query, Entity* limitToEntityArray, int limitToEntityArrayLength, void* jobPtr) {{ {type}.RunWithoutJobsInternal(ref Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef<{description.JobStructName}>(jobPtr), ref *query, limitToEntityArray, limitToEntityArrayLength); }}"); } else { return($@" {BurstCompileAttribute(description)} public static unsafe void RunWithoutJobSystem(ArchetypeChunkIterator* archetypeChunkIterator, void* jobPtr) {{ {type}.RunWithoutJobsInternal(ref Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef<{description.JobStructName}>(jobPtr), ref *archetypeChunkIterator); }}"); } } else { return($@" {BurstCompileAttribute(description)} public static unsafe void RunWithoutJobSystem(void* jobPtr) {{ Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef<{description.JobStructName}>(jobPtr).Execute(); }}"); } }
internal static ClassDeclarationSyntax GenericEntityQueryWrapperFor(LambdaJobDescription description) { // TODO: Need to handle multiple generic parameters var template = $@" public static class EntityQueryWrapper{description.Name}<T> {GenericParameterConstraints(description)} {{ public static bool _created = false; public static EntityQuery {description.QueryName}; static EntityQueryWrapper{description.Name}() {{ _created = false; }} public static EntityQuery GetQuery(SystemBase system) {{ if (!_created) {{ {description.QueryName} = system.{EntityQuerySetupStatementFor(description)} _created = true; }} return {description.QueryName}; }} }}"; return((ClassDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(template)); }
internal static MethodDeclarationSyntax LambdaBodyMethodFor(LambdaJobDescription description) { var template = $@"void {description.LambdaBodyMethodName}({description.LambdaParameters.Select( param => param.LambdaBodyMethodParameter(description.UsesBurst)).SeparateByComma()}) {description.RewrittenLambdaBody.ToString()}"; return((MethodDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(template)); }
static string GenericParameterConstraints(LambdaJobDescription description) { string ParamConstraintFor(ITypeSymbol symbol) => $@"{symbol.Name}"; string GenericParameterFor(LambdaParamDescription_Generic param) => $@" where T : unmanaged, {param.Constraints.Select(constraint => ParamConstraintFor(constraint)).SeparateByComma()}"; return(description.LambdaParameters.OfType <LambdaParamDescription_Generic>() .Select(GenericParameterFor).SeparateByComma()); }
static string SharedComponentFilterInvocations(LambdaJobDescription description) { var sb = new StringBuilder(); foreach (var argumentSyntax in description.WithSharedComponentFilterArgumentSyntaxes) { sb.AppendLine($@"{description.QueryName}.SetSharedComponentFilter({argumentSyntax});"); } return(sb.ToString()); }
static IEnumerable <string> DistinctQueryTypesFor(LambdaJobDescription description) { var readOnlyTypeNames = new HashSet <string>(); var readWriteTypeNames = new HashSet <string>(); void AddQueryType(ITypeSymbol queryType, bool isReadOnly) { if (queryType != null) { if (!isReadOnly) { readOnlyTypeNames.Remove(queryType.ToFullName()); readWriteTypeNames.Add(queryType.ToFullName()); } else { if (!readWriteTypeNames.Contains(queryType.ToFullName()) && !readOnlyTypeNames.Contains(queryType.ToFullName())) { readOnlyTypeNames.Add(queryType.ToFullName()); } } } } foreach (var param in description.LambdaParameters) { var queryType = param.QueryType(); if (queryType != null) { AddQueryType(queryType, param.QueryTypeIsReadOnly()); } } foreach (var allComponentType in description.WithAllTypes) { AddQueryType(allComponentType, true); } foreach (var sharedComponentType in description.WithSharedComponentFilterTypes) { AddQueryType(sharedComponentType, true); } foreach (var changeFilterType in description.WithChangeFilterTypes) { AddQueryType(changeFilterType, true); } return(readOnlyTypeNames.Select(type => $@"ComponentType.ReadOnly<{type}>()").Concat( readWriteTypeNames.Select(type => $@"ComponentType.ReadWrite<{type}>()"))); }
static string EntityQuerySetupStatementFor(LambdaJobDescription description) { return ($@"GetEntityQuery( new EntityQueryDesc {{ All = new ComponentType[] {{ {DistinctQueryTypesFor(description).Distinct().SeparateByCommaAndNewLine()} }}, Any = new ComponentType[] {{ {description.WithAnyTypes.Select(type => type.QueryTypeForType()).Distinct().SeparateByCommaAndNewLine()} }}, None = new ComponentType[] {{ {description.WithNoneTypes.Select(type => type.QueryTypeForType()).Distinct().SeparateByCommaAndNewLine()} }}, Options = {description.EntityQueryOptions.GetFlags().Select(flag => $"EntityQueryOptions.{flag.ToString()}").SeparateByBinaryOr()} }});"); }
static string RunWithoutJobSystemDelegateFields(LambdaJobDescription description) { if (description.LambdaJobKind == LambdaJobKind.Entities) { if (description.WithFilter_EntityArray != null) { return($@" internal static InternalCompilerInterface.JobEntityBatchRunWithoutJobSystemDelegateLimitEntities s_RunWithoutJobSystemDelegateFieldNoBurst; {"internal static InternalCompilerInterface.JobEntityBatchRunWithoutJobSystemDelegateLimitEntities s_RunWithoutJobSystemDelegateFieldBurst;".EmitIfTrue(description.UsesBurst)}"); } else { return($@" internal static InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldNoBurst; {"internal static InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldBurst;".EmitIfTrue(description.UsesBurst)}"); } } else { return($@" internal static InternalCompilerInterface.JobRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldNoBurst; {"internal static InternalCompilerInterface.JobRunWithoutJobSystemDelegate s_RunWithoutJobSystemDelegateFieldBurst;".EmitIfTrue(description.UsesBurst)}"); } }
internal static MethodDeclarationSyntax ExecuteMethodFor(LambdaJobDescription description) { string ExecuteMethodParams() { string ParamsForCapturedVariable(LambdaCapturedVariableDescription variable) { return((description.ScheduleMode == ScheduleMode.Run && variable.IsWritable) ? $@"ref {variable.Symbol.GetSymbolTypeName()} {variable.Symbol.Name}" : $@"{variable.Symbol.GetSymbolTypeName()} {variable.Symbol.Name}"); } var paramStrings = new List <string>(); paramStrings.AddRange(description.VariablesCaptured.Where(variable => !variable.IsThis).Select(ParamsForCapturedVariable)); if (description.DependencyArgument != null) { paramStrings.Add($@"Unity.Jobs.JobHandle __inputDependency"); } if (description.WithFilter_EntityArray != null) { paramStrings.Add($@"Unity.Collections.NativeArray<Entity> __entityArray"); } foreach (var argumentSyntax in description.WithSharedComponentFilterArgumentSyntaxes) { if (argumentSyntax.Expression is IdentifierNameSyntax argumentIdentifier && description.VariablesCaptured.All(variable => variable.OriginalVariableName != argumentSyntax.ToString())) { var argumentSymbol = description.Model.GetSymbolInfo(argumentIdentifier); paramStrings.Add($@"{argumentSymbol.Symbol.GetSymbolTypeName()} {argumentSyntax.ToString()}"); } } return(paramStrings.Distinct().SeparateByComma()); } string JobStructFieldAssignment() { var allAssignments = new List <string>(); allAssignments.AddRange(description.VariablesCaptured.Select(variable => $@"{variable.VariableFieldName} = {variable.OriginalVariableName}")); if (!description.WithStructuralChanges) { allAssignments.AddRange(description.LambdaParameters.Select(param => param.TypeHandleAssign())); } allAssignments.AddRange(description.AdditionalFields.Select(field => field.JobStructAssign())); return(allAssignments.SeparateByCommaAndNewLine()); } string ScheduleJobInvocation() { switch (description.ScheduleMode) { case ScheduleMode.Run: { if (description.UsesBurst) { return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} var __functionPointer = Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? {description.JobStructName}.s_RunWithoutJobSystemDelegateFieldBurst : {description.JobStructName}.s_RunWithoutJobSystemDelegateFieldNoBurst; var __jobPtr = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AddressOf(ref __job); Unity.Entities.InternalCompilerInterface.UnsafeRunIJob(__jobPtr, __functionPointer);"); } else { return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} __job.Execute();"); } } case ScheduleMode.Schedule: { if (description.DependencyArgument != null) { return($@"var __jobHandle = Unity.Jobs.IJobExtensions.Schedule(__job, __inputDependency);"); } else { return($@"Dependency = Unity.Jobs.IJobExtensions.Schedule(__job, Dependency);"); } } } throw new InvalidOperationException("Can't create ScheduleJobInvocation for invalid lambda description"); } string ScheduleEntitiesInvocation() { string EntityQueryParameter(ArgumentSyntax entityArray, bool parallel) { if (description.HasGenericParameters) { if (entityArray != null) { return($"EntityQueryWrapper{description.Name}<T>.GetQuery(this), __entityArray"); } else if (parallel) { return($"EntityQueryWrapper{description.Name}<T>.GetQuery(this), 1"); } else { return($"EntityQueryWrapper{description.Name}<T>.GetQuery(this)"); } } else { if (entityArray != null) { return($"{description.QueryName}, __entityArray"); } else if (parallel) { return($"{description.QueryName}, 1"); } else { return($"{description.QueryName}"); } } } string JobEntityBatchExtensionType() => description.NeedsEntityInQueryIndex ? "Unity.Entities.JobEntityBatchIndexExtensions": "Unity.Entities.JobEntityBatchExtensions"; switch (description.ScheduleMode) { case ScheduleMode.Run: if (description.WithStructuralChanges) { var entityArray = ", __entityArray".EmitIfTrue(description.WithFilter_EntityArray != null); return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} __job.RunWithStructuralChange({description.QueryName}{entityArray});"); } else if (description.UsesBurst) { var entityArray = ", __entityArray".EmitIfTrue(description.WithFilter_EntityArray != null); return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} var __functionPointer = Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? {description.JobStructName}.s_RunWithoutJobSystemDelegateFieldBurst : {description.JobStructName}.s_RunWithoutJobSystemDelegateFieldNoBurst; var __jobPtr = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AddressOf(ref __job); Unity.Entities.InternalCompilerInterface.UnsafeRunJobEntityBatch(__jobPtr, {description.QueryName}{entityArray}, __functionPointer);"); } else { if (description.WithFilter_EntityArray != null) { return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} {JobEntityBatchExtensionType()}.RunWithoutJobs(ref __job, {description.QueryName}, __entityArray);"); } else { return($@" {"CompleteDependency();".EmitIfTrue(description.ContainingSystemType == ContainingSystemType.SystemBase)} {JobEntityBatchExtensionType()}.RunWithoutJobs(ref __job, {description.QueryName});"); } } // Special case where we treat Schedule as ScheduleParallel in JobComponentSystem case ScheduleMode.Schedule when description.ContainingSystemType == ContainingSystemType.JobComponentSystem: { return ($@"var __jobHandle = {JobEntityBatchExtensionType()}.ScheduleParallel(__job, {EntityQueryParameter(description.WithFilter_EntityArray, true)}, __inputDependency);"); } case ScheduleMode.Schedule: { if (description.DependencyArgument != null) { return($@"var __jobHandle = {JobEntityBatchExtensionType()}.Schedule(__job, {EntityQueryParameter(description.WithFilter_EntityArray, false)}, __inputDependency);"); } else { return($@"Dependency = {JobEntityBatchExtensionType()}.Schedule(__job, {EntityQueryParameter(description.WithFilter_EntityArray, false)}, Dependency);"); } } case ScheduleMode.ScheduleParallel: { if (description.DependencyArgument != null) { return($@"var __jobHandle = {JobEntityBatchExtensionType()}.ScheduleParallel(__job, {EntityQueryParameter(description.WithFilter_EntityArray, true)}, __inputDependency);"); } else { return($@"Dependency = {JobEntityBatchExtensionType()}.ScheduleParallel(__job, {EntityQueryParameter(description.WithFilter_EntityArray, true)}, Dependency);"); } } } throw new InvalidOperationException("Can't create ScheduleJobInvocation for invalid lambda description"); } string ScheduleInvocation() { return(description.LambdaJobKind == LambdaJobKind.Entities ? ScheduleEntitiesInvocation() : ScheduleJobInvocation()); } string WriteBackCapturedVariablesAssignments() { if (description.ScheduleMode == ScheduleMode.Run) { var writeBackStatements = new List <string>(); writeBackStatements.AddRange(description.VariablesCaptured.Where(variable => !variable.IsThis && variable.IsWritable) .Select(variable => $@"{variable.OriginalVariableName} = __job.{variable.VariableFieldName};")); return(writeBackStatements.SeparateByNewLine()); } return(string.Empty); } string DisposeOnCompletionInvocation() { if (!description.DisposeOnJobCompletionVariables.Any()) { return(string.Empty); } if (description.ScheduleMode == ScheduleMode.Run) { return($@"__job.DisposeOnCompletion();"); } else if (description.DependencyArgument != null) { return($@"__jobHandle = __job.DisposeOnCompletion(__jobHandle);"); } else { return($@"Dependency = __job.DisposeOnCompletion(Dependency);"); } } string ReturnType() => (description.DependencyArgument != null) ? "Unity.Jobs.JobHandle" : "void"; var template = $@" {ReturnType()} {description.ExecuteInSystemMethodName}{GenericArguments(description)}({ExecuteMethodParams()}){GenericParameterConstraints(description)} {{ var __job = new {description.JobStructName}{GenericArguments(description)} {{ {JobStructFieldAssignment()} }}; {SharedComponentFilterInvocations(description)} {ScheduleInvocation()} {DisposeOnCompletionInvocation()} {WriteBackCapturedVariablesAssignments()} {"return __jobHandle;".EmitIfTrue(description.DependencyArgument != null)} }}"; return((MethodDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(template)); }
static string NoAliasAttribute(LambdaJobDescription description) => description.UsesBurst ? $@"[Unity.Burst.NoAlias]" : string.Empty;
internal static FieldDeclarationSyntax EntityQueryFieldFor(LambdaJobDescription description) { var template = $@"EntityQuery {description.QueryName};"; return((FieldDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(template)); }
// var componentArray1 = (ComponentType1*)chunk.GetNativeArray(_componentTypeAccessor1).GetUnsafePtr(); static string GetChunkNativeArrays(LambdaJobDescription description) => description.LambdaParameters.Select(param => param.GetNativeArray()).SeparateByNewLine();
static string BurstCompileAttribute(LambdaJobDescription description) => description.UsesBurst ? $@" [Unity.Burst.BurstCompile(FloatMode=Unity.Burst.FloatMode.{description.FloatMode.ToString()}, FloatPrecision=Unity.Burst.FloatPrecision.{description.FloatPrecision.ToString()}, CompileSynchronously={description.BurstSynchronousCompilation.ToString().ToLower()})]" : string.Empty;
internal static StructDeclarationSyntax JobStructFor(LambdaJobDescription description) { // public [ReadOnly] CapturedFieldType capturedFieldName; // Need to also declare these for variables used by local methods string CapturedVariableFields() { string FieldForCapturedVariable(LambdaCapturedVariableDescription variable) => $@"{variable.Attributes.JoinAttributes()}public {variable.Symbol.GetSymbolTypeName()} {variable.VariableFieldName};"; return(description.VariablesCaptured.Concat(description.VariablesCapturedOnlyByLocals).Select(FieldForCapturedVariable).SeparateByNewLine()); } // public ComponentTypeHandle<ComponentType> _rotationTypeAccessor; string TypeHandleFields() => description.LambdaParameters.Select(param => param.TypeHandleField()).SeparateByNewLine(); // public ComponentDataFromEntity<ComponentType> _rotationDataFromEntity; string AdditionalDataFromEntityFields() => description.AdditionalFields.Select(dataFromEntityField => dataFromEntityField.ToFieldDeclaration().ToString()).SeparateByNewLine(); // void OriginalLambdaBody(ref ComponentType1 component1, in ComponentType2 component2) {}"; string OriginalLambdaBody() => $@" {"[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]".EmitIfTrue(description.UsesBurst)} void OriginalLambdaBody({description.LambdaParameters.Select(param => param.LambdaBodyMethodParameter(description.UsesBurst)).SeparateByComma()}) {{}} {GeneratedLineTriviaToGeneratedSource()}"; // OriginalLambdaBody(ref Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef<ComponentType1>(componentArray1 + i), *(componentArray2 + i)); string PerformLambda() { var result = string.Empty; result += description.LambdaParameters.Select(param => param.LambdaBodyParameterSetup()).SeparateBySemicolonAndNewLine(); if (description.WithStructuralChangesAndLambdaBodyInSystem) { result += $@"__this.{description.LambdaBodyMethodName}({description.LambdaParameters.Select(param => param.StructuralChanges_LambdaBodyParameter()).SeparateByComma()});"; } else if (description.WithStructuralChanges) { result += $@"OriginalLambdaBody({description.LambdaParameters.Select(param => param.StructuralChanges_LambdaBodyParameter()).SeparateByComma()});"; } else { result += $@"OriginalLambdaBody({description.LambdaParameters.Select(param => param.LambdaBodyParameter()).SeparateByComma()});"; } return(result); } string MethodsForLocalFunctions() => description.MethodsForLocalFunctions.Select(method => method.ToString()).SeparateByNewLine(); string ExecuteMethodForJob() => $@" public unsafe void Execute() {{ {PerformLambda()} }}"; string ExecuteMethodDefault() => $@" public unsafe void Execute(ArchetypeChunk chunk, int batchIndex) {{ {GetChunkNativeArrays(description)} int count = chunk.Count; for (int entityIndex = 0; entityIndex != count; entityIndex++) {{ {PerformLambda()} }} }}"; string ExecuteMethodWithEntityInQueryIndex() => $@" public unsafe void Execute(ArchetypeChunk chunk, int batchIndex, int indexOfFirstEntityInQuery) {{ {GetChunkNativeArrays(description)} int count = chunk.Count; for (int entityIndex = 0; entityIndex != count; entityIndex++) {{ int entityInQueryIndex = indexOfFirstEntityInQuery + entityIndex; {PerformLambda()} }} }}"; string ExecuteMethodForStructuralChanges() => $@" public unsafe void RunWithStructuralChange(EntityQuery query) {{ {GeneratedLineTriviaToGeneratedSource()} var mask = __this.EntityManager.GetEntityQueryMask(query); Unity.Entities.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult); {StructuralChanges_GetTypeIndices(description)} try {{ int entityCount = gatherEntitiesResult.EntityCount; for (int entityIndex = 0; entityIndex != entityCount; entityIndex++) {{ var entity = gatherEntitiesResult.EntityBuffer[entityIndex]; if (mask.Matches(entity)) {{ {StructuralChanges_ReadLambdaParams(description)} {PerformLambda()} {StructuralChanges_WriteBackLambdaParams(description)} }} }} }} finally {{ Unity.Entities.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult); }} }}"; string ExecuteMethodForStructuralChangesWithEntities() => $@" public unsafe void RunWithStructuralChange(EntityQuery query, NativeArray<Entity> withEntities) {{ {GeneratedLineTriviaToGeneratedSource()} var mask = __this.EntityManager.GetEntityQueryMask(query); {StructuralChanges_GetTypeIndices(description)} int entityCount = withEntities.Length; for (int entityIndex = 0; entityIndex != entityCount; entityIndex++) {{ var entity = withEntities[entityIndex]; if (mask.Matches(entity)) {{ {StructuralChanges_ReadLambdaParams(description)} {PerformLambda()} {StructuralChanges_WriteBackLambdaParams(description)} }} }} }}"; string ExecuteMethod() { if (description.LambdaJobKind == LambdaJobKind.Job) { return(ExecuteMethodForJob()); } else if (description.WithStructuralChanges && description.WithFilter_EntityArray == null) { return(ExecuteMethodForStructuralChanges()); } else if (description.WithStructuralChanges && description.WithFilter_EntityArray != null) { return(ExecuteMethodForStructuralChangesWithEntities()); } else if (description.NeedsEntityInQueryIndex) { return(ExecuteMethodWithEntityInQueryIndex()); } else { return(ExecuteMethodDefault()); } } string DisposeOnCompletionMethod() { if (!description.DisposeOnJobCompletionVariables.Any()) { return(string.Empty); } var allDisposableFieldsAndChildren = new List <string>(); foreach (var variable in description.DisposeOnJobCompletionVariables) { allDisposableFieldsAndChildren.AddRange(variable.NamesOfAllDisposableMembersIncludingOurselves()); } if (description.ScheduleMode == ScheduleMode.Run) { return($@" public void DisposeOnCompletion() {{ {allDisposableFieldsAndChildren.Select(disposable => $"{disposable}.Dispose();").SeparateByNewLine()} }}"); } else { return($@" public Unity.Jobs.JobHandle DisposeOnCompletion(Unity.Jobs.JobHandle jobHandle) {{ {allDisposableFieldsAndChildren.Select(disposable => $"jobHandle = {disposable}.Dispose(jobHandle);").SeparateByNewLine()} return jobHandle; }}"); } } string JobInterface() { if (description.LambdaJobKind == LambdaJobKind.Job) { return(" : Unity.Jobs.IJob"); } else { if (!description.WithStructuralChanges) { return(description.NeedsEntityInQueryIndex ? " : Unity.Entities.IJobEntityBatchWithIndex" : " : Unity.Entities.IJobEntityBatch"); } } return(string.Empty); } var template = $@" {GeneratedLineTriviaToGeneratedSource()} {NoAliasAttribute(description)} {BurstCompileAttribute(description)} struct {description.JobStructName}{GenericArguments(description)} {JobInterface()} {GenericParameterConstraints(description)} {{ {RunWithoutJobSystemDelegateFields(description).EmitIfTrue(description.NeedsJobDelegateFields)} {CapturedVariableFields()} {TypeHandleFields().EmitIfTrue(!description.WithStructuralChanges)} {AdditionalDataFromEntityFields()} {MethodsForLocalFunctions()} {(!description.WithStructuralChangesAndLambdaBodyInSystem ? OriginalLambdaBody() : string.Empty)} {ExecuteMethod()} {DisposeOnCompletionMethod()} {RunWithoutJobSystemMethod(description).EmitIfTrue(description.NeedsJobDelegateFields)} }}"; var jobStructDeclaration = (StructDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(template); // Find lambda body in job struct template and replace rewritten lambda body into method if (!description.WithStructuralChangesAndLambdaBodyInSystem) { var templateLambdaMethodBody = jobStructDeclaration.DescendantNodes().OfType <MethodDeclarationSyntax>().First( method => method.Identifier.ValueText == "OriginalLambdaBody").DescendantNodes().OfType <BlockSyntax>().First(); jobStructDeclaration = jobStructDeclaration.ReplaceNode(templateLambdaMethodBody, description.RewrittenLambdaBody.WithoutPreprocessorTrivia()); } return(jobStructDeclaration); }
Rewrite(LambdaJobDescription description) { SemanticModel model = description.Model; SyntaxNode originalLambdaExpression = description.OriginalLambdaExpression; List <LambdaCapturedVariableDescription> variablesCapturedOnlyByLocals = description.VariablesCapturedOnlyByLocals; var lambdaBodyRewriter = new LambdaBodyRewriter(); // Find all locations where we are accessing a member on the declaring SystemBase // and change them to access through "__this" instead. // This also annotates the changed nodes so that we can find them later for patching (and get their original symbols). var rewrittenLambdaBodyData = LambdaBodySyntaxReplacer.Rewrite(model, originalLambdaExpression); var rewrittenLambdaExpression = rewrittenLambdaBodyData.rewrittenLambdaExpression; // Go through all changed nodes and check to see if they are a component access method that we need to patch (GetComponent/SetComponent/etc) // Only need to do this if we are not doing structural changes (in which case we can't as structural changes will invalidate) if (!description.WithStructuralChanges) { foreach (var originalNode in rewrittenLambdaBodyData.thisAccessNodesNeedingReplacement) { var originalInvocation = originalNode.Ancestors().OfType <InvocationExpressionSyntax>().First(); var currentNode = rewrittenLambdaExpression.GetCurrentNode(originalNode); var currentMemberAccessExpression = currentNode.Ancestors().OfType <MemberAccessExpressionSyntax>().First(); var currentNodeInvocationExpression = currentNode.Ancestors().OfType <InvocationExpressionSyntax>().FirstOrDefault(); if (currentNodeInvocationExpression == null) { continue; } var replacementMemberAccessExpression = lambdaBodyRewriter.GenerateReplacementMemberAccessExpression(originalInvocation, currentMemberAccessExpression, model); if (replacementMemberAccessExpression != null) { rewrittenLambdaExpression = rewrittenLambdaExpression.ReplaceNode(currentNodeInvocationExpression, replacementMemberAccessExpression); } } } // Go through all local declaration nodes and replace them with assignment nodes (or remove) if they are now captured variables that live in job struct // This is needed for variables captured for local methods foreach (var localDeclarationSyntax in rewrittenLambdaExpression.DescendantNodes().OfType <LocalDeclarationStatementSyntax>()) { var variableDeclaration = localDeclarationSyntax.DescendantNodes().OfType <VariableDeclarationSyntax>().FirstOrDefault(); if (variableDeclaration != null && variablesCapturedOnlyByLocals.Any(variable => variable.OriginalVariableName == variableDeclaration.Variables.First().Identifier.Text)) { if (variableDeclaration.DescendantTokens().Any(token => token.Kind() == SyntaxKind.EqualsToken)) { var variableIdentifier = variableDeclaration.Variables.First().Identifier; var nodeAfter = variableDeclaration.NodeAfter(node => node.Kind() == SyntaxKind.EqualsToken); rewrittenLambdaExpression = rewrittenLambdaExpression.ReplaceNode(localDeclarationSyntax, SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.IdentifierName(variableIdentifier.Text), (ExpressionSyntax)nodeAfter))); } else { rewrittenLambdaExpression = rewrittenLambdaExpression.RemoveNode(localDeclarationSyntax, SyntaxRemoveOptions.KeepExteriorTrivia); } } } // Go through all local function statements and omit them as method declarations on the job struct // (local methods accessing fields on this are not allowed in C#) // https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxNGAfAAgJgIwCwAUFgMwAEu5AwuQN7HlOUVYAs5AsgBQCU9jZgF9BTUeVgIArgGMY5AKIA7GAEsYAT3oiizCTGlyaAezAAHY0rgqAKhrNwAEhCUATADZwAPDYB828UlZeWpTCysVABEIGAgAMQRTZTVNH386HT0gowUZKBs4WGjY5PUtcQZdPWYyRRUy8gA3CHcpODwAbnFM5mz5XPzCmGKIASrqlkU8gqKYiG5VFSaWtv4M7vFN8fJF+AQAMwgZOHIASQApY2BqAAspJQBrAO2+8gAZCDBgVwhL4AB9AAM/z+5BAZz+t3uDwq4j0tVC5ks1hgdgczjcni8AxmwzmpU0/n+MFccDRThcHjgXW28IoiPCKJGCSS9VSOKGIyJ/w5s1i/xZYAJGhpEzEtJqHAA8ghVABzRYtD5fH4AIWMrg03GF5BRZSQOyUU0GfIgOpJcF4cOYlTF1V25AA4nAYMKAGorODatlaS3Wia2u1irAAdnI/x5005cwFiSFPoA2nAALoAOmarWp/uqOmz8I4AGUXe7Pd6Ur6DQ6M6s8zbaxMI7y8fzBcKk8nyABechWADuxtxI241ctoqDTB64/r5CLrp9Hsz3D1mgNzrn5YXbW4FvTnrwvF4Y4muYlTAA9AAqbO1e5QCD7E7sRQADzgMik8G4AEEEDIbi77DgKFHnIP9oUrJYwMeU43DgZ8IPkfZVAQWBhRg0lnytU8xnHJhmgQXUfQABQMLtyDLMoL14KCHlTNcADkYlURo4B/BAIC1f5lw0ckMSpXg6JdABVJQ7wfAAlOAIFcKUlHcDQSIQPgjyDfDyAtL8ZGOKAoGMAju21KNTWFKiaMEmBGLUFi2I47hiVJXjKU8AS1xEsS4Ek6TZPkxTlOnB1uNCe55G7MygpUFS7X2PTyIdVQyMBDodnIABCbtAuMYKktUABqHKsNwnDCuYGV5UVdxlW+CB1U1bgLyXYjSJynZeANeqNK0wpdII5rVAPSKxUnHNswvM8NiIIQgA=== var localFunctions = rewrittenLambdaExpression.DescendantNodes().OfType <LocalFunctionStatementSyntax>(); rewrittenLambdaExpression = rewrittenLambdaExpression.RemoveNodes(localFunctions, SyntaxRemoveOptions.KeepNoTrivia); var methodsForLocalFunctions = new List <MethodDeclarationSyntax>(); foreach (var localFunction in localFunctions) { methodsForLocalFunctions.Add((MethodDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(localFunction.ToString())); } return(rewrittenLambdaExpression, lambdaBodyRewriter.DataFromEntityFields.Values.ToList(), methodsForLocalFunctions); }
// TODO: Fix to allow for more than one static string GenericArguments(LambdaJobDescription description) => description.HasGenericParameters ? "<T>" : "";
static string GenericParameterConstraints(LambdaJobDescription description) => string.Empty;
static string GenericArguments(LambdaJobDescription description) => string.Empty;
// WriteComponentData<Rotation>(__this.EntityManager, entity, rotationTypeIndex, ref rotation, ref T originalrotation);"; static string StructuralChanges_WriteBackLambdaParams(LambdaJobDescription description) => description.LambdaParameters.Select(param => param.StructuralChanges_WriteBackLambdaParam()).SeparateByNewLine();
public void Execute(GeneratorExecutionContext context) { try { if (context.Compilation.ReferencedAssemblyNames.All(n => n.Name != "Unity.Entities") || context.Compilation.Assembly.Name.Contains("CodeGen.Tests")) { return; } SourceGenHelpers.LogInfo($"Source generating assembly {context.Compilation.Assembly.Name} for lambda jobs..."); var stopwatch = Stopwatch.StartNew();; //context.WaitForDebugger("HelloCube"); // Setup project path for logging and emitting debug source // This isn't fantastic but I haven't come up with a better way to get the project path since we might be running out of process if (context.AdditionalFiles.Any()) { SourceGenHelpers.SetProjectPath(context.AdditionalFiles[0].Path); } // Do initial filter for early out var entitiesSyntaxReceiver = (EntitiesSyntaxReceiver)context.SyntaxReceiver; var lambdaJobCandidates = FilterCandidates(context, entitiesSyntaxReceiver.EntitiesGetterCandidates.Concat(entitiesSyntaxReceiver.JobGetterCandidates)); if (!lambdaJobCandidates.Any()) { return; } // Create map from SyntaxTrees to candidates var syntaxTreeToCandidates = lambdaJobCandidates.GroupBy(c => c.SyntaxTree).ToDictionary(group => group.Key, group => group.ToList()); // Outer loop - iterate over syntax tree foreach (var treeKVP in syntaxTreeToCandidates) { var syntaxTree = treeKVP.Key; var treeCandidates = treeKVP.Value; try { // Build up list of job descriptions inside of containing class declarations var classDeclarationToDescriptions = new Dictionary <ClassDeclarationSyntax, List <LambdaJobDescription> >(); var jobIndex = 0; foreach (var candidate in treeCandidates) { var declaringType = candidate.Ancestors().OfType <ClassDeclarationSyntax>().FirstOrDefault(); var containingMethod = candidate.Ancestors().OfType <MethodDeclarationSyntax>().FirstOrDefault(); if (declaringType == null || containingMethod == null) { continue; } SourceGenHelpers.LogInfo($"Parsing LambdaJobDescription in {declaringType.Identifier}.{containingMethod.Identifier}"); var jobDescription = LambdaJobDescription.From(candidate, context, jobIndex++); if (jobDescription == null) { continue; } if (classDeclarationToDescriptions.ContainsKey(declaringType)) { classDeclarationToDescriptions[declaringType].Add(jobDescription); } else { classDeclarationToDescriptions[declaringType] = new List <LambdaJobDescription>() { jobDescription } }; } // Inner loop - iterate through class descriptions with lambda jobs and generate new class declaration nodes var originalToGeneratedNode = new Dictionary <SyntaxNode, MemberDeclarationSyntax>(); foreach (var kvp in classDeclarationToDescriptions) { var classDeclaration = kvp.Key; var descriptionsInClass = kvp.Value; SourceGenHelpers.LogInfo($"Generating code for system type: {classDeclaration.Identifier}"); var newClassDeclaration = GenerateNewClassDeclarationForDescriptions(classDeclaration, descriptionsInClass); //SourceGenHelpers.LogInfo(newClassDeclaration.ToString()); originalToGeneratedNode[classDeclaration] = newClassDeclaration; } // recurse and create nodes down to our created system nodes var rootNodesWithGenerated = new List <MemberDeclarationSyntax>(); foreach (var child in syntaxTree.GetRoot().ChildNodes()) { if (child is NamespaceDeclarationSyntax || child is ClassDeclarationSyntax) { var generatedRootNode = ConstructTreeWithGeneratedNodes(child, originalToGeneratedNode); if (generatedRootNode != null) { rootNodesWithGenerated.Add(generatedRootNode); } } } if (rootNodesWithGenerated.Any()) { OutputGeneratedSyntaxTreeNodes(context, syntaxTree, rootNodesWithGenerated); } } catch (Exception exception) { context.LogError("SGICE001", "LambdaJobs", exception.ToString(), syntaxTree.GetRoot().GetLocation()); } } stopwatch.Stop(); SourceGenHelpers.LogInfo($"TIME : LambdaJobs : {context.Compilation.Assembly.Name} : {stopwatch.ElapsedMilliseconds}ms"); } catch (Exception exception) { context.LogError("SGICE001", "LambdaJobs", exception.ToString(), context.Compilation.SyntaxTrees.First().GetRoot().GetLocation()); } }
//var rotationTypeIndex = TypeManager.GetTypeIndex<Rotation>(); static string StructuralChanges_GetTypeIndices(LambdaJobDescription description) => description.LambdaParameters.Select(param => param.StructuralChanges_GetTypeIndex()).SeparateByNewLine();