Skip to content

Add support for searching for alternate invocees for externs #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 1.0-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public ArrayConstructorExtern(AbstractPhaseContext context, TypeSymbol arrayType
private BoundExpression[] Initializers { get; }

public BoundArrayCreationExpression(SyntaxNode node, AbstractPhaseContext context, TypeSymbol arrayType, BoundExpression[] rankSizes, BoundExpression[] initializers)
: base(node, new ArrayConstructorExtern(context, arrayType), null, rankSizes)
: base(node, context, new ArrayConstructorExtern(context, arrayType), null, rankSizes)
{
ArrayType = arrayType;
Initializers = initializers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,67 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using UdonSharp.Compiler.Emit;
using UdonSharp.Compiler.Symbols;
using UdonSharp.Compiler.Udon;
using UdonSharp.Core;
using UdonSharp.Localization;

namespace UdonSharp.Compiler.Binder
{
internal class BoundExternInvocation : BoundInvocationExpression
{
public BoundExternInvocation(SyntaxNode node, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
private ExternMethodSymbol externMethodSymbol;

public BoundExternInvocation(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
: base(node, method, instanceExpression, parameterExpressions)
{
externMethodSymbol = (ExternMethodSymbol)method;
if (!CompilerUdonInterface.IsExposedToUdon(externMethodSymbol.ExternSignature))
{
externMethodSymbol = FindAlternateInvocation(context, method, instanceExpression, parameterExpressions);
if (externMethodSymbol == null)
{
throw new NotExposedException(LocStr.CE_UdonMethodNotExposed, node, $"{method.RoslynSymbol?.ToDisplayString() ?? method.ToString()}, sig: {((ExternMethodSymbol)method).ExternSignature}");
}
}
}

private ExternMethodSymbol FindAlternateInvocation(AbstractPhaseContext context, MethodSymbol originalSymbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
{
if (originalSymbol.IsStatic || originalSymbol.IsConstructor) return null;

List<TypeSymbol> candidates = new List<TypeSymbol>();
FindCandidateInvocationTypes(context, candidates, instanceExpression.ValueType);

TypeSymbol[] paramTypes = parameterExpressions.Select(ex => ex.ValueType).ToArray();

foreach (var candidate in candidates)
{
ExternMethodSymbol externMethodSymbol = new ExternSynthesizedMethodSymbol(context, originalSymbol.Name, candidate, paramTypes, originalSymbol.ReturnType, false, false);
if (CompilerUdonInterface.IsExposedToUdon(externMethodSymbol.ExternSignature))
{
return externMethodSymbol;
}
}

return null;
}

void FindCandidateInvocationTypes(AbstractPhaseContext context, List<TypeSymbol> candidates, TypeSymbol ty)
{
foreach (var intf in ty.RoslynSymbol.AllInterfaces)
{
candidates.Add(context.GetTypeSymbol(intf));
}

while (ty != null)
{
candidates.Add(ty);
ty = ty.BaseType;
}
}

public override Value EmitValue(EmitContext context)
Expand Down Expand Up @@ -83,7 +133,7 @@ public override Value EmitValue(EmitContext context)
module.AddPush(returnValue);
}

module.AddExtern((ExternMethodSymbol) Method);
module.AddExtern(externMethodSymbol);

if (recursiveValues != null)
PopRecursiveValues(recursiveValues, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private static bool TryCreateInstantiationInvocation(AbstractPhaseContext contex
switch (symbol.Name)
{
case "Instantiate_Extern" when symbol.ContainingType == context.GetTypeSymbol(typeof(InstantiationShim)):
createdInvocation = new BoundExternInvocation(node,
createdInvocation = new BoundExternInvocation(node, context,
new ExternSynthesizedMethodSymbol(context,
"VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
parameterExpressions.Select(e => e.ValueType).ToArray(),
Expand Down Expand Up @@ -213,7 +213,7 @@ private static bool TryCreateSetProgramVariableInvocation(AbstractPhaseContext c
.GetMembers<MethodSymbol>("SetProgramVariable", context)
.First(e => !e.RoslynSymbol.IsGenericMethod);

createdInvocation = new BoundExternInvocation(node, setProgramVarObjMethod, instanceExpression,
createdInvocation = new BoundExternInvocation(node, context, setProgramVarObjMethod, instanceExpression,
parameterExpressions);
return true;
}
Expand All @@ -233,7 +233,7 @@ private static bool TryCreateArrayMethodInvocation(AbstractPhaseContext context,
.GetMembers<MethodSymbol>(symbol.Name, context)
.First(e => !e.RoslynSymbol.IsGenericMethod && e.Parameters.Length == symbol.Parameters.Length);

createdInvocation = new BoundExternInvocation(node, arrayMethod, instanceExpression,
createdInvocation = new BoundExternInvocation(node, context, arrayMethod, instanceExpression,
parameterExpressions);
return true;
}
Expand All @@ -249,7 +249,7 @@ private static bool TryCreateTMPMethodInvocation(AbstractPhaseContext context, S
if (symbol.ContainingType != null &&
symbol.ContainingType.ToString() == "TMPro.TMP_Text")
{
createdInvocation = new BoundExternInvocation(node,
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);
Expand All @@ -269,7 +269,7 @@ private static bool TryCreateBaseEnumMethodInvocation(AbstractPhaseContext conte
symbol.ContainingType != null &&
symbol.ContainingType == context.GetTypeSymbol(SpecialType.System_Enum))
{
createdInvocation = new BoundExternInvocation(node,
createdInvocation = new BoundExternInvocation(node, context,
context.GetTypeSymbol(SpecialType.System_Object).GetMember<MethodSymbol>(symbol.Name, context),
instanceExpression,
parameterExpressions);
Expand All @@ -289,7 +289,7 @@ private static bool TryCreateCompareToInvocation(AbstractPhaseContext context, S
symbol.ContainingType != null &&
symbol.ContainingType == context.GetTypeSymbol(typeof(IComparable)))
{
createdInvocation = new BoundExternInvocation(node,
createdInvocation = new BoundExternInvocation(node, context,
new ExternSynthesizedMethodSymbol(context, "CompareTo", instanceExpression.ValueType,
new [] { instanceExpression.ValueType },
context.GetTypeSymbol(SpecialType.System_Int32), false),
Expand Down Expand Up @@ -344,15 +344,6 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
if (CompilerUdonInterface.IsUdonEvent(symbol.Name) &&
symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour))) // Pass through for making base calls on the U# behaviour type return noop
return new BoundUdonSharpBehaviourInvocationExpression(node, symbol, instanceExpression, parameterExpressions);

var doExposureCheck = (!symbol.IsOperator || (symbol.ContainingType == null || !symbol.ContainingType.IsEnum));

if (symbol.IsOperator && symbol is ExternBuiltinOperatorSymbol operatorSymbol &&
operatorSymbol.OperatorType == BuiltinOperatorType.BitwiseNot)
doExposureCheck = false;

if (doExposureCheck && !CompilerUdonInterface.IsExposedToUdon(((ExternMethodSymbol) symbol).ExternSignature))
throw new NotExposedException(LocStr.CE_UdonMethodNotExposed, node, $"{symbol.RoslynSymbol?.ToDisplayString() ?? symbol.ToString()}, sig: {((ExternMethodSymbol) symbol).ExternSignature}");

if (symbol.IsOperator)
{
Expand All @@ -371,7 +362,7 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
BuiltinOperatorType.UnaryNegation, context.GetTypeSymbol(SpecialType.System_Boolean),
context);

return new BoundExternInvocation(node, boolNotOperator, null, new BoundExpression[] {boundEqualsInvocation});
return new BoundExternInvocation(node, context, boolNotOperator, null, new BoundExpression[] {boundEqualsInvocation});
}

if (node is AssignmentExpressionSyntax)
Expand All @@ -383,13 +374,13 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte

if (parameterExpressions.Length == 2 || symbol.Name == "op_UnaryNegation" || symbol.Name == "op_LogicalNot")
{
return new BoundBuiltinOperatorInvocationExpression(node, symbol, parameterExpressions);
return new BoundBuiltinOperatorInvocationExpression(node, context, symbol, parameterExpressions);
}

throw new NotSupportedException("Operator expressions must have either 1 or 2 parameters", node.GetLocation());
}

return new BoundExternInvocation(node, symbol, instanceExpression, parameterExpressions);
return new BoundExternInvocation(node, context, symbol, instanceExpression, parameterExpressions);
}

if (symbol.IsStatic)
Expand Down Expand Up @@ -596,8 +587,8 @@ protected void PopRecursiveValues(Value[] values, EmitContext context)

private sealed class BoundBuiltinOperatorInvocationExpression : BoundExternInvocation
{
public BoundBuiltinOperatorInvocationExpression(SyntaxNode node, MethodSymbol method, BoundExpression[] operandExpressions)
:base(node, method, null, operandExpressions)
public BoundBuiltinOperatorInvocationExpression(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression[] operandExpressions)
:base(node, context, method, null, operandExpressions)
{
}
}
Expand Down Expand Up @@ -642,7 +633,7 @@ private sealed class BoundGetUnityEngineComponentInvocation : BoundExternInvocat
public override TypeSymbol ValueType { get; }

public BoundGetUnityEngineComponentInvocation(AbstractPhaseContext context, SyntaxNode node, MethodSymbol methodSymbol, BoundExpression sourceExpression, BoundExpression[] parametersExpressions)
: base(node, BuildMethod(context, methodSymbol), sourceExpression, GetParameterExpressions(context, methodSymbol, parametersExpressions))
: base(node, context, BuildMethod(context, methodSymbol), sourceExpression, GetParameterExpressions(context, methodSymbol, parametersExpressions))
{
ValueType = methodSymbol.TypeArguments[0];

Expand Down
Loading