CraftTweaker / ZenScript

MIT License
77 stars 22 forks source link

Using a function as a parameter for a custom function causes ArrayIndexOutOfBoundsException #26

Open ErzengelLichtes opened 5 years ago

ErzengelLichtes commented 5 years ago

Creating a custom function that accepts a function causes a java.lang.ArrayIndexOutOfBoundsException

Example Script

function test(callback as function(int)bool)
{
    if(callback(3))
    {
        print("callback success");
    }
}
//Note: The below is unnecessary but provided for completeness
test(function(a as int) as bool
{
    print("value was " ~ a);
    return true;
} as function(int)bool);

Exception in crafttweaker.log

[INITIALIZATION][CLIENT][ERROR] [crafttweaker]: Error executing {[0:crafttweaker]: testFunction.zs}: 69
java.lang.ArrayIndexOutOfBoundsException: 69
    at org.objectweb.asm.Type.getArgumentTypes(Unknown Source)
    at org.objectweb.asm.commons.LocalVariablesSorter.<init>(Unknown Source)
    at org.objectweb.asm.commons.LocalVariablesSorter.<init>(Unknown Source)
    at stanhebben.zenscript.util.MethodOutput.<init>(MethodOutput.java:26)
    at stanhebben.zenscript.ZenModule.compileScripts(ZenModule.java:114)
    at crafttweaker.runtime.CrTTweaker.loadScript(CrTTweaker.java:211)
    at crafttweaker.runtime.CrTTweaker.loadScript(CrTTweaker.java:101)
    at crafttweaker.mc1120.events.CommonEventHandler.registerRecipes(CommonEventHandler.java:69)
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_818_CommonEventHandler_registerRecipes_Register.invoke(.dynamic)
    at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:90)
    at net.minecraftforge.fml.common.eventhandler.EventBus$1.invoke(EventBus.java:144)
    at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:182)
    at net.minecraftforge.registries.GameData.fireRegistryEvents(GameData.java:814)
    at net.minecraftforge.common.crafting.CraftingHelper.loadRecipes(CraftingHelper.java:624)
    at net.minecraftforge.fml.common.Loader.initializeMods(Loader.java:742)
    at net.minecraftforge.fml.client.FMLClientHandler.finishMinecraftLoading(FMLClientHandler.java:336)
    at net.minecraft.client.Minecraft.func_71384_a(Minecraft.java:535)
    at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:3931)
    at net.minecraft.client.main.Main.main(SourceFile:123)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
    at net.minecraft.launchwrapper.Launch.main(Launch.java:28)

CraftTweaker version: CraftTweaker2-1.12-4.1.15

kindlich commented 5 years ago

ZS does not allow for functions as parameters because internally it uses different types. This would only be possible if you were to use a function that is registered to ZS as a functional Interface (e.g. IItemCondition)

ErzengelLichtes commented 5 years ago

Short of forking CT, can we register a functional interface using ZenClasses?

kindlich commented 5 years ago

Take this example (the 2nd function parameter for recipes)

https://github.com/CraftTweaker/CraftTweaker/blob/1.12/CraftTweaker2-API/src/main/java/crafttweaker/api/recipes/IRecipeAction.java

hnOsmium0001 commented 5 years ago

So it's like using lambda needs an interface? If we are allowed to define interfaces or function signatures, it seems this is possible.

kindlich commented 5 years ago

You can use lambdas when you have registered a functional interface using Java Code. You cannot create functional interfaces from within ZS, but you can implement them (that is what a lambda is)

kindlich commented 4 years ago

Hey there,

I apologize for the late reply. Currently I have a rudimentary implementation for function types as parameters and members in my local dev environment.

var myTestFun as function(string)void = function(s as string) as void {print(s);};
myTestFun('Hello World');

function myAcceptFun(consumer as function(string)void) as void {
   consumer('hello from inside myAcceptFun');
}

myAcceptFun(myTestFun);
myAcceptFun(function(s as string) as void {print('Another thing: ' ~ s);});

Would print

Hello World
hello from inside myAcceptFun
Another thing: hello from inside myAcceptFun

And

zenClass myClass {
    var consumer as function(string)void;
    zenConstructor(consumer as function(string)void) {
        this.consumer = consumer;
    }
    function acceptString(x as string) as void {
        consumer(x);
    }
}
myClass(function(x as string) as void {print('inside fun: ' ~ x);}).acceptString('abc');

Would print

inside fun: abc