private List <ClassDefinition> BuildClassDefinitions() { string[] udonSharpDataAssets = AssetDatabase.FindAssets($"t:{typeof(UdonSharpProgramAsset).Name}"); List <UdonSharpProgramAsset> udonSharpPrograms = new List <UdonSharpProgramAsset>(); foreach (string dataGuid in udonSharpDataAssets) { udonSharpPrograms.Add(AssetDatabase.LoadAssetAtPath <UdonSharpProgramAsset>(AssetDatabase.GUIDToAssetPath(dataGuid))); } List <ClassDefinition> classDefinitions = new List <ClassDefinition>(); foreach (UdonSharpProgramAsset udonSharpProgram in udonSharpPrograms) { if (udonSharpProgram.sourceCsScript == null) { continue; } string sourcePath = AssetDatabase.GetAssetPath(udonSharpProgram.sourceCsScript); string programSource = UdonSharpUtils.ReadFileTextSync(sourcePath); ResolverContext resolver = new ResolverContext(); SymbolTable classSymbols = new SymbolTable(resolver, null); classSymbols.OpenSymbolTable(); LabelTable classLabels = new LabelTable(); SyntaxTree tree = CSharpSyntaxTree.ParseText(programSource); ClassVisitor classVisitor = new ClassVisitor(resolver, classSymbols, classLabels); try { classVisitor.Visit(tree.GetRoot()); } catch (System.Exception e) { UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", sourcePath.Replace("/", "\\"), 0, 0); return(null); } classSymbols.CloseSymbolTable(); classVisitor.classDefinition.classScript = udonSharpProgram.sourceCsScript; classDefinitions.Add(classVisitor.classDefinition); } return(classDefinitions); }
public int Compile(List <ClassDefinition> classDefinitions) { if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } Profiler.BeginSample("Compile Module"); programAsset.compileErrors.Clear(); sourceCode = File.ReadAllText(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); Profiler.BeginSample("Parse AST"); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode); Profiler.EndSample(); int errorCount = 0; string errorString = ""; foreach (Diagnostic diagnostic in tree.GetDiagnostics()) { if (diagnostic.Severity == DiagnosticSeverity.Error) { errorCount++; LinePosition linePosition = diagnostic.Location.GetLineSpan().StartLinePosition; errorString = UdonSharpUtils.LogBuildError($"error {diagnostic.Descriptor.Id}: {diagnostic.GetMessage()}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), linePosition.Line, linePosition.Character); programAsset.compileErrors.Add(errorString); } } if (errorCount > 0) { ErrorCount = errorCount; Profiler.EndSample(); return(errorCount); } Profiler.BeginSample("Visit"); UdonSharpFieldVisitor fieldVisitor = new UdonSharpFieldVisitor(fieldsWithInitializers); fieldVisitor.Visit(tree.GetRoot()); MethodVisitor methodVisitor = new MethodVisitor(resolver, moduleSymbols, moduleLabels); methodVisitor.Visit(tree.GetRoot()); UdonSharpSettings settings = UdonSharpSettings.GetSettings(); ClassDebugInfo debugInfo = null; if (settings == null || settings.buildDebugInfo) { debugInfo = new ClassDebugInfo(sourceCode, settings == null || settings.includeInlineCode); } ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions, debugInfo); try { visitor.Visit(tree.GetRoot()); visitor.VerifyIntegrity(); } catch (System.Exception e) { SyntaxNode currentNode = visitor.visitorContext.currentNode; string logMessage = ""; if (currentNode != null) { FileLinePositionSpan lineSpan = currentNode.GetLocation().GetLineSpan(); logMessage = UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), lineSpan.StartLinePosition.Line, lineSpan.StartLinePosition.Character); } else { logMessage = e.ToString(); Debug.LogException(e); } programAsset.compileErrors.Add(logMessage); errorCount++; } Profiler.EndSample(); if (errorCount == 0) { Profiler.BeginSample("Build assembly"); string dataBlock = BuildHeapDataBlock(); string codeBlock = visitor.GetCompiledUasm(); programAsset.SetUdonAssembly(dataBlock + codeBlock); Profiler.EndSample(); Profiler.BeginSample("Assemble Program"); programAsset.AssembleCsProgram((uint)(moduleSymbols.GetAllUniqueChildSymbols().Count + visitor.GetExternStrCount())); Profiler.EndSample(); programAsset.behaviourIDHeapVarName = visitor.GetIDHeapVarName(); programAsset.fieldDefinitions = visitor.visitorContext.localFieldDefinitions; if (debugInfo != null) { debugInfo.FinalizeDebugInfo(); } programAsset.debugInfo = debugInfo; } Profiler.EndSample(); return(errorCount); }
public int Compile(List <ClassDefinition> classDefinitions) { if (programAsset.sourceCsScript == null) { throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from"); } sourceCode = File.ReadAllText(AssetDatabase.GetAssetPath(programAsset.sourceCsScript)); SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode); int errorCount = 0; foreach (Diagnostic diagnostic in tree.GetDiagnostics()) { if (diagnostic.Severity == DiagnosticSeverity.Error) { errorCount++; LinePosition linePosition = diagnostic.Location.GetLineSpan().StartLinePosition; UdonSharpUtils.LogBuildError($"error {diagnostic.Descriptor.Id}: {diagnostic.GetMessage()}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), linePosition.Line, linePosition.Character); } if (errorCount > 0) { return(errorCount); } } UdonSharpFieldVisitor fieldVisitor = new UdonSharpFieldVisitor(fieldsWithInitializers); fieldVisitor.Visit(tree.GetRoot()); MethodVisitor methodVisitor = new MethodVisitor(resolver, moduleSymbols, moduleLabels); methodVisitor.Visit(tree.GetRoot()); ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions); try { visitor.Visit(tree.GetRoot()); visitor.VerifyIntegrity(); } catch (System.Exception e) { SyntaxNode currentNode = visitor.visitorContext.currentNode; if (currentNode != null) { FileLinePositionSpan lineSpan = currentNode.GetLocation().GetLineSpan(); UdonSharpUtils.LogBuildError($"{e.GetType()}: {e.Message}", AssetDatabase.GetAssetPath(programAsset.sourceCsScript).Replace("/", "\\"), lineSpan.StartLinePosition.Line, lineSpan.StartLinePosition.Character); } else { Debug.LogException(e); } errorCount++; } string dataBlock = BuildHeapDataBlock(); string codeBlock = visitor.GetCompiledUasm(); programAsset.SetUdonAssembly(dataBlock + codeBlock); programAsset.AssembleCsProgram(); programAsset.fieldDefinitions = visitor.visitorContext.localFieldDefinitions; return(errorCount); }
private int RunFieldInitalizers(CompilationModule[] compiledModules) { CompilationModule[] modulesToInitialize = compiledModules.Where(e => e.fieldsWithInitializers.Count > 0).ToArray(); // We don't need to run the costly compilation if the user hasn't defined any fields with initializers if (modulesToInitialize.Length == 0) { return(0); } int initializerErrorCount = 0; SyntaxTree[] initializerTrees = new SyntaxTree[modulesToInitialize.Length]; StringBuilder[] codeStringBuilders = new StringBuilder[modulesToInitialize.Length]; for (int moduleIdx = 0; moduleIdx < modulesToInitialize.Length; ++moduleIdx) { CompilationModule module = modulesToInitialize[moduleIdx]; CodeCompileUnit compileUnit = new CodeCompileUnit(); CodeNamespace ns = new CodeNamespace("FieldInitialzers"); compileUnit.Namespaces.Add(ns); foreach (var resolverUsingNamespace in module.resolver.usingNamespaces) { if (!string.IsNullOrEmpty(resolverUsingNamespace)) { ns.Imports.Add(new CodeNamespaceImport(resolverUsingNamespace)); } } CodeTypeDeclaration _class = new CodeTypeDeclaration($"Initializer{moduleIdx}"); ns.Types.Add(_class); CodeMemberMethod method = new CodeMemberMethod(); _class.Members.Add(method); method.Attributes = MemberAttributes.Public | MemberAttributes.Static; method.ReturnType = new CodeTypeReference(typeof(void)); method.Name = "DoInit"; method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IUdonProgram), "program")); foreach (var fieldDeclarationSyntax in module.fieldsWithInitializers) { var type = fieldDeclarationSyntax.Declaration.Type; int count = 0; bool isConst = fieldDeclarationSyntax.Modifiers.Any(t => t.ToString() == "const"); foreach (var variable in fieldDeclarationSyntax.Declaration.Variables) { FieldDefinition fieldDef = module.compiledClassDefinition?.fieldDefinitions?.Find(e => (e.fieldSymbol.declarationType == SymbolDeclTypeFlags.Private || e.fieldSymbol.declarationType == SymbolDeclTypeFlags.Public) && e.fieldSymbol.symbolOriginalName == variable.Identifier.ToString()); string typeQualifiedName = type.ToString(); if (fieldDef != null) { if (fieldDef.fieldSymbol.symbolCsType.Namespace.Length == 0) { typeQualifiedName = fieldDef.fieldSymbol.symbolCsType.Name; } else { typeQualifiedName = fieldDef.fieldSymbol.symbolCsType.Namespace + "." + fieldDef.fieldSymbol.symbolCsType.Name; } } if (variable.Initializer != null) { string name = variable.Identifier.ToString(); if (isConst) { _class.Members.Add(new CodeSnippetTypeMember($"const {typeQualifiedName} {name} {variable.Initializer};")); } else { method.Statements.Add(new CodeSnippetStatement($"{typeQualifiedName} {name} {variable.Initializer};")); } method.Statements.Add(new CodeSnippetStatement( $"program.Heap.SetHeapVariable(program.SymbolTable.GetAddressFromSymbol(\"{variable.Identifier}\"), {name});")); count++; } } } StringBuilder sb = new StringBuilder(); codeStringBuilders[moduleIdx] = sb; CSharpCodeProvider provider = new CSharpCodeProvider(); using (StringWriter streamWriter = new StringWriter(sb)) { provider.GenerateCodeFromCompileUnit(compileUnit, streamWriter, new CodeGeneratorOptions()); } SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sb.ToString()); initializerTrees[moduleIdx] = syntaxTree; } var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); var references = new List <MetadataReference>(); for (int i = 0; i < assemblies.Length; i++) { if (!assemblies[i].IsDynamic && assemblies[i].Location.Length > 0) { references.Add(MetadataReference.CreateFromFile(assemblies[i].Location)); } } CSharpCompilation compilation = CSharpCompilation.Create( $"init{initAssemblyCounter++}", syntaxTrees: initializerTrees, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var memoryStream = new MemoryStream()) { EmitResult result = compilation.Emit(memoryStream); if (!result.Success) { // todo: make these errors point to the correct source files bool error = false; foreach (Diagnostic diagnostic in result.Diagnostics) { if (diagnostic.Severity == DiagnosticSeverity.Error) { Debug.LogError(diagnostic); error = true; initializerErrorCount++; } } if (error) { Debug.LogError($"Generated Source code: {string.Join("\n", codeStringBuilders.Select(e => e.ToString()))}"); } } else { memoryStream.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(memoryStream.ToArray()); for (int moduleIdx = 0; moduleIdx < modulesToInitialize.Length; ++moduleIdx) { CompilationModule module = modulesToInitialize[moduleIdx]; IUdonProgram program = module.programAsset.GetRealProgram(); System.Type cls = assembly.GetType($"FieldInitialzers.Initializer{moduleIdx}"); MethodInfo methodInfo = cls.GetMethod("DoInit", BindingFlags.Public | BindingFlags.Static); methodInfo.Invoke(null, new[] { program }); foreach (var fieldDeclarationSyntax in module.fieldsWithInitializers) { foreach (var variable in fieldDeclarationSyntax.Declaration.Variables) { string varName = variable.Identifier.ToString(); object heapValue = program.Heap.GetHeapVariable(program.SymbolTable.GetAddressFromSymbol(varName)); if (heapValue != null && UdonSharpUtils.IsUserDefinedType(heapValue.GetType())) { UdonSharpUtils.LogBuildError($"Field: '{varName}' UdonSharp does not yet support field initializers on user-defined types or jagged arrays", AssetDatabase.GetAssetPath(module.programAsset.sourceCsScript), 0, 0); } } } } } } return(initializerErrorCount); }
public void Compile() { Profiler.BeginSample("UdonSharp Compile"); System.Diagnostics.Stopwatch compileTimer = new System.Diagnostics.Stopwatch(); compileTimer.Start(); int totalErrorCount = 0; try { List <ClassDefinition> classDefinitions = BuildClassDefinitions(); if (classDefinitions == null) { totalErrorCount++; } if (totalErrorCount == 0) { #if UDONSHARP_DEBUG // Single threaded compile List <CompileTaskResult> compileTasks = new List <CompileTaskResult>(); foreach (CompilationModule module in modules) { compileTasks.Add(module.Compile(classDefinitions)); } #else List <Task <CompileTaskResult> > compileTasks = new List <Task <CompileTaskResult> >(); foreach (CompilationModule module in modules) { compileTasks.Add(Task.Factory.StartNew(() => module.Compile(classDefinitions))); } #endif int totalTaskCount = compileTasks.Count; while (compileTasks.Count > 0) { #if UDONSHARP_DEBUG CompileTaskResult compileResult = compileTasks.Last(); compileTasks.RemoveAt(compileTasks.Count - 1); #else Task <CompileTaskResult> compileResultTask = Task.WhenAny(compileTasks).Result; compileTasks.Remove(compileResultTask); CompileTaskResult compileResult = compileResultTask.Result; #endif if (compileResult.compileErrors.Count == 0) { compileResult.programAsset.SetUdonAssembly(compileResult.compiledAssembly); compileResult.programAsset.AssembleCsProgram(compileResult.symbolCount); } else { foreach (CompileError error in compileResult.compileErrors) { string errorMessage = UdonSharpUtils.LogBuildError(error.errorStr, AssetDatabase.GetAssetPath(error.script).Replace("/", "\\"), error.lineIdx, error.charIdx); compileResult.programAsset.compileErrors.Add(errorMessage); } totalErrorCount += compileResult.compileErrors.Count; } int processedTaskCount = totalTaskCount - compileTasks.Count; EditorUtility.DisplayProgressBar("UdonSharp Compile", $"Compiling scripts ({processedTaskCount}/{totalTaskCount})...", Mathf.Clamp01((processedTaskCount / ((float)totalTaskCount + 1f)))); } if (totalErrorCount == 0) { EditorUtility.DisplayProgressBar("UdonSharp Compile", "Assigning constants...", 1f); int initializerErrorCount = AssignHeapConstants(); totalErrorCount += initializerErrorCount; if (initializerErrorCount == 0) { foreach (CompilationModule module in modules) { module.programAsset.ApplyProgram(); } } } } } finally { EditorUtility.ClearProgressBar(); } compileTimer.Stop(); EditorUtility.ClearProgressBar(); if (totalErrorCount == 0) { if (modules.Length > 5) { Debug.Log($"[UdonSharp] Compile of {modules.Length} scripts finished in {compileTimer.Elapsed.ToString("mm\\:ss\\.fff")}"); } else { Debug.Log($"[UdonSharp] Compile of script{(modules.Length > 1 ? "s" : "")} {string.Join(", ", modules.Select(e => Path.GetFileName(AssetDatabase.GetAssetPath(e.programAsset.sourceCsScript))))} finished in {compileTimer.Elapsed.ToString("mm\\:ss\\.fff")}"); } } Profiler.EndSample(); }