MerlinVR/UdonSharp

Calling a method on a class that overrides a UdonSharpBehavior method stub causes the compiler to crash Unity.

techanon opened this issue · 3 comments

Describe the bug in detail:
I am attempting to have one behavior class act as a proxy manager for various other behavior objects. On this manager class, I have a method that takes in a UdonSharpBehavior as a single parameter (dynamically telling the class to proxy the object). When I save the file with that method being present, unity shuts down/crashes. If I remove/comment-out that single line that does the method call, unity does not crash. If the method call is present when I try to boot up unity, it also crashes during the "Udon Compile" loading bar, preventing it from booting fully. I attempted a type casting to see if coercion would work, to no avail.

Provide steps/code to reproduce the bug:

  • Create a UdonSharpBehavior class.
  • Add a method on that class that receives a single UdonSharpBehavior object as a parameter.
  • Create a second UdonSharpBehavior class.
  • Add a method on the second class that calls the method of the first class, passing the the value this as the parameter (any UdonSharpBehavior type will cause the issue, including this)
  • Save file/trigger UdonSharp compiler.
  • Unity crashes

Expected behavior:
For Unity not to crash and instead produce an error in the console (or more optimistically, for it to successfully compile)

Additional Information:
Unity version: 2018.4.22f
UdonSharp version: 0.18.5

Do you have any example scripts? I can't repro this issue following your steps.

public class TestScriptA : UdonSharpBehaviour
{
    public void RunMethod(TestScriptB scriptB)
    {
        Debug.Log(scriptB);
    }
}
public class TestScriptB : UdonSharpBehaviour
{
    public TestScriptA scriptA;

    void Start()
    {
        scriptA.RunMethod(this);
    }
}

These compile correctly

After some fiddling around, I was able to recreate the crash with new classes. I found there are two things occurring in conjunction which triggers the crash. There was a bit more than my initial discovery found. Here are my sample classes:

Proxy

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class TestProxy : UdonSharpBehaviour
{
    private TestManager manager;
}

Caller

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class TestCaller : UdonSharpBehaviour
{
    private TestManager manager;
    private TestProxy proxy;

    void Start() {
        proxy = transform.Find("Target").GetComponent<TestProxy>();
    }

    public void SwitchContext() {
        // manager.PushContext(proxy); // offending line
    }
}

Manager

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class TestManager : UdonSharpBehaviour
{
    public UdonSharpBehaviour baseContext;
    private UdonSharpBehaviour[] stack = new UdonSharpBehaviour[0];

    void Start()
    {
        if (baseContext == null)
            Debug.Log("Error: A base context needs to be added in the inspector.");
        else PushContext(baseContext);
    }

    public void PushContext(UdonSharpBehaviour ctx) {
        int newStackLength = stack.Length + 1;
        var newStack = new UdonSharpBehaviour[newStackLength];
        for (int i = 0; i < stack.Length; i++) newStack[i] = stack[i];
        newStack[newStackLength] = ctx;
        stack = newStack;
        ctx.SetProgramVariable("manager", this);
    }
    

    public void PopContext()
    {
        if (stack.Length < 2) return; // do not allow the baseContext to be removed.
        int newStackLength = stack.Length - 1;
        var newStack = new UdonSharpBehaviour[newStackLength];
        for (int i = 0; i < newStackLength; i++) newStack[i] = stack[i];
        stack = newStack;
    }
    
// The offending code causing the method call to fail, any one of these

    // public object GetProgramVariable(string name)
    // {
    //     return stack[stack.Length - 1].GetProgramVariable(name);
    // }
    // public void SetProgramVariable(string name, object value)
    // {
    //     stack[stack.Length - 1].SetProgramVariable(name, value);
    // }
    public void SendCustomEvent(string eventName)
    {
        stack[stack.Length - 1].SendCustomEvent(eventName);
    }
    // public void SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget target, string eventName)
    // {
    //     stack[stack.Length - 1].SendCustomNetworkEvent(target, eventName);
    // }
}

Trying to override one of these methods for the sake of proxying to the stack causes the compiler to fail, but ONLY when another class calls one of the methods it seems.. Does not happen when an outside class does a public field assignment, just the method call. Take the code for these three classes and compile them, then uncomment the "offending line" and compile again. The latter should crash.

If the methods cannot be overridden for whatever reason, that's fine. This was merely experimental work. But the crash is where my issue lies.

You can only override the methods on UdonSharpBehaviours that are marked virtual. Do not attempt to override any other methods on the behaviour, it is not supported. If you want to emulate them then you need to make your own unique names for them. I will add checking for attempts to override these methods.