Skip to content
/ luaj Public
forked from farmboy0/luaj

Lightweight, fast, Java-centric Lua interpreter written for JME and JSE, with string, table, package, math, io, os, debug, coroutine & luajava libraries, JSR-223 bindings, all metatags, weak tables and unique direct lua-to-java-bytecode compiling.

License

Notifications You must be signed in to change notification settings

serasset/luaj

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is a fork!

This repository has been forked from the original CVS sources of Luaj. The commit history has been converted to make sure that the original work of James Roseborough and Ian Farmer is not lost. Unfortunately, I was not able to contact either James or Ian to hand over ownership of the Github organization/repo as I have originally intended to. The community however seems interested enough to continue work on the original sources and therefore I have decided to make sure that any useful pull requests that may add some value to the original code base shall be merged in from now on.
-- Benjamin P. Jung, Jan. 26th 2018

Getting Started with LuaJ

James Roseborough, Ian Farmer, Version 3.0.2

Copyright © 2009-2014 Luaj.org. Freely available under the terms of the Luaj license.


introduction · examples · concepts · libraries · luaj api · parser · building · downloads · release notes

Goals of Luaj

Luaj is a lua interpreter based on the 5.2.x version of lua with the following goals in mind:
  • Java-centric implementation of lua vm built to leverage standard Java features.
  • Lightweight, high performance execution of lua.
  • Multi-platform to be able to run on JME, JSE, or JEE environments.
  • Complete set of libraries and tools for integration into real-world projects.
  • Dependable due to sufficient unit testing of vm and library features.

Luaj version and Lua Versions

Luaj 3.0.x

Support for lua 5.2.x features:
  • _ENV environments model.
  • yield from pcall or metatags.
  • Bitwise operator library.
It also includes miscellaneous improvements over luaj 2.0.x:
  • Better thread safety.
  • More compatible table behavior.
  • Better coroutine-related garbage collection.
  • Maven integration.
  • Better debug reporting when using closures.
  • Line numbers in parse syntax tree.

Luaj 2.0.x

Support for lua 5.1.x features, plus:
  • Support for compiling lua source code into Java source code.
  • Support for compiling lua bytecode directly into Java bytecode.
  • Stackless vm design centered around dynamically typed objects.
  • Good alignment with C API (see names.csv for details)
  • Implementation of weak keys and values, and all metatags.

Luaj 1.0.x

Support for most lua 5.1.x features.

Performance

Good performance is a major goal of luaj. The following table provides measured execution times on a subset of benchmarks from the computer language benchmarks game in comparison with the standard C distribution.
Project Version Mode    Benchmark execution time (sec)    Language Sample command
binarytrees 15 fannkuch 10 nbody 1e6 nsieve 9
luaj 3.0 -b (luajc) 2.980 5.073 16.794 11.274 Java java -cp luaj-jse-3.0.2.jar;bcel-5.2.jar lua -b fannkuch.lua 10
-n (interpreted) 12.838 23.290 36.894 15.163 java -cp luaj-jse-3.0.2.jar lua -n fannkuch.lua 10
lua 5.1.4 17.637 16.044 15.201 5.477 C lua fannkuch.lua 10
jill 1.0.1 44.512 54.630 72.172 20.779 Java
kahlua 1.0 jse 22.963 63.277 68.223 21.529 Java
mochalua 1.0 50.457 70.368 82.868 41.262 Java

Luaj in interpreted mode performs well for the benchmarks, and even better when the lua-to-java-bytecode (luajc) compiler is used, and actually executes faster than C-based lua in some cases.
It is also faster than Java-lua implementations Jill, Kahlua, and Mochalua for all benchmarks tested.

Run a lua script in Java SE

From the main distribution directory line type:

	java -cp luaj-jse-3.0.2.jar lua examples/lua/hello.lua

You should see the following output:

	hello, world

To see how luaj can be used to acccess most Java API's including swing, try:

	java -cp luaj-jse-3.0.2.jar lua examples/lua/swingapp.lua

Links to sources:

	examples/lua/hello.lua
	examples/lua/swingapp.lua

Compile lua source to lua bytecode

From the main distribution directory line type:

	java -cp luaj-jse-3.0.2.jar luac examples/lua/hello.lua
	java -cp luaj-jse-3.0.2.jar lua luac.out

The compiled output "luac.out" is lua bytecode and should run and produce the same result.

Compile lua source or bytecode to java bytecode

Luaj can compile lua sources or binaries directly to java bytecode if the bcel library is on the class path. From the main distribution directory line type:

	ant bcel-lib
	java -cp "luaj-jse-3.0.2.jar;lib/bcel-5.2.jar" luajc -s examples/lua -d . hello.lua
	java -cp "luaj-jse-3.0.2.jar;." lua -l hello

The output hello.class is Java bytecode, should run and produce the same result. There is no runtime dependency on the bcel library, but the compiled classes must be in the class path at runtime, unless runtime jit-compiling via luajc and bcel are desired (see later sections).

Lua scripts can also be run directly in this mode without precompiling using the lua command with the -b option and providing the bcel library in the class path:

	java -cp "luaj-jse-3.0.2.jar;lib/bcel-5.2.jar" lua -b examples/lua/hello.lua

Run a script in a Java Application

A simple hello, world example in luaj is:

	import org.luaj.vm2.*;
	import org.luaj.vm2.lib.jse.*;

	Globals globals = JsePlatform.standardGlobals();
	LuaValue chunk = globals.load("print 'hello, world'");
	chunk.call();
	

Loading from a file is done via Globals.loadFile():

	LuaValue chunk = globals.loadfile("examples/lua/hello.lua");

Chunks can also be loaded from a Reader as text source

	chunk = globals.load(new StringReader("print 'hello, world'"), "main.lua");

or an InputStream to be loaded as text source "t", or binary lua file "b":

	chunk = globals.load(new FileInputSStream("examples/lua/hello.lua"), "main.lua", "bt"));

A simple example may be found in

	examples/jse/SampleJseMain.java

You must include the library luaj-jse-3.0.2.jar in your class path.

Run a script in a MIDlet

For MIDlets the JmePlatform is used instead:

	import org.luaj.vm2.*;
	import org.luaj.vm2.lib.jme.*;

	Globals globals = JmePlatform.standardGlobals();
	LuaValue chunk = globals.loadfile("examples/lua/hello.lua");
	chunk.call();

The file must be a resource within within the midlet jar for the loader to find it. Any files included via require() must also be part of the midlet resources.

A simple example may be found in

	examples/jme/SampleMIDlet.java

You must include the library luaj-jme-3.0.2.jar in your midlet jar.

An ant script to build and run the midlet is in

	build-midlet.xml

You must install the wireless toolkit and define WTK_HOME for this script to work.

Run a script using JSR-223 Dynamic Scripting

The standard use of JSR-223 scripting engines may be used:

	ScriptEngineManager mgr = new ScriptEngineManager();
	ScriptEngine e = mgr.getEngineByName("luaj");
	e.put("x", 25);
	e.eval("y = math.sqrt(x)");
	System.out.println( "y="+e.get("y") );

You can also look up the engine by language "lua" or mimetypes "text/lua" or "application/lua".

All standard aspects of script engines including compiled statements are supported.

You must include the library luaj-jse-3.0.2.jar in your class path.

A working example may be found in

	examples/jse/ScriptEngineSample.java

To compile and run it using Java 1.6 or higher:

	javac -cp luaj-jse-3.0.2.jar examples/jse/ScriptEngineSample.java
	java -cp "luaj-jse-3.0.2.jar;examples/jse" ScriptEngineSample

Excluding the lua bytecode compiler

By default, the compiler is included whenever standardGlobals() or debugGlobals() are called.
Without a compiler, files can still be executed, but they must be compiled elsewhere beforehand. The "luac" utility is provided in the jse jar for this purpose, or a standard lua compiler can be used.

To exclude the lua-to-lua-bytecode compiler, do not call standardGlobals() or debugGlobals() but instead initialize globals with including only those libraries that are needed and omitting the line:

	org.luaj.vm2.compiler.LuaC.install(globals);

Including the LuaJC lua-bytecode-to-Java-bytecode compiler

To compile from lua to Java bytecode for all lua loaded at runtime, install the LuaJC compiler into a globals object use:

	org.luaj.vm2.jse.luajc.LuaJC.install(globals);

This will compile all lua bytecode into Java bytecode, regardless of if they are loaded as lua source or lua binary files.

The requires bcel to be on the class path, and the ClassLoader of JSE or CDC.

Globals

The old notion of platform has been replaced with creation of globals. The Globals class holds global state needed for executing closures as well as providing convenience functions for compiling and loading scripts.

Platform

To simplify construction of Globals, and encapsulate differences needed to support the diverse family of Java runtimes, luaj uses a Platform notion. Typically, a platform is used to construct a Globals, which is then provided as a global environment for client scripts.

JsePlatform

The JsePlatform class can be used as a factory for globals in a typical Java SE application. All standard libraries are included, as well as the luajava library. The default search path is the current directory, and the math operations include all those supported by Java SE.

Android

Android applications should use the JsePlatform, and can include the Luajava library to simplify access to underlying Android APIs.
A specialized Globals.finder should be provided to find scripts and data for loading. See examples/android/src/android/LuajView.java for an example that loads from the "res" Android project directory. The ant build script is examples/android/build.xml.

Applet

Applets in browsers should use the JsePlatform. The permissions model in applets is highly restrictive, so a specialization of the Luajava library must be used that uses default class loading. This is illustrated in the sample Applet examples/jse/SampleApplet.java, which can be built using build-applet.xml.

JmePlatform

The JmePlatform class can be used to set up the basic environment for a Java ME application. The default search path is limited to the jar resources, and the math operations are limited to those supported by Java ME. All libraries are included except luajava, and the os, io, and math libraries are limited to those functions that can be supported on that platform.

MIDlet

MIDlets require the JmePlatform.
The JME platform has several limitations which carry over to luaj. In particular Globals.finder is overridden to load as resources, so scripts should be colocated with class files in the MIDlet jar file. Luajava cannot be used. Camples code is in
examples/jme/SampleMIDlet.java, which can be built using build-midlet.xml.

Thread Safety

Luaj 3.0 can be run in multiple threads, with the following restrictions:

  • Each thread created by client code must be given its own, distinct Globals instance
  • Each thread must not be allowed to access Globals from other threads
  • Metatables for Number, String, Thread, Function, Boolean, and and Nil are shared and therefore should not be mutated once lua code is running in any thread.

For an example of loading allocating per-thread Globals and invoking scripts in multiple threads see examples/jse/SampleMultiThreaded.java

As an alternative, the JSR-223 scripting interface can be used, and should always provide a separate Globals instance per script engine instance by using a ThreadLocal internally.

Sandboxing

Lua and luaj are allow for easy sandboxing of scripts in a server environment.

Considerations include

  • The debug and luajava library give unfettered access to the luaj vm and java vm so can be abused
  • Portions of the os, io, and coroutine libraries are prone to abuse
  • Rogue scripts may need to be throttled or killed
  • Shared metatables (string, booleans, etc.) need to be made read-only or isolated via class loaders such as LuajClassLoader

Luaj provides sample code covering various approaches:

Standard Libraries

Libraries are coded to closely match the behavior specified in See standard lua documentation for details on the library API's

The following libraries are loaded by both JsePlatform.standardGlobals() and JmePlatform.standardGlobals():

	base
	bit32
	coroutine
	io
	math
	os
	package
	string
	table

The JsePlatform.standardGlobals() globals also include:

	luajava 

The JsePlatform.debugGlobals() and JsePlatform.debugGlobals() functions produce globals that include:

	debug

I/O Library

The implementation of the io library differs by platform owing to platform limitations.

The JmePlatform.standardGlobals() instantiated the io library io in

	src/jme/org/luaj/vm2/lib/jme/JmeIoLib.java

The JsePlatform.standardGlobals() includes support for random access and is in

	src/jse/org/luaj/vm2/lib/jse/JseIoLib.java

OS Library

The implementation of the os library also differs per platform.

The basic os library implementation us used by JmePlatform and is in:

	src/core/org/luaj/lib/OsLib.java

A richer version for use by JsePlatform is :

	src/jse/org/luaj/vm2/lib/jse/JseOsLib.java

Time is a represented as number of seconds since the epoch, and locales are not implemented.

Coroutine Library

The coroutine library is implemented using one JavaThread per coroutine. This allows coroutine.yield() can be called from anywhere, as with the yield-from-anywhere patch in C-based lua.

Luaj uses WeakReferences and the OrphanedThread error to ensure that coroutines that are no longer referenced are properly garbage collected. For thread safety, OrphanedThread should not be caught by Java code. See LuaThread and OrphanedThread javadoc for details. The sample code in examples/jse/CollectingOrphanedCoroutines.java provides working examples.

Debug Library

The debug library is not included by default by JmePlatform.standardGlobals() or JsePlatform.standardGlobsls() .

The functions JmePlatform.debugGlobals() and JsePlatform.debugGlobsls() create globals that contain the debug library in addition to the other standard libraries.

To install dynamically from lua use java-class-based require::

	require 'org.luaj.vm2.lib.DebugLib'

The lua command line utility includes the debug library by default.

The JsePlatform.standardGlobals() includes the luajava library, which simplifies binding to Java classes and methods. It is patterned after the original luajava project.

The following lua script will open a swing frame on Java SE:

	jframe = luajava.bindClass( "javax.swing.JFrame" )
	frame = luajava.newInstance( "javax.swing.JFrame", "Texts" );
	frame:setDefaultCloseOperation(jframe.EXIT_ON_CLOSE)
	frame:setSize(300,400)
	frame:setVisible(true)

See a longer sample in examples/lua/swingapp.lua for details, including a simple animation loop, rendering graphics, mouse and key handling, and image loading. Or try running it using:

	java -cp luaj-jse-3.0.2.jar lua examples/lua/swingapp.lua

The Java ME platform does not include this library, and it cannot be made to work because of the lack of a reflection API in Java ME.

The lua connand line tool includes luajava.

API Javadoc

The javadoc for the main classes in the LuaJ API are on line at
	 https://luaj.org/luaj/3.0/api

You can also build a local version from sources using

	 ant doc

LuaValue and Varargs

All lua value manipulation is now organized around LuaValue which exposes the majority of interfaces used for lua computation.
	 org.luaj.vm2.LuaValue

Common Functions

LuaValue exposes functions for each of the operations in LuaJ. Some commonly used functions and constants include:
	call();               // invoke the function with no arguments
	call(LuaValue arg1);  // call the function with 1 argument
	invoke(Varargs arg);  // call the function with variable arguments, variable return values
	get(int index);       // get a table entry using an integer key
	get(LuaValue key);    // get a table entry using an arbitrary key, may be a LuaInteger
	rawget(int index);    // raw get without metatable calls
	valueOf(int i);       // return LuaValue corresponding to an integer
	valueOf(String s);    // return LuaValue corresponding to a String
	toint();              // return value as a Java int
	tojstring();          // return value as a Java String
	isnil();              // is the value nil
	NIL;                  // the value nil
	NONE;                 // a Varargs instance with no values	 

Varargs

The interface Varargs provides an abstraction for both a variable argument list and multiple return values. For convenience, LuaValue implements Varargs so a single value can be supplied anywhere variable arguments are expected.
	 org.luaj.vm2.Varargs

Common Functions

Varargs exposes functions for accessing elements, and coercing them to specific types:
	narg();                 // return number of arguments
	arg1();                 // return the first argument
	arg(int n);             // return the nth argument
	isnil(int n);           // true if the nth argument is nil
	checktable(int n);      // return table or throw error
	optlong(int n,long d);  // return n if a long, d if no argument, or error if not a long

See the Varargs API for a complete list.

LibFunction

The simplest way to implement a function is to choose a base class based on the number of arguments to the function. LuaJ provides 5 base classes for this purpose, depending if the function has 0, 1, 2, 3 or variable arguments, and if it provide multiple return values.
	 org.luaj.vm2.lib.ZeroArgFunction
	 org.luaj.vm2.lib.OneArgFunction
	 org.luaj.vm2.lib.TwoArgFunction
	 org.luaj.vm2.lib.ThreeArgFunction
	 org.luaj.vm2.lib.VarArgFunction

Each of these functions has an abstract method that must be implemented, and argument fixup is done automatically by the classes as each Java function is invoked.

An example of a function with no arguments but a useful return value might be:

	pubic class hostname extends ZeroArgFunction {
		public LuaValue call() {
			return valueOf(java.net.InetAddress.getLocalHost().getHostName());
		}
	}

The value env is the environment of the function, and is normally supplied by the instantiating object whenever default loading is used.

Calling this function from lua could be done by:

 
	local hostname = require( 'hostname' )

while calling this function from Java would look like:

 
	new hostname().call();

Note that in both the lua and Java case, extra arguments will be ignored, and the function will be called.
Also, no virtual machine instance is necessary to call the function. To allow for arguments, or return multiple values, extend one of the other base classes.

Libraries of Java Functions

When require() is called, it will first attempt to load the module as a Java class that implements LuaFunction. To succeed, the following requirements must be met:
  • The class must be on the class path with name, modname.
  • The class must have a public default constructor.
  • The class must inherit from LuaFunction.

If luaj can find a class that meets these critera, it will instantiate it, cast it to LuaFunction then call() the instance with two arguments: the modname used in the call to require(), and the environment for that function. The Java may use these values however it wishes. A typical case is to create named functions in the environment that can be called from lua.

A complete example of Java code for a simple toy library is in examples/jse/hyperbolic.java

import org.luaj.vm