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

Commit

Permalink
Merge pull request #85 from mika-f/natsuneko/support-user-defined-pro…
Browse files Browse the repository at this point in the history
…perties

Add user property declarations support
  • Loading branch information
MerlinVR authored Jun 14, 2021
2 parents 44b47e8 + 0790f21 commit 1bdf1f6
Show file tree
Hide file tree
Showing 17 changed files with 1,268 additions and 10 deletions.
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

0 comments on commit 1bdf1f6

Please sign in to comment.