Example #1
0
        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 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);
        }
Example #3
0
        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 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);
        }
Example #5
0
        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)));
        }
Example #6
0
        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 BoundPrefixOperatorExpression(AbstractPhaseContext context, SyntaxNode node, BoundAccessExpression assignmentTarget, MethodSymbol operatorMethod)
                : base(node, null, null, null)
            {
                TargetExpression = assignmentTarget;
                Type           targetType     = TargetExpression.ValueType.UdonType.SystemType;
                IConstantValue incrementValue = (IConstantValue)Activator.CreateInstance(
                    typeof(ConstantValue <>).MakeGenericType(targetType), Convert.ChangeType(1, targetType));

                InternalExpression = CreateBoundInvocation(context, null, operatorMethod, null,
                                                           new BoundExpression[] { assignmentTarget, new BoundConstantExpression(incrementValue, TargetExpression.ValueType, node) });
            }
        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));
        }
        private static bool TryCreateUdonSharpMetadataInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                                 MethodSymbol symbol, BoundExpression instanceExpression,
                                                                 out BoundInvocationExpression createdInvocation)
        {
            if (symbol.Name == "GetUdonTypeID" || symbol.Name == "GetUdonTypeName")
            {
                if (symbol.IsStatic &&
                    symbol.TypeArguments.Length == 1 &&
                    symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
                {
                    IConstantValue constantValue;
                    TypeSymbol     constantType;

                    var typeArgs = symbol.TypeArguments.Select(e => context.GetTypeSymbol(e.RoslynSymbol)).ToArray();

                    if (symbol.Name == "GetUdonTypeID")
                    {
                        constantValue = new ConstantValue <long>(UdonSharpInternalUtility.GetTypeID(TypeSymbol.GetFullTypeName(typeArgs[0].RoslynSymbol)));
                        constantType  = context.GetTypeSymbol(SpecialType.System_Int64);
                    }
                    else
                    {
                        constantValue = new ConstantValue <string>(TypeSymbol.GetFullTypeName(typeArgs[0].RoslynSymbol));
                        constantType  = context.GetTypeSymbol(SpecialType.System_String);
                    }

                    createdInvocation = new BoundConstantInvocationExpression(node, constantValue, constantType);

                    return(true);
                }

                if (!symbol.IsStatic &&
                    instanceExpression != null &&
                    symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
                {
                    TypeSymbol methodContainer = context.GetTypeSymbol(typeof(UdonSharpBehaviourMethods));
                    var        shimMethod      = methodContainer.GetMember <MethodSymbol>(symbol.Name, context);
                    context.MarkSymbolReferenced(shimMethod);

                    createdInvocation = CreateBoundInvocation(context, node, shimMethod, null, new [] { instanceExpression });

                    return(true);
                }
            }

            createdInvocation = null;
            return(false);
        }
        private static bool TryCreateShimInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                    MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                    out BoundInvocationExpression createdInvocation)
        {
            if (TryCreateUdonSharpMetadataInvocation(context, node, symbol, instanceExpression, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateGetComponentInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateInstantiationInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateSetProgramVariableInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateArrayMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateTMPMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateBaseEnumMethodInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            if (TryCreateCompareToInvocation(context, node, symbol, instanceExpression, parameterExpressions, out createdInvocation))
            {
                return(true);
            }

            return(false);
        }
        private static bool TryCreateTMPMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                         MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                         out BoundInvocationExpression createdInvocation)
        {
            if (symbol.ContainingType != null &&
                symbol.ContainingType.ToString() == "TMPro.TMP_Text")
            {
                createdInvocation = new BoundExternInvocation(node, context,
                                                              new ExternSynthesizedMethodSymbol(context, symbol.Name, instanceExpression.ValueType, symbol.Parameters.Select(e => e.Type).ToArray(), symbol.ReturnType, symbol.IsStatic),
                                                              instanceExpression,
                                                              parameterExpressions);

                return(true);
            }

            createdInvocation = null;
            return(false);
        }
        private static bool TryCreateInstantiationInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                             MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                             out BoundInvocationExpression createdInvocation)
        {
            switch (symbol.Name)
            {
            case "Instantiate_Extern" when symbol.ContainingType == context.GetTypeSymbol(typeof(InstantiationShim)):
                createdInvocation = new BoundExternInvocation(node, context,
                                                              new ExternSynthesizedMethodSymbol(context,
                                                                                                "VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
                                                                                                parameterExpressions.Select(e => e.ValueType).ToArray(),
                                                                                                context.GetTypeSymbol(typeof(GameObject)), true),
                                                              instanceExpression, parameterExpressions);

                return(true);

            case "VRCInstantiate" when symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)):     // Backwards compatibility for UdonSharpBehaviour.VRCInstantiate
            case "Instantiate" when symbol.ContainingType == context.GetTypeSymbol(typeof(UnityEngine.Object)):
            {
                if (symbol.Name != "VRCInstantiate" &&
                    (symbol.TypeArguments.Length != 1 ||
                     symbol.TypeArguments[0] != context.GetTypeSymbol(typeof(GameObject))))
                {
                    throw new NotSupportedException("Udon does not support instantiating non-GameObject types");
                }

                TypeSymbol   instantiateShim   = context.GetTypeSymbol(typeof(InstantiationShim));
                MethodSymbol instantiateMethod = instantiateShim.GetMembers <MethodSymbol>("Instantiate", context)
                                                 .First(e => e.Parameters
                                                        .Select(p => p.Type)
                                                        .SequenceEqual(parameterExpressions
                                                                       .Select(p => p.ValueType)));

                context.MarkSymbolReferenced(instantiateMethod);

                createdInvocation = new BoundStaticUserMethodInvocation(node, instantiateMethod, parameterExpressions);
                return(true);
            }
            }

            createdInvocation = null;
            return(false);
        }
        /// <summary>
        /// Udon exposes a generic SetProgramVariable which the overload finding will attempt to use and fail to find,
        ///  so just use the non-generic version in this case
        /// </summary>
        private static bool TryCreateSetProgramVariableInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                                  MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                                  out BoundInvocationExpression createdInvocation)
        {
            if (symbol.Name == "SetProgramVariable" &&
                symbol.ContainingType == context.GetTypeSymbol(typeof(UdonBehaviour)))
            {
                MethodSymbol setProgramVarObjMethod = context.GetTypeSymbol(typeof(UdonBehaviour))
                                                      .GetMembers <MethodSymbol>("SetProgramVariable", context)
                                                      .First(e => !e.RoslynSymbol.IsGenericMethod);

                createdInvocation = new BoundExternInvocation(node, context, setProgramVarObjMethod, instanceExpression,
                                                              parameterExpressions);
                return(true);
            }

            createdInvocation = null;
            return(false);
        }
        private static bool TryCreateArrayMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                           MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                           out BoundInvocationExpression createdInvocation)
        {
            if ((symbol.Name == "IndexOf" || symbol.Name == "BinarySearch" || symbol.Name == "LastIndexOf" || symbol.Name == "Reverse") &&
                symbol.ContainingType == context.GetTypeSymbol(typeof(Array)))
            {
                MethodSymbol arrayMethod = context.GetTypeSymbol(typeof(Array))
                                           .GetMembers <MethodSymbol>(symbol.Name, context)
                                           .First(e => !e.RoslynSymbol.IsGenericMethod && e.Parameters.Length == symbol.Parameters.Length);

                createdInvocation = new BoundExternInvocation(node, context, arrayMethod, instanceExpression,
                                                              parameterExpressions);
                return(true);
            }

            createdInvocation = null;
            return(false);
        }
        private static bool TryCreateBaseEnumMethodInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                              MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                              out BoundInvocationExpression createdInvocation)
        {
            if ((symbol.Name == "ToString" || symbol.Name == "GetHashCode" || symbol.Name == "Equals") &&
                symbol.ContainingType != null &&
                symbol.ContainingType == context.GetTypeSymbol(SpecialType.System_Enum))
            {
                createdInvocation = new BoundExternInvocation(node, context,
                                                              context.GetTypeSymbol(SpecialType.System_Object).GetMember <MethodSymbol>(symbol.Name, context),
                                                              instanceExpression,
                                                              parameterExpressions);

                return(true);
            }

            createdInvocation = null;
            return(false);
        }
            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);
        }
        private static bool TryCreateCompareToInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                         MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                         out BoundInvocationExpression createdInvocation)
        {
            if (symbol.Name == "CompareTo" &&
                symbol.ContainingType != null &&
                symbol.ContainingType == context.GetTypeSymbol(typeof(IComparable)))
            {
                createdInvocation = new BoundExternInvocation(node, context,
                                                              new ExternSynthesizedMethodSymbol(context, "CompareTo", instanceExpression.ValueType,
                                                                                                new [] { instanceExpression.ValueType },
                                                                                                context.GetTypeSymbol(SpecialType.System_Int32), false),
                                                              instanceExpression, parameterExpressions);

                return(true);
            }

            createdInvocation = null;
            return(false);
        }
        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));
        }
Example #21
0
        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();
        }
Example #22
0
        public override Value EmitValue(EmitContext context)
        {
            // Make base calls to UdonSharpBehaviour events a noop
            if (IsBaseCall)
            {
                if (Method.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour)))
                {
                    if (Method.Name == "OnOwnershipRequest")
                    {
                        return(context.GetConstantValue(context.GetTypeSymbol(SpecialType.System_Boolean), true));
                    }

                    return(null);
                }
            }

            if (SourceExpression == null || SourceExpression.IsThis)
            {
                return(base.EmitValue(context));
            }

            // Calls across UdonBehaviours
            CompilationContext.MethodExportLayout layout =
                context.CompileContext.GetUsbMethodLayout(Method, context);

            Value.CowValue        instanceCowValue = GetInstanceValue(context);
            Value                 instanceValue    = instanceCowValue.Value;
            BoundAccessExpression instanceAccess   = BoundAccessExpression.BindAccess(instanceValue);

            TypeSymbol stringType = context.GetTypeSymbol(SpecialType.System_String);

            Value[] recursiveValues   = null;
            Value   stackSizeCheckVal = null;
            bool    isRecursiveCall   = context.IsRecursiveMethodEmit;

            if (isRecursiveCall)
            {
                stackSizeCheckVal = context.CreateGlobalInternalValue(context.GetTypeSymbol(SpecialType.System_Int32));
            }

            if (Method.Parameters.Length > 0)
            {
                MethodSymbol setProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
                                                        .GetMembers <MethodSymbol>("SetProgramVariable", context)
                                                        .First(e => e.Parameters.Length == 2 &&
                                                               e.Parameters[0].Type == stringType);

                Value[] parameterValues = GetParameterValues(context);
                instanceValue  = instanceCowValue.Value;
                instanceAccess = BoundAccessExpression.BindAccess(instanceValue); // Re-bind here since the parameters may have changed the cowvalue

                if (isRecursiveCall)
                {
                    EmitContext.MethodLinkage selfLinkage = context.GetMethodLinkage(context.CurrentEmitMethod, false);
                    recursiveValues = selfLinkage.ParameterValues;

                    CheckStackSize(stackSizeCheckVal, context);
                    PushRecursiveValues(recursiveValues, context);

                    for (int i = 0; i < parameterValues.Length; ++i)
                    {
                        Value paramIntermediate = context.CreateInternalValue(parameterValues[i].UserType);
                        context.Module.AddCopy(parameterValues[i], paramIntermediate);
                        parameterValues[i] = paramIntermediate;
                    }
                }
                else
                {
                    instanceCowValue.Dispose();
                }

                context.TopTable.DirtyAllValues();

                for (int i = 0; i < Method.Parameters.Length; ++i)
                {
                    context.Emit(CreateBoundInvocation(context, SyntaxNode, setProgramVariableMethod, instanceAccess,
                                                       new BoundExpression[]
                    {
                        BoundAccessExpression.BindAccess(context.GetConstantValue(stringType, layout.ParameterExportNames[i])),
                        BoundAccessExpression.BindAccess(parameterValues[i])
                    }));
                }
            }

            if (isRecursiveCall)
            {
                if (recursiveValues == null)
                {
                    recursiveValues = Array.Empty <Value>();
                }

                Value[] scopeValues = context.CollectRecursiveValues();

                PushRecursiveValues(scopeValues, context);

                recursiveValues = recursiveValues.Concat(scopeValues).ToArray();

                stackSizeCheckVal.DefaultValue = recursiveValues.Length;
                context.UpdateRecursiveStackMaxSize(recursiveValues.Length);
            }

            MethodSymbol sendCustomEventMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour)).GetMember <MethodSymbol>("SendCustomEvent", context);

            context.Emit(CreateBoundInvocation(context, SyntaxNode, sendCustomEventMethod, BoundAccessExpression.BindAccess(instanceValue),
                                               new BoundExpression[]
            {
                BoundAccessExpression.BindAccess(context.GetConstantValue(stringType, layout.ExportMethodName))
            }));

            if (isRecursiveCall)
            {
                PopRecursiveValues(recursiveValues, context);
            }

            if (Method.Parameters.Length > 0 &&
                Method.Parameters.Any(e => e.IsOut))
            {
                if (isRecursiveCall)
                {
                    throw new CompilerException("U# does not yet support calling user methods with ref/out parameters from methods marked with RecursiveMethod");
                }

                MethodSymbol getProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
                                                        .GetMembers <MethodSymbol>("GetProgramVariable", context)
                                                        .First(e => e.Parameters.Length == 1 &&
                                                               e.Parameters[0].Type == stringType);

                // Copy out/ref parameters back
                for (int i = 0; i < Method.Parameters.Length; ++i)
                {
                    ParameterSymbol parameterSymbol = Method.Parameters[i];

                    if (parameterSymbol.IsOut)
                    {
                        BoundAccessExpression currentAccessExpression = (BoundAccessExpression)ParameterExpressions[i];

                        currentAccessExpression.EmitSet(context, CreateBoundInvocation(context, SyntaxNode,
                                                                                       getProgramVariableMethod, instanceAccess, new[]
                        {
                            BoundAccessExpression.BindAccess(context.GetConstantValue(stringType,
                                                                                      layout.ParameterExportNames[i]))
                        }));
                    }
                }
            }

            if (IsPropertySetter)
            {
                return(GetParameterValues(context).Last());
            }

            if (Method.ReturnType != null)
            {
                MethodSymbol getProgramVariableMethod = context.GetTypeSymbol(typeof(UdonSharpBehaviour))
                                                        .GetMembers <MethodSymbol>("GetProgramVariable", context)
                                                        .First(e => e.Parameters.Length == 1 &&
                                                               e.Parameters[0].Type == stringType);

                Value returnVal = context.CreateInternalValue(Method.ReturnType);

                BoundInvocationExpression boundGetReturn = CreateBoundInvocation(context, SyntaxNode,
                                                                                 getProgramVariableMethod, instanceAccess,
                                                                                 new BoundExpression[]
                {
                    BoundAccessExpression.BindAccess(context.GetConstantValue(stringType,
                                                                              layout.ReturnExportName))
                });

                context.EmitValueAssignment(returnVal, boundGetReturn);

                return(returnVal);
            }

            return(null);
        }
        private static bool TryCreateGetComponentInvocation(AbstractPhaseContext context, SyntaxNode node,
                                                            MethodSymbol symbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions,
                                                            out BoundInvocationExpression createdInvocation)
        {
            if (symbol.RoslynSymbol != null &&
                symbol.RoslynSymbol.IsGenericMethod &&
                symbol.TypeArguments.Length == 1 &&
                _getComponentNames.Contains(symbol.Name) &&
                (symbol.ContainingType.UdonType.SystemType == typeof(Component) || symbol.ContainingType.UdonType.SystemType == typeof(GameObject)))
            {
                TypeSymbol gameObjectType = context.GetTypeSymbol(typeof(GameObject));
                TypeSymbol typeArgument   = symbol.TypeArguments[0];

                // udon-workaround: Work around the udon bug where it checks the strongbox type instead of variable type and blows up when the strong box is `object`
                if (instanceExpression.ValueType == gameObjectType)
                {
                    PropertySymbol accessProperty = gameObjectType.GetMember <PropertySymbol>("transform", context);
                    instanceExpression = BoundAccessExpression.BindAccess(context, node, accessProperty, instanceExpression);
                }
                else
                {
                    PropertySymbol accessProperty = context.GetTypeSymbol(typeof(Component)).GetMember <PropertySymbol>("transform", context);
                    instanceExpression = BoundAccessExpression.BindAccess(context, node, accessProperty, instanceExpression);
                }

                TypeSymbol udonSharpBehaviourType = context.GetTypeSymbol(typeof(UdonSharpBehaviour));

                // Exact UdonSharpBehaviour type match
                if (typeArgument == udonSharpBehaviourType)
                {
                    MethodSymbol getComponentMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
                                                          .GetMembers <MethodSymbol>(symbol.Name + "USB", context)
                                                          .First(e => e.Parameters.Length == parameterExpressions.Length + 1);

                    createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentMethodShim,
                                                                            new [] { instanceExpression }.Concat(parameterExpressions).ToArray());

                    context.MarkSymbolReferenced(getComponentMethodShim);

                    return(true);
                }

                // Subclass of UdonSharpBehaviour
                if (typeArgument.IsUdonSharpBehaviour)
                {
                    // Handle inherited types
                    if (context.CompileContext.HasInheritedUdonSharpBehaviours(typeArgument))
                    {
                        MethodSymbol getComponentInheritedMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
                                                                       .GetMembers <MethodSymbol>(symbol.Name + "I", context)
                                                                       .First(e => e.Parameters.Length == parameterExpressions.Length + 1);

                        getComponentInheritedMethodShim = getComponentInheritedMethodShim.ConstructGenericMethod(context, new [] { typeArgument });

                        createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentInheritedMethodShim,
                                                                                new [] { instanceExpression }.Concat(parameterExpressions).ToArray());

                        context.MarkSymbolReferenced(getComponentInheritedMethodShim);

                        return(true);
                    }

                    MethodSymbol getComponentMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
                                                          .GetMembers <MethodSymbol>(symbol.Name, context)
                                                          .First(e => e.Parameters.Length == parameterExpressions.Length + 1);

                    getComponentMethodShim = getComponentMethodShim.ConstructGenericMethod(context, new [] { typeArgument });

                    createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentMethodShim,
                                                                            new [] { instanceExpression }.Concat(parameterExpressions).ToArray());

                    context.MarkSymbolReferenced(getComponentMethodShim);

                    return(true);
                }

                if (_brokenGetComponentTypes.Contains(typeArgument.UdonType.SystemType))
                {
                    MethodSymbol getComponentInheritedMethodShim = context.GetTypeSymbol(typeof(GetComponentShim))
                                                                   .GetMembers <MethodSymbol>(symbol.Name + "VRC", context)
                                                                   .First(e => e.Parameters.Length == parameterExpressions.Length + 1);

                    getComponentInheritedMethodShim = getComponentInheritedMethodShim.ConstructGenericMethod(context, new [] { typeArgument });

                    createdInvocation = new BoundStaticUserMethodInvocation(node, getComponentInheritedMethodShim,
                                                                            new [] { instanceExpression }.Concat(parameterExpressions).ToArray());

                    context.MarkSymbolReferenced(getComponentInheritedMethodShim);

                    return(true);
                }

                createdInvocation = new BoundGetUnityEngineComponentInvocation(context, node, symbol,
                                                                               instanceExpression,
                                                                               parameterExpressions);

                return(true);
            }

            createdInvocation = null;
            return(false);
        }
Example #24
0
        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);
        }