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