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