Updated to Lua 5.4
This commit is contained in:
@@ -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) );
|
||||
}
|
||||
|
||||
@@ -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>": "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() );
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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("");
|
||||
|
||||
5
core/src/main/java/org/luaj/vm2/PrototypeProvider.java
Normal file
5
core/src/main/java/org/luaj/vm2/PrototypeProvider.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package org.luaj.vm2;
|
||||
|
||||
public interface PrototypeProvider {
|
||||
Prototype prototype();
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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('='))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() );
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user