Beispiel #1
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #5
0
        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();
        }