Пример #1
0
        public void AddExternCall(string externCall, string comment = "")
        {
            externStringSet.Add(externCall);

            AppendCommentedLine($"EXTERN, \"{externCall}\"", comment);
            programCounter += UdonSharpUtils.GetUdonInstructionSize("EXTERN");
        }
        // Mostly copy paste of above adapted for just checking the types in `params` args
        private int ScoreMethodParamArgPair(System.Type methodParam, System.Type argType)
        {
            // This doesn't yet handle implicit user defined casts... there are probably other things this should handle too.
            int score = 1000000;

            if (methodParam == argType)
            {
                score = 0;
            }
            else if (methodParam.IsValidNumericImplicitCastTargetType() && argType.IsValidNumericImplictCastSourceType())
            {
                score = UdonSharpUtils.GetImplicitNumericCastDistance(methodParam, argType);
            }
            else if (methodParam == typeof(object))
            {
                score = 30; // We want to avoid object args as much as possible
            }
            else if (argType.IsSubclassOf(methodParam))
            {
                // Count the distance in the inheritance

                System.Type currentType = argType;

                score = 0;
                while (currentType != methodParam && score < 20)
                {
                    score++;
                    currentType = currentType.BaseType;
                }
            }

            return(score);
        }
Пример #3
0
        /// <summary>
        /// Verifies that Udon supports the given type and resolves the type name used to reference it in Udon
        /// </summary>
        /// <param name="externType">The found type</param>
        /// <param name="skipBaseTypeRemap">Skips remapping base VRCSDK types, this is primarily used for VRCSDK function return value types since they may point to the base type and we want to maintain that.</param>
        /// <returns>The Udon type name string if it is a valid Udon type,
        ///     or null if it is not a valid Udon type.</returns>
        public string GetUdonTypeName(System.Type externType, bool skipBaseTypeRemap = false)
        {
            if (!skipBaseTypeRemap)
            {
                externType = UdonSharpUtils.RemapBaseType(externType);
            }

            string externTypeName = externType.GetNameWithoutGenericArity();

            while (externType.IsArray || externType.IsByRef)
            {
                externType = externType.GetElementType();
            }

            string typeNamespace = externType.Namespace;

            // Handle nested type names (+ sign in names)
            if (externType.DeclaringType != null)
            {
                string declaringTypeNamespace = "";

                System.Type declaringType = externType.DeclaringType;

                while (declaringType != null)
                {
                    declaringTypeNamespace = $"{externType.DeclaringType.Name}.{declaringTypeNamespace}";
                    declaringType          = declaringType.DeclaringType;
                }

                typeNamespace += $".{declaringTypeNamespace}";
            }

            if (externTypeName == "T" || externTypeName == "T[]")
            {
                typeNamespace = "";
            }

            string fullTypeName = SanitizeTypeName($"{typeNamespace}.{externTypeName}");

            foreach (System.Type genericType in externType.GetGenericArguments())
            {
                fullTypeName += GetUdonTypeName(genericType);
            }

            // Seems like Udon does shortening for this specific type somewhere
            if (fullTypeName == "SystemCollectionsGenericListT")
            {
                fullTypeName = "ListT";
            }
            else if (fullTypeName == "SystemCollectionsGenericIEnumerableT")
            {
                fullTypeName = "IEnumerableT";
            }

            fullTypeName = fullTypeName.Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver");

            return(fullTypeName);
        }
Пример #4
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);
        }
Пример #5
0
        public void AddJumpIfFalse(JumpLabel jumpTarget, string comment = "")
        {
            if (jumpTarget.IsResolved)
            {
                AppendCommentedLine($"JUMP_IF_FALSE, {jumpTarget.AddresStr()}", comment);
            }
            else
            {
                AppendCommentedLine($"JUMP_IF_FALSE_LABEL, [{jumpTarget.uniqueName}]", comment);
            }

            programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_IF_FALSE");
        }
Пример #6
0
        public MethodBase FindBestOverloadFunction(MethodBase[] methods, List <System.Type> methodArgs, bool checkIfInUdon = true)
        {
            if (methods.Length == 0)
            {
                throw new System.ArgumentException("Cannot find overload from 0 length method array");
            }

            List <MethodBase> validMethods = new List <MethodBase>();

            foreach (MethodBase method in methods)
            {
                ParameterInfo[] methodParams = method.GetParameters();

                bool isMethodValid = true;

                for (int i = 0; i < methodParams.Length; ++i)
                {
                    ParameterInfo currentParam = methodParams[i];

                    // Check method arg count
                    if (i >= methodArgs.Count && !currentParam.HasDefaultValue)
                    {
                        isMethodValid = false;
                        break;
                    }
                    else if (currentParam.HasDefaultValue)
                    {
                        continue;
                    }

                    System.Type argType = methodArgs[i];

                    if (!currentParam.ParameterType.IsImplicitlyAssignableFrom(argType) && !currentParam.HasParamsParameter() && !currentParam.ParameterType.IsByRef)
                    {
                        // Handle implicit upcasts to int from lower precision types
                        if (method is OperatorMethodInfo operatorParam &&
                            (operatorParam.operatorType == BuiltinOperatorType.LeftShift || operatorParam.operatorType == BuiltinOperatorType.RightShift) &&
                            (argType != typeof(uint) && argType != typeof(ulong) && argType != typeof(long)))
                        {
                            if (UdonSharpUtils.GetNumericConversionMethod(currentParam.ParameterType, argType) == null)
                            {
                                isMethodValid = false;
                                break;
                            }
                        }
                        else
                        {
                            isMethodValid = false;
                            break;
                        }
                    }
        public void AddJump(JumpLabel jumpTarget, string comment = "")
        {
#if USE_UDON_LABELS
            AppendCommentedLine($"JUMP, {jumpTarget.uniqueName}", comment);
#else
            if (jumpTarget.IsResolved)
            {
                AppendCommentedLine($"JUMP, {jumpTarget.AddresStr()}", comment);
            }
            else
            {
                AppendCommentedLine($"JUMP_LABEL, [{jumpTarget.uniqueName}]", comment);
            }
#endif

            programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP");
        }
Пример #8
0
        private static string HashSourceFile(MonoScript script)
        {
            string scriptPath = AssetDatabase.GetAssetPath(script);
            string scriptText = "";

            try
            {
                scriptText = UdonSharpUtils.ReadFileTextSync(scriptPath);
            }
            catch (System.Exception e)
            {
                scriptText = Random.value.ToString();
                Debug.Log(e);
            }

            return(UdonSharpUtils.HashString(scriptText));
        }
        public CompilationModule(UdonSharpProgramAsset sourceAsset)
        {
            programAsset           = sourceAsset;
            resolver               = new ResolverContext();
            moduleSymbols          = new SymbolTable(resolver, null);
            moduleLabels           = new LabelTable();
            fieldsWithInitializers = new HashSet <FieldDeclarationSyntax>();

            if (programAsset.sourceCsScript == null)
            {
                throw new System.ArgumentException($"Asset '{AssetDatabase.GetAssetPath(programAsset)}' does not have a valid program source to compile from");
            }


            sourceCode = UdonSharpUtils.ReadFileTextSync(AssetDatabase.GetAssetPath(programAsset.sourceCsScript));

            settings = UdonSharpSettings.GetSettings();
        }
Пример #10
0
        public string GetUdonFieldAccessorName(FieldInfo externField, FieldAccessorType accessorType, bool validate = true)
        {
            System.Type fieldType = UdonSharpUtils.RemapBaseType(externField.DeclaringType);

            string functionNamespace = SanitizeTypeName(fieldType.FullName).Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver");
            string methodName        = $"__{(accessorType == FieldAccessorType.Get ? "get" : "set")}_{externField.Name.Trim('_')}";

            string paramStr = $"__{GetUdonTypeName(externField.FieldType)}";

            string finalFunctionSig = $"{functionNamespace}.{methodName}{paramStr}";

            if (validate && !nodeDefinitionLookup.Contains(finalFunctionSig))
            {
                throw new System.Exception($"Field accessor {finalFunctionSig} is not exposed in Udon");
            }

            return(finalFunctionSig);
        }
Пример #11
0
        public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
        {
            if (requestedBuildType == VRCSDKRequestedBuildType.Avatar)
            {
                return(true);
            }

            if (UdonSharpSettings.GetSettings()?.disableUploadCompile ?? false)
            {
                return(true);
            }

            UdonSharpProgramAsset.CompileAllCsPrograms(true, false);
            UdonSharpEditorCache.SaveAllCache();

            if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError())
            {
                Debug.LogError("[<color=#FF00FF>UdonSharp</color>] Failed to compile UdonSharp scripts for build, check error log for details.");
                UdonSharpUtils.ShowEditorNotification("Failed to compile UdonSharp scripts for build, check error log for details.");
                return(false);
            }

            return(true);
        }
        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 void AddCopy(string comment = "")
 {
     AppendCommentedLine("COPY", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("COPY");
 }
 public void AddJumpIndirect(SymbolDefinition addressSymbol, string comment = "")
 {
     AppendCommentedLine($"JUMP_INDIRECT, {addressSymbol.symbolUniqueName}", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_INDIRECT");
 }
 public void AddJumpToExit()
 {
     AppendCommentedLine($"JUMP, 0xFFFFFFFF", "");
     programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP");
 }
 private void AddPush(string heapAddress, string comment)
 {
     AppendCommentedLine($"PUSH, {heapAddress}", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("PUSH");
 }
Пример #17
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);
        }
Пример #18
0
 public void AddJumpIndirect(SymbolDefinition addressSymbol, string comment = "")
 {
     //throw new System.NotImplementedException("Jump indirect instruction is not implemented in UdonSharp yet."); // I'm not sure why JUMP_INDIRECT both takes an address and pops an address from the stack
     AppendCommentedLine($"JUMP_INDIRECT, {addressSymbol.symbolUniqueName}", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_INDIRECT");
 }
 private bool IsNormalUnityObject(System.Type declaredType, FieldDefinition fieldDefinition)
 {
     return(!UdonSharpUtils.IsUserDefinedBehaviour(declaredType) && (fieldDefinition == null || fieldDefinition.fieldSymbol.userCsType == null || !fieldDefinition.fieldSymbol.IsUserDefinedBehaviour()));
 }
        /// <summary>
        /// Create a symbol given a name. At the moment assumes that only internal symbols get incremented, this means there's no masking at the moment for local variables.
        /// </summary>
        /// <param name="symbolName"></param>
        /// <param name="resolvedSymbolType"></param>
        /// <param name="declType"></param>
        /// <param name="appendType">Used to disable redundant type append from unnamed variable allocations</param>
        /// <returns></returns>
        private SymbolDefinition CreateNamedSymbolInternal(string symbolName, System.Type resolvedSymbolType, SymbolDeclTypeFlags declType, bool appendType = true)
        {
            if (resolvedSymbolType == null || symbolName == null)
            {
                throw new System.ArgumentNullException();
            }

            if (!declType.HasFlag(SymbolDeclTypeFlags.Internal) && symbolName.StartsWith("__"))
            {
                throw new System.ArgumentException($"Symbol {symbolName} cannot have name starting with \"__\", this naming is reserved for internal variables.");
            }

            string uniqueSymbolName = symbolName;

            bool hasGlobalDeclaration = false;

            if (declType.HasFlag(SymbolDeclTypeFlags.Internal))
            {
                uniqueSymbolName = $"intnl_{uniqueSymbolName}";
            }
            if (declType.HasFlag(SymbolDeclTypeFlags.Constant))
            {
                uniqueSymbolName     = $"const_{uniqueSymbolName}";
                hasGlobalDeclaration = true;
            }
            if (declType.HasFlag(SymbolDeclTypeFlags.This))
            {
                uniqueSymbolName     = $"this_{uniqueSymbolName}";
                hasGlobalDeclaration = true;
            }
            if (declType.HasFlag(SymbolDeclTypeFlags.Reflection))
            {
                uniqueSymbolName     = $"__refl_{uniqueSymbolName}";
                hasGlobalDeclaration = true;
            }

            if (!declType.HasFlag(SymbolDeclTypeFlags.Public) && !declType.HasFlag(SymbolDeclTypeFlags.Private) && !declType.HasFlag(SymbolDeclTypeFlags.Reflection))
            {
                if (appendType)
                {
                    string sanitizedName = resolver.SanitizeTypeName(resolvedSymbolType.Name);
                    uniqueSymbolName += $"_{sanitizedName}";
                }

                if (hasGlobalDeclaration)
                {
                    uniqueSymbolName = $"__{IncrementGlobalNameCounter(uniqueSymbolName)}_{uniqueSymbolName}";
                }
                else
                {
                    uniqueSymbolName = $"__{IncrementUniqueNameCounter(uniqueSymbolName)}_{uniqueSymbolName}";
                }
            }

            System.Type typeForName = UdonSharpUtils.UserTypeToUdonType(resolvedSymbolType);

            string udonTypeName = resolver.GetUdonTypeName(typeForName);

            if (udonTypeName == null)
            {
                throw new System.ArgumentException($"Could not locate Udon type for system type {resolvedSymbolType.FullName}");
            }

            udonTypeName = udonTypeName.Replace("VRCUdonCommonInterfacesIUdonEventReceiver", "VRCUdonUdonBehaviour");

            SymbolDefinition symbolDefinition = new SymbolDefinition();

            symbolDefinition.declarationType        = declType;
            symbolDefinition.symbolCsType           = resolvedSymbolType;
            symbolDefinition.symbolOriginalName     = symbolName;
            symbolDefinition.symbolResolvedTypeName = udonTypeName;
            symbolDefinition.symbolUniqueName       = uniqueSymbolName;

            if (hasGlobalDeclaration)
            {
                GetGlobalSymbolTable().symbolDefinitions.Add(symbolDefinition);
            }
            else
            {
                symbolDefinitions.Add(symbolDefinition);
            }

            return(symbolDefinition);
        }
        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);
        }
Пример #22
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();
        }
Пример #23
0
        /// <summary>
        /// Verifies that Udon supports the given method and resolves the name used to reference it in Udon EXTERN calls
        /// </summary>
        /// <param name="externMethod"></param>
        /// <returns></returns>
        public string GetUdonMethodName(MethodBase externMethod, bool validate = true, List <System.Type> genericArguments = null)
        {
            System.Type methodSourceType = externMethod.ReflectedType;

            if (genericArguments != null)
            {
                if (genericArguments.Count != 1)
                {
                    throw new System.ArgumentException("UdonSharp only supports 1 type generic methods at the moment");
                }

                methodSourceType = genericArguments.First();
            }

            methodSourceType = UdonSharpUtils.RemapBaseType(methodSourceType);

            bool isUdonSharpBehaviour = false;

            if (methodSourceType == typeof(UdonSharpBehaviour) || methodSourceType.IsSubclassOf(typeof(UdonSharpBehaviour)))
            {
                methodSourceType     = typeof(VRC.Udon.UdonBehaviour);
                isUdonSharpBehaviour = true;
            }

            string functionNamespace = SanitizeTypeName(methodSourceType.FullName ?? methodSourceType.Namespace + methodSourceType.Name).Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver");

            string methodName = $"__{externMethod.Name.Trim('_').TrimStart('.')}";

            ParameterInfo[] methodParams = externMethod.GetParameters();

            if (isUdonSharpBehaviour &&
                methodName == "__VRCInstantiate")
            {
                functionNamespace = "VRCInstantiate";
                methodName        = "__Instantiate";
            }

            string paramStr = "";

            if (methodParams.Length > 0)
            {
                paramStr = "_"; // Arg separator

                foreach (ParameterInfo parameterInfo in methodParams)
                {
                    paramStr += $"_{GetUdonTypeName(parameterInfo.ParameterType, true)}";
                }
            }
            else if (externMethod is ConstructorInfo)
            {
                paramStr = "__";
            }

            string returnStr = "";

            if (externMethod is MethodInfo)
            {
                returnStr = $"__{GetUdonTypeName(((MethodInfo)externMethod).ReturnType, true)}";
            }
            else if (externMethod is ConstructorInfo)
            {
                returnStr = $"__{GetUdonTypeName(((ConstructorInfo)externMethod).DeclaringType)}";
            }
            else
            {
                throw new System.Exception("Invalid extern method type for getting Udon name");
            }

            string finalFunctionSig = $"{functionNamespace}.{methodName}{paramStr}{returnStr}";

            if (validate && !nodeDefinitionLookup.Contains(finalFunctionSig))
            {
                throw new System.Exception($"Method {finalFunctionSig} is not exposed in Udon");
            }

            return(finalFunctionSig);
        }
Пример #24
0
 public bool IsUserDefinedBehaviour()
 {
     return(UdonSharpUtils.IsUserDefinedBehaviour(internalType));
 }
Пример #25
0
 public bool IsUserDefinedType()
 {
     return(UdonSharpUtils.IsUserDefinedType(internalType));
 }
Пример #26
0
        public SymbolDefinition Invoke(SymbolDefinition[] invokeParams)
        {
            SymbolDefinition methodResult = null;

            System.Type resultSymbolType = null;
            string      lookupSymbolName = null;

            switch (captureFunc)
            {
            case InternalFunc.TypeIDInstance:
            case InternalFunc.TypeIDGeneric:
                resultSymbolType = typeof(long);
                lookupSymbolName = "udonTypeID";
                break;

            case InternalFunc.TypeNameInstance:
            case InternalFunc.TypeNameGeneric:
                resultSymbolType = typeof(string);
                lookupSymbolName = "udonTypeName";
                break;

            default:
                throw new System.ArgumentException("Invalid internal method invocation");
            }

            methodResult = visitorContext.topTable.CreateUnnamedSymbol(resultSymbolType, SymbolDeclTypeFlags.Internal);

            if (captureFunc == InternalFunc.TypeIDInstance ||
                captureFunc == InternalFunc.TypeNameInstance)
            {
                SymbolDefinition invokeSymbol = captureScope.accessSymbol;

                using (ExpressionCaptureScope resultSetterScope = new ExpressionCaptureScope(visitorContext, null))
                {
                    resultSetterScope.SetToLocalSymbol(methodResult);

                    using (ExpressionCaptureScope getInvokeScope = new ExpressionCaptureScope(visitorContext, null))
                    {
                        getInvokeScope.SetToLocalSymbol(invokeSymbol);
                        getInvokeScope.ResolveAccessToken(nameof(VRC.Udon.UdonBehaviour.GetProgramVariable));

                        string symbolName = visitorContext.topTable.GetReflectionSymbol(lookupSymbolName, resultSymbolType).symbolUniqueName;

                        SymbolDefinition invokeResult = getInvokeScope.Invoke(new SymbolDefinition[] { visitorContext.topTable.CreateConstSymbol(typeof(string), symbolName) });

                        JumpLabel exitBranchJump = visitorContext.labelTable.GetNewJumpLabel("exitUdonTypeIdLoc");
                        JumpLabel falseBranchLoc = visitorContext.labelTable.GetNewJumpLabel("falseUdonTypeIdLoc");

                        SymbolDefinition nullCheckSymbol = null;

                        using (ExpressionCaptureScope nullCheckCondition = new ExpressionCaptureScope(visitorContext, null))
                        {
                            nullCheckCondition.SetToMethods(UdonSharpUtils.GetOperators(typeof(object), BuiltinOperatorType.Inequality));
                            nullCheckSymbol = nullCheckCondition.Invoke(new SymbolDefinition[] { invokeResult, visitorContext.topTable.CreateConstSymbol(typeof(object), null) });
                        }

                        visitorContext.uasmBuilder.AddJumpIfFalse(falseBranchLoc, nullCheckSymbol);

                        resultSetterScope.ExecuteSet(captureScope.CastSymbolToType(invokeResult, resultSymbolType, true));
                        visitorContext.uasmBuilder.AddJump(exitBranchJump);

                        // If the value is null
                        visitorContext.uasmBuilder.AddJumpLabel(falseBranchLoc);

                        if (captureFunc == InternalFunc.TypeIDInstance)
                        {
                            resultSetterScope.ExecuteSet(visitorContext.topTable.CreateConstSymbol(typeof(long), 0L));
                        }
                        else
                        {
                            resultSetterScope.ExecuteSet(visitorContext.topTable.CreateConstSymbol(typeof(string), "UnknownType"));
                        }

                        visitorContext.uasmBuilder.AddJumpLabel(exitBranchJump);
                    }
                }
            }
            else
            {
                object resultSymbolValue = null;

                if (captureFunc == InternalFunc.TypeIDGeneric)
                {
                    resultSymbolValue = Internal.UdonSharpInternalUtility.GetTypeID(genericType);
                }
                else if (captureFunc == InternalFunc.TypeNameGeneric)
                {
                    resultSymbolValue = Internal.UdonSharpInternalUtility.GetTypeName(genericType);
                }

                methodResult = visitorContext.topTable.CreateConstSymbol(resultSymbolType, resultSymbolValue);
            }

            using (ExpressionCaptureScope propagateScope = new ExpressionCaptureScope(visitorContext, visitorContext.topCaptureScope))
            {
                propagateScope.SetToLocalSymbol(methodResult);
            }

            return(methodResult);
        }