예제 #1
0
        public void AssignHeapConstants(CompilationModule module)
        {
            IUdonProgram program = module.programAsset.GetRealProgram();

            if (program != null)
            {
                foreach (SymbolDefinition symbol in module.moduleSymbols.GetAllUniqueChildSymbols())
                {
                    uint symbolAddress = program.SymbolTable.GetAddressFromSymbol(symbol.symbolUniqueName);

                    if (symbol.symbolDefaultValue != null)
                    {
                        program.Heap.SetHeapVariable(symbolAddress, symbol.symbolDefaultValue, symbol.symbolCsType);
                    }
                    else if (symbol.symbolCsType.IsArray &&
                             (symbol.declarationType.HasFlag(SymbolDeclTypeFlags.Public))) // Initialize null array fields to a 0-length array like Unity does
                    {
                        program.Heap.SetHeapVariable(symbolAddress, System.Activator.CreateInstance(symbol.symbolCsType, new object[] { 0 }), symbol.symbolCsType);
                    }
                }

                RunFieldInitalizers(module);

                // Do not let users assign null to array fields, Unity does not allow this in its normal handling
                foreach (SymbolDefinition symbol in module.moduleSymbols.GetAllUniqueChildSymbols())
                {
                    uint symbolAddress = program.SymbolTable.GetAddressFromSymbol(symbol.symbolUniqueName);

                    if (symbol.symbolCsType.IsArray &&
                        (symbol.declarationType.HasFlag(SymbolDeclTypeFlags.Public)))     // Initialize null array fields to a 0-length array like Unity does
                    {
                        object currentArrayValue = program.Heap.GetHeapVariable(symbolAddress);

                        if (currentArrayValue == null)
                        {
                            program.Heap.SetHeapVariable(symbolAddress, System.Activator.CreateInstance(symbol.symbolCsType, new object[] { 0 }), symbol.symbolCsType);
                        }
                    }
                }
            }
        }
        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);
        }
예제 #3
0
        private void RunFieldInitalizers(CompilationModule module)
        {
            IUdonProgram program = module.programAsset.GetRealProgram();

            // We don't need to run the costly compilation if the user hasn't defined any fields with initializers
            if (module.fieldsWithInitializers.Count == 0)
            {
                return;
            }

            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");

            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)
                {
                    if (variable.Initializer != null)
                    {
                        string name = variable.Identifier.ToString();
                        if (isConst)
                        {
                            _class.Members.Add(new CodeSnippetTypeMember($"const {type} {name} {variable.Initializer};"));
                        }
                        else
                        {
                            method.Statements.Add(new CodeSnippetStatement($"{type} {name} {variable.Initializer};"));
                        }

                        method.Statements.Add(new CodeSnippetStatement(
                                                  $"program.Heap.SetHeapVariable(program.SymbolTable.GetAddressFromSymbol(\"{variable.Identifier}\"), {name});"));

                        count++;
                    }
                }
            }

            CSharpCodeProvider provider = new CSharpCodeProvider();
            StringBuilder      sb       = new StringBuilder();

            using (StringWriter streamWriter = new StringWriter(sb))
            {
                provider.GenerateCodeFromCompileUnit(compileUnit, streamWriter, new CodeGeneratorOptions());
            }

            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sb.ToString());

            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: new[] { syntaxTree },
                references: references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using (var memoryStream = new MemoryStream())
            {
                EmitResult result = compilation.Emit(memoryStream);
                if (!result.Success)
                {
                    bool error = false;
                    foreach (Diagnostic diagnostic in result.Diagnostics)
                    {
                        if (diagnostic.Severity == DiagnosticSeverity.Error)
                        {
                            Debug.LogError(diagnostic);
                            error = true;
                        }
                    }

                    if (error)
                    {
                        Debug.LogError($"Generated Source code: {sb}");
                    }
                }
                else
                {
                    memoryStream.Seek(0, SeekOrigin.Begin);

                    Assembly   assembly   = Assembly.Load(memoryStream.ToArray());
                    var        cls        = assembly.GetType("FieldInitialzers.Initializer");
                    MethodInfo methodInfo = cls.GetMethod("DoInit", BindingFlags.Public | BindingFlags.Static);
                    methodInfo.Invoke(null, new[] { program });
                }
            }
        }