Skip to content

Commit 365bc75

Browse files
authored
Merge pull request #11 from bdunderscore/alt-invocees-serialization-dev
Add support for searching for alternate invocees for externs
2 parents ad40935 + b42e629 commit 365bc75

File tree

7 files changed

+232
-26
lines changed

7 files changed

+232
-26
lines changed

Assets/UdonSharp/Editor/Compiler/Binder/BoundNodes/BoundArrayCreationExpression.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public ArrayConstructorExtern(AbstractPhaseContext context, TypeSymbol arrayType
2727
private BoundExpression[] Initializers { get; }
2828

2929
public BoundArrayCreationExpression(SyntaxNode node, AbstractPhaseContext context, TypeSymbol arrayType, BoundExpression[] rankSizes, BoundExpression[] initializers)
30-
: base(node, new ArrayConstructorExtern(context, arrayType), null, rankSizes)
30+
: base(node, context, new ArrayConstructorExtern(context, arrayType), null, rankSizes)
3131
{
3232
ArrayType = arrayType;
3333
Initializers = initializers;

Assets/UdonSharp/Editor/Compiler/Binder/BoundNodes/BoundExternMethodInvocation.cs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,67 @@
11

22
using System;
3+
using System.Collections.Generic;
34
using System.Linq;
45
using Microsoft.CodeAnalysis;
56
using UdonSharp.Compiler.Emit;
67
using UdonSharp.Compiler.Symbols;
8+
using UdonSharp.Compiler.Udon;
9+
using UdonSharp.Core;
10+
using UdonSharp.Localization;
711

812
namespace UdonSharp.Compiler.Binder
913
{
1014
internal class BoundExternInvocation : BoundInvocationExpression
1115
{
12-
public BoundExternInvocation(SyntaxNode node, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
16+
private ExternMethodSymbol externMethodSymbol;
17+
18+
public BoundExternInvocation(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
1319
: base(node, method, instanceExpression, parameterExpressions)
1420
{
21+
externMethodSymbol = (ExternMethodSymbol)method;
22+
if (!CompilerUdonInterface.IsExposedToUdon(externMethodSymbol.ExternSignature))
23+
{
24+
externMethodSymbol = FindAlternateInvocation(context, method, instanceExpression, parameterExpressions);
25+
if (externMethodSymbol == null)
26+
{
27+
throw new NotExposedException(LocStr.CE_UdonMethodNotExposed, node, $"{method.RoslynSymbol?.ToDisplayString() ?? method.ToString()}, sig: {((ExternMethodSymbol)method).ExternSignature}");
28+
}
29+
}
30+
}
31+
32+
private ExternMethodSymbol FindAlternateInvocation(AbstractPhaseContext context, MethodSymbol originalSymbol, BoundExpression instanceExpression, BoundExpression[] parameterExpressions)
33+
{
34+
if (originalSymbol.IsStatic || originalSymbol.IsConstructor) return null;
35+
36+
List<TypeSymbol> candidates = new List<TypeSymbol>();
37+
FindCandidateInvocationTypes(context, candidates, instanceExpression.ValueType);
38+
39+
TypeSymbol[] paramTypes = parameterExpressions.Select(ex => ex.ValueType).ToArray();
40+
41+
foreach (var candidate in candidates)
42+
{
43+
ExternMethodSymbol externMethodSymbol = new ExternSynthesizedMethodSymbol(context, originalSymbol.Name, candidate, paramTypes, originalSymbol.ReturnType, false, false);
44+
if (CompilerUdonInterface.IsExposedToUdon(externMethodSymbol.ExternSignature))
45+
{
46+
return externMethodSymbol;
47+
}
48+
}
49+
50+
return null;
51+
}
52+
53+
void FindCandidateInvocationTypes(AbstractPhaseContext context, List<TypeSymbol> candidates, TypeSymbol ty)
54+
{
55+
foreach (var intf in ty.RoslynSymbol.AllInterfaces)
56+
{
57+
candidates.Add(context.GetTypeSymbol(intf));
58+
}
59+
60+
while (ty != null)
61+
{
62+
candidates.Add(ty);
63+
ty = ty.BaseType;
64+
}
1565
}
1666

1767
public override Value EmitValue(EmitContext context)
@@ -83,7 +133,7 @@ public override Value EmitValue(EmitContext context)
83133
module.AddPush(returnValue);
84134
}
85135

86-
module.AddExtern((ExternMethodSymbol) Method);
136+
module.AddExtern(externMethodSymbol);
87137

88138
if (recursiveValues != null)
89139
PopRecursiveValues(recursiveValues, context);

Assets/UdonSharp/Editor/Compiler/Binder/BoundNodes/BoundInvocationExpression.cs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private static bool TryCreateInstantiationInvocation(AbstractPhaseContext contex
164164
switch (symbol.Name)
165165
{
166166
case "Instantiate_Extern" when symbol.ContainingType == context.GetTypeSymbol(typeof(InstantiationShim)):
167-
createdInvocation = new BoundExternInvocation(node,
167+
createdInvocation = new BoundExternInvocation(node, context,
168168
new ExternSynthesizedMethodSymbol(context,
169169
"VRCInstantiate.__Instantiate__UnityEngineGameObject__UnityEngineGameObject",
170170
parameterExpressions.Select(e => e.ValueType).ToArray(),
@@ -213,7 +213,7 @@ private static bool TryCreateSetProgramVariableInvocation(AbstractPhaseContext c
213213
.GetMembers<MethodSymbol>("SetProgramVariable", context)
214214
.First(e => !e.RoslynSymbol.IsGenericMethod);
215215

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

236-
createdInvocation = new BoundExternInvocation(node, arrayMethod, instanceExpression,
236+
createdInvocation = new BoundExternInvocation(node, context, arrayMethod, instanceExpression,
237237
parameterExpressions);
238238
return true;
239239
}
@@ -249,7 +249,7 @@ private static bool TryCreateTMPMethodInvocation(AbstractPhaseContext context, S
249249
if (symbol.ContainingType != null &&
250250
symbol.ContainingType.ToString() == "TMPro.TMP_Text")
251251
{
252-
createdInvocation = new BoundExternInvocation(node,
252+
createdInvocation = new BoundExternInvocation(node, context,
253253
new ExternSynthesizedMethodSymbol(context, symbol.Name, instanceExpression.ValueType, symbol.Parameters.Select(e => e.Type).ToArray(), symbol.ReturnType, symbol.IsStatic),
254254
instanceExpression,
255255
parameterExpressions);
@@ -269,7 +269,7 @@ private static bool TryCreateBaseEnumMethodInvocation(AbstractPhaseContext conte
269269
symbol.ContainingType != null &&
270270
symbol.ContainingType == context.GetTypeSymbol(SpecialType.System_Enum))
271271
{
272-
createdInvocation = new BoundExternInvocation(node,
272+
createdInvocation = new BoundExternInvocation(node, context,
273273
context.GetTypeSymbol(SpecialType.System_Object).GetMember<MethodSymbol>(symbol.Name, context),
274274
instanceExpression,
275275
parameterExpressions);
@@ -289,7 +289,7 @@ private static bool TryCreateCompareToInvocation(AbstractPhaseContext context, S
289289
symbol.ContainingType != null &&
290290
symbol.ContainingType == context.GetTypeSymbol(typeof(IComparable)))
291291
{
292-
createdInvocation = new BoundExternInvocation(node,
292+
createdInvocation = new BoundExternInvocation(node, context,
293293
new ExternSynthesizedMethodSymbol(context, "CompareTo", instanceExpression.ValueType,
294294
new [] { instanceExpression.ValueType },
295295
context.GetTypeSymbol(SpecialType.System_Int32), false),
@@ -344,15 +344,6 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
344344
if (CompilerUdonInterface.IsUdonEvent(symbol.Name) &&
345345
symbol.ContainingType == context.GetTypeSymbol(typeof(UdonSharpBehaviour))) // Pass through for making base calls on the U# behaviour type return noop
346346
return new BoundUdonSharpBehaviourInvocationExpression(node, symbol, instanceExpression, parameterExpressions);
347-
348-
bool doExposureCheck = (!symbol.IsOperator || (symbol.ContainingType == null || !symbol.ContainingType.IsEnum));
349-
350-
if (symbol.IsOperator && symbol is ExternBuiltinOperatorSymbol operatorSymbol &&
351-
operatorSymbol.OperatorType == BuiltinOperatorType.BitwiseNot)
352-
doExposureCheck = false;
353-
354-
if (doExposureCheck && !CompilerUdonInterface.IsExposedToUdon(((ExternMethodSymbol) symbol).ExternSignature))
355-
throw new NotExposedException(LocStr.CE_UdonMethodNotExposed, node, $"{symbol.RoslynSymbol?.ToDisplayString() ?? symbol.ToString()}, sig: {((ExternMethodSymbol) symbol).ExternSignature}");
356347

357348
if (symbol.IsOperator)
358349
{
@@ -362,7 +353,7 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
362353
MethodSymbol objectEqualsMethod = context.GetTypeSymbol(SpecialType.System_Object)
363354
.GetMember<MethodSymbol>("Equals", context);
364355

365-
BoundInvocationExpression boundEqualsInvocation = CreateBoundInvocation(context, node, objectEqualsMethod, parameterExpressions[0],
356+
var boundEqualsInvocation = CreateBoundInvocation(context, node, objectEqualsMethod, parameterExpressions[0],
366357
new[] {parameterExpressions[1]});
367358
if (symbol.Name == "op_Equality")
368359
return boundEqualsInvocation;
@@ -371,7 +362,7 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
371362
BuiltinOperatorType.UnaryNegation, context.GetTypeSymbol(SpecialType.System_Boolean),
372363
context);
373364

374-
return new BoundExternInvocation(node, boolNotOperator, null, new BoundExpression[] {boundEqualsInvocation});
365+
return new BoundExternInvocation(node, context, boolNotOperator, null, new BoundExpression[] {boundEqualsInvocation});
375366
}
376367

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

384375
if (parameterExpressions.Length == 2 || symbol.Name == "op_UnaryNegation" || symbol.Name == "op_LogicalNot")
385376
{
386-
return new BoundBuiltinOperatorInvocationExpression(node, symbol, parameterExpressions);
377+
return new BoundBuiltinOperatorInvocationExpression(node, context, symbol, parameterExpressions);
387378
}
388379

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

392-
return new BoundExternInvocation(node, symbol, instanceExpression, parameterExpressions);
383+
return new BoundExternInvocation(node, context, symbol, instanceExpression, parameterExpressions);
393384
}
394385

395386
if (symbol.IsStatic)
@@ -404,7 +395,7 @@ public static BoundInvocationExpression CreateBoundInvocation(AbstractPhaseConte
404395
parameterExpressions);
405396
}
406397

407-
throw new NotImplementedException();
398+
throw new System.NotImplementedException();
408399
}
409400

410401
protected override void ReleaseCowValuesImpl(EmitContext context)
@@ -596,8 +587,8 @@ protected void PopRecursiveValues(Value[] values, EmitContext context)
596587

597588
private sealed class BoundBuiltinOperatorInvocationExpression : BoundExternInvocation
598589
{
599-
public BoundBuiltinOperatorInvocationExpression(SyntaxNode node, MethodSymbol method, BoundExpression[] operandExpressions)
600-
:base(node, method, null, operandExpressions)
590+
public BoundBuiltinOperatorInvocationExpression(SyntaxNode node, AbstractPhaseContext context, MethodSymbol method, BoundExpression[] operandExpressions)
591+
:base(node, context, method, null, operandExpressions)
601592
{
602593
}
603594
}
@@ -642,7 +633,7 @@ private sealed class BoundGetUnityEngineComponentInvocation : BoundExternInvocat
642633
public override TypeSymbol ValueType { get; }
643634

644635
public BoundGetUnityEngineComponentInvocation(AbstractPhaseContext context, SyntaxNode node, MethodSymbol methodSymbol, BoundExpression sourceExpression, BoundExpression[] parametersExpressions)
645-
: base(node, BuildMethod(context, methodSymbol), sourceExpression, GetParameterExpressions(context, methodSymbol, parametersExpressions))
636+
: base(node, context, BuildMethod(context, methodSymbol), sourceExpression, GetParameterExpressions(context, methodSymbol, parametersExpressions))
646637
{
647638
ValueType = methodSymbol.TypeArguments[0];
648639

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
%YAML 1.1
2+
%TAG !u! tag:unity3d.com,2011:
3+
--- !u!114 &11400000
4+
MonoBehaviour:
5+
m_ObjectHideFlags: 0
6+
m_CorrespondingSourceObject: {fileID: 0}
7+
m_PrefabInstance: {fileID: 0}
8+
m_PrefabAsset: {fileID: 0}
9+
m_GameObject: {fileID: 0}
10+
m_Enabled: 1
11+
m_EditorHideFlags: 0
12+
m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3}
13+
m_Name: AccessViaAlternateInvocee
14+
m_EditorClassIdentifier:
15+
serializedUdonProgramAsset: {fileID: 11400000, guid: 8876b3583cec99942b84819c559e175c,
16+
type: 2}
17+
udonAssembly:
18+
assemblyError:
19+
sourceCsScript: {fileID: 11500000, guid: f4acb09f599aab7418e27fcf7aff58e3, type: 3}
20+
behaviourSyncMode: 0
21+
compileErrors: []
22+
hasInteractEvent: 0
23+
serializationData:
24+
SerializedFormat: 2
25+
SerializedBytes:
26+
ReferencedUnityObjects: []
27+
SerializedBytesString:
28+
Prefab: {fileID: 0}
29+
PrefabModificationsReferencedUnityObjects: []
30+
PrefabModifications: []
31+
SerializationNodes:
32+
- Name: fieldDefinitions
33+
Entry: 7
34+
Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UdonSharp.Compiler.FieldDefinition,
35+
UdonSharp.Editor]], mscorlib
36+
- Name: comparer
37+
Entry: 7
38+
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String,
39+
mscorlib]], mscorlib
40+
- Name:
41+
Entry: 8
42+
Data:
43+
- Name:
44+
Entry: 12
45+
Data: 1
46+
- Name:
47+
Entry: 7
48+
Data:
49+
- Name: $k
50+
Entry: 1
51+
Data: tester
52+
- Name: $v
53+
Entry: 7
54+
Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
55+
- Name: <Name>k__BackingField
56+
Entry: 1
57+
Data: tester
58+
- Name: <UserType>k__BackingField
59+
Entry: 7
60+
Data: 3|System.RuntimeType, mscorlib
61+
- Name:
62+
Entry: 1
63+
Data: UdonSharp.Tests.IntegrationTestSuite, Assembly-CSharp
64+
- Name:
65+
Entry: 8
66+
Data:
67+
- Name: <SystemType>k__BackingField
68+
Entry: 7
69+
Data: 4|System.RuntimeType, mscorlib
70+
- Name:
71+
Entry: 1
72+
Data: VRC.Udon.UdonBehaviour, VRC.Udon
73+
- Name:
74+
Entry: 8
75+
Data:
76+
- Name: <SyncMode>k__BackingField
77+
Entry: 7
78+
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
79+
- Name:
80+
Entry: 6
81+
Data:
82+
- Name:
83+
Entry: 8
84+
Data:
85+
- Name: <IsSerialized>k__BackingField
86+
Entry: 5
87+
Data: false
88+
- Name: fieldAttributes
89+
Entry: 7
90+
Data: 5|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
91+
- Name:
92+
Entry: 12
93+
Data: 1
94+
- Name:
95+
Entry: 7
96+
Data: 6|System.NonSerializedAttribute, mscorlib
97+
- Name:
98+
Entry: 8
99+
Data:
100+
- Name:
101+
Entry: 13
102+
Data:
103+
- Name:
104+
Entry: 8
105+
Data:
106+
- Name: userBehaviourSource
107+
Entry: 6
108+
Data:
109+
- Name:
110+
Entry: 8
111+
Data:
112+
- Name:
113+
Entry: 8
114+
Data:
115+
- Name:
116+
Entry: 13
117+
Data:
118+
- Name:
119+
Entry: 8
120+
Data:

Assets/UdonSharp/Tests~/TestScripts/BugTests/AccessViaAlternateInvocee/AccessViaAlternateInvocee.asset.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+

2+
using UdonSharp;
3+
using UnityEngine;
4+
using VRC.SDKBase;
5+
using VRC.Udon;
6+
7+
namespace UdonSharp.Tests
8+
{
9+
/// <summary>
10+
/// Certain Udon externs are exposed not via the C# type or interface they're implemented on, but rather
11+
/// on some subclass directly. This test verifies that we can search for such an alternate subclass and
12+
/// use it for the extern invocation.
13+
/// </summary>
14+
[AddComponentMenu("Udon Sharp/Tests/AccessViaAlternateInvocee")]
15+
public class AccessViaAlternateInvocee : UdonSharpBehaviour
16+
{
17+
[System.NonSerialized]
18+
public IntegrationTestSuite tester;
19+
20+
public void ExecuteTests()
21+
{
22+
System.Type tyString = typeof(string);
23+
tester.TestAssertion("Access System.Type.Name", tyString.Name.Equals("String"));
24+
}
25+
}
26+
}

Assets/UdonSharp/Tests~/TestScripts/BugTests/AccessViaAlternateInvocee/AccessViaAlternateInvocee.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)