Example #1
0
        public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
        {
            UpdateSyntaxNode(node);

            var variables = node.Declaration.Variables;

            for (int i = 0; i < variables.Count; ++i)
            {
                VariableDeclaratorSyntax variable = variables[i];

                if (variable.Initializer != null)
                {
                    fieldsWithInitializers.Add(node);
                }
            }

            if (node.Modifiers.HasModifier("static"))
            {
                throw new System.NotSupportedException("Static fields are not yet supported by UdonSharp");
            }

            UdonSyncMode fieldSyncMode = GetSyncAttributeValue(node);

            List <System.Attribute> fieldAttributes = GetFieldAttributes(node);

            bool isPublic             = (node.Modifiers.Any(SyntaxKind.PublicKeyword) || fieldAttributes.Find(e => e is SerializeField) != null) && fieldAttributes.Find(e => e is System.NonSerializedAttribute) == null;
            bool isConst              = (node.Modifiers.Any(SyntaxKind.ConstKeyword) || node.Modifiers.Any(SyntaxKind.ReadOnlyKeyword));
            SymbolDeclTypeFlags flags = (isPublic ? SymbolDeclTypeFlags.Public : SymbolDeclTypeFlags.Private) |
                                        (isConst ? SymbolDeclTypeFlags.Readonly : 0);

            List <SymbolDefinition> fieldSymbols = HandleVariableDeclaration(node.Declaration, flags, fieldSyncMode);

            foreach (SymbolDefinition fieldSymbol in fieldSymbols)
            {
                FieldDefinition fieldDefinition = new FieldDefinition(fieldSymbol);
                fieldDefinition.fieldAttributes = fieldAttributes;

                if (fieldSymbol.IsUserDefinedType())
                {
                    System.Type fieldType = fieldSymbol.userCsType;
                    while (fieldType.IsArray)
                    {
                        fieldType = fieldType.GetElementType();
                    }

                    foreach (ClassDefinition classDefinition in visitorContext.externClassDefinitions)
                    {
                        if (classDefinition.userClassType == fieldType)
                        {
                            fieldDefinition.userBehaviourSource = classDefinition.classScript;
                            break;
                        }
                    }
                }

                visitorContext.localFieldDefinitions.Add(fieldSymbol.symbolUniqueName, fieldDefinition);
            }
        }
        // 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));
        }
        public SymbolDefinition CreateUnnamedSymbol(System.Type type, SymbolDeclTypeFlags declType)
        {
            string typeName = resolver.GetUdonTypeName(type);

            if (type.IsArray)
            {
                declType |= SymbolDeclTypeFlags.Array;
            }

            // Not a valid Udon type
            if (typeName == null)
            {
                return(null);
            }

            return(CreateNamedSymbolInternal(typeName, type, declType | SymbolDeclTypeFlags.Internal | (IsGlobalSymbolTable ? 0 : SymbolDeclTypeFlags.Local), false));
        }
Example #4
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);
        }
 public SymbolDefinition CreateNamedSymbol(string symbolName, System.Type symbolType, SymbolDeclTypeFlags declType)
 {
     return(CreateNamedSymbolInternal(symbolName, symbolType, declType));
 }
        /// <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);
        }
        /// <summary>
        /// Tries to find a global constant that already has a given constant value to avoid duplication
        /// </summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <param name="foundSymbol"></param>
        /// <returns></returns>
        public bool TryGetGlobalSymbol(System.Type type, object value, out SymbolDefinition foundSymbol, SymbolDeclTypeFlags flags)
        {
            SymbolTable globalSymTable = GetGlobalSymbolTable();

            foreach (SymbolDefinition definition in globalSymTable.symbolDefinitions)
            {
                bool hasFlags = ((int)flags & (int)definition.declarationType) == (int)flags;

                if (hasFlags &&
                    definition.symbolCsType == type &&
                    ((value == null && definition.symbolDefaultValue == null) ||
                     (definition.symbolDefaultValue != null && definition.symbolDefaultValue.Equals(value))))
                {
                    foundSymbol = definition;
                    return(true);
                }
            }

            foundSymbol = null;
            return(false);
        }
        public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
        {
            UpdateSyntaxNode(node);

            var variables = node.Declaration.Variables;

            for (int i = 0; i < variables.Count; ++i)
            {
                VariableDeclaratorSyntax variable = variables[i];

                if (variable.Initializer != null)
                {
                    fieldsWithInitializers.Add(node);
                }
            }

            if (node.Modifiers.HasModifier("static"))
            {
                throw new System.NotSupportedException("Static fields are not yet supported by UdonSharp");
            }

            UdonSyncMode fieldSyncMode = GetSyncAttributeValue(node);

            List <System.Attribute> fieldAttributes = GetFieldAttributes(node);


            bool isPublic             = (node.Modifiers.Any(SyntaxKind.PublicKeyword) || fieldAttributes.Find(e => e is SerializeField) != null) && fieldAttributes.Find(e => e is System.NonSerializedAttribute) == null;
            bool isConst              = (node.Modifiers.Any(SyntaxKind.ConstKeyword) || node.Modifiers.Any(SyntaxKind.ReadOnlyKeyword));
            SymbolDeclTypeFlags flags = (isPublic ? SymbolDeclTypeFlags.Public : SymbolDeclTypeFlags.Private) |
                                        (isConst ? SymbolDeclTypeFlags.Readonly : 0);

            FieldChangeCallbackAttribute varChange = fieldAttributes.OfType <FieldChangeCallbackAttribute>().FirstOrDefault();

            List <SymbolDefinition> fieldSymbols = HandleVariableDeclaration(node.Declaration, flags, fieldSyncMode);

            foreach (SymbolDefinition fieldSymbol in fieldSymbols)
            {
                FieldDefinition fieldDefinition = new FieldDefinition(fieldSymbol);
                fieldDefinition.fieldAttributes = fieldAttributes;

                if (fieldSymbol.IsUserDefinedType())
                {
                    System.Type fieldType = fieldSymbol.userCsType;
                    while (fieldType.IsArray)
                    {
                        fieldType = fieldType.GetElementType();
                    }

                    foreach (ClassDefinition classDefinition in visitorContext.externClassDefinitions)
                    {
                        if (classDefinition.userClassType == fieldType)
                        {
                            fieldDefinition.userBehaviourSource = classDefinition.classScript;
                            break;
                        }
                    }
                }

                visitorContext.localFieldDefinitions.Add(fieldSymbol.symbolUniqueName, fieldDefinition);

                if (varChange != null)
                {
                    string targetProperty = varChange.CallbackPropertyName;

                    if (variables.Count > 1 || visitorContext.onModifyCallbackFields.ContainsKey(targetProperty))
                    {
                        throw new System.Exception($"Only one field may target property '{targetProperty}'");
                    }

                    PropertyDefinition foundProperty = visitorContext.definedProperties.FirstOrDefault(e => e.originalPropertyName == targetProperty);

                    if (foundProperty == null)
                    {
                        throw new System.ArgumentException($"Invalid target property for {nameof(FieldChangeCallbackAttribute)} on {node.Declaration}");
                    }

                    PropertyDefinition property = visitorContext.definedProperties.FirstOrDefault(e => e.originalPropertyName == targetProperty);
                    if (property == null)
                    {
                        throw new System.ArgumentException($"Property not found for '{targetProperty}'");
                    }

                    if (property.type != fieldDefinition.fieldSymbol.userCsType)
                    {
                        throw new System.Exception($"Types must match between property and variable change field");
                    }

                    visitorContext.onModifyCallbackFields.Add(targetProperty, fieldDefinition);
                }
            }
        }