Add formatter definition and format code

This commit is contained in:
Enrico Horn
2021-07-03 22:23:09 +02:00
parent 3c266bcc98
commit e7e6190f9c
144 changed files with 17201 additions and 14311 deletions

View File

@@ -21,61 +21,64 @@
******************************************************************************/
package org.luaj.vm2;
/**
* String buffer for use in string library methods, optimized for production
* of StrValue instances.
* String buffer for use in string library methods, optimized for production of
* StrValue instances.
* <p>
* The buffer can begin initially as a wrapped {@link LuaValue}
* and only when concatenation actually occurs are the bytes first copied.
* <p>
* To convert back to a {@link LuaValue} again,
* the function {@link Buffer#value()} is used.
* The buffer can begin initially as a wrapped {@link LuaValue} and only when
* concatenation actually occurs are the bytes first copied.
* <p>
* To convert back to a {@link LuaValue} again, the function
* {@link Buffer#value()} is used.
*
* @see LuaValue
* @see LuaValue#buffer()
* @see LuaString
*/
public final class Buffer {
/** Default capacity for a buffer: 64 */
private static final int DEFAULT_CAPACITY = 64;
/** Shared static array with no bytes */
private static final byte[] NOBYTES = {};
/** Bytes in this buffer */
private byte[] bytes;
/** Length of this buffer */
private int length;
/** Offset into the byte array */
private int offset;
/** Value of this buffer, when not represented in bytes */
private LuaValue value;
/**
* Create buffer with default capacity
*
* @see #DEFAULT_CAPACITY
*/
public Buffer() {
this(DEFAULT_CAPACITY);
}
/**
* Create buffer with specified initial capacity
*
* @param initialCapacity the initial capacity
*/
public Buffer( int initialCapacity ) {
bytes = new byte[ initialCapacity ];
public Buffer(int initialCapacity) {
bytes = new byte[initialCapacity];
length = 0;
offset = 0;
value = null;
}
/**
* Create buffer with specified initial value
*
* @param value the initial value
*/
public Buffer(LuaValue value) {
@@ -84,16 +87,18 @@ public final class Buffer {
this.value = value;
}
/**
/**
* Get buffer contents as a {@link LuaValue}
*
* @return value as a {@link LuaValue}, converting as necessary
*/
public LuaValue value() {
return value != null? value: this.tostring();
}
/**
/**
* Set buffer contents as a {@link LuaValue}
*
* @param value value to set
*/
public Buffer setvalue(LuaValue value) {
@@ -102,145 +107,170 @@ public final class Buffer {
this.value = value;
return this;
}
/**
/**
* Convert the buffer to a {@link LuaString}
*
* @return the value as a {@link LuaString}
*/
public final LuaString tostring() {
realloc( length, 0 );
return LuaString.valueOf( bytes, offset, length );
realloc(length, 0);
return LuaString.valueOf(bytes, offset, length);
}
/**
/**
* Convert the buffer to a Java String
*
* @return the value as a Java String
*/
public String tojstring() {
return value().tojstring();
}
/**
/**
* Convert the buffer to a Java String
*
* @return the value as a Java String
*/
public String toString() {
return tojstring();
}
/**
/**
* Append a single byte to the buffer.
*
* @return {@code this} to allow call chaining
*/
public final Buffer append( byte b ) {
makeroom( 0, 1 );
bytes[ offset + length++ ] = b;
public final Buffer append(byte b) {
makeroom(0, 1);
bytes[offset+length++] = b;
return this;
}
/**
/**
* Append a {@link LuaValue} to the buffer.
*
* @return {@code this} to allow call chaining
*/
public final Buffer append( LuaValue val ) {
append( val.strvalue() );
public final Buffer append(LuaValue val) {
append(val.strvalue());
return this;
}
/**
/**
* Append a {@link LuaString} to the buffer.
*
* @return {@code this} to allow call chaining
*/
public final Buffer append( LuaString str ) {
public final Buffer append(LuaString str) {
final int n = str.m_length;
makeroom( 0, n );
str.copyInto( 0, bytes, offset + length, n );
makeroom(0, n);
str.copyInto(0, bytes, offset+length, n);
length += n;
return this;
}
/**
* Append a Java String to the buffer.
* The Java string will be converted to bytes using the UTF8 encoding.
/**
* Append a Java String to the buffer. The Java string will be converted to
* bytes using the UTF8 encoding.
*
* @return {@code this} to allow call chaining
* @see LuaString#encodeToUtf8(char[], int, byte[], int)
*/
public final Buffer append( String str ) {
public final Buffer append(String str) {
char[] c = str.toCharArray();
final int n = LuaString.lengthAsUtf8( c );
makeroom( 0, n );
LuaString.encodeToUtf8( c, c.length, bytes, offset + length );
final int n = LuaString.lengthAsUtf8(c);
makeroom(0, n);
LuaString.encodeToUtf8(c, c.length, bytes, offset+length);
length += n;
return this;
}
/** Concatenate this buffer onto a {@link LuaValue}
* @param lhs the left-hand-side value onto which we are concatenating {@code this}
/**
* Concatenate this buffer onto a {@link LuaValue}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaValue lhs) {
return setvalue(lhs.concat(value()));
}
/** Concatenate this buffer onto a {@link LuaString}
* @param lhs the left-hand-side value onto which we are concatenating {@code this}
/**
* Concatenate this buffer onto a {@link LuaString}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaString lhs) {
return value!=null&&!value.isstring()? setvalue(lhs.concat(value)): prepend(lhs);
return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs);
}
/** Concatenate this buffer onto a {@link LuaNumber}
/**
* Concatenate this buffer onto a {@link LuaNumber}
* <p>
* The {@link LuaNumber} will be converted to a string before concatenating.
* @param lhs the left-hand-side value onto which we are concatenating {@code this}
* The {@link LuaNumber} will be converted to a string before concatenating.
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer concatTo(LuaNumber lhs) {
return value!=null&&!value.isstring()? setvalue(lhs.concat(value)): prepend(lhs.strvalue());
return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs.strvalue());
}
/** Concatenate bytes from a {@link LuaString} onto the front of this buffer
* @param s the left-hand-side value which we will concatenate onto the front of {@code this}
/**
* Concatenate bytes from a {@link LuaString} onto the front of this buffer
*
* @param s the left-hand-side value which we will concatenate onto the
* front of {@code this}
* @return {@link Buffer} for use in call chaining.
*/
public Buffer prepend(LuaString s) {
int n = s.m_length;
makeroom( n, 0 );
System.arraycopy( s.m_bytes, s.m_offset, bytes, offset-n, n );
makeroom(n, 0);
System.arraycopy(s.m_bytes, s.m_offset, bytes, offset-n, n);
offset -= n;
length += n;
value = null;
return this;
}
/** Ensure there is enough room before and after the bytes.
* @param nbefore number of unused bytes which must precede the data after this completes
* @param nafter number of unused bytes which must follow the data after this completes
/**
* Ensure there is enough room before and after the bytes.
*
* @param nbefore number of unused bytes which must precede the data after
* this completes
* @param nafter number of unused bytes which must follow the data after
* this completes
*/
public final void makeroom( int nbefore, int nafter ) {
if ( value != null ) {
public final void makeroom(int nbefore, int nafter) {
if (value != null) {
LuaString s = value.strvalue();
value = null;
length = s.m_length;
offset = nbefore;
bytes = new byte[nbefore+length+nafter];
System.arraycopy(s.m_bytes, s.m_offset, bytes, offset, length);
} else if ( offset+length+nafter > bytes.length || offset<nbefore ) {
} else if (offset+length+nafter > bytes.length || offset < nbefore) {
int n = nbefore+length+nafter;
int m = n<32? 32: n<length*2? length*2: n;
realloc( m, nbefore==0? 0: m-length-nafter );
int m = n < 32? 32: n < length*2? length*2: n;
realloc(m, nbefore == 0? 0: m-length-nafter);
}
}
/** Reallocate the internal storage for the buffer
* @param newSize the size of the buffer to use
* @param newOffset the offset to use
/**
* Reallocate the internal storage for the buffer
*
* @param newSize the size of the buffer to use
* @param newOffset the offset to use
*/
private final void realloc( int newSize, int newOffset ) {
if ( newSize != bytes.length ) {
byte[] newBytes = new byte[ newSize ];
System.arraycopy( bytes, offset, newBytes, newOffset, length );
private final void realloc(int newSize, int newOffset) {
if (newSize != bytes.length) {
byte[] newBytes = new byte[newSize];
System.arraycopy(bytes, offset, newBytes, newOffset, length);
bytes = newBytes;
offset = newOffset;
}

View File

@@ -33,73 +33,94 @@ import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.ResourceFinder;
/**
* Global environment used by luaj. Contains global variables referenced by executing lua.
* Global environment used by luaj. Contains global variables referenced by
* executing lua.
* <p>
*
* <h3>Constructing and Initializing Instances</h3>
* Typically, this is constructed indirectly by a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()},
* and then used to load lua scripts for execution as in the following example.
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
* } </pre>
* <h3>Constructing and Initializing Instances</h3> Typically, this is
* constructed indirectly by a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}, and then used to
* load lua scripts for execution as in the following example.
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* The creates a complete global environment with the standard libraries loaded.
* <p>
* For specialized circumstances, the Globals may be constructed directly and loaded
* with only those libraries that are needed, for example.
* <pre> {@code
* Globals globals = new Globals();
* globals.load( new BaseLib() );
* } </pre>
* For specialized circumstances, the Globals may be constructed directly and
* loaded with only those libraries that are needed, for example.
*
* <h3>Loading and Executing Lua Code</h3>
* Globals contains convenience functions to load and execute lua source code given a Reader.
* A simple example is:
* <pre> {@code
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new BaseLib());
* }
* </pre>
*
* <h3>Loading and Executing Lua Code</h3> Globals contains convenience
* functions to load and execute lua source code given a Reader. A simple
* example is:
*
* <pre>
* {@code
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
* } </pre>
* }
* </pre>
*
* <h3>Fine-Grained Control of Compiling and Loading Lua</h3>
* Executable LuaFunctions are created from lua code in several steps
* <h3>Fine-Grained Control of Compiling and Loading Lua</h3> Executable
* LuaFunctions are created from lua code in several steps
* <ul>
* <li>find the resource using the platform's {@link ResourceFinder}
* <li>compile lua to lua bytecode using {@link Compiler}
* <li>load lua bytecode to a {@link Prototype} using {@link Undumper}
* <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals} using {@link Loader}
* <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals}
* using {@link Loader}
* </ul>
* <p>
* There are alternate flows when the direct lua-to-Java bytecode compiling {@link org.luaj.vm2.luajc.LuaJC} is used.
* There are alternate flows when the direct lua-to-Java bytecode compiling
* {@link org.luaj.vm2.luajc.LuaJC} is used.
* <ul>
* <li>compile lua to lua bytecode using {@link Compiler} or load precompiled code using {@link Undumper}
* <li>convert lua bytecode to equivalent Java bytecode using {@link org.luaj.vm2.luajc.LuaJC} that implements {@link Loader} directly
* <li>compile lua to lua bytecode using {@link Compiler} or load precompiled
* code using {@link Undumper}
* <li>convert lua bytecode to equivalent Java bytecode using
* {@link org.luaj.vm2.luajc.LuaJC} that implements {@link Loader} directly
* </ul>
*
* <h3>Java Field</h3>
* Certain public fields are provided that contain the current values of important global state:
* <h3>Java Field</h3> Certain public fields are provided that contain the
* current values of important global state:
* <ul>
* <li>{@link #STDIN} Current value for standard input in the laaded {@link IoLib}, if any.
* <li>{@link #STDOUT} Current value for standard output in the loaded {@link IoLib}, if any.
* <li>{@link #STDERR} Current value for standard error in the loaded {@link IoLib}, if any.
* <li>{@link #STDIN} Current value for standard input in the laaded
* {@link IoLib}, if any.
* <li>{@link #STDOUT} Current value for standard output in the loaded
* {@link IoLib}, if any.
* <li>{@link #STDERR} Current value for standard error in the loaded
* {@link IoLib}, if any.
* <li>{@link #finder} Current loaded {@link ResourceFinder}, if any.
* <li>{@link #compiler} Current loaded {@link Compiler}, if any.
* <li>{@link #undumper} Current loaded {@link Undumper}, if any.
* <li>{@link #loader} Current loaded {@link Loader}, if any.
* </ul>
*
* <h3>Lua Environment Variables</h3>
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} or {@link org.luaj.vm2.lib.jme.JmePlatform},
* these environment variables are created within the Globals.
* <h3>Lua Environment Variables</h3> When using
* {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}, these environment variables are
* created within the Globals.
* <ul>
* <li>"_G" Pointer to this Globals.
* <li>"_VERSION" String containing the version of luaj.
* </ul>
*
* <h3>Use in Multithreaded Environments</h3>
* In a multi-threaded server environment, each server thread should create one Globals instance,
* which will be logically distinct and not interfere with each other, but share certain
* static immutable resources such as class data and string data.
* <h3>Use in Multithreaded Environments</h3> In a multi-threaded server
* environment, each server thread should create one Globals instance, which
* will be logically distinct and not interfere with each other, but share
* certain static immutable resources such as class data and string data.
* <p>
*
* @see org.luaj.vm2.lib.jse.JsePlatform
@@ -115,7 +136,7 @@ import org.luaj.vm2.lib.ResourceFinder;
public class Globals extends LuaTable {
/** The current default input stream. */
public InputStream STDIN = null;
public InputStream STDIN = null;
/** The current default output stream. */
public PrintStream STDOUT = System.out;
@@ -125,28 +146,42 @@ public class Globals extends LuaTable {
/** The installed ResourceFinder for looking files by name. */
public ResourceFinder finder;
/** The currently running thread. Should not be changed by non-library code. */
/**
* The currently running thread. Should not be changed by non-library code.
*/
public LuaThread running = new LuaThread(this);
/** The BaseLib instance loaded into this Globals */
public BaseLib baselib;
/** The PackageLib instance loaded into this Globals */
public PackageLib package_;
/** The DebugLib instance loaded into this Globals, or null if debugging is not enabled */
/**
* The DebugLib instance loaded into this Globals, or null if debugging is
* not enabled
*/
public DebugLib debuglib;
/** Interface for module that converts a Prototype into a LuaFunction with an environment. */
/**
* Interface for module that converts a Prototype into a LuaFunction with an
* environment.
*/
public interface Loader {
/** Convert the prototype into a LuaFunction with the supplied environment. */
/**
* Convert the prototype into a LuaFunction with the supplied
* environment.
*/
LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException;
}
/** Interface for module that converts lua source text into a prototype. */
public interface Compiler {
/** Compile lua source into a Prototype. The InputStream is assumed to be in UTF-8. */
/**
* Compile lua source into a Prototype. The InputStream is assumed to be
* in UTF-8.
*/
Prototype compile(InputStream stream, String chunkname) throws IOException;
}
@@ -155,100 +190,142 @@ public class Globals extends LuaTable {
/** Load the supplied input stream into a prototype. */
Prototype undump(InputStream stream, String chunkname) throws IOException;
}
/** Check that this object is a Globals object, and return it, otherwise throw an error. */
/**
* Check that this object is a Globals object, and return it, otherwise
* throw an error.
*/
public Globals checkglobals() {
return this;
}
/** The installed loader.
* @see Loader */
/**
* The installed loader.
*
* @see Loader
*/
public Loader loader;
/** The installed compiler.
* @see Compiler */
/**
* The installed compiler.
*
* @see Compiler
*/
public Compiler compiler;
/** The installed undumper.
* @see Undumper */
/**
* The installed undumper.
*
* @see Undumper
*/
public Undumper undumper;
/** Convenience function for loading a file that is either binary lua or lua source.
/**
* Convenience function for loading a file that is either binary lua or lua
* source.
*
* @param filename Name of the file to load.
* @return LuaValue that can be call()'ed or invoke()'ed.
* @throws LuaError if the file could not be loaded.
*/
public LuaValue loadfile(String filename) {
try {
return load(finder.findResource(filename), "@"+filename, "bt", this);
return load(finder.findResource(filename), "@" + filename, "bt", this);
} catch (Exception e) {
return error("load "+filename+": "+e);
return error("load " + filename + ": " + e);
}
}
/** Convenience function to load a string value as a script. Must be lua source.
* @param script Contents of a lua script, such as "print 'hello, world.'"
/**
* Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello,
* world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script, String chunkname) {
return load(new StrReader(script), chunkname);
}
/** Convenience function to load a string value as a script. Must be lua source.
/**
* Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello, world.'"
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script) {
return load(new StrReader(script), script);
}
/** Convenience function to load a string value as a script with a custom environment.
* Must be lua source.
* @param script Contents of a lua script, such as "print 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded function.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
/**
* Convenience function to load a string value as a script with a custom
* environment. Must be lua source.
*
* @param script Contents of a lua script, such as "print 'hello,
* world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue load(String script, String chunkname, LuaTable environment) {
return load(new StrReader(script), chunkname, environment);
}
/** Load the content form a reader as a text file. Must be lua source.
* The source is converted to UTF-8, so any characters appearing in quoted literals
* above the range 128 will be converted into multiple bytes.
* @param reader Reader containing text of a lua script, such as "print 'hello, world.'"
/**
* Load the content form a reader as a text file. Must be lua source. The
* source is converted to UTF-8, so any characters appearing in quoted
* literals above the range 128 will be converted into multiple bytes.
*
* @param reader Reader containing text of a lua script, such as "print
* 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
*/
public LuaValue load(Reader reader, String chunkname) {
return load(new UTF8Stream(reader), chunkname, "t", this);
}
/** Load the content form a reader as a text file, supplying a custom environment.
* Must be lua source. The source is converted to UTF-8, so any characters
* appearing in quoted literals above the range 128 will be converted into
* multiple bytes.
* @param reader Reader containing text of a lua script, such as "print 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded function.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls.
/**
* Load the content form a reader as a text file, supplying a custom
* environment. Must be lua source. The source is converted to UTF-8, so any
* characters appearing in quoted literals above the range 128 will be
* converted into multiple bytes.
*
* @param reader Reader containing text of a lua script, such as "print
* 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled.
*/
*/
public LuaValue load(Reader reader, String chunkname, LuaTable environment) {
return load(new UTF8Stream(reader), chunkname, "t", environment);
}
}
/** Load the content form an input stream as a binary chunk or text file.
* @param is InputStream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control loading as binary or text or either.
* @param environment LuaTable to be used as the environment for the loaded function.
* */
/**
* Load the content form an input stream as a binary chunk or text file.
*
* @param is InputStream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control
* loading as binary or text or either.
* @param environment LuaTable to be used as the environment for the loaded
* function.
*/
public LuaValue load(InputStream is, String chunkname, String mode, LuaValue environment) {
try {
Prototype p = loadPrototype(is, chunkname, mode);
@@ -256,16 +333,20 @@ public class Globals extends LuaTable {
} catch (LuaError l) {
throw l;
} catch (Exception e) {
return error("load "+chunkname+": "+e);
return error("load " + chunkname + ": " + e);
}
}
/** Load lua source or lua binary from an input stream into a Prototype.
* The InputStream is either a binary lua chunk starting with the lua binary chunk signature,
* or a text input file. If it is a text input file, it is interpreted as a UTF-8 byte sequence.
* @param is Input stream containing a lua script or compiled lua"
/**
* Load lua source or lua binary from an input stream into a Prototype. The
* InputStream is either a binary lua chunk starting with the lua binary
* chunk signature, or a text input file. If it is a text input file, it is
* interpreted as a UTF-8 byte sequence.
*
* @param is Input stream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control loading as binary or text or either.
* @param mode String containing 'b' or 't' or both to control loading
* as binary or text or either.
*/
public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException {
if (mode.indexOf('b') >= 0) {
@@ -282,21 +363,25 @@ public class Globals extends LuaTable {
if (mode.indexOf('t') >= 0) {
return compilePrototype(is, chunkname);
}
error("Failed to load prototype "+chunkname+" using mode '"+mode+"'");
error("Failed to load prototype " + chunkname + " using mode '" + mode + "'");
return null;
}
/** Compile lua source from a Reader into a Prototype. The characters in the reader
* are converted to bytes using the UTF-8 encoding, so a string literal containing
* characters with codepoints 128 or above will be converted into multiple bytes.
/**
* Compile lua source from a Reader into a Prototype. The characters in the
* reader are converted to bytes using the UTF-8 encoding, so a string
* literal containing characters with codepoints 128 or above will be
* converted into multiple bytes.
*/
public Prototype compilePrototype(Reader reader, String chunkname) throws IOException {
return compilePrototype(new UTF8Stream(reader), chunkname);
}
/** Compile lua source from an InputStream into a Prototype.
* The input is assumed to be UTf-8, but since bytes in the range 128-255 are passed along as
* literal bytes, any ASCII-compatible encoding such as ISO 8859-1 may also be used.
/**
* Compile lua source from an InputStream into a Prototype. The input is
* assumed to be UTf-8, but since bytes in the range 128-255 are passed
* along as literal bytes, any ASCII-compatible encoding such as ISO 8859-1
* may also be used.
*/
public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException {
if (compiler == null)
@@ -304,9 +389,13 @@ public class Globals extends LuaTable {
return compiler.compile(stream, chunkname);
}
/** Function which yields the current thread.
* @param args Arguments to supply as return values in the resume function of the resuming thread.
* @return Values supplied as arguments to the resume() call that reactivates this thread.
/**
* Function which yields the current thread.
*
* @param args Arguments to supply as return values in the resume function
* of the resuming thread.
* @return Values supplied as arguments to the resume() call that
* reactivates this thread.
*/
public Varargs yield(Varargs args) {
if (running == null || running.isMainThread())
@@ -318,23 +407,27 @@ public class Globals extends LuaTable {
/** Reader implementation to read chars from a String in JME or JSE. */
static class StrReader extends Reader {
final String s;
int i = 0;
final int n;
int i = 0;
final int n;
StrReader(String s) {
this.s = s;
n = s.length();
}
public void close() throws IOException {
i = n;
}
public int read() throws IOException {
return i < n ? s.charAt(i++) : -1;
return i < n? s.charAt(i++): -1;
}
public int read(char[] cbuf, int off, int len) throws IOException {
int j = 0;
for (; j < len && i < n; ++j, ++i)
cbuf[off+j] = s.charAt(i);
return j > 0 || len == 0 ? j : -1;
return j > 0 || len == 0? j: -1;
}
}
@@ -343,49 +436,61 @@ public class Globals extends LuaTable {
*/
abstract static class AbstractBufferedStream extends InputStream {
protected byte[] b;
protected int i = 0, j = 0;
protected int i = 0, j = 0;
protected AbstractBufferedStream(int buflen) {
this.b = new byte[buflen];
}
abstract protected int avail() throws IOException;
public int read() throws IOException {
int a = avail();
return (a <= 0 ? -1 : 0xff & b[i++]);
return (a <= 0? -1: 0xff & b[i++]);
}
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
public int read(byte[] b, int i0, int n) throws IOException {
int a = avail();
if (a <= 0) return -1;
if (a <= 0)
return -1;
final int n_read = Math.min(a, n);
System.arraycopy(this.b, i, b, i0, n_read);
System.arraycopy(this.b, i, b, i0, n_read);
i += n_read;
return n_read;
}
public long skip(long n) throws IOException {
final long k = Math.min(n, j - i);
final long k = Math.min(n, j-i);
i += k;
return k;
}
}
public int available() throws IOException {
return j - i;
return j-i;
}
}
/** Simple converter from Reader to InputStream using UTF8 encoding that will work
* on both JME and JSE.
* This class may be moved to its own package in the future.
/**
* Simple converter from Reader to InputStream using UTF8 encoding that will
* work on both JME and JSE. This class may be moved to its own package in
* the future.
*/
static class UTF8Stream extends AbstractBufferedStream {
private final char[] c = new char[32];
private final Reader r;
UTF8Stream(Reader r) {
super(96);
this.r = r;
}
protected int avail() throws IOException {
if (i < j) return j - i;
if (i < j)
return j-i;
int n = r.read(c);
if (n < 0)
return -1;
@@ -399,31 +504,38 @@ public class Globals extends LuaTable {
j = LuaString.encodeToUtf8(c, n, b, i = 0);
return j;
}
public void close() throws IOException {
r.close();
}
}
/** Simple buffered InputStream that supports mark.
* Used to examine an InputStream for a 4-byte binary lua signature,
* and fall back to text input when the signature is not found,
* as well as speed up normal compilation and reading of lua scripts.
* This class may be moved to its own package in the future.
/**
* Simple buffered InputStream that supports mark. Used to examine an
* InputStream for a 4-byte binary lua signature, and fall back to text
* input when the signature is not found, as well as speed up normal
* compilation and reading of lua scripts. This class may be moved to its
* own package in the future.
*/
static class BufferedStream extends AbstractBufferedStream {
private final InputStream s;
public BufferedStream(InputStream s) {
this(128, s);
}
BufferedStream(int buflen, InputStream s) {
super(buflen);
this.s = s;
}
protected int avail() throws IOException {
if (i < j) return j - i;
if (j >= b.length) i = j = 0;
if (i < j)
return j-i;
if (j >= b.length)
i = j = 0;
// leave previous bytes in place to implement mark()/reset().
int n = s.read(b, j, b.length - j);
int n = s.read(b, j, b.length-j);
if (n < 0)
return -1;
if (n == 0) {
@@ -436,21 +548,25 @@ public class Globals extends LuaTable {
j += n;
return n;
}
public void close() throws IOException {
s.close();
}
public synchronized void mark(int n) {
if (i > 0 || n > b.length) {
byte[] dest = n > b.length ? new byte[n] : b;
System.arraycopy(b, i, dest, 0, j - i);
byte[] dest = n > b.length? new byte[n]: b;
System.arraycopy(b, i, dest, 0, j-i);
j -= i;
i = 0;
b = dest;
}
}
public boolean markSupported() {
return true;
}
public synchronized void reset() throws IOException {
i = 0;
}

View File

@@ -25,116 +25,141 @@ import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Class to undump compiled lua bytecode into a {@link Prototype} instances.
* <p>
* The {@link LoadState} class provides the default {@link Globals.Undumper}
* which is used to undump a string of bytes that represent a lua binary file
* using either the C-based lua compiler, or luaj's
* {@link org.luaj.vm2.compiler.LuaC} compiler.
* <p>
* The canonical method to load and execute code is done
* indirectly using the Globals:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaValue chunk = globasl.load("print('hello, world')", "main.lua");
* chunk.call();
* } </pre>
* This should work regardless of which {@link Globals.Compiler} or {@link Globals.Undumper}
* have been installed.
* <p>
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* to construct globals, the {@link LoadState} default undumper is installed
* as the default {@link Globals.Undumper}.
* <p>
*
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState} class
:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* org.luaj.vm2.compiler.DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* } </pre>
*
* The {@link LoadState}'s default undumper {@link #instance}
* may be used directly to undump these bytes:
* <pre> {@code
* Class to undump compiled lua bytecode into a {@link Prototype} instances.
* <p>
* The {@link LoadState} class provides the default {@link Globals.Undumper}
* which is used to undump a string of bytes that represent a lua binary file
* using either the C-based lua compiler, or luaj's
* {@link org.luaj.vm2.compiler.LuaC} compiler.
* <p>
* The canonical method to load and execute code is done indirectly using the
* Globals:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaValue chunk = globasl.load("print('hello, world')", "main.lua");
* chunk.call();
* }
* </pre>
*
* This should work regardless of which {@link Globals.Compiler} or
* {@link Globals.Undumper} have been installed.
* <p>
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform} to construct globals, the
* {@link LoadState} default undumper is installed as the default
* {@link Globals.Undumper}.
* <p>
*
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState}
* class :
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* org.luaj.vm2.compiler.DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
*
* The {@link LoadState}'s default undumper {@link #instance} may be used
* directly to undump these bytes:
*
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* } </pre>
*
*
* More commonly, the {@link Globals.Undumper} may be used to undump them:
* <pre> {@code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* } </pre>
*
* @see Globals.Compiler
* @see Globals.Undumper
* @see LuaClosure
* @see LuaFunction
* @see org.luaj.vm2.compiler.LuaC
* @see org.luaj.vm2.luajc.LuaJC
* @see Globals#compiler
* @see Globals#load(InputStream, String, LuaValue)
*/
* }
* </pre>
*
*
* More commonly, the {@link Globals.Undumper} may be used to undump them:
*
* <pre>
* {
* &#64;code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
* @see Globals.Compiler
* @see Globals.Undumper
* @see LuaClosure
* @see LuaFunction
* @see org.luaj.vm2.compiler.LuaC
* @see org.luaj.vm2.luajc.LuaJC
* @see Globals#compiler
* @see Globals#load(InputStream, String, LuaValue)
*/
public class LoadState {
/** Shared instance of Globals.Undumper to use loading prototypes from binary lua files */
/**
* Shared instance of Globals.Undumper to use loading prototypes from binary
* lua files
*/
public static final Globals.Undumper instance = new GlobalsUndumper();
/** format corresponding to non-number-patched lua, all numbers are floats or doubles */
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
// type constants
public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0;
public static final int LUA_TBOOLEAN = 1;
public static final int LUA_TLIGHTUSERDATA = 2;
public static final int LUA_TNUMBER = 3;
public static final int LUA_TSTRING = 4;
public static final int LUA_TTABLE = 5;
public static final int LUA_TFUNCTION = 6;
public static final int LUA_TUSERDATA = 7;
public static final int LUA_TTHREAD = 8;
public static final int LUA_TVALUE = 9;
/** The character encoding to use for file encoding. Null means the default encoding */
public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0;
public static final int LUA_TBOOLEAN = 1;
public static final int LUA_TLIGHTUSERDATA = 2;
public static final int LUA_TNUMBER = 3;
public static final int LUA_TSTRING = 4;
public static final int LUA_TTABLE = 5;
public static final int LUA_TFUNCTION = 6;
public static final int LUA_TUSERDATA = 7;
public static final int LUA_TTHREAD = 8;
public static final int LUA_TVALUE = 9;
/**
* The character encoding to use for file encoding. Null means the default
* encoding
*/
public static String encoding = null;
/** Signature byte indicating the file is a compiled binary chunk */
public static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' };
public static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' };
/** Data to catch conversion errors */
public static final byte[] LUAC_TAIL = { (byte) 0x19, (byte) 0x93, '\r', '\n', (byte) 0x1a, '\n', };
/** Name for compiled chunks */
public static final String SOURCE_BINARY_STRING = "binary string";
/** for header of binary files -- this is Lua 5.2 */
public static final int LUAC_VERSION = 0x52;
public static final int LUAC_VERSION = 0x52;
/** for header of binary files -- this is the official format */
public static final int LUAC_FORMAT = 0;
public static final int LUAC_FORMAT = 0;
/** size of header of binary files */
public static final int LUAC_HEADERSIZE = 12;
public static final int LUAC_HEADERSIZE = 12;
// values read from the header
private int luacVersion;
@@ -144,7 +169,7 @@ public class LoadState {
private int luacSizeofSizeT;
private int luacSizeofInstruction;
private int luacSizeofLuaNumber;
private int luacNumberFormat;
private int luacNumberFormat;
/** input stream from which we are loading */
public final DataInputStream is;
@@ -152,127 +177,141 @@ public class LoadState {
/** Name of what is being loaded? */
String name;
private static final LuaValue[] NOVALUES = {};
private static final Prototype[] NOPROTOS = {};
private static final LocVars[] NOLOCVARS = {};
private static final Upvaldesc[] NOUPVALDESCS = {};
private static final int[] NOINTS = {};
private static final LuaValue[] NOVALUES = {};
private static final Prototype[] NOPROTOS = {};
private static final LocVars[] NOLOCVARS = {};
private static final Upvaldesc[] NOUPVALDESCS = {};
private static final int[] NOINTS = {};
/** Read buffer */
private byte[] buf = new byte[512];
/** Install this class as the standard Globals.Undumper for the supplied Globals */
/**
* Install this class as the standard Globals.Undumper for the supplied
* Globals
*/
public static void install(Globals globals) {
globals.undumper = instance;
}
/** Load a 4-byte int value from the input stream
/**
* Load a 4-byte int value from the input stream
*
* @return the int value laoded.
**/
int loadInt() throws IOException {
is.readFully(buf,0,4);
return luacLittleEndian?
(buf[3] << 24) | ((0xff & buf[2]) << 16) | ((0xff & buf[1]) << 8) | (0xff & buf[0]):
(buf[0] << 24) | ((0xff & buf[1]) << 16) | ((0xff & buf[2]) << 8) | (0xff & buf[3]);
is.readFully(buf, 0, 4);
return luacLittleEndian? (buf[3]<<24) | ((0xff & buf[2])<<16) | ((0xff & buf[1])<<8) | (0xff & buf[0])
: (buf[0]<<24) | ((0xff & buf[1])<<16) | ((0xff & buf[2])<<8) | (0xff & buf[3]);
}
/** Load an array of int values from the input stream
/**
* Load an array of int values from the input stream
*
* @return the array of int values laoded.
**/
int[] loadIntArray() throws IOException {
int n = loadInt();
if ( n == 0 )
if (n == 0)
return NOINTS;
// read all data at once
int m = n << 2;
if ( buf.length < m )
int m = n<<2;
if (buf.length < m)
buf = new byte[m];
is.readFully(buf,0,m);
is.readFully(buf, 0, m);
int[] array = new int[n];
for ( int i=0, j=0; i<n; ++i, j+=4 )
array[i] = luacLittleEndian?
(buf[j+3] << 24) | ((0xff & buf[j+2]) << 16) | ((0xff & buf[j+1]) << 8) | (0xff & buf[j+0]):
(buf[j+0] << 24) | ((0xff & buf[j+1]) << 16) | ((0xff & buf[j+2]) << 8) | (0xff & buf[j+3]);
for (int i = 0, j = 0; i < n; ++i, j += 4)
array[i] = luacLittleEndian
? (buf[j+3]<<24) | ((0xff & buf[j+2])<<16) | ((0xff & buf[j+1])<<8) | (0xff & buf[j+0])
: (buf[j+0]<<24) | ((0xff & buf[j+1])<<16) | ((0xff & buf[j+2])<<8) | (0xff & buf[j+3]);
return array;
}
/** Load a long value from the input stream
/**
* Load a long value from the input stream
*
* @return the long value laoded.
**/
long loadInt64() throws IOException {
int a,b;
if ( this.luacLittleEndian ) {
int a, b;
if (this.luacLittleEndian) {
a = loadInt();
b = loadInt();
} else {
b = loadInt();
a = loadInt();
}
return (((long)b)<<32) | (((long)a)&0xffffffffL);
return (((long) b)<<32) | (((long) a) & 0xffffffffL);
}
/** Load a lua strin gvalue from the input stream
/**
* Load a lua strin gvalue from the input stream
*
* @return the {@link LuaString} value laoded.
**/
LuaString loadString() throws IOException {
int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
if ( size == 0 )
if (size == 0)
return null;
byte[] bytes = new byte[size];
is.readFully( bytes, 0, size );
return LuaString.valueUsing( bytes, 0, bytes.length - 1 );
is.readFully(bytes, 0, size);
return LuaString.valueUsing(bytes, 0, bytes.length-1);
}
/**
* Convert bits in a long value to a {@link LuaValue}.
*
* @param bits long value containing the bits
* @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds to the bits provided.
* @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds
* to the bits provided.
*/
public static LuaValue longBitsToLuaNumber( long bits ) {
if ( ( bits & ( ( 1L << 63 ) - 1 ) ) == 0L ) {
public static LuaValue longBitsToLuaNumber(long bits) {
if ((bits & ((1L<<63)-1)) == 0L) {
return LuaValue.ZERO;
}
int e = (int)((bits >> 52) & 0x7ffL) - 1023;
if ( e >= 0 && e < 31 ) {
int e = (int) ((bits>>52) & 0x7ffL)-1023;
if (e >= 0 && e < 31) {
long f = bits & 0xFFFFFFFFFFFFFL;
int shift = 52 - e;
long intPrecMask = ( 1L << shift ) - 1;
if ( ( f & intPrecMask ) == 0 ) {
int intValue = (int)( f >> shift ) | ( 1 << e );
return LuaInteger.valueOf( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue );
int shift = 52-e;
long intPrecMask = (1L<<shift)-1;
if ((f & intPrecMask) == 0) {
int intValue = (int) (f>>shift) | (1<<e);
return LuaInteger.valueOf(((bits>>63) != 0)? -intValue: intValue);
}
}
return LuaValue.valueOf( Double.longBitsToDouble(bits) );
return LuaValue.valueOf(Double.longBitsToDouble(bits));
}
/**
* Load a number from a binary chunk
*
* @return the {@link LuaValue} loaded
* @throws IOException if an i/o exception occurs
*/
LuaValue loadNumber() throws IOException {
if ( luacNumberFormat == NUMBER_FORMAT_INTS_ONLY ) {
return LuaInteger.valueOf( loadInt() );
if (luacNumberFormat == NUMBER_FORMAT_INTS_ONLY) {
return LuaInteger.valueOf(loadInt());
} else {
return longBitsToLuaNumber( loadInt64() );
return longBitsToLuaNumber(loadInt64());
}
}
/**
* Load a list of constants from a binary chunk
*
* @param f the function prototype
* @throws IOException if an i/o exception occurs
*/
void loadConstants(Prototype f) throws IOException {
int n = loadInt();
LuaValue[] values = n>0? new LuaValue[n]: NOVALUES;
for ( int i=0; i<n; i++ ) {
switch ( is.readByte() ) {
LuaValue[] values = n > 0? new LuaValue[n]: NOVALUES;
for (int i = 0; i < n; i++) {
switch (is.readByte()) {
case LUA_TNIL:
values[i] = LuaValue.NIL;
break;
@@ -280,7 +319,7 @@ public class LoadState {
values[i] = (0 != is.readUnsignedByte()? LuaValue.TRUE: LuaValue.FALSE);
break;
case LUA_TINT:
values[i] = LuaInteger.valueOf( loadInt() );
values[i] = LuaInteger.valueOf(loadInt());
break;
case LUA_TNUMBER:
values[i] = loadNumber();
@@ -293,19 +332,18 @@ public class LoadState {
}
}
f.k = values;
n = loadInt();
Prototype[] protos = n>0? new Prototype[n]: NOPROTOS;
for ( int i=0; i<n; i++ )
Prototype[] protos = n > 0? new Prototype[n]: NOPROTOS;
for (int i = 0; i < n; i++)
protos[i] = loadFunction(f.source);
f.p = protos;
}
void loadUpvalues(Prototype f) throws IOException {
int n = loadInt();
f.upvalues = n>0? new Upvaldesc[n]: NOUPVALDESCS;
for (int i=0; i<n; i++) {
f.upvalues = n > 0? new Upvaldesc[n]: NOUPVALDESCS;
for (int i = 0; i < n; i++) {
boolean instack = is.readByte() != 0;
int idx = ((int) is.readByte()) & 0xff;
f.upvalues[i] = new Upvaldesc(null, instack, idx);
@@ -314,28 +352,30 @@ public class LoadState {
/**
* Load the debug info for a function prototype
*
* @param f the function Prototype
* @throws IOException if there is an i/o exception
*/
void loadDebug( Prototype f ) throws IOException {
void loadDebug(Prototype f) throws IOException {
f.source = loadString();
f.lineinfo = loadIntArray();
int n = loadInt();
f.locvars = n>0? new LocVars[n]: NOLOCVARS;
for ( int i=0; i<n; i++ ) {
f.locvars = n > 0? new LocVars[n]: NOLOCVARS;
for (int i = 0; i < n; i++) {
LuaString varname = loadString();
int startpc = loadInt();
int endpc = loadInt();
f.locvars[i] = new LocVars(varname, startpc, endpc);
}
n = loadInt();
for ( int i=0; i<n; i++ )
for (int i = 0; i < n; i++)
f.upvalues[i].name = loadString();
}
/**
* Load a function prototype from the input stream
*
* @param p name of the source
* @return {@link Prototype} instance that was loaded
* @throws IOException
@@ -355,17 +395,18 @@ public class LoadState {
loadConstants(f);
loadUpvalues(f);
loadDebug(f);
// TODO: add check here, for debugging purposes, I believe
// see ldebug.c
// IF (!luaG_checkcode(f), "bad code");
// this.L.pop();
return f;
return f;
}
/**
* Load the lua chunk header values.
*
* @throws IOException if an i/o exception occurs.
*/
public void loadHeader() throws IOException {
@@ -377,33 +418,35 @@ public class LoadState {
luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte();
luacNumberFormat = is.readByte();
for (int i=0; i < LUAC_TAIL.length; ++i)
for (int i = 0; i < LUAC_TAIL.length; ++i)
if (is.readByte() != LUAC_TAIL[i])
throw new LuaError("Unexpeted byte in luac tail of header, index="+i);
throw new LuaError("Unexpeted byte in luac tail of header, index=" + i);
}
/**
* Load input stream as a lua binary chunk if the first 4 bytes are the lua binary signature.
* @param stream InputStream to read, after having read the first byte already
* Load input stream as a lua binary chunk if the first 4 bytes are the lua
* binary signature.
*
* @param stream InputStream to read, after having read the first byte
* already
* @param chunkname Name to apply to the loaded chunk
* @return {@link Prototype} that was loaded, or null if the first 4 bytes were not the lua signature.
* @return {@link Prototype} that was loaded, or null if the first 4 bytes
* were not the lua signature.
* @throws IOException if an IOException occurs
*/
public static Prototype undump(InputStream stream, String chunkname) throws IOException {
// check rest of signature
if ( stream.read() != LUA_SIGNATURE[0]
|| stream.read() != LUA_SIGNATURE[1]
|| stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3] )
if (stream.read() != LUA_SIGNATURE[0] || stream.read() != LUA_SIGNATURE[1] || stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3])
return null;
// load file as a compiled chunk
String sname = getSourceName(chunkname);
LoadState s = new LoadState( stream, sname );
LoadState s = new LoadState(stream, sname);
s.loadHeader();
// check format
switch ( s.luacNumberFormat ) {
switch (s.luacNumberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
@@ -411,33 +454,33 @@ public class LoadState {
default:
throw new LuaError("unsupported int size");
}
return s.loadFunction( LuaString.valueOf(sname) );
return s.loadFunction(LuaString.valueOf(sname));
}
/**
* Construct a source name from a supplied chunk name
*
* @param name String name that appears in the chunk
* @return source file name
*/
public static String getSourceName(String name) {
String sname = name;
if ( name.startsWith("@") || name.startsWith("=") )
public static String getSourceName(String name) {
String sname = name;
if (name.startsWith("@") || name.startsWith("="))
sname = name.substring(1);
else if ( name.startsWith("\033") )
else if (name.startsWith("\033"))
sname = SOURCE_BINARY_STRING;
return sname;
}
return sname;
}
/** Private constructor for create a load state */
private LoadState( InputStream stream, String name ) {
private LoadState(InputStream stream, String name) {
this.name = name;
this.is = new DataInputStream( stream );
this.is = new DataInputStream(stream);
}
private static final class GlobalsUndumper implements Globals.Undumper {
public Prototype undump(InputStream stream, String chunkname)
throws IOException {
return LoadState.undump(stream, chunkname);
public Prototype undump(InputStream stream, String chunkname) throws IOException {
return LoadState.undump(stream, chunkname);
}
}
}

View File

@@ -22,31 +22,33 @@
package org.luaj.vm2;
/**
* Data class to hold debug information relating to local variables for a {@link Prototype}
* Data class to hold debug information relating to local variables for a
* {@link Prototype}
*/
public class LocVars {
/** The local variable name */
public LuaString varname;
/** The instruction offset when the variable comes into scope */
/** The instruction offset when the variable comes into scope */
public int startpc;
/** The instruction offset when the variable goes out of scope */
/** The instruction offset when the variable goes out of scope */
public int endpc;
/**
* Construct a LocVars instance.
* Construct a LocVars instance.
*
* @param varname The local variable name
* @param startpc The instruction offset when the variable comes into scope
* @param endpc The instruction offset when the variable goes out of scope
* @param endpc The instruction offset when the variable goes out of scope
*/
public LocVars(LuaString varname, int startpc, int endpc) {
this.varname = varname;
this.startpc = startpc;
this.endpc = endpc;
}
public String tojstring() {
return varname+" "+startpc+"-"+endpc;
return varname + " " + startpc + "-" + endpc;
}
}

View File

@@ -21,17 +21,16 @@
******************************************************************************/
package org.luaj.vm2;
/**
* Constants for lua limits and opcodes.
* <p>
* This is a direct translation of C lua distribution header file constants
* for bytecode creation and processing.
* This is a direct translation of C lua distribution header file constants for
* bytecode creation and processing.
*/
public class Lua {
/** version is supplied by ant build task */
public static final String _VERSION = "Luaj 0.0";
/** use return values from previous op */
public static final int LUA_MULTRET = -1;
@@ -46,7 +45,7 @@ public class Lua {
`C' : 9 bits
`Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
@@ -54,39 +53,37 @@ public class Lua {
unsigned argument.
===========================================================================*/
/* basic instruction format */
public static final int iABC = 0;
public static final int iABx = 1;
public static final int iAsBx = 2;
public static final int iAx = 3;
public static final int iABC = 0;
public static final int iABx = 1;
public static final int iAsBx = 2;
public static final int iAx = 3;
/*
** size and position of opcode arguments.
*/
public static final int SIZE_C = 9;
public static final int SIZE_B = 9;
public static final int SIZE_Bx = (SIZE_C + SIZE_B);
public static final int SIZE_A = 8;
public static final int SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A);
public static final int SIZE_C = 9;
public static final int SIZE_B = 9;
public static final int SIZE_Bx = (SIZE_C+SIZE_B);
public static final int SIZE_A = 8;
public static final int SIZE_Ax = (SIZE_C+SIZE_B+SIZE_A);
public static final int SIZE_OP = 6;
public static final int SIZE_OP = 6;
public static final int POS_OP = 0;
public static final int POS_A = (POS_OP + SIZE_OP);
public static final int POS_C = (POS_A + SIZE_A);
public static final int POS_B = (POS_C + SIZE_C);
public static final int POS_Bx = POS_C;
public static final int POS_Ax = POS_A;
public static final int POS_OP = 0;
public static final int POS_A = (POS_OP+SIZE_OP);
public static final int POS_C = (POS_A+SIZE_A);
public static final int POS_B = (POS_C+SIZE_C);
public static final int POS_Bx = POS_C;
public static final int POS_Ax = POS_A;
public static final int MAX_OP = ((1<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1);
public static final int MAXARG_C = ((1<<SIZE_C)-1);
public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1);
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MAXARG_Ax = ((1<<SIZE_Ax)-1);
public static final int MAX_OP = ((1<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1);
public static final int MAXARG_C = ((1<<SIZE_C)-1);
public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1);
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MAXARG_Ax = ((1<<SIZE_Ax)-1);
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
@@ -105,40 +102,39 @@ public class Lua {
** the following macros help to manipulate instructions
*/
public static int GET_OPCODE(int i) {
return (i >> POS_OP) & MAX_OP;
return (i>>POS_OP) & MAX_OP;
}
public static int GETARG_A(int i) {
return (i >> POS_A) & MAXARG_A;
return (i>>POS_A) & MAXARG_A;
}
public static int GETARG_Ax(int i) {
return (i >> POS_Ax) & MAXARG_Ax;
return (i>>POS_Ax) & MAXARG_Ax;
}
public static int GETARG_B(int i) {
return (i >> POS_B) & MAXARG_B;
return (i>>POS_B) & MAXARG_B;
}
public static int GETARG_C(int i) {
return (i >> POS_C) & MAXARG_C;
return (i>>POS_C) & MAXARG_C;
}
public static int GETARG_Bx(int i) {
return (i >> POS_Bx) & MAXARG_Bx;
return (i>>POS_Bx) & MAXARG_Bx;
}
public static int GETARG_sBx(int i) {
return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx;
return ((i>>POS_Bx) & MAXARG_Bx)-MAXARG_sBx;
}
/*
** Macros to operate RK indices
*/
/** this bit 1 means constant (0 means register) */
public static final int BITRK = (1 << (SIZE_B - 1));
public static final int BITRK = (1<<(SIZE_B-1));
/** test whether value is a constant */
public static boolean ISK(int x) {
@@ -147,22 +143,20 @@ public class Lua {
/** gets the index of the constant */
public static int INDEXK(int r) {
return ((int)(r) & ~BITRK);
return ((int) (r) & ~BITRK);
}
public static final int MAXINDEXRK = (BITRK - 1);
public static final int MAXINDEXRK = (BITRK-1);
/** code a constant index as a RK value */
public static int RKASK(int x) {
return ((x) | BITRK);
}
/**
** invalid register that fits in 8 bits
*/
public static final int NO_REG = MAXARG_A;
** invalid register that fits in 8 bits
*/
public static final int NO_REG = MAXARG_A;
/*
** R(x) - register
@@ -170,7 +164,6 @@ public class Lua {
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
@@ -178,18 +171,18 @@ public class Lua {
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
public static final int OP_MOVE = 0;/* A B R(A) := R(B) */
public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */
public static final int OP_LOADKX = 2;/* A R(A) := Kst(extra arg) */
public static final int OP_LOADBOOL = 3;/* A B C R(A) := (Bool)B; if (C) pc++ */
public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(A+B) := nil */
public static final int OP_MOVE = 0; /* A B R(A) := R(B) */
public static final int OP_LOADK = 1; /* A Bx R(A) := Kst(Bx) */
public static final int OP_LOADKX = 2; /* A R(A) := Kst(extra arg) */
public static final int OP_LOADBOOL = 3; /* A B C R(A) := (Bool)B; if (C) pc++ */
public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(A+B) := nil */
public static final int OP_GETUPVAL = 5; /* A B R(A) := UpValue[B] */
public static final int OP_GETTABUP = 6; /* A B C R(A) := UpValue[B][RK(C)] */
public static final int OP_GETTABLE = 7; /* A B C R(A) := R(B)[RK(C)] */
public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */
public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */
public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */
public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */
public static final int OP_SETTABLE = 10; /* A B C R(A)[RK(B)] := RK(C) */
public static final int OP_NEWTABLE = 11; /* A B C R(A) := {} (size = B,C) */
@@ -209,24 +202,24 @@ public class Lua {
public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */
public static final int OP_JMP = 23; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */
public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */
public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
public static final int OP_FORPREP = 33; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORCALL = 34; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
@@ -234,7 +227,7 @@ public class Lua {
public static final int OP_EXTRAARG = 39; /* Ax extra (larger) argument for previous opcode */
public static final int NUM_OPCODES = OP_EXTRAARG + 1;
public static final int NUM_OPCODES = OP_EXTRAARG+1;
/* pseudo-opcodes used in parsing only. */
public static final int OP_GT = 63; // >
@@ -242,28 +235,27 @@ public class Lua {
public static final int OP_NEQ = 61; // ~=
public static final int OP_AND = 60; // and
public static final int OP_OR = 59; // or
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
@@ -273,69 +265,73 @@ public class Lua {
** bit 7: operator is a test
*/
public static final int OpArgN = 0; /* argument is not used */
public static final int OpArgU = 1; /* argument is used */
public static final int OpArgR = 2; /* argument is a register or a jump offset */
public static final int OpArgK = 3; /* argument is a constant or register/constant */
public static final int OpArgN = 0; /* argument is not used */
public static final int OpArgU = 1; /* argument is used */
public static final int OpArgR = 2; /* argument is a register or a jump offset */
public static final int OpArgK = 3; /* argument is a constant or register/constant */
public static final int[] luaP_opmodes = {
/* T A B C mode opcode */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */
(0<<7) | (1<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABx), /* OP_LOADKX */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABUP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABUP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */
(0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */
(1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */
(0<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORCALL */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_TFORLOOP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iAx), /* OP_EXTRAARG */
};
public static final int[] luaP_opmodes = {
/* T A B C mode opcode */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */
(0<<7) | (1<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABx), /* OP_LOADKX */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABUP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABUP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */
(0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */
(1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */
(0<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORCALL */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_TFORLOOP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iAx), /* OP_EXTRAARG */
};
public static int getOpMode(int m) {
return luaP_opmodes[m] & 3;
}
public static int getBMode(int m) {
return (luaP_opmodes[m] >> 4) & 3;
return (luaP_opmodes[m]>>4) & 3;
}
public static int getCMode(int m) {
return (luaP_opmodes[m] >> 2) & 3;
return (luaP_opmodes[m]>>2) & 3;
}
public static boolean testAMode(int m) {
return 0 != (luaP_opmodes[m] & (1 << 6));
return 0 != (luaP_opmodes[m] & (1<<6));
}
public static boolean testTMode(int m) {
return 0 != (luaP_opmodes[m] & (1 << 7));
return 0 != (luaP_opmodes[m] & (1<<7));
}
/* number of list items to accumulate before a SETLIST instruction */
@@ -343,19 +339,19 @@ public class Lua {
private static final int MAXSRC = 80;
public static String chunkid( String source ) {
if ( source.startsWith("=") )
return source.substring(1);
String end = "";
if ( source.startsWith("@") ) {
source = source.substring(1);
} else {
source = "[string \""+source;
end = "\"]";
}
int n = source.length() + end.length();
if ( n > MAXSRC )
source = source.substring(0,MAXSRC-end.length()-3) + "...";
return source + end;
public static String chunkid(String source) {
if (source.startsWith("="))
return source.substring(1);
String end = "";
if (source.startsWith("@")) {
source = source.substring(1);
} else {
source = "[string \"" + source;
end = "\"]";
}
int n = source.length()+end.length();
if (n > MAXSRC)
source = source.substring(0, MAXSRC-end.length()-3) + "...";
return source+end;
}
}

View File

@@ -22,18 +22,18 @@
package org.luaj.vm2;
/**
* Extension of {@link LuaValue} which can hold a Java boolean as its value.
* Extension of {@link LuaValue} which can hold a Java boolean as its value.
* <p>
* These instance are not instantiated directly by clients.
* Instead, there are exactly twon instances of this class,
* {@link LuaValue#TRUE} and {@link LuaValue#FALSE}
* representing the lua values {@code true} and {@code false}.
* The function {@link LuaValue#valueOf(boolean)} will always
* return one of these two values.
* These instance are not instantiated directly by clients. Instead, there are
* exactly twon instances of this class, {@link LuaValue#TRUE} and
* {@link LuaValue#FALSE} representing the lua values {@code true} and
* {@code false}. The function {@link LuaValue#valueOf(boolean)} will always
* return one of these two values.
* <p>
* Any {@link LuaValue} can be converted to its equivalent
* boolean representation using {@link LuaValue#toboolean()}
* Any {@link LuaValue} can be converted to its equivalent boolean
* representation using {@link LuaValue#toboolean()}
* <p>
*
* @see LuaValue
* @see LuaValue#valueOf(boolean)
* @see LuaValue#TRUE
@@ -43,10 +43,10 @@ public final class LuaBoolean extends LuaValue {
/** The singleton instance representing lua {@code true} */
static final LuaBoolean _TRUE = new LuaBoolean(true);
/** The singleton instance representing lua {@code false} */
static final LuaBoolean _FALSE = new LuaBoolean(false);
/** Shared static metatable for boolean values represented in lua. */
public static LuaValue s_metatable;
@@ -70,11 +70,12 @@ public final class LuaBoolean extends LuaValue {
}
public LuaValue not() {
return v ? FALSE : LuaValue.TRUE;
return v? FALSE: LuaValue.TRUE;
}
/**
* Return the boolean value for this boolean
*
* @return value as a Java boolean
*/
public boolean booleanValue() {
@@ -86,18 +87,18 @@ public final class LuaBoolean extends LuaValue {
}
public String tojstring() {
return v ? "true" : "false";
return v? "true": "false";
}
public boolean optboolean(boolean defval) {
return this.v;
}
public boolean checkboolean() {
return v;
}
public LuaValue getmetatable() {
return s_metatable;
public LuaValue getmetatable() {
return s_metatable;
}
}

View File

@@ -26,44 +26,56 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
/**
* Extension of {@link LuaFunction} which executes lua bytecode.
* <p>
* A {@link LuaClosure} is a combination of a {@link Prototype}
* and a {@link LuaValue} to use as an environment for execution.
* Normally the {@link LuaValue} is a {@link Globals} in which case the environment
* will contain standard lua libraries.
* A {@link LuaClosure} is a combination of a {@link Prototype} and a
* {@link LuaValue} to use as an environment for execution. Normally the
* {@link LuaValue} is a {@link Globals} in which case the environment will
* contain standard lua libraries.
*
* <p>
* There are three main ways {@link LuaClosure} instances are created:
* <ul>
* <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li>
* <li>Construct it indirectly by loading a chunk via {@link Globals#load(java.io.Reader, String)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode processing
* <li>Construct it indirectly by loading a chunk via
* {@link Globals#load(java.io.Reader, String)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode
* processing
* </ul>
* <p>
* To construct it directly, the {@link Prototype} is typically created via a compiler such as
* {@link org.luaj.vm2.compiler.LuaC}:
* <pre> {@code
* String script = "print( 'hello, world' )";
* InputStream is = new ByteArrayInputStream(script.getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }</pre>
* To construct it directly, the {@link Prototype} is typically created via a
* compiler such as {@link org.luaj.vm2.compiler.LuaC}:
*
* <pre>
* {
* &#64;code
* String script = "print( 'hello, world' )";
* InputStream is = new ByteArrayInputStream(script.getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
* <p>
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)} method may be used:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call();
* }</pre>
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)}
* method may be used:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call();
* }
* </pre>
* <p>
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* compiling using LuaJC is installed, because no LuaClosure is created in that case
* and the value returned is a {@link LuaFunction} but not a {@link LuaClosure}.
* compiling using LuaJC is installed, because no LuaClosure is created in that
* case and the value returned is a {@link LuaFunction} but not a
* {@link LuaClosure}.
* <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue},
* all the value operations can be used directly such as:
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a
* {@link LuaValue}, all the value operations can be used directly such as:
* <ul>
* <li>{@link LuaValue#call()}</li>
* <li>{@link LuaValue#call(LuaValue)}</li>
@@ -73,8 +85,9 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
* <li>{@link LuaValue#method(String,LuaValue)}</li>
* <li>{@link LuaValue#invokemethod(String)}</li>
* <li>{@link LuaValue#invokemethod(String,Varargs)}</li>
* <li> ...</li>
* <li>...</li>
* </ul>
*
* @see LuaValue
* @see LuaFunction
* @see LuaValue#isclosure()
@@ -85,16 +98,19 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
*/
public class LuaClosure extends LuaFunction {
private static final UpValue[] NOUPVALUES = new UpValue[0];
public final Prototype p;
public UpValue[] upValues;
final Globals globals;
/** Create a closure around a Prototype with a specific environment.
* If the prototype has upvalues, the environment will be written into the first upvalue.
* @param p the Prototype to construct this Closure for.
/**
* Create a closure around a Prototype with a specific environment. If the
* prototype has upvalues, the environment will be written into the first
* upvalue.
*
* @param p the Prototype to construct this Closure for.
* @param env the environment to associate with the closure.
*/
public LuaClosure(Prototype p, LuaValue env) {
@@ -102,21 +118,20 @@ public class LuaClosure extends LuaFunction {
this.initupvalue1(env);
globals = env instanceof Globals? (Globals) env: null;
}
public void initupvalue1(LuaValue env) {
if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES;
else {
this.upValues = new UpValue[p.upvalues.length];
this.upValues[0] = new UpValue(new LuaValue[] {env}, 0);
this.upValues[0] = new UpValue(new LuaValue[] { env }, 0);
}
}
public boolean isclosure() {
return true;
}
public LuaClosure optclosure(LuaClosure defval) {
return this;
}
@@ -124,379 +139,435 @@ public class LuaClosure extends LuaFunction {
public LuaClosure checkclosure() {
return this;
}
public String tojstring() {
return "function: " + p.toString();
}
private LuaValue[] getNewStack() {
int max = p.maxstacksize;
LuaValue[] stack = new LuaValue[max];
System.arraycopy(NILS, 0, stack, 0, max);
return stack;
}
public final LuaValue call() {
LuaValue[] stack = getNewStack();
return execute(stack,NONE).arg1();
return execute(stack, NONE).arg1();
}
public final LuaValue call(LuaValue arg) {
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg; return execute(stack,NONE).arg1();
case 0: return execute(stack,arg).arg1();
switch (p.numparams) {
default:
stack[0] = arg;
return execute(stack, NONE).arg1();
case 0:
return execute(stack, arg).arg1();
}
}
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg1; stack[1]=arg2; return execute(stack,NONE).arg1();
case 1: stack[0]=arg1; return execute(stack,arg2).arg1();
case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2): NONE).arg1();
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, NONE).arg1();
case 1:
stack[0] = arg1;
return execute(stack, arg2).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2): NONE).arg1();
}
}
public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
LuaValue[] stack = getNewStack();
switch ( p.numparams ) {
default: stack[0]=arg1; stack[1]=arg2; stack[2]=arg3; return execute(stack,NONE).arg1();
case 2: stack[0]=arg1; stack[1]=arg2; return execute(stack,arg3).arg1();
case 1: stack[0]=arg1; return execute(stack,p.is_vararg!=0? varargsOf(arg2,arg3): NONE).arg1();
case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2,arg3): NONE).arg1();
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
stack[2] = arg3;
return execute(stack, NONE).arg1();
case 2:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, arg3).arg1();
case 1:
stack[0] = arg1;
return execute(stack, p.is_vararg != 0? varargsOf(arg2, arg3): NONE).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2, arg3): NONE).arg1();
}
}
public final Varargs invoke(Varargs varargs) {
return onInvoke(varargs).eval();
}
public final Varargs onInvoke(Varargs varargs) {
LuaValue[] stack = getNewStack();
for ( int i=0; i<p.numparams; i++ )
for (int i = 0; i < p.numparams; i++)
stack[i] = varargs.arg(i+1);
return execute(stack,p.is_vararg!=0? varargs.subargs(p.numparams+1): NONE);
return execute(stack, p.is_vararg != 0? varargs.subargs(p.numparams+1): NONE);
}
protected Varargs execute( LuaValue[] stack, Varargs varargs ) {
protected Varargs execute(LuaValue[] stack, Varargs varargs) {
// loop through instructions
int i,a,b,c,pc=0,top=0;
int i, a, b, c, pc = 0, top = 0;
LuaValue o;
Varargs v = NONE;
int[] code = p.code;
LuaValue[] k = p.k;
// upvalues are only possible when closures create closures
// TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null;
UpValue[] openups = p.p.length > 0? new UpValue[stack.length]: null;
// allow for debug hooks
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall( this, varargs, stack );
globals.debuglib.onCall(this, varargs, stack);
// process instructions
try {
for (; true; ++pc) {
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction( pc, v, top );
globals.debuglib.onInstruction(pc, v, top);
// pull out instruction
i = code[pc];
a = ((i>>6) & 0xff);
// process the op code
switch ( i & 0x3f ) {
switch (i & 0x3f) {
case Lua.OP_MOVE:/* A B R(A):= R(B) */
stack[a] = stack[i>>>23];
continue;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
stack[a] = k[i>>>14];
continue;
case Lua.OP_LOADKX:/* A R(A) := Kst(extra arg) */
++pc;
i = code[pc];
if ((i & 0x3f) != Lua.OP_EXTRAARG) {
int op = i & 0x3f;
throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got " +
(op < Print.OPNAMES.length - 1 ? Print.OPNAMES[op] : "UNKNOWN_OP_" + op));
throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got "
+ (op < Print.OPNAMES.length-1? Print.OPNAMES[op]: "UNKNOWN_OP_" + op));
}
stack[a] = k[i>>>6];
continue;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
stack[a] = (i>>>23!=0)? LuaValue.TRUE: LuaValue.FALSE;
if ((i&(0x1ff<<14)) != 0)
++pc; /* skip next instruction (if C) */
continue;
stack[a] = (i>>>23 != 0)? LuaValue.TRUE: LuaValue.FALSE;
if ((i & (0x1ff<<14)) != 0)
++pc; /* skip next instruction (if C) */
continue;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
for ( b=i>>>23; b-->=0; )
for (b = i>>>23; b-- >= 0;)
stack[a++] = LuaValue.NIL;
continue;
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
stack[a] = upValues[i>>>23].getValue();
continue;
stack[a] = upValues[i>>>23].getValue();
continue;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
stack[a] = upValues[i>>>23].getValue().get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = upValues[i>>>23].getValue().get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
stack[a] = stack[i>>>23].get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = stack[i>>>23].get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
upValues[a].getValue().set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
upValues[a].getValue().set(((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b]),
(c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
upValues[i>>>23].setValue(stack[a]);
continue;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
stack[a].set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a].set(((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b]),
(c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
stack[a] = new LuaTable(i>>>23,(i>>14)&0x1ff);
stack[a] = new LuaTable(i>>>23, (i>>14) & 0x1ff);
continue;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
stack[a+1] = (o = stack[i>>>23]);
stack[a] = o.get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = o.get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).add((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.add((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).sub((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.sub((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mul((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mul((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).div((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.div((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mod((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mod((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).pow((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]);
stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.pow((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue;
case Lua.OP_UNM: /* A B R(A):= -R(B) */
stack[a] = stack[i>>>23].neg();
continue;
case Lua.OP_NOT: /* A B R(A):= not R(B) */
stack[a] = stack[i>>>23].not();
continue;
case Lua.OP_LEN: /* A B R(A):= length of R(B) */
stack[a] = stack[i>>>23].len();
continue;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
b = i>>>23;
c = (i>>14)&0x1ff;
{
if ( c > b+1 ) {
Buffer sb = stack[c].buffer();
while ( --c>=b )
sb.concatTo(stack[c]);
stack[a] = sb.value();
} else {
stack[a] = stack[c-1].concat(stack[c]);
}
c = (i>>14) & 0x1ff; {
if (c > b+1) {
Buffer sb = stack[c].buffer();
while ( --c >= b )
sb.concatTo(stack[c]);
stack[a] = sb.value();
} else {
stack[a] = stack[c-1].concat(stack[c]);
}
}
continue;
case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
pc += (i>>>14)-0x1ffff;
pc += (i>>>14)-0x1ffff;
if (a > 0) {
for (--a, b = openups.length; --b>=0; )
for (--a, b = openups.length; --b >= 0;)
if (openups[b] != null && openups[b].index >= a) {
openups[b].close();
openups[b] = null;
}
}
continue;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).eq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.eq_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lt_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lt_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lteq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) )
if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lteq_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if ( stack[a].toboolean() != ((i&(0x1ff<<14))!=0) )
if (stack[a].toboolean() != ((i & (0x1ff<<14)) != 0))
++pc;
continue;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
/* note: doc appears to be reversed */
if ( (o=stack[i>>>23]).toboolean() != ((i&(0x1ff<<14))!=0) )
if ((o = stack[i>>>23]).toboolean() != ((i & (0x1ff<<14)) != 0))
++pc;
else
stack[a] = o; // TODO: should be sBx?
continue;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
switch ( i & (Lua.MASK_B | Lua.MASK_C) ) {
case (1<<Lua.POS_B) | (0<<Lua.POS_C): v=stack[a].invoke(NONE); top=a+v.narg(); continue;
case (2<<Lua.POS_B) | (0<<Lua.POS_C): v=stack[a].invoke(stack[a+1]); top=a+v.narg(); continue;
case (1<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(); continue;
case (2<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1]); continue;
case (3<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1],stack[a+2]); continue;
case (4<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1],stack[a+2],stack[a+3]); continue;
case (1<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(); continue;
case (2<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1]); continue;
case (3<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1],stack[a+2]); continue;
case (4<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1],stack[a+2],stack[a+3]); continue;
switch (i & (Lua.MASK_B | Lua.MASK_C)) {
case (1<<Lua.POS_B) | (0<<Lua.POS_C):
v = stack[a].invoke(NONE);
top = a+v.narg();
continue;
case (2<<Lua.POS_B) | (0<<Lua.POS_C):
v = stack[a].invoke(stack[a+1]);
top = a+v.narg();
continue;
case (1<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call();
continue;
case (2<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1]);
continue;
case (3<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1], stack[a+2]);
continue;
case (4<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
case (1<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call();
continue;
case (2<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1]);
continue;
case (3<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1], stack[a+2]);
continue;
case (4<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
default:
b = i>>>23;
c = (i>>14)&0x1ff;
v = stack[a].invoke(b>0?
varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
if ( c > 0 ) {
c = (i>>14) & 0x1ff;
v = stack[a].invoke(b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
if (c > 0) {
v.copyto(stack, a, c-1);
v = NONE;
} else {
top = a + v.narg();
top = a+v.narg();
v = v.dealias();
}
continue;
}
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
switch ( i & Lua.MASK_B ) {
case (1<<Lua.POS_B): return new TailcallVarargs(stack[a], NONE);
case (2<<Lua.POS_B): return new TailcallVarargs(stack[a], stack[a+1]);
case (3<<Lua.POS_B): return new TailcallVarargs(stack[a], varargsOf(stack[a+1],stack[a+2]));
case (4<<Lua.POS_B): return new TailcallVarargs(stack[a], varargsOf(stack[a+1],stack[a+2],stack[a+3]));
switch (i & Lua.MASK_B) {
case (1<<Lua.POS_B):
return new TailcallVarargs(stack[a], NONE);
case (2<<Lua.POS_B):
return new TailcallVarargs(stack[a], stack[a+1]);
case (3<<Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2]));
case (4<<Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2], stack[a+3]));
default:
b = i>>>23;
v = b>0?
varargsOf(stack,a+1,b-1): // exact arg count
v = b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top
return new TailcallVarargs( stack[a], v );
return new TailcallVarargs(stack[a], v);
}
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
b = i>>>23;
switch ( b ) {
case 0: return varargsOf(stack, a, top-v.narg()-a, v);
case 1: return NONE;
case 2: return stack[a];
switch (b) {
case 0:
return varargsOf(stack, a, top-v.narg()-a, v);
case 1:
return NONE;
case 2:
return stack[a];
default:
return varargsOf(stack, a, b-1);
}
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
{
LuaValue limit = stack[a + 1];
LuaValue step = stack[a + 2];
LuaValue idx = stack[a].add(step);
if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) {
stack[a] = idx;
stack[a + 3] = idx;
pc += (i>>>14)-0x1ffff;
}
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a + 1].checknumber("'for' limit must be a number");
LuaValue step = stack[a + 2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a + 1] = limit;
stack[a + 2] = step;
{
LuaValue limit = stack[a+1];
LuaValue step = stack[a+2];
LuaValue idx = stack[a].add(step);
if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) {
stack[a] = idx;
stack[a+3] = idx;
pc += (i>>>14)-0x1ffff;
}
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a+1].checknumber("'for' limit must be a number");
LuaValue step = stack[a+2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a+1] = limit;
stack[a+2] = step;
pc += (i>>>14)-0x1ffff;
}
continue;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
v = stack[a].invoke(varargsOf(stack[a+1],stack[a+2]));
v = stack[a].invoke(varargsOf(stack[a+1], stack[a+2]));
c = (i>>14) & 0x1ff;
while (--c >= 0)
while ( --c >= 0 )
stack[a+3+c] = v.arg(c+1);
v = NONE;
continue;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
if (!stack[a+1].isnil()) { /* continue loop? */
stack[a] = stack[a+1]; /* save control varible. */
stack[a] = stack[a+1]; /* save control varible. */
pc += (i>>>14)-0x1ffff;
}
continue;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
{
if ( (c=(i>>14)&0x1ff) == 0 )
c = code[++pc];
int offset = (c-1) * Lua.LFIELDS_PER_FLUSH;
o = stack[a];
if ( (b=i>>>23) == 0 ) {
b = top - a - 1;
int m = b - v.narg();
int j=1;
for ( ;j<=m; j++ )
o.set(offset+j, stack[a + j]);
for ( ;j<=b; j++ )
o.set(offset+j, v.arg(j-m));
} else {
o.presize( offset + b );
for (int j=1; j<=b; j++)
o.set(offset+j, stack[a + j]);
}
{
if ((c = (i>>14) & 0x1ff) == 0)
c = code[++pc];
int offset = (c-1)*Lua.LFIELDS_PER_FLUSH;
o = stack[a];
if ((b = i>>>23) == 0) {
b = top-a-1;
int m = b-v.narg();
int j = 1;
for (; j <= m; j++)
o.set(offset+j, stack[a+j]);
for (; j <= b; j++)
o.set(offset+j, v.arg(j-m));
} else {
o.presize(offset+b);
for (int j = 1; j <= b; j++)
o.set(offset+j, stack[a+j]);
}
}
continue;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */
{
Prototype newp = p.p[i>>>14];
LuaClosure ncl = new LuaClosure(newp, globals);
Upvaldesc[] uv = newp.upvalues;
for ( int j=0, nup=uv.length; j<nup; ++j ) {
if (uv[j].instack) /* upvalue refes to local variable? */
ncl.upValues[j] = findupval(stack, uv[j].idx, openups);
else /* get upvalue from enclosing function */
ncl.upValues[j] = upValues[uv[j].idx];
}
stack[a] = ncl;
{
Prototype newp = p.p[i>>>14];
LuaClosure ncl = new LuaClosure(newp, globals);
Upvaldesc[] uv = newp.upvalues;
for (int j = 0, nup = uv.length; j < nup; ++j) {
if (uv[j].instack) /* upvalue refes to local variable? */
ncl.upValues[j] = findupval(stack, uv[j].idx, openups);
else /* get upvalue from enclosing function */
ncl.upValues[j] = upValues[uv[j].idx];
}
stack[a] = ncl;
}
continue;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
b = i>>>23;
if ( b == 0 ) {
top = a + (b = varargs.narg());
if (b == 0) {
top = a+(b = varargs.narg());
v = varargs;
} else {
for ( int j=1; j<b; ++j )
for (int j = 1; j < b; ++j)
stack[a+j-1] = varargs.arg(j);
}
continue;
@@ -508,18 +579,18 @@ public class LuaClosure extends LuaFunction {
throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f));
}
}
} catch ( LuaError le ) {
} catch (LuaError le) {
if (le.traceback == null)
processErrorHooks(le, p, pc);
throw le;
} catch ( Exception e ) {
} catch (Exception e) {
LuaError le = new LuaError(e);
processErrorHooks(le, p, pc);
throw le;
} finally {
if ( openups != null )
for ( int u=openups.length; --u>=0; )
if ( openups[u] != null )
if (openups != null)
for (int u = openups.length; --u >= 0;)
if (openups[u] != null)
openups[u].close();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
@@ -527,21 +598,21 @@ public class LuaClosure extends LuaFunction {
}
/**
* Run the error hook if there is one
* @param msg the message to use in error hook processing.
* */
* Run the error hook if there is one
*
* @param msg the message to use in error hook processing.
*/
String errorHook(String msg, int level) {
if (globals == null ) return msg;
if (globals == null)
return msg;
final LuaThread r = globals.running;
if (r.errorfunc == null)
return globals.debuglib != null?
msg + "\n" + globals.debuglib.traceback(level):
msg;
return globals.debuglib != null? msg + "\n" + globals.debuglib.traceback(level): msg;
final LuaValue e = r.errorfunc;
r.errorfunc = null;
try {
return e.call( LuaValue.valueOf(msg) ).tojstring();
} catch ( Throwable t ) {
return e.call(LuaValue.valueOf(msg)).tojstring();
} catch (Throwable t) {
return "error in error handling";
} finally {
r.errorfunc = e;
@@ -557,19 +628,19 @@ public class LuaClosure extends LuaFunction {
frame = globals.debuglib.getCallFrame(le.level);
if (frame != null) {
String src = frame.shortsource();
file = src != null ? src : "?";
file = src != null? src: "?";
line = frame.currentline();
}
}
if (frame == null) {
file = p.source != null? p.source.tojstring(): "?";
line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length ? p.lineinfo[pc] : -1;
line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length? p.lineinfo[pc]: -1;
}
}
le.fileline = file + ":" + line;
le.traceback = errorHook(le.getMessage(), le.level);
}
private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) {
final int n = openups.length;
for (int i = 0; i < n; ++i)
@@ -585,14 +656,13 @@ public class LuaClosure extends LuaFunction {
protected LuaValue getUpvalue(int i) {
return upValues[i].getValue();
}
protected void setUpvalue(int i, LuaValue v) {
upValues[i].setValue(v);
}
public String name() {
return "<"+p.shortsource()+":"+p.linedefined+">";
return "<" + p.shortsource() + ":" + p.linedefined + ">";
}
}

View File

@@ -26,18 +26,21 @@ import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java double as its value.
* <p>
* These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
* functions. This ensures that values which can be represented as int
* are wrapped in {@link LuaInteger} instead of {@link LuaDouble}.
* These instance are not instantiated directly by clients, but indirectly via
* the static functions {@link LuaValue#valueOf(int)} or
* {@link LuaValue#valueOf(double)} functions. This ensures that values which
* can be represented as int are wrapped in {@link LuaInteger} instead of
* {@link LuaDouble}.
* <p>
* Almost all API's implemented in LuaDouble are defined and documented in {@link LuaValue}.
* Almost all API's implemented in LuaDouble are defined and documented in
* {@link LuaValue}.
* <p>
* However the constants {@link #NAN}, {@link #POSINF}, {@link #NEGINF},
* {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be useful
* when dealing with Nan or Infinite values.
* {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be
* useful when dealing with Nan or Infinite values.
* <p>
* LuaDouble also defines functions for handling the unique math rules of lua devision and modulo in
* LuaDouble also defines functions for handling the unique math rules of lua
* devision and modulo in
* <ul>
* <li>{@link #ddiv(double, double)}</li>
* <li>{@link #ddiv_d(double, double)}</li>
@@ -45,6 +48,7 @@ import org.luaj.vm2.lib.MathLib;
* <li>{@link #dmod_d(double, double)}</li>
* </ul>
* <p>
*
* @see LuaValue
* @see LuaNumber
* @see LuaInteger
@@ -54,186 +58,260 @@ import org.luaj.vm2.lib.MathLib;
public class LuaDouble extends LuaNumber {
/** Constant LuaDouble representing NaN (not a number) */
public static final LuaDouble NAN = new LuaDouble( Double.NaN );
public static final LuaDouble NAN = new LuaDouble(Double.NaN);
/** Constant LuaDouble representing positive infinity */
public static final LuaDouble POSINF = new LuaDouble( Double.POSITIVE_INFINITY );
public static final LuaDouble POSINF = new LuaDouble(Double.POSITIVE_INFINITY);
/** Constant LuaDouble representing negative infinity */
public static final LuaDouble NEGINF = new LuaDouble( Double.NEGATIVE_INFINITY );
public static final LuaDouble NEGINF = new LuaDouble(Double.NEGATIVE_INFINITY);
/** Constant String representation for NaN (not a number), "nan" */
public static final String JSTR_NAN = "nan";
public static final String JSTR_NAN = "nan";
/** Constant String representation for positive infinity, "inf" */
public static final String JSTR_POSINF = "inf";
/** Constant String representation for negative infinity, "-inf" */
public static final String JSTR_NEGINF = "-inf";
/** The value being held by this instance. */
final double v;
public static LuaNumber valueOf(double d) {
int id = (int) d;
return d==id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d);
return d == id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d);
}
/** Don't allow ints to be boxed by DoubleValues */
/** Don't allow ints to be boxed by DoubleValues */
private LuaDouble(double d) {
this.v = d;
}
public int hashCode() {
long l = Double.doubleToLongBits(v + 1);
return ((int)(l>>32)) + (int) l;
long l = Double.doubleToLongBits(v+1);
return ((int) (l>>32))+(int) l;
}
public boolean islong() {
return v == (long) v;
}
public byte tobyte() { return (byte) (long) v; }
public char tochar() { return (char) (long) v; }
public double todouble() { return v; }
public float tofloat() { return (float) v; }
public int toint() { return (int) (long) v; }
public long tolong() { return (long) v; }
public short toshort() { return (short) (long) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return (int) (long) v; }
public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long)v); }
public long optlong(long defval) { return (long) v; }
public LuaInteger checkinteger() { return LuaInteger.valueOf( (int) (long) v ); }
public byte tobyte() { return (byte) (long) v; }
public char tochar() { return (char) (long) v; }
public double todouble() { return v; }
public float tofloat() { return (float) v; }
public int toint() { return (int) (long) v; }
public long tolong() { return (long) v; }
public short toshort() { return (short) (long) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return (int) (long) v; }
public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long) v); }
public long optlong(long defval) { return (long) v; }
public LuaInteger checkinteger() { return LuaInteger.valueOf((int) (long) v); }
// unary operators
public LuaValue neg() { return valueOf(-v); }
// object equality, used for key comparison
public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; }
public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble) o).v == v: false; }
// equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(v); }
public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(v); }
public boolean raweq( double val ) { return v == val; }
public boolean raweq( int val ) { return v == val; }
public boolean raweq(LuaValue val) { return val.raweq(v); }
public boolean raweq(double val) { return v == val; }
public boolean raweq(int val) { return v == val; }
// basic binary arithmetic
public LuaValue add( LuaValue rhs ) { return rhs.add(v); }
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); }
public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); }
public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); }
public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue mul( int lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); }
public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); }
public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
/** Divide two double numbers according to lua math, and return a {@link LuaValue} result.
public LuaValue add(LuaValue rhs) { return rhs.add(v); }
public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue sub(int rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue mul(int lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
/**
* Divide two double numbers according to lua math, and return a
* {@link LuaValue} result.
*
* @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division.
* @return {@link LuaValue} for the result of the division,
* taking into account positive and negiative infinity, and Nan
* @return {@link LuaValue} for the result of the division, taking into
* account positive and negiative infinity, and Nan
* @see #ddiv_d(double, double)
*/
public static LuaValue ddiv(double lhs, double rhs) {
return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF;
return rhs != 0? valueOf(lhs/rhs): lhs > 0? POSINF: lhs == 0? NAN: NEGINF;
}
/** Divide two double numbers according to lua math, and return a double result.
/**
* Divide two double numbers according to lua math, and return a double
* result.
*
* @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division.
* @return Value of the division, taking into account positive and negative infinity, and Nan
* @return Value of the division, taking into account positive and negative
* infinity, and Nan
* @see #ddiv(double, double)
*/
public static double ddiv_d(double lhs, double rhs) {
return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY;
return rhs != 0? lhs/rhs: lhs > 0? Double.POSITIVE_INFINITY: lhs == 0? Double.NaN: Double.NEGATIVE_INFINITY;
}
/** Take modulo double numbers according to lua math, and return a {@link LuaValue} result.
/**
* Take modulo double numbers according to lua math, and return a
* {@link LuaValue} result.
*
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
* @return {@link LuaValue} for the result of the modulo,
* using lua's rules for modulo
* @return {@link LuaValue} for the result of the modulo, using lua's rules
* for modulo
* @see #dmod_d(double, double)
*/
public static LuaValue dmod(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return NAN;
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY)
return NAN;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? POSINF : valueOf(lhs);
return lhs < 0? POSINF: valueOf(lhs);
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? NEGINF : valueOf(lhs);
return lhs > 0? NEGINF: valueOf(lhs);
}
return valueOf( lhs-rhs*Math.floor(lhs/rhs) );
return valueOf(lhs-rhs*Math.floor(lhs/rhs));
}
/** Take modulo for double numbers according to lua math, and return a double result.
/**
* Take modulo for double numbers according to lua math, and return a double
* result.
*
* @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo.
* @return double value for the result of the modulo,
* using lua's rules for modulo
* @return double value for the result of the modulo, using lua's rules for
* modulo
* @see #dmod(double, double)
*/
public static double dmod_d(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return Double.NaN;
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY)
return Double.NaN;
if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? Double.POSITIVE_INFINITY : lhs;
return lhs < 0? Double.POSITIVE_INFINITY: lhs;
}
if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? Double.NEGATIVE_INFINITY : lhs;
return lhs > 0? Double.NEGATIVE_INFINITY: lhs;
}
return lhs-rhs*Math.floor(lhs/rhs);
}
// relational operators
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.gt_b(v)? TRUE: FALSE): super.lt(rhs); }
public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
public boolean lt_b(int rhs) { return v < rhs; }
public boolean lt_b(double rhs) { return v < rhs; }
public LuaValue lteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.gteq_b(v)? TRUE: FALSE): super.lteq(rhs);
}
public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
public boolean lteq_b(int rhs) { return v <= rhs; }
public boolean lteq_b(double rhs) { return v <= rhs; }
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.lt_b(v)? TRUE: FALSE): super.gt(rhs); }
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
public boolean gt_b(int rhs) { return v > rhs; }
public boolean gt_b(double rhs) { return v > rhs; }
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.lteq_b(v)? TRUE: FALSE): super.gteq(rhs);
}
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
public boolean gteq_b(int rhs) { return v >= rhs; }
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; }
public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
public String tojstring() {
/*
if ( v == 0.0 ) { // never occurs in J2me
@@ -242,58 +320,63 @@ public class LuaDouble extends LuaNumber {
}
*/
long l = (long) v;
if ( l == v )
if (l == v)
return Long.toString(l);
if ( Double.isNaN(v) )
if (Double.isNaN(v))
return JSTR_NAN;
if ( Double.isInfinite(v) )
return (v<0? JSTR_NEGINF: JSTR_POSINF);
return Float.toString((float)v);
if (Double.isInfinite(v))
return (v < 0? JSTR_NEGINF: JSTR_POSINF);
return Float.toString((float) v);
}
public LuaString strvalue() {
return LuaString.valueOf(tojstring());
}
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(tojstring());
}
public LuaValue tostring() {
return LuaString.valueOf(tojstring());
}
public String optjstring(String defval) {
return tojstring();
}
public LuaNumber optnumber(LuaNumber defval) {
return this;
}
public boolean isnumber() {
return true;
}
public boolean isstring() {
return true;
}
public LuaValue tonumber() {
return this;
}
public int checkint() { return (int) (long) v; }
public long checklong() { return (long) v; }
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
public int checkint() { return (int) (long) v; }
public long checklong() { return (long) v; }
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
public String checkjstring() {
return tojstring();
}
public LuaString checkstring() {
return LuaString.valueOf(tojstring());
}
public boolean isvalidkey() {
return !Double.isNaN(v);
}

View File

@@ -21,38 +21,38 @@
******************************************************************************/
package org.luaj.vm2;
/**
* RuntimeException that is thrown and caught in response to a lua error.
* RuntimeException that is thrown and caught in response to a lua error.
* <p>
* {@link LuaError} is used wherever a lua call to {@code error()}
* would be used within a script.
* {@link LuaError} is used wherever a lua call to {@code error()} would be used
* within a script.
* <p>
* Since it is an unchecked exception inheriting from {@link RuntimeException},
* Java method signatures do notdeclare this exception, althoug it can
* be thrown on almost any luaj Java operation.
* This is analagous to the fact that any lua script can throw a lua error at any time.
* <p>
* The LuaError may be constructed with a message object, in which case the message
* is the string representation of that object. getMessageObject will get the object
* supplied at construct time, or a LuaString containing the message of an object
* was not supplied.
* Java method signatures do notdeclare this exception, althoug it can be thrown
* on almost any luaj Java operation. This is analagous to the fact that any lua
* script can throw a lua error at any time.
* <p>
* The LuaError may be constructed with a message object, in which case the
* message is the string representation of that object. getMessageObject will
* get the object supplied at construct time, or a LuaString containing the
* message of an object was not supplied.
*/
public class LuaError extends RuntimeException {
private static final long serialVersionUID = 1L;
protected int level;
protected String fileline;
protected String traceback;
protected Throwable cause;
private LuaValue object;
/** Get the string message if it was supplied, or a string
* representation of the message object if that was supplied.
/**
* Get the string message if it was supplied, or a string representation of
* the message object if that was supplied.
*/
public String getMessage() {
if (traceback != null)
@@ -65,66 +65,70 @@ public class LuaError extends RuntimeException {
return m;
}
/** Get the LuaValue that was provided in the constructor, or
* a LuaString containing the message if it was a string error argument.
/**
* Get the LuaValue that was provided in the constructor, or a LuaString
* containing the message if it was a string error argument.
*
* @return LuaValue which was used in the constructor, or a LuaString
* containing the message.
* containing the message.
*/
public LuaValue getMessageObject() {
if (object != null) return object;
if (object != null)
return object;
String m = getMessage();
return m != null ? LuaValue.valueOf(m): null;
return m != null? LuaValue.valueOf(m): null;
}
/** Construct LuaError when a program exception occurs.
* <p>
/**
* Construct LuaError when a program exception occurs.
* <p>
* All errors generated from lua code should throw LuaError(String) instead.
* @param cause the Throwable that caused the error, if known.
*
* @param cause the Throwable that caused the error, if known.
*/
public LuaError(Throwable cause) {
super( "vm error: "+cause );
super("vm error: " + cause);
this.cause = cause;
this.level = 1;
}
/**
* Construct a LuaError with a specific message.
*
* Construct a LuaError with a specific message.
*
* @param message message to supply
*/
public LuaError(String message) {
super( message );
super(message);
this.level = 1;
}
}
/**
* Construct a LuaError with a message, and level to draw line number information from.
* Construct a LuaError with a message, and level to draw line number
* information from.
*
* @param message message to supply
* @param level where to supply line info from in call stack
* @param level where to supply line info from in call stack
*/
public LuaError(String message, int level) {
super( message );
super(message);
this.level = level;
}
}
/**
* Construct a LuaError with a LuaValue as the message object,
* and level to draw line number information from.
* Construct a LuaError with a LuaValue as the message object, and level to
* draw line number information from.
*
* @param message_object message string or object to supply
*/
public LuaError(LuaValue message_object) {
super( message_object.tojstring() );
super(message_object.tojstring());
this.object = message_object;
this.level = 1;
}
/**
* Get the cause, if any.
*/
public Throwable getCause() {
return cause;
}
/**
* Get the cause, if any.
*/
public Throwable getCause() { return cause; }
}

View File

@@ -21,41 +21,39 @@
******************************************************************************/
package org.luaj.vm2;
/**
* Base class for functions implemented in Java.
* <p>
* Direct subclass include {@link org.luaj.vm2.lib.LibFunction}
* which is the base class for
* all built-in library functions coded in Java,
* and {@link LuaClosure}, which represents a lua closure
* whose bytecode is interpreted when the function is invoked.
* Direct subclass include {@link org.luaj.vm2.lib.LibFunction} which is the
* base class for all built-in library functions coded in Java, and
* {@link LuaClosure}, which represents a lua closure whose bytecode is
* interpreted when the function is invoked.
*
* @see LuaValue
* @see LuaClosure
* @see org.luaj.vm2.lib.LibFunction
*/
abstract
public class LuaFunction extends LuaValue {
abstract public class LuaFunction extends LuaValue {
/** Shared static metatable for all functions and closures. */
public static LuaValue s_metatable;
public int type() {
return TFUNCTION;
}
public String typename() {
return "function";
}
public boolean isfunction() {
return true;
}
public LuaFunction checkfunction() {
public LuaFunction checkfunction() {
return this;
}
public LuaFunction optfunction(LuaFunction defval) {
return this;
}
@@ -72,20 +70,29 @@ public class LuaFunction extends LuaValue {
return valueOf(tojstring());
}
/** Return the last part of the class name, to be used as a function name in tojstring and elsewhere.
* @return String naming the last part of the class name after the last dot (.) or dollar sign ($).
* If the first character is '_', it is skipped.
/**
* Return the last part of the class name, to be used as a function name in
* tojstring and elsewhere.
*
* @return String naming the last part of the class name after the last dot
* (.) or dollar sign ($). If the first character is '_', it is
* skipped.
*/
public String classnamestub() {
String s = getClass().getName();
int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$')) + 1;
if (s.charAt(offset) == '_') offset++;
int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$'))+1;
if (s.charAt(offset) == '_')
offset++;
return s.substring(offset);
}
/** Return a human-readable name for this function. Returns the last part of the class name by default.
* Is overridden by LuaClosure to return the source file and line, and by LibFunctions to return the name.
* @return common name for this function. */
/**
* Return a human-readable name for this function. Returns the last part of
* the class name by default. Is overridden by LuaClosure to return the
* source file and line, and by LibFunctions to return the name.
*
* @return common name for this function.
*/
public String name() {
return classnamestub();
}

View File

@@ -26,13 +26,13 @@ import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java int as its value.
* <p>
* These instance are not instantiated directly by clients, but indirectly
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)}
* functions. This ensures that policies regarding pooling of instances are
* encapsulated.
* These instance are not instantiated directly by clients, but indirectly via
* the static functions {@link LuaValue#valueOf(int)} or
* {@link LuaValue#valueOf(double)} functions. This ensures that policies
* regarding pooling of instances are encapsulated.
* <p>
* There are no API's specific to LuaInteger that are useful beyond what is already
* exposed in {@link LuaValue}.
* There are no API's specific to LuaInteger that are useful beyond what is
* already exposed in {@link LuaValue}.
*
* @see LuaValue
* @see LuaNumber
@@ -44,16 +44,18 @@ public class LuaInteger extends LuaNumber {
private static final LuaInteger[] intValues = new LuaInteger[512];
static {
for ( int i=0; i<512; i++ )
for (int i = 0; i < 512; i++)
intValues[i] = new LuaInteger(i-256);
}
public static LuaInteger valueOf(int i) {
return i<=255 && i>=-256? intValues[i+256]: new LuaInteger(i);
return i <= 255 && i >= -256? intValues[i+256]: new LuaInteger(i);
};
// TODO consider moving this to LuaValue
/** Return a LuaNumber that represents the value provided
// TODO consider moving this to LuaValue
/**
* Return a LuaNumber that represents the value provided
*
* @param l long value to represent.
* @return LuaNumber that is eithe LuaInteger or LuaDouble representing l
* @see LuaValue#valueOf(int)
@@ -61,38 +63,49 @@ public class LuaInteger extends LuaNumber {
*/
public static LuaNumber valueOf(long l) {
int i = (int) l;
return l==i? (i<=255 && i>=-256? intValues[i+256]:
(LuaNumber) new LuaInteger(i)):
(LuaNumber) LuaDouble.valueOf(l);
return l == i? (i <= 255 && i >= -256? intValues[i+256]: (LuaNumber) new LuaInteger(i))
: (LuaNumber) LuaDouble.valueOf(l);
}
/** The value being held by this instance. */
public final int v;
/**
* Package protected constructor.
*
* @see LuaValue#valueOf(int)
**/
LuaInteger(int i) {
this.v = i;
}
public boolean isint() { return true; }
public boolean isinttype() { return true; }
public boolean islong() { return true; }
public byte tobyte() { return (byte) v; }
public char tochar() { return (char) v; }
public double todouble() { return v; }
public float tofloat() { return v; }
public int toint() { return v; }
public long tolong() { return v; }
public short toshort() { return (short) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return v; }
public LuaInteger optinteger(LuaInteger defval) { return this; }
public long optlong(long defval) { return v; }
public boolean isint() { return true; }
public boolean isinttype() { return true; }
public boolean islong() { return true; }
public byte tobyte() { return (byte) v; }
public char tochar() { return (char) v; }
public double todouble() { return v; }
public float tofloat() { return v; }
public int toint() { return v; }
public long tolong() { return v; }
public short toshort() { return (short) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return v; }
public LuaInteger optinteger(LuaInteger defval) { return this; }
public long optlong(long defval) { return v; }
public String tojstring() {
return Integer.toString(v);
@@ -101,27 +114,27 @@ public class LuaInteger extends LuaNumber {
public LuaString strvalue() {
return LuaString.valueOf(Integer.toString(v));
}
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(Integer.toString(v));
}
public LuaValue tostring() {
return LuaString.valueOf(Integer.toString(v));
}
public String optjstring(String defval) {
return Integer.toString(v);
}
public LuaInteger checkinteger() {
return this;
}
public boolean isstring() {
return true;
}
public int hashCode() {
return v;
}
@@ -131,89 +144,146 @@ public class LuaInteger extends LuaNumber {
}
// unary operators
public LuaValue neg() { return valueOf(-(long)v); }
public LuaValue neg() { return valueOf(-(long) v); }
// object equality, used for key comparison
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)o).v == v: false; }
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger) o).v == v: false; }
// equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(v); }
public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(v); }
public boolean raweq( double val ) { return v == val; }
public boolean raweq( int val ) { return v == val; }
public boolean raweq(LuaValue val) { return val.raweq(v); }
public boolean raweq(double val) { return v == val; }
public boolean raweq(int val) { return v == val; }
// arithmetic operators
public LuaValue add( LuaValue rhs ) { return rhs.add(v); }
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + (long)v); }
public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); }
public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); }
public LuaValue subFrom( int lhs ) { return LuaInteger.valueOf(lhs - (long)v); }
public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); }
public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue mul( int lhs ) { return LuaInteger.valueOf(lhs * (long)v); }
public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); }
public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); }
public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
public LuaValue add(LuaValue rhs) { return rhs.add(v); }
public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
public LuaValue add(int lhs) { return LuaInteger.valueOf(lhs+(long) v); }
public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue sub(int rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
public LuaValue subFrom(int lhs) { return LuaInteger.valueOf(lhs-(long) v); }
public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue mul(int lhs) { return LuaInteger.valueOf(lhs*(long) v); }
public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
// relational operators
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); }
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; }
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.gt_b(v)? TRUE: FALSE): super.lt(rhs); }
public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
public boolean lt_b(int rhs) { return v < rhs; }
public boolean lt_b(double rhs) { return v < rhs; }
public LuaValue lteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.gteq_b(v)? TRUE: FALSE): super.lteq(rhs);
}
public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
public boolean lteq_b(int rhs) { return v <= rhs; }
public boolean lteq_b(double rhs) { return v <= rhs; }
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.lt_b(v)? TRUE: FALSE): super.gt(rhs); }
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
public boolean gt_b(int rhs) { return v > rhs; }
public boolean gt_b(double rhs) { return v > rhs; }
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.lteq_b(v)? TRUE: FALSE): super.gteq(rhs);
}
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
public boolean gteq_b(int rhs) { return v >= rhs; }
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; }
public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
public int checkint() {
return v;
}
public long checklong() {
return v;
}
public double checkdouble() {
return v;
}
public String checkjstring() {
return String.valueOf(v);
}
public LuaString checkstring() {
return valueOf( String.valueOf(v) );
return valueOf(String.valueOf(v));
}
}

View File

@@ -22,27 +22,27 @@
package org.luaj.vm2;
/**
* Class to encapsulate behavior of the singleton instance {@code nil}
* Class to encapsulate behavior of the singleton instance {@code nil}
* <p>
* There will be one instance of this class, {@link LuaValue#NIL},
* per Java virtual machine.
* However, the {@link Varargs} instance {@link LuaValue#NONE}
* which is the empty list,
* is also considered treated as a nil value by default.
* There will be one instance of this class, {@link LuaValue#NIL}, per Java
* virtual machine. However, the {@link Varargs} instance {@link LuaValue#NONE}
* which is the empty list, is also considered treated as a nil value by
* default.
* <p>
* Although it is possible to test for nil using Java == operator,
* the recommended approach is to use the method {@link LuaValue#isnil()}
* instead. By using that any ambiguities between
* {@link LuaValue#NIL} and {@link LuaValue#NONE} are avoided.
* Although it is possible to test for nil using Java == operator, the
* recommended approach is to use the method {@link LuaValue#isnil()} instead.
* By using that any ambiguities between {@link LuaValue#NIL} and
* {@link LuaValue#NONE} are avoided.
*
* @see LuaValue
* @see LuaValue#NIL
*/
public class LuaNil extends LuaValue {
static final LuaNil _NIL = new LuaNil();
public static LuaValue s_metatable;
LuaNil() {}
public int type() {
@@ -50,59 +50,73 @@ public class LuaNil extends LuaValue {
}
public String toString() {
return "nil";
return "nil";
}
public String typename() {
return "nil";
}
public String tojstring() {
return "nil";
}
public LuaValue not() {
return LuaValue.TRUE;
public LuaValue not() {
return LuaValue.TRUE;
}
public boolean toboolean() {
return false;
public boolean toboolean() {
return false;
}
public boolean isnil() {
return true;
}
public LuaValue getmetatable() {
return s_metatable;
public LuaValue getmetatable() {
return s_metatable;
}
public boolean equals(Object o) {
return o instanceof LuaNil;
}
public LuaValue checknotnil() {
public LuaValue checknotnil() {
return argerror("value");
}
public boolean isvalidkey() {
return false;
}
// optional argument conversions - nil alwas falls badk to default value
public boolean optboolean(boolean defval) { return defval; }
public LuaClosure optclosure(LuaClosure defval) { return defval; }
public double optdouble(double defval) { return defval; }
public LuaFunction optfunction(LuaFunction defval) { return defval; }
public int optint(int defval) { return defval; }
public LuaInteger optinteger(LuaInteger defval) { return defval; }
public long optlong(long defval) { return defval; }
public LuaNumber optnumber(LuaNumber defval) { return defval; }
public LuaTable opttable(LuaTable defval) { return defval; }
public LuaThread optthread(LuaThread defval) { return defval; }
public String optjstring(String defval) { return defval; }
public LuaString optstring(LuaString defval) { return defval; }
public Object optuserdata(Object defval) { return defval; }
public Object optuserdata(Class c, Object defval) { return defval; }
public LuaValue optvalue(LuaValue defval) { return defval; }
public boolean optboolean(boolean defval) { return defval; }
public LuaClosure optclosure(LuaClosure defval) { return defval; }
public double optdouble(double defval) { return defval; }
public LuaFunction optfunction(LuaFunction defval) { return defval; }
public int optint(int defval) { return defval; }
public LuaInteger optinteger(LuaInteger defval) { return defval; }
public long optlong(long defval) { return defval; }
public LuaNumber optnumber(LuaNumber defval) { return defval; }
public LuaTable opttable(LuaTable defval) { return defval; }
public LuaThread optthread(LuaThread defval) { return defval; }
public String optjstring(String defval) { return defval; }
public LuaString optstring(LuaString defval) { return defval; }
public Object optuserdata(Object defval) { return defval; }
public Object optuserdata(Class c, Object defval) { return defval; }
public LuaValue optvalue(LuaValue defval) { return defval; }
}

View File

@@ -21,61 +21,64 @@
******************************************************************************/
package org.luaj.vm2;
/**
* Base class for representing numbers as lua values directly.
/**
* Base class for representing numbers as lua values directly.
* <p>
* The main subclasses are {@link LuaInteger} which holds values that fit in a java int,
* and {@link LuaDouble} which holds all other number values.
* The main subclasses are {@link LuaInteger} which holds values that fit in a
* java int, and {@link LuaDouble} which holds all other number values.
*
* @see LuaInteger
* @see LuaDouble
* @see LuaValue
*
*/
abstract
public class LuaNumber extends LuaValue {
abstract public class LuaNumber extends LuaValue {
/** Shared static metatable for all number values represented in lua. */
public static LuaValue s_metatable;
public int type() {
return TNUMBER;
}
public String typename() {
return "number";
}
public LuaNumber checknumber() {
return this;
return this;
}
public LuaNumber checknumber(String errmsg) {
return this;
return this;
}
public LuaNumber optnumber(LuaNumber defval) {
return this;
return this;
}
public LuaValue tonumber() {
return this;
}
public boolean isnumber() {
return true;
}
public boolean isstring() {
return true;
}
public LuaValue getmetatable() {
return s_metatable;
public LuaValue getmetatable() {
return s_metatable;
}
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,45 +21,43 @@
******************************************************************************/
package org.luaj.vm2;
import java.lang.ref.WeakReference;
/**
* Subclass of {@link LuaValue} that implements
* a lua coroutine thread using Java Threads.
/**
* Subclass of {@link LuaValue} that implements a lua coroutine thread using
* Java Threads.
* <p>
* A LuaThread is typically created in response to a scripted call to
* A LuaThread is typically created in response to a scripted call to
* {@code coroutine.create()}
* <p>
* The threads must be initialized with the globals, so that
* the global environment may be passed along according to rules of lua.
* This is done via the constructor arguments {@link #LuaThread(Globals)} or
* The threads must be initialized with the globals, so that the global
* environment may be passed along according to rules of lua. This is done via
* the constructor arguments {@link #LuaThread(Globals)} or
* {@link #LuaThread(Globals, LuaValue)}.
* <p>
* The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* see to it that this {@link Globals} are initialized properly.
* <p>
* The behavior of coroutine threads matches closely the behavior
* of C coroutine library. However, because of the use of Java threads
* to manage call state, it is possible to yield from anywhere in luaj.
* The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform} see to it that this {@link Globals}
* are initialized properly.
* <p>
* Each Java thread wakes up at regular intervals and checks a weak reference
* to determine if it can ever be resumed. If not, it throws
* {@link OrphanedThread} which is an {@link java.lang.Error}.
* Applications should not catch {@link OrphanedThread}, because it can break
* the thread safety of luaj. The value controlling the polling interval
* is {@link #thread_orphan_check_interval} and may be set by the user.
* <p>
* There are two main ways to abandon a coroutine. The first is to call
* {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)},
* and arrange to have it never resumed possibly by values passed to yield.
* The second is to throw {@link OrphanedThread}, which should put the thread
* in a dead state. In either case all references to the thread must be
* dropped, and the garbage collector must run for the thread to be
* garbage collected.
* The behavior of coroutine threads matches closely the behavior of C coroutine
* library. However, because of the use of Java threads to manage call state, it
* is possible to yield from anywhere in luaj.
* <p>
* Each Java thread wakes up at regular intervals and checks a weak reference to
* determine if it can ever be resumed. If not, it throws {@link OrphanedThread}
* which is an {@link java.lang.Error}. Applications should not catch
* {@link OrphanedThread}, because it can break the thread safety of luaj. The
* value controlling the polling interval is
* {@link #thread_orphan_check_interval} and may be set by the user.
* <p>
* There are two main ways to abandon a coroutine. The first is to call
* {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, and
* arrange to have it never resumed possibly by values passed to yield. The
* second is to throw {@link OrphanedThread}, which should put the thread in a
* dead state. In either case all references to the thread must be dropped, and
* the garbage collector must run for the thread to be garbage collected.
*
*
*
* @see LuaValue
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
@@ -70,107 +68,102 @@ public class LuaThread extends LuaValue {
/** Shared metatable for lua threads. */
public static LuaValue s_metatable;
/** The current number of coroutines. Should not be set. */
/** The current number of coroutines. Should not be set. */
public static int coroutine_count = 0;
/** Polling interval, in milliseconds, which each thread uses while waiting to
* return from a yielded state to check if the lua threads is no longer
* referenced and therefore should be garbage collected.
* A short polling interval for many threads will consume server resources.
* Orphaned threads cannot be detected and collected unless garbage
* collection is run. This can be changed by Java startup code if desired.
/**
* Polling interval, in milliseconds, which each thread uses while waiting
* to return from a yielded state to check if the lua threads is no longer
* referenced and therefore should be garbage collected. A short polling
* interval for many threads will consume server resources. Orphaned threads
* cannot be detected and collected unless garbage collection is run. This
* can be changed by Java startup code if desired.
*/
public static long thread_orphan_check_interval = 5000;
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2;
public static final int STATUS_NORMAL = 3;
public static final int STATUS_DEAD = 4;
public static final String[] STATUS_NAMES = {
"suspended",
"suspended",
"running",
"normal",
"dead",};
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2;
public static final int STATUS_NORMAL = 3;
public static final int STATUS_DEAD = 4;
public static final String[] STATUS_NAMES = { "suspended", "suspended", "running", "normal", "dead", };
public final State state;
public static final int MAX_CALLSTACK = 256;
public static final int MAX_CALLSTACK = 256;
/** Thread-local used by DebugLib to store debugging state.
* This is an opaque value that should not be modified by applications. */
/**
* Thread-local used by DebugLib to store debugging state. This is an opaque
* value that should not be modified by applications.
*/
public Object callstack;
public final Globals globals;
/** Error message handler for this thread, if any. */
/** Error message handler for this thread, if any. */
public LuaValue errorfunc;
/** Private constructor for main thread only */
public LuaThread(Globals globals) {
state = new State(globals, this, null);
state.status = STATUS_RUNNING;
this.globals = globals;
}
/**
/**
* Create a LuaThread around a function and environment
*
* @param func The function to execute
*/
public LuaThread(Globals globals, LuaValue func) {
public LuaThread(Globals globals, LuaValue func) {
LuaValue.assert_(func != null, "function cannot be null");
state = new State(globals, this, func);
this.globals = globals;
}
public int type() {
return LuaValue.TTHREAD;
}
public String typename() {
return "thread";
}
public boolean isthread() {
return true;
}
public LuaThread optthread(LuaThread defval) {
return this;
}
public LuaThread checkthread() {
return this;
}
public LuaValue getmetatable() {
return s_metatable;
}
public String getStatus() {
return STATUS_NAMES[state.status];
public LuaValue getmetatable() {
return s_metatable;
}
public boolean isMainThread() {
return this.state.function == null;
}
public String getStatus() { return STATUS_NAMES[state.status]; }
public boolean isMainThread() { return this.state.function == null; }
public Varargs resume(Varargs args) {
final LuaThread.State s = this.state;
if (s.status > LuaThread.STATUS_SUSPENDED)
return LuaValue.varargsOf(LuaValue.FALSE,
LuaValue.valueOf("cannot resume "+(s.status==LuaThread.STATUS_DEAD? "dead": "non-suspended")+" coroutine"));
return LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(
"cannot resume " + (s.status == LuaThread.STATUS_DEAD? "dead": "non-suspended") + " coroutine"));
return s.lua_resume(this, args);
}
public static class State implements Runnable {
private final Globals globals;
final WeakReference lua_thread;
final WeakReference lua_thread;
public final LuaValue function;
Varargs args = LuaValue.NONE;
Varargs result = LuaValue.NONE;
String error = null;
Varargs args = LuaValue.NONE;
Varargs result = LuaValue.NONE;
String error = null;
/** Hook function control state used by debug lib. */
public LuaValue hookfunc;
@@ -178,11 +171,11 @@ public class LuaThread extends LuaValue {
public boolean hookline;
public boolean hookcall;
public boolean hookrtrn;
public int hookcount;
public int hookcount;
public boolean inhook;
public int lastline;
public int bytecodes;
public int lastline;
public int bytecodes;
public int status = LuaThread.STATUS_INITIAL;
State(Globals globals, LuaThread lua_thread, LuaValue function) {
@@ -190,7 +183,7 @@ public class LuaThread extends LuaValue {
this.lua_thread = new WeakReference(lua_thread);
this.function = function;
}
public synchronized void run() {
try {
Varargs a = this.args;
@@ -210,8 +203,8 @@ public class LuaThread extends LuaValue {
globals.running = new_thread;
this.args = args;
if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-" + (++coroutine_count)).start();
} else {
this.notify();
}
@@ -219,9 +212,8 @@ public class LuaThread extends LuaValue {
previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING;
this.wait();
return (this.error != null?
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)):
LuaValue.varargsOf(LuaValue.TRUE, this.result));
return (this.error != null? LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error))
: LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) {
throw new OrphanedThread();
} finally {
@@ -230,7 +222,7 @@ public class LuaThread extends LuaValue {
this.error = null;
globals.running = previous_thread;
if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING;
globals.running.state.status = STATUS_RUNNING;
}
}
@@ -245,7 +237,7 @@ public class LuaThread extends LuaValue {
this.status = STATUS_DEAD;
throw new OrphanedThread();
}
} while (this.status == STATUS_SUSPENDED);
} while ( this.status == STATUS_SUSPENDED );
return this.args;
} catch (InterruptedException ie) {
this.status = STATUS_DEAD;
@@ -256,5 +248,5 @@ public class LuaThread extends LuaValue {
}
}
}
}

View File

@@ -21,29 +21,28 @@
******************************************************************************/
package org.luaj.vm2;
public class LuaUserdata extends LuaValue {
public Object m_instance;
public Object m_instance;
public LuaValue m_metatable;
public LuaUserdata(Object obj) {
m_instance = obj;
}
public LuaUserdata(Object obj, LuaValue metatable) {
m_instance = obj;
m_metatable = metatable;
}
public String tojstring() {
return String.valueOf(m_instance);
}
public int type() {
return LuaValue.TUSERDATA;
}
public String typename() {
return "userdata";
}
@@ -51,22 +50,27 @@ public class LuaUserdata extends LuaValue {
public int hashCode() {
return m_instance.hashCode();
}
public Object userdata() {
return m_instance;
}
public boolean isuserdata() { return true; }
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
public Object touserdata() { return m_instance; }
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
public Object optuserdata(Object defval) { return m_instance; }
public boolean isuserdata() { return true; }
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
public Object touserdata() { return m_instance; }
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
public Object optuserdata(Object defval) { return m_instance; }
public Object optuserdata(Class c, Object defval) {
if (!c.isAssignableFrom(m_instance.getClass()))
typerror(c.getName());
return m_instance;
}
public LuaValue getmetatable() {
return m_metatable;
}
@@ -79,48 +83,53 @@ public class LuaUserdata extends LuaValue {
public Object checkuserdata() {
return m_instance;
}
public Object checkuserdata(Class c) {
if ( c.isAssignableFrom(m_instance.getClass()) )
return m_instance;
public Object checkuserdata(Class c) {
if (c.isAssignableFrom(m_instance.getClass()))
return m_instance;
return typerror(c.getName());
}
public LuaValue get( LuaValue key ) {
return m_metatable!=null? gettable(this,key): NIL;
}
public void set( LuaValue key, LuaValue value ) {
if ( m_metatable==null || ! settable(this,key,value) )
error( "cannot set "+key+" for userdata" );
public LuaValue get(LuaValue key) {
return m_metatable != null? gettable(this, key): NIL;
}
public boolean equals( Object val ) {
if ( this == val )
public void set(LuaValue key, LuaValue value) {
if (m_metatable == null || !settable(this, key, value))
error("cannot set " + key + " for userdata");
}
public boolean equals(Object val) {
if (this == val)
return true;
if ( ! (val instanceof LuaUserdata) )
if (!(val instanceof LuaUserdata))
return false;
LuaUserdata u = (LuaUserdata) val;
return m_instance.equals(u.m_instance);
}
// equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return eq_b(val)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) {
if ( val.raweq(this) ) return true;
if ( m_metatable == null || !val.isuserdata() ) return false;
public LuaValue eq(LuaValue val) { return eq_b(val)? TRUE: FALSE; }
public boolean eq_b(LuaValue val) {
if (val.raweq(this))
return true;
if (m_metatable == null || !val.isuserdata())
return false;
LuaValue valmt = val.getmetatable();
return valmt!=null && LuaValue.eqmtcall(this, m_metatable, val, valmt);
return valmt != null && LuaValue.eqmtcall(this, m_metatable, val, valmt);
}
// equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(this); }
public boolean raweq( LuaUserdata val ) {
return this == val || (m_metatable == val.m_metatable && m_instance.equals(val.m_instance));
public boolean raweq(LuaValue val) { return val.raweq(this); }
public boolean raweq(LuaUserdata val) {
return this == val || (m_metatable == val.m_metatable && m_instance.equals(val.m_instance));
}
// __eq metatag processing
public boolean eqmt( LuaValue val ) {
return m_metatable!=null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable()): false;
public boolean eqmt(LuaValue val) {
return m_metatable != null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable())
: false;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -38,14 +38,14 @@ interface Metatable {
public LuaValue toLuaValue();
/** Return an instance of Slot appropriate for the given key and value. */
public Slot entry( LuaValue key, LuaValue value );
public Slot entry(LuaValue key, LuaValue value);
/** Returns the given value wrapped in a weak reference if appropriate. */
public LuaValue wrap( LuaValue value );
public LuaValue wrap(LuaValue value);
/**
* Returns the value at the given index in the array, or null if it is a weak reference that
* has been dropped.
* Returns the value at the given index in the array, or null if it is a
* weak reference that has been dropped.
*/
public LuaValue arrayget(LuaValue[] array, int index);
}

View File

@@ -29,7 +29,8 @@ package org.luaj.vm2;
* {@link LuaThread} being used as a coroutine that could not possibly be
* resumed again because there are no more references to the LuaThread with
* which it is associated. Rather than locking up resources forever, this error
* is thrown, and should fall through all the way to the thread's {@link Thread#run()} method.
* is thrown, and should fall through all the way to the thread's
* {@link Thread#run()} method.
* <p>
* Java code mixed with the luaj vm should not catch this error because it may
* occur when the coroutine is not running, so any processing done during error

View File

@@ -25,131 +25,96 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**
* Debug helper class to pretty-print lua bytecodes.
* Debug helper class to pretty-print lua bytecodes.
*
* @see Prototype
* @see LuaClosure
* @see LuaClosure
*/
public class Print extends Lua {
/** opcode names */
private static final String STRING_FOR_NULL = "null";
public static PrintStream ps = System.out;
public static PrintStream ps = System.out;
/** String names for each lua opcode value. */
public static final String[] OPNAMES = {
"MOVE",
"LOADK",
"LOADKX",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETTABUP",
"GETTABLE",
"SETTABUP",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"POW",
"UNM",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"EXTRAARG",
null,
};
public static final String[] OPNAMES = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETTABUP",
"GETTABLE", "SETTABUP", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "DIV", "MOD",
"POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL",
"RETURN", "FORLOOP", "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", "CLOSURE", "VARARG", "EXTRAARG", null, };
static void printString(PrintStream ps, final LuaString s) {
ps.print('"');
for (int i = 0, n = s.m_length; i < n; i++) {
int c = s.m_bytes[s.m_offset+i];
if ( c >= ' ' && c <= '~' && c != '\"' && c != '\\' )
if (c >= ' ' && c <= '~' && c != '\"' && c != '\\')
ps.print((char) c);
else {
switch (c) {
case '"':
ps.print("\\\"");
break;
case '\\':
ps.print("\\\\");
break;
case 0x0007: /* bell */
ps.print("\\a");
break;
case '\b': /* backspace */
ps.print("\\b");
break;
case '\f': /* form feed */
ps.print("\\f");
break;
case '\t': /* tab */
ps.print("\\t");
break;
case '\r': /* carriage return */
ps.print("\\r");
break;
case '\n': /* newline */
ps.print("\\n");
break;
case 0x000B: /* vertical tab */
ps.print("\\v");
break;
default:
ps.print('\\');
ps.print(Integer.toString(1000 + 0xff&c).substring(1));
break;
case '"':
ps.print("\\\"");
break;
case '\\':
ps.print("\\\\");
break;
case 0x0007: /* bell */
ps.print("\\a");
break;
case '\b': /* backspace */
ps.print("\\b");
break;
case '\f': /* form feed */
ps.print("\\f");
break;
case '\t': /* tab */
ps.print("\\t");
break;
case '\r': /* carriage return */
ps.print("\\r");
break;
case '\n': /* newline */
ps.print("\\n");
break;
case 0x000B: /* vertical tab */
ps.print("\\v");
break;
default:
ps.print('\\');
ps.print(Integer.toString(1000+0xff & c).substring(1));
break;
}
}
}
ps.print('"');
}
static void printValue( PrintStream ps, LuaValue v ) {
static void printValue(PrintStream ps, LuaValue v) {
if (v == null) {
ps.print("null");
return;
}
switch ( v.type() ) {
case LuaValue.TSTRING: printString( ps, (LuaString) v ); break;
default: ps.print( v.tojstring() );
switch (v.type()) {
case LuaValue.TSTRING:
printString(ps, (LuaString) v);
break;
default:
ps.print(v.tojstring());
}
}
static void printConstant(PrintStream ps, Prototype f, int i) {
printValue( ps, i < f.k.length ? f.k[i] : LuaValue.valueOf("UNKNOWN_CONST_" + i) );
printValue(ps, i < f.k.length? f.k[i]: LuaValue.valueOf("UNKNOWN_CONST_" + i));
}
static void printUpvalue(PrintStream ps, Upvaldesc u) {
ps.print( u.idx + " " );
printValue( ps, u.name );
ps.print(u.idx + " ");
printValue(ps, u.name);
}
/**
/**
* Print the code in a prototype
*
* @param f the {@link Prototype}
*/
public static void printCode(Prototype f) {
@@ -161,20 +126,22 @@ public class Print extends Lua {
}
}
/**
/**
* Print an opcode in a prototype
* @param f the {@link Prototype}
*
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
public static int printOpCode(Prototype f, int pc) {
return printOpCode(ps,f,pc);
return printOpCode(ps, f, pc);
}
/**
/**
* Print an opcode in a prototype
*
* @param ps the {@link PrintStream} to print to
* @param f the {@link Prototype}
* @param f the {@link Prototype}
* @param pc the program counter to look up and print
* @return pc same as above or changed
*/
@@ -188,33 +155,33 @@ public class Print extends Lua {
int bx = GETARG_Bx(i);
int sbx = GETARG_sBx(i);
int line = getline(f, pc);
ps.print(" " + (pc + 1) + " ");
ps.print(" " + (pc+1) + " ");
if (line > 0)
ps.print("[" + line + "] ");
else
ps.print("[-] ");
if (o >= OPNAMES.length - 1) {
if (o >= OPNAMES.length-1) {
ps.print("UNKNOWN_OP_" + o + " ");
} else {
ps.print(OPNAMES[o] + " ");
switch (getOpMode(o)) {
case iABC:
ps.print( a );
ps.print(a);
if (getBMode(o) != OpArgN)
ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b));
ps.print(" " + (ISK(b)? (-1-INDEXK(b)): b));
if (getCMode(o) != OpArgN)
ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c));
ps.print(" " + (ISK(c)? (-1-INDEXK(c)): c));
break;
case iABx:
if (getBMode(o) == OpArgK) {
ps.print(a + " " + (-1 - bx));
ps.print(a + " " + (-1-bx));
} else {
ps.print(a + " " + (bx));
}
break;
case iAsBx:
if (o == OP_JMP)
ps.print( sbx );
ps.print(sbx);
else
ps.print(a + " " + sbx);
break;
@@ -231,7 +198,7 @@ public class Print extends Lua {
printUpvalue(ps, f.upvalues[b]);
} else {
ps.print("UNKNOWN_UPVALUE_" + b);
}
}
break;
case OP_GETTABUP:
ps.print(" ; ");
@@ -296,7 +263,7 @@ public class Print extends Lua {
case OP_JMP:
case OP_FORLOOP:
case OP_FORPREP:
ps.print(" ; to " + (sbx + pc + 2));
ps.print(" ; to " + (sbx+pc+2));
break;
case OP_CLOSURE:
if (bx < f.p.length) {
@@ -312,8 +279,8 @@ public class Print extends Lua {
ps.print(" ; " + ((int) c));
break;
case OP_VARARG:
ps.print( " ; is_vararg="+ f.is_vararg );
break;
ps.print(" ; is_vararg=" + f.is_vararg);
break;
default:
break;
}
@@ -322,7 +289,7 @@ public class Print extends Lua {
}
private static int getline(Prototype f, int pc) {
return pc>0 && f.lineinfo!=null && pc<f.lineinfo.length? f.lineinfo[pc]: -1;
return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length? f.lineinfo[pc]: -1;
}
static void printHeader(Prototype f) {
@@ -333,23 +300,20 @@ public class Print extends Lua {
s = "(bstring)";
else
s = "(string)";
String a = (f.linedefined == 0) ? "main" : "function";
ps.print("\n%" + a + " <" + s + ":" + f.linedefined + ","
+ f.lastlinedefined + "> (" + f.code.length + " instructions, "
+ f.code.length * 4 + " bytes at " + id(f) + ")\n");
ps.print(f.numparams + " param, " + f.maxstacksize + " slot, "
+ f.upvalues.length + " upvalue, ");
ps.print(f.locvars.length + " local, " + f.k.length
+ " constant, " + f.p.length + " function\n");
String a = (f.linedefined == 0)? "main": "function";
ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," + f.lastlinedefined + "> (" + f.code.length
+ " instructions, " + f.code.length*4 + " bytes at " + id(f) + ")\n");
ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " + f.upvalues.length + " upvalue, ");
ps.print(f.locvars.length + " local, " + f.k.length + " constant, " + f.p.length + " function\n");
}
static void printConstants(Prototype f) {
int i, n = f.k.length;
ps.print("constants (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) {
ps.print(" " + (i + 1) + " ");
printValue( ps, f.k[i] );
ps.print( "\n");
ps.print(" " + (i+1) + " ");
printValue(ps, f.k[i]);
ps.print("\n");
}
}
@@ -357,7 +321,8 @@ public class Print extends Lua {
int i, n = f.locvars.length;
ps.print("locals (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) {
ps.println(" "+i+" "+f.locvars[i].varname+" "+(f.locvars[i].startpc+1)+" "+(f.locvars[i].endpc+1));
ps.println(
" " + i + " " + f.locvars[i].varname + " " + (f.locvars[i].startpc+1) + " " + (f.locvars[i].endpc+1));
}
}
@@ -369,18 +334,20 @@ public class Print extends Lua {
}
}
/** Pretty-prints contents of a Prototype.
/**
* Pretty-prints contents of a Prototype.
*
* @param prototype Prototype to print.
*/
public static void print(Prototype prototype) {
printFunction(prototype, true);
}
/** Pretty-prints contents of a Prototype in short or long form.
/**
* Pretty-prints contents of a Prototype in short or long form.
*
* @param prototype Prototype to print.
* @param full true to print all fields, false to print short form.
* @param full true to print all fields, false to print short form.
*/
public static void printFunction(Prototype prototype, boolean full) {
int i, n = prototype.p.length;
@@ -395,43 +362,45 @@ public class Print extends Lua {
printFunction(prototype.p[i], full);
}
private static void format( String s, int maxcols ) {
private static void format(String s, int maxcols) {
int n = s.length();
if ( n > maxcols )
ps.print( s.substring(0,maxcols) );
if (n > maxcols)
ps.print(s.substring(0, maxcols));
else {
ps.print( s );
for ( int i=maxcols-n; --i>=0; )
ps.print( ' ' );
ps.print(s);
for (int i = maxcols-n; --i >= 0;)
ps.print(' ');
}
}
private static String id(Prototype f) {
return "Proto";
}
private void _assert(boolean b) {
if ( !b )
if (!b)
throw new NullPointerException("_assert failed");
}
/**
* Print the state of a {@link LuaClosure} that is being executed
* @param cl the {@link LuaClosure}
* @param pc the program counter
* @param stack the stack of {@link LuaValue}
* @param top the top of the stack
*
* @param cl the {@link LuaClosure}
* @param pc the program counter
* @param stack the stack of {@link LuaValue}
* @param top the top of the stack
* @param varargs any {@link Varargs} value that may apply
*/
public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) {
// print opcode into buffer
PrintStream previous = ps;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ps = new PrintStream( baos );
printOpCode( cl.p, pc );
ps = new PrintStream(baos);
printOpCode(cl.p, pc);
ps.flush();
ps.close();
ps = previous;
format( baos.toString(), 50 );
format(baos.toString(), 50);
printStack(stack, top, varargs);
ps.println();
}
@@ -439,38 +408,38 @@ public class Print extends Lua {
public static void printStack(LuaValue[] stack, int top, Varargs varargs) {
// print stack
ps.print('[');
for ( int i=0; i<stack.length; i++ ) {
for (int i = 0; i < stack.length; i++) {
LuaValue v = stack[i];
if ( v == null )
if (v == null)
ps.print(STRING_FOR_NULL);
else switch ( v.type() ) {
case LuaValue.TSTRING:
LuaString s = v.checkstring();
ps.print( s.length() < 48?
s.tojstring():
s.substring(0, 32).tojstring()+"...+"+(s.length()-32)+"b");
break;
case LuaValue.TFUNCTION:
ps.print( v.tojstring() );
break;
case LuaValue.TUSERDATA:
Object o = v.touserdata();
if ( o != null ) {
String n = o.getClass().getName();
n = n.substring(n.lastIndexOf('.')+1);
ps.print( n+": "+Integer.toHexString(o.hashCode()) );
} else {
ps.print( v.toString() );
else
switch (v.type()) {
case LuaValue.TSTRING:
LuaString s = v.checkstring();
ps.print(s.length() < 48? s.tojstring()
: s.substring(0, 32).tojstring() + "...+" + (s.length()-32) + "b");
break;
case LuaValue.TFUNCTION:
ps.print(v.tojstring());
break;
case LuaValue.TUSERDATA:
Object o = v.touserdata();
if (o != null) {
String n = o.getClass().getName();
n = n.substring(n.lastIndexOf('.')+1);
ps.print(n + ": " + Integer.toHexString(o.hashCode()));
} else {
ps.print(v.toString());
}
break;
default:
ps.print(v.tojstring());
}
break;
default:
ps.print(v.tojstring());
}
if ( i+1 == top )
if (i+1 == top)
ps.print(']');
ps.print( " | " );
ps.print(" | ");
}
ps.print(varargs);
}
}
}

View File

@@ -22,59 +22,91 @@
package org.luaj.vm2;
/**
* Prototype representing compiled lua code.
* Prototype representing compiled lua code.
*
* <p>
* This is both a straight translation of the corresponding C type,
* and the main data structure for execution of compiled lua bytecode.
* This is both a straight translation of the corresponding C type, and the main
* data structure for execution of compiled lua bytecode.
*
* <p>
* Generally, the {@link Prototype} is not constructed directly is an intermediate result
* as lua code is loaded using {@link Globals#load(java.io.Reader, String)}:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
* } </pre>
* Generally, the {@link Prototype} is not constructed directly is an
* intermediate result as lua code is loaded using
* {@link Globals#load(java.io.Reader, String)}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* <p>
* To create a {@link Prototype} directly, a compiler such as
* To create a {@link Prototype} directly, a compiler such as
* {@link org.luaj.vm2.compiler.LuaC} may be used:
* <pre> {@code
* InputStream is = new ByteArrayInputStream("print('hello,world')".getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* }</pre>
*
* To simplify loading, the {@link Globals#compilePrototype(java.io.InputStream, String)} method may be used:
* <pre> {@code
* Prototype p = globals.compileProtoytpe(is, "script");
* }</pre>
* <pre>
* {
* &#64;code
* InputStream is = new ByteArrayInputStream("print('hello,world')".getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* }
* </pre>
*
* It may also be loaded from a {@link java.io.Reader} via {@link Globals#compilePrototype(java.io.Reader, String)}:
* <pre> {@code
* Prototype p = globals.compileProtoytpe(new StringReader(script), "script");
* }</pre>
* To simplify loading, the
* {@link Globals#compilePrototype(java.io.InputStream, String)} method may be
* used:
*
* To un-dump a binary file known to be a binary lua file that has been dumped to a string,
* the {@link Globals.Undumper} interface may be used:
* <pre> {@code
* FileInputStream lua_binary_file = new FileInputStream("foo.lc"); // Known to be compiled lua.
* Prototype p = globals.undumper.undump(lua_binary_file, "foo.lua");
* }</pre>
* <pre>
* {
* &#64;code
* Prototype p = globals.compileProtoytpe(is, "script");
* }
* </pre>
*
* To execute the code represented by the {@link Prototype} it must be supplied to
* the constructor of a {@link LuaClosure}:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }</pre>
* It may also be loaded from a {@link java.io.Reader} via
* {@link Globals#compilePrototype(java.io.Reader, String)}:
*
* To simplify the debugging of prototype values, the contents may be printed using {@link Print#print}:
* <pre> {@code
* <pre>
* {
* &#64;code
* Prototype p = globals.compileProtoytpe(new StringReader(script), "script");
* }
* </pre>
*
* To un-dump a binary file known to be a binary lua file that has been dumped
* to a string, the {@link Globals.Undumper} interface may be used:
*
* <pre>
* {
* &#64;code
* FileInputStream lua_binary_file = new FileInputStream("foo.lc"); // Known to be compiled lua.
* Prototype p = globals.undumper.undump(lua_binary_file, "foo.lua");
* }
* </pre>
*
* To execute the code represented by the {@link Prototype} it must be supplied
* to the constructor of a {@link LuaClosure}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
*
* To simplify the debugging of prototype values, the contents may be printed
* using {@link Print#print}:
*
* <pre>
* {@code
* Print.print(p);
* }</pre>
* }
* </pre>
* <p>
*
*
* @see LuaClosure
* @see Globals
* @see Globals#undumper
@@ -84,8 +116,8 @@ package org.luaj.vm2;
public class Prototype {
/* constants used by the function */
public LuaValue[] k;
public int[] code;
public LuaValue[] k;
public int[] code;
/* functions defined inside the function */
public Prototype[] p;
/* map from opcodes to source lines */
@@ -93,14 +125,14 @@ public class Prototype {
/* information about local variables */
public LocVars[] locvars;
/* upvalue information */
public Upvaldesc[] upvalues;
public LuaString source;
public int linedefined;
public int lastlinedefined;
public int numparams;
public int is_vararg;
public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {};
public Upvaldesc[] upvalues;
public LuaString source;
public int linedefined;
public int lastlinedefined;
public int numparams;
public int is_vararg;
public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {};
private static final Prototype[] NOSUBPROTOS = {};
public Prototype() {
@@ -112,35 +144,36 @@ public class Prototype {
p = NOSUBPROTOS;
upvalues = new Upvaldesc[n_upvalues];
}
public String toString() {
return source + ":" + linedefined+"-"+lastlinedefined;
return source + ":" + linedefined + "-" + lastlinedefined;
}
/** Get the name of a local variable.
/**
* Get the name of a local variable.
*
* @param number the local variable number to look up
* @param pc the program counter
* @param pc the program counter
* @return the name, or null if not found
*/
public LuaString getlocalname(int number, int pc) {
int i;
for (i = 0; i<locvars.length && locvars[i].startpc <= pc; i++) {
if (pc < locvars[i].endpc) { /* is variable active? */
number--;
if (number == 0)
return locvars[i].varname;
}
}
return null; /* not found */
int i;
for (i = 0; i < locvars.length && locvars[i].startpc <= pc; i++) {
if (pc < locvars[i].endpc) { /* is variable active? */
number--;
if (number == 0)
return locvars[i].varname;
}
}
return null; /* not found */
}
public String shortsource() {
String name = source.tojstring();
if ( name.startsWith("@") || name.startsWith("=") )
if (name.startsWith("@") || name.startsWith("="))
name = name.substring(1);
else if ( name.startsWith("\033") )
else if (name.startsWith("\033"))
name = "binary string";
return name;
return name;
}
}

View File

@@ -22,44 +22,41 @@
package org.luaj.vm2;
/**
* Subclass of {@link Varargs} that represents a lua tail call
* in a Java library function execution environment.
* Subclass of {@link Varargs} that represents a lua tail call in a Java library
* function execution environment.
* <p>
* Since Java doesn't have direct support for tail calls,
* any lua function whose {@link Prototype} contains the
* {@link Lua#OP_TAILCALL} bytecode needs a mechanism
* for tail calls when converting lua-bytecode to java-bytecode.
* Since Java doesn't have direct support for tail calls, any lua function whose
* {@link Prototype} contains the {@link Lua#OP_TAILCALL} bytecode needs a
* mechanism for tail calls when converting lua-bytecode to java-bytecode.
* <p>
* The tail call holds the next function and arguments,
* and the client a call to {@link #eval()} executes the function
* repeatedly until the tail calls are completed.
* The tail call holds the next function and arguments, and the client a call to
* {@link #eval()} executes the function repeatedly until the tail calls are
* completed.
* <p>
* Normally, users of luaj need not concern themselves with the
* details of this mechanism, as it is built into the core
* execution framework.
* @see Prototype
* Normally, users of luaj need not concern themselves with the details of this
* mechanism, as it is built into the core execution framework.
*
* @see Prototype
* @see org.luaj.vm2.luajc.LuaJC
*/
public class TailcallVarargs extends Varargs {
private LuaValue func;
private Varargs args;
private Varargs result;
private Varargs args;
private Varargs result;
public TailcallVarargs(LuaValue f, Varargs args) {
this.func = f;
this.args = args;
}
public TailcallVarargs(LuaValue object, LuaValue methodname, Varargs args) {
this.func = object.get(methodname);
this.args = LuaValue.varargsOf(object, args);
}
public boolean isTailcall() {
return true;
}
public boolean isTailcall() { return true; }
public Varargs eval() {
while ( result == null ) {
Varargs r = func.onInvoke(args);
@@ -67,28 +64,27 @@ public class TailcallVarargs extends Varargs {
TailcallVarargs t = (TailcallVarargs) r;
func = t.func;
args = t.args;
}
else {
result = r;
} else {
result = r;
func = null;
args = null;
}
}
return result;
}
public LuaValue arg( int i ) {
if ( result == null )
public LuaValue arg(int i) {
if (result == null)
eval();
return result.arg(i);
}
public LuaValue arg1() {
if (result == null)
eval();
return result.arg1();
}
public int narg() {
if (result == null)
eval();
@@ -100,4 +96,4 @@ public class TailcallVarargs extends Varargs {
eval();
return result.subargs(start);
}
}
}

View File

@@ -21,23 +21,25 @@
******************************************************************************/
package org.luaj.vm2;
/** Upvalue used with Closure formulation
/**
* Upvalue used with Closure formulation
* <p>
*
* @see LuaClosure
* @see Prototype
*/
public final class UpValue {
LuaValue[] array; // initially the stack, becomes a holder
int index;
int index;
/**
* Create an upvalue relative to a stack
* Create an upvalue relative to a stack
*
* @param stack the stack
* @param index the index on the stack for the upvalue
*/
public UpValue( LuaValue[] stack, int index) {
public UpValue(LuaValue[] stack, int index) {
this.array = stack;
this.index = index;
}
@@ -45,32 +47,31 @@ public final class UpValue {
public String toString() {
return index + "/" + array.length + " " + array[index];
}
/**
/**
* Convert this upvalue to a Java String
*
* @return the Java String for this upvalue.
* @see LuaValue#tojstring()
*/
public String tojstring() {
return array[index].tojstring();
}
/**
* Get the value of the upvalue
*
* @return the {@link LuaValue} for this upvalue
*/
public final LuaValue getValue() {
return array[index];
}
public final LuaValue getValue() { return array[index]; }
/**
* Set the value of the upvalue
*
* @param value the {@link LuaValue} to set it to
*/
public final void setValue( LuaValue value ) {
array[index] = value;
}
public final void setValue(LuaValue value) { array[index] = value; }
/**
* Close this upvalue so it is no longer on the stack
*/

View File

@@ -25,20 +25,20 @@ public class Upvaldesc {
/* upvalue name (for debug information) */
public LuaString name;
/* whether it is in stack */
public final boolean instack;
/* index of upvalue (in stack or in outer function's list) */
public final short idx;
public Upvaldesc(LuaString name, boolean instack, int idx) {
this.name = name;
this.instack = instack;
this.idx = (short) idx;
}
public String toString() {
return idx + (instack? " instack ": " closed ") + String.valueOf(name);
return idx+(instack? " instack ": " closed ")+String.valueOf(name);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,26 +27,26 @@ import org.luaj.vm2.LuaTable.Slot;
import org.luaj.vm2.LuaTable.StrongSlot;
/**
* Subclass of {@link LuaTable} that provides weak key and weak value semantics.
* <p>
* Normally these are not created directly, but indirectly when changing the mode
* of a {@link LuaTable} as lua script executes.
* Subclass of {@link LuaTable} that provides weak key and weak value semantics.
* <p>
* However, calling the constructors directly when weak tables are required from
* Java will reduce overhead.
* Normally these are not created directly, but indirectly when changing the
* mode of a {@link LuaTable} as lua script executes.
* <p>
* However, calling the constructors directly when weak tables are required from
* Java will reduce overhead.
*/
public class WeakTable implements Metatable {
private boolean weakkeys, weakvalues;
private boolean weakkeys, weakvalues;
private LuaValue backing;
public static LuaTable make(boolean weakkeys, boolean weakvalues) {
LuaString mode;
if ( weakkeys && weakvalues ) {
if (weakkeys && weakvalues) {
mode = LuaString.valueOf("kv");
} else if ( weakkeys ) {
} else if (weakkeys) {
mode = LuaString.valueOf("k");
} else if ( weakvalues ) {
} else if (weakvalues) {
mode = LuaString.valueOf("v");
} else {
return LuaTable.tableOf();
@@ -59,7 +59,8 @@ public class WeakTable implements Metatable {
/**
* Construct a table with weak keys, weak values, or both
* @param weakkeys true to let the table have weak keys
*
* @param weakkeys true to let the table have weak keys
* @param weakvalues true to let the table have weak values
*/
public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) {
@@ -82,26 +83,26 @@ public class WeakTable implements Metatable {
public Slot entry(LuaValue key, LuaValue value) {
value = value.strongvalue();
if ( value == null )
if (value == null)
return null;
if ( weakkeys && !( key.isnumber() || key.isstring() || key.isboolean() )) {
if ( weakvalues && !( value.isnumber() || value.isstring() || value.isboolean() )) {
return new WeakKeyAndValueSlot( key, value, null );
if (weakkeys && !(key.isnumber() || key.isstring() || key.isboolean())) {
if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakKeyAndValueSlot(key, value, null);
} else {
return new WeakKeySlot( key, value, null );
return new WeakKeySlot(key, value, null);
}
}
if ( weakvalues && ! (value.isnumber() || value.isstring() || value.isboolean() )) {
return new WeakValueSlot( key, value, null );
if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakValueSlot(key, value, null);
}
return LuaTable.defaultEntry( key, value );
return LuaTable.defaultEntry(key, value);
}
public static abstract class WeakSlot implements Slot {
protected Object key;
protected Object value;
protected Slot next;
protected Slot next;
protected WeakSlot(Object key, Object value, Slot next) {
this.key = key;
@@ -109,14 +110,14 @@ public class WeakTable implements Metatable {
this.next = next;
}
public abstract int keyindex( int hashMask );
public abstract int keyindex(int hashMask);
public abstract Slot set(LuaValue value);
public StrongSlot first() {
LuaValue key = strongkey();
LuaValue value = strongvalue();
if ( key != null && value != null ) {
if (key != null && value != null) {
return new LuaTable.NormalEntry(key, value);
} else {
this.key = null;
@@ -127,12 +128,12 @@ public class WeakTable implements Metatable {
public StrongSlot find(LuaValue key) {
StrongSlot first = first();
return ( first != null ) ? first.find( key ) : null;
return (first != null)? first.find(key): null;
}
public boolean keyeq(LuaValue key) {
StrongSlot first = first();
return ( first != null ) && first.keyeq( key );
return (first != null) && first.keyeq(key);
}
public Slot rest() {
@@ -146,46 +147,46 @@ public class WeakTable implements Metatable {
public Slot set(StrongSlot target, LuaValue value) {
LuaValue key = strongkey();
if ( key != null && target.find( key ) != null ) {
return set( value );
} else if ( key != null ) {
if (key != null && target.find(key) != null) {
return set(value);
} else if (key != null) {
// Our key is still good.
next = next.set( target, value );
next = next.set(target, value);
return this;
} else {
// our key was dropped, remove ourselves from the chain.
return next.set( target, value );
return next.set(target, value);
}
}
public Slot add( Slot entry ) {
next = ( next != null ) ? next.add( entry ) : entry;
if ( strongkey() != null && strongvalue() != null ) {
public Slot add(Slot entry) {
next = (next != null)? next.add(entry): entry;
if (strongkey() != null && strongvalue() != null) {
return this;
} else {
return next;
}
}
public Slot remove( StrongSlot target ) {
public Slot remove(StrongSlot target) {
LuaValue key = strongkey();
if ( key == null ) {
return next.remove( target );
} else if ( target.keyeq( key ) ) {
if (key == null) {
return next.remove(target);
} else if (target.keyeq(key)) {
this.value = null;
return this;
} else {
next = next.remove( target );
next = next.remove(target);
return this;
}
}
public Slot relink( Slot rest ) {
if ( strongkey() != null && strongvalue() != null ) {
if ( rest == null && this.next == null ) {
public Slot relink(Slot rest) {
if (strongkey() != null && strongvalue() != null) {
if (rest == null && this.next == null) {
return this;
} else {
return copy( rest );
return copy(rest);
}
} else {
return rest;
@@ -200,25 +201,25 @@ public class WeakTable implements Metatable {
return (LuaValue) value;
}
protected abstract WeakSlot copy( Slot next );
protected abstract WeakSlot copy(Slot next);
}
static class WeakKeySlot extends WeakSlot {
private final int keyhash;
protected WeakKeySlot( LuaValue key, LuaValue value, Slot next ) {
protected WeakKeySlot(LuaValue key, LuaValue value, Slot next) {
super(weaken(key), value, next);
keyhash = key.hashCode();
}
protected WeakKeySlot( WeakKeySlot copyFrom, Slot next ) {
super( copyFrom.key, copyFrom.value, next );
protected WeakKeySlot(WeakKeySlot copyFrom, Slot next) {
super(copyFrom.key, copyFrom.value, next);
this.keyhash = copyFrom.keyhash;
}
public int keyindex( int mask ) {
return LuaTable.hashmod( keyhash, mask );
public int keyindex(int mask) {
return LuaTable.hashmod(keyhash, mask);
}
public Slot set(LuaValue value) {
@@ -227,26 +228,26 @@ public class WeakTable implements Metatable {
}
public LuaValue strongkey() {
return strengthen( key );
return strengthen(key);
}
protected WeakSlot copy( Slot rest ) {
return new WeakKeySlot( this, rest );
protected WeakSlot copy(Slot rest) {
return new WeakKeySlot(this, rest);
}
}
static class WeakValueSlot extends WeakSlot {
protected WeakValueSlot( LuaValue key, LuaValue value, Slot next ) {
super( key, weaken(value), next);
protected WeakValueSlot(LuaValue key, LuaValue value, Slot next) {
super(key, weaken(value), next);
}
protected WeakValueSlot( WeakValueSlot copyFrom, Slot next ) {
super( copyFrom.key, copyFrom.value, next );
protected WeakValueSlot(WeakValueSlot copyFrom, Slot next) {
super(copyFrom.key, copyFrom.value, next);
}
public int keyindex( int mask ) {
return LuaTable.hashSlot( strongkey(), mask );
public int keyindex(int mask) {
return LuaTable.hashSlot(strongkey(), mask);
}
public Slot set(LuaValue value) {
@@ -255,11 +256,11 @@ public class WeakTable implements Metatable {
}
public LuaValue strongvalue() {
return strengthen( value );
return strengthen(value);
}
protected WeakSlot copy(Slot next) {
return new WeakValueSlot( this, next );
return new WeakValueSlot(this, next);
}
}
@@ -267,18 +268,18 @@ public class WeakTable implements Metatable {
private final int keyhash;
protected WeakKeyAndValueSlot( LuaValue key, LuaValue value, Slot next ) {
super( weaken(key), weaken(value), next );
protected WeakKeyAndValueSlot(LuaValue key, LuaValue value, Slot next) {
super(weaken(key), weaken(value), next);
keyhash = key.hashCode();
}
protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) {
super( copyFrom.key, copyFrom.value, next );
super(copyFrom.key, copyFrom.value, next);
keyhash = copyFrom.keyhash;
}
public int keyindex( int hashMask ) {
return LuaTable.hashmod( keyhash, hashMask );
public int keyindex(int hashMask) {
return LuaTable.hashmod(keyhash, hashMask);
}
public Slot set(LuaValue value) {
@@ -287,53 +288,58 @@ public class WeakTable implements Metatable {
}
public LuaValue strongkey() {
return strengthen( key );
return strengthen(key);
}
public LuaValue strongvalue() {
return strengthen( value );
return strengthen(value);
}
protected WeakSlot copy( Slot next ) {
return new WeakKeyAndValueSlot( this, next );
protected WeakSlot copy(Slot next) {
return new WeakKeyAndValueSlot(this, next);
}
}
/**
* Self-sent message to convert a value to its weak counterpart
*
* @param value value to convert
* @return {@link LuaValue} that is a strong or weak reference, depending on type of {@code value}
* @return {@link LuaValue} that is a strong or weak reference, depending on
* type of {@code value}
*/
protected static LuaValue weaken( LuaValue value ) {
switch ( value.type() ) {
case LuaValue.TFUNCTION:
case LuaValue.TTHREAD:
case LuaValue.TTABLE:
return new WeakValue(value);
case LuaValue.TUSERDATA:
return new WeakUserdata(value);
default:
return value;
protected static LuaValue weaken(LuaValue value) {
switch (value.type()) {
case LuaValue.TFUNCTION:
case LuaValue.TTHREAD:
case LuaValue.TTABLE:
return new WeakValue(value);
case LuaValue.TUSERDATA:
return new WeakUserdata(value);
default:
return value;
}
}
/**
* Unwrap a LuaValue from a WeakReference and/or WeakUserdata.
*
* @param ref reference to convert
* @return LuaValue or null
* @see #weaken(LuaValue)
*/
protected static LuaValue strengthen(Object ref) {
if ( ref instanceof WeakReference ) {
if (ref instanceof WeakReference) {
ref = ((WeakReference) ref).get();
}
if ( ref instanceof WeakValue ) {
if (ref instanceof WeakValue) {
return ((WeakValue) ref).strongvalue();
}
return (LuaValue) ref;
}
/** Internal class to implement weak values.
/**
* Internal class to implement weak values.
*
* @see WeakTable
*/
static class WeakValue extends LuaValue {
@@ -344,36 +350,38 @@ public class WeakTable implements Metatable {
}
public int type() {
illegal("type","weak value");
illegal("type", "weak value");
return 0;
}
public String typename() {
illegal("typename","weak value");
illegal("typename", "weak value");
return null;
}
public String toString() {
return "weak<"+ref.get()+">";
return "weak<" + ref.get() + ">";
}
public LuaValue strongvalue() {
Object o = ref.get();
return (LuaValue)o;
return (LuaValue) o;
}
public boolean raweq(LuaValue rhs) {
Object o = ref.get();
return o!=null && rhs.raweq((LuaValue)o);
return o != null && rhs.raweq((LuaValue) o);
}
}
/** Internal class to implement weak userdata values.
/**
* Internal class to implement weak userdata values.
*
* @see WeakTable
*/
static final class WeakUserdata extends WeakValue {
private final WeakReference ob;
private final LuaValue mt;
private final LuaValue mt;
private WeakUserdata(LuaValue value) {
super(value);
@@ -383,11 +391,11 @@ public class WeakTable implements Metatable {
public LuaValue strongvalue() {
Object u = ref.get();
if ( u != null )
if (u != null)
return (LuaValue) u;
Object o = ob.get();
if ( o != null ) {
LuaValue ud = LuaValue.userdataOf(o,mt);
if (o != null) {
LuaValue ud = LuaValue.userdataOf(o, mt);
ref = new WeakReference(ud);
return ud;
} else {
@@ -397,7 +405,7 @@ public class WeakTable implements Metatable {
}
public LuaValue wrap(LuaValue value) {
return weakvalues ? weaken( value ) : value;
return weakvalues? weaken(value): value;
}
public LuaValue arrayget(LuaValue[] array, int index) {

View File

@@ -36,153 +36,141 @@ import org.luaj.vm2.Upvaldesc;
* @see FuncState
*/
public class Constants extends Lua {
/** Maximum stack size of a luaj vm interpreter instance. */
public static final int MAXSTACK = 250;
static final int LUAI_MAXUPVAL = 0xff;
static final int LUAI_MAXVARS = 200;
static final int NO_REG = MAXARG_A;
static final int LUAI_MAXVARS = 200;
static final int NO_REG = MAXARG_A;
/* OpMode - basic instruction format */
static final int
iABC = 0,
iABx = 1,
iAsBx = 2;
static final int iABC = 0, iABx = 1, iAsBx = 2;
/* OpArgMask */
static final int
OpArgN = 0, /* argument is not used */
OpArgU = 1, /* argument is used */
OpArgR = 2, /* argument is a register or a jump offset */
OpArgK = 3; /* argument is a constant or register/constant */
static final int OpArgN = 0, /* argument is not used */
OpArgU = 1, /* argument is used */
OpArgR = 2, /* argument is a register or a jump offset */
OpArgK = 3; /* argument is a constant or register/constant */
protected static void _assert(boolean b) {
if (!b)
throw new LuaError("compiler assert failed");
}
static void SET_OPCODE(InstructionPtr i,int o) {
i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) );
static void SET_OPCODE(InstructionPtr i, int o) {
i.set((i.get() & (MASK_NOT_OP)) | ((o<<POS_OP) & MASK_OP));
}
static void SETARG_A(int[] code, int index, int u) {
code[index] = (code[index] & (MASK_NOT_A)) | ((u << POS_A) & MASK_A);
code[index] = (code[index] & (MASK_NOT_A)) | ((u<<POS_A) & MASK_A);
}
static void SETARG_A(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) );
static void SETARG_A(InstructionPtr i, int u) {
i.set((i.get() & (MASK_NOT_A)) | ((u<<POS_A) & MASK_A));
}
static void SETARG_B(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) );
static void SETARG_B(InstructionPtr i, int u) {
i.set((i.get() & (MASK_NOT_B)) | ((u<<POS_B) & MASK_B));
}
static void SETARG_C(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) );
static void SETARG_C(InstructionPtr i, int u) {
i.set((i.get() & (MASK_NOT_C)) | ((u<<POS_C) & MASK_C));
}
static void SETARG_Bx(InstructionPtr i,int u) {
i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) );
static void SETARG_Bx(InstructionPtr i, int u) {
i.set((i.get() & (MASK_NOT_Bx)) | ((u<<POS_Bx) & MASK_Bx));
}
static void SETARG_sBx(InstructionPtr i,int u) {
SETARG_Bx( i, u + MAXARG_sBx );
static void SETARG_sBx(InstructionPtr i, int u) {
SETARG_Bx(i, u+MAXARG_sBx);
}
static int CREATE_ABC(int o, int a, int b, int c) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_A) & MASK_A) |
((b << POS_B) & MASK_B) |
((c << POS_C) & MASK_C) ;
return ((o<<POS_OP) & MASK_OP) | ((a<<POS_A) & MASK_A) | ((b<<POS_B) & MASK_B) | ((c<<POS_C) & MASK_C);
}
static int CREATE_ABx(int o, int a, int bc) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_A) & MASK_A) |
((bc << POS_Bx) & MASK_Bx) ;
}
return ((o<<POS_OP) & MASK_OP) | ((a<<POS_A) & MASK_A) | ((bc<<POS_Bx) & MASK_Bx);
}
static int CREATE_Ax(int o, int a) {
return ((o << POS_OP) & MASK_OP) |
((a << POS_Ax) & MASK_Ax) ;
}
return ((o<<POS_OP) & MASK_OP) | ((a<<POS_Ax) & MASK_Ax);
}
// vector reallocation
static LuaValue[] realloc(LuaValue[] v, int n) {
LuaValue[] a = new LuaValue[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static Prototype[] realloc(Prototype[] v, int n) {
Prototype[] a = new Prototype[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LuaString[] realloc(LuaString[] v, int n) {
LuaString[] a = new LuaString[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LocVars[] realloc(LocVars[] v, int n) {
LocVars[] a = new LocVars[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static Upvaldesc[] realloc(Upvaldesc[] v, int n) {
Upvaldesc[] a = new Upvaldesc[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) {
LexState.Vardesc[] a = new LexState.Vardesc[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) {
return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v;
return v == null? new LexState.Labeldesc[2]: v.length < min_n? realloc(v, v.length*2): v;
}
static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) {
LexState.Labeldesc[] a = new LexState.Labeldesc[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static int[] realloc(int[] v, int n) {
int[] a = new int[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static byte[] realloc(byte[] v, int n) {
byte[] a = new byte[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}
static char[] realloc(char[] v, int n) {
char[] a = new char[n];
if ( v != null )
System.arraycopy(v, 0, a, 0, Math.min(v.length,n));
if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a;
}

View File

@@ -32,35 +32,47 @@ import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
/** Class to dump a {@link Prototype} into an output stream, as part of compiling.
/**
* Class to dump a {@link Prototype} into an output stream, as part of
* compiling.
* <p>
* Generally, this class is not used directly, but rather indirectly via a command
* line interface tool such as {@link luac}.
* Generally, this class is not used directly, but rather indirectly via a
* command line interface tool such as {@link luac}.
* <p>
* A lua binary file is created via {@link DumpState#dump}:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* } </pre>
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
*
* The {@link LoadState} may be used directly to undump these bytes:
* <pre> {@code
*
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* } </pre>
* }
* </pre>
*
*
* More commonly, the {@link Globals#undumper} may be used to undump them:
* <pre> {@code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* } </pre>
*
* <pre>
* {
* &#64;code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
*
* @see luac
* @see LoadState
@@ -71,33 +83,39 @@ public class DumpState {
/** set true to allow integer compilation */
public static boolean ALLOW_INTEGER_CASTING = false;
/** format corresponding to non-number-patched lua, all numbers are floats or doubles */
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
/** default number format */
public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
// header fields
private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4;
private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4;
private static final int SIZEOF_INSTRUCTION = 4;
DataOutputStream writer;
boolean strip;
int status;
boolean strip;
int status;
public DumpState(OutputStream w, boolean strip) {
this.writer = new DataOutputStream( w );
this.writer = new DataOutputStream(w);
this.strip = strip;
this.status = 0;
}
@@ -107,58 +125,58 @@ public class DumpState {
}
void dumpChar(int b) throws IOException {
writer.write( b );
writer.write(b);
}
void dumpInt(int x) throws IOException {
if ( IS_LITTLE_ENDIAN ) {
writer.writeByte(x&0xff);
writer.writeByte((x>>8)&0xff);
writer.writeByte((x>>16)&0xff);
writer.writeByte((x>>24)&0xff);
if (IS_LITTLE_ENDIAN) {
writer.writeByte(x & 0xff);
writer.writeByte((x>>8) & 0xff);
writer.writeByte((x>>16) & 0xff);
writer.writeByte((x>>24) & 0xff);
} else {
writer.writeInt(x);
}
}
void dumpString(LuaString s) throws IOException {
final int len = s.len().toint();
dumpInt( len+1 );
s.write( writer, 0, len );
writer.write( 0 );
dumpInt(len+1);
s.write(writer, 0, len);
writer.write(0);
}
void dumpDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d);
if ( IS_LITTLE_ENDIAN ) {
dumpInt( (int) l );
dumpInt( (int) (l>>32) );
if (IS_LITTLE_ENDIAN) {
dumpInt((int) l);
dumpInt((int) (l>>32));
} else {
writer.writeLong(l);
}
}
void dumpCode( final Prototype f ) throws IOException {
void dumpCode(final Prototype f) throws IOException {
final int[] code = f.code;
int n = code.length;
dumpInt( n );
for ( int i=0; i<n; i++ )
dumpInt( code[i] );
dumpInt(n);
for (int i = 0; i < n; i++)
dumpInt(code[i]);
}
void dumpConstants(final Prototype f) throws IOException {
final LuaValue[] k = f.k;
int i, n = k.length;
dumpInt(n);
for (i = 0; i < n; i++) {
final LuaValue o = k[i];
switch ( o.type() ) {
switch (o.type()) {
case LuaValue.TNIL:
writer.write(LuaValue.TNIL);
break;
case LuaValue.TBOOLEAN:
writer.write(LuaValue.TBOOLEAN);
dumpChar(o.toboolean() ? 1 : 0);
dumpChar(o.toboolean()? 1: 0);
break;
case LuaValue.TNUMBER:
switch (NUMBER_FORMAT) {
@@ -167,13 +185,13 @@ public class DumpState {
dumpDouble(o.todouble());
break;
case NUMBER_FORMAT_INTS_ONLY:
if ( ! ALLOW_INTEGER_CASTING && ! o.isint() )
throw new java.lang.IllegalArgumentException("not an integer: "+o);
if (!ALLOW_INTEGER_CASTING && !o.isint())
throw new java.lang.IllegalArgumentException("not an integer: " + o);
writer.write(LuaValue.TNUMBER);
dumpInt(o.toint());
break;
case NUMBER_FORMAT_NUM_PATCH_INT32:
if ( o.isint() ) {
if (o.isint()) {
writer.write(LuaValue.TINT);
dumpInt(o.toint());
} else {
@@ -182,12 +200,12 @@ public class DumpState {
}
break;
default:
throw new IllegalArgumentException("number format not supported: "+NUMBER_FORMAT);
throw new IllegalArgumentException("number format not supported: " + NUMBER_FORMAT);
}
break;
case LuaValue.TSTRING:
writer.write(LuaValue.TSTRING);
dumpString((LuaString)o);
dumpString((LuaString) o);
break;
default:
throw new IllegalArgumentException("bad type for " + o);
@@ -203,7 +221,7 @@ public class DumpState {
int n = f.upvalues.length;
dumpInt(n);
for (int i = 0; i < n; i++) {
writer.writeByte(f.upvalues[i].instack ? 1 : 0);
writer.writeByte(f.upvalues[i].instack? 1: 0);
writer.writeByte(f.upvalues[i].idx);
}
}
@@ -214,11 +232,11 @@ public class DumpState {
dumpInt(0);
else
dumpString(f.source);
n = strip ? 0 : f.lineinfo.length;
n = strip? 0: f.lineinfo.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpInt(f.lineinfo[i]);
n = strip ? 0 : f.locvars.length;
n = strip? 0: f.locvars.length;
dumpInt(n);
for (i = 0; i < n; i++) {
LocVars lvi = f.locvars[i];
@@ -226,12 +244,12 @@ public class DumpState {
dumpInt(lvi.startpc);
dumpInt(lvi.endpc);
}
n = strip ? 0 : f.upvalues.length;
n = strip? 0: f.upvalues.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpString(f.upvalues[i].name);
}
void dumpFunction(final Prototype f) throws IOException {
dumpInt(f.linedefined);
dumpInt(f.lastlinedefined);
@@ -245,23 +263,23 @@ public class DumpState {
}
void dumpHeader() throws IOException {
writer.write( LoadState.LUA_SIGNATURE );
writer.write( LoadState.LUAC_VERSION );
writer.write( LoadState.LUAC_FORMAT );
writer.write( IS_LITTLE_ENDIAN? 1: 0 );
writer.write( SIZEOF_INT );
writer.write( SIZEOF_SIZET );
writer.write( SIZEOF_INSTRUCTION );
writer.write( SIZEOF_LUA_NUMBER );
writer.write( NUMBER_FORMAT );
writer.write( LoadState.LUAC_TAIL );
writer.write(LoadState.LUA_SIGNATURE);
writer.write(LoadState.LUAC_VERSION);
writer.write(LoadState.LUAC_FORMAT);
writer.write(IS_LITTLE_ENDIAN? 1: 0);
writer.write(SIZEOF_INT);
writer.write(SIZEOF_SIZET);
writer.write(SIZEOF_INSTRUCTION);
writer.write(SIZEOF_LUA_NUMBER);
writer.write(NUMBER_FORMAT);
writer.write(LoadState.LUAC_TAIL);
}
/*
** dump Lua function as precompiled chunk
*/
public static int dump( Prototype f, OutputStream w, boolean strip ) throws IOException {
DumpState D = new DumpState(w,strip);
public static int dump(Prototype f, OutputStream w, boolean strip) throws IOException {
DumpState D = new DumpState(w, strip);
D.dumpHeader();
D.dumpFunction(f);
return D.status;
@@ -269,28 +287,32 @@ public class DumpState {
/**
*
* @param f the function to dump
* @param w the output stream to dump to
* @param stripDebug true to strip debugging info, false otherwise
* @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES, NUMBER_FORMAT_INTS_ONLY, NUMBER_FORMAT_NUM_PATCH_INT32
* @param littleendian true to use little endian for numbers, false for big endian
* @param f the function to dump
* @param w the output stream to dump to
* @param stripDebug true to strip debugging info, false otherwise
* @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES,
* NUMBER_FORMAT_INTS_ONLY,
* NUMBER_FORMAT_NUM_PATCH_INT32
* @param littleendian true to use little endian for numbers, false for big
* endian
* @return 0 if dump succeeds
* @throws IOException
* @throws IllegalArgumentException if the number format it not supported
*/
public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian) throws IOException {
switch ( numberFormat ) {
public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian)
throws IOException {
switch (numberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new IllegalArgumentException("number format not supported: "+numberFormat);
throw new IllegalArgumentException("number format not supported: " + numberFormat);
}
DumpState D = new DumpState(w,stripDebug);
DumpState D = new DumpState(w, stripDebug);
D.IS_LITTLE_ENDIAN = littleendian;
D.NUMBER_FORMAT = numberFormat;
D.SIZEOF_LUA_NUMBER = (numberFormat==NUMBER_FORMAT_INTS_ONLY? 4: 8);
D.SIZEOF_LUA_NUMBER = (numberFormat == NUMBER_FORMAT_INTS_ONLY? 4: 8);
D.dumpHeader();
D.dumpFunction(f);
return D.status;

View File

@@ -34,44 +34,42 @@ import org.luaj.vm2.Upvaldesc;
import org.luaj.vm2.compiler.LexState.ConsControl;
import org.luaj.vm2.compiler.LexState.expdesc;
public class FuncState extends Constants {
static class BlockCnt {
BlockCnt previous; /* chain */
short firstlabel; /* index of first label in this block */
short firstgoto; /* index of first pending goto in this block */
short nactvar; /* # active locals outside the breakable structure */
boolean upval; /* true if some variable in the block is an upvalue */
boolean isloop; /* true if `block' is a loop */
BlockCnt previous; /* chain */
short firstlabel; /* index of first label in this block */
short firstgoto; /* index of first pending goto in this block */
short nactvar; /* # active locals outside the breakable structure */
boolean upval; /* true if some variable in the block is an upvalue */
boolean isloop; /* true if `block' is a loop */
};
Prototype f; /* current function header */
Hashtable h; /* table to find (and reuse) elements in `k' */
FuncState prev; /* enclosing function */
LexState ls; /* lexical state */
BlockCnt bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
IntPtr jpc; /* list of pending jumps to `pc' */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
int firstlocal; /* index of first local var (in Dyndata array) */
short nlocvars; /* number of elements in `locvars' */
short nactvar; /* number of active local variables */
short nups; /* number of upvalues */
short freereg; /* first free register */
Prototype f; /* current function header */
Hashtable h; /* table to find (and reuse) elements in `k' */
FuncState prev; /* enclosing function */
LexState ls; /* lexical state */
BlockCnt bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
IntPtr jpc; /* list of pending jumps to `pc' */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
int firstlocal; /* index of first local var (in Dyndata array) */
short nlocvars; /* number of elements in `locvars' */
short nactvar; /* number of active local variables */
short nups; /* number of upvalues */
short freereg; /* first free register */
FuncState() {
}
// =============================================================
// from lcode.h
// =============================================================
InstructionPtr getcodePtr(expdesc e) {
return new InstructionPtr( f.code, e.u.info );
return new InstructionPtr(f.code, e.u.info);
}
int getcode(expdesc e) {
@@ -79,93 +77,88 @@ public class FuncState extends Constants {
}
int codeAsBx(int o, int A, int sBx) {
return codeABx(o,A,sBx+MAXARG_sBx);
return codeABx(o, A, sBx+MAXARG_sBx);
}
void setmultret(expdesc e) {
setreturns(e, LUA_MULTRET);
}
// =============================================================
// from lparser.c
// =============================================================
/* check for repeated labels on the same block */
void checkrepeated (LexState.Labeldesc[] ll, int ll_n, LuaString label) {
void checkrepeated(LexState.Labeldesc[] ll, int ll_n, LuaString label) {
int i;
for (i = bl.firstlabel; i < ll_n; i++) {
if (label.eq_b(ll[i].name)) {
String msg = ls.L.pushfstring(
"label '" + label + " already defined on line " + ll[i].line);
String msg = ls.L.pushfstring("label '" + label + " already defined on line " + ll[i].line);
ls.semerror(msg);
}
}
}
void checklimit(int v, int l, String msg) {
if ( v > l )
errorlimit( l, msg );
if (v > l)
errorlimit(l, msg);
}
void errorlimit (int limit, String what) {
void errorlimit(int limit, String what) {
// TODO: report message logic.
String msg = (f.linedefined == 0) ?
ls.L.pushfstring("main function has more than "+limit+" "+what) :
ls.L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what);
ls.lexerror(msg, 0);
String msg = (f.linedefined == 0)? ls.L.pushfstring("main function has more than " + limit + " " + what)
: ls.L.pushfstring("function at line " + f.linedefined + " has more than " + limit + " " + what);
ls.lexerror(msg, 0);
}
LocVars getlocvar(int i) {
int idx = ls.dyd.actvar[firstlocal + i].idx;
int idx = ls.dyd.actvar[firstlocal+i].idx;
_assert(idx < nlocvars);
return f.locvars[idx];
}
void removevars (int tolevel) {
ls.dyd.n_actvar -= (nactvar - tolevel);
while (nactvar > tolevel)
getlocvar(--nactvar).endpc = pc;
void removevars(int tolevel) {
ls.dyd.n_actvar -= (nactvar-tolevel);
while ( nactvar > tolevel )
getlocvar(--nactvar).endpc = pc;
}
int searchupvalue (LuaString name) {
int i;
Upvaldesc[] up = f.upvalues;
for (i = 0; i < nups; i++)
if (up[i].name.eq_b(name))
return i;
return -1; /* not found */
int searchupvalue(LuaString name) {
int i;
Upvaldesc[] up = f.upvalues;
for (i = 0; i < nups; i++)
if (up[i].name.eq_b(name))
return i;
return -1; /* not found */
}
int newupvalue (LuaString name, expdesc v) {
checklimit(nups + 1, LUAI_MAXUPVAL, "upvalues");
if (f.upvalues == null || nups + 1 > f.upvalues.length)
f.upvalues = realloc( f.upvalues, nups > 0 ? nups*2 : 1 );
int newupvalue(LuaString name, expdesc v) {
checklimit(nups+1, LUAI_MAXUPVAL, "upvalues");
if (f.upvalues == null || nups+1 > f.upvalues.length)
f.upvalues = realloc(f.upvalues, nups > 0? nups*2: 1);
f.upvalues[nups] = new Upvaldesc(name, v.k == LexState.VLOCAL, v.u.info);
return nups++;
return nups++;
}
int searchvar(LuaString n) {
int i;
for (i = nactvar - 1; i >= 0; i--) {
for (i = nactvar-1; i >= 0; i--) {
if (n.eq_b(getlocvar(i).varname))
return i;
}
return -1; /* not found */
}
void markupval(int level) {
BlockCnt bl = this.bl;
while (bl.nactvar > level)
while ( bl.nactvar > level )
bl = bl.previous;
bl.upval = true;
}
static int singlevaraux(FuncState fs, LuaString n, expdesc var, int base) {
if (fs == null) /* no more levels? */
return LexState.VVOID; /* default is global */
if (fs == null) /* no more levels? */
return LexState.VVOID; /* default is global */
int v = fs.searchvar(n); /* look up at current level */
if (v >= 0) {
var.init(LexState.VLOCAL, v);
@@ -173,15 +166,15 @@ public class FuncState extends Constants {
fs.markupval(v); /* local will be used as an upval */
return LexState.VLOCAL;
} else { /* not found at current level; try upvalues */
int idx = fs.searchupvalue(n); /* try existing upvalues */
if (idx < 0) { /* not found? */
if (singlevaraux(fs.prev, n, var, 0) == LexState.VVOID) /* try upper levels */
return LexState.VVOID; /* not found; is a global */
/* else was LOCAL or UPVAL */
idx = fs.newupvalue(n, var); /* will be a new upvalue */
}
var.init(LexState.VUPVAL, idx);
return LexState.VUPVAL;
int idx = fs.searchupvalue(n); /* try existing upvalues */
if (idx < 0) { /* not found? */
if (singlevaraux(fs.prev, n, var, 0) == LexState.VVOID) /* try upper levels */
return LexState.VVOID; /* not found; is a global */
/* else was LOCAL or UPVAL */
idx = fs.newupvalue(n, var); /* will be a new upvalue */
}
var.init(LexState.VUPVAL, idx);
return LexState.VUPVAL;
}
}
@@ -196,7 +189,7 @@ public class FuncState extends Constants {
final LexState.Labeldesc[] gl = ls.dyd.gt;
/* correct pending gotos to current block and try to close it
with visible labels */
while (i < ls.dyd.n_gt) {
while ( i < ls.dyd.n_gt ) {
LexState.Labeldesc gt = gl[i];
if (gt.nactvar > bl.nactvar) {
if (bl.upval)
@@ -207,37 +200,37 @@ public class FuncState extends Constants {
i++; /* move to next one */
}
}
void enterblock (BlockCnt bl, boolean isloop) {
bl.isloop = isloop;
bl.nactvar = nactvar;
bl.firstlabel = (short) ls.dyd.n_label;
bl.firstgoto = (short) ls.dyd.n_gt;
bl.upval = false;
bl.previous = this.bl;
this.bl = bl;
_assert(this.freereg == this.nactvar);
void enterblock(BlockCnt bl, boolean isloop) {
bl.isloop = isloop;
bl.nactvar = nactvar;
bl.firstlabel = (short) ls.dyd.n_label;
bl.firstgoto = (short) ls.dyd.n_gt;
bl.upval = false;
bl.previous = this.bl;
this.bl = bl;
_assert(this.freereg == this.nactvar);
}
void leaveblock() {
BlockCnt bl = this.bl;
if (bl.previous != null && bl.upval) {
/* create a 'jump to here' to close upvalues */
int j = this.jump();
this.patchclose(j, bl.nactvar);
this.patchtohere(j);
/* create a 'jump to here' to close upvalues */
int j = this.jump();
this.patchclose(j, bl.nactvar);
this.patchtohere(j);
}
if (bl.isloop)
ls.breaklabel(); /* close pending breaks */
ls.breaklabel(); /* close pending breaks */
this.bl = bl.previous;
this.removevars(bl.nactvar);
_assert(bl.nactvar == this.nactvar);
this.freereg = this.nactvar; /* free registers */
ls.dyd.n_label = bl.firstlabel; /* remove local labels */
if (bl.previous != null) /* inner block? */
this.movegotosout(bl); /* update pending gotos to outer block */
else if (bl.firstgoto < ls.dyd.n_gt) /* pending gotos in outer block? */
ls.undefgoto(ls.dyd.gt[bl.firstgoto]); /* error */
this.freereg = this.nactvar; /* free registers */
ls.dyd.n_label = bl.firstlabel; /* remove local labels */
if (bl.previous != null) /* inner block? */
this.movegotosout(bl); /* update pending gotos to outer block */
else if (bl.firstgoto < ls.dyd.n_gt) /* pending gotos in outer block? */
ls.undefgoto(ls.dyd.gt[bl.firstgoto]); /* error */
}
void closelistfield(ConsControl cc) {
@@ -255,50 +248,49 @@ public class FuncState extends Constants {
return ((k) == LexState.VCALL || (k) == LexState.VVARARG);
}
void lastlistfield (ConsControl cc) {
if (cc.tostore == 0) return;
void lastlistfield(ConsControl cc) {
if (cc.tostore == 0)
return;
if (hasmultret(cc.v.k)) {
this.setmultret(cc.v);
this.setlist(cc.t.u.info, cc.na, LUA_MULTRET);
cc.na--; /** do not count last expression (unknown number of elements) */
}
else {
if (cc.v.k != LexState.VVOID)
this.exp2nextreg(cc.v);
this.setlist(cc.t.u.info, cc.na, cc.tostore);
this.setmultret(cc.v);
this.setlist(cc.t.u.info, cc.na, LUA_MULTRET);
cc.na--; /**
* do not count last expression (unknown number of
* elements)
*/
} else {
if (cc.v.k != LexState.VVOID)
this.exp2nextreg(cc.v);
this.setlist(cc.t.u.info, cc.na, cc.tostore);
}
}
// =============================================================
// from lcode.c
// =============================================================
void nil(int from, int n) {
int l = from + n - 1; /* last register to set nil */
if (this.pc > this.lasttarget && pc > 0) { /* no jumps to current position? */
final int previous_code = f.code[pc - 1];
int l = from+n-1; /* last register to set nil */
if (this.pc > this.lasttarget && pc > 0) { /* no jumps to current position? */
final int previous_code = f.code[pc-1];
if (GET_OPCODE(previous_code) == OP_LOADNIL) {
int pfrom = GETARG_A(previous_code);
int pl = pfrom + GETARG_B(previous_code);
if ((pfrom <= from && from <= pl + 1)
|| (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
int pl = pfrom+GETARG_B(previous_code);
if ((pfrom <= from && from <= pl+1) || (from <= pfrom && pfrom <= l+1)) { /* can connect both? */
if (pfrom < from)
from = pfrom; /* from = min(from, pfrom) */
if (pl > l)
l = pl; /* l = max(l, pl) */
InstructionPtr previous = new InstructionPtr(this.f.code, this.pc - 1);
InstructionPtr previous = new InstructionPtr(this.f.code, this.pc-1);
SETARG_A(previous, from);
SETARG_B(previous, l - from);
SETARG_B(previous, l-from);
return;
}
} /* else go through */
} /* else go through */
}
this.codeABC(OP_LOADNIL, from, n - 1, 0);
this.codeABC(OP_LOADNIL, from, n-1, 0);
}
int jump() {
int jpc = this.jpc.i; /* save list of jumps to here */
this.jpc.i = LexState.NO_JUMP;
@@ -308,24 +300,23 @@ public class FuncState extends Constants {
}
void ret(int first, int nret) {
this.codeABC(OP_RETURN, first, nret + 1, 0);
this.codeABC(OP_RETURN, first, nret+1, 0);
}
int condjump(int /* OpCode */op, int A, int B, int C) {
int condjump(int /* OpCode */ op, int A, int B, int C) {
this.codeABC(op, A, B, C);
return this.jump();
}
void fixjump(int pc, int dest) {
InstructionPtr jmp = new InstructionPtr(this.f.code, pc);
int offset = dest - (pc + 1);
_assert (dest != LexState.NO_JUMP);
int offset = dest-(pc+1);
_assert(dest != LexState.NO_JUMP);
if (Math.abs(offset) > MAXARG_sBx)
ls.syntaxerror("control structure too long");
SETARG_sBx(jmp, offset);
}
/*
* * returns current `pc' and marks it as a jump target (to avoid wrong *
* optimizations with consecutive instructions not in the same basic block).
@@ -335,7 +326,6 @@ public class FuncState extends Constants {
return this.pc;
}
int getjump(int pc) {
int offset = GETARG_sBx(this.f.code[pc]);
/* point to itself represents end of list */
@@ -344,19 +334,17 @@ public class FuncState extends Constants {
return LexState.NO_JUMP;
else
/* turn offset into absolute position */
return (pc + 1) + offset;
return (pc+1)+offset;
}
InstructionPtr getjumpcontrol(int pc) {
InstructionPtr pi = new InstructionPtr(this.f.code, pc);
if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1])))
return new InstructionPtr(pi.code, pi.idx - 1);
if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx-1])))
return new InstructionPtr(pi.code, pi.idx-1);
else
return pi;
}
/*
* * check whether list has any jump that do not produce a value * (or
* produce an inverted value)
@@ -370,7 +358,6 @@ public class FuncState extends Constants {
return false; /* not found */
}
boolean patchtestreg(int node, int reg) {
InstructionPtr i = this.getjumpcontrol(node);
if (GET_OPCODE(i.get()) != OP_TESTSET)
@@ -385,14 +372,13 @@ public class FuncState extends Constants {
return true;
}
void removevalues(int list) {
for (; list != LexState.NO_JUMP; list = this.getjump(list))
this.patchtestreg(list, NO_REG);
}
void patchlistaux(int list, int vtarget, int reg, int dtarget) {
while (list != LexState.NO_JUMP) {
while ( list != LexState.NO_JUMP ) {
int next = this.getjump(list);
if (this.patchtestreg(list, reg))
this.fixjump(list, vtarget);
@@ -411,17 +397,17 @@ public class FuncState extends Constants {
if (target == this.pc)
this.patchtohere(list);
else {
_assert (target < this.pc);
_assert(target < this.pc);
this.patchlistaux(list, target, NO_REG, target);
}
}
void patchclose(int list, int level) {
level++; /* argument is +1 to reserve 0 as non-op */
while (list != LexState.NO_JUMP) {
while ( list != LexState.NO_JUMP ) {
int next = getjump(list);
_assert(GET_OPCODE(f.code[list]) == OP_JMP
&& (GETARG_A(f.code[list]) == 0 || GETARG_A(f.code[list]) >= level));
_assert(
GET_OPCODE(f.code[list]) == OP_JMP && (GETARG_A(f.code[list]) == 0 || GETARG_A(f.code[list]) >= level));
SETARG_A(f.code, list, level);
list = next;
}
@@ -440,7 +426,7 @@ public class FuncState extends Constants {
else {
int list = l1.i;
int next;
while ((next = this.getjump(list)) != LexState.NO_JUMP)
while ( (next = this.getjump(list)) != LexState.NO_JUMP )
/* find last element */
list = next;
this.fixjump(list, l2);
@@ -448,7 +434,7 @@ public class FuncState extends Constants {
}
void checkstack(int n) {
int newstack = this.freereg + n;
int newstack = this.freereg+n;
if (newstack > this.f.maxstacksize) {
if (newstack >= MAXSTACK)
ls.syntaxerror("function or expression too complex");
@@ -464,7 +450,7 @@ public class FuncState extends Constants {
void freereg(int reg) {
if (!ISK(reg) && reg >= this.nactvar) {
this.freereg--;
_assert (reg == this.freereg);
_assert(reg == this.freereg);
}
}
@@ -472,6 +458,7 @@ public class FuncState extends Constants {
if (e.k == LexState.VNONRELOC)
this.freereg(e.u.info);
}
int addk(LuaValue v) {
if (this.h == null) {
this.h = new Hashtable();
@@ -481,8 +468,8 @@ public class FuncState extends Constants {
final int idx = this.nk;
this.h.put(v, new Integer(idx));
final Prototype f = this.f;
if (f.k == null || nk + 1 >= f.k.length)
f.k = realloc( f.k, nk*2 + 1 );
if (f.k == null || nk+1 >= f.k.length)
f.k = realloc(f.k, nk*2+1);
f.k[this.nk++] = v;
return idx;
}
@@ -492,17 +479,17 @@ public class FuncState extends Constants {
}
int numberK(LuaValue r) {
if ( r instanceof LuaDouble ) {
if (r instanceof LuaDouble) {
double d = r.todouble();
int i = (int) d;
if ( d == (double) i )
if (d == (double) i)
r = LuaInteger.valueOf(i);
}
return this.addk(r);
}
int boolK(boolean b) {
return this.addk((b ? LuaValue.TRUE : LuaValue.FALSE));
return this.addk((b? LuaValue.TRUE: LuaValue.FALSE));
}
int nilK() {
@@ -511,9 +498,9 @@ public class FuncState extends Constants {
void setreturns(expdesc e, int nresults) {
if (e.k == LexState.VCALL) { /* expression is an open function call? */
SETARG_C(this.getcodePtr(e), nresults + 1);
SETARG_C(this.getcodePtr(e), nresults+1);
} else if (e.k == LexState.VVARARG) {
SETARG_B(this.getcodePtr(e), nresults + 1);
SETARG_B(this.getcodePtr(e), nresults+1);
SETARG_A(this.getcodePtr(e), this.freereg);
this.reserveregs(1);
}
@@ -541,9 +528,9 @@ public class FuncState extends Constants {
break;
}
case LexState.VINDEXED: {
int op = OP_GETTABUP; /* assume 't' is in an upvalue */
int op = OP_GETTABUP; /* assume 't' is in an upvalue */
this.freereg(e.u.ind_idx);
if (e.u.ind_vt == LexState.VLOCAL) { /* 't' is in a register? */
if (e.u.ind_vt == LexState.VLOCAL) { /* 't' is in a register? */
this.freereg(e.u.ind_t);
op = OP_GETTABLE;
}
@@ -575,8 +562,7 @@ public class FuncState extends Constants {
}
case LexState.VFALSE:
case LexState.VTRUE: {
this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE ? 1 : 0),
0);
this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE? 1: 0), 0);
break;
}
case LexState.VK: {
@@ -598,7 +584,7 @@ public class FuncState extends Constants {
break;
}
default: {
_assert (e.k == LexState.VVOID || e.k == LexState.VJMP);
_assert(e.k == LexState.VVOID || e.k == LexState.VJMP);
return; /* nothing to do... */
}
}
@@ -609,7 +595,7 @@ public class FuncState extends Constants {
void discharge2anyreg(expdesc e) {
if (e.k != LexState.VNONRELOC) {
this.reserveregs(1);
this.discharge2reg(e, this.freereg - 1);
this.discharge2reg(e, this.freereg-1);
}
}
@@ -622,8 +608,7 @@ public class FuncState extends Constants {
int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */
int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */
if (this.need_value(e.t.i) || this.need_value(e.f.i)) {
int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this
.jump();
int fj = (e.k == LexState.VJMP)? LexState.NO_JUMP: this.jump();
p_f = this.code_label(reg, 0, 1);
p_t = this.code_label(reg, 1, 0);
this.patchtohere(fj);
@@ -641,7 +626,7 @@ public class FuncState extends Constants {
this.dischargevars(e);
this.freeexp(e);
this.reserveregs(1);
this.exp2reg(e, this.freereg - 1);
this.exp2reg(e, this.freereg-1);
}
int exp2anyreg(expdesc e) {
@@ -658,7 +643,7 @@ public class FuncState extends Constants {
return e.u.info;
}
void exp2anyregup (expdesc e) {
void exp2anyregup(expdesc e) {
if (e.k != LexState.VUPVAL || e.hasjumps())
exp2anyreg(e);
}
@@ -677,17 +662,16 @@ public class FuncState extends Constants {
case LexState.VFALSE:
case LexState.VNIL: {
if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e.u.info = (e.k == LexState.VNIL) ? this.nilK()
: this.boolK((e.k == LexState.VTRUE));
e.u.info = (e.k == LexState.VNIL)? this.nilK(): this.boolK((e.k == LexState.VTRUE));
e.k = LexState.VK;
return RKASK(e.u.info);
} else
break;
}
case LexState.VKNUM: {
e.u.info = this.numberK(e.u.nval());
e.k = LexState.VK;
/* go through */
e.u.info = this.numberK(e.u.nval());
e.k = LexState.VK;
/* go through */
}
case LexState.VK: {
if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */
@@ -715,13 +699,13 @@ public class FuncState extends Constants {
break;
}
case LexState.VINDEXED: {
int op = (var.u.ind_vt == LexState.VLOCAL) ? OP_SETTABLE : OP_SETTABUP;
int op = (var.u.ind_vt == LexState.VLOCAL)? OP_SETTABLE: OP_SETTABUP;
int e = this.exp2RK(ex);
this.codeABC(op, var.u.ind_t, var.u.ind_idx, e);
this.codeABC(op, var.u.ind_t, var.u.ind_idx, e);
break;
}
default: {
_assert (false); /* invalid var kind to store */
_assert(false); /* invalid var kind to store */
break;
}
}
@@ -742,12 +726,11 @@ public class FuncState extends Constants {
void invertjump(expdesc e) {
InstructionPtr pc = this.getjumpcontrol(e.u.info);
_assert (testTMode(GET_OPCODE(pc.get()))
&& GET_OPCODE(pc.get()) != OP_TESTSET && Lua
.GET_OPCODE(pc.get()) != OP_TEST);
_assert(testTMode(GET_OPCODE(pc.get())) && GET_OPCODE(pc.get()) != OP_TESTSET
&& Lua.GET_OPCODE(pc.get()) != OP_TEST);
// SETARG_A(pc, !(GETARG_A(pc.get())));
int a = GETARG_A(pc.get());
int nota = (a!=0? 0: 1);
int nota = (a != 0? 0: 1);
SETARG_A(pc, nota);
}
@@ -756,7 +739,7 @@ public class FuncState extends Constants {
int ie = this.getcode(e);
if (GET_OPCODE(ie) == OP_NOT) {
this.pc--; /* remove previous OP_NOT */
return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond!=0? 0: 1));
return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond != 0? 0: 1));
}
/* else go through */
}
@@ -840,7 +823,7 @@ public class FuncState extends Constants {
break;
}
default: {
_assert (false); /* cannot happen */
_assert(false); /* cannot happen */
break;
}
}
@@ -862,7 +845,7 @@ public class FuncState extends Constants {
t.u.ind_t = (short) t.u.info;
t.u.ind_idx = (short) this.exp2RK(k);
LuaC._assert(t.k == LexState.VUPVAL || vkisinreg(t.k));
t.u.ind_vt = (short) ((t.k == LexState.VUPVAL) ? LexState.VUPVAL : LexState.VLOCAL);
t.u.ind_vt = (short) ((t.k == LexState.VUPVAL)? LexState.VUPVAL: LexState.VLOCAL);
t.k = LexState.VINDEXED;
}
@@ -871,7 +854,7 @@ public class FuncState extends Constants {
if (!e1.isnumeral() || !e2.isnumeral())
return false;
if ((op == OP_DIV || op == OP_MOD) && e2.u.nval().eq_b(LuaValue.ZERO))
return false; /* do not attempt to divide by 0 */
return false; /* do not attempt to divide by 0 */
v1 = e1.u.nval();
v2 = e2.u.nval();
switch (op) {
@@ -901,13 +884,13 @@ public class FuncState extends Constants {
// break;
return false; /* no constant folding for 'len' */
default:
_assert (false);
_assert(false);
r = null;
break;
}
if ( Double.isNaN(r.todouble()) )
if (Double.isNaN(r.todouble()))
return false; /* do not attempt to produce NaN */
e1.u.setNval( r );
e1.u.setNval(r);
return true;
}
@@ -915,8 +898,7 @@ public class FuncState extends Constants {
if (constfolding(op, e1, e2))
return;
else {
int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2)
: 0;
int o2 = (op != OP_UNM && op != OP_LEN)? this.exp2RK(e2): 0;
int o1 = this.exp2RK(e1);
if (o1 > o2) {
this.freeexp(e1);
@@ -931,7 +913,7 @@ public class FuncState extends Constants {
}
}
void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) {
void codecomp(int /* OpCode */ op, int cond, expdesc e1, expdesc e2) {
int o1 = this.exp2RK(e1);
int o2 = this.exp2RK(e2);
this.freeexp(e2);
@@ -947,17 +929,17 @@ public class FuncState extends Constants {
e1.k = LexState.VJMP;
}
void prefix(int /* UnOpr */op, expdesc e, int line) {
void prefix(int /* UnOpr */ op, expdesc e, int line) {
expdesc e2 = new expdesc();
e2.init(LexState.VKNUM, 0);
switch (op) {
case LexState.OPR_MINUS: {
if (e.isnumeral()) /* minus constant? */
e.u.setNval(e.u.nval().neg()); /* fold it */
else {
this.exp2anyreg(e);
this.codearith(OP_UNM, e, e2, line);
}
if (e.isnumeral()) /* minus constant? */
e.u.setNval(e.u.nval().neg()); /* fold it */
else {
this.exp2anyreg(e);
this.codearith(OP_UNM, e, e2, line);
}
break;
}
case LexState.OPR_NOT:
@@ -969,11 +951,11 @@ public class FuncState extends Constants {
break;
}
default:
_assert (false);
_assert(false);
}
}
void infix(int /* BinOpr */op, expdesc v) {
void infix(int /* BinOpr */ op, expdesc v) {
switch (op) {
case LexState.OPR_AND: {
this.goiftrue(v);
@@ -1004,11 +986,10 @@ public class FuncState extends Constants {
}
}
void posfix(int op, expdesc e1, expdesc e2, int line) {
switch (op) {
case LexState.OPR_AND: {
_assert (e1.t.i == LexState.NO_JUMP); /* list must be closed */
_assert(e1.t.i == LexState.NO_JUMP); /* list must be closed */
this.dischargevars(e2);
this.concat(e2.f, e1.f.i);
// *e1 = *e2;
@@ -1016,7 +997,7 @@ public class FuncState extends Constants {
break;
}
case LexState.OPR_OR: {
_assert (e1.f.i == LexState.NO_JUMP); /* list must be closed */
_assert(e1.f.i == LexState.NO_JUMP); /* list must be closed */
this.dischargevars(e2);
this.concat(e2.t, e1.t.i);
// *e1 = *e2;
@@ -1025,9 +1006,8 @@ public class FuncState extends Constants {
}
case LexState.OPR_CONCAT: {
this.exp2val(e2);
if (e2.k == LexState.VRELOCABLE
&& GET_OPCODE(this.getcode(e2)) == OP_CONCAT) {
_assert (e1.u.info == GETARG_B(this.getcode(e2)) - 1);
if (e2.k == LexState.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) {
_assert(e1.u.info == GETARG_B(this.getcode(e2))-1);
this.freeexp(e1);
SETARG_B(this.getcodePtr(e2), e1.u.info);
e1.k = LexState.VRELOCABLE;
@@ -1075,44 +1055,39 @@ public class FuncState extends Constants {
this.codecomp(OP_LE, 0, e1, e2);
break;
default:
_assert (false);
_assert(false);
}
}
void fixline(int line) {
this.f.lineinfo[this.pc - 1] = line;
this.f.lineinfo[this.pc-1] = line;
}
int code(int instruction, int line) {
Prototype f = this.f;
this.dischargejpc(); /* `pc' will change */
/* put new instruction in code array */
if (f.code == null || this.pc + 1 > f.code.length)
f.code = LuaC.realloc(f.code, this.pc * 2 + 1);
if (f.code == null || this.pc+1 > f.code.length)
f.code = LuaC.realloc(f.code, this.pc*2+1);
f.code[this.pc] = instruction;
/* save corresponding line information */
if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length)
f.lineinfo = LuaC.realloc(f.lineinfo,
this.pc * 2 + 1);
if (f.lineinfo == null || this.pc+1 > f.lineinfo.length)
f.lineinfo = LuaC.realloc(f.lineinfo, this.pc*2+1);
f.lineinfo[this.pc] = line;
return this.pc++;
}
int codeABC(int o, int a, int b, int c) {
_assert (getOpMode(o) == iABC);
_assert (getBMode(o) != OpArgN || b == 0);
_assert (getCMode(o) != OpArgN || c == 0);
_assert(getOpMode(o) == iABC);
_assert(getBMode(o) != OpArgN || b == 0);
_assert(getCMode(o) != OpArgN || c == 0);
return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline);
}
int codeABx(int o, int a, int bc) {
_assert (getOpMode(o) == iABx || getOpMode(o) == iAsBx);
_assert (getCMode(o) == OpArgN);
_assert (bc >= 0 && bc <= Lua.MAXARG_Bx);
_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
_assert(getCMode(o) == OpArgN);
_assert(bc >= 0 && bc <= Lua.MAXARG_Bx);
return this.code(CREATE_ABx(o, a, bc), this.ls.lastline);
}
@@ -1132,16 +1107,16 @@ public class FuncState extends Constants {
}
void setlist(int base, int nelems, int tostore) {
int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
_assert (tostore != 0);
int c = (nelems-1)/LFIELDS_PER_FLUSH+1;
int b = (tostore == LUA_MULTRET)? 0: tostore;
_assert(tostore != 0);
if (c <= MAXARG_C)
this.codeABC(OP_SETLIST, base, b, c);
else {
this.codeABC(OP_SETLIST, base, b, 0);
this.code(c, this.ls.lastline);
}
this.freereg = (short) (base + 1); /* free registers with list values */
this.freereg = (short) (base+1); /* free registers with list values */
}
}

View File

@@ -23,15 +23,18 @@ package org.luaj.vm2.compiler;
class InstructionPtr {
final int[] code;
final int idx;
InstructionPtr(int[] code, int idx ) {
final int idx;
InstructionPtr(int[] code, int idx) {
this.code = code;
this.idx = idx;
}
int get() {
return code[idx];
}
void set(int value) {
code[idx] = value;
}
}
}

View File

@@ -23,8 +23,10 @@ package org.luaj.vm2.compiler;
public class IntPtr {
int i;
IntPtr() {
}
IntPtr(int value) {
this.i = value;
}

File diff suppressed because it is too large Load Diff

View File

@@ -37,30 +37,37 @@ import org.luaj.vm2.lib.BaseLib;
* Compiler for Lua.
*
* <p>
* Compiles lua source files into lua bytecode within a {@link Prototype},
* loads lua binary files directly into a {@link Prototype},
* and optionaly instantiates a {@link LuaClosure} around the result
* using a user-supplied environment.
* Compiles lua source files into lua bytecode within a {@link Prototype}, loads
* lua binary files directly into a {@link Prototype}, and optionaly
* instantiates a {@link LuaClosure} around the result using a user-supplied
* environment.
*
* <p>
* Implements the {@link org.luaj.vm2.Globals.Compiler} interface for loading
* initialized chunks, which is an interface common to
* lua bytecode compiling and java bytecode compiling.
*
* <p>
* The {@link LuaC} compiler is installed by default by both the
* {@link org.luaj.vm2.lib.jse.JsePlatform} and {@link org.luaj.vm2.lib.jme.JmePlatform} classes,
* so in the following example, the default {@link LuaC} compiler
* will be used:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua" ).call();
* } </pre>
* Implements the {@link org.luaj.vm2.Globals.Compiler} interface for loading
* initialized chunks, which is an interface common to lua bytecode compiling
* and java bytecode compiling.
*
* <p>
* The {@link LuaC} compiler is installed by default by both the
* {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform} classes, so in the following
* example, the default {@link LuaC} compiler will be used:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* To load the LuaC compiler manually, use the install method:
* <pre> {@code
*
* <pre>
* {@code
* LuaC.install(globals);
* } </pre>
* }
* </pre>
*
* @see #install(Globals)
* @see Globals#compiler
@@ -76,10 +83,11 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
/** A sharable instance of the LuaC compiler. */
public static final LuaC instance = new LuaC();
/** Install the compiler so that LoadState will first
* try to use it when handed bytes that are
* not already a compiled lua chunk.
/**
* Install the compiler so that LoadState will first try to use it when
* handed bytes that are not already a compiled lua chunk.
*
* @param globals the Globals into which this is to be installed.
*/
public static void install(Globals globals) {
@@ -89,8 +97,11 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
protected LuaC() {}
/** Compile lua source into a Prototype.
* @param stream InputStream representing the text source conforming to lua source syntax.
/**
* Compile lua source into a Prototype.
*
* @param stream InputStream representing the text source conforming to
* lua source syntax.
* @param chunkname String name of the chunk to use.
* @return Prototype representing the lua chunk for this source.
* @throws IOException
@@ -103,55 +114,57 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
return new LuaClosure(prototype, env);
}
/** @deprecated
* Use Globals.load(InputString, String, String) instead,
* or LuaC.compile(InputStream, String) and construct LuaClosure directly.
/**
* @deprecated Use Globals.load(InputString, String, String) instead, or
* LuaC.compile(InputStream, String) and construct LuaClosure
* directly.
*/
public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException {
return new LuaClosure(compile(stream, chunkname), globals);
}
static class CompileState {
int nCcalls = 0;
int nCcalls = 0;
private Hashtable strings = new Hashtable();
protected CompileState() {}
/** Parse the input */
Prototype luaY_parser(InputStream z, String name) throws IOException{
Prototype luaY_parser(InputStream z, String name) throws IOException {
LexState lexstate = new LexState(this, z);
FuncState funcstate = new FuncState();
// lexstate.buff = buff;
lexstate.fs = funcstate;
lexstate.setinput(this, z.read(), z, (LuaString) LuaValue.valueOf(name) );
lexstate.setinput(this, z.read(), z, (LuaString) LuaValue.valueOf(name));
/* main func. is always vararg */
funcstate.f = new Prototype();
funcstate.f.source = (LuaString) LuaValue.valueOf(name);
lexstate.mainfunc(funcstate);
LuaC._assert (funcstate.prev == null);
LuaC._assert(funcstate.prev == null);
/* all scopes should be correctly finished */
LuaC._assert (lexstate.dyd == null
|| (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0));
LuaC._assert(lexstate.dyd == null
|| (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0));
return funcstate.f;
}
// look up and keep at most one copy of each string
public LuaString newTString(String s) {
return cachedLuaString(LuaString.valueOf(s));
}
// look up and keep at most one copy of each string
public LuaString newTString(LuaString s) {
return cachedLuaString(s);
}
public LuaString cachedLuaString(LuaString s) {
LuaString c = (LuaString) strings.get(s);
if (c != null)
if (c != null)
return c;
strings.put(s, s);
return s;
}
public String pushfstring(String string) {
return string;
}

View File

@@ -34,14 +34,14 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua basic library functions.
* Subclass of {@link LibFunction} which implements the lua basic library
* functions.
* <p>
* This contains all library functions listed as "basic functions" in the lua documentation for JME.
* The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files.
* Since JME has no file system by default, {@link BaseLib} implements
* {@link ResourceFinder} using {@link Class#getResource(String)},
* which is the closest equivalent on JME.
* This contains all library functions listed as "basic functions" in the lua
* documentation for JME. The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files. Since JME has no file
* system by default, {@link BaseLib} implements {@link ResourceFinder} using
* {@link Class#getResource(String)}, which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well.
* <p>
* To use basic library functions that include a {@link ResourceFinder} based on
@@ -50,47 +50,60 @@ import org.luaj.vm2.Varargs;
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p>
* For special cases where the smallest possible footprint is desired,
* a minimal set of libraries could be loaded
* directly via {@link Globals#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* For special cases where the smallest possible footprint is desired, a minimal
* set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p>
* This is a direct port of the corresponding library in C.
*
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder
* @see Globals#finder
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib
* Reference</a>
*/
public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
/** Perform one-time initialization on the library by adding base functions
Globals globals;
/**
* Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance.
* @param env the environment to load into, which must be a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
globals.finder = this;
globals.baselib = this;
env.set( "_G", env );
env.set( "_VERSION", Lua._VERSION );
env.set("_G", env);
env.set("_VERSION", Lua._VERSION);
env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile());
@@ -115,24 +128,24 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
env.set("next", next = new next());
env.set("pairs", new pairs(next));
env.set("ipairs", new ipairs());
return env;
}
/** ResourceFinder implementation
/**
* ResourceFinder implementation
*
* Tries to open the file as a resource, which can work for JSE and JME.
*/
public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename);
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/" + filename);
}
// "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() )
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" );
if (!args.arg1().toboolean())
error(args.narg() > 1? args.optjstring(2, "assertion failed!"): "assertion failed!");
return args;
}
}
@@ -141,14 +154,14 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) {
if ("collect".equals(s)) {
System.gc();
return ZERO;
} else if ( "count".equals(s) ) {
} else if ("count".equals(s)) {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
long used = rt.totalMemory()-rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) {
} else if ("step".equals(s)) {
System.gc();
return LuaValue.TRUE;
} else {
@@ -163,9 +176,8 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null?
loadStream( globals.STDIN, "=stdin", "bt", globals ):
loadFile( args.checkjstring(1), "bt", globals );
Varargs v = filename == null? loadStream(globals.STDIN, "=stdin", "bt", globals)
: loadFile(args.checkjstring(1), "bt", globals);
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
}
}
@@ -173,8 +185,10 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
if (arg1.isnil()) throw new LuaError(NIL);
if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1);
if (arg1.isnil())
throw new LuaError(NIL);
if (!arg1.isstring() || arg2.optint(1) == 0)
throw new LuaError(arg1);
throw new LuaError(arg1.tojstring(), arg2.optint(1));
}
}
@@ -184,23 +198,26 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL;
return mt != null? mt.rawget(METATABLE).optvalue(mt): NIL;
}
}
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1();
if (!ld.isstring() && !ld.isfunction()) {
throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")");
throw new LuaError(
"bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")");
}
String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)");
String mode = args.optjstring(3, "bt");
LuaValue env = args.optvalue(4, globals);
return loadStream(ld.isstring()? ld.strvalue().toInputStream():
new StringInputStream(ld.checkfunction()), source, mode, env);
return loadStream(ld.isstring()? ld.strvalue().toInputStream(): new StringInputStream(ld.checkfunction()),
source, mode, env);
}
}
@@ -211,12 +228,10 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
String filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals);
return filename == null?
loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env );
return filename == null? loadStream(globals.STDIN, "=stdin", mode, env): loadFile(filename, mode, env);
}
}
// "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) {
@@ -225,12 +240,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) {
} catch (LuaError le) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
return varargsOf(FALSE, m != null? m: NIL);
} catch (Exception e) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
return varargsOf(FALSE, valueOf(m != null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
@@ -241,30 +256,34 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "print", // (...) -> void
final class print extends VarArgFunction {
final BaseLib baselib;
print(BaseLib baselib) {
this.baselib = baselib;
}
public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) {
if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue();
for (int i = 1, n = args.narg(); i <= n; i++) {
if (i > 1)
globals.STDOUT.print('\t');
LuaString s = tostring.call(args.arg(i)).strvalue();
globals.STDOUT.print(s.tojstring());
}
globals.STDOUT.print('\n');
return NONE;
}
}
// "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction {
public LuaValue call() {
return argerror(1, "value expected");
}
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2));
}
@@ -275,12 +294,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public LuaValue call(LuaValue arg) {
return argerror(2, "value expected");
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2);
}
}
// "rawlen", // (v) -> value
static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) {
@@ -291,68 +310,73 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "rawset", // (table, index, value) -> table
static final class rawset extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"value expected");
return argerror(2, "value expected");
}
public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value expected");
return argerror(3, "value expected");
}
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable();
if (!index.isvalidkey()) argerror(2, "table index is nil");
if (!index.isvalidkey())
argerror(2, "table index is nil");
t.rawset(index, value);
return t;
}
}
// "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) {
int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) )
if (args.arg1().equals(valueOf("#")))
return valueOf(n);
int i = args.checkint(1);
if ( i == 0 || i < -n )
argerror(1,"index out of range");
return args.subargs(i<0? n+i+2: i+1);
if (i == 0 || i < -n)
argerror(1, "index out of range");
return args.subargs(i < 0? n+i+2: i+1);
}
}
// "setmetatable", // (table, metatable) -> table
static final class setmetatable extends TableLibFunction {
public LuaValue call(LuaValue table) {
return argerror(2,"nil or table expected");
return argerror(2, "nil or table expected");
}
public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() )
if (mt0 != null && !mt0.rawget(METATABLE).isnil())
error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable());
}
}
// "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) {
return e.tonumber();
}
public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil())
return e.tonumber();
final int b = base.checkint();
if ( b < 2 || b > 36 )
if (b < 2 || b > 36)
argerror(2, "base out of range");
return e.checkstring().tonumber(b);
}
}
// "tostring", // (e) -> value
static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() )
if (!h.isnil())
return h.call(arg);
LuaValue v = arg.tostring();
if ( ! v.isnil() )
if (!v.isnil())
return v;
return valueOf(arg.tojstring());
}
@@ -376,12 +400,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
globals.debuglib.onCall(this);
try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) {
} catch (LuaError le) {
final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL);
} catch ( Exception e ) {
return varargsOf(FALSE, m != null? m: NIL);
} catch (Exception e) {
final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString()));
return varargsOf(FALSE, valueOf(m != null? m: e.toString()));
} finally {
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
@@ -391,56 +415,60 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
}
}
}
// "pairs" (t) -> iter-func, t, nil
static final class pairs extends VarArgFunction {
final next next;
pairs(next next) {
this.next = next;
}
public Varargs invoke(Varargs args) {
return varargsOf( next, args.checktable(1), NIL );
return varargsOf(next, args.checktable(1), NIL);
}
}
// // "ipairs", // (t) -> iter-func, t, 0
static final class ipairs extends VarArgFunction {
inext inext = new inext();
public Varargs invoke(Varargs args) {
return varargsOf( inext, args.checktable(1), ZERO );
return varargsOf(inext, args.checktable(1), ZERO);
}
}
// "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2));
}
}
// "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction {
public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2));
}
}
/**
* Load from a named file, returning the chunk or nil,error of can't load
*
* @param env
* @param mode
* @return Varargs containing chunk, or NIL,error-text on error
*/
public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename);
if ( is == null )
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory"));
if (is == null)
return varargsOf(NIL, valueOf("cannot open " + filename + ": No such file or directory"));
try {
return loadStream(is, "@"+filename, mode, env);
return loadStream(is, "@" + filename, mode, env);
} finally {
try {
is.close();
} catch ( Exception e ) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -448,28 +476,29 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try {
if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname));
if (is == null)
return varargsOf(NIL, valueOf("not found: " + chunkname));
return globals.load(is, chunkname, mode, env);
} catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
private static class StringInputStream extends InputStream {
final LuaValue func;
byte[] bytes;
int offset, remaining = 0;
byte[] bytes;
int offset, remaining = 0;
StringInputStream(LuaValue func) {
this.func = func;
}
public int read() throws IOException {
if ( remaining < 0 )
if (remaining < 0)
return -1;
if ( remaining == 0 ) {
if (remaining == 0) {
LuaValue s = func.call();
if ( s.isnil() )
if (s.isnil())
return remaining = -1;
LuaString ls = s.strvalue();
bytes = ls.m_bytes;
@@ -479,7 +508,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
return -1;
}
--remaining;
return 0xFF&bytes[offset++];
return 0xFF & bytes[offset++];
}
}
}

View File

@@ -26,68 +26,86 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of LibFunction that implements the Lua standard {@code bit32} library.
* Subclass of LibFunction that implements the Lua standard {@code bit32}
* library.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
* } </pre>
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new Bit32Lib());
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) );
* } </pre>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new Bit32Lib());
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise Operation Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise
* Operation Lib Reference</a>
*/
public class Bit32Lib extends TwoArgFunction {
public Bit32Lib() {
}
/** Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment,
* adding the table to package.loaded, and returning table as the return value.
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance.
* @param env the environment to load into, which must be a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable t = new LuaTable();
bind(t, Bit32LibV.class, new String[] {
"band", "bnot", "bor", "btest", "bxor", "extract", "replace"
});
bind(t, Bit32Lib2.class, new String[] {
"arshift", "lrotate", "lshift", "rrotate", "rshift"
});
bind(t, Bit32LibV.class, new String[] { "band", "bnot", "bor", "btest", "bxor", "extract", "replace" });
bind(t, Bit32Lib2.class, new String[] { "arshift", "lrotate", "lshift", "rrotate", "rshift" });
env.set("bit32", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("bit32", t);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("bit32", t);
return t;
}
static final class Bit32LibV extends VarArgFunction {
public Varargs invoke(Varargs args) {
switch ( opcode ) {
case 0: return Bit32Lib.band( args );
case 1: return Bit32Lib.bnot( args );
case 2: return Bit32Lib.bor( args );
case 3: return Bit32Lib.btest( args );
case 4: return Bit32Lib.bxor( args );
switch (opcode) {
case 0:
return Bit32Lib.band(args);
case 1:
return Bit32Lib.bnot(args);
case 2:
return Bit32Lib.bor(args);
case 3:
return Bit32Lib.btest(args);
case 4:
return Bit32Lib.bxor(args);
case 5:
return Bit32Lib.extract( args.checkint(1), args.checkint(2), args.optint(3, 1) );
return Bit32Lib.extract(args.checkint(1), args.checkint(2), args.optint(3, 1));
case 6:
return Bit32Lib.replace( args.checkint(1), args.checkint(2),
args.checkint(3), args.optint(4, 1) );
return Bit32Lib.replace(args.checkint(1), args.checkint(2), args.checkint(3), args.optint(4, 1));
}
return NIL;
}
@@ -96,23 +114,28 @@ public class Bit32Lib extends TwoArgFunction {
static final class Bit32Lib2 extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
switch ( opcode ) {
case 0: return Bit32Lib.arshift(arg1.checkint(), arg2.checkint());
case 1: return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint());
case 2: return Bit32Lib.lshift(arg1.checkint(), arg2.checkint());
case 3: return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint());
case 4: return Bit32Lib.rshift(arg1.checkint(), arg2.checkint());
switch (opcode) {
case 0:
return Bit32Lib.arshift(arg1.checkint(), arg2.checkint());
case 1:
return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint());
case 2:
return Bit32Lib.lshift(arg1.checkint(), arg2.checkint());
case 3:
return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint());
case 4:
return Bit32Lib.rshift(arg1.checkint(), arg2.checkint());
}
return NIL;
}
}
static LuaValue arshift(int x, int disp) {
if (disp >= 0) {
return bitsToValue(x >> disp);
return bitsToValue(x>>disp);
} else {
return bitsToValue(x << -disp);
return bitsToValue(x<<-disp);
}
}
@@ -120,9 +143,9 @@ public class Bit32Lib extends TwoArgFunction {
if (disp >= 32 || disp <= -32) {
return ZERO;
} else if (disp >= 0) {
return bitsToValue(x >>> disp);
return bitsToValue(x>>>disp);
} else {
return bitsToValue(x << -disp);
return bitsToValue(x<<-disp);
}
}
@@ -130,46 +153,46 @@ public class Bit32Lib extends TwoArgFunction {
if (disp >= 32 || disp <= -32) {
return ZERO;
} else if (disp >= 0) {
return bitsToValue(x << disp);
return bitsToValue(x<<disp);
} else {
return bitsToValue(x >>> -disp);
return bitsToValue(x>>>-disp);
}
}
static Varargs band( Varargs args ) {
static Varargs band(Varargs args) {
int result = -1;
for ( int i = 1; i <= args.narg(); i++ ) {
for (int i = 1; i <= args.narg(); i++) {
result &= args.checkint(i);
}
return bitsToValue( result );
return bitsToValue(result);
}
static Varargs bnot( Varargs args ) {
return bitsToValue( ~args.checkint(1) );
static Varargs bnot(Varargs args) {
return bitsToValue(~args.checkint(1));
}
static Varargs bor( Varargs args ) {
static Varargs bor(Varargs args) {
int result = 0;
for ( int i = 1; i <= args.narg(); i++ ) {
for (int i = 1; i <= args.narg(); i++) {
result |= args.checkint(i);
}
return bitsToValue( result );
return bitsToValue(result);
}
static Varargs btest( Varargs args ) {
static Varargs btest(Varargs args) {
int bits = -1;
for ( int i = 1; i <= args.narg(); i++ ) {
for (int i = 1; i <= args.narg(); i++) {
bits &= args.checkint(i);
}
return valueOf( bits != 0 );
return valueOf(bits != 0);
}
static Varargs bxor( Varargs args ) {
static Varargs bxor(Varargs args) {
int result = 0;
for ( int i = 1; i <= args.narg(); i++ ) {
for (int i = 1; i <= args.narg(); i++) {
result ^= args.checkint(i);
}
return bitsToValue( result );
return bitsToValue(result);
}
static LuaValue lrotate(int x, int disp) {
@@ -177,7 +200,7 @@ public class Bit32Lib extends TwoArgFunction {
return rrotate(x, -disp);
} else {
disp = disp & 31;
return bitsToValue((x << disp) | (x >>> (32 - disp)));
return bitsToValue((x<<disp) | (x>>>(32-disp)));
}
}
@@ -186,7 +209,7 @@ public class Bit32Lib extends TwoArgFunction {
return lrotate(x, -disp);
} else {
disp = disp & 31;
return bitsToValue((x >>> disp) | (x << (32 - disp)));
return bitsToValue((x>>>disp) | (x<<(32-disp)));
}
}
@@ -197,10 +220,10 @@ public class Bit32Lib extends TwoArgFunction {
if (width < 0) {
argerror(3, "width must be postive");
}
if (field + width > 32) {
if (field+width > 32) {
error("trying to access non-existent bits");
}
return bitsToValue((n >>> field) & (-1 >>> (32 - width)));
return bitsToValue((n>>>field) & (-1>>>(32-width)));
}
static LuaValue replace(int n, int v, int field, int width) {
@@ -210,15 +233,15 @@ public class Bit32Lib extends TwoArgFunction {
if (width < 0) {
argerror(4, "width must be postive");
}
if (field + width > 32) {
if (field+width > 32) {
error("trying to access non-existent bits");
}
int mask = (-1 >>> (32 - width)) << field;
n = (n & ~mask) | ((v << field) & mask);
int mask = (-1>>>(32-width))<<field;
n = (n & ~mask) | ((v<<field) & mask);
return bitsToValue(n);
}
private static LuaValue bitsToValue( int x ) {
return ( x < 0 ) ? valueOf((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x);
private static LuaValue bitsToValue(int x) {
return (x < 0)? valueOf((double) ((long) x & 0xFFFFFFFFL)): valueOf(x);
}
}

View File

@@ -28,49 +28,64 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code coroutine}
* library.
* Subclass of {@link LibFunction} which implements the lua standard
* {@code coroutine} library.
* <p>
* The coroutine library in luaj has the same behavior as the
* coroutine library in C, but is implemented using Java Threads to maintain
* the call state between invocations. Therefore it can be yielded from anywhere,
* similar to the "Coco" yield-from-anywhere patch available for C-based lua.
* However, coroutines that are yielded but never resumed to complete their execution
* may not be collected by the garbage collector.
* The coroutine library in luaj has the same behavior as the coroutine library
* in C, but is implemented using Java Threads to maintain the call state
* between invocations. Therefore it can be yielded from anywhere, similar to
* the "Coco" yield-from-anywhere patch available for C-based lua. However,
* coroutines that are yielded but never resumed to complete their execution may
* not be collected by the garbage collector.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("coroutine").get("running").call() );
* } </pre>
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new CoroutineLib());
* System.out.println( globals.get("coroutine").get("running").call() );
* } </pre>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new CoroutineLib());
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p>
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2 Coroutine Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2
* Coroutine Lib Reference</a>
*/
public class CoroutineLib extends TwoArgFunction {
static int coroutine_count = 0;
Globals globals;
/** Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment,
* adding the table to package.loaded, and returning table as the return value.
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance.
* @param env the environment to load into, which must be a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
@@ -82,7 +97,8 @@ public class CoroutineLib extends TwoArgFunction {
coroutine.set("yield", new yield());
coroutine.set("wrap", new wrap());
env.set("coroutine", coroutine);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("coroutine", coroutine);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("coroutine", coroutine);
return coroutine;
}
@@ -95,7 +111,7 @@ public class CoroutineLib extends TwoArgFunction {
static final class resume extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread t = args.checkthread(1);
return t.resume( args.subargs(2) );
return t.resume(args.subargs(2));
}
}
@@ -109,13 +125,13 @@ public class CoroutineLib extends TwoArgFunction {
static final class status extends LibFunction {
public LuaValue call(LuaValue t) {
LuaThread lt = t.checkthread();
return valueOf( lt.getStatus() );
return valueOf(lt.getStatus());
}
}
final class yield extends VarArgFunction {
public Varargs invoke(Varargs args) {
return globals.yield( args );
return globals.yield(args);
}
}
@@ -129,15 +145,17 @@ public class CoroutineLib extends TwoArgFunction {
static final class wrapper extends VarArgFunction {
final LuaThread luathread;
wrapper(LuaThread luathread) {
this.luathread = luathread;
}
public Varargs invoke(Varargs args) {
final Varargs result = luathread.resume(args);
if ( result.arg1().toboolean() ) {
if (result.arg1().toboolean()) {
return result.subargs(2);
} else {
return error( result.arg(2).tojstring() );
return error(result.arg(2).tojstring());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,77 +32,100 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Abstract base class extending {@link LibFunction} which implements the
* core of the lua standard {@code io} library.
* Abstract base class extending {@link LibFunction} which implements the core
* of the lua standard {@code io} library.
* <p>
* It contains the implementation of the io library support that is common to
* the JSE and JME platforms.
* In practice on of the concrete IOLib subclasses is chosen:
* {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* the JSE and JME platforms. In practice on of the concrete IOLib subclasses is
* chosen: {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* {@link org.luaj.vm2.lib.jme.JmeIoLib} for the JME platform.
* <p>
* The JSE implementation conforms almost completely to the C-based lua library,
* while the JME implementation follows closely except in the area of random-access files,
* which are difficult to support properly on JME.
* while the JME implementation follows closely except in the area of
* random-access files, which are difficult to support properly on JME.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre>
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib} library will be loaded, which will include
* the base functionality provided by this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load the
* {@link org.luaj.vm2.lib.jse.JseIoLib}.
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib}
* library will be loaded, which will include the base functionality provided by
* this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load
* the {@link org.luaj.vm2.lib.jse.JseIoLib}.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseIoLib
* @see org.luaj.vm2.lib.jme.JmeIoLib
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
* @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
*/
abstract
public class IoLib extends TwoArgFunction {
abstract public class IoLib extends TwoArgFunction {
abstract protected class File extends LuaValue {
abstract public void write(LuaString string) throws IOException;
abstract
protected class File extends LuaValue{
abstract public void write( LuaString string ) throws IOException;
abstract public void flush() throws IOException;
abstract public boolean isstdfile();
abstract public void close() throws IOException;
abstract public boolean isclosed();
// returns new position
abstract public int seek(String option, int bytecount) throws IOException;
abstract public void setvbuf(String mode, int size);
// get length remaining to read
abstract public int remaining() throws IOException;
// peek ahead one character
abstract public int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception
abstract public int read() throws IOException, EOFException;
// return number of bytes read if positive, false if eof, throw IOException on other exception
abstract public int read(byte[] bytes, int offset, int length) throws IOException;
public boolean eof() throws IOException {
try {
return peek() < 0;
} catch (EOFException e) { return true; }
} catch (EOFException e) {
return true;
}
}
// delegate method access to file methods table
public LuaValue get( LuaValue key ) {
public LuaValue get(LuaValue key) {
return filemethods.get(key);
}
@@ -110,35 +133,38 @@ public class IoLib extends TwoArgFunction {
public int type() {
return LuaValue.TUSERDATA;
}
public String typename() {
return "userdata";
}
// displays as "file" type
public String tojstring() {
return "file: " + Integer.toHexString(hashCode());
}
public void finalize() {
if (!isclosed()) {
try {
close();
} catch (IOException ignore) {}
} catch (IOException ignore) {
}
}
}
}
/** Enumerated value representing stdin */
protected static final int FTYPE_STDIN = 0;
protected static final int FTYPE_STDIN = 0;
/** Enumerated value representing stdout */
protected static final int FTYPE_STDOUT = 1;
/** Enumerated value representing stderr */
protected static final int FTYPE_STDERR = 2;
/** Enumerated value representing a file type for a named file */
protected static final int FTYPE_NAMED = 3;
protected static final int FTYPE_NAMED = 3;
/**
* Wrap the standard input.
*
* @return File
* @throws IOException
*/
@@ -146,32 +172,37 @@ public class IoLib extends TwoArgFunction {
/**
* Wrap the standard output.
*
* @return File
* @throws IOException
*/
abstract protected File wrapStdout() throws IOException;
/**
* Wrap the standard error output.
*
* @return File
* @throws IOException
*/
abstract protected File wrapStderr() throws IOException;
/**
* Open a file in a particular mode.
*
* @param filename
* @param readMode true if opening in read mode
* @param readMode true if opening in read mode
* @param appendMode true if opening in append mode
* @param updateMode true if opening in update mode
* @param binaryMode true if opening in binary mode
* @return File object if successful
* @throws IOException if could not be opened
*/
abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException;
abstract protected File openFile(String filename, boolean readMode, boolean appendMode, boolean updateMode,
boolean binaryMode) throws IOException;
/**
* Open a temporary file.
*
* @return File object if successful
* @throws IOException if could not be opened
*/
@@ -179,6 +210,7 @@ public class IoLib extends TwoArgFunction {
/**
* Start a new process and return a file for input or output
*
* @param prog the program to execute
* @param mode "r" to read, "w" to write
* @return File to read to or write from
@@ -195,103 +227,88 @@ public class IoLib extends TwoArgFunction {
private static final LuaValue STDERR = valueOf("stderr");
private static final LuaValue FILE = valueOf("file");
private static final LuaValue CLOSED_FILE = valueOf("closed file");
private static final int IO_CLOSE = 0;
private static final int IO_FLUSH = 1;
private static final int IO_INPUT = 2;
private static final int IO_LINES = 3;
private static final int IO_OPEN = 4;
private static final int IO_OUTPUT = 5;
private static final int IO_POPEN = 6;
private static final int IO_READ = 7;
private static final int IO_TMPFILE = 8;
private static final int IO_TYPE = 9;
private static final int IO_WRITE = 10;
private static final int FILE_CLOSE = 11;
private static final int FILE_FLUSH = 12;
private static final int FILE_LINES = 13;
private static final int FILE_READ = 14;
private static final int FILE_SEEK = 15;
private static final int FILE_SETVBUF = 16;
private static final int FILE_WRITE = 17;
private static final int IO_INDEX = 18;
private static final int LINES_ITER = 19;
private static final int IO_CLOSE = 0;
private static final int IO_FLUSH = 1;
private static final int IO_INPUT = 2;
private static final int IO_LINES = 3;
private static final int IO_OPEN = 4;
private static final int IO_OUTPUT = 5;
private static final int IO_POPEN = 6;
private static final int IO_READ = 7;
private static final int IO_TMPFILE = 8;
private static final int IO_TYPE = 9;
private static final int IO_WRITE = 10;
public static final String[] IO_NAMES = {
"close",
"flush",
"input",
"lines",
"open",
"output",
"popen",
"read",
"tmpfile",
"type",
"write",
};
public static final String[] FILE_NAMES = {
"close",
"flush",
"lines",
"read",
"seek",
"setvbuf",
"write",
};
private static final int FILE_CLOSE = 11;
private static final int FILE_FLUSH = 12;
private static final int FILE_LINES = 13;
private static final int FILE_READ = 14;
private static final int FILE_SEEK = 15;
private static final int FILE_SETVBUF = 16;
private static final int FILE_WRITE = 17;
private static final int IO_INDEX = 18;
private static final int LINES_ITER = 19;
public static final String[] IO_NAMES = { "close", "flush", "input", "lines", "open", "output", "popen", "read",
"tmpfile", "type", "write", };
public static final String[] FILE_NAMES = { "close", "flush", "lines", "read", "seek", "setvbuf", "write", };
LuaTable filemethods;
protected Globals globals;
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
// io lib functions
LuaTable t = new LuaTable();
bind(t, IoLibV.class, IO_NAMES );
bind(t, IoLibV.class, IO_NAMES);
// create file methods table
filemethods = new LuaTable();
bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE );
bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE);
// set up file metatable
LuaTable mt = new LuaTable();
bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX );
t.setmetatable( mt );
bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX);
t.setmetatable(mt);
// all functions link to library instance
setLibInstance( t );
setLibInstance( filemethods );
setLibInstance( mt );
setLibInstance(t);
setLibInstance(filemethods);
setLibInstance(mt);
// return the table
env.set("io", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("io", t);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("io", t);
return t;
}
private void setLibInstance(LuaTable t) {
LuaValue[] k = t.keys();
for ( int i=0, n=k.length; i<n; i++ )
for (int i = 0, n = k.length; i < n; i++)
((IoLibV) t.get(k[i])).iolib = this;
}
static final class IoLibV extends VarArgFunction {
private File f;
public IoLib iolib;
private File f;
public IoLib iolib;
private boolean toclose;
private Varargs args;
public IoLibV() {
}
public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) {
this(f, name, opcode, iolib);
this.toclose = toclose;
this.args = args.dealias();
}
public IoLibV(File f, String name, int opcode, IoLib iolib) {
super();
this.f = f;
@@ -302,45 +319,65 @@ public class IoLib extends TwoArgFunction {
public Varargs invoke(Varargs args) {
try {
switch ( opcode ) {
case IO_FLUSH: return iolib._io_flush();
case IO_TMPFILE: return iolib._io_tmpfile();
case IO_CLOSE: return iolib._io_close(args.arg1());
case IO_INPUT: return iolib._io_input(args.arg1());
case IO_OUTPUT: return iolib._io_output(args.arg1());
case IO_TYPE: return iolib._io_type(args.arg1());
case IO_POPEN: return iolib._io_popen(args.checkjstring(1),args.optjstring(2,"r"));
case IO_OPEN: return iolib._io_open(args.checkjstring(1), args.optjstring(2,"r"));
case IO_LINES: return iolib._io_lines(args);
case IO_READ: return iolib._io_read(args);
case IO_WRITE: return iolib._io_write(args);
case FILE_CLOSE: return iolib._file_close(args.arg1());
case FILE_FLUSH: return iolib._file_flush(args.arg1());
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192));
case FILE_LINES: return iolib._file_lines(args);
case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2));
case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0));
case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2));
switch (opcode) {
case IO_FLUSH:
return iolib._io_flush();
case IO_TMPFILE:
return iolib._io_tmpfile();
case IO_CLOSE:
return iolib._io_close(args.arg1());
case IO_INPUT:
return iolib._io_input(args.arg1());
case IO_OUTPUT:
return iolib._io_output(args.arg1());
case IO_TYPE:
return iolib._io_type(args.arg1());
case IO_POPEN:
return iolib._io_popen(args.checkjstring(1), args.optjstring(2, "r"));
case IO_OPEN:
return iolib._io_open(args.checkjstring(1), args.optjstring(2, "r"));
case IO_LINES:
return iolib._io_lines(args);
case IO_READ:
return iolib._io_read(args);
case IO_WRITE:
return iolib._io_write(args);
case IO_INDEX: return iolib._io_index(args.arg(2));
case LINES_ITER: return iolib._lines_iter(f, toclose, this.args);
case FILE_CLOSE:
return iolib._file_close(args.arg1());
case FILE_FLUSH:
return iolib._file_flush(args.arg1());
case FILE_SETVBUF:
return iolib._file_setvbuf(args.arg1(), args.checkjstring(2), args.optint(3, 8192));
case FILE_LINES:
return iolib._file_lines(args);
case FILE_READ:
return iolib._file_read(args.arg1(), args.subargs(2));
case FILE_SEEK:
return iolib._file_seek(args.arg1(), args.optjstring(2, "cur"), args.optint(3, 0));
case FILE_WRITE:
return iolib._file_write(args.arg1(), args.subargs(2));
case IO_INDEX:
return iolib._io_index(args.arg(2));
case LINES_ITER:
return iolib._lines_iter(f, toclose, this.args);
}
} catch ( IOException ioe ) {
} catch (IOException ioe) {
if (opcode == LINES_ITER) {
String s = ioe.getMessage();
error(s != null ? s : ioe.toString());
error(s != null? s: ioe.toString());
}
return errorresult(ioe);
}
return NONE;
}
}
private File input() {
return infile!=null? infile: (infile=ioopenfile(FTYPE_STDIN, "-","r"));
return infile != null? infile: (infile = ioopenfile(FTYPE_STDIN, "-", "r"));
}
// io.flush() -> bool
public Varargs _io_flush() throws IOException {
checkopen(output());
@@ -362,31 +399,28 @@ public class IoLib extends TwoArgFunction {
// io.input([file]) -> file
public Varargs _io_input(LuaValue file) {
infile = file.isnil()? input():
file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(),"r"):
checkfile(file);
infile = file.isnil()? input()
: file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(), "r"): checkfile(file);
return infile;
}
// io.output(filename) -> file
public Varargs _io_output(LuaValue filename) {
outfile = filename.isnil()? output():
filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(),"w"):
checkfile(filename);
outfile = filename.isnil()? output()
: filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(), "w"): checkfile(filename);
return outfile;
}
// io.type(obj) -> "file" | "closed file" | nil
public Varargs _io_type(LuaValue obj) {
File f = optfile(obj);
return f!=null?
f.isclosed()? CLOSED_FILE: FILE:
NIL;
return f != null? f.isclosed()? CLOSED_FILE: FILE: NIL;
}
// io.popen(prog, [mode]) -> file
public Varargs _io_popen(String prog, String mode) throws IOException {
if (!"r".equals(mode) && !"w".equals(mode)) argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'");
if (!"r".equals(mode) && !"w".equals(mode))
argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'");
return openProgram(prog, mode);
}
@@ -398,7 +432,7 @@ public class IoLib extends TwoArgFunction {
// io.lines(filename, ...) -> iterator
public Varargs _io_lines(Varargs args) {
String filename = args.optjstring(1, null);
File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r");
File infile = filename == null? input(): ioopenfile(FTYPE_NAMED, filename, "r");
checkopen(infile);
return lines(infile, filename != null, args.subargs(2));
}
@@ -406,13 +440,13 @@ public class IoLib extends TwoArgFunction {
// io.read(...) -> (...)
public Varargs _io_read(Varargs args) throws IOException {
checkopen(input());
return ioread(infile,args);
return ioread(infile, args);
}
// io.write(...) -> void
public Varargs _io_write(Varargs args) throws IOException {
checkopen(output());
return iowrite(outfile,args);
return iowrite(outfile, args);
}
// file:close() -> void
@@ -434,7 +468,7 @@ public class IoLib extends TwoArgFunction {
} else {
argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'");
}
checkfile(file).setvbuf(mode,size);
checkfile(file).setvbuf(mode, size);
return LuaValue.TRUE;
}
@@ -445,7 +479,7 @@ public class IoLib extends TwoArgFunction {
// file:read(...) -> (...)
public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException {
return ioread(checkfile(file),subargs);
return ioread(checkfile(file), subargs);
}
// file:seek([whence][,offset]) -> pos | nil,error
@@ -456,50 +490,51 @@ public class IoLib extends TwoArgFunction {
} else {
argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'");
}
return valueOf( checkfile(file).seek(whence,offset) );
return valueOf(checkfile(file).seek(whence, offset));
}
// file:write(...) -> void
public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException {
return iowrite(checkfile(file),subargs);
return iowrite(checkfile(file), subargs);
}
// __index, returns a field
public Varargs _io_index(LuaValue v) {
return v.equals(STDOUT)?output():
v.equals(STDIN)? input():
v.equals(STDERR)? errput(): NIL;
return v.equals(STDOUT)? output(): v.equals(STDIN)? input(): v.equals(STDERR)? errput(): NIL;
}
// lines iterator(s,var) -> var'
public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException {
File f = optfile(file);
if ( f == null ) argerror(1, "not a file: " + file);
if ( f.isclosed() ) error("file is already closed");
if (f == null)
argerror(1, "not a file: " + file);
if (f.isclosed())
error("file is already closed");
Varargs ret = ioread(f, args);
if (toclose && ret.isnil(1) && f.eof()) f.close();
if (toclose && ret.isnil(1) && f.eof())
f.close();
return ret;
}
private File output() {
return outfile!=null? outfile: (outfile=ioopenfile(FTYPE_STDOUT,"-","w"));
return outfile != null? outfile: (outfile = ioopenfile(FTYPE_STDOUT, "-", "w"));
}
private File errput() {
return errfile!=null? errfile: (errfile=ioopenfile(FTYPE_STDERR,"-","w"));
return errfile != null? errfile: (errfile = ioopenfile(FTYPE_STDERR, "-", "w"));
}
private File ioopenfile(int filetype, String filename, String mode) {
try {
return rawopenfile(filetype, filename, mode);
} catch ( Exception e ) {
error("io error: "+e.getMessage());
} catch (Exception e) {
error("io error: " + e.getMessage());
return null;
}
}
private static Varargs ioclose(File f) throws IOException {
if ( f.isstdfile() )
if (f.isstdfile())
return errorresult("cannot close standard file");
else {
f.close();
@@ -513,71 +548,80 @@ public class IoLib extends TwoArgFunction {
static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage();
return errorresult("io error: "+(s!=null? s: ioe.toString()));
return errorresult("io error: " + (s != null? s: ioe.toString()));
}
private static Varargs errorresult(String errortext) {
return varargsOf(NIL, valueOf(errortext));
}
private Varargs lines(final File f, boolean toclose, Varargs args) {
try {
return new IoLibV(f,"lnext",LINES_ITER,this,toclose,args);
} catch ( Exception e ) {
return error("lines: "+e);
return new IoLibV(f, "lnext", LINES_ITER, this, toclose, args);
} catch (Exception e) {
return error("lines: " + e);
}
}
private static Varargs iowrite(File f, Varargs args) throws IOException {
for ( int i=1, n=args.narg(); i<=n; i++ )
f.write( args.checkstring(i) );
for (int i = 1, n = args.narg(); i <= n; i++)
f.write(args.checkstring(i));
return f;
}
private Varargs ioread(File f, Varargs args) throws IOException {
int i,n=args.narg();
if (n == 0) return freadline(f,false);
int i, n = args.narg();
if (n == 0)
return freadline(f, false);
LuaValue[] v = new LuaValue[n];
LuaValue ai,vi;
LuaValue ai, vi;
LuaString fmt;
for ( i=0; i<n; ) {
item: switch ( (ai = args.arg(i+1)).type() ) {
case LuaValue.TNUMBER:
vi = freadbytes(f,ai.toint());
break item;
case LuaValue.TSTRING:
fmt = ai.checkstring();
if ( fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*' ) {
switch ( fmt.m_bytes[fmt.m_offset+1] ) {
case 'n': vi = freadnumber(f); break item;
case 'l': vi = freadline(f,false); break item;
case 'L': vi = freadline(f,true); break item;
case 'a': vi = freadall(f); break item;
}
for (i = 0; i < n;) {
item: switch ((ai = args.arg(i+1)).type()) {
case LuaValue.TNUMBER:
vi = freadbytes(f, ai.toint());
break item;
case LuaValue.TSTRING:
fmt = ai.checkstring();
if (fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*') {
switch (fmt.m_bytes[fmt.m_offset+1]) {
case 'n':
vi = freadnumber(f);
break item;
case 'l':
vi = freadline(f, false);
break item;
case 'L':
vi = freadline(f, true);
break item;
case 'a':
vi = freadall(f);
break item;
}
default:
return argerror( i+1, "(invalid format)" );
}
default:
return argerror(i+1, "(invalid format)");
}
if ( (v[i++] = vi).isnil() )
if ((v[i++] = vi).isnil())
break;
}
return i==0? NIL: varargsOf(v, 0, i);
return i == 0? NIL: varargsOf(v, 0, i);
}
private static File checkfile(LuaValue val) {
File f = optfile(val);
if ( f == null )
argerror(1,"file");
checkopen( f );
if (f == null)
argerror(1, "file");
checkopen(f);
return f;
}
private static File optfile(LuaValue val) {
return (val instanceof File)? (File) val: null;
}
private static File checkopen(File file) {
if ( file.isclosed() )
if (file.isclosed())
error("attempt to use a closed file");
return file;
}
@@ -586,99 +630,115 @@ public class IoLib extends TwoArgFunction {
int len = mode.length();
for (int i = 0; i < len; i++) { // [rwa][+]?b*
char ch = mode.charAt(i);
if (i == 0 && "rwa".indexOf(ch) >= 0) continue;
if (i == 1 && ch == '+') continue;
if (i >= 1 && ch == 'b') continue;
if (i == 0 && "rwa".indexOf(ch) >= 0)
continue;
if (i == 1 && ch == '+')
continue;
if (i >= 1 && ch == 'b')
continue;
len = -1;
break;
}
if (len <= 0) argerror(2, "invalid mode: '" + mode + "'");
if (len <= 0)
argerror(2, "invalid mode: '" + mode + "'");
switch (filetype) {
case FTYPE_STDIN: return wrapStdin();
case FTYPE_STDOUT: return wrapStdout();
case FTYPE_STDERR: return wrapStderr();
case FTYPE_STDIN:
return wrapStdin();
case FTYPE_STDOUT:
return wrapStdout();
case FTYPE_STDERR:
return wrapStderr();
}
boolean isreadmode = mode.startsWith("r");
boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf('+') > 0;
boolean isbinary = mode.endsWith("b");
return openFile( filename, isreadmode, isappend, isupdate, isbinary );
return openFile(filename, isreadmode, isappend, isupdate, isbinary);
}
// ------------- file reading utilitied ------------------
public static LuaValue freadbytes(File f, int count) throws IOException {
if (count == 0) return f.eof() ? NIL : EMPTYSTRING;
if (count == 0)
return f.eof()? NIL: EMPTYSTRING;
byte[] b = new byte[count];
int r;
if ( ( r = f.read(b,0,b.length) ) < 0 )
if ((r = f.read(b, 0, b.length)) < 0)
return NIL;
return LuaString.valueUsing(b, 0, r);
}
public static LuaValue freaduntil(File f,boolean lineonly,boolean withend) throws IOException {
public static LuaValue freaduntil(File f, boolean lineonly, boolean withend) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c;
try {
if ( lineonly ) {
if (lineonly) {
loop: while ( (c = f.read()) >= 0 ) {
switch ( c ) {
case '\r': if (withend) baos.write(c); break;
case '\n': if (withend) baos.write(c); break loop;
default: baos.write(c); break;
switch (c) {
case '\r':
if (withend)
baos.write(c);
break;
case '\n':
if (withend)
baos.write(c);
break loop;
default:
baos.write(c);
break;
}
}
} else {
while ( (c = f.read()) >= 0 )
baos.write(c);
}
} catch ( EOFException e ) {
} catch (EOFException e) {
c = -1;
}
return ( c < 0 && baos.size() == 0 )?
(LuaValue) NIL:
(LuaValue) LuaString.valueUsing(baos.toByteArray());
return (c < 0 && baos.size() == 0)? (LuaValue) NIL: (LuaValue) LuaString.valueUsing(baos.toByteArray());
}
public static LuaValue freadline(File f,boolean withend) throws IOException {
return freaduntil(f,true,withend);
public static LuaValue freadline(File f, boolean withend) throws IOException {
return freaduntil(f, true, withend);
}
public static LuaValue freadall(File f) throws IOException {
int n = f.remaining();
if ( n >= 0 ) {
return n == 0 ? EMPTYSTRING : freadbytes(f, n);
if (n >= 0) {
return n == 0? EMPTYSTRING: freadbytes(f, n);
} else {
return freaduntil(f,false,false);
return freaduntil(f, false, false);
}
}
public static LuaValue freadnumber(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
freadchars(f," \t\r\n",null);
freadchars(f,"-+",baos);
freadchars(f, " \t\r\n", null);
freadchars(f, "-+", baos);
//freadchars(f,"0",baos);
//freadchars(f,"xX",baos);
freadchars(f,"0123456789",baos);
freadchars(f,".",baos);
freadchars(f,"0123456789",baos);
freadchars(f, "0123456789", baos);
freadchars(f, ".", baos);
freadchars(f, "0123456789", baos);
//freadchars(f,"eEfFgG",baos);
// freadchars(f,"+-",baos);
//freadchars(f,"0123456789",baos);
String s = baos.toString();
return s.length()>0? valueOf( Double.parseDouble(s) ): NIL;
return s.length() > 0? valueOf(Double.parseDouble(s)): NIL;
}
private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException {
int c;
while ( true ) {
c = f.peek();
if ( chars.indexOf(c) < 0 ) {
if (chars.indexOf(c) < 0) {
return;
}
f.read();
if ( baos != null )
baos.write( c );
if (baos != null)
baos.write(c);
}
}
}

View File

@@ -29,20 +29,20 @@ import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* <p>
* To provide for common implementations in JME and JSE,
* library functions are typically grouped on one or more library classes
* and an opcode per library function is defined and used to key the switch
* to the correct function within the library.
* To provide for common implementations in JME and JSE, library functions are
* typically grouped on one or more library classes and an opcode per library
* function is defined and used to key the switch to the correct function within
* the library.
* <p>
* Since lua functions can be called with too few or too many arguments,
* and there are overloaded {@link LuaValue#call()} functions with varying
* number of arguments, a Java function exposed in lua needs to handle the
* argument fixup when a function is called with a number of arguments
* differs from that expected.
* Since lua functions can be called with too few or too many arguments, and
* there are overloaded {@link LuaValue#call()} functions with varying number of
* arguments, a Java function exposed in lua needs to handle the argument fixup
* when a function is called with a number of arguments differs from that
* expected.
* <p>
* To simplify the creation of library functions,
* there are 5 direct subclasses to handle common cases based on number of
* argument values and number of return return values.
* To simplify the creation of library functions, there are 5 direct subclasses
* to handle common cases based on number of argument values and number of
* return return values.
* <ul>
* <li>{@link ZeroArgFunction}</li>
* <li>{@link OneArgFunction}</li>
@@ -51,13 +51,15 @@ import org.luaj.vm2.Varargs;
* <li>{@link VarArgFunction}</li>
* </ul>
* <p>
* To be a Java library that can be loaded via {@code require}, it should have
* a public constructor that returns a {@link LuaValue} that, when executed,
* To be a Java library that can be loaded via {@code require}, it should have a
* public constructor that returns a {@link LuaValue} that, when executed,
* initializes the library.
* <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;
* import org.luaj.vm2.lib.*;
*
@@ -85,18 +87,22 @@ import org.luaj.vm2.Varargs;
* }
* }
*}
*}</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.
*}
* </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
*
* <pre>
* {@code
* local t = require('hyperbolic')
* print( 't', t )
* print( 'hyperbolic', hyperbolic )
@@ -105,118 +111,147 @@ import org.luaj.vm2.Varargs;
* end
* print( 'sinh(.5)', hyperbolic.sinh(.5) )
* print( 'cosh(.5)', hyperbolic.cosh(.5) )
* }</pre>
* }
* </pre>
* <p>
* It should produce something like:
* <pre> {@code
*
* <pre>
* {@code
* t table: 3dbbd23f
* hyperbolic table: 3dbbd23f
* k,v cosh function: 3dbbd128
* k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953
* cosh(.5) 1.127626
* }</pre>
* }
* </pre>
* <p>
* See the source code in any of the library functions
* such as {@link BaseLib} or {@link TableLib} for other examples.
* See the source code in any of the library functions such as {@link BaseLib}
* or {@link TableLib} for other examples.
*/
abstract public class LibFunction extends LuaFunction {
/** User-defined opcode to differentiate between instances of the library function class.
/**
* User-defined opcode to differentiate between instances of the library
* function class.
* <p>
* Subclass will typicall switch on this value to provide the specific behavior for each function.
* Subclass will typicall switch on this value to provide the specific
* behavior for each function.
*/
protected int opcode;
/** The common name for this function, useful for debugging.
/**
* The common name for this function, useful for debugging.
* <p>
* Binding functions initialize this to the name to which it is bound.
*/
protected String name;
/** Default constructor for use by subclasses */
protected LibFunction() {
}
public String tojstring() {
return name != null ? "function: " + name : super.tojstring();
return name != null? "function: " + name: super.tojstring();
}
/**
* Bind a set of library functions.
* <p>
* An array of names is provided, and the first name is bound
* with opcode = 0, second with 1, etc.
* @param env The environment to apply to each bound function
* An array of names is provided, and the first name is bound with opcode =
* 0, second with 1, etc.
*
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* @param names array of String names, one for each function.
* @see #bind(LuaValue, Class, String[], int)
*/
protected void bind(LuaValue env, Class factory, String[] names ) {
bind( env, factory, names, 0 );
protected void bind(LuaValue env, Class factory, String[] names) {
bind(env, factory, names, 0);
}
/**
* Bind a set of library functions, with an offset
* <p>
* An array of names is provided, and the first name is bound
* with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc.
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* An array of names is provided, and the first name is bound with opcode =
* {@code firstopcode}, second with {@code firstopcode+1}, etc.
*
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* @param firstopcode the first opcode to use
* @see #bind(LuaValue, Class, String[])
*/
protected void bind(LuaValue env, Class factory, String[] names, int firstopcode ) {
protected void bind(LuaValue env, Class factory, String[] names, int firstopcode) {
try {
for ( int i=0, n=names.length; i<n; i++ ) {
for (int i = 0, n = names.length; i < n; i++) {
LibFunction f = (LibFunction) factory.newInstance();
f.opcode = firstopcode + i;
f.opcode = firstopcode+i;
f.name = names[i];
env.set(f.name, f);
}
} catch ( Exception e ) {
throw new LuaError( "bind failed: "+e );
} catch (Exception e) {
throw new LuaError("bind failed: " + e);
}
}
/** Java code generation utility to allocate storage for upvalue, leave it empty */
/**
* Java code generation utility to allocate storage for upvalue, leave it
* empty
*/
protected static LuaValue[] newupe() {
return new LuaValue[1];
}
/** Java code generation utility to allocate storage for upvalue, initialize with nil */
/**
* Java code generation utility to allocate storage for upvalue, initialize
* with nil
*/
protected static LuaValue[] newupn() {
return new LuaValue[] { NIL };
}
/** Java code generation utility to allocate storage for upvalue, initialize with value */
/**
* Java code generation utility to allocate storage for upvalue, initialize
* with value
*/
protected static LuaValue[] newupl(LuaValue v) {
return new LuaValue[] { v };
}
public LuaValue call() {
return argerror(1,"value expected");
return argerror(1, "value expected");
}
public LuaValue call(LuaValue a) {
return call();
}
public LuaValue call(LuaValue a, LuaValue b) {
return call(a);
}
public LuaValue call(LuaValue a, LuaValue b, LuaValue c) {
return call(a,b);
return call(a, b);
}
public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) {
return call(a,b,c);
return call(a, b, c);
}
public Varargs invoke(Varargs args) {
switch(args.narg()) {
case 0: return call();
case 1: return call(args.arg1());
case 2: return call(args.arg1(),args.arg(2));
case 3: return call(args.arg1(),args.arg(2),args.arg(3));
default: return call(args.arg1(),args.arg(2),args.arg(3),args.arg(4));
switch (args.narg()) {
case 0:
return call();
case 1:
return call(args.arg1());
case 2:
return call(args.arg1(), args.arg(2));
case 3:
return call(args.arg1(), args.arg(2), args.arg(3));
default:
return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4));
}
}
}

View File

@@ -29,13 +29,13 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code math}
* library.
* Subclass of {@link LibFunction} which implements the lua standard
* {@code math} library.
* <p>
* It contains only the math library support that is possible on JME.
* For a more complete implementation based on math functions specific to JSE
* use {@link org.luaj.vm2.lib.jse.JseMathLib}.
* In Particular the following math functions are <b>not</b> implemented by this library:
* It contains only the math library support that is possible on JME. For a more
* complete implementation based on math functions specific to JSE use
* {@link org.luaj.vm2.lib.jse.JseMathLib}. In Particular the following math
* functions are <b>not</b> implemented by this library:
* <ul>
* <li>acos</li>
* <li>asin</li>
@@ -48,60 +48,81 @@ import org.luaj.vm2.Varargs;
* </ul>
* <p>
* The implementations of {@code exp()} and {@code pow()} are constructed by
* hand for JME, so will be slower and less accurate than when executed on the JSE platform.
* hand for JME, so will be slower and less accurate than when executed on the
* JSE platform.
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
* } </pre>
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example,
* the subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will
* be included, which also includes this base functionality.
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
*
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example, the
* subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will be included, which also
* includes this base functionality.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new MathLib());
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) );
* } </pre>
* Doing so will ensure the library is properly initialized
* and loaded into the globals table.
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new MathLib());
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseMathLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib
* Reference</a>
*/
public class MathLib extends TwoArgFunction {
/** Pointer to the latest MathLib instance, used only to dispatch
* math.exp to tha correct platform math library.
/**
* Pointer to the latest MathLib instance, used only to dispatch math.exp to
* tha correct platform math library.
*/
public static MathLib MATHLIB = null;
/** Construct a MathLib, which can be initialized by calling it with a
/**
* Construct a MathLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using
* {@link #call(LuaValue, LuaValue)}. */
* {@link #call(LuaValue, LuaValue)}.
*/
public MathLib() {
MATHLIB = this;
}
/** Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment,
* adding the table to package.loaded, and returning table as the return value.
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance.
* @param env the environment to load into, typically a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable math = new LuaTable(0,30);
LuaTable math = new LuaTable(0, 30);
math.set("abs", new abs());
math.set("ceil", new ceil());
math.set("cos", new cos());
@@ -110,12 +131,12 @@ public class MathLib extends TwoArgFunction {
math.set("floor", new floor());
math.set("fmod", new fmod());
math.set("frexp", new frexp());
math.set("huge", LuaDouble.POSINF );
math.set("huge", LuaDouble.POSINF);
math.set("ldexp", new ldexp());
math.set("max", new max());
math.set("min", new min());
math.set("modf", new modf());
math.set("pi", Math.PI );
math.set("pi", Math.PI);
math.set("pow", new pow());
random r;
math.set("random", r = new random());
@@ -125,14 +146,16 @@ public class MathLib extends TwoArgFunction {
math.set("sqrt", new sqrt());
math.set("tan", new tan());
env.set("math", math);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("math", math);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("math", math);
return math;
}
abstract protected static class UnaryOp extends OneArgFunction {
public LuaValue call(LuaValue arg) {
return valueOf(call(arg.checkdouble()));
}
abstract protected double call(double d);
}
@@ -140,43 +163,74 @@ public class MathLib extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue y) {
return valueOf(call(x.checkdouble(), y.checkdouble()));
}
abstract protected double call(double x, double y);
}
static final class abs extends UnaryOp { protected double call(double d) { return Math.abs(d); } }
static final class ceil extends UnaryOp { protected double call(double d) { return Math.ceil(d); } }
static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } }
static final class deg extends UnaryOp { protected double call(double d) { return Math.toDegrees(d); } }
static final class floor extends UnaryOp { protected double call(double d) { return Math.floor(d); } }
static final class rad extends UnaryOp { protected double call(double d) { return Math.toRadians(d); } }
static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } }
static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } }
static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } }
static final class abs extends UnaryOp {
protected double call(double d) { return Math.abs(d); }
}
static final class ceil extends UnaryOp {
protected double call(double d) { return Math.ceil(d); }
}
static final class cos extends UnaryOp {
protected double call(double d) { return Math.cos(d); }
}
static final class deg extends UnaryOp {
protected double call(double d) { return Math.toDegrees(d); }
}
static final class floor extends UnaryOp {
protected double call(double d) { return Math.floor(d); }
}
static final class rad extends UnaryOp {
protected double call(double d) { return Math.toRadians(d); }
}
static final class sin extends UnaryOp {
protected double call(double d) { return Math.sin(d); }
}
static final class sqrt extends UnaryOp {
protected double call(double d) { return Math.sqrt(d); }
}
static final class tan extends UnaryOp {
protected double call(double d) { return Math.tan(d); }
}
static final class exp extends UnaryOp {
final MathLib mathlib;
exp(MathLib mathlib) {
this.mathlib = mathlib;
}
protected double call(double d) {
return mathlib.dpow_lib(Math.E,d);
return mathlib.dpow_lib(Math.E, d);
}
}
static final class fmod extends TwoArgFunction {
public LuaValue call(LuaValue xv, LuaValue yv) {
if (xv.islong() && yv.islong()) {
return valueOf(xv.tolong() % yv.tolong());
return valueOf(xv.tolong()%yv.tolong());
}
return valueOf(xv.checkdouble() % yv.checkdouble());
return valueOf(xv.checkdouble()%yv.checkdouble());
}
}
static final class ldexp extends BinaryOp {
protected double call(double x, double y) {
// This is the behavior on os-x, windows differs in rounding behavior.
return x * Double.longBitsToDouble((((long) y) + 1023) << 52);
return x*Double.longBitsToDouble((((long) y)+1023)<<52);
}
}
static final class pow extends BinaryOp {
protected double call(double x, double y) {
return MathLib.dpow_default(x, y);
@@ -186,117 +240,127 @@ public class MathLib extends TwoArgFunction {
static class frexp extends VarArgFunction {
public Varargs invoke(Varargs args) {
double x = args.checkdouble(1);
if ( x == 0 ) return varargsOf(ZERO,ZERO);
long bits = Double.doubleToLongBits( x );
double m = ((bits & (~(-1L<<52))) + (1L<<52)) * ((bits >= 0)? (.5 / (1L<<52)): (-.5 / (1L<<52)));
double e = (((int) (bits >> 52)) & 0x7ff) - 1022;
return varargsOf( valueOf(m), valueOf(e) );
if (x == 0)
return varargsOf(ZERO, ZERO);
long bits = Double.doubleToLongBits(x);
double m = ((bits & (~(-1L<<52)))+(1L<<52))*((bits >= 0)? (.5/(1L<<52)): (-.5/(1L<<52)));
double e = (((int) (bits>>52)) & 0x7ff)-1022;
return varargsOf(valueOf(m), valueOf(e));
}
}
static class max extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) {
for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checkvalue(i);
if (m.lt_b(v)) m = v;
if (m.lt_b(v))
m = v;
}
return m;
}
}
static class min extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) {
for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checkvalue(i);
if (v.lt_b(m)) m = v;
if (v.lt_b(m))
m = v;
}
return m;
}
}
static class modf extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaValue n = args.arg1();
/* number is its own integer part, no fractional part */
if (n.islong()) return varargsOf(n, valueOf(0.0));
if (n.islong())
return varargsOf(n, valueOf(0.0));
double x = n.checkdouble();
/* integer part (rounds toward zero) */
double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x );
double intPart = (x > 0)? Math.floor(x): Math.ceil(x);
/* fractional part (test needed for inf/-inf) */
double fracPart = x == intPart ? 0.0 : x - intPart;
return varargsOf( valueOf(intPart), valueOf(fracPart) );
double fracPart = x == intPart? 0.0: x-intPart;
return varargsOf(valueOf(intPart), valueOf(fracPart));
}
}
static class random extends LibFunction {
Random random = new Random();
public LuaValue call() {
return valueOf( random.nextDouble() );
return valueOf(random.nextDouble());
}
public LuaValue call(LuaValue a) {
int m = a.checkint();
if (m<1) argerror(1, "interval is empty");
return valueOf( 1 + random.nextInt(m) );
if (m < 1)
argerror(1, "interval is empty");
return valueOf(1+random.nextInt(m));
}
public LuaValue call(LuaValue a, LuaValue b) {
int m = a.checkint();
int n = b.checkint();
if (n<m) argerror(2, "interval is empty");
return valueOf( m + random.nextInt(n+1-m) );
if (n < m)
argerror(2, "interval is empty");
return valueOf(m+random.nextInt(n+1-m));
}
}
static class randomseed extends OneArgFunction {
final random random;
randomseed(random random) {
this.random = random;
}
public LuaValue call(LuaValue arg) {
long seed = arg.checklong();
random.random = new Random(seed);
return NONE;
}
}
/** compute power using installed math library, or default if there is no math library installed */
/**
* compute power using installed math library, or default if there is no
* math library installed
*/
public static LuaValue dpow(double a, double b) {
return LuaDouble.valueOf(
MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b) );
return LuaDouble.valueOf(MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b));
}
public static double dpow_d(double a, double b) {
return MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b);
return MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b);
}
/**
* Hook to override default dpow behavior with faster implementation.
*/
public double dpow_lib(double a, double b) {
return dpow_default(a,b);
return dpow_default(a, b);
}
/**
* Default JME version computes using longhand heuristics.
*/
protected static double dpow_default(double a, double b) {
if ( b < 0 )
return 1 / dpow_default( a, -b );
if (b < 0)
return 1/dpow_default(a, -b);
double p = 1;
int whole = (int) b;
for ( double v=a; whole > 0; whole>>=1, v*=v )
if ( (whole & 1) != 0 )
for (double v = a; whole > 0; whole >>= 1, v *= v)
if ((whole & 1) != 0)
p *= v;
if ( (b -= whole) > 0 ) {
int frac = (int) (0x10000 * b);
for ( ; (frac&0xffff)!=0; frac<<=1 ) {
if ((b -= whole) > 0) {
int frac = (int) (0x10000*b);
for (; (frac & 0xffff) != 0; frac <<= 1) {
a = Math.sqrt(a);
if ( (frac & 0x8000) != 0 )
if ((frac & 0x8000) != 0)
p *= a;
}
}

View File

@@ -24,21 +24,23 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take one argument and
* return one value.
/**
* Abstract base class for Java function implementations that take one argument
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete this class,
* simplifying development.
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
* are routed through this method by this class,
* Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete
* this class, simplifying development. All other uses of {@link #call()},
* {@link #invoke(Varargs)},etc, are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required.
* <p>
* If more than one argument are required, or no arguments are required,
* or variable argument or variable return values,
* then use one of the related function
* {@link ZeroArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* If more than one argument are required, or no arguments are required, or
* variable argument or variable return values, then use one of the related
* function {@link ZeroArgFunction}, {@link TwoArgFunction},
* {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and library functions.
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue)
* @see LibFunction
* @see ZeroArgFunction
@@ -49,11 +51,11 @@ import org.luaj.vm2.Varargs;
abstract public class OneArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg);
/** Default constructor */
public OneArgFunction() {
}
public final LuaValue call() {
return call(NIL);
}
@@ -69,4 +71,4 @@ abstract public class OneArgFunction extends LibFunction {
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1());
}
}
}

View File

@@ -32,21 +32,21 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library.
* Subclass of {@link LibFunction} which implements the standard lua {@code os}
* library.
* <p>
* It is a usable base with simplified stub functions
* for library functions that cannot be implemented uniformly
* on Jse and Jme.
* It is a usable base with simplified stub functions for library functions that
* cannot be implemented uniformly on Jse and Jme.
* <p>
* This can be installed as-is on either platform, or extended
* and refined to be used in a complete Jse implementation.
* This can be installed as-is on either platform, or extended and refined to be
* used in a complete Jse implementation.
* <p>
* Because the nature of the {@code os} library is to encapsulate
* os-specific features, the behavior of these functions varies considerably
* from their counterparts in the C platform.
* Because the nature of the {@code os} library is to encapsulate os-specific
* features, the behavior of these functions varies considerably from their
* counterparts in the C platform.
* <p>
* The following functions have limited implementations of features
* that are not supported well on Jme:
* The following functions have limited implementations of features that are not
* supported well on Jme:
* <ul>
* <li>{@code execute()}</li>
* <li>{@code remove()}</li>
@@ -55,33 +55,46 @@ import org.luaj.vm2.Varargs;
* </ul>
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("os").get("time").call() );
* } </pre>
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib} library will be loaded, which will include
* the base functionality provided by this class.
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib}
* library will be loaded, which will include the base functionality provided by
* this class.
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* System.out.println( globals.get("os").get("time").call() );
* } </pre>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p>
* @see LibFunction
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JseOsLib
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
* @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
*/
public class OsLib extends TwoArgFunction {
public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp";
public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp";
private static final int CLOCK = 0;
private static final int DATE = 1;
@@ -95,36 +108,29 @@ public class OsLib extends TwoArgFunction {
private static final int TIME = 9;
private static final int TMPNAME = 10;
private static final String[] NAMES = {
"clock",
"date",
"difftime",
"execute",
"exit",
"getenv",
"remove",
"rename",
"setlocale",
"time",
"tmpname",
};
private static final long t0 = System.currentTimeMillis();
private static long tmpnames = t0;
private static final String[] NAMES = { "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
"rename", "setlocale", "time", "tmpname", };
private static final long t0 = System.currentTimeMillis();
private static long tmpnames = t0;
protected Globals globals;
/**
* Create and OsLib instance.
*/
public OsLib() {
}
/** Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment,
* adding the table to package.loaded, and returning table as the return value.
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance.
* @param env the environment to load into, typically a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
@@ -132,7 +138,8 @@ public class OsLib extends TwoArgFunction {
for (int i = 0; i < NAMES.length; ++i)
os.set(NAMES[i], new OsLibFunc(i, NAMES[i]));
env.set("os", os);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("os", os);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("os", os);
return os;
}
@@ -141,9 +148,10 @@ public class OsLib extends TwoArgFunction {
this.opcode = opcode;
this.name = name;
}
public Varargs invoke(Varargs args) {
try {
switch ( opcode ) {
switch (opcode) {
case CLOCK:
return valueOf(clock());
case DATE: {
@@ -151,7 +159,7 @@ public class OsLib extends TwoArgFunction {
double t = args.isnumber(2)? args.todouble(2): time(null);
if (s.equals("*t")) {
Calendar d = Calendar.getInstance();
d.setTime(new Date((long)(t*1000)));
d.setTime(new Date((long) (t*1000)));
LuaTable tbl = LuaValue.tableOf();
tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR)));
tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1));
@@ -164,10 +172,10 @@ public class OsLib extends TwoArgFunction {
tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
return tbl;
}
return valueOf( date(s, t==-1? time(null): t) );
return valueOf(date(s, t == -1? time(null): t));
}
case DIFFTIME:
return valueOf(difftime(args.checkdouble(1),args.checkdouble(2)));
return valueOf(difftime(args.checkdouble(1), args.checkdouble(2)));
case EXECUTE:
return execute(args.optjstring(1, null));
case EXIT:
@@ -175,7 +183,7 @@ public class OsLib extends TwoArgFunction {
return NONE;
case GETENV: {
final String val = getenv(args.checkjstring(1));
return val!=null? valueOf(val): NIL;
return val != null? valueOf(val): NIL;
}
case REMOVE:
remove(args.checkjstring(1));
@@ -184,8 +192,8 @@ public class OsLib extends TwoArgFunction {
rename(args.checkjstring(1), args.checkjstring(2));
return LuaValue.TRUE;
case SETLOCALE: {
String s = setlocale(args.optjstring(1,null), args.optjstring(2, "all"));
return s!=null? valueOf(s): NIL;
String s = setlocale(args.optjstring(1, null), args.optjstring(2, "all"));
return s != null? valueOf(s): NIL;
}
case TIME:
return valueOf(time(args.opttable(1, null)));
@@ -193,78 +201,79 @@ public class OsLib extends TwoArgFunction {
return valueOf(tmpname());
}
return NONE;
} catch ( IOException e ) {
} catch (IOException e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
}
/**
* @return an approximation of the amount in seconds of CPU time used by
* the program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
* @return an approximation of the amount in seconds of CPU time used by the
* program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
*/
protected double clock() {
return (System.currentTimeMillis()-t0) / 1000.;
return (System.currentTimeMillis()-t0)/1000.;
}
/**
* Returns the number of seconds from time t1 to time t2.
* In POSIX, Windows, and some other systems, this value is exactly t2-t1.
* Returns the number of seconds from time t1 to time t2. In POSIX, Windows,
* and some other systems, this value is exactly t2-t1.
*
* @param t2
* @param t1
* @return diffeence in time values, in seconds
*/
protected double difftime(double t2, double t1) {
return t2 - t1;
return t2-t1;
}
/**
* If the time argument is present, this is the time to be formatted
* (see the os.time function for a description of this value).
* Otherwise, date formats the current time.
* If the time argument is present, this is the time to be formatted (see
* the os.time function for a description of this value). Otherwise, date
* formats the current time.
*
* Date returns the date as a string,
* formatted according to the same rules as ANSII strftime, but without
* support for %g, %G, or %V.
* Date returns the date as a string, formatted according to the same rules
* as ANSII strftime, but without support for %g, %G, or %V.
*
* When called without arguments, date returns a reasonable date and
* time representation that depends on the host system and on the
* current locale (that is, os.date() is equivalent to os.date("%c")).
* When called without arguments, date returns a reasonable date and time
* representation that depends on the host system and on the current locale
* (that is, os.date() is equivalent to os.date("%c")).
*
* @param format
* @param time time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time,
* formatted according to the given string format.
* @param time time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time, formatted
* according to the given string format.
*/
public String date(String format, double time) {
Calendar d = Calendar.getInstance();
d.setTime(new Date((long)(time*1000)));
d.setTime(new Date((long) (time*1000)));
if (format.startsWith("!")) {
time -= timeZoneOffset(d);
d.setTime(new Date((long)(time*1000)));
d.setTime(new Date((long) (time*1000)));
format = format.substring(1);
}
byte[] fmt = format.getBytes();
final int n = fmt.length;
Buffer result = new Buffer(n);
byte c;
for ( int i = 0; i < n; ) {
switch ( c = fmt[i++ ] ) {
for (int i = 0; i < n;) {
switch (c = fmt[i++]) {
case '\n':
result.append( "\n" );
result.append("\n");
break;
default:
result.append( c );
result.append(c);
break;
case '%':
if (i >= n) break;
switch ( c = fmt[i++ ] ) {
if (i >= n)
break;
switch (c = fmt[i++]) {
default:
LuaValue.argerror(1, "invalid conversion specifier '%"+c+"'");
LuaValue.argerror(1, "invalid conversion specifier '%" + c + "'");
break;
case '%':
result.append( (byte)'%' );
result.append((byte) '%');
break;
case 'a':
result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
@@ -292,7 +301,7 @@ public class OsLib extends TwoArgFunction {
break;
case 'j': { // day of year.
Calendar y0 = beginningOfYear(d);
int dayOfYear = (int) ((d.getTime().getTime() - y0.getTime().getTime()) / (24 * 3600L * 1000L));
int dayOfYear = (int) ((d.getTime().getTime()-y0.getTime().getTime())/(24*3600L*1000L));
result.append(String.valueOf(1001+dayOfYear).substring(1));
break;
}
@@ -330,11 +339,11 @@ public class OsLib extends TwoArgFunction {
result.append(String.valueOf(d.get(Calendar.YEAR)));
break;
case 'z': {
final int tzo = timeZoneOffset(d) / 60;
final int tzo = timeZoneOffset(d)/60;
final int a = Math.abs(tzo);
final String h = String.valueOf(100 + a / 60).substring(1);
final String m = String.valueOf(100 + a % 60).substring(1);
result.append((tzo>=0? "+": "-") + h + m);
final String h = String.valueOf(100+a/60).substring(1);
final String m = String.valueOf(100+a%60).substring(1);
result.append((tzo >= 0? "+": "-")+h+m);
break;
}
}
@@ -342,11 +351,14 @@ public class OsLib extends TwoArgFunction {
}
return result.tojstring();
}
private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday" };
private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December" };
private Calendar beginningOfYear(Calendar d) {
Calendar y0 = Calendar.getInstance();
@@ -359,42 +371,35 @@ public class OsLib extends TwoArgFunction {
y0.set(Calendar.MILLISECOND, 0);
return y0;
}
private int weekNumber(Calendar d, int startDay) {
Calendar y0 = beginningOfYear(d);
y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7);
Calendar y0 = beginningOfYear(d);
y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
if (y0.after(d)) {
y0.set(Calendar.YEAR, y0.get(Calendar.YEAR) - 1);
y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7);
y0.set(Calendar.YEAR, y0.get(Calendar.YEAR)-1);
y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
}
long dt = d.getTime().getTime() - y0.getTime().getTime();
return 1 + (int) (dt / (7L * 24L * 3600L * 1000L));
long dt = d.getTime().getTime()-y0.getTime().getTime();
return 1+(int) (dt/(7L*24L*3600L*1000L));
}
private int timeZoneOffset(Calendar d) {
int localStandarTimeMillis = (
d.get(Calendar.HOUR_OF_DAY) * 3600 +
d.get(Calendar.MINUTE) * 60 +
d.get(Calendar.SECOND)) * 1000;
return d.getTimeZone().getOffset(
1,
d.get(Calendar.YEAR),
d.get(Calendar.MONTH),
d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.DAY_OF_WEEK),
localStandarTimeMillis) / 1000;
int localStandarTimeMillis = (d.get(Calendar.HOUR_OF_DAY)*3600+d.get(Calendar.MINUTE)*60+d.get(Calendar.SECOND))
*1000;
return d.getTimeZone().getOffset(1, d.get(Calendar.YEAR), d.get(Calendar.MONTH), d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.DAY_OF_WEEK), localStandarTimeMillis)/1000;
}
private boolean isDaylightSavingsTime(Calendar d) {
return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000;
return timeZoneOffset(d) != d.getTimeZone().getRawOffset()/1000;
}
/**
* This function is equivalent to the C function system.
* It passes command to be executed by an operating system shell.
* It returns a status code, which is system-dependent.
* If command is absent, then it returns nonzero if a shell
* is available and zero otherwise.
* This function is equivalent to the C function system. It passes command
* to be executed by an operating system shell. It returns a status code,
* which is system-dependent. If command is absent, then it returns nonzero
* if a shell is available and zero otherwise.
*
* @param command command to pass to the system
*/
protected Varargs execute(String command) {
@@ -402,7 +407,9 @@ public class OsLib extends TwoArgFunction {
}
/**
* Calls the C function exit, with an optional code, to terminate the host program.
* Calls the C function exit, with an optional code, to terminate the host
* program.
*
* @param code
*/
protected void exit(int code) {
@@ -410,19 +417,19 @@ public class OsLib extends TwoArgFunction {
}
/**
* Returns the value of the process environment variable varname,
* or the System property value for varname,
* or null if the variable is not defined in either environment.
* Returns the value of the process environment variable varname, or the
* System property value for varname, or null if the variable is not defined
* in either environment.
*
* The default implementation, which is used by the JmePlatform,
* only queryies System.getProperty().
* The default implementation, which is used by the JmePlatform, only
* queryies System.getProperty().
*
* The JsePlatform overrides this behavior and returns the
* environment variable value using System.getenv() if it exists,
* or the System property value if it does not.
* The JsePlatform overrides this behavior and returns the environment
* variable value using System.getenv() if it exists, or the System property
* value if it does not.
*
* A SecurityException may be thrown if access is not allowed for 'varname'.
*
* A SecurityException may be thrown if access is not allowed
* for 'varname'.
* @param varname
* @return String value, or null if not defined
*/
@@ -431,57 +438,57 @@ public class OsLib extends TwoArgFunction {
}
/**
* Deletes the file or directory with the given name.
* Directories must be empty to be removed.
* If this function fails, it throws and IOException
* Deletes the file or directory with the given name. Directories must be
* empty to be removed. If this function fails, it throws and IOException
*
* @param filename
* @throws IOException if it fails
*/
protected void remove(String filename) throws IOException {
throw new IOException( "not implemented" );
throw new IOException("not implemented");
}
/**
* Renames file or directory named oldname to newname.
* If this function fails,it throws and IOException
* Renames file or directory named oldname to newname. If this function
* fails,it throws and IOException
*
* @param oldname old file name
* @param newname new file name
* @throws IOException if it fails
*/
protected void rename(String oldname, String newname) throws IOException {
throw new IOException( "not implemented" );
throw new IOException("not implemented");
}
/**
* Sets the current locale of the program. locale is a string specifying
* a locale; category is an optional string describing which category to change:
* "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category
* is "all".
* Sets the current locale of the program. locale is a string specifying a
* locale; category is an optional string describing which category to
* change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the
* default category is "all".
*
* If locale is the empty string, the current locale is set to an implementation-
* defined native locale. If locale is the string "C", the current locale is set
* to the standard C locale.
* If locale is the empty string, the current locale is set to an
* implementation- defined native locale. If locale is the string "C", the
* current locale is set to the standard C locale.
*
* When called with null as the first argument, this function only returns the
* name of the current locale for the given category.
* When called with null as the first argument, this function only returns
* the name of the current locale for the given category.
*
* @param locale
* @param category
* @return the name of the new locale, or null if the request
* cannot be honored.
* @return the name of the new locale, or null if the request cannot be
* honored.
*/
protected String setlocale(String locale, String category) {
return "C";
}
/**
* Returns the current time when called without arguments,
* or a time representing the date and time specified by the given table.
* This table must have fields year, month, and day,
* and may have fields hour, min, sec, and isdst
* (for a description of these fields, see the os.date function).
* Returns the current time when called without arguments, or a time
* representing the date and time specified by the given table. This table
* must have fields year, month, and day, and may have fields hour, min,
* sec, and isdst (for a description of these fields, see the os.date
* function).
*
* @param table
* @return long value for the time
*/
@@ -500,7 +507,7 @@ public class OsLib extends TwoArgFunction {
c.set(Calendar.MILLISECOND, 0);
d = c.getTime();
}
return d.getTime() / 1000.;
return d.getTime()/1000.;
}
/**
@@ -508,16 +515,16 @@ public class OsLib extends TwoArgFunction {
* The file must be explicitly opened before its use and explicitly removed
* when no longer needed.
*
* On some systems (POSIX), this function also creates a file with that name,
* to avoid security risks. (Someone else might create the file with wrong
* permissions in the time between getting the name and creating the file.)
* You still have to open the file to use it and to remove it (even if you
* do not use it).
* On some systems (POSIX), this function also creates a file with that
* name, to avoid security risks. (Someone else might create the file with
* wrong permissions in the time between getting the name and creating the
* file.) You still have to open the file to use it and to remove it (even
* if you do not use it).
*
* @return String filename to use
*/
protected String tmpname() {
synchronized ( OsLib.class ) {
synchronized (OsLib.class) {
return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX;
}
}

View File

@@ -31,56 +31,73 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard package and module
* library functions.
* Subclass of {@link LibFunction} which implements the lua standard package and
* module library functions.
*
* <h3>Lua Environment Variables</h3>
* The following variables are available to lua scrips when this library has been loaded:
* <h3>Lua Environment Variables</h3> The following variables are available to
* lua scrips when this library has been loaded:
* <ul>
* <li><code>"package.loaded"</code> Lua table of loaded modules.
* <li><code>"package.path"</code> Search path for lua scripts.
* <li><code>"package.preload"</code> Lua table of uninitialized preload functions.
* <li><code>"package.searchers"</code> Lua table of functions that search for object to load.
* <li><code>"package.preload"</code> Lua table of uninitialized preload
* functions.
* <li><code>"package.searchers"</code> Lua table of functions that search for
* object to load.
* </ul>
*
* <h3>Java Environment Variables</h3>
* These Java environment variables affect the library behavior:
* <h3>Java Environment Variables</h3> These Java environment variables affect
* the library behavior:
* <ul>
* <li><code>"luaj.package.path"</code> Initial value for <code>"package.path"</code>. Default value is <code>"?.lua"</code>
* <li><code>"luaj.package.path"</code> Initial value for
* <code>"package.path"</code>. Default value is <code>"?.lua"</code>
* </ul>
*
* <h3>Loading</h3>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* <h3>Loading</h3> Typically, this library is included as part of a call to
* either {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("require").call"foo") );
* } </pre>
* }
* </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* System.out.println( globals.get("require").call("foo") );
* } </pre>
* <h3>Limitations</h3>
* This library has been implemented to match as closely as possible the behavior in the corresponding library in C.
* However, the default filesystem search semantics are different and delegated to the bas library
* as outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib} documentation.
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* System.out.println(globals.get("require").call("foo"));
* }
* </pre>
*
* <h3>Limitations</h3> This library has been implemented to match as closely as
* possible the behavior in the corresponding library in C. However, the default
* filesystem search semantics are different and delegated to the bas library as
* outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib}
* documentation.
* <p>
*
* @see LibFunction
* @see BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package
* Lib Reference</a>
*/
public class PackageLib extends TwoArgFunction {
/** The default value to use for package.path. This can be set with the system property
* <code>"luaj.package.path"</code>, and is <code>"?.lua"</code> by default. */
/**
* The default value to use for package.path. This can be set with the
* system property <code>"luaj.package.path"</code>, and is
* <code>"?.lua"</code> by default.
*/
public static final String DEFAULT_LUA_PATH;
static {
String path = null;
@@ -95,40 +112,49 @@ public class PackageLib extends TwoArgFunction {
DEFAULT_LUA_PATH = path;
}
static final LuaString _LOADED = valueOf("loaded");
private static final LuaString _LOADLIB = valueOf("loadlib");
static final LuaString _PRELOAD = valueOf("preload");
static final LuaString _PATH = valueOf("path");
static final LuaString _SEARCHPATH = valueOf("searchpath");
static final LuaString _SEARCHERS = valueOf("searchers");
static final LuaString _LOADED = valueOf("loaded");
private static final LuaString _LOADLIB = valueOf("loadlib");
static final LuaString _PRELOAD = valueOf("preload");
static final LuaString _PATH = valueOf("path");
static final LuaString _SEARCHPATH = valueOf("searchpath");
static final LuaString _SEARCHERS = valueOf("searchers");
/** The globals that were used to load this library. */
Globals globals;
/** The table for this package. */
LuaTable package_;
/** Loader that loads from {@code preload} table if found there */
public preload_searcher preload_searcher;
/** Loader that loads as a lua script using the lua path currently in {@link path} */
/**
* Loader that loads as a lua script using the lua path currently in
* {@link path}
*/
public lua_searcher lua_searcher;
/** Loader that loads as a Java class. Class must have public constructor and be a LuaValue. */
/**
* Loader that loads as a Java class. Class must have public constructor and
* be a LuaValue.
*/
public java_searcher java_searcher;
private static final LuaString _SENTINEL = valueOf("\u0001");
private static final LuaString _SENTINEL = valueOf("\u0001");
private static final String FILE_SEP = System.getProperty("file.separator");
public PackageLib() {}
/** Perform one-time initialization on the library by adding package functions
* to the supplied environment, and returning it as the return value.
* It also creates the package.preload and package.loaded tables for use by
* other libraries.
/**
* Perform one-time initialization on the library by adding package
* functions to the supplied environment, and returning it as the return
* value. It also creates the package.preload and package.loaded tables for
* use by other libraries.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance.
* @param env the environment to load into, typically a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
@@ -141,8 +167,8 @@ public class PackageLib extends TwoArgFunction {
package_.set(_SEARCHPATH, new searchpath());
LuaTable searchers = new LuaTable();
searchers.set(1, preload_searcher = new preload_searcher());
searchers.set(2, lua_searcher = new lua_searcher());
searchers.set(3, java_searcher = new java_searcher());
searchers.set(2, lua_searcher = new lua_searcher());
searchers.set(3, java_searcher = new java_searcher());
package_.set(_SEARCHERS, searchers);
package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n");
package_.get(_LOADED).set("package", package_);
@@ -150,94 +176,100 @@ public class PackageLib extends TwoArgFunction {
globals.package_ = this;
return env;
}
/** Allow packages to mark themselves as loaded */
public void setIsLoaded(String name, LuaTable value) {
package_.get(_LOADED).set(name, value);
}
/** Set the lua path used by this library instance to a new value.
* Merely sets the value of {@link path} to be used in subsequent searches. */
public void setLuaPath( String newLuaPath ) {
/**
* Set the lua path used by this library instance to a new value. Merely
* sets the value of {@link path} to be used in subsequent searches.
*/
public void setLuaPath(String newLuaPath) {
package_.set(_PATH, LuaValue.valueOf(newLuaPath));
}
public String tojstring() {
return "package";
}
// ======================== Package loading =============================
/**
* 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
* stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.
* 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.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.
* 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 Java loader using
* the classpath, using the public default constructor, and casting the instance to LuaFunction.
* 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 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 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].
* 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.
* 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 ) {
public LuaValue call(LuaValue arg) {
LuaString name = arg.checkstring();
LuaValue loaded = package_.get(_LOADED);
LuaValue result = loaded.get(name);
if ( result.toboolean() ) {
if ( result == _SENTINEL )
error("loop or previous error loading module '"+name+"'");
if (result.toboolean()) {
if (result == _SENTINEL)
error("loop or previous error loading module '" + name + "'");
return result;
}
/* else must load it; iterate over available loaders */
LuaTable tbl = package_.get(_SEARCHERS).checktable();
StringBuffer sb = new StringBuffer();
Varargs loader = null;
for ( int i=1; true; i++ ) {
for (int i = 1; true; i++) {
LuaValue searcher = tbl.get(i);
if ( searcher.isnil() ) {
error( "module '"+name+"' not found: "+name+sb );
}
/* call loader with module name as argument */
if (searcher.isnil()) {
error("module '" + name + "' not found: " + name + sb);
}
/* call loader with module name as argument */
loader = searcher.invoke(name);
if ( loader.isfunction(1) )
if (loader.isfunction(1))
break;
if ( loader.isstring(1) )
sb.append( loader.tojstring(1) );
if (loader.isstring(1))
sb.append(loader.tojstring(1));
}
// load the module using the loader
loaded.set(name, _SENTINEL);
result = loader.arg1().call(name, loader.arg(2));
if ( ! result.isnil() )
loaded.set( name, result );
else if ( (result = loaded.get(name)) == _SENTINEL )
loaded.set( name, result = LuaValue.TRUE );
if (!result.isnil())
loaded.set(name, result);
else if ((result = loaded.get(name)) == _SENTINEL)
loaded.set(name, result = LuaValue.TRUE);
return result;
}
}
public static class loadlib extends VarArgFunction {
public Varargs invoke( Varargs args ) {
public Varargs invoke(Varargs args) {
args.checkstring(1);
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
}
@@ -247,36 +279,34 @@ public class PackageLib extends TwoArgFunction {
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
LuaValue val = package_.get(_PRELOAD).get(name);
return val.isnil()?
valueOf("\n\tno field package.preload['"+name+"']"):
val;
return val.isnil()? valueOf("\n\tno field package.preload['" + name + "']"): val;
}
}
public class lua_searcher extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1);
// get package path
LuaValue path = package_.get(_PATH);
if ( ! path.isstring() )
if (!path.isstring())
return valueOf("package.path is not a string");
// get the searchpath function.
Varargs v = package_.get(_SEARCHPATH).invoke(varargsOf(name, path));
// Did we get a result?
if (!v.isstring(1))
return v.arg(2).tostring();
LuaString filename = v.arg1().strvalue();
// Try to load the file.
v = globals.loadfile(filename.tojstring());
if ( v.arg1().isfunction() )
if (v.arg1().isfunction())
return LuaValue.varargsOf(v.arg1(), filename);
// report error
return varargsOf(NIL, valueOf("'"+filename+"': "+v.arg(2).tojstring()));
return varargsOf(NIL, valueOf("'" + filename + "': " + v.arg(2).tojstring()));
}
}
@@ -286,90 +316,91 @@ public class PackageLib extends TwoArgFunction {
String path = args.checkjstring(2);
String sep = args.optjstring(3, ".");
String rep = args.optjstring(4, FILE_SEP);
// check the path elements
int e = -1;
int n = path.length();
StringBuffer sb = null;
name = name.replace(sep.charAt(0), rep.charAt(0));
while ( e < n ) {
// find next template
int b = e+1;
e = path.indexOf(';',b);
if ( e < 0 )
e = path.indexOf(';', b);
if (e < 0)
e = path.length();
String template = path.substring(b,e);
String template = path.substring(b, e);
// create filename
int q = template.indexOf('?');
String filename = template;
if ( q >= 0 ) {
filename = template.substring(0,q) + name + template.substring(q+1);
if (q >= 0) {
filename = template.substring(0, q)+name+template.substring(q+1);
}
// try opening the file
InputStream is = globals.finder.findResource(filename);
if (is != null) {
try { is.close(); } catch ( java.io.IOException ioe ) {}
try {
is.close();
} catch (java.io.IOException ioe) {
}
return valueOf(filename);
}
// report error
if ( sb == null )
if (sb == null)
sb = new StringBuffer();
sb.append( "\n\t"+filename );
sb.append("\n\t" + filename);
}
return varargsOf(NIL, valueOf(sb.toString()));
}
}
public class java_searcher extends VarArgFunction {
public Varargs invoke(Varargs args) {
String name = args.checkjstring(1);
String classname = toClassname( name );
String classname = toClassname(name);
Class c = null;
LuaValue v = null;
try {
c = Class.forName(classname);
v = (LuaValue) c.newInstance();
if (v.isfunction())
((LuaFunction)v).initupvalue1(globals);
((LuaFunction) v).initupvalue1(globals);
return varargsOf(v, globals);
} catch ( ClassNotFoundException cnfe ) {
return valueOf("\n\tno class '"+classname+"'" );
} catch ( Exception e ) {
return valueOf("\n\tjava load failed on '"+classname+"', "+e );
} catch (ClassNotFoundException cnfe) {
return valueOf("\n\tno class '" + classname + "'");
} catch (Exception e) {
return valueOf("\n\tjava load failed on '" + classname + "', " + e);
}
}
}
/** Convert lua filename to valid class name */
public static final String toClassname( String filename ) {
int n=filename.length();
int j=n;
if ( filename.endsWith(".lua") )
public static final String toClassname(String filename) {
int n = filename.length();
int j = n;
if (filename.endsWith(".lua"))
j -= 4;
for ( int k=0; k<j; k++ ) {
for (int k = 0; k < j; k++) {
char c = filename.charAt(k);
if ( (!isClassnamePart(c)) || (c=='/') || (c=='\\') ) {
if ((!isClassnamePart(c)) || (c == '/') || (c == '\\')) {
StringBuffer sb = new StringBuffer(j);
for ( int i=0; i<j; i++ ) {
for (int i = 0; i < j; i++) {
c = filename.charAt(i);
sb.append(
(isClassnamePart(c))? c:
((c=='/') || (c=='\\'))? '.': '_' );
sb.append((isClassnamePart(c))? c: ((c == '/') || (c == '\\'))? '.': '_');
}
return sb.toString();
}
}
return n==j? filename: filename.substring(0,j);
return n == j? filename: filename.substring(0, j);
}
private static final boolean isClassnamePart(char c) {
if ( (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') )
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
return true;
switch ( c ) {
switch (c) {
case '.':
case '$':
case '_':

View File

@@ -25,35 +25,36 @@ import java.io.InputStream;
import org.luaj.vm2.Globals;
/**
* Interface for opening application resource files such as scripts sources.
/**
* Interface for opening application resource files such as scripts sources.
* <p>
* This is used by required to load files that are part of
* the application, and implemented by BaseLib
* for both the Jme and Jse platforms.
* This is used by required to load files that are part of the application, and
* implemented by BaseLib for both the Jme and Jse platforms.
* <p>
* The Jme version of base lib {@link BaseLib}
* implements {@link Globals#finder} via {@link Class#getResourceAsStream(String)},
* while the Jse version {@link org.luaj.vm2.lib.jse.JseBaseLib} implements it using {@link java.io.File#File(String)}.
* The Jme version of base lib {@link BaseLib} implements {@link Globals#finder}
* via {@link Class#getResourceAsStream(String)}, while the Jse version
* {@link org.luaj.vm2.lib.jse.JseBaseLib} implements it using
* {@link java.io.File#File(String)}.
* <p>
* The io library does not use this API for file manipulation.
* <p>
*
* @see BaseLib
* @see Globals#finder
* @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jse.JsePlatform
*/
public interface ResourceFinder {
/**
/**
* Try to open a file, or return null if not found.
*
* @see org.luaj.vm2.lib.BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib
*
* @param filename
* @return InputStream, or null if not found.
* @return InputStream, or null if not found.
*/
public InputStream findResource( String filename );
}
public InputStream findResource(String filename);
}

File diff suppressed because it is too large Load Diff

View File

@@ -26,40 +26,56 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the lua standard {@code table}
* library.
* Subclass of {@link LibFunction} which implements the lua standard
* {@code table} library.
*
* <p>
* Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
* } </pre>
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p>
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new TableLib());
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) );
* } </pre>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new TableLib());
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C.
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table
* Lib Reference</a>
*/
public class TableLib extends TwoArgFunction {
/** Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment,
* adding the table to package.loaded, and returning table as the return value.
/**
* Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance.
* @param env the environment to load into, typically a Globals
* instance.
*/
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable();
@@ -70,23 +86,27 @@ public class TableLib extends TwoArgFunction {
table.set("sort", new sort());
table.set("unpack", new unpack());
env.set("table", table);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("table", table);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("table", table);
return NIL;
}
// "concat" (table [, sep [, i [, j]]]) -> string
static class concat extends TableLibFunction {
public LuaValue call(LuaValue list) {
return list.checktable().concat(EMPTYSTRING,1,list.length());
return list.checktable().concat(EMPTYSTRING, 1, list.length());
}
public LuaValue call(LuaValue list, LuaValue sep) {
return list.checktable().concat(sep.checkstring(),1,list.length());
return list.checktable().concat(sep.checkstring(), 1, list.length());
}
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) {
return list.checktable().concat(sep.checkstring(),i.checkint(),list.length());
return list.checktable().concat(sep.checkstring(), i.checkint(), list.length());
}
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i, LuaValue j) {
return list.checktable().concat(sep.checkstring(),i.checkint(),j.checkint());
return list.checktable().concat(sep.checkstring(), i.checkint(), j.checkint());
}
}
@@ -96,14 +116,15 @@ public class TableLib extends TwoArgFunction {
switch (args.narg()) {
case 2: {
LuaTable table = args.checktable(1);
table.insert(table.length()+1,args.arg(2));
table.insert(table.length()+1, args.arg(2));
return NONE;
}
case 3: {
LuaTable table = args.checktable(1);
int pos = args.checkint(2);
int max = table.length() + 1;
if (pos < 1 || pos > max) argerror(2, "position out of bounds: " + pos + " not between 1 and " + max);
int max = table.length()+1;
if (pos < 1 || pos > max)
argerror(2, "position out of bounds: " + pos + " not between 1 and " + max);
table.insert(pos, args.arg(3));
return NONE;
}
@@ -113,7 +134,7 @@ public class TableLib extends TwoArgFunction {
}
}
}
// "pack" (...) -> table
static class pack extends VarArgFunction {
public Varargs invoke(Varargs args) {
@@ -129,8 +150,8 @@ public class TableLib extends TwoArgFunction {
LuaTable table = args.checktable(1);
int size = table.length();
int pos = args.optint(2, size);
if (pos != size && (pos < 1 || pos > size + 1)) {
argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size + 1));
if (pos != size && (pos < 1 || pos > size+1)) {
argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size+1));
}
return table.remove(pos);
}
@@ -139,19 +160,17 @@ public class TableLib extends TwoArgFunction {
// "sort" (table [, comp])
static class sort extends VarArgFunction {
public Varargs invoke(Varargs args) {
args.checktable(1).sort(
args.isnil(2)? NIL: args.checkfunction(2));
args.checktable(1).sort(args.isnil(2)? NIL: args.checkfunction(2));
return NONE;
}
}
// "unpack", // (list [,i [,j]]) -> result1, ...
static class unpack extends VarArgFunction {
public Varargs invoke(Varargs args) {
LuaTable t = args.checktable(1);
// do not waste resource for calc rawlen if arg3 is not nil
int len = args.arg(3).isnil() ? t.length() : 0;
int len = args.arg(3).isnil()? t.length(): 0;
return t.unpack(args.optint(2, 1), args.optint(3, len));
}
}

View File

@@ -24,21 +24,24 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take two arguments and
* return one value.
/**
* Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class,
* simplifying development.
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
* are routed through this method by this class,
* Subclasses need only implement
* {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class,
* simplifying development. All other uses of {@link #call()},
* {@link #invoke(Varargs)},etc, are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required.
* <p>
* If more or less than three arguments are required,
* or variable argument or variable return values,
* then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link VarArgFunction}.
* If more or less than three arguments are required, or variable argument or
* variable return values, then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or
* {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and library functions.
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue,LuaValue)
* @see LibFunction
* @see ZeroArgFunction
@@ -49,11 +52,11 @@ import org.luaj.vm2.Varargs;
abstract public class ThreeArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3);
/** Default constructor */
public ThreeArgFunction() {
}
public final LuaValue call() {
return call(NIL, NIL, NIL);
}
@@ -65,9 +68,9 @@ abstract public class ThreeArgFunction extends LibFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1, arg2, NIL);
}
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(),varargs.arg(2),varargs.arg(3));
return call(varargs.arg1(), varargs.arg(2), varargs.arg(3));
}
}
}

View File

@@ -24,21 +24,24 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take two arguments and
* return one value.
/**
* Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to complete this class,
* simplifying development.
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc,
* are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required.
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to
* complete this class, simplifying development. All other uses of
* {@link #call()}, {@link #invoke(Varargs)},etc, are routed through this method
* by this class, dropping or extending arguments with {@code nil} values as
* required.
* <p>
* If more or less than two arguments are required,
* or variable argument or variable return values,
* then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* If more or less than two arguments are required, or variable argument or
* variable return values, then use one of the related function
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or
* {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and library functions.
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue)
* @see LibFunction
* @see ZeroArgFunction
@@ -49,11 +52,11 @@ import org.luaj.vm2.Varargs;
abstract public class TwoArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
/** Default constructor */
public TwoArgFunction() {
}
public final LuaValue call() {
return call(NIL, NIL);
}
@@ -65,9 +68,9 @@ abstract public class TwoArgFunction extends LibFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(arg1, arg2);
}
public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(),varargs.arg(2));
return call(varargs.arg1(), varargs.arg(2));
}
}
}

View File

@@ -24,20 +24,23 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that takes varaiable arguments and
* returns multiple return values.
/**
* Abstract base class for Java function implementations that takes varaiable
* arguments and returns multiple return values.
* <p>
* Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete this class,
* simplifying development.
* All other uses of {@link #call(LuaValue)}, {@link #invoke()},etc,
* are routed through this method by this class,
* converting arguments to {@link Varargs} and
* dropping or extending return values with {@code nil} values as required.
* Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete
* this class, simplifying development. All other uses of
* {@link #call(LuaValue)}, {@link #invoke()},etc, are routed through this
* method by this class, converting arguments to {@link Varargs} and dropping or
* extending return values with {@code nil} values as required.
* <p>
* If between one and three arguments are required, and only one return value is returned,
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link ThreeArgFunction}.
* If between one and three arguments are required, and only one return value is
* returned, {@link ZeroArgFunction}, {@link OneArgFunction},
* {@link TwoArgFunction}, or {@link ThreeArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and library functions.
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #invoke(Varargs)
* @see LibFunction
* @see ZeroArgFunction
@@ -49,7 +52,7 @@ abstract public class VarArgFunction extends LibFunction {
public VarArgFunction() {
}
public LuaValue call() {
return invoke(NONE).arg1();
}
@@ -59,25 +62,25 @@ abstract public class VarArgFunction extends LibFunction {
}
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return invoke(varargsOf(arg1,arg2)).arg1();
return invoke(varargsOf(arg1, arg2)).arg1();
}
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return invoke(varargsOf(arg1,arg2,arg3)).arg1();
return invoke(varargsOf(arg1, arg2, arg3)).arg1();
}
/**
* Subclass responsibility.
* May not have expected behavior for tail calls.
* Should not be used if:
* - function has a possibility of returning a TailcallVarargs
/**
* Subclass responsibility. May not have expected behavior for tail calls.
* Should not be used if: - function has a possibility of returning a
* TailcallVarargs
*
* @param args the arguments to the function call.
*/
public Varargs invoke(Varargs args) {
return onInvoke(args).eval();
}
public Varargs onInvoke(Varargs args) {
return invoke(args);
}
}
}

View File

@@ -24,19 +24,21 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take no arguments and
* return one value.
/**
* Abstract base class for Java function implementations that take no arguments
* and return one value.
* <p>
* Subclasses need only implement {@link LuaValue#call()} to complete this class,
* simplifying development.
* All other uses of {@link #call(LuaValue)}, {@link #invoke(Varargs)},etc,
* are routed through this method by this class.
* Subclasses need only implement {@link LuaValue#call()} to complete this
* class, simplifying development. All other uses of {@link #call(LuaValue)},
* {@link #invoke(Varargs)},etc, are routed through this method by this class.
* <p>
* If one or more arguments are required, or variable argument or variable return values,
* then use one of the related function
* {@link OneArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* If one or more arguments are required, or variable argument or variable
* return values, then use one of the related function {@link OneArgFunction},
* {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p>
* See {@link LibFunction} for more information on implementation libraries and library functions.
* See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call()
* @see LibFunction
* @see OneArgFunction
@@ -51,7 +53,7 @@ abstract public class ZeroArgFunction extends LibFunction {
/** Default constructor */
public ZeroArgFunction() {
}
public LuaValue call(LuaValue arg) {
return call();
}
@@ -67,4 +69,4 @@ abstract public class ZeroArgFunction extends LibFunction {
public Varargs invoke(Varargs varargs) {
return call();
}
}
}