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

Commit

Permalink
Add support for user type ID's and GetComponent(s)<T> on user types
Browse files Browse the repository at this point in the history
- Add support for getting user type ID's using GetUdonTypeID() on objects
- Add support for getting user type names using GetUdonTypeName() on objects
- Add support for GetComponent<T> variants on user defined types
- Add support for GetComponents<T> variants on user defined types

Fixes:
- Add implicit cast to unary negation operator to convert the input type to bool if it isn't
- Add symbol scope to `for` loop to reuse increment symbols when possible and avoid collisions on incrementor symbol names
  • Loading branch information
MerlinVR committed Mar 11, 2020
1 parent a1f6844 commit 068cc49
Show file tree
Hide file tree
Showing 11 changed files with 1,030 additions and 95 deletions.
99 changes: 35 additions & 64 deletions Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class ASTVisitor : CSharpSyntaxWalker
private Stack<string> namespaceStack = new Stack<string>();

public ASTVisitor(ResolverContext resolver, SymbolTable rootTable, LabelTable labelTable, List<MethodDefinition> methodDefinitions, List<ClassDefinition> externUserClassDefinitions)
:base(SyntaxWalkerDepth.Node)
: base(SyntaxWalkerDepth.Node)
{
visitorContext = new ASTVisitorContext(resolver, rootTable, labelTable);
visitorContext.returnJumpTarget = rootTable.CreateNamedSymbol("returnTarget", typeof(uint), SymbolDeclTypeFlags.Internal);
Expand Down Expand Up @@ -168,7 +168,7 @@ public override void VisitCompilationUnit(CompilationUnitSyntax node)
public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
{
UpdateSyntaxNode(node);

namespaceStack.Push(node.Name.ToFullString().TrimEnd('\r', '\n', ' '));

foreach (UsingDirectiveSyntax usingDirective in node.Usings)
Expand Down Expand Up @@ -231,6 +231,9 @@ public override void VisitClassDeclaration(ClassDeclarationSyntax node)
visitorContext.behaviourUserType = selfTypeCaptureScope.captureType;
}

visitorContext.topTable.CreateReflectionSymbol("udonTypeID", typeof(long), Internal.UdonSharpInternalUtility.GetTypeID(visitorContext.behaviourUserType));
visitorContext.topTable.CreateReflectionSymbol("udonTypeName", typeof(string), Internal.UdonSharpInternalUtility.GetTypeName(visitorContext.behaviourUserType));

visitorContext.uasmBuilder.AppendLine(".code_start", 0);

foreach (MemberDeclarationSyntax member in node.Members)
Expand Down Expand Up @@ -367,7 +370,7 @@ public override void VisitArrayCreationExpression(ArrayCreationExpressionSyntax
{
SymbolDefinition arraySymbol = visitorContext.topTable.CreateUnnamedSymbol(arrayType, SymbolDeclTypeFlags.Internal);
varCaptureScope.SetToLocalSymbol(arraySymbol);

if (node.Type.RankSpecifiers.Count != 1)
throw new System.NotSupportedException("UdonSharp does not support multidimensional or jagged arrays at the moment");

Expand Down Expand Up @@ -433,8 +436,8 @@ private UdonSyncMode GetSyncAttributeValue(FieldDeclarationSyntax node)
if (attributeTypeCapture.captureType != typeof(UdonSyncedAttribute))
continue;

if (attribute.ArgumentList == null ||
attribute.ArgumentList.Arguments == null ||
if (attribute.ArgumentList == null ||
attribute.ArgumentList.Arguments == null ||
attribute.ArgumentList.Arguments.Count == 0)
{
syncMode = UdonSyncMode.None;
Expand Down Expand Up @@ -533,7 +536,7 @@ public override void VisitFieldDeclaration(FieldDeclarationSyntax node)

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

bool isPublic = node.Modifiers.HasModifier("public");

UdonSyncMode fieldSyncMode = GetSyncAttributeValue(node);
Expand Down Expand Up @@ -608,7 +611,7 @@ public SymbolDefinition HandleVariableDeclaration(VariableDeclarationSyntax node
variableType = initializerCapture.GetReturnType();

newSymbol = visitorContext.topTable.CreateNamedSymbol(variableName, variableType, symbolType);

symbolCreationScope.SetToLocalSymbol(newSymbol);
createdSymbol = true;
}
Expand All @@ -634,14 +637,14 @@ public SymbolDefinition HandleVariableDeclaration(VariableDeclarationSyntax node
}
}
}

string udonTypeName = visitorContext.resolverContext.GetUdonTypeName(variableType);

if (newSymbol != null)
VerifySyncValidForType(newSymbol.symbolCsType, syncMode);

bool isUserDefinedType = variableType == typeof(UdonSharpBehaviour) ||
variableType.IsSubclassOf(typeof(UdonSharpBehaviour)) ||
bool isUserDefinedType = variableType == typeof(UdonSharpBehaviour) ||
variableType.IsSubclassOf(typeof(UdonSharpBehaviour)) ||
(variableType.IsArray && (variableType.GetElementType().IsSubclassOf(typeof(UdonSharpBehaviour)) || variableType.GetElementType() == typeof(UdonSharpBehaviour)));

if (!visitorContext.resolverContext.ValidateUdonTypeName(udonTypeName, UdonReferenceType.Variable) &&
Expand Down Expand Up @@ -832,8 +835,13 @@ public override void VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node
case SyntaxKind.PreIncrementExpression:
case SyntaxKind.MinusMinusToken:
case SyntaxKind.PreDecrementExpression:
operatorMethods.AddRange(GetOperators(operandCapture.GetReturnType(), node.OperatorToken.Kind()));
break;
case SyntaxKind.ExclamationToken:
operatorMethods.AddRange(GetOperators(operandCapture.GetReturnType(), node.OperatorToken.Kind()));

if (operandCapture.GetReturnType() != typeof(bool))
operatorMethods.AddRange(GetOperators(typeof(bool), node.OperatorToken.Kind()));
break;
case SyntaxKind.MinusToken:
operatorMethods.AddRange(GetOperators(operandCapture.GetReturnType(), node.OperatorToken.Kind()));
Expand All @@ -858,6 +866,11 @@ public override void VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node
{
SymbolDefinition operandResult = operandCapture.ExecuteGet();

if (operatorType == BuiltinOperatorType.UnaryNegation &&
operandResult.symbolCsType != typeof(bool) &&
operatorMethods.Count == 1) // If the count isn't 1 it means we found an override for `!` for the specific type so we skip attempting the implicit cast
operandResult = HandleImplicitBoolCast(operandResult);

resultSymbol = operatorMethodCapture.Invoke(new SymbolDefinition[] { operandResult });

if (topScope != null)
Expand Down Expand Up @@ -986,6 +999,9 @@ public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
string functionName = node.Identifier.ValueText;
bool isBuiltinEvent = visitorContext.resolverContext.ReplaceInternalEventName(ref functionName);

if (functionName == "Awake")
throw new System.NotSupportedException("Udon does not support the 'Awake' event, use 'Start' instead");

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

Expand Down Expand Up @@ -1076,60 +1092,10 @@ public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax no
Visit(node.Expression);
Visit(node.Name);
}

private readonly HashSet<System.Type> builtinTypes = new HashSet<System.Type>
{
typeof(string),
typeof(bool),
typeof(byte),
typeof(sbyte),
typeof(char),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
typeof(object),
};

private MethodInfo[] GetOperators(System.Type type, BuiltinOperatorType builtinOperatorType)
{
List<MethodInfo> foundOperators = new List<MethodInfo>();

// If it's a builtin type then create a fake operator methodinfo for it.
// If this operator doesn't actually exist, it will get filtered by the overload finding
if (builtinTypes.Contains(type))
foundOperators.Add(new OperatorMethodInfo(type, builtinOperatorType));

// Now look for operators that the type defines
string operatorName = System.Enum.GetName(typeof(BuiltinOperatorType), builtinOperatorType);
if (builtinOperatorType == BuiltinOperatorType.Multiplication)
operatorName = "Multiply"; // Udon breaks standard naming with its multiplication overrides on base types
else if (builtinOperatorType == BuiltinOperatorType.UnaryMinus)
operatorName = "UnaryNegation";

operatorName = $"op_{operatorName}";

System.Type currentType = type;

while (currentType != null)
{
foundOperators.AddRange(currentType.GetMethods(BindingFlags.Public | BindingFlags.Static).Where(e => e.Name == operatorName));
currentType = currentType.BaseType;
}

// Add the object equality and inequality operators if we haven't already found better matches
if (foundOperators.Count == 0 && type != typeof(object) && !type.IsValueType &&
(builtinOperatorType == BuiltinOperatorType.Equality || builtinOperatorType == BuiltinOperatorType.Inequality))
foundOperators.AddRange(GetOperators(typeof(object), builtinOperatorType));
else if (foundOperators.Count == 0 && type.IsEnum && (builtinOperatorType == BuiltinOperatorType.Equality || builtinOperatorType == BuiltinOperatorType.Inequality)) // Handle enum comparisons
foundOperators.Add(typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static));

return foundOperators.ToArray();

private static MethodInfo[] GetOperators(System.Type type, BuiltinOperatorType builtinOperatorType)
{
return UdonSharpUtils.GetOperators(type, builtinOperatorType);
}

private MethodInfo[] GetImplicitHigherPrecisionOperator(System.Type lhsType, System.Type rhsType, BuiltinOperatorType builtinOperatorType, bool isAssignment = false)
Expand Down Expand Up @@ -1740,6 +1706,9 @@ public override void VisitForStatement(ForStatementSyntax node)
{
UpdateSyntaxNode(node);

SymbolTable forLoopSymbolTable = new SymbolTable(visitorContext.resolverContext, visitorContext.topTable);
visitorContext.PushTable(forLoopSymbolTable);

Visit(node.Declaration);

foreach (ExpressionSyntax initializer in node.Initializers)
Expand Down Expand Up @@ -1778,6 +1747,8 @@ public override void VisitForStatement(ForStatementSyntax node)
visitorContext.uasmBuilder.AddJump(forLoopStart);

visitorContext.uasmBuilder.AddJumpLabel(forLoopEnd);

visitorContext.PopTable();
}

public override void VisitForEachStatement(ForEachStatementSyntax node)
Expand Down
6 changes: 6 additions & 0 deletions Assets/UdonSharp/Editor/UdonSharpAssemblyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ public void AddJumpToExit()
programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP");
}

public void AddJumpIfFalse(JumpLabel jumpTarget, SymbolDefinition conditionSymbol, string comment = "")
{
AddPush(conditionSymbol);
AddJumpIfFalse(jumpTarget, comment);
}

public void AddJumpIfFalse(JumpLabel jumpTarget, string comment = "")
{
if (jumpTarget.IsResolved)
Expand Down
1 change: 1 addition & 0 deletions Assets/UdonSharp/Editor/UdonSharpCompilationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ private string BuildHeapDataBlock()
foreach (SymbolDefinition symbol in moduleSymbols.GetAllUniqueChildSymbols()
.OrderBy(e => e.declarationType.HasFlag(SymbolDeclTypeFlags.Public))
.ThenBy(e => e.declarationType.HasFlag(SymbolDeclTypeFlags.Private))
.ThenBy(e => e.declarationType.HasFlag(SymbolDeclTypeFlags.Reflection))
.ThenBy(e => e.declarationType.HasFlag(SymbolDeclTypeFlags.This))
.ThenBy(e => !e.declarationType.HasFlag(SymbolDeclTypeFlags.Internal))
.ThenBy(e => e.declarationType.HasFlag(SymbolDeclTypeFlags.Constant))
Expand Down
Loading

0 comments on commit 068cc49

Please sign in to comment.