javaluacallbackluaj

How can I pass objects to an exposed luaj function?


I am trying to build a controller using Luaj + java. I have the following java classes

public class Duck {
  public void talk() { System.out.println("Duck quacks!"); }
  public void walk() { System.out.println("Duck walks!"); }
}

public class Person {
  public void talk() { System.out.println("Person talks!"); }
  public void walk() { System.out.println("Person walks!"); }
}

and the following lua script for the controller:

onTalk(obj) 
  obj:talk();
end

onWalk(obj)
   obj:walk();
end

I would ideally like to define one controller (written in lua) where I will keep all of the program's logic, and I would like to expose API from that controller to my java code. I was trying to use the following approach:

ScriptEngineManager sem     = new ScriptEngineManager();
ScriptEngine        engine  = sem.getEngineByExtension(".lua");
ScriptEngineFactory factory = engine.getFactory();

// Script defined above
CompiledScript cs = ((Compilable)engine).compile(MY_LUA_SCRIPT);
SimpleBindings b = new SimpleBindings();

b = newSimpletBindings();

LuaValue onWalkHandler = (LuaValue)b.get("onWalk");
//func.call(LuaValue.valueOf(duck)); // Passing duck object does not work ???

I am not able to pass the object to the LuaValue. How can I pass a java object to the lua script?

PS: In general, when using Java and embedded scripting, do people bundle functions in one script, or is there a separate script for every callback?


Solution

  • I was searching entries and found your unanswered. Your question was interesting and made me search. Then I realized that it was asked 2 years ago... I hope my answer can be useful to somebody else! Code based on luaj-3.0-alpha1.

    We need a valid Lua script (you forgot the function keyword):

    function onTalk(javaObj)
      print(type(javaObj) .. " " .. tostring(javaObj))
      print(javaObj.name)
      javaObj:talk()
      return true
    end
    
    function onWalk(javaObj)
      javaObj:walk()
      return 1, "km"
    end
    

    I added a bit of trace... I also made classes similar to your:

    class Dog
    {
      public String name;
      Dog(String n) { name = n; }
      public void talk() { System.out.println("Dog " + name + " barks!"); }
      public void walk() { System.out.println("Dog " + name + " walks..."); }
    }
    class Cat
    {
      String name;
      Cat(String n) { name = n; }
      public void talk() { System.out.println("Cat " + name + " meows!"); }
      public void walk() { System.out.println("Cat " + name + " walks..."); }
    }
    

    Adding a field to test this too. For my test, I just declared the classes inside the method creating their instances:

    Dog dog = new Dog("Rex");
    Cat cat = new Cat("Felix");
    

    I first tried to convert these Java objects to Lua, using LuaValue luaDog = LuaValue.userdataOf(dog); but it doesn't work: we indeed have userdata, as shown by the traces, but no metatable, so we cannot call the methods nor access the fields.

    After searching a lot, I found out the right incantation:

    CompiledScript script = ((Compilable) scriptEngine).compile(reader);
    Bindings sb = new SimpleBindings();
    script.eval(sb); // Put the Lua functions into the sb environment
    LuaValue luaDog = CoerceJavaToLua.coerce(dog); // Java to Lua
    LuaFunction onTalk = (LuaFunction) sb.get("onTalk"); // Get Lua function
    LuaValue b = onTalk.call(luaDog); // Call the function
    System.out.println("onTalk answered: " + b);
    LuaFunction onWalk = (LuaFunction) sb.get("onWalk");
    LuaValue[] dogs = { luaDog };
    Varargs dist = onWalk.invoke(LuaValue.varargsOf(dogs)); // Alternative
    System.out.println("onWalk returned: " + dist);
    

    I appreciate the Luaj API... :-) Probably more made for Java programmers while other libraries seem to aim more at Lua / C programmers...