Skip to content
This repository has been archived by the owner on Jan 22, 2022. It is now read-only.

Add user property declarations support #85

Merged
merged 14 commits into from
Jun 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ public static Formatter<T> GetFormatter<T>() where T : UdonSharpBehaviour

foreach (FieldInfo field in allFields)
{
if (field.IsDefined(typeof(CompilerGeneratedAttribute), false))
continue;

if ((field.IsPublic && field.GetAttribute<System.NonSerializedAttribute>() == null) ||
(!field.IsPublic && field.GetAttribute<SerializeField>() != null))
{
Expand Down
239 changes: 234 additions & 5 deletions Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class ASTVisitorContext

public List<MethodDefinition> definedMethods;

public List<PropertyDefinition> definedProperties;

// Tracking labels for the current function and flow control
public JumpLabel returnLabel = null;
public SymbolDefinition returnJumpTarget = null;
Expand Down Expand Up @@ -118,11 +120,12 @@ public SymbolDefinition requestedDestination
/// </summary>
public class ASTVisitor : UdonSharpSyntaxWalker
{
public ASTVisitor(ResolverContext resolver, SymbolTable rootTable, LabelTable labelTable, List<MethodDefinition> methodDefinitions, List<ClassDefinition> externUserClassDefinitions, ClassDebugInfo debugInfo)
public ASTVisitor(ResolverContext resolver, SymbolTable rootTable, LabelTable labelTable, List<MethodDefinition> methodDefinitions, List<PropertyDefinition> propertyDefinitions, List<ClassDefinition> externUserClassDefinitions, ClassDebugInfo debugInfo)
: base(resolver, rootTable, labelTable, debugInfo)
{
visitorContext.returnJumpTarget = rootTable.CreateNamedSymbol("returnTarget", typeof(uint), SymbolDeclTypeFlags.Internal);
visitorContext.definedMethods = methodDefinitions;
visitorContext.definedProperties = propertyDefinitions;
visitorContext.externClassDefinitions = externUserClassDefinitions;
}

Expand Down Expand Up @@ -365,7 +368,233 @@ public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
{
UpdateSyntaxNode(node);

throw new System.NotSupportedException("User property declarations are not yet supported by UdonSharp");
System.Type propertyType = null;

using (ExpressionCaptureScope propertyTypeScope = new ExpressionCaptureScope(visitorContext, null))
{
Visit(node.Type);
propertyType = propertyTypeScope.captureType;
}

if (node.Modifiers.HasModifier("static"))
throw new System.NotSupportedException("UdonSharp does not currently support static user-defined property declarations");

if (node.Initializer != null)
throw new System.NotSupportedException("UdonSharp does not currently support initializers on properties.");

PropertyDefinition definition = visitorContext.definedProperties.Where(e => e.originalPropertyName == node.Identifier.ValueText).First();

if (definition.getter != null)
{
var getter = definition.getter;

if ((node.Modifiers.HasModifier("public") && getter.declarationFlags == PropertyDeclFlags.None) || getter.declarationFlags == PropertyDeclFlags.Public)
{
visitorContext.uasmBuilder.AppendLine($".export {getter.accessorName}", 1);
visitorContext.uasmBuilder.AppendLine("");
}

visitorContext.uasmBuilder.AppendLine($"{getter.accessorName}:", 1);
visitorContext.uasmBuilder.AppendLine("");

Debug.Assert(visitorContext.returnLabel == null, "Return label must be null");
var returnLabel = visitorContext.labelTable.GetNewJumpLabel("return");
visitorContext.returnLabel = returnLabel;
visitorContext.returnSymbol = getter.returnSymbol;

visitorContext.uasmBuilder.AddJumpLabel(getter.entryPoint);

SymbolDefinition constEndAddrVal = visitorContext.topTable.CreateConstSymbol(typeof(uint), 0xFFFFFFFF);
visitorContext.uasmBuilder.AddPush(constEndAddrVal);
visitorContext.uasmBuilder.AddJumpLabel(getter.userCallStart);

if (!visitorContext.topTable.IsGlobalSymbolTable)
throw new System.Exception("Parent symbol table for property table must be the global symbol table");

var getterNode = node.AccessorList?.Accessors.First(accessor => accessor.Keyword.Kind() == SyntaxKind.GetKeyword);
if (getterNode == null)
{
using (ExpressionCaptureScope expressionBodyCapture = new ExpressionCaptureScope(visitorContext, null))
{
Visit(node.ExpressionBody);

if (visitorContext.returnSymbol != null)
{
SymbolDefinition returnValue = expressionBodyCapture.ExecuteGet();

using (ExpressionCaptureScope returnSetterScope = new ExpressionCaptureScope(visitorContext, null))
{
returnSetterScope.SetToLocalSymbol(visitorContext.returnSymbol);
returnSetterScope.ExecuteSet(returnValue);
}

if (visitorContext.requiresVRCReturn)
{
SymbolTable globalSymbolTable = visitorContext.topTable.GetGlobalSymbolTable();
SymbolDefinition autoAssignedEventSymbol = globalSymbolTable.FindUserDefinedSymbol("__returnValue");

if (autoAssignedEventSymbol == null)
autoAssignedEventSymbol = globalSymbolTable.CreateNamedSymbol("__returnValue", typeof(System.Object), SymbolDeclTypeFlags.Private | SymbolDeclTypeFlags.BuiltinVar);

using (ExpressionCaptureScope returnValueSetMethod = new ExpressionCaptureScope(visitorContext, null))
{
returnValueSetMethod.SetToLocalSymbol(autoAssignedEventSymbol);
returnValueSetMethod.ExecuteSet(returnValue);
}
}
}
}
}
else if (getterNode.Body != null)
{
Visit(getterNode.Body);
}
else if (getterNode.ExpressionBody != null)
{
using (ExpressionCaptureScope expressionBodyCapture = new ExpressionCaptureScope(visitorContext, null))
{
Visit(getterNode.ExpressionBody);

if (visitorContext.returnSymbol != null)
{
SymbolDefinition returnValue = expressionBodyCapture.ExecuteGet();

using (ExpressionCaptureScope returnSetterScope = new ExpressionCaptureScope(visitorContext, null))
{
returnSetterScope.SetToLocalSymbol(visitorContext.returnSymbol);
returnSetterScope.ExecuteSet(returnValue);
}

if (visitorContext.requiresVRCReturn)
{
SymbolTable globalSymbolTable = visitorContext.topTable.GetGlobalSymbolTable();
SymbolDefinition autoAssignedEventSymbol = globalSymbolTable.FindUserDefinedSymbol("__returnValue");

if (autoAssignedEventSymbol == null)
autoAssignedEventSymbol = globalSymbolTable.CreateNamedSymbol("__returnValue", typeof(System.Object), SymbolDeclTypeFlags.Private | SymbolDeclTypeFlags.BuiltinVar);

using (ExpressionCaptureScope returnValueSetMethod = new ExpressionCaptureScope(visitorContext, null))
{
returnValueSetMethod.SetToLocalSymbol(autoAssignedEventSymbol);
returnValueSetMethod.ExecuteSet(returnValue);
}
}
}
}
}
else if (getterNode.Body == null)
{
SymbolTable backingField = new SymbolTable(visitorContext.resolverContext, visitorContext.topTable);
backingField.symbolDefinitions.Add(getter.backingField.fieldSymbol);
visitorContext.PushTable(backingField);

SymbolDefinition returnValue = getter.backingField.fieldSymbol;

using (ExpressionCaptureScope returnSetterScope = new ExpressionCaptureScope(visitorContext, null))
{
returnSetterScope.SetToLocalSymbol(visitorContext.returnSymbol);
returnSetterScope.ExecuteSet(returnValue);
}

if (visitorContext.requiresVRCReturn)
{
SymbolTable globalSymbolTable = visitorContext.topTable.GetGlobalSymbolTable();
SymbolDefinition autoAssignedEventSymbol = globalSymbolTable.FindUserDefinedSymbol("__returnValue");

if (autoAssignedEventSymbol == null)
autoAssignedEventSymbol = globalSymbolTable.CreateNamedSymbol("__returnValue", typeof(System.Object), SymbolDeclTypeFlags.Private | SymbolDeclTypeFlags.BuiltinVar);

using (ExpressionCaptureScope returnValueSetMethod = new ExpressionCaptureScope(visitorContext, null))
{
returnValueSetMethod.SetToLocalSymbol(autoAssignedEventSymbol);
returnValueSetMethod.ExecuteSet(returnValue);
}
}


visitorContext.topTable.FlattenTableCountersToGlobal();
visitorContext.PopTable();
}

visitorContext.topTable.FlattenTableCountersToGlobal();

visitorContext.uasmBuilder.AddJumpLabel(returnLabel);
visitorContext.uasmBuilder.AddJumpLabel(getter.returnPoint);
visitorContext.uasmBuilder.AddReturnSequence(visitorContext.returnJumpTarget, "Property epilogue");

visitorContext.uasmBuilder.AppendLine("");

visitorContext.returnLabel = null;
}

if (definition.setter != null)
{
var setter = definition.setter;

if ((node.Modifiers.HasModifier("public") && setter.declarationFlags == PropertyDeclFlags.None) || setter.declarationFlags == PropertyDeclFlags.Public)
{
visitorContext.uasmBuilder.AppendLine($".export {setter.accessorName}", 1);
visitorContext.uasmBuilder.AppendLine("");
}

visitorContext.uasmBuilder.AppendLine($"{setter.accessorName}:", 1);
visitorContext.uasmBuilder.AppendLine("");

Debug.Assert(visitorContext.returnLabel == null, "Return label must be null");
var returnLabel = visitorContext.labelTable.GetNewJumpLabel("return");
visitorContext.returnLabel = returnLabel;

visitorContext.uasmBuilder.AddJumpLabel(setter.entryPoint);

SymbolDefinition constEndAddrVal = visitorContext.topTable.CreateConstSymbol(typeof(uint), 0xFFFFFFFF);
visitorContext.uasmBuilder.AddPush(constEndAddrVal);
visitorContext.uasmBuilder.AddJumpLabel(setter.userCallStart);

if (!visitorContext.topTable.IsGlobalSymbolTable)
throw new System.Exception("Parent symbol table for property table must be the global symbol table");

SymbolTable functionSymbolTable = new SymbolTable(visitorContext.resolverContext, visitorContext.topTable);
functionSymbolTable.symbolDefinitions.Add(setter.paramSymbol);

visitorContext.PushTable(functionSymbolTable);

var setterNode = node.AccessorList?.Accessors.First(accessor => accessor.Keyword.Kind() == SyntaxKind.SetKeyword);
if (setterNode.Body != null)
{
Visit(setterNode.Body);
}
else if (setterNode.ExpressionBody != null)
{
Visit(setterNode.ExpressionBody);
}
else
{
SymbolTable backingField = new SymbolTable(visitorContext.resolverContext, visitorContext.topTable);
backingField.symbolDefinitions.Add(setter.backingField.fieldSymbol);
visitorContext.PushTable(backingField);

// <Property>_k_BackingField = value;
visitorContext.uasmBuilder.AddPush(setter.paramSymbol);
visitorContext.uasmBuilder.AddPush(setter.backingField.fieldSymbol);
visitorContext.uasmBuilder.AddCopy();

visitorContext.topTable.FlattenTableCountersToGlobal();
visitorContext.PopTable();
}

visitorContext.topTable.FlattenTableCountersToGlobal();
visitorContext.PopTable();

visitorContext.uasmBuilder.AddJumpLabel(returnLabel);
visitorContext.uasmBuilder.AddJumpLabel(setter.returnPoint);
visitorContext.uasmBuilder.AddReturnSequence(visitorContext.returnJumpTarget, "Property epilogue");

visitorContext.uasmBuilder.AppendLine("");

visitorContext.returnLabel = null;
}

// throw new System.NotSupportedException("User property declarations are not yet supported by UdonSharp");
}

public override void VisitBaseExpression(BaseExpressionSyntax node)
Expand Down Expand Up @@ -979,7 +1208,7 @@ public override void VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node
operatorMethodCapture.SetToMethods(operatorMethods.ToArray());

SymbolDefinition valueConstant = visitorContext.topTable.CreateConstSymbol(operandCapture.GetReturnType(), System.Convert.ChangeType(1, operandCapture.GetReturnType()));

try
{
resultSymbol = operatorMethodCapture.Invoke(new SymbolDefinition[] { operandCapture.ExecuteGet(), valueConstant });
Expand All @@ -992,7 +1221,7 @@ public override void VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node
}

if (topScope != null)
topScope.SetToLocalSymbol(operandCapture.ExecuteGet());
topScope.SetToLocalSymbol(resultSymbol);
}
}
}
Expand Down Expand Up @@ -1043,7 +1272,7 @@ public override void VisitPostfixUnaryExpression(PostfixUnaryExpressionSyntax no

SymbolDefinition valueConstant = visitorContext.topTable.CreateConstSymbol(operandCapture.GetReturnType(), System.Convert.ChangeType(1, operandCapture.GetReturnType()));

SymbolDefinition resultSymbol = operatorMethodCapture.Invoke(new SymbolDefinition[] { operandCapture.ExecuteGet(), valueConstant });
SymbolDefinition resultSymbol = operatorMethodCapture.Invoke(new SymbolDefinition[] { preIncrementStore, valueConstant });

operandCapture.ExecuteSet(resultSymbol, true);
}
Expand Down
1 change: 1 addition & 0 deletions Assets/UdonSharp/Editor/UdonSharpClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,6 @@ public class ClassDefinition

public List<FieldDefinition> fieldDefinitions = new List<FieldDefinition>();
public List<MethodDefinition> methodDefinitions = new List<MethodDefinition>();
public List<PropertyDefinition> propertyDefinitions = new List<PropertyDefinition>();
}
}
15 changes: 15 additions & 0 deletions Assets/UdonSharp/Editor/UdonSharpClassVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ public class ClassVisitor : UdonSharpSyntaxWalker
{
public ClassDefinition classDefinition { get; private set; }
private MethodVisitor methodVisitor;
private PropertyVisitor propertyVisitor;

private int classCount = 0;

public ClassVisitor(ResolverContext resolver, SymbolTable rootTable, LabelTable labelTable)
: base(UdonSharpSyntaxWalkerDepth.ClassDefinitions, resolver, rootTable, labelTable)
{
methodVisitor = new MethodVisitor(resolver, rootTable, labelTable);
propertyVisitor = new PropertyVisitor(resolver, rootTable, labelTable);

classDefinition = new ClassDefinition();
}
Expand All @@ -39,6 +41,19 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node)

classDefinition.methodDefinitions = methodVisitor.definedMethods;

try
{
propertyVisitor.Visit(node);
}
catch (System.Exception e)
{
visitorContext.currentNode = propertyVisitor.visitorContext.currentNode;

throw e;
}

classDefinition.propertyDefinitions = propertyVisitor.definedProperties;

if (classCount == 0)
throw new System.Exception($"No UdonSharpBehaviour class found in script file, you must define an UdonSharpBehaviour class in a script referenced by an UdonSharpProgramAsset");
}
Expand Down
20 changes: 19 additions & 1 deletion Assets/UdonSharp/Editor/UdonSharpCompilationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,25 @@ public CompileTaskResult Compile(List<ClassDefinition> classDefinitions, Microso
if (ErrorCount > 0)
return result;

ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, classDefinitions, debugInfo);
PropertyVisitor propertyVisitor = new PropertyVisitor(resolver, moduleSymbols, moduleLabels);

try
{
propertyVisitor.Visit(syntaxTree.GetRoot());
}
catch (System.Exception e)
{
LogException(result, e, propertyVisitor.visitorContext.currentNode, out string logMessage);

programAsset.compileErrors.Add(logMessage);

ErrorCount++;
}

if (ErrorCount > 0)
return result;

ASTVisitor visitor = new ASTVisitor(resolver, moduleSymbols, moduleLabels, methodVisitor.definedMethods, propertyVisitor.definedProperties, classDefinitions, debugInfo);

try
{
Expand Down
Loading