Refactor API's related to compiling and loading scripts and character encoding handling.

This commit is contained in:
James Roseborough
2013-09-18 05:32:30 +00:00
parent a552494b72
commit 2123d3f924
35 changed files with 489 additions and 293 deletions

View File

@@ -163,13 +163,13 @@ public final class Buffer {
* 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[], byte[], int)
* @see LuaString#encodeToUtf8(char[], int, byte[], int)
*/
public final Buffer append( String str ) {
char[] chars = str.toCharArray();
final int n = LuaString.lengthAsUtf8( chars );
char[] c = str.toCharArray();
final int n = LuaString.lengthAsUtf8( c );
makeroom( 0, n );
LuaString.encodeToUtf8( chars, bytes, offset + length );
LuaString.encodeToUtf8( c, c.length, bytes, offset + length );
length += n;
return this;
}

View File

@@ -21,10 +21,11 @@
******************************************************************************/
package org.luaj.vm2;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.DebugLib;
import org.luaj.vm2.lib.PackageLib;
@@ -68,9 +69,6 @@ public class Globals extends LuaTable {
/** The installed ResourceFinder for looking files by name. */
public ResourceFinder FINDER;
/** The installed compiler. */
public LuaCompiler compiler = null;
/** The currently running thread. Should not be changed by non-library code. */
public LuaThread running = new LuaThread(this);
@@ -86,30 +84,119 @@ public class Globals extends LuaTable {
/** The current error handler for this Globals */
public LuaValue errorfunc;
/** 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. */
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. */
Prototype compile(InputStream stream, String chunkname) throws IOException;
}
/** Interface for module that loads lua binary chunk into a prototype. */
public interface Undumper {
/** 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. */
public Globals checkglobals() {
return this;
}
/** The installed loader. */
public Loader loader;
/** Convenience function for loading a file.
/** The installed compiler. */
public Compiler compiler;
/** The installed undumper. */
public Undumper undumper;
/** 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) {
Varargs v = baselib.loadFile(filename, "bt", this);
return !v.isnil(1)? v.arg1(): error(v.arg(2).tojstring());
public LuaValue loadfile(String filename) {
try {
return load(FINDER.findResource(filename), "@"+filename, "bt", this);
} catch (Exception e) {
return error("load "+filename+": "+e);
}
}
/** Convenience function to load a string value as a script.
/** 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.
* @throws LuaError if the script could not be compiled.
*/
public LuaValue loadString(String script, String chunkname) {
Varargs v = baselib.loadStream(valueOf(script).toInputStream(), chunkname, "bt", this);
return !v.isnil(1)? v.arg1(): error(v.arg(2).tojstring());
public LuaValue load(String script, String chunkname) {
return load(new StrReader(script), chunkname);
}
/** 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. */
public LuaValue load(Reader reader, String chunkname) {
return load(new UTF8Stream(reader), chunkname, "t", this);
}
/** Load the content form an input stream as a binary chunk or text file. */
public LuaValue load(InputStream is, String chunkname, String mode, LuaValue env) {
try {
Prototype p = loadPrototype(is, chunkname, mode);
return loader.load(p, chunkname, env);
} catch (LuaError l) {
throw l;
} catch (Exception 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.
*/
public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException {
if (mode.indexOf('b') >= 0) {
if (undumper == null)
error("No undumper.");
if (!is.markSupported())
is = new MarkStream(is);
is.mark(4);
final Prototype p = undumper.undump(is, chunkname);
if (p != null)
return p;
is.reset();
}
if (mode.indexOf('t') >= 0) {
return compilePrototype(is, chunkname);
}
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.
*/
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.
*/
public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException {
if (compiler == null)
error("No compiler.");
return compiler.compile(stream, chunkname);
}
/** Function which yields the current thread.
@@ -123,4 +210,80 @@ public class Globals extends LuaTable {
return s.lua_yield(args);
}
/** Reader implementation to read chars from a String in JME or JSE. */
static class StrReader extends Reader {
final String s;
int i = 0, n;
StrReader(String s) {
this.s = s;
n = s.length();
}
public void close() throws IOException {
i = n;
}
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;
}
}
/** Simple converter from Reader to InputStream using UTF8 encoding that will work
* on both JME and JSE.
*/
static class UTF8Stream extends InputStream {
final char[] c = new char[32];
final byte[] b = new byte[96];
int i = 0, j = 0;
final Reader r;
UTF8Stream(Reader r) {
this.r = r;
}
public int read() throws IOException {
if (i < j)
return c[i++];
int n = r.read(c);
if (n < 0)
return -1;
j = LuaString.encodeToUtf8(c, n, b, i = 0);
return b[i++];
}
}
/** Simple 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.
*/
static class MarkStream extends InputStream {
private int[] b;
private int i = 0, j = 0;
private final InputStream s;
MarkStream(InputStream s) {
this.s = s;
}
public int read() throws IOException {
if (i < j)
return b[i++];
final int c = s.read();
if (c < 0)
return -1;
if (j < b.length) {
b[j++] = c;
i = j;
}
return c;
}
public synchronized void mark(int n) {
b = new int[n];
i = j = 0;
}
public boolean markSupported() {
return true;
}
public synchronized void reset() throws IOException {
i = 0;
}
}
}

View File

@@ -35,10 +35,10 @@ import java.io.InputStream;
* <p>
* A simple pattern for loading and executing code is
* <pre> {@code
* LuaValue _G = JsePlatform.standardGlobals();
* LoadState.load( new FileInputStream("main.lua"), "main.lua", _G ).call();
* Globals _G = JsePlatform.standardGlobals();
* _G.load(new FileReader("main.lua"), "main.lua", _G ).call();
* } </pre>
* This should work regardless of which {@link LuaCompiler}
* This should work regardless of which {@link Globals.Compiler}
* has been installed.
* <p>
*
@@ -52,7 +52,7 @@ import java.io.InputStream;
* for example:
* <pre> {@code
* LuaValue _G = JsePlatform.standardGlobals();
* LuaJC.install();
* LuaJC.install(_G);
* LoadState.load( new FileInputStream("main.lua"), "main.lua", _G ).call();
* } </pre>
*
@@ -64,7 +64,10 @@ import java.io.InputStream;
* @see LuaC
* @see LuaJC
*/
public class LoadState {
public class LoadState implements Globals.Undumper {
/** Shared instance of Globals.Undumper to use loading prototypes from binary lua files */
public static final Globals.Undumper instance = new LoadState();
/** format corresponding to non-number-patched lua, all numbers are floats or doubles */
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
@@ -89,22 +92,9 @@ public class LoadState {
public static final int LUA_TTHREAD = 8;
public static final int LUA_TVALUE = 9;
/** Interface for the compiler, if it is installed.
* <p>
* See the {@link LuaClosure} documentation for examples of how to use the compiler.
* @see LuaClosure
* @see #load(InputStream, String, LuaValue)
* */
public interface LuaCompiler {
/** Load into a Closure or LuaFunction from a Stream and initializes the environment
* @throws IOException */
public LuaFunction load(InputStream stream, String filename, LuaValue env) throws IOException;
}
/** Compiler instance, if installed */
public static LuaCompiler compiler = null;
/** 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' };
@@ -150,7 +140,11 @@ public class LoadState {
/** Read buffer */
private byte[] buf = new byte[512];
/** 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
* @return the int value laoded.
@@ -369,60 +363,22 @@ public class LoadState {
}
/**
* Load lua in either binary or text from an input stream.
* @param firstByte the first byte of the input stream
* 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 name Name to apply to the loaded chunk
* @param mode "b" for binary only, "t" for text only, "bt" for binary or text.
* @return {@link Prototype} that was loaded
* @throws IllegalArgumentException if the signature is bac
* @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 LuaFunction load( InputStream stream, String name, String mode, LuaValue env ) throws IOException {
if ( compiler != null )
return compiler.load(stream, name, env);
else {
int firstByte = stream.read();
if ( firstByte != LUA_SIGNATURE[0] )
throw new LuaError("no compiler");
Prototype p = loadBinaryChunk( firstByte, stream, name );
return new LuaClosure( p, env );
}
}
/**
* Load lua in the default mode "bt" from an input stream.
* @param firstByte the first byte of the input stream
* @param stream InputStream to read, after having read the first byte already
* @param name Name to apply to the loaded chunk
* @return {@link Prototype} that was loaded
* @throws IllegalArgumentException if the signature is bac
* @throws IOException if an IOException occurs
*/
public static LuaFunction load( InputStream stream, String name, LuaValue env ) throws IOException {
return load(stream, name, "bt", env);
}
/**
* Load lua thought to be a binary chunk from its first byte from an input stream.
* @param firstByte the first byte of the input stream
* @param stream InputStream to read, after having read the first byte already
* @param name Name to apply to the loaded chunk
* @return {@link Prototype} that was loaded
* @throws IllegalArgumentException if the signature is bac
* @throws IOException if an IOException occurs
*/
public static Prototype loadBinaryChunk( int firstByte, InputStream stream, String name ) throws IOException {
public Prototype undump(InputStream stream, String chunkname) throws IOException {
// check rest of signature
if ( firstByte != LUA_SIGNATURE[0]
if ( stream.read() != LUA_SIGNATURE[0]
|| stream.read() != LUA_SIGNATURE[1]
|| stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3] )
throw new IllegalArgumentException("bad signature");
return null;
// load file as a compiled chunk
String sname = getSourceName(name);
String sname = getSourceName(chunkname);
LoadState s = new LoadState( stream, sname );
s.loadHeader();
@@ -457,4 +413,9 @@ public class LoadState {
this.name = name;
this.is = new DataInputStream( stream );
}
private LoadState() {
this.name = "";
this.is = null;
}
}

View File

@@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import org.luaj.vm2.lib.MathLib;
import org.luaj.vm2.lib.StringLib;
@@ -48,7 +49,7 @@ import org.luaj.vm2.lib.StringLib;
* When Java Strings are used to initialize {@link LuaString} data, the UTF8 encoding is assumed.
* The functions
* {@link LuaString#lengthAsUtf8(char[]),
* {@link LuaString#encodeToUtf8(char[], byte[], int)}, and
* {@link LuaString#encodeToUtf8(char[], int, byte[], int)}, and
* {@link LuaString#decodeAsUtf8(byte[], int, int)
* are used to convert back and forth between UTF8 byte arrays and character arrays.
*
@@ -108,7 +109,7 @@ public class LuaString extends LuaValue {
public static LuaString valueOf(String string) {
char[] c = string.toCharArray();
byte[] b = new byte[lengthAsUtf8(c)];
encodeToUtf8(c, b, 0);
encodeToUtf8(c, c.length, b, 0);
return valueOf(b, 0, b.length);
}
@@ -143,20 +144,31 @@ public class LuaString extends LuaValue {
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only th elow-order 8-bits of each character are used, the remainder is ignored.
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes) {
int n = bytes.length;
byte[] b = new byte[n];
for ( int i=0; i<n; i++ )
b[i] = (byte) bytes[i];
return valueOf(b, 0, b.length);
return valueOf(bytes, 0, bytes.length);
}
/** Construct a {@link LuaString} using the supplied characters as byte values.
* <p>
* Only the low-order 8-bits of each character are used, the remainder is ignored.
* <p>
* This is most useful for constructing byte sequences that do not conform to UTF8.
* @param bytes array of char, whose values are truncated at 8-bits each and put into a byte array.
* @return {@link LuaString} wrapping a copy of the byte buffer
*/
public static LuaString valueOf(char[] bytes, int off, int len) {
byte[] b = new byte[len];
for ( int i=0; i<len; i++ )
b[i] = (byte) bytes[i + off];
return valueOf(b, 0, len);
}
/** Construct a {@link LuaString} around a byte array without copying the contents.
* <p>
@@ -556,7 +568,7 @@ public class LuaString extends LuaValue {
* @param length number of bytes to convert
* @return Java String corresponding to the value of bytes interpreted using UTF8
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], byte[], int)
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #isValidUtf8()
*/
public static String decodeAsUtf8(byte[] bytes, int offset, int length) {
@@ -581,7 +593,7 @@ public class LuaString extends LuaValue {
* Count the number of bytes required to encode the string as UTF-8.
* @param chars Array of unicode characters to be encoded as UTF-8
* @return count of bytes needed to encode using UTF-8
* @see #encodeToUtf8(char[], byte[], int)
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #decodeAsUtf8(byte[], int, int)
* @see #isValidUtf8()
*/
@@ -601,16 +613,18 @@ public class LuaString extends LuaValue {
* The string should be measured first with lengthAsUtf8
* to make sure the given byte array is large enough.
* @param chars Array of unicode characters to be encoded as UTF-8
* @param nchars Number of characters in the array to convert.
* @param bytes byte array to hold the result
* @param off offset into the byte array to start writing
* @return number of bytes converted.
* @see #lengthAsUtf8(char[])
* @see #decodeAsUtf8(byte[], int, int)
* @see #isValidUtf8()
*/
public static void encodeToUtf8(char[] chars, byte[] bytes, int off) {
final int n = chars.length;
public static int encodeToUtf8(char[] chars, int nchars, byte[] bytes, int off) {
char c;
for ( int i=0, j=off; i<n; i++ ) {
int j = off;
for ( int i=0; i<nchars; i++ ) {
if ( (c = chars[i]) < 0x80 ) {
bytes[j++] = (byte) c;
} else if ( c < 0x800 ) {
@@ -622,12 +636,13 @@ public class LuaString extends LuaValue {
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
}
}
return j - off;
}
/** Check that a byte sequence is valid UTF-8
* @return true if it is valid UTF-8, otherwise false
* @see #lengthAsUtf8(char[])
* @see #encodeToUtf8(char[], byte[], int)
* @see #encodeToUtf8(char[], int, byte[], int)
* @see #decodeAsUtf8(byte[], int, int)
*/
public boolean isValidUtf8() {
@@ -759,4 +774,15 @@ public class LuaString extends LuaValue {
}
}
/**
* Print the bytes of the LuaString to a PrintStream as if it were
* an ASCII string, quoting and escaping control characters.
* @param ps PrintStream to print to.
*/
public void printToStream(PrintStream ps) {
for (int i = 0, n = m_length; i < n; i++) {
int c = m_bytes[m_offset+i];
ps.print((char) c);
}
}
}

View File

@@ -23,6 +23,7 @@ package org.luaj.vm2.compiler;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Hashtable;
import org.luaj.vm2.LocVars;
@@ -136,7 +137,7 @@ public class LexState {
FuncState fs; /* `FuncState' is private to the parser */
LuaC L;
InputStream z; /* input stream */
byte[] buff; /* buffer for tokens */
char[] buff; /* buffer for tokens */
int nbuff; /* length of buffer */
Dyndata dyd = new Dyndata(); /* dynamic structures used by the parser */
LuaString source; /* current source name */
@@ -204,7 +205,7 @@ public class LexState {
public LexState(LuaC state, InputStream stream) {
this.z = stream;
this.buff = new byte[32];
this.buff = new char[32];
this.L = state;
}
@@ -229,7 +230,7 @@ public class LexState {
void save(int c) {
if ( buff == null || nbuff + 1 > buff.length )
buff = LuaC.realloc( buff, nbuff*2+1 );
buff[nbuff++] = (byte) c;
buff[nbuff++] = (char) c;
}
@@ -272,12 +273,11 @@ public class LexState {
// only called by new_localvarliteral() for var names.
LuaString newstring( String s ) {
byte[] b = s.getBytes();
return L.newTString(b, 0, b.length);
return L.newTString(s);
}
LuaString newstring( byte[] bytes, int offset, int len ) {
return L.newTString( bytes, offset, len );
LuaString newstring( char[] chars, int offset, int len ) {
return L.newTString(new String(chars, offset, len));
}
void inclinenumber() {
@@ -327,9 +327,9 @@ public class LexState {
return true;
}
void buffreplace(byte from, byte to) {
void buffreplace(char from, char to) {
int n = nbuff;
byte[] p = buff;
char[] p = buff;
while ((--n) >= 0)
if (p[n] == from)
p[n] = to;
@@ -477,7 +477,7 @@ public class LexState {
}
}
if (seminfo != null)
seminfo.ts = newstring(buff, 2 + sep, nbuff - 2 * (2 + sep));
seminfo.ts = L.newTString(LuaString.valueOf(buff, 2 + sep, nbuff - 2 * (2 + sep)));
}
int hexvalue(int c) {
@@ -574,7 +574,7 @@ public class LexState {
}
}
save_and_next(); /* skip delimiter */
seminfo.ts = newstring(buff, 1, nbuff - 2);
seminfo.ts = L.newTString(LuaString.valueOf(buff, 1, nbuff-2));
}
int llex(SemInfo seminfo) {
@@ -845,7 +845,7 @@ public class LexState {
if (t.token == TK_NAME || t.token == TK_STRING) {
LuaString ts = t.seminfo.ts;
// TODO: is this necessary?
newstring(ts.m_bytes, 0, ts.m_length);
L.cachedLuaString(t.seminfo.ts);
}
}

View File

@@ -25,7 +25,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LocVars;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaClosure;
@@ -35,7 +35,7 @@ import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.Upvaldesc;
import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.lib.BaseLib;
/**
* Compiler for Lua.
@@ -45,7 +45,7 @@ import org.luaj.vm2.LoadState.LuaCompiler;
* and optionaly instantiates a {@link LuaClosure} around the result
* using a user-supplied environment.
* <p>
* Implements the {@link LuaCompiler} interface for loading
* Implements the {@link Globals.Compiler} interface for loading
* initialized chunks, which is an interface common to
* lua bytecode compiling and java bytecode compiling.
* <p>
@@ -66,7 +66,7 @@ import org.luaj.vm2.LoadState.LuaCompiler;
* @see LuaCompiler
* @see Prototype
*/
public class LuaC extends Lua implements LuaCompiler {
public class LuaC extends Lua implements Globals.Compiler, Globals.Loader {
public static final LuaC instance = new LuaC();
@@ -74,8 +74,9 @@ public class LuaC extends Lua implements LuaCompiler {
* try to use it when handed bytes that are
* not already a compiled lua chunk.
*/
public static void install() {
org.luaj.vm2.LoadState.compiler = instance;
public static void install(Globals g) {
g.compiler = instance;
g.loader = instance;
}
protected static void _assert(boolean b) {
@@ -213,6 +214,13 @@ public class LuaC extends Lua implements LuaCompiler {
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));
return a;
}
public int nCcalls;
Hashtable strings;
@@ -221,29 +229,25 @@ public class LuaC extends Lua implements LuaCompiler {
private LuaC(Hashtable strings) {
this.strings = strings;
}
/** Load into a Closure or LuaFunction, with the supplied initial environment */
public LuaFunction load(InputStream stream, String name, LuaValue env) throws IOException {
Prototype p = compile( stream, name );
return new LuaClosure( p, env );
}
/** Compile a prototype or load as a binary chunk */
public static Prototype compile(InputStream stream, String name) throws IOException {
int firstByte = stream.read();
return ( firstByte == '\033' )?
LoadState.loadBinaryChunk(firstByte, stream, name):
(new LuaC(new Hashtable())).luaY_parser(firstByte, stream, name);
/** 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
*/
public Prototype compile(InputStream stream, String chunkname) throws IOException {
return (new LuaC(new Hashtable())).luaY_parser(stream, chunkname);
}
/** Parse the input */
private Prototype luaY_parser(int firstByte, InputStream z, String name) {
private 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, firstByte, 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);
@@ -256,25 +260,28 @@ public class LuaC extends Lua implements LuaCompiler {
}
// look up and keep at most one copy of each string
public LuaString newTString(byte[] bytes, int offset, int len) {
LuaString tmp = LuaString.valueOf(bytes, offset, len);
LuaString v = (LuaString) strings.get(tmp);
if ( v == null ) {
// must copy bytes, since bytes could be from reusable buffer
byte[] copy = new byte[len];
System.arraycopy(bytes, offset, copy, 0, len);
v = LuaString.valueOf(copy);
strings.put(v, v);
}
return v;
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)
return c;
strings.put(s, s);
return s;
}
public String pushfstring(String string) {
return string;
}
public LuaFunction load(Prototype p, String filename, LuaValue env) {
return new LuaClosure( p, env );
public LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException {
return new LuaClosure(prototype, env);
}
}

View File

@@ -228,9 +228,9 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
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.write( '\t' );
if ( i>1 ) globals.STDOUT.print( '\t' );
LuaString s = tostring.call( args.arg(i) ).strvalue();
globals.STDOUT.write( s.m_bytes, s.m_offset, s.m_length );
globals.STDOUT.print(s.tojstring());
}
globals.STDOUT.println();
return NONE;
@@ -438,7 +438,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
try {
if ( is == null )
return varargsOf(NIL, valueOf("not found: "+chunkname));
return LoadState.load(is, chunkname, mode, env);
return globals.load(is, chunkname, mode, env);
} catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}

View File

@@ -22,7 +22,6 @@
package org.luaj.vm2.lib;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaBoolean;
import org.luaj.vm2.LuaClosure;

View File

@@ -151,7 +151,7 @@ public class OsLib extends TwoArgFunction {
tbl.set("min", LuaValue.valueOf(d.get(Calendar.MINUTE)));
tbl.set("sec", LuaValue.valueOf(d.get(Calendar.SECOND)));
tbl.set("wday", LuaValue.valueOf(d.get(Calendar.DAY_OF_WEEK)));
tbl.set("yday", LuaValue.valueOf(d.get(Calendar.DAY_OF_YEAR)));
tbl.set("yday", LuaValue.valueOf(d.get(0x6))); // Day of year
tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
return tbl;
}

View File

@@ -240,7 +240,7 @@ public class PackageLib extends TwoArgFunction {
LuaString filename = v.arg1().strvalue();
// Try to load the file.
v = globals.loadFile(filename.tojstring());
v = globals.loadfile(filename.tojstring());
if ( v.arg1().isfunction() )
return LuaValue.varargsOf(v.arg1(), filename);

View File

@@ -24,7 +24,6 @@ package org.luaj.vm2.lib.jme;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.BaseLib;
@@ -114,8 +113,8 @@ public class JmePlatform {
_G.load(new StringLib());
_G.load(new CoroutineLib());
_G.load(new JmeIoLib());
LuaC.install();
_G.compiler = LuaC.instance;
LoadState.install(_G);
LuaC.install(_G);
return _G;
}

View File

@@ -56,6 +56,7 @@ public class lua {
" -b use luajc bytecode-to-bytecode compiler (requires bcel on class path)\n" +
" -n nodebug - do not load debug library by default\n" +
" -p print the prototype\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n" +
" - execute stdin and stop handling options";
@@ -66,6 +67,7 @@ public class lua {
private static Globals _G;
private static boolean print = false;
private static String encoding = null;
public static void main( String[] args ) throws IOException {
@@ -113,6 +115,11 @@ public class lua {
case 'p':
print = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case '-':
if ( args[i].length() > 2 )
usageExit();
@@ -131,7 +138,7 @@ public class lua {
// new lua state
_G = nodebug? JsePlatform.standardGlobals(): JsePlatform.debugGlobals();
if ( luajc ) LuaJC.install();
if ( luajc ) LuaJC.install(_G);
for ( int i=0, n=libs!=null? libs.size(): 0; i<n; i++ )
loadLibrary( (String) libs.elementAt(i) );
@@ -147,6 +154,7 @@ public class lua {
} else {
switch ( args[i].charAt(1) ) {
case 'l':
case 'c':
++i;
break;
case 'e':
@@ -187,9 +195,11 @@ public class lua {
private static void processScript( InputStream script, String chunkname, String[] args, int firstarg ) throws IOException {
try {
LuaFunction c;
LuaValue c;
try {
c = LoadState.load(script, chunkname, "bt", _G);
c = encoding != null?
_G.load(new InputStreamReader(script, encoding), chunkname):
_G.load(script, chunkname, "bt", _G);
} finally {
script.close();
}

View File

@@ -24,13 +24,14 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.Print;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.DumpState;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.jse.JsePlatform;
@@ -51,6 +52,7 @@ public class luac {
" -e little endian format for numbers\n" +
" -i<n> number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" +
" -v show version information\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n";
private static void usageExit() {
@@ -66,6 +68,7 @@ public class luac {
private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT;
private boolean versioninfo = false;
private boolean processing = true;
private String encoding = null;
public static void main( String[] args ) throws IOException {
new luac( args );
@@ -108,6 +111,11 @@ public class luac {
case 'v':
versioninfo = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case '-':
if ( args[i].length() > 2 )
usageExit();
@@ -129,17 +137,18 @@ public class luac {
// process input files
try {
JsePlatform.standardGlobals();
Globals globals = JsePlatform.standardGlobals();
processing = true;
for ( int i=0; i<args.length; i++ ) {
if ( ! processing || ! args[i].startsWith("-") ) {
String chunkname = args[i].substring(0,args[i].length()-4);
processScript( new FileInputStream(args[i]), chunkname, fos );
processScript( globals, new FileInputStream(args[i]), chunkname, fos );
} else if ( args[i].length() <= 1 ) {
processScript( System.in, "=stdin", fos );
processScript( globals, System.in, "=stdin", fos );
} else {
switch ( args[i].charAt(1) ) {
case 'o':
case 'c':
++i;
break;
case '-':
@@ -158,10 +167,12 @@ public class luac {
}
}
private void processScript( InputStream script, String chunkname, OutputStream out ) throws IOException {
private void processScript( Globals globals, InputStream script, String chunkname, OutputStream out ) throws IOException {
try {
// create the chunk
Prototype chunk = LuaC.instance.compile(script, chunkname);
Prototype chunk = encoding != null?
globals.compilePrototype(new InputStreamReader(script, encoding), chunkname):
globals.compilePrototype(script, chunkname);
// list the chunk
if (list)

View File

@@ -24,11 +24,13 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
@@ -49,6 +51,7 @@ public class luajc {
" -m generate main(String[]) function for JSE\n" +
" -r recursively compile all\n" +
" -l load classes to verify generated bytecode\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -v verbose\n";
private static void usageExit() {
@@ -62,8 +65,10 @@ public class luajc {
private boolean recurse = false;
private boolean verbose = false;
private boolean loadclasses = false;
private String encoding = null;
private String pkgprefix = null;
private List files = new ArrayList();
private Globals globals;
public static void main( String[] args ) throws IOException {
new luajc( args );
@@ -104,6 +109,11 @@ public class luajc {
case 'r':
recurse = true;
break;
case 'c':
if ( ++i >= args.length )
usageExit();
encoding = args[i];
break;
case 'v':
verbose = true;
break;
@@ -140,7 +150,7 @@ public class luajc {
}
// process input files
JsePlatform.standardGlobals();
globals = JsePlatform.standardGlobals();
for ( int i=0,n=files.size(); i<n; i++ )
processFile( (InputFile) files.get(i) );
}
@@ -197,7 +207,9 @@ public class luajc {
// create the chunk
FileInputStream fis = new FileInputStream( inf.infile );
final Hashtable t = LuaJC.getInstance().compileAll(fis, inf.luachunkname, inf.srcfilename, genmain);
final Hashtable t = encoding != null?
LuaJC.instance.compileAll( new InputStreamReader(fis, encoding), inf.luachunkname, inf.srcfilename, globals, genmain):
LuaJC.instance.compileAll( fis, inf.luachunkname, inf.srcfilename, globals, genmain);
fis.close();
// write out the chunk

View File

@@ -22,6 +22,7 @@
package org.luaj.vm2.lib.jse;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.LuaC;
@@ -101,8 +102,8 @@ public class JsePlatform {
_G.load(new JseIoLib());
_G.load(new JseOsLib());
_G.load(new LuajavaLib());
LuaC.install();
_G.compiler = LuaC.instance;
LoadState.install(_G);
LuaC.install(_G);
return _G;
}

View File

@@ -23,14 +23,15 @@ package org.luaj.vm2.luajc;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Hashtable;
import org.luaj.vm2.LoadState;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.LoadState.LuaCompiler;
import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.BaseLib;
/**
* Implementation of {@link LuaCompiler} which does direct
@@ -46,45 +47,44 @@ import org.luaj.vm2.compiler.LuaC;
* as in the following:
* <pre> {@code
* LuaValue _G = JsePlatform.standardGlobals();
* LuaJC.install();
* LoadState.load( new ByteArrayInputStream("print 'hello'".getBytes()), "main.lua", _G ).call();
* LuaJC.install(_G);
* _G.loadString("print 'hello'").call();
* } </pre>
* @see LuaCompiler
* @see LuaC
* @see JsePlatform
* @see JmePlatform
* @see BaseLib
* @see LuaValue
*/
public class LuaJC implements LuaCompiler {
private static final String NON_IDENTIFIER = "[^a-zA-Z0-9_$/.\\-]";
public class LuaJC implements Globals.Loader {
private static LuaJC instance;
public static LuaJC getInstance() {
if ( instance == null )
instance = new LuaJC();
return instance;
}
public static final LuaJC instance = new LuaJC();
/**
* Install the compiler as the main compiler to use.
* Install the compiler as the main Globals.Loader to use in a set of globals.
* Will fall back to the LuaC prototype compiler.
*/
public static final void install() {
LoadState.compiler = getInstance();
public static final void install(Globals G) {
G.loader = instance;
}
public LuaJC() {
}
protected LuaJC() {}
public Hashtable compileAll(InputStream script, String chunkname, String filename, boolean genmain) throws IOException {
String classname = toStandardJavaClassName( chunkname );
String luaname = toStandardLuaFileName( filename );
Hashtable h = new Hashtable();
Prototype p = LuaC.instance.compile(script, classname);
JavaGen gen = new JavaGen(p, classname, luaname, genmain);
public Hashtable compileAll(InputStream script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException {
final String classname = toStandardJavaClassName( chunkname );
final Prototype p = globals.loadPrototype(script, classname, "bt");
return compileProtoAndSubProtos(p, classname, filename, genmain);
}
public Hashtable compileAll(Reader script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException {
final String classname = toStandardJavaClassName( chunkname );
final Prototype p = globals.compilePrototype(script, classname);
return compileProtoAndSubProtos(p, classname, filename, genmain);
}
private Hashtable compileProtoAndSubProtos(Prototype p, String classname, String filename, boolean genmain) throws IOException {
final String luaname = toStandardLuaFileName( filename );
final Hashtable h = new Hashtable();
final JavaGen gen = new JavaGen(p, classname, luaname, genmain);
insert( h, gen );
return h;
}
@@ -95,12 +95,11 @@ public class LuaJC implements LuaCompiler {
insert(h, gen.inners[i]);
}
public LuaFunction load(InputStream stream, String name, LuaValue env) throws IOException {
Prototype p = LuaC.instance.compile(stream, name);
public LuaFunction load(Prototype p, String name, LuaValue globals) throws IOException {
String luaname = toStandardLuaFileName( name );
String classname = toStandardJavaClassName( luaname );
JavaLoader loader = new JavaLoader();
return loader.load(p, classname, luaname, env);
return loader.load(p, classname, luaname, globals);
}
private static String toStandardJavaClassName( String luachunkname ) {

View File

@@ -83,7 +83,7 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
InputStream is = new Utf8Encoder(script);
try {
final Globals g = context.globals;
final LuaFunction f = LoadState.load(is, "script", "bt", g);
final LuaFunction f = g.load(script, "script").checkfunction();
return new LuajCompiledScript(f, g);
} catch ( LuaError lee ) {
throw new ScriptException(lee.getMessage() );

View File

@@ -38,7 +38,6 @@ public class LuaScriptEngineFactory implements ScriptEngineFactory {
};
private static final String [] MIMETYPES = {
"text/plain",
"text/lua",
"application/lua"
};

View File

@@ -28,12 +28,12 @@ import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.SimpleScriptContext;
import org.luaj.vm2.Globals;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Context for LuaScriptEngine execution which maintains its own Globals,
@@ -61,11 +61,13 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext {
* have negative impact on performance.
*/
public LuajContext() {
this("true".equals(System.getProperty("org.luaj.debug")));
this("true".equals(System.getProperty("org.luaj.debug")),
"true".equals(System.getProperty("org.luaj.luajc")));
}
/** Construct a LuajContext with its own globals, which
* which optionally are debug globals.
* which optionally are debug globals, and optionally use the
* luajc direct lua to java bytecode compiler.
* <p>
* If createDebugGlobals is set, the globals
* created will be a debug globals that includes the debug
@@ -73,11 +75,15 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext {
* have negative impact on performance.
* @param createDebugGlobals true to create debug globals,
* false for standard globals.
* @param useLuaJCCompiler true to use the luajc compiler,
* reqwuires bcel to be on the class path.
*/
public LuajContext(boolean createDebugGlobals) {
public LuajContext(boolean createDebugGlobals, boolean useLuaJCCompiler) {
globals = createDebugGlobals?
JsePlatform.debugGlobals():
JsePlatform.standardGlobals();
if (useLuaJCCompiler)
LuaJC.install(globals);
stdin = globals.STDIN;
stdout = globals.STDOUT;
stderr = globals.STDERR;