/// <summary> /// Retrieves the linkage for a local user method call. /// A local user method call means any method that is not a call on a different Udon Behaviour. /// This includes any static function call, function call on an imported user type, or call on the local UdonSharpBehaviour /// </summary> /// <param name="methodSymbol">The method symbol to get the linkage for</param> /// <param name="useVirtual">Whether this is a virtual call where we want the most derived method to be called</param> /// <returns></returns> public MethodLinkage GetMethodLinkage(MethodSymbol methodSymbol, bool useVirtual) { if (!useVirtual && _linkages.TryGetValue(methodSymbol, out var linkage)) { return(linkage); } MethodSymbol derivedMethod = GetMostDerivedMethod(methodSymbol); if (useVirtual && _linkages.TryGetValue(derivedMethod, out linkage)) { return(linkage); } MethodLinkage newLinkage; JumpLabel methodLabel = Module.CreateLabel(); Value[] parameterValues = new Value[methodSymbol.Parameters.Length]; if ((useVirtual || derivedMethod == methodSymbol) && !methodSymbol.IsStatic && methodSymbol is UdonSharpBehaviourMethodSymbol) { methodLabel.DebugMethod = derivedMethod; CompilationContext.MethodExportLayout layout = CompileContext.GetUsbMethodLayout(methodSymbol, this); Value returnValue = layout.ReturnExportName == null ? null : Module.RootTable.CreateParameterValue(layout.ReturnExportName, derivedMethod.ReturnType); for (int i = 0; i < parameterValues.Length; ++i) { parameterValues[i] = Module.RootTable.CreateParameterValue(layout.ParameterExportNames[i], methodSymbol.Parameters[i].Type); } newLinkage = new MethodLinkage(methodLabel, layout.ExportMethodName, returnValue, parameterValues); _linkages.Add(derivedMethod, newLinkage); } else { methodLabel.DebugMethod = methodSymbol; Value returnValue = methodSymbol.ReturnType == null ? null : Module.RootTable.CreateParameterValue(GetInternalParamName(), methodSymbol.ReturnType); for (int i = 0; i < parameterValues.Length; ++i) { parameterValues[i] = Module.RootTable.CreateParameterValue(GetInternalParamName(), methodSymbol.Parameters[i].Type); } newLinkage = new MethodLinkage(methodLabel, null, returnValue, parameterValues); _linkages.Add(methodSymbol, newLinkage); } return(newLinkage); }
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); }