Supply environment as extra argument when loading functions.

This commit is contained in:
James Roseborough
2013-01-21 21:37:44 +00:00
parent 17db89fd3b
commit 706d9ba47e
6 changed files with 259 additions and 87 deletions

View File

@@ -16,7 +16,7 @@
Getting Started with LuaJ
</h1>
James Roseborough, Ian Farmer, Version 3.0-alpha1
James Roseborough, Ian Farmer, Version 3.0-alpha2
<p>
<small>
Copyright &copy; 2009-2012 Luaj.org.
@@ -117,7 +117,7 @@ in comparison with the standard C distribution.
<td>16.794</td>
<td>11.274</td>
<td>Java</td>
<td>java -cp luaj-jse-3.0-alpha1.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
<td>java -cp luaj-jse-3.0-alpha2.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
<tr valign="top">
<td></td>
<td></td>
@@ -127,7 +127,7 @@ in comparison with the standard C distribution.
<td>36.894</td>
<td>15.163</td>
<td></td>
<td>java -cp luaj-jse-3.0-alpha1.jar lua -n fannkuch.lua 10</td></tr>
<td>java -cp luaj-jse-3.0-alpha2.jar lua -n fannkuch.lua 10</td></tr>
<tr valign="top">
<td>lua</td>
<td>5.1.4</td>
@@ -183,7 +183,7 @@ It is also faster than Java-lua implementations Jill, Kahlua, and Mochalua for a
From the main distribution directory line type:
<pre>
java -cp lib/luaj-jse-3.0-alpha1.jar lua examples/lua/hello.lua
java -cp lib/luaj-jse-3.0-alpha2.jar lua examples/lua/hello.lua
</pre>
<p>
@@ -195,7 +195,7 @@ You should see the following output:
To see how luaj can be used to acccess most Java API's including swing, try:
<pre>
java -cp lib/luaj-jse-3.0-alpha1.jar lua examples/lua/swingapp.lua
java -cp lib/luaj-jse-3.0-alpha2.jar lua examples/lua/swingapp.lua
</pre>
<h2>Compile lua source to lua bytecode</h2>
@@ -204,8 +204,8 @@ To see how luaj can be used to acccess most Java API's including swing, try:
From the main distribution directory line type:
<pre>
java -cp lib/luaj-jse-3.0-alpha1.jar luac examples/lua/hello.lua
java -cp lib/luaj-jse-3.0-alpha1.jar lua luac.out
java -cp lib/luaj-jse-3.0-alpha2.jar luac examples/lua/hello.lua
java -cp lib/luaj-jse-3.0-alpha2.jar lua luac.out
</pre>
<p>
@@ -219,8 +219,8 @@ Luaj can compile lua sources or binaries directly to java bytecode if the bcel l
<pre>
ant bcel-lib
java -cp &quot;lib/luaj-jse-3.0-alpha1.jar;lib/bcel-5.2.jar&quot; luajc -s examples/lua -d . hello.lua
java -cp &quot;lib/luaj-jse-3.0-alpha1.jar;.&quot; lua -l hello
java -cp &quot;lib/luaj-jse-3.0-alpha2.jar;lib/bcel-5.2.jar&quot; luajc -s examples/lua -d . hello.lua
java -cp &quot;lib/luaj-jse-3.0-alpha2.jar;.&quot; lua -l hello
</pre>
<p>
@@ -231,7 +231,7 @@ but the compiled classes must be in the class path at runtime, unless runtime ji
<p>
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-b</em></b> option and providing the <em>bcel</em> library in the class path:
<pre>
java -cp &quot;lib/luaj-jse-3.0-alpha1.jar;lib/bcel-5.2.jar&quot; lua -b examples/lua/hello.lua
java -cp &quot;lib/luaj-jse-3.0-alpha2.jar;lib/bcel-5.2.jar&quot; lua -b examples/lua/hello.lua
</pre>
@@ -256,7 +256,7 @@ A simple example may be found in
</pre>
<p>
You must include the library <b>lib/luaj-jse-3.0-alpha1.jar</b> in your class path.
You must include the library <b>lib/luaj-jse-3.0-alpha2.jar</b> in your class path.
<h2>Run a script in a MIDlet</h2>
@@ -283,7 +283,7 @@ A simple example may be found in
</pre>
<p>
You must include the library <b>lib/luaj-jme-3.0-alpha1.jar</b> in your midlet jar.
You must include the library <b>lib/luaj-jme-3.0-alpha2.jar</b> in your midlet jar.
<p>
An ant script to build and run the midlet is in
@@ -311,7 +311,7 @@ The standard use of JSR-223 scripting engines may be used:
All standard aspects of script engines including compiled statements should be supported.
<p>
You must include the library <b>lib/luaj-jse-3.0-alpha1.jar</b> in your class path.
You must include the library <b>lib/luaj-jse-3.0-alpha2.jar</b> in your class path.
<p>
A working example may be found in
@@ -323,7 +323,7 @@ To compile and run it using Java 1.6 or higher:
<pre>
javac examples/jse/ScriptEngineSample.java
java -cp &quot;lib/luaj-jse-3.0-alpha1.jar;examples/jse&quot; ScriptEngineSample
java -cp &quot;lib/luaj-jse-3.0-alpha2.jar;examples/jse&quot; ScriptEngineSample
</pre>
<h2>Excluding the lua bytecode compiler</h2>
@@ -487,7 +487,7 @@ The following lua script will open a swing frame on Java SE:
See a longer sample in <em>examples/lua/swingapp.lua</em> for details, including a simple animation loop, rendering graphics, mouse and key handling, and image loading.
Or try running it using:
<pre>
java -cp lib/luaj-jse-3.0-alpha1.jar lua examples/lua/swingapp.lua
java -cp lib/luaj-jse-3.0-alpha2.jar lua examples/lua/swingapp.lua
</pre>
<p>
@@ -574,11 +574,11 @@ 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.
<p>
For example, to implement a &quot;hello, world&quot; function, we could supply:
An example of a function with no arguments but a useful return value might be:
<pre>
pubic class hello extends ZeroArgFunction {
pubic class hostname extends ZeroArgFunction {
public LuaValue call() {
env.get("print").call(valueOf("hello, world"));
return valueOf(java.net.InetAddress.getLocalHost().getHostName());
}
}
</pre>
@@ -589,20 +589,90 @@ by the instantiating object whenever default loading is used.
<p>
Calling this function from lua could be done by:
<pre>
require( 'hello' )()
local hostname = require( 'hostname' )
</pre>
while calling this function from Java would look like:
<pre>
new hello().call();
new hostname().call();
</pre>
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.
<h2>Libraries of Java Functions</h2>
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:
<ul>
<li>The class must be on the class path with name, <em>modname</em>.</li>
<li>The class must have a public default constructor.</li>
<li>The class must inherit from LuaFunction.</li>
</ul>
<p>
If luaj can find a class that meets these critera, it will instantiate it, cast it to <em>LuaFunction</em>
then call() the instance with two arguments:
the <em>modname</em> 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.
<p>
A complete example of Java code for a simple toy library is in <a href="examples/jse/hyperbolic.java">examples/jse/hyperbolic.java</a>
<pre>
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.*;
public class hyperbolic extends TwoArgFunction {
public hyperbolic() {}
public LuaValue call(LuaValue modname, LuaValue env) {
LuaValue library = tableOf();
library.set( "sinh", new sinh() );
library.set( "cosh", new cosh() );
env.set( "hyperbolic", library );
return library;
}
static class sinh extends OneArgFunction {
public LuaValue call(LuaValue x) {
return LuaValue.valueOf(Math.sinh(x.checkdouble()));
}
}
static class cosh extends OneArgFunction {
public LuaValue call(LuaValue x) {
return LuaValue.valueOf(Math.cosh(x.checkdouble()));
}
}
}
</pre>
In this case the call to require invokes the library itself to initialize it. The library implementation
puts entries into a table, and stores this table in the environment.
<p>
The lua script used to load and test it is in <a href="examples/lua/hyperbolicapp.lua">examples/lua/hyperbolicapp.lua</a>
<pre>
require 'hyperbolic'
print('hyperbolic', hyperbolic)
print('hyperbolic.sinh', hyperbolic.sinh)
print('hyperbolic.cosh', hyperbolic.cosh)
print('sinh(0.5)', hyperbolic.sinh(0.5))
print('cosh(0.5)', hyperbolic.cosh(0.5))
</pre>
For this example to work the code in <em>hyperbolic.java</em> must be compiled and put on the class path.
<h2>Closures</h2>
Closures still exist in this framework, but are optional, and are only used to implement lua bytecode execution.
Closures still exist in this framework, but are optional, and are only used to implement lua bytecode execution,
and is generally not directly manipulated by the user of luaj.
<p>
See the <a href="http://luaj.sourceforge.net/api/3.0/org/luaj/vm2/LuaClosure.html">org.luaj.vm2.LuaClosure</a>
javadoc for details on using that class directly.
<h1>6 - <a name="6">Parser</a></h1>
@@ -712,11 +782,13 @@ and LuaForge:
<tr valign="top"><td>&nbsp;&nbsp;<b>2.0</b></td><td><ul>
<li>Initial release of 2.0 version </li>
</ul></td></tr>
<tr valign="top"><td>&nbsp;&nbsp;<b>2.0.1</b></td><td><ul>
<li>Improve correctness of singleton construction related to static initialization </li>
<li>Fix nan-related error in constant folding logic that was failing on some JVMs </li>
<li>JSR-223 fixes: add META-INF/services entry in jse jar, improve bindings implementation </li>
</ul></td></tr>
<tr valign="top"><td>&nbsp;&nbsp;<b>2.0.2</b></td><td><ul>
<li>JSR-223 bindings change: non Java-primitives will now be passed as LuaValue </li>
<li>JSR-223 enhancement: allow both ".lua" and "lua" as extensions in getScriptEngine() </li>
@@ -725,16 +797,28 @@ and LuaForge:
<li>Enhance javadoc, put it <a href="docs/api/index.html">in distribution</a> and <a href="http://luaj.sourceforge.net/api/3.0/index.html">on line</a></li>
<li>Major refactor of luajava type coercion logic, improve method selection.</li>
<li>Add lib/luaj-sources-2.0.2.jar for easier integration into an IDE such as Netbeans </li>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.0-alpha1</b></td><td><ul>
<tr valign="top"><td>&nbsp;&nbsp;<b>2.0.3</b></td><td><ul>
<li>Improve coroutine state logic including let unreferenced coroutines be garbage collected </li>
<li>Fix lua command vararg values passed into main script to match what is in global arg table </li>
<li>Add arithmetic metatag processing when left hand side is a number and right hand side has metatable </li>
<li>Fix load(func) when mutiple string fragments are supplied by calls to func </li>
<li>Allow access to public members of private inner classes where possible </li>
<li>Add line and column info to org.luaj.vm2.ast parse tree elements generated using LuaParser </li>
<li>Turn on error reporting in LuaParser so line numbers ar available in ParseException </li>
<li>Improve compatibility of table.remove() </li>
<li>Disallow base library setfenv() calls on Java functions </li>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.0-alpha1</b></td><td><ul>
<li>Convert internal and external API's to match lua 5.2.x environment changes </li>
<li>Add bit32 library </li>
<li>Add explicit Globals object to manage global state, especially to imrpove thread safety </li>
<li>Drop support for lua source to java surce (lua2java) in favor of direct java bytecode output (luajc) </li>
<li>Remove compatibility functions like table.getn(), table.maxn(), table.foreach(), and math.log10() </li>
<li>Add ability to create runnable jar file from lua script with sample build file build-app.xml </li>
<tr valign="top"><td>&nbsp;&nbsp;<b>3.0-alpha2</b></td><td><ul>
<li>Supply environment as second argument to LibFunction when loading via require() </li>
</ul></td></tr>
</table></td></tr></table>

View File

@@ -0,0 +1,65 @@
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* Sample library that can be called via luaj's require() implementation.
*
* This library, when loaded, creates a lua package called "hyperbolic"
* which has two functions, "sinh()" and "cosh()".
*
* Because the class is in the default Java package, it can be called using
* lua code such as:
*
<pre> {@code
* require 'hyperbolic'
* print('sinh', hyperbolic.sinh)
* print('sinh(1.0)', hyperbolic.sinh(1.0))
* }</pre>
*
* When require() loads the code, two things happen: 1) the public constructor
* is called to construct a library instance, and 2) the instance is invoked
* as a java call with no arguments. This invocation should be used to initialize
* the library, and add any values to globals that are desired.
*/
public class hyperbolic extends TwoArgFunction {
/** Public constructor. To be loaded via require(), the library class
* must have a public constructor.
*/
public hyperbolic() {}
/** The implementation of the TwoArgFunction interface.
* This will be called once when the library is loaded via require().
* @param modname LuaString containing the name used in the call to require().
* @param env LuaValue containing the environment for this function.
* @return Value that will be returned in the require() call. In this case,
* it is the library itself.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
LuaValue library = tableOf();
library.set( "sinh", new sinh() );
library.set( "cosh", new cosh() );
env.set( "hyperbolic", library );
return library;
}
/* Each library function is coded as a specific LibFunction based on the
* arguments it expects and returns. By using OneArgFunction, rather than
* LibFunction directly, the number of arguments supplied will be coerced
* to match what the implementation expects. */
/** Mathematical sinh function provided as a OneArgFunction. */
static class sinh extends OneArgFunction {
public LuaValue call(LuaValue x) {
return LuaValue.valueOf(Math.sinh(x.checkdouble()));
}
}
/** Mathematical cosh function provided as a OneArgFunction. */
static class cosh extends OneArgFunction {
public LuaValue call(LuaValue x) {
return LuaValue.valueOf(Math.cosh(x.checkdouble()));
}
}
}

View File

@@ -0,0 +1,19 @@
-- Sample luaj code to try loading and executing the 'hyperbolic' sample library
--
-- The sample library source is in examples/jse/hyperbolic.java.
-- For this sample to work, that source must be compiled, and the class must
-- be on the class path.
--
-- First load the library via require(). This will call the public constructor
-- for the class named 'hyperbolic' if it exists, and then initialize the
-- library by invoking LuaValue.call('hyperbolic') on that instance
require 'hyperbolic'
-- Test that the table is in the globals, and the functions exist.
print('hyperbolic', hyperbolic)
print('hyperbolic.sinh', hyperbolic.sinh)
print('hyperbolic.cosh', hyperbolic.cosh)
-- Try exercising the functions.
print('sinh(0.5)', hyperbolic.sinh(0.5))
print('cosh(0.5)', hyperbolic.cosh(0.5))

View File

@@ -57,40 +57,43 @@ import org.luaj.vm2.Varargs;
* <p>
* For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh":
* <pre> {@code
<pre> {@code
* import org.luaj.vm2.LuaValue;
* public class hyperbolic extends org.luaj.vm2.lib.OneArgFunction {
* public hyperbolic() {}
* public LuaValue call(LuaValue arg) {
* switch ( opcode ) {
* case 0: {
* LuaValue t = tableOf();
* this.bind(t, hyperbolic.class, new String[] { "sinh", "cosh" }, 1 );
* env.set("hyperbolic", t);
* return t;
* }
* case 1: return valueOf(Math.sinh(arg.todouble()));
* case 2: return valueOf(Math.cosh(arg.todouble()));
* default: return error("bad opcode: "+opcode);
* }
* }
* }
* }</pre>
* The default constructor is both to instantiate the library
* in response to {@code require 'hyperbolic'} statement,
* provided it is on Javas class path,
* and to instantiate copies of the {@code hyperbolic}
* class when initializing library instances. .
* The instance returned by the default constructor will be invoked
* as part of library loading.
* In response, it creates two more instances, one for each library function,
* in the body of the {@code switch} statement {@code case 0}
* via the {@link #bind(LuaValue, Class, String[], int)} utility method.
* It also registers the table in the globals via the {@link #env}
* local variable, which should be the global environment unless
* it has been changed.
* {@code case 1} and {@code case 2} will be called when {@code hyperbolic.sinh}
* {@code hyperbolic.sinh} and {@code hyperbolic.cosh} are invoked.
* import org.luaj.vm2.lib.*;
*
* public class hyperbolic extends TwoArgFunction {
*
* public hyperbolic() {}
*
* public LuaValue call(LuaValue modname, LuaValue env) {
* LuaValue library = tableOf();
* library.set( "sinh", new sinh() );
* library.set( "cosh", new cosh() );
* env.set( "hyperbolic", library );
* return library;
* }
*
* static class sinh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.sinh(x.checkdouble()));
* }
* }
*
* static class cosh extends OneArgFunction {
* public LuaValue call(LuaValue x) {
* return LuaValue.valueOf(Math.cosh(x.checkdouble()));
* }
* }
*}
*}</pre>
* The default constructor is used to instantiate the library
* in response to {@code require 'hyperbolic'} statement,
* provided it is on Java&quot;s class path.
* This instance is then invoked with 2 arguments: the name supplied to require(),
* and the environment for this function. The library may ignore these, or use
* them to leave side effects in the global environment, for example.
* In the previous example, two functions are created, 'sinh', and 'cosh', and placed
* into a global table called 'hyperbolic' using the supplied 'env' argument.
* <p>
* To test it, a script such as this can be used:
* <pre> {@code
@@ -108,14 +111,14 @@ import org.luaj.vm2.Varargs;
* <pre> {@code
* t table: 3dbbd23f
* hyperbolic table: 3dbbd23f
* k,v cosh cosh
* k,v sinh sinh
* k,v cosh function: 3dbbd128
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
* }</pre>
* <p>
* See the source code in any of the library functions
* such as {@link BaseLib} or {@link TableLib} for specific examples.
* such as {@link BaseLib} or {@link TableLib} for other examples.
*/
abstract public class LibFunction extends LuaFunction {

View File

@@ -148,28 +148,29 @@ public class PackageLib extends OneArgFunction {
/**
* require (modname)
*
* Loads the given module. The function starts by looking into the package.loaded table to
* determine whether modname is already loaded. If it is, then require returns the value
* Loads the given module. The function starts by looking into the package.loaded table
* to determine whether modname is already loaded. If it is, then require returns the value
* stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.
*
* To find a loader, require is guided by the package.loaders array. By changing this array,
* we can change how require looks for a module. The following explanation is based on the
* default configuration for package.loaders.
*
* To find a loader, require is guided by the package.searchers sequence.
* By changing this sequence, we can change how require looks for a module.
* The following explanation is based on the default configuration for package.searchers.
*
* First require queries package.preload[modname]. If it has a value, this value
* (which should be a function) is the loader. Otherwise require searches for a Lua loader
* using the path stored in package.path. If that also fails, it searches for a C loader
* using the path stored in package.cpath. If that also fails, it tries an all-in-one loader
* (see package.loaders).
* (which should be a function) is the loader. Otherwise require searches for a Lua loader using
* the path stored in package.path. If that also fails, it searches for a Java loader using
* the classpath, using the public default constructor, and casting the instance to LuaFunction.
*
* Once a loader is found, require calls the loader with a single argument, modname.
* If the loader returns any value, require assigns the returned value to package.loaded[modname].
* If the loader returns no value and has not assigned any value to package.loaded[modname],
* then require assigns true to this entry. In any case, require returns the final value of
* package.loaded[modname].
*
* If there is any error loading or running the module, or if it cannot find any loader for
* the module, then require signals an error.
* Once a loader is found, require calls the loader with two arguments: modname and an extra value
* dependent on how it got the loader. If the loader came from a file, this extra value is the file name.
* If the loader is a Java instance of LuaFunction, this extra value is the environment.
* If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname].
* If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname],
* then require assigns true to this entry.
* In any case, require returns the final value of package.loaded[modname].
*
* If there is any error loading or running the module, or if it cannot find any loader for the module,
* then require raises an error.
*/
public class require extends OneArgFunction {
public LuaValue call( LuaValue arg ) {
@@ -184,24 +185,24 @@ public class PackageLib extends OneArgFunction {
/* else must load it; iterate over available loaders */
LuaTable tbl = PackageLib.this.searchers.checktable();
StringBuffer sb = new StringBuffer();
LuaValue chunk = null;
Varargs loader = null;
for ( int i=1; true; i++ ) {
LuaValue loader = tbl.get(i);
if ( loader.isnil() ) {
LuaValue searcher = tbl.get(i);
if ( searcher.isnil() ) {
error( "module '"+name+"' not found: "+name+sb );
}
/* call loader with module name as argument */
chunk = loader.call(name);
if ( chunk.isfunction() )
loader = searcher.invoke(name);
if ( loader.isfunction(1) )
break;
if ( chunk.isstring() )
sb.append( chunk.tojstring() );
if ( loader.isstring(1) )
sb.append( loader.tojstring(1) );
}
// load the module using the loader
loaded.set(name, _SENTINEL);
result = chunk.call(name);
result = loader.arg1().call(name, loader.arg(2));
if ( ! result.isnil() )
loaded.set( name, result );
else if ( (result = PackageLib.this.loaded.get(name)) == _SENTINEL )
@@ -309,7 +310,7 @@ public class PackageLib extends OneArgFunction {
v = (LuaValue) c.newInstance();
if (v.isfunction())
((LuaFunction)v).initupvalue1(globals);
return v;
return varargsOf(v, globals);
} catch ( ClassNotFoundException cnfe ) {
return valueOf("\n\tno class '"+classname+"'" );
} catch ( Exception e ) {

View File

@@ -1 +1 @@
version: 3.0-alpha1
version: 3.0-alpha2