Пример #1
0
    public static Type GetTypeFromTypeStringWithErrors(this TypeResolverGroup typeResolverGroup, string typeString)
    {
        var type = typeResolverGroup.GetTypeFromTypeString(typeString);

        if (type == null)
        {
            Debug.LogError($"Unable to find type {typeString} in type resolver group");
        }
        return(type);
    }
Пример #2
0
 public void AddGenericArguments(TDType rootType, TypeResolverGroup typeResolverGroup, params string[] strings)
 {
     if (!GeneratedGenericArguments)
     {
         foreach (var genericArgument in strings)
         {
             GenericArguments.Add(
                 UdonTypeExporter.GetOrCreateTDType(rootType, genericArgument, typeResolverGroup));
             GeneratedGenericArguments = true;
         }
     }
 }
Пример #3
0
    static void ExportUdonTypes()
    {
        string path         = EditorUtility.SaveFilePanel("Save Udon Types", "", "UdonNodeInfo", "dll");
        var    typeResolver = new TypeResolverGroup(new List <IUAssemblyTypeResolver>()
        {
            new SystemTypeResolver(),
            new UnityEngineTypeResolver(),
            new VRCSDK2TypeResolver(),
            new UdonTypeResolver(),
            new ExceptionTypeResolver(),
            new UdonBehaviourTypeResolver(),
        });
        var rootType = new TDType();

        try
        {
            EditorUtility.DisplayProgressBar("Progress", "Parsing Definitions...", 1f / 2);
            ParseDefinitions(rootType, typeResolver);

            EditorUtility.DisplayProgressBar("Progress", "Saving to file...", 2f / 2);

            string codeString = UdonTypeDLLExporter.ExportTDTypeAsDLL(rootType, typeResolver);

            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateExecutable = false;
            parameters.CompilerOptions    = "-nostdlib -noconfig";
            parameters.OutputAssembly     = "Output.dll";
            CompilerResults r = CodeDomProvider.CreateProvider("CSharp")
                                .CompileAssemblyFromSource(parameters, codeString);

            foreach (var s in r.Output)
            {
                if (s.Contains("warning"))
                {
                    continue;
                }
                Debug.Log(s);
            }

            File.WriteAllLines("source.txt", new[] { codeString });

            File.Copy("Output.dll", path, true);
        }
        finally
        {
            EditorUtility.ClearProgressBar();
        }

        Debug.Log($"Done\nOutput to: {path}");
    }
Пример #4
0
 public static void AddTypeInterfaces(this Type type, HashSet <string> interfaces,
                                      TypeResolverGroup typeResolverGroup)
 {
     if (type == null)
     {
         return;
     }
     foreach (var Interface in type.GetInterfaces())
     {
         if (typeResolverGroup.GetTypeFromTypeString(UdonTypeExporter.GenerateUdonName(Interface)) != null)
         {
             interfaces.Add(UdonTypeExporter.GetTypeFullName(Interface));
             Interface.AddTypeInterfaces(interfaces, typeResolverGroup);
         }
     }
 }
    private void HandlePushLabel(string rawLine, string[] instructionArgs, Stack <object> stack,
                                 TypeResolverGroup typeResolver, Dictionary <string, uint> symbolNameToAddress)
    {
        if (instructionArgs.Length != 2)
        {
            throw new InvalidOperationException(
                      $"PUSHLABEL instruction requires 1 arguments(AssemblyVariableName). EX: 'PUSHLABEL \"_update\"'");
        }
        var labelName = instructionArgs[1].Replace("\"", "");

        if (symbolNameToAddress.TryGetValue(labelName, out uint address))
        {
            stack.Push(address);
        }
        else
        {
            throw new InvalidOperationException(
                      $"Unable to find label '{labelName}'");
        }
    }
Пример #6
0
    private void HandleSetHeapInstruction(string rawLine, string[] instructionArgs, Stack <object> stack,
                                          TypeResolverGroup typeResolver)
    {
        if (instructionArgs.Length != 2)
        {
            throw new InvalidOperationException(
                      $"SETHEAP instruction requires 1 arguments(AssemblyVariableName). EX: 'SETHEAP \"variableName\"'");
        }
        var variableName = instructionArgs[1].Replace("\"", "");
        var addr         = program.SymbolTable.GetAddressFromSymbol(variableName);
        var data         = stack.Pop();

        if (data is Type)
        {
            program.Heap.SetHeapVariable(addr, data, typeof(Type)); //Prevents .GetType from getting RuntimeType
        }
        else
        {
            program.Heap.SetHeapVariable(addr, data, data.GetType());
        }
    }
Пример #7
0
    private void HandlePushEnumInstruction(string rawLine, string[] instructionArgs, Stack <object> stack,
                                           TypeResolverGroup typeResolver)
    {
        if (instructionArgs.Length != 3)
        {
            throw new InvalidOperationException(
                      $"PUSHENUM requires 2 arguments(EnumType, EnumValueName). EX: 'PUSHENUM UnityEngineFFTWindow Rectangular'");
        }
        var enumName      = instructionArgs[1];
        var enumValueName = instructionArgs[2];

        var enumType = typeResolver.GetTypeFromTypeString(enumName);

        if (enumType == null)
        {
            throw new InvalidOperationException(
                      $"Unable to find type '{enumName}'.");
        }
        object output = Enum.Parse(enumType, enumValueName);

        stack.Push(output);
    }
Пример #8
0
    private void HandleConstructInstruction(string rawLine, string[] instructionArgs, Stack <object> stack,
                                            TypeResolverGroup typeResolver)
    {
        if (instructionArgs.Length != 3)
        {
            throw new InvalidOperationException(
                      $"CONSTRUCT instruction requires 2 arguments(TypeName, ArgumentCount). EX: 'CONSTRUCT UnityEngineVector2 2'");
        }
        var typeName = instructionArgs[1];
        var constructorArgumentcount = int.Parse(instructionArgs[2]);

        Stack <object> flipped = new Stack <object>();

        for (int i = 0; i < constructorArgumentcount; i++)
        {
            flipped.Push(stack.Pop());
        }

        object obj = Activator.CreateInstance(typeResolver.GetTypeFromTypeString(typeName), flipped.ToArray());

        stack.Push(obj);
    }
Пример #9
0
    private static string RecurseInheritedType(Type originalType, Type type, TypeResolverGroup typeResolverGroup)
    {
        if (type == null || type == typeof(object))
        {
            return("");
        }
        if (originalType == type)
        {
            return(RecurseInheritedType(originalType, type.BaseType, typeResolverGroup));
        }

        if (typeResolverGroup.GetTypeFromTypeString(UdonTypeExporter.GenerateUdonName(type)) != null)
        {
            return(UdonTypeExporter.GetTypeFullName(type));
        }

        if (type.BaseType != null)
        {
            return(RecurseInheritedType(originalType, type.BaseType, typeResolverGroup));
        }

        return("");
    }
Пример #10
0
    private static void ParseDefinitions(
        TDType rootType, TypeResolverGroup typeResolverGroup)
    {
        foreach (var definition in UdonEditorManager.Instance.GetNodeDefinitions())
        {
            if (StartsWithIgnoredKeyword(definition) | IsSpecialDefinition(definition))
            {
                continue;
            }
            //Try to match by the non constructor regex, if it fails fallback to the constructor regex.
            //Perhaps they can be combined but this works.
            var match = NonCtorRegex.Match(definition.fullName);
            if (match.Groups.Count != NonCtorRegex.GetGroupNumbers().Length)
            {
                match = CtorRegex.Match(definition.fullName);
            }
            var groups = match.Groups;
            //Make sure all groups are filled. If not then the regex failed.
            if (groups.Count == NonCtorRegex.GetGroupNumbers().Length)
            {
                var definitionName = groups["namespace"].ToString();
                var methodType     = groups["methodType"].ToString();
                var methodName     = groups["methodName"].ToString();
                var inputsRaw      = groups["inputs"].ToString();
                var methodOutput   = groups["outputs"].ToString();

                //For some reason underscores are allowed and I'm not quite sure how to deal with them, so let's just do this
                //Replace with -, split by _, replace - with _
                inputsRaw = inputsRaw.Replace("VRCSDKBaseVRC_", "VRCSDKBaseVRC-");
                var methodInputs = inputsRaw.Split('_');
                for (int i = 0; i < methodInputs.Length; i++)
                {
                    methodInputs[i] = methodInputs[i].Replace("-", "_");
                }

                var isStatic = (definition.inputNames.Length > 0 && definition.inputNames[0] != "instance");
                //Some of the methods don't have any inputs(so definition.inputNames[0] doesn't exist) so we have to check the wrapper
                try
                {
                    int outputCount         = definition.outputs[0] != typeof(void) ? 1 : 0;
                    int inputParameterCount =
                        Wrapper.GetExternFunctionParameterCount(definition.fullName) - outputCount;
                    if (definition.inputNames.Length == 0 && inputParameterCount == 0)
                    {
                        isStatic = true;
                    }
                }
                catch //Catch because the wrapper just throws for some unsupported externs that exist in node definitions
                {
                }

                var fullUdonExternString = definition.fullName;
                var definitionType       = typeResolverGroup.GetTypeFromTypeStringWithErrors(definitionName);
                var namespaceName        = GetTypeFullName(definitionType);
                var definitionTDType     = GetOrCreateTDType(rootType, namespaceName, typeResolverGroup);
                definitionTDType.UdonName   = GenerateUdonName(definitionType);
                definitionTDType.CSharpType = definitionType;

                //Create TDTypes for all C# types encountered in the definition, and attach methods to them for each of the Extern functions
                var method = new Method
                {
                    FullUdonExternString = fullUdonExternString,
                    MethodName           = methodName,
                    MethodType           = methodType,
                    IsStatic             = isStatic
                };

                foreach (var udonTypeName in methodInputs)
                {
                    if (udonTypeName != "")
                    {
                        var    thisType = typeResolverGroup.GetTypeFromTypeStringWithErrors(udonTypeName);
                        var    typeName = GetTypeFullName(thisType);
                        TDType tdType   = GetOrCreateTDType(rootType, typeName, typeResolverGroup);
                        tdType.UdonName   = GenerateUdonName(thisType);
                        tdType.CSharpType = thisType;
                        if (typeResolverGroup.GetTypeFromTypeStringWithErrors(tdType.UdonName) != thisType)
                        {
                            Debug.LogError(
                                $"Could not generate proper udon name for {thisType}. Generated: {tdType.UdonName}");
                        }

                        method.Inputs.Add(tdType);
                    }
                }

                if (methodOutput != "")
                {
                    var    thisType = typeResolverGroup.GetTypeFromTypeStringWithErrors(methodOutput);
                    TDType tdType   = GetOrCreateTDType(rootType, GetTypeFullName(thisType), typeResolverGroup);
                    tdType.UdonName   = GenerateUdonName(thisType);
                    tdType.CSharpType = thisType;
                    method.Output     = tdType;
                }

                if (method.IsStatic)
                {
                    definitionTDType.StaticMethods.Add(method);
                }
                else
                {
                    definitionTDType.NonStaticMethods.Add(method);
                }
            }
            else
            {
                Debug.LogError($"Unhandled definition: {definition.fullName}");
            }
        }
    }
Пример #11
0
    public static TDType GetOrCreateTDType(TDType rootType, string fullName, TypeResolverGroup typeResolverGroup)
    {
        bool           containsGenericArguments = fullName.Contains("<");
        Queue <string> namespaces;
        string         genericArguments = null;

        if (containsGenericArguments) //Generic types
        {
            var match    = GenericsRegex.Match(fullName);
            var groups   = match.Groups;
            var baseType = groups["GenericBaseType"].ToString();
            genericArguments = groups["GenericArguments"].ToString();
            namespaces       = new Queue <string>(baseType.Split('.'));
        }
        else
        {
            namespaces = new Queue <string>(fullName.Split('.'));
        }

        var current = rootType;

        while (namespaces.Count > 0)
        {
            var name = namespaces.Dequeue();
            //Only the full string is "generic", so it must be the last thing in the queue.
            //IE. System.Collections.Generic isn't generic itself, but System.Collections.Generic.List is generic
            bool isGeneric = containsGenericArguments && namespaces.Count == 0;
            var  child     = current.Children.Find(x =>
                                                   x.TypeName == name && x.IsGeneric == isGeneric && genericArguments == x.InputGenericArguments);
            if (child != null)
            {
                //Go down tree
                current = child;
            }
            else
            {
                //Create an go down tree
                var type = new TDType
                {
                    NamespaceName         = current.FullName,
                    FullName              = (current.FullName != null ? current.FullName + "." : "") + name,
                    TypeName              = name,
                    InputGenericArguments = genericArguments
                };
                string attemptedUdonName =
                    GenerateUdonName(type.FullName, true); //Try to generate udon name and set it if it's correct.
                if (typeResolverGroup.GetTypeFromTypeString(attemptedUdonName) != null)
                {
                    type.UdonName = attemptedUdonName;
                }

                current.Children.Add(type);
                current           = type;
                current.IsGeneric = isGeneric;
            }
        }

        if (current.IsGeneric)
        {
            current.IsGeneric = true;
            if (!genericArguments.Contains("<"))
            {
                current.AddGenericArguments(rootType, typeResolverGroup, genericArguments.Replace(" ", "").Split(','));
            }
            else
            {
                //Only one thing contains a nested generic argument in Udon currently
                //and luckily it looks like "thing<other<something, else>>"
                //which means it's a single layer of nesting
                //So for now we can just pass "other<something, else>" to GetOrCreateTDType
                //In the future this might change?
                current.AddGenericArguments(rootType, typeResolverGroup, genericArguments);
            }
        }

        if (fullName.Contains("[]"))
        {
            //Add base type for arrays
            GetOrCreateTDType(rootType, fullName.Replace("[]", ""), typeResolverGroup);
        }

        return(current);
    }
Пример #12
0
    private void HandlePushInstruction(string rawLine, string[] instructionArgs, Stack <object> stack,
                                       TypeResolverGroup typeResolver)
    {
        if (instructionArgs.Length < 3)
        {
            throw new InvalidOperationException(
                      $"PUSH instruction requires 2 arguments(TypeName, literal). EX: 'PUSH SystemSingle 5'");
        }
        var typeName = instructionArgs[1];
        var literal  = rawLine.Substring(rawLine.IndexOf(typeName) + typeName.Length + 1);

        object output = null;

        switch (typeName)
        {
        case "SystemSingle":
            output = Single.Parse(literal);
            break;

        case "SystemDouble":
            output = Double.Parse(literal);
            break;

        case "SystemInt64":
            output = Int64.Parse(literal);
            break;

        case "SystemInt32":
            output = Int32.Parse(literal);
            break;

        case "SystemInt16":
            output = Int16.Parse(literal);
            break;

        case "SystemUInt64":
            output = UInt64.Parse(literal);
            break;

        case "SystemUInt32":
            output = UInt32.Parse(literal);
            break;

        case "SystemUInt16":
            output = UInt16.Parse(literal);
            break;

        case "SystemString":
            output = literal.Trim('"');
            break;

        case "SystemBoolean":
            output = Boolean.Parse(literal);
            break;

        case "SystemChar":
            output = char.Parse(literal);
            break;

        case "SystemByte":
            output = byte.Parse(literal);
            break;

        case "SystemSByte":
            output = sbyte.Parse(literal);
            break;

        case "SystemType":
            output = typeResolver.GetTypeFromTypeString(literal);
            if (output == null)
            {
                throw new InvalidOperationException(
                          $"Unable to find type '{literal}'.");
            }
            break;

        default:
            throw new InvalidOperationException($"PUSH literal unsupported for type {typeName}");
        }

        stack.Push(output);
    }
Пример #13
0
 public static string GetFirstInheritedUdonType(this Type c, TypeResolverGroup typeResolverGroup)
 {
     return(RecurseInheritedType(c, c, typeResolverGroup));
 }
Пример #14
0
    public static string ExportTDTypeAsDLL(TDType root, TypeResolverGroup typeResolverGroup)
    {
        HashSet <Class>            parentlessClasses = new HashSet <Class>();
        HashSet <Class>            extenstionClasses = new HashSet <Class>();
        Dictionary <string, Class> fullNameToClass   = new Dictionary <string, Class>();

        void WriteTypeCode(TDType type)
        {
            if (string.IsNullOrEmpty(type.FullName) || type.FullName.Contains("*"))
            {
                return;
            }
            var    fullName       = type.FullName.Replace("[]", "Array");
            var    typeName       = type.TypeName.Replace("[]", "Array");
            bool   extensionClass = type.FullName.EndsWith("[]");
            string dictionaryKey  = type.IsGeneric ? type.FullName + type.GenericArguments.Count : type.FullName;

            //Create or get class
            if (!fullNameToClass.TryGetValue(dictionaryKey, out var Class))
            {
                Class = new Class
                {
                    UdonName         = type.UdonName,
                    TypeName         = typeName,
                    IsExtentionClass = extensionClass,
                    Type             = type,
                    FullName         = type.FullName
                };
                Class.IsNamespace = Class.TypeName == "System" || Class.TypeName == "Collections" ||
                                    (string.IsNullOrEmpty(Class.UdonName) && type.GenericArguments.Count == 0);
                if (type.CSharpType != null)
                {
                    Class.IsEnum      = type.CSharpType.IsEnum;
                    Class.IsInterface = type.CSharpType.IsInterface;
                    Class.IsStruct    = type.CSharpType.IsValueType;
                    if (!Class.IsEnum && !Class.IsExtentionClass)
                    {
                        Class.InheritedClass = type.CSharpType.GetFirstInheritedUdonType(typeResolverGroup);
                    }
                    if (!Class.IsExtentionClass)
                    {
                        type.CSharpType.AddTypeInterfaces(Class.Interfaces, typeResolverGroup);
                    }
                    if (Class.IsEnum)
                    {
                        var names = Enum.GetNames(type.CSharpType);
                        foreach (var name in names)
                        {
                            Class.EnumNames.Add(name);
                        }
                    }
                }

                fullNameToClass[dictionaryKey] = Class;

                //Add new class to children
                if (string.IsNullOrEmpty(type.NamespaceName) || Class.IsNamespace)
                {
                    parentlessClasses.Add(Class);
                }
                else
                {
                    if (fullNameToClass.TryGetValue(type.NamespaceName, out var Parent))
                    {
                        if (extensionClass)
                        {
                            extenstionClasses.Add(Class);
                        }
                        else
                        {
                            Parent.Children.Add(Class);
                        }
                    }
                    else
                    {
                        Debug.LogError($"Parent class not created {type.NamespaceName}");
                    }
                }
            }

            if (type.UdonName != null)
            {
                AddMethods(type.StaticMethods, Class, extensionClass, type.FullName);
                AddMethods(type.NonStaticMethods, Class, extensionClass, type.FullName);
            }
        }

        void VisitType(TDType type)
        {
            WriteTypeCode(type);
            foreach (var child in type.Children)
            {
                VisitType(child);
            }
        }

        //Visit tree and build classes/namespaces
        VisitType(root);

        //Required for -nostdlib
        var collections = fullNameToClass["System.Collections"].Methods;

        collections.AppendLine("public interface IEnumerable { }");
        var system = fullNameToClass["System"].Methods;

        system.AppendLine("public abstract class ValueType { }");
        system.AppendLine("public abstract class Enum : ValueType { }");
        system.AppendLine("public class Attribute { }");
        system.AppendLine("public abstract class Delegate { }");
        system.AppendLine("public abstract class MulticastDelegate : Delegate { }");
        system.AppendLine("public struct IntPtr { }");
        system.AppendLine("public struct UIntPtr { }");
        system.AppendLine("public struct RuntimeTypeHandle { }");
        system.AppendLine("public struct RuntimeMethodHandle { }");
        system.AppendLine("public struct RuntimeFieldHandle { }");
        system.AppendLine("public interface IDisposable { }");
        system.AppendLine("public sealed class ParamArrayAttribute : Attribute { }");

        //Write all namespaces out
        var fullCode = new StringBuilder();

        foreach (var Class in parentlessClasses)
        {
            fullCode.AppendLine(Class.GenerateCode());
        }

        foreach (var Class in extenstionClasses)
        {
            fullCode.AppendLine(Class.GenerateCode());
        }

        fullCode.AppendLine(attributeClassString);

        return(fullCode.ToString());
    }