Updated to Lua 5.4

This commit is contained in:
UnlegitDqrk
2026-03-03 10:55:46 +01:00
parent 67429a7a31
commit 862296b343
60 changed files with 2605 additions and 1384 deletions

View File

@@ -127,8 +127,10 @@ public class LoadState {
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;
/** for header of binary files -- this is Lua 5.4 */
public static final int LUAC_VERSION = 0x54;
public static final int LUAC_VERSION_53 = 0x53;
public static final int LUAC_VERSION_52 = 0x52;
/** for header of binary files -- this is the official format */
public static final int LUAC_FORMAT = 0;
@@ -136,6 +138,13 @@ public class LoadState {
/** size of header of binary files */
public static final int LUAC_HEADERSIZE = 12;
public static final int LUA_TNUMFLT = LUA_TNUMBER;
public static final int LUA_TSHRSTR = LUA_TSTRING;
public static final int LUA_TNUMINT = LUA_TNUMBER | (1 << 4);
public static final int LUA_TLNGSTR = LUA_TSTRING | (1 << 4);
public static final long LUAC_INT = 0x5678;
public static final double LUAC_NUM = 370.5;
// values read from the header
private int luacVersion;
private int luacFormat;
@@ -143,6 +152,7 @@ public class LoadState {
private int luacSizeofInt;
private int luacSizeofSizeT;
private int luacSizeofInstruction;
private int luacSizeofLuaInteger;
private int luacSizeofLuaNumber;
private int luacNumberFormat;
@@ -165,6 +175,10 @@ public class LoadState {
public static void install(Globals globals) {
globals.undumper = instance;
}
private boolean isModernVersion() {
return luacVersion == LUAC_VERSION || luacVersion == LUAC_VERSION_53;
}
/** Load a 4-byte int value from the input stream
* @return the int value laoded.
@@ -217,6 +231,19 @@ public class LoadState {
* @return the {@link LuaString} value laoded.
**/
LuaString loadString() throws IOException {
if (isModernVersion()) {
long size = is.readUnsignedByte();
if (size == 0) {
return null;
}
if (size == 0xFFL) {
size = this.luacSizeofSizeT == 8 ? loadInt64() : (loadInt() & 0xffffffffL);
}
int len = (int) size - 1;
byte[] bytes = new byte[len];
is.readFully(bytes, 0, len);
return LuaString.valueUsing(bytes);
}
int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
if ( size == 0 )
return null;
@@ -256,6 +283,9 @@ public class LoadState {
* @throws IOException if an i/o exception occurs
*/
LuaValue loadNumber() throws IOException {
if (isModernVersion()) {
return LuaValue.valueOf(Double.longBitsToDouble(loadInt64()));
}
if ( luacNumberFormat == NUMBER_FORMAT_INTS_ONLY ) {
return LuaInteger.valueOf( loadInt() );
} else {
@@ -263,6 +293,10 @@ public class LoadState {
}
}
LuaValue loadInteger() throws IOException {
return LuaInteger.valueOf(luacSizeofLuaInteger == 8 ? loadInt64() : loadInt());
}
/**
* Load a list of constants from a binary chunk
* @param f the function prototype
@@ -272,7 +306,8 @@ public class LoadState {
int n = loadInt();
LuaValue[] values = n>0? new LuaValue[n]: NOVALUES;
for ( int i=0; i<n; i++ ) {
switch ( is.readByte() ) {
int type = is.readByte();
switch ( type ) {
case LUA_TNIL:
values[i] = LuaValue.NIL;
break;
@@ -282,10 +317,14 @@ public class LoadState {
case LUA_TINT:
values[i] = LuaInteger.valueOf( loadInt() );
break;
case LUA_TNUMINT:
values[i] = loadInteger();
break;
case LUA_TNUMBER:
values[i] = loadNumber();
break;
case LUA_TSTRING:
case LUA_TLNGSTR:
values[i] = loadString();
break;
default:
@@ -293,8 +332,10 @@ public class LoadState {
}
}
f.k = values;
n = loadInt();
}
void loadProtos(Prototype f) throws IOException {
int n = loadInt();
Prototype[] protos = n>0? new Prototype[n]: NOPROTOS;
for ( int i=0; i<n; i++ )
protos[i] = loadFunction(f.source);
@@ -308,7 +349,7 @@ public class LoadState {
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);
f.upvalues[i] = new Upvaldesc(null, instack, idx, false);
}
}
@@ -318,7 +359,9 @@ public class LoadState {
* @throws IOException if there is an i/o exception
*/
void loadDebug( Prototype f ) throws IOException {
f.source = loadString();
if (!isModernVersion()) {
f.source = loadString();
}
f.lineinfo = loadIntArray();
int n = loadInt();
f.locvars = n>0? new LocVars[n]: NOLOCVARS;
@@ -326,7 +369,14 @@ public class LoadState {
LuaString varname = loadString();
int startpc = loadInt();
int endpc = loadInt();
f.locvars[i] = new LocVars(varname, startpc, endpc);
if (luacVersion == LUAC_VERSION) {
int slot = loadInt();
boolean toclose = is.readByte() != 0;
boolean isconst = is.readByte() != 0;
f.locvars[i] = new LocVars(varname, startpc, endpc, slot, toclose, isconst);
} else {
f.locvars[i] = new LocVars(varname, startpc, endpc);
}
}
n = loadInt();
@@ -342,19 +392,30 @@ public class LoadState {
*/
public Prototype loadFunction(LuaString p) throws IOException {
Prototype f = new Prototype();
//// this.L.push(f);
// f.source = loadString();
// if ( f.source == null )
// f.source = p;
f.linedefined = loadInt();
f.lastlinedefined = loadInt();
if (isModernVersion()) {
f.source = loadString();
if (f.source == null)
f.source = p;
f.linedefined = loadInt();
f.lastlinedefined = loadInt();
} else {
f.linedefined = loadInt();
f.lastlinedefined = loadInt();
}
f.numparams = is.readUnsignedByte();
f.is_vararg = is.readUnsignedByte();
f.maxstacksize = is.readUnsignedByte();
f.code = loadIntArray();
loadConstants(f);
loadUpvalues(f);
loadDebug(f);
if (isModernVersion()) {
loadConstants(f);
loadUpvalues(f);
loadProtos(f);
loadDebug(f);
} else {
loadConstants(f);
loadUpvalues(f);
loadDebug(f);
}
// TODO: add check here, for debugging purposes, I believe
// see ldebug.c
@@ -371,15 +432,36 @@ public class LoadState {
public void loadHeader() throws IOException {
luacVersion = is.readByte();
luacFormat = is.readByte();
luacLittleEndian = (0 != is.readByte());
luacSizeofInt = is.readByte();
luacSizeofSizeT = is.readByte();
luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte();
luacNumberFormat = is.readByte();
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);
if (isModernVersion()) {
for (int i = 0; i < LUAC_TAIL.length; ++i)
if (is.readByte() != LUAC_TAIL[i])
throw new LuaError("unexpected byte in luac data, index=" + i);
luacSizeofInt = is.readUnsignedByte();
luacSizeofSizeT = is.readUnsignedByte();
luacSizeofInstruction = is.readUnsignedByte();
luacSizeofLuaInteger = is.readUnsignedByte();
luacSizeofLuaNumber = is.readUnsignedByte();
luacLittleEndian = true;
long luacInt = luacSizeofLuaInteger == 8 ? loadInt64() : loadInt();
double luacNum = Double.longBitsToDouble(loadInt64());
if (luacInt == Long.reverseBytes(LUAC_INT) || Double.doubleToLongBits(luacNum) == Long.reverseBytes(Double.doubleToLongBits(LUAC_NUM))) {
luacLittleEndian = false;
} else if (luacInt != LUAC_INT || luacNum != LUAC_NUM) {
throw new LuaError("incompatible binary chunk");
}
luacNumberFormat = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
} else {
luacLittleEndian = (0 != is.readByte());
luacSizeofInt = is.readByte();
luacSizeofSizeT = is.readByte();
luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte();
luacNumberFormat = is.readByte();
luacSizeofLuaInteger = luacSizeofLuaNumber;
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);
}
}
/**
@@ -403,13 +485,17 @@ public class LoadState {
s.loadHeader();
// check format
switch ( s.luacNumberFormat ) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new LuaError("unsupported int size");
if (s.isModernVersion()) {
s.is.readUnsignedByte();
} else {
switch ( s.luacNumberFormat ) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32:
break;
default:
throw new LuaError("unsupported int size");
}
}
return s.loadFunction( LuaString.valueOf(sname) );
}

View File

@@ -33,6 +33,15 @@ public class LocVars {
/** The instruction offset when the variable goes out of scope */
public int endpc;
/** The stack slot used by this local variable. */
public int slot;
/** Whether this local variable should be closed when leaving scope. */
public boolean toclose;
/** Whether this local variable is constant after initialization. */
public boolean isconst;
/**
* Construct a LocVars instance.
@@ -41,12 +50,23 @@ public class LocVars {
* @param endpc The instruction offset when the variable goes out of scope
*/
public LocVars(LuaString varname, int startpc, int endpc) {
this(varname, startpc, endpc, -1, false);
}
public LocVars(LuaString varname, int startpc, int endpc, int slot, boolean toclose) {
this(varname, startpc, endpc, slot, toclose, false);
}
public LocVars(LuaString varname, int startpc, int endpc, int slot, boolean toclose, boolean isconst) {
this.varname = varname;
this.startpc = startpc;
this.endpc = endpc;
this.slot = slot;
this.toclose = toclose;
this.isconst = isconst;
}
public String tojstring() {
return varname+" "+startpc+"-"+endpc;
return varname+" "+startpc+"-"+endpc+(toclose? " <close>": "")+(isconst? " <const>": "");
}
}

View File

@@ -30,7 +30,7 @@ package org.luaj.vm2;
*/
public class Lua {
/** version is supplied by ant build task */
public static final String _VERSION = "Lua 5.3";
public static final String _VERSION = "Lua 5.4";
/** use return values from previous op */
public static final int LUA_MULTRET = -1;

View File

@@ -86,8 +86,9 @@ import java.util.List;
* @see LoadState
* @see Globals#compiler
*/
public class LuaClosure extends LuaFunction {
public class LuaClosure extends LuaFunction implements PrototypeProvider {
private static final UpValue[] NOUPVALUES = new UpValue[0];
private static final boolean[] NOCLOSEVARS = new boolean[0];
public final Prototype p;
@@ -106,6 +107,10 @@ public class LuaClosure extends LuaFunction {
globals = env instanceof Globals? (Globals) env: null;
}
public Prototype prototype() {
return p;
}
public void initupvalue1(LuaValue env) {
if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES;
@@ -251,6 +256,7 @@ public class LuaClosure extends LuaFunction {
// upvalues are only possible when closures create closures
// TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null;
boolean[] closedLocals = hasToCloseLocals()? new boolean[stack.length]: NOCLOSEVARS;
// allow for debug hooks
if (globals != null && globals.debuglib != null)
@@ -262,6 +268,7 @@ public class LuaClosure extends LuaFunction {
if (Thread.currentThread().isInterrupted()) {
throw new LuaError("interrupted");
}
prepareToCloseLocals(stack, closedLocals, pc);
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction( pc, v, top );
@@ -417,11 +424,15 @@ public class LuaClosure extends LuaFunction {
case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
pc += (i>>>14)-0x1ffff;
if (a > 0) {
for (--a, b = openups.length; --b>=0; )
if (openups[b] != null && openups[b].index >= a) {
openups[b].close();
openups[b] = null;
}
--a;
if (openups != null) {
for (b = openups.length; --b>=0; )
if (openups[b] != null && openups[b].index >= a) {
openups[b].close();
openups[b] = null;
}
}
closeLocals(stack, closedLocals, a, pc + 1, LuaValue.NIL);
}
continue;
@@ -496,6 +507,7 @@ public class LuaClosure extends LuaFunction {
}
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
closeLocals(stack, closedLocals, 0, p.code.length, LuaValue.NIL);
b = i>>>23;
switch ( b ) {
case 0: return varargsOf(stack, a, top-v.narg()-a, v);
@@ -603,12 +615,15 @@ public class LuaClosure extends LuaFunction {
} catch ( LuaError le ) {
if (le.traceback == null)
processErrorHooks(le, p, pc);
closeLocals(stack, closedLocals, 0, p.code.length, le.getMessageObject());
throw le;
} catch ( Exception e ) {
LuaError le = new LuaError(e);
processErrorHooks(le, p, pc);
closeLocals(stack, closedLocals, 0, p.code.length, le.getMessageObject());
throw le;
} finally {
closeLocals(stack, closedLocals, 0, p.code.length, LuaValue.NIL);
if ( openups != null )
for ( int u=openups.length; --u>=0; )
if ( openups[u] != null )
@@ -618,6 +633,59 @@ public class LuaClosure extends LuaFunction {
}
}
private boolean hasToCloseLocals() {
if (p.locvars == null)
return false;
for (int i = 0; i < p.locvars.length; i++) {
LocVars local = p.locvars[i];
if (local != null && local.toclose)
return true;
}
return false;
}
private void prepareToCloseLocals(LuaValue[] stack, boolean[] closedLocals, int pc) {
if (closedLocals.length == 0 || p.locvars == null)
return;
for (int i = 0; i < p.locvars.length; i++) {
LocVars local = p.locvars[i];
if (local == null || !local.toclose || local.slot < 0 || local.startpc != pc)
continue;
if (local.slot < closedLocals.length)
closedLocals[local.slot] = false;
checkClosable(local, stack[local.slot]);
}
}
private void closeLocals(LuaValue[] stack, boolean[] closedLocals, int minSlot, int closePcExclusive, LuaValue err) {
if (closedLocals.length == 0 || p.locvars == null)
return;
for (int i = p.locvars.length - 1; i >= 0; i--) {
LocVars local = p.locvars[i];
if (local == null || !local.toclose || local.slot < 0 || local.slot < minSlot)
continue;
if (local.slot >= closedLocals.length || closedLocals[local.slot])
continue;
if (local.endpc > closePcExclusive)
continue;
closedLocals[local.slot] = true;
LuaValue value = stack[local.slot];
if (value == null || value == LuaValue.FALSE)
continue;
LuaValue close = value.metatag(LuaValue.CLOSE);
if (!close.isnil())
close.call(value, err.isnil() ? LuaValue.NIL : err);
}
}
private void checkClosable(LocVars local, LuaValue value) {
if (value == null || value == LuaValue.FALSE)
return;
if (!value.metatag(LuaValue.CLOSE).isnil())
return;
throw new LuaError("variable '" + local.varname.tojstring() + "' got a non-closable value");
}
/**
* Run the error hook if there is one
* @param msg the message to use in error hook processing.

View File

@@ -27,6 +27,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.luaj.vm2.libs.MathLib;
@@ -347,19 +348,19 @@ public class LuaString extends LuaValue {
}
public int checkint() {
return (int) (long) checkdouble();
return (int) checklong();
}
public LuaInteger checkinteger() {
double d = scannumber();
if (Double.isNaN(d) || d != (long) d)
Long value = scanLuaIntegerValue();
if (value == null)
argerror("integer");
return LuaInteger.valueOf((long) d);
return LuaInteger.valueOf(value.longValue());
}
public long checklong() {
double d = scannumber();
if (Double.isNaN(d) || d != (long) d)
Long value = scanLuaIntegerValue();
if (value == null)
argerror("integer");
return (long) d;
return value.longValue();
}
public double checkdouble() {
double d = scannumber();
@@ -383,19 +384,12 @@ public class LuaString extends LuaValue {
}
public boolean isint() {
double d = scannumber();
if ( Double.isNaN(d) )
return false;
int i = (int) d;
return i == d;
Long value = scanLuaIntegerValue();
return value != null && value.intValue() == value.longValue();
}
public boolean islong() {
double d = scannumber();
if ( Double.isNaN(d) )
return false;
long l = (long) d;
return l == d;
return scanLuaIntegerValue() != null;
}
public byte tobyte() { return (byte) toint(); }
@@ -653,21 +647,34 @@ public class LuaString extends LuaValue {
* @see #isValidUtf8()
*/
public static String decodeAsUtf8(byte[] bytes, int offset, int length) {
int i,j,n,b;
for ( i=offset,j=offset+length,n=0; i<j; ++n ) {
switch ( 0xE0 & bytes[i++] ) {
case 0xE0: ++i;
case 0xC0: ++i;
int i, j, n;
for (i = offset, j = offset + length, n = 0; i < j; ++n) {
int b = bytes[i++] & 0xff;
if ((b & 0x80) == 0) {
continue;
}
if ((b & 0xe0) == 0xc0 && i < j) {
++i;
continue;
}
if ((b & 0xf0) == 0xe0 && i + 1 < j) {
i += 2;
}
}
char[] chars=new char[n];
for ( i=offset,j=offset+length,n=0; i<j; ) {
chars[n++] = (char) (
((b=bytes[i++])>=0||i>=j)? b:
(b<-32||i+1>=j)? (((b&0x3f) << 6) | (bytes[i++]&0x3f)):
(((b&0xf) << 12) | ((bytes[i++]&0x3f)<<6) | (bytes[i++]&0x3f)));
char[] chars = new char[n];
for (i = offset, j = offset + length, n = 0; i < j;) {
int b = bytes[i++] & 0xff;
if ((b & 0x80) == 0 || i > j) {
chars[n++] = (char) b;
} else if ((b & 0xe0) == 0xc0 && i <= j) {
chars[n++] = (char) (((b & 0x1f) << 6) | (bytes[i++] & 0x3f));
} else if ((b & 0xf0) == 0xe0 && i + 1 <= j) {
chars[n++] = (char) (((b & 0x0f) << 12) | ((bytes[i++] & 0x3f) << 6) | (bytes[i++] & 0x3f));
} else {
chars[n++] = (char) b;
}
}
return new String(chars);
return new String(chars, 0, n);
}
/**
@@ -679,12 +686,11 @@ public class LuaString extends LuaValue {
* @see #isValidUtf8()
*/
public static int lengthAsUtf8(char[] chars) {
int i,b;
char c;
for ( i=b=chars.length; --i>=0; )
if ( (c=chars[i]) >=0x80 )
b += (c>=0x800)? 2: 1;
return b;
int n = 0;
for (char c : chars) {
n += c < 0x80 ? 1 : c < 0x800 ? 2 : 3;
}
return n;
}
/**
@@ -703,21 +709,21 @@ public class LuaString extends LuaValue {
* @see #isValidUtf8()
*/
public static int encodeToUtf8(char[] chars, int nchars, byte[] bytes, int off) {
char c;
int j = off;
for ( int i=0; i<nchars; i++ ) {
if ( (c = chars[i]) < 0x80 ) {
bytes[j++] = (byte) c;
} else if ( c < 0x800 ) {
bytes[j++] = (byte) (0xC0 | ((c>>6) & 0x1f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
int start = off;
for (int i = 0; i < nchars; i++) {
int c = chars[i];
if (c < 0x80) {
bytes[off++] = (byte) c;
} else if (c < 0x800) {
bytes[off++] = (byte) (0xc0 | ((c >> 6) & 0x1f));
bytes[off++] = (byte) (0x80 | (c & 0x3f));
} else {
bytes[j++] = (byte) (0xE0 | ((c>>12) & 0x0f));
bytes[j++] = (byte) (0x80 | ((c>>6) & 0x3f));
bytes[j++] = (byte) (0x80 | ( c & 0x3f));
bytes[off++] = (byte) (0xe0 | ((c >> 12) & 0x0f));
bytes[off++] = (byte) (0x80 | ((c >> 6) & 0x3f));
bytes[off++] = (byte) (0x80 | (c & 0x3f));
}
}
return j - off;
return off - start;
}
/** Check that a byte sequence is valid UTF-8
@@ -751,8 +757,7 @@ public class LuaString extends LuaValue {
* @see LuaValue#tonumber()
*/
public LuaValue tonumber() {
double d = scannumber();
return Double.isNaN(d)? NIL: valueOf(d);
return scanLuaNumberValue();
}
/**
@@ -763,7 +768,11 @@ public class LuaString extends LuaValue {
*/
public LuaValue tonumber( int base ) {
double d = scannumber( base );
return Double.isNaN(d)? NIL: valueOf(d);
if (Double.isNaN(d)) {
return NIL;
}
long l = (long) d;
return l == d ? LuaInteger.valueOf(l) : valueOf(d);
}
/**
@@ -785,6 +794,47 @@ public class LuaString extends LuaValue {
double l = scanlong(10, i, j);
return Double.isNaN(l)? scandouble(i,j): l;
}
private LuaValue scanLuaNumberValue() {
int i = m_offset, j = m_offset + m_length;
while (i < j && m_bytes[i] == ' ') ++i;
while (i < j && m_bytes[j - 1] == ' ') --j;
if (i >= j) {
return NIL;
}
int prefix = (m_bytes[i] == '+' || m_bytes[i] == '-') ? i + 1 : i;
if (prefix + 1 < j && m_bytes[prefix] == '0' && (m_bytes[prefix + 1] == 'x' || m_bytes[prefix + 1] == 'X')) {
try {
String s = new String(m_bytes, i, j - i, java.nio.charset.StandardCharsets.ISO_8859_1);
if (s.indexOf('.') >= 0 || s.indexOf('p') >= 0 || s.indexOf('P') >= 0) {
return valueOf(Double.parseDouble(s));
}
String digits = s.startsWith("+") || s.startsWith("-") ? s.substring(3) : s.substring(2);
long value = Long.parseUnsignedLong(digits, 16);
if (s.startsWith("-")) {
if (value == Long.MIN_VALUE) {
return LuaInteger.valueOf(Long.MIN_VALUE);
}
return LuaInteger.valueOf(-value);
}
return LuaInteger.valueOf(value);
} catch (NumberFormatException e) {
return NIL;
}
}
double l = scanlong(10, i, j);
if (!Double.isNaN(l)) {
long lv = (long) l;
return lv == l ? LuaInteger.valueOf(lv) : valueOf(l);
}
double d = scandouble(i, j);
return Double.isNaN(d) ? NIL : valueOf(d);
}
private Long scanLuaIntegerValue() {
LuaValue value = scanLuaNumberValue();
return value.islong() ? Long.valueOf(value.tolong()) : null;
}
/**
* Convert to a number in a base, or return Double.NaN if not a number.
@@ -846,8 +896,12 @@ public class LuaString extends LuaValue {
case '+':
case '.':
case 'e': case 'E':
case 'p': case 'P':
case 'x': case 'X':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'f':
case 'A': case 'B': case 'C': case 'D': case 'F':
break;
default:
return Double.NaN;

View File

@@ -236,7 +236,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
public LuaValue rawget( LuaValue key ) {
if ( key.isint() ) {
if ( key.isinttype() ) {
int ikey = key.toint();
if ( ikey>0 && ikey<=array.length ) {
LuaValue v = m_metatable == null
@@ -279,7 +279,7 @@ public class LuaTable extends LuaValue implements Metatable {
/** caller must ensure key is not nil */
public void rawset( LuaValue key, LuaValue value ) {
if ( !key.isint() || !arrayset(key.toint(), value) )
if ( !key.isinttype() || !arrayset(key.toint(), value) )
hashset( key, value );
}
@@ -388,7 +388,7 @@ public class LuaTable extends LuaValue implements Metatable {
do {
// find current key index
if ( ! key.isnil() ) {
if ( key.isint() ) {
if ( key.isinttype() ) {
i = key.toint();
if ( i>0 && i<=array.length ) {
break;
@@ -472,8 +472,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
}
if ( checkLoadFactor() ) {
if ( (m_metatable == null || !m_metatable.useWeakValues())
&& key.isint() && key.toint() > 0 ) {
if ( key.isinttype() && key.toint() > 0 ) {
// a rehash might make room in the array portion for this key.
rehash( key.toint() );
if ( arrayset(key.toint(), value) )
@@ -719,7 +718,9 @@ public class LuaTable extends LuaValue implements Metatable {
if ( ( k = slot.arraykey( newArraySize ) ) > 0 ) {
StrongSlot entry = slot.first();
if (entry != null)
newArray[ k - 1 ] = entry.value();
newArray[ k - 1 ] = m_metatable != null
? m_metatable.wrap(entry.value())
: entry.value();
} else if ( !(slot instanceof DeadSlot) ) {
int j = slot.keyindex( newHashMask );
newHash[j] = slot.relink( newHash[j] );
@@ -767,7 +768,7 @@ public class LuaTable extends LuaValue implements Metatable {
}
protected static Entry defaultEntry(LuaValue key, LuaValue value) {
if ( key.isint() ) {
if ( key.isinttype() ) {
return new IntKeyEntry( key.toint(), value );
} else if (value.type() == TNUMBER && !value.isinttype()) {
return new NumberValueEntry( key, value.todouble() );

View File

@@ -181,6 +181,12 @@ public class LuaThread extends LuaValue {
return s.lua_resume(this, args);
}
public Varargs close() {
if (isMainThread())
return LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf("cannot close main thread"));
return state.lua_close();
}
public static class State implements Runnable {
private final Globals globals;
final WeakReference lua_thread;
@@ -203,6 +209,7 @@ public class LuaThread extends LuaValue {
public int status = LuaThread.STATUS_INITIAL;
private Lock locker = new ReentrantLock();
private Condition cond = locker.newCondition();
private Thread thread;
State(Globals globals, LuaThread lua_thread, LuaValue function) {
this.globals = globals;
@@ -251,8 +258,10 @@ public class LuaThread extends LuaValue {
}
}
if (t == null){
new Thread(this, "Coroutine-"+(++coroutine_count)).start();
t = new Thread(this, "Coroutine-"+(++coroutine_count));
t.start();
}
this.thread = t;
} else {
cond.signal();
}
@@ -304,6 +313,23 @@ public class LuaThread extends LuaValue {
locker.unlock();
}
}
public Varargs lua_close() {
locker.lock();
try {
if (status == STATUS_DEAD)
return LuaValue.TRUE;
if (status == STATUS_RUNNING || status == STATUS_NORMAL)
return LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf("cannot close running coroutine"));
status = STATUS_DEAD;
if (thread != null)
thread.interrupt();
cond.signalAll();
return LuaValue.TRUE;
} finally {
locker.unlock();
}
}
}
}
}

View File

@@ -269,6 +269,9 @@ public class LuaValue extends Varargs {
/** LuaString constant with value "__ipairs" for use as metatag */
public static final LuaString IPAIRS = valueOf("__ipairs");
/** LuaString constant with value "__close" for use as metatag */
public static final LuaString CLOSE = valueOf("__close");
/** LuaString constant with value "" */
public static final LuaString EMPTYSTRING = valueOf("");

View File

@@ -0,0 +1,5 @@
package org.luaj.vm2;
public interface PrototypeProvider {
Prototype prototype();
}

View File

@@ -31,11 +31,19 @@ public class Upvaldesc {
/* index of upvalue (in stack or in outer function's list) */
public final short idx;
/* whether assignments to this upvalue are forbidden by a <const> local */
public final boolean isconst;
public Upvaldesc(LuaString name, boolean instack, int idx) {
this(name, instack, idx, false);
}
public Upvaldesc(LuaString name, boolean instack, int idx, boolean isconst) {
this.name = name;
this.instack = instack;
this.idx = (short) idx;
this.isconst = isconst;
}
public String toString() {

View File

@@ -121,6 +121,7 @@ public class WeakTable implements Metatable {
} else {
if ( key == null ) {
this.key = null;
this.value = null;
}
if ( value == null ) {
this.value = null;
@@ -144,7 +145,11 @@ public class WeakTable implements Metatable {
}
public int arraykey(int max) {
// Integer keys can never be weak.
LuaValue key = strongkey();
if ( key != null && key.isinttype() ) {
int k = key.toint();
return ( k >= 1 && k <= max ) ? k : 0;
}
return 0;
}

View File

@@ -87,9 +87,10 @@ public class DumpState {
// header fields
private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_INTEGER = 8;
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_SIZET = 8;
private static final int SIZEOF_INSTRUCTION = 4;
DataOutputStream writer;
@@ -120,22 +121,39 @@ public class DumpState {
writer.writeInt(x);
}
}
void dumpInt64(long x) throws IOException {
if (IS_LITTLE_ENDIAN) {
dumpInt((int) x);
dumpInt((int) (x >> 32));
} else {
writer.writeLong(x);
}
}
void dumpString(LuaString s) throws IOException {
if (s == null) {
dumpChar(0);
return;
}
final int len = s.len().toint();
dumpInt( len+1 );
s.write( writer, 0, len );
writer.write( 0 );
long size = len + 1L;
if (size < 0xFFL) {
dumpChar((int) size);
} else {
dumpChar(0xFF);
if (SIZEOF_SIZET == 8) {
dumpInt64(size);
} else {
dumpInt((int) size);
}
}
s.write(writer, 0, len);
}
void dumpDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d);
if ( IS_LITTLE_ENDIAN ) {
dumpInt( (int) l );
dumpInt( (int) (l>>32) );
} else {
writer.writeLong(l);
}
dumpInt64(l);
}
void dumpCode( final Prototype f ) throws IOException {
@@ -163,14 +181,19 @@ public class DumpState {
case LuaValue.TNUMBER:
switch (NUMBER_FORMAT) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
writer.write(LuaValue.TNUMBER);
dumpDouble(o.todouble());
if (o.isint()) {
writer.write(LoadState.LUA_TNUMINT);
dumpInt64(o.tolong());
} else {
writer.write(LoadState.LUA_TNUMFLT);
dumpDouble(o.todouble());
}
break;
case NUMBER_FORMAT_INTS_ONLY:
if ( ! ALLOW_INTEGER_CASTING && ! o.isint() )
throw new IllegalArgumentException("not an integer: "+o);
writer.write(LuaValue.TNUMBER);
dumpInt(o.toint());
writer.write(LoadState.LUA_TNUMINT);
dumpInt64(o.tolong());
break;
case NUMBER_FORMAT_NUM_PATCH_INT32:
if ( o.isint() ) {
@@ -186,16 +209,19 @@ public class DumpState {
}
break;
case LuaValue.TSTRING:
writer.write(LuaValue.TSTRING);
writer.write(((LuaString)o).len().toint() < 0xFF ? LoadState.LUA_TSHRSTR: LoadState.LUA_TLNGSTR);
dumpString((LuaString)o);
break;
default:
throw new IllegalArgumentException("bad type for " + o);
}
}
n = f.p.length;
}
void dumpProtos(final Prototype f) throws IOException {
int n = f.p.length;
dumpInt(n);
for (i = 0; i < n; i++)
for (int i = 0; i < n; i++)
dumpFunction(f.p[i]);
}
@@ -210,29 +236,41 @@ public class DumpState {
void dumpDebug(final Prototype f) throws IOException {
int i, n;
if (strip)
dumpInt(0);
else
dumpString(f.source);
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 ? countSemanticLocals(f) : f.locvars.length;
dumpInt(n);
for (i = 0; i < n; i++) {
for (i = 0; i < f.locvars.length; i++) {
LocVars lvi = f.locvars[i];
if (strip && (lvi == null || !lvi.toclose))
continue;
dumpString(lvi.varname);
dumpInt(lvi.startpc);
dumpInt(lvi.endpc);
dumpInt(lvi.slot);
writer.writeByte(lvi.toclose ? 1 : 0);
writer.writeByte(lvi.isconst ? 1 : 0);
}
n = strip ? 0 : f.upvalues.length;
dumpInt(n);
for (i = 0; i < n; i++)
dumpString(f.upvalues[i].name);
}
private int countSemanticLocals(final Prototype f) {
int count = 0;
for (int i = 0; i < f.locvars.length; i++) {
LocVars local = f.locvars[i];
if (local != null && local.toclose)
count++;
}
return count;
}
void dumpFunction(final Prototype f) throws IOException {
dumpString(f.source);
dumpInt(f.linedefined);
dumpInt(f.lastlinedefined);
dumpChar(f.numparams);
@@ -241,6 +279,7 @@ public class DumpState {
dumpCode(f);
dumpConstants(f);
dumpUpvalues(f);
dumpProtos(f);
dumpDebug(f);
}
@@ -248,13 +287,14 @@ public class DumpState {
writer.write( LoadState.LUA_SIGNATURE );
writer.write( LoadState.LUAC_VERSION );
writer.write( LoadState.LUAC_FORMAT );
writer.write( IS_LITTLE_ENDIAN? 1: 0 );
writer.write( LoadState.LUAC_TAIL );
writer.write( SIZEOF_INT );
writer.write( SIZEOF_SIZET );
writer.write( SIZEOF_INSTRUCTION );
writer.write( SIZEOF_LUA_INTEGER );
writer.write( SIZEOF_LUA_NUMBER );
writer.write( NUMBER_FORMAT );
writer.write( LoadState.LUAC_TAIL );
dumpInt64(LoadState.LUAC_INT);
dumpDouble(LoadState.LUAC_NUM);
}
/*
@@ -263,6 +303,7 @@ public class DumpState {
public static int dump( Prototype f, OutputStream w, boolean strip ) throws IOException {
DumpState D = new DumpState(w,strip);
D.dumpHeader();
D.dumpChar(f.upvalues.length);
D.dumpFunction(f);
return D.status;
}
@@ -290,8 +331,10 @@ public class DumpState {
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_INTEGER = 8;
D.SIZEOF_LUA_NUMBER = 8;
D.dumpHeader();
D.dumpChar(f.upvalues.length);
D.dumpFunction(f);
return D.status;
}

View File

@@ -43,6 +43,7 @@ public class FuncState extends Constants {
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 toclose; /* true if some variable in the block needs __close handling */
boolean isloop; /* true if `block' is a loop */
};
@@ -143,7 +144,7 @@ public class FuncState extends Constants {
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);
f.upvalues[nups] = new Upvaldesc(name, v.k == LexState.VLOCAL, v.u.info, v.isconst);
return nups++;
}
@@ -163,12 +164,20 @@ public class FuncState extends Constants {
bl.upval = true;
}
void marktoclose(int level) {
BlockCnt bl = this.bl;
while (bl.nactvar > level)
bl = bl.previous;
bl.toclose = true;
}
static int singlevaraux(FuncState fs, LuaString n, expdesc var, int base) {
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);
var.isconst = fs.ls.dyd.actvar[fs.firstlocal + v].isconst;
if (base == 0)
fs.markupval(v); /* local will be used as an upval */
return LexState.VLOCAL;
@@ -181,6 +190,7 @@ public class FuncState extends Constants {
idx = fs.newupvalue(n, var); /* will be a new upvalue */
}
var.init(LexState.VUPVAL, idx);
var.isconst = fs.f.upvalues[idx].isconst;
return LexState.VUPVAL;
}
}
@@ -199,7 +209,7 @@ public class FuncState extends Constants {
while (i < ls.dyd.n_gt) {
LexState.Labeldesc gt = gl[i];
if (gt.nactvar > bl.nactvar) {
if (bl.upval)
if (bl.upval || bl.toclose)
patchclose(gt.pc, bl.nactvar);
gt.nactvar = bl.nactvar;
}
@@ -214,6 +224,7 @@ public class FuncState extends Constants {
bl.firstlabel = (short) ls.dyd.n_label;
bl.firstgoto = (short) ls.dyd.n_gt;
bl.upval = false;
bl.toclose = false;
bl.previous = this.bl;
this.bl = bl;
_assert(this.freereg == this.nactvar);
@@ -221,8 +232,8 @@ public class FuncState extends Constants {
void leaveblock() {
BlockCnt bl = this.bl;
if (bl.previous != null && bl.upval) {
/* create a 'jump to here' to close upvalues */
if (bl.previous != null && (bl.upval || bl.toclose)) {
/* create a 'jump to here' to close upvalues and to-be-closed locals */
int j = this.jump();
this.patchclose(j, bl.nactvar);
this.patchtohere(j);

View File

@@ -243,6 +243,8 @@ public class LexState extends Constants {
L.pushfstring( "char("+((int)token)+")" ):
L.pushfstring( String.valueOf( (char) token ) );
} else {
if (token == TK_EOS)
return "<eof>";
return luaX_tokens[token-FIRST_RESERVED];
}
}
@@ -264,10 +266,12 @@ public class LexState extends Constants {
void lexerror( String msg, int token ) {
String cid = Lua.chunkid( source.tojstring() );
boolean hasNear = msg.indexOf(" near ") >= 0;
String full = token != 0 && !hasNear ? msg+" near "+txtToken(token) : msg;
L.pushfstring( cid+":"+linenumber+": "+msg );
if ( token != 0 )
L.pushfstring( "syntax error: "+msg+" near "+txtToken(token) );
throw new LuaError(cid+":"+linenumber+": "+msg);
L.pushfstring( "syntax error: "+full );
throw new LuaError(cid+":"+linenumber+": "+full);
}
void syntaxerror( String msg ) {
@@ -371,7 +375,11 @@ public class LexState extends Constants {
try {
String trimmed = str.trim();
if (trimmed.indexOf('.') < 0 && trimmed.indexOf('e') < 0 && trimmed.indexOf('E') < 0) {
seminfo.r = LuaValue.valueOf(Long.parseLong(trimmed));
try {
seminfo.r = LuaValue.valueOf(Long.parseLong(trimmed));
} catch (NumberFormatException overflow) {
seminfo.r = LuaValue.valueOf(Double.parseDouble(trimmed));
}
} else {
seminfo.r = LuaValue.valueOf(Double.parseDouble(trimmed));
}
@@ -481,6 +489,47 @@ public class LexState extends Constants {
return (hexvalue(c1) << 4) + hexvalue(c2);
}
int readutf8esc() {
nextChar();
if (current != '{')
lexerror("missing '{' after '\\u'", TK_STRING);
nextChar();
long codepoint = 0;
int digits = 0;
while (current != '}') {
if (!isxdigit(current))
lexerror("hexadecimal digit expected", TK_STRING);
codepoint = (codepoint << 4) + hexvalue(current);
if (codepoint > 0x10FFFFL)
lexerror("UTF-8 value too large", TK_STRING);
digits++;
nextChar();
}
if (digits == 0)
lexerror("missing hexadecimal digits after '\\u{'", TK_STRING);
if (codepoint >= 0xD800L && codepoint <= 0xDFFFL)
lexerror("invalid Unicode code point", TK_STRING);
return (int) codepoint;
}
void saveutf8(int codepoint) {
if (codepoint < 0x80) {
save(codepoint);
} else if (codepoint < 0x800) {
save(0xC0 | (codepoint >> 6));
save(0x80 | (codepoint & 0x3F));
} else if (codepoint < 0x10000) {
save(0xE0 | (codepoint >> 12));
save(0x80 | ((codepoint >> 6) & 0x3F));
save(0x80 | (codepoint & 0x3F));
} else {
save(0xF0 | (codepoint >> 18));
save(0x80 | ((codepoint >> 12) & 0x3F));
save(0x80 | ((codepoint >> 6) & 0x3F));
save(0x80 | (codepoint & 0x3F));
}
}
void read_string(int del, SemInfo seminfo) {
save_and_next();
while (current != del) {
@@ -520,6 +569,11 @@ public class LexState extends Constants {
case 'x':
c = readhexaesc();
break;
case 'u':
c = readutf8esc();
saveutf8(c);
nextChar();
continue;
case '\n': /* go through */
case '\r':
save('\n');
@@ -757,6 +811,7 @@ public class LexState extends Constants {
static class expdesc {
int k; // expkind, from enumerated list, above
boolean isconst;
static class U { // originally a union
short ind_idx; // index (R/K)
short ind_t; // table(register or upvalue)
@@ -777,6 +832,7 @@ public class LexState extends Constants {
this.f.i = NO_JUMP;
this.t.i = NO_JUMP;
this.k = k;
this.isconst = false;
this.u.info = i;
}
@@ -791,6 +847,7 @@ public class LexState extends Constants {
public void setvalue(expdesc other) {
this.f.i = other.f.i;
this.k = other.k;
this.isconst = other.isconst;
this.t.i = other.t.i;
this.u._nval = other.u._nval;
this.u.ind_idx = other.u.ind_idx;
@@ -804,8 +861,10 @@ public class LexState extends Constants {
/* description of active local variable */
static class Vardesc {
final short idx; /* variable index in stack */
Vardesc(int idx) {
final boolean isconst;
Vardesc(int idx, boolean isconst) {
this.idx = (short) idx;
this.isconst = isconst;
}
};
@@ -917,21 +976,29 @@ public class LexState extends Constants {
}
int registerlocalvar(LuaString varname) {
int registerlocalvar(LuaString varname, boolean toclose, boolean isconst) {
FuncState fs = this.fs;
Prototype f = fs.f;
if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length)
f.locvars = realloc( f.locvars, fs.nlocvars*2+1 );
f.locvars[fs.nlocvars] = new LocVars(varname,0,0);
f.locvars[fs.nlocvars] = new LocVars(varname,0,0,-1,toclose,isconst);
return fs.nlocvars++;
}
void new_localvar(LuaString name) {
int reg = registerlocalvar(name);
new_localvar(name, false, false);
}
void new_localvar(LuaString name, boolean toclose) {
new_localvar(name, toclose, false);
}
void new_localvar(LuaString name, boolean toclose, boolean isconst) {
int reg = registerlocalvar(name, toclose, isconst);
fs.checklimit(dyd.n_actvar + 1, FuncState.LUAI_MAXVARS, "local variables");
if (dyd.actvar == null || dyd.n_actvar + 1 > dyd.actvar.length)
dyd.actvar = realloc(dyd.actvar, Math.max(1, dyd.n_actvar * 2));
dyd.actvar[dyd.n_actvar++] = new Vardesc(reg);
dyd.actvar[dyd.n_actvar++] = new Vardesc(reg, isconst);
}
void new_localvarliteral(String v) {
@@ -943,7 +1010,11 @@ public class LexState extends Constants {
FuncState fs = this.fs;
fs.nactvar = (short) (fs.nactvar + nvars);
for (; nvars > 0; nvars--) {
fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc;
LocVars local = fs.getlocvar(fs.nactvar - nvars);
local.startpc = fs.pc;
local.slot = fs.nactvar - nvars;
if (local.toclose)
fs.marktoclose(local.slot);
}
}
@@ -1692,6 +1763,11 @@ public class LexState extends Constants {
}
}
void check_readonly(expdesc v) {
if ((v.k == VLOCAL || v.k == VUPVAL) && v.isconst)
this.semerror("attempt to assign to const variable");
}
void assignment (LHS_assign lh, int nvars) {
expdesc e = new expdesc();
@@ -1703,6 +1779,7 @@ public class LexState extends Constants {
this.suffixedexp(nv.v);
if (nv.v.k != VINDEXED)
this.check_conflict(lh, nv.v);
this.check_readonly(nv.v);
this.assignment(nv, nvars+1);
}
else { /* assignment . `=' explist1 */
@@ -1716,11 +1793,13 @@ public class LexState extends Constants {
}
else {
fs.setoneret(e); /* close last expression */
this.check_readonly(lh.v);
fs.storevar(lh.v, e);
return; /* avoid default */
}
}
e.init(VNONRELOC, this.fs.freereg-1); /* default assignment */
this.check_readonly(lh.v);
fs.storevar(lh.v, e);
}
@@ -1988,12 +2067,27 @@ public class LexState extends Constants {
void localstat() {
/* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */
/* stat -> LOCAL NAME attrib {`,' NAME attrib} [`=' explist1] */
int nvars = 0;
int nexps;
expdesc e = new expdesc();
do {
this.new_localvar(this.str_checkname());
LuaString name = this.str_checkname();
boolean toclose = false;
boolean isconst = false;
if (this.t.token == '<') {
this.next();
LuaString attr = this.str_checkname();
this.checknext('>');
if (attr.eq_b(LuaValue.valueOf("close"))) {
toclose = true;
} else if (attr.eq_b(LuaValue.valueOf("const"))) {
isconst = true;
} else {
this.syntaxerror("unknown attribute '" + attr.tojstring() + "'");
}
}
this.new_localvar(name, toclose, isconst);
++nvars;
} while (this.testnext(','));
if (this.testnext('='))

View File

@@ -78,6 +78,7 @@ import org.luaj.vm2.Varargs;
public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
private boolean warningsEnabled = true;
/** Perform one-time initialization on the library by adding base functions
@@ -109,6 +110,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
env.set("tonumber", new tonumber());
env.set("tostring", new tostring());
env.set("type", new type());
env.set("warn", new warn(this));
env.set("xpcall", new xpcall());
next next;
@@ -255,6 +257,38 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
return NONE;
}
}
final class warn extends VarArgFunction {
final BaseLib baselib;
warn(BaseLib baselib) {
this.baselib = baselib;
}
public Varargs invoke(Varargs args) {
if (args.narg() == 1 && args.arg1().isstring()) {
String control = args.arg1().tojstring();
if ("@off".equals(control)) {
baselib.warningsEnabled = false;
return NONE;
}
if ("@on".equals(control)) {
baselib.warningsEnabled = true;
return NONE;
}
}
if (!baselib.warningsEnabled || args.narg() == 0)
return NONE;
for (int i = 1; i <= args.narg(); i++) {
LuaValue value = args.arg(i);
if (!value.isstring())
argerror(i, "string expected");
if (i == 1)
globals.STDERR.print("Lua warning: ");
globals.STDERR.print(value.tojstring());
}
globals.STDERR.print('\n');
return NONE;
}
}
// "rawequal", // (v1, v2) -> boolean

View File

@@ -58,7 +58,7 @@ import org.luaj.vm2.Varargs;
* @see LibFunction
* @see org.luaj.vm2.libs.jse.JsePlatform
* @see org.luaj.vm2.libs.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.3/manual.html#6.2">Lua 5.3 Coroutine Lib Reference</a>
*/
public class CoroutineLib extends TwoArgFunction {
@@ -76,6 +76,7 @@ public class CoroutineLib extends TwoArgFunction {
globals = env.checkglobals();
LuaTable coroutine = new LuaTable();
coroutine.set("create", new Create());
coroutine.set("close", new Close());
coroutine.set("isyieldable", new IsYieldable());
coroutine.set("resume", new Resume());
coroutine.set("running", new Running());
@@ -100,6 +101,13 @@ public class CoroutineLib extends TwoArgFunction {
}
}
static final class Close extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread t = args.checkthread(1);
return t.close();
}
}
final class IsYieldable extends VarArgFunction {
public Varargs invoke(Varargs args) {
final LuaThread r = globals.running;

View File

@@ -196,26 +196,26 @@ public class IoLib extends TwoArgFunction {
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 IO_LINESX = 4;
private static final int IO_OPEN = 5;
private static final int IO_OUTPUT = 6;
private static final int IO_POPEN = 7;
private static final int IO_READ = 8;
private static final int IO_TMPFILE = 9;
private static final int IO_TYPE = 10;
private static final int IO_WRITE = 11;
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 FILE_CLOSE = 12;
private static final int FILE_FLUSH = 13;
private static final int FILE_LINES = 14;
private static final int FILE_LINESX = 15;
private static final int FILE_READ = 16;
private static final int FILE_SEEK = 17;
private static final int FILE_SETVBUF = 18;
private static final int FILE_WRITE = 19;
private static final int IO_INDEX = 18;
private static final int LINES_ITER = 19;
private static final int IO_LINESX = 20;
private static final int FILE_LINESX = 21;
private static final int IO_INDEX = 20;
private static final int LINES_ITER = 21;
public static final String[] IO_NAMES = {
"close",

View File

@@ -78,7 +78,7 @@ import org.luaj.vm2.Varargs;
* @see org.luaj.vm2.libs.jse.JsePlatform
* @see org.luaj.vm2.libs.jme.JmePlatform
* @see org.luaj.vm2.libs.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.3/manual.html#6.7">Lua 5.3 Math Lib Reference</a>
*/
public class MathLib extends TwoArgFunction {
@@ -170,7 +170,7 @@ public class MathLib extends TwoArgFunction {
static final class fmod extends TwoArgFunction {
public LuaValue call(LuaValue xv, LuaValue yv) {
if (xv.islong() && yv.islong()) {
if (xv.islong() && yv.islong() && yv.tolong() != 0) {
return valueOf(xv.tolong() % yv.tolong());
}
return valueOf(xv.checkdouble() % yv.checkdouble());
@@ -293,15 +293,40 @@ public class MathLib extends TwoArgFunction {
}
}
static class randomseed extends OneArgFunction {
static class randomseed extends VarArgFunction {
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;
public Varargs invoke(Varargs args) {
long seed1;
long seed2;
switch (args.narg()) {
case 0:
seed1 = System.currentTimeMillis();
seed2 = System.nanoTime();
break;
case 1:
seed1 = args.checklong(1);
seed2 = 0L;
break;
default:
seed1 = args.checklong(1);
seed2 = args.checklong(2);
break;
}
random.random = new Random(mixSeeds(seed1, seed2));
return varargsOf(valueOf(seed1), valueOf(seed2));
}
private long mixSeeds(long seed1, long seed2) {
long z = seed1 ^ Long.rotateLeft(seed2, 32) ^ 0x9E3779B97F4A7C15L;
z ^= (z >>> 30);
z *= 0xBF58476D1CE4E5B9L;
z ^= (z >>> 27);
z *= 0x94D049BB133111EBL;
z ^= (z >>> 31);
return z;
}
}

View File

@@ -31,6 +31,7 @@ import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.PrototypeProvider;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.compiler.DumpState;
@@ -59,7 +60,7 @@ import org.luaj.vm2.compiler.DumpState;
* @see LibFunction
* @see org.luaj.vm2.libs.jse.JsePlatform
* @see org.luaj.vm2.libs.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.4">Lua 5.2 String Lib Reference</a>
* @see <a href="http://www.lua.org/manual/5.3/manual.html#6.4">Lua 5.3 String Lib Reference</a>
*/
public class StringLib extends TwoArgFunction {
@@ -182,7 +183,13 @@ public class StringLib extends TwoArgFunction {
LuaValue f = args.checkfunction(1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
DumpState.dump( ((LuaClosure)f).p, baos, args.optboolean(2, true) );
PrototypeProvider provider =
f instanceof PrototypeProvider ? (PrototypeProvider) f :
f instanceof LuaClosure ? (LuaClosure) f : null;
if (provider == null) {
argerror(1, "unable to dump given function");
}
DumpState.dump(provider.prototype(), baos, args.optboolean(2, true));
return LuaString.valueUsing(baos.toByteArray());
} catch (IOException e) {
return error( e.getMessage() );

View File

@@ -62,19 +62,18 @@ public class Utf8Lib extends TwoArgFunction {
LuaValue[] values = new LuaValue[end - start + 1];
int count = 0;
int pos = start - 1;
int limit = end;
while (pos < limit) {
int limit = end - 1;
while (pos <= limit) {
Decoded decoded = decode(s, pos);
if (decoded.next > limit) {
throw new LuaError("invalid UTF-8 code");
}
values[count++] = LuaValue.valueOf(decoded.codepoint);
pos = decoded.next;
}
if (pos != limit) {
throw new LuaError("invalid UTF-8 code");
if (count == values.length) {
return LuaValue.varargsOf(values);
}
return LuaValue.varargsOf(values);
LuaValue[] trimmed = new LuaValue[count];
System.arraycopy(values, 0, trimmed, 0, count);
return LuaValue.varargsOf(trimmed);
}
}
@@ -136,20 +135,20 @@ public class Utf8Lib extends TwoArgFunction {
}
return LuaValue.valueOf(i);
}
int pos = i;
int pos = i - 1;
if (n > 0) {
pos--;
while (n > 0) {
if (pos < len && isContinuation(s.luaByte(pos))) {
throw new LuaError("initial position is a continuation byte");
}
while (--n > 0) {
Decoded decoded = decode(s, pos);
pos = decoded.next;
if (pos >= len) {
return NIL;
}
Decoded decoded = decode(s, pos);
pos = decoded.next;
n--;
}
return LuaValue.valueOf(pos + 1);
}
pos--;
while (n < 0) {
if (pos <= 0) {
return NIL;