public override BoundNode VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node) { MethodSymbol unaryMethodSymbol = (MethodSymbol)GetSymbol(node); BoundExpression expression = VisitExpression(node.Operand, unaryMethodSymbol.Parameters[0].Type); // + operator is a no-op on builtins so ignore it until we allow user operator overloads if (node.OperatorToken.Kind() == SyntaxKind.PlusToken) { return(expression); } BoundExpression constantResult = ConstantExpressionOptimizer.FoldConstantUnaryPrefixExpression(Context, node, unaryMethodSymbol, expression); if (constantResult != null) { return(constantResult); } if (node.Kind() == SyntaxKind.PreIncrementExpression || node.Kind() == SyntaxKind.PreDecrementExpression) { return(new BoundInvocationExpression.BoundPrefixOperatorExpression(Context, node, (BoundAccessExpression)expression, unaryMethodSymbol)); } return(BoundInvocationExpression.CreateBoundInvocation(Context, node, unaryMethodSymbol, null, new[] { expression })); }
public override BoundNode VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) { if (node.Initializer != null) { throw new NotSupportedException(LocStr.CE_InitializerListsNotSupported, node); } MethodSymbol constructorSymbol = (MethodSymbol)GetSymbol(node); BoundExpression[] boundArguments = new BoundExpression[node.ArgumentList.Arguments.Count]; bool isConstant = true; for (int i = 0; i < boundArguments.Length; ++i) { boundArguments[i] = VisitExpression(node.ArgumentList.Arguments[i].Expression, constructorSymbol.Parameters[i].Type); isConstant &= boundArguments[i].IsConstant; } // Constant folding on struct creation when possible // Also implicitly handles parameterless constructors on value types, which Udon does not expose constructors for if (isConstant && constructorSymbol.IsExtern && constructorSymbol.ContainingType.IsValueType) { var constArgs = boundArguments.Select(e => e.ConstantValue.Value).ToArray(); object constantValue = Activator.CreateInstance(constructorSymbol.ContainingType.UdonType.SystemType, constArgs); IConstantValue constantStore = (IConstantValue)Activator.CreateInstance(typeof(ConstantValue <>).MakeGenericType(constantValue.GetType()), constantValue); return(new BoundConstantExpression(constantStore, constructorSymbol.ContainingType, node)); } return(BoundInvocationExpression.CreateBoundInvocation(Context, node, constructorSymbol, null, boundArguments)); }
public override BoundNode VisitAssignmentExpression(AssignmentExpressionSyntax node) { BoundAccessExpression assignmentTarget = VisitAccessExpression(node.Left); if (node.Kind() != SyntaxKind.SimpleAssignmentExpression) { MethodSymbol operatorSymbol = (MethodSymbol)GetSymbol(node); BoundExpression rhsExpression = VisitExpression(node.Right); // Apparently Roslyn returns string + string for string += char, but returns string + object for string + char /shrug // Do ToString here if the constant folding can't convert the char if (assignmentTarget.ValueType == Context.GetTypeSymbol(SpecialType.System_String) && rhsExpression.ValueType == Context.GetTypeSymbol(SpecialType.System_Char)) { if (rhsExpression.IsConstant) { rhsExpression = new BoundConstantExpression(rhsExpression.ConstantValue.Value.ToString(), Context.GetTypeSymbol(SpecialType.System_String)); } else { rhsExpression = BoundInvocationExpression.CreateBoundInvocation(Context, node, Context.GetTypeSymbol(SpecialType.System_Char).GetMember <MethodSymbol>("ToString", Context), rhsExpression, Array.Empty <BoundExpression>()); } } if (operatorSymbol is ExternBuiltinOperatorSymbol builtinOperatorSymbol) { operatorSymbol = new ExternSynthesizedOperatorSymbol(builtinOperatorSymbol.OperatorType, assignmentTarget.ValueType, Context); } return(BoundInvocationExpression.CreateBoundInvocation(Context, node, operatorSymbol, null, new[] { assignmentTarget, ConvertExpression(node, rhsExpression, operatorSymbol.Parameters[1].Type) })); } return(new BoundAssignmentExpression(node, assignmentTarget, VisitExpression(node.Right, assignmentTarget.ValueType))); }
public override BoundNode VisitBinaryExpression(BinaryExpressionSyntax node) { if (node.Kind() == SyntaxKind.LogicalOrExpression || node.Kind() == SyntaxKind.LogicalAndExpression) { return(HandleShortCircuitOperator(node)); } if (node.Kind() == SyntaxKind.CoalesceExpression) { return(HandleNullCoalescingExpression(node)); } MethodSymbol binaryMethodSymbol = (MethodSymbol)GetSymbol(node); BoundExpression lhs = VisitExpression(node.Left, binaryMethodSymbol.Parameters[0].Type); BoundExpression rhs = VisitExpression(node.Right, binaryMethodSymbol.Parameters[1].Type); BoundExpression constantResult = ConstantExpressionOptimizer.FoldConstantBinaryExpression(Context, node, binaryMethodSymbol, lhs, rhs); if (constantResult != null) { return(constantResult); } return(BoundInvocationExpression.CreateBoundInvocation(Context, node, binaryMethodSymbol, null, new[] { lhs, rhs })); }
public override Value EmitSet(EmitContext context, BoundExpression valueExpression) { if (SourceExpression == null || SourceExpression.IsThis) { return(context.EmitValueAssignment(context.GetUserValue(Field), valueExpression)); } if (Field.HasAttribute <FieldChangeCallbackAttribute>()) { throw new CompilerException("Cannot set field on U# behaviour by reference when that field has a FieldChangeCallback attribute."); } TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String); MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour)) .GetMembers <MethodSymbol>("SetProgramVariable", context) .First(e => e.Parameters.Length == 2 && e.Parameters[0].Type == stringType); Value value = context.EmitValue(valueExpression); context.Emit(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, setProgramVariableMethod, SourceExpression, new BoundExpression[] { BindAccess(context.GetConstantValue(stringType, Field.Name)), BindAccess(value) })); return(value); }
public override Value EmitValue(EmitContext context) { // We don't want any references outside the flow control to be dirtied conditionally context.TopTable.DirtyAllValues(); Value returnValue = context.GetReturnValue(ValueType); context.EmitValueAssignment(returnValue, Lhs); TypeSymbol systemObjectType = context.GetTypeSymbol(SpecialType.System_Object); MethodSymbol objectEquality = new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Equality, systemObjectType, context); Value conditionCheck = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, null, objectEquality, null, new BoundExpression[] { BoundAccessExpression.BindAccess(returnValue), BoundAccessExpression.BindAccess(context.GetConstantValue(systemObjectType, null)) })); JumpLabel notNullLabel = context.Module.CreateLabel(); context.Module.AddJumpIfFalse(notNullLabel, conditionCheck); context.EmitValueAssignment(returnValue, Rhs); context.Module.LabelJump(notNullLabel); return(returnValue); }
public override Value EmitSet(EmitContext context, BoundExpression valueExpression) { BoundExpression instanceValue = GetInstanceExpression(context); BoundInvocationExpression invocationExpression = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, Property.SetMethod, instanceValue, GetParameters(context, valueExpression)); if (_isBaseCall) { invocationExpression.MarkForcedBaseCall(); } invocationExpression.MarkPropertySetter(); Value resultVal = context.EmitValue(invocationExpression); if (resultVal == null) { throw new NullReferenceException(); } if (instanceValue != null && SourceExpression.ValueType.IsValueType && SourceExpression is BoundArrayAccessExpression sourceAccessExpression) { context.EmitSet(sourceAccessExpression, instanceValue); } return(resultVal); }
public override Value EmitValue(EmitContext context) { BoundInvocationExpression invocationExpression = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, Property.GetMethod, GetInstanceExpression(context), GetParameters(context)); if (_isBaseCall) { invocationExpression.MarkForcedBaseCall(); } return(context.EmitValue(invocationExpression)); }
public override Value EmitValue(EmitContext context) { if (SourceExpression == null || SourceExpression.IsThis) { return(context.GetUserValue(Field)); } TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String); MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour)) .GetMembers <MethodSymbol>("GetProgramVariable", context) .First(e => e.Parameters.Length == 1 && e.Parameters[0].Type == stringType); return(context.CastValue(context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, setProgramVariableMethod, SourceExpression, new BoundExpression[] { BindAccess(context.GetConstantValue(stringType, Field.Name)) })), Field.Type, true)); }
public override Value EmitValue(EmitContext context) { Value returnValue = context.GetReturnValue(ValueType); var charArray = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, _toCharArraySymbol, BindAccess(context.EmitValue(SourceExpression)), new[] { IndexerExpression, BindAccess(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32), 1)) }); context.EmitValueAssignment(returnValue, BindElementAccess(context, SyntaxNode, charArray, new BoundExpression[] { BindAccess(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Int32), 0)) })); return(returnValue); }
public override Value EmitValue(EmitContext context) { BoundInvocationExpression formatInvoke; if (InterpolationExpressions.Length > 3) { BoundConstArrayCreationExpression interpolationArray = new BoundConstArrayCreationExpression(SyntaxNode, ObjectArr, InterpolationExpressions); BoundAccessExpression arrayAccess = BoundAccessExpression.BindAccess(context.EmitValue(interpolationArray)); formatInvoke = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, StringFormatMethod, null, new BoundExpression[] { BuiltStr, arrayAccess }); } else { formatInvoke = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, StringFormatMethod, null, new BoundExpression[] { BuiltStr }.Concat(InterpolationExpressions).ToArray()); } return(context.EmitValue(formatInvoke)); }
public override BoundNode VisitInvocationExpression(InvocationExpressionSyntax node) { MethodSymbol methodSymbol = (MethodSymbol)GetSymbol(node); // Check if the symbol is null because you can technically have methods named nameof since it is not reserved if (methodSymbol == null && node.Expression is IdentifierNameSyntax nameSyntax && nameSyntax.Identifier.Text == "nameof") { return(HandleNameOfExpression(node)); } BoundExpression instanceExpression = null; if (node.Expression is MemberAccessExpressionSyntax accessExpressionSyntax) { instanceExpression = VisitExpression(accessExpressionSyntax.Expression); } // Implicit this on member functions for this behaviour if (instanceExpression == null && !methodSymbol.IsStatic && methodSymbol.IsExtern) { instanceExpression = BoundAccessExpression.BindThisAccess(OwningSymbol.ContainingType); } BoundExpression[] boundArguments = new BoundExpression[methodSymbol.Parameters.Length]; var argumentsList = node.ArgumentList.Arguments; int startIdx = 0; if (instanceExpression != null && methodSymbol.RoslynSymbol.IsExtensionMethod) { boundArguments[0] = instanceExpression; instanceExpression = null; startIdx = 1; } bool hasParams = false; int handledArgsCount = startIdx; ArgumentSyntax paramsNamedArg = null; for (int i = startIdx; i < boundArguments.Length; ++i) { if (methodSymbol.Parameters[i].IsParams) { hasParams = true; paramsNamedArg = argumentsList.FirstOrDefault(x => x.NameColon?.Name.Identifier.ValueText == methodSymbol.Parameters[i].Name); break; } ArgumentSyntax argument; if (i - startIdx >= argumentsList.Count) { argument = null; } else if (argumentsList[i - startIdx].NameColon != null) { argument = argumentsList.FirstOrDefault(x => x.NameColon?.Name.Identifier.ValueText == methodSymbol.Parameters[i].Name); } else { argument = argumentsList[i - startIdx]; } if (argument == null) // Default argument handling { boundArguments[i] = new BoundConstantExpression(methodSymbol.Parameters[i].DefaultValue, methodSymbol.Parameters[i].Type, node); continue; } boundArguments[i] = VisitExpression(argument.Expression, methodSymbol.Parameters[i].Type); handledArgsCount++; } if (hasParams) { int paramCount; BoundExpression[] paramExpressions; if (paramsNamedArg != null) { paramCount = 1; paramExpressions = new BoundExpression[paramCount]; paramExpressions[0] = VisitExpression(paramsNamedArg.Expression); } else { paramCount = argumentsList.Count - handledArgsCount; paramExpressions = new BoundExpression[paramCount]; int idx = 0; for (int i = handledArgsCount; i < argumentsList.Count; ++i) { paramExpressions[idx++] = VisitExpression(argumentsList[i].Expression); } } void SetParamsArray() { TypeSymbol paramType = methodSymbol.Parameters.Last().Type; boundArguments[boundArguments.Length - 1] = new BoundConstArrayCreationExpression(node, paramType, paramExpressions.Select(e => ConvertExpression(node, e, paramType.ElementType)).ToArray()); } void SetDirectParam() { boundArguments[boundArguments.Length - 1] = paramExpressions[0]; } if (paramCount != 1) { SetParamsArray(); } else if (paramExpressions[0].ValueType == methodSymbol.Parameters.Last().Type) { SetDirectParam(); } else { Conversion conversion = Context.CompileContext.RoslynCompilation.ClassifyConversion(paramExpressions[0].ValueType.RoslynSymbol, methodSymbol.Parameters.Last().Type.RoslynSymbol); if (conversion.IsImplicit) // Covariant array param conversion { SetDirectParam(); } else { SetParamsArray(); } } } var invocation = BoundInvocationExpression.CreateBoundInvocation(Context, node, methodSymbol, instanceExpression, boundArguments); if ((instanceExpression == null || instanceExpression.IsThis) && node.Expression is MemberAccessExpressionSyntax accessExpressionSyntax2 && accessExpressionSyntax2.Expression.Kind() == SyntaxKind.BaseExpression) { invocation.MarkForcedBaseCall(); } return(invocation); }
public override void Emit(EmitContext context) { var blockScope = context.OpenBlockScope(); TypeSymbol intType = context.GetTypeSymbol(SpecialType.System_Int32); MethodSymbol toCharArrayMethod = context.GetTypeSymbol(SpecialType.System_String).GetMembers <MethodSymbol>("ToCharArray", context).First(e => e.Parameters.Length == 0); PropertySymbol lengthProperty = context.GetTypeSymbol(SpecialType.System_Array).GetMember <PropertySymbol>("Length", context); Value iteratorValue = context.EmitValue(BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, toCharArrayMethod, IteratorSource, new BoundExpression[] {})); iteratorValue.MarkUsedRecursively(); var iteratorAccess = BoundAccessExpression.BindAccess(iteratorValue); Value arraySize = context.CreateInternalValue(intType); arraySize.MarkUsedRecursively(); BoundAccessExpression getLength = BoundAccessExpression.BindAccess(context, SyntaxNode, lengthProperty, iteratorAccess); context.EmitValueAssignment(arraySize, getLength); // Declare and reset incrementor value Value incrementorValue = context.CreateInternalValue(intType); incrementorValue.MarkUsedRecursively(); context.EmitValueAssignment(incrementorValue, BoundAccessExpression.BindAccess(context.GetConstantValue(intType, 0))); JumpLabel loopLabel = context.Module.CreateLabel(); context.Module.LabelJump(loopLabel); var incrementorAccess = BoundAccessExpression.BindAccess(incrementorValue); BoundExpression increment = new BoundInvocationExpression.BoundPrefixOperatorExpression(context, SyntaxNode, incrementorAccess, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.Addition, intType, context)); var lengthCheck = BoundInvocationExpression.CreateBoundInvocation(context, SyntaxNode, new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LessThan, intType, context), null, new BoundExpression[] { incrementorAccess, BoundAccessExpression.BindAccess(arraySize) }); JumpLabel exitLoopLabel = context.PushBreakLabel(); JumpLabel continueLabel = context.PushContinueLabel(); Value lengthCheckResult = context.EmitValue(lengthCheck); context.Module.AddJumpIfFalse(exitLoopLabel, lengthCheckResult); context.EmitValueAssignment(context.GetUserValue(ValueSymbol), BoundAccessExpression.BindElementAccess(context, SyntaxNode, iteratorAccess, new BoundExpression[] { incrementorAccess })); context.Emit(BodyStatement); context.Module.LabelJump(continueLabel); context.Emit(increment); context.Module.AddJump(loopLabel); context.Module.LabelJump(exitLoopLabel); context.PopBreakLabel(); context.PopContinueLabel(); blockScope.Dispose(); }