// Automatically infers the name of the symbol based on its type
        // Used for intermediate values usually
        public SymbolDefinition CreateUnnamedSymbol(string symbolType, SymbolDeclTypeFlags declType)
        {
            System.Type resolvedType = resolver.ResolveExternType(symbolType);
            if (declType.HasFlag(SymbolDeclTypeFlags.Array))
            {
                resolvedType = resolvedType.MakeArrayType();
            }

            return(CreateUnnamedSymbol(resolvedType, declType));
        }
Example #2
0
        protected List <SymbolDefinition> HandleVariableDeclaration(VariableDeclarationSyntax node, SymbolDeclTypeFlags symbolType, UdonSyncMode syncMode)
        {
            UpdateSyntaxNode(node);

            bool isVar = node.Type.IsVar;

            System.Type variableType = null;

            if (!isVar)
            {
                using (ExpressionCaptureScope typeCapture = new ExpressionCaptureScope(visitorContext, null))
                {
                    Visit(node.Type);

                    if (!typeCapture.IsType())
                    {
                        throw new System.Exception($"The type or namespace name '{typeCapture.unresolvedAccessChain}' could not be found (are you missing a using directive?)");
                    }

                    variableType = typeCapture.captureType;
                }
            }

            List <SymbolDefinition> newSymbols = new List <SymbolDefinition>();

            foreach (VariableDeclaratorSyntax variableDeclarator in node.Variables)
            {
                SymbolDefinition newSymbol = null;

                string variableName = variableDeclarator.Identifier.ValueText;

                using (ExpressionCaptureScope symbolCreationScope = new ExpressionCaptureScope(visitorContext, null))
                {
                    if (!isVar)
                    {
                        newSymbol = visitorContext.topTable.CreateNamedSymbol(variableDeclarator.Identifier.ValueText, variableType, symbolType);
                    }

                    // Run the initializer if it exists
                    // Todo: Run the set on the new symbol scope from within the initializer scope for direct setting
                    if (variableDeclarator.Initializer != null && symbolType.HasFlag(SymbolDeclTypeFlags.Local))
                    {
                        using (ExpressionCaptureScope initializerCapture = new ExpressionCaptureScope(visitorContext, null, newSymbol))
                        {
                            Visit(variableDeclarator.Initializer);

                            if (newSymbol == null)
                            {
                                // TODO: Find a way to determine the return type before generating initializer code, to avoid a copy on 'var' local initializers
                                variableType = initializerCapture.GetReturnType(true);
                                newSymbol    = visitorContext.topTable.CreateNamedSymbol(variableDeclarator.Identifier.ValueText, variableType, symbolType);
                            }

                            symbolCreationScope.SetToLocalSymbol(newSymbol);
                            symbolCreationScope.ExecuteSet(initializerCapture.ExecuteGet());
                        }
                    }

                    newSymbol.syncMode = syncMode;
                }

                VerifySyncValidForType(newSymbol.symbolCsType, syncMode);
                newSymbols.Add(newSymbol);
            }

            if (!visitorContext.resolverContext.IsValidUdonType(variableType))
            {
                throw new System.NotSupportedException($"Udon does not support variables of type '{variableType.Name}' yet");
            }

            return(newSymbols);
        }
        /// <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.Public) && !declType.HasFlag(SymbolDeclTypeFlags.Private))
            {
                if (appendType)
                {
                    string sanitizedName = resolver.SanitizeTypeName(resolvedSymbolType.Name);
                    uniqueSymbolName += $"_{sanitizedName}";
                }

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

            System.Type typeForName = resolvedSymbolType;
            if (resolvedSymbolType == typeof(UdonSharpBehaviour) || resolvedSymbolType.IsSubclassOf(typeof(UdonSharpBehaviour)))
            {
                typeForName = typeof(VRC.Udon.UdonBehaviour);
            }
            else if (resolvedSymbolType.IsArray && (resolvedSymbolType.GetElementType() == typeof(UdonSharpBehaviour) || resolvedSymbolType.GetElementType().IsSubclassOf(typeof(UdonSharpBehaviour))))
            {
                typeForName = typeof(Component[]); // Hack because VRC doesn't expose UdonBehaviour array type
            }
            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);
        }