Updated to Lua 5.5

This commit is contained in:
UnlegitDqrk
2026-03-03 11:48:26 +01:00
parent 862296b343
commit d917ba6146
14 changed files with 324 additions and 40 deletions

View File

@@ -127,8 +127,9 @@ public class LoadState {
public static final String SOURCE_BINARY_STRING = "binary string";
/** for header of binary files -- this is Lua 5.4 */
public static final int LUAC_VERSION = 0x54;
/** for header of binary files -- this is Lua 5.5 */
public static final int LUAC_VERSION = 0x55;
public static final int LUAC_VERSION_54 = 0x54;
public static final int LUAC_VERSION_53 = 0x53;
public static final int LUAC_VERSION_52 = 0x52;
@@ -177,7 +178,7 @@ public class LoadState {
}
private boolean isModernVersion() {
return luacVersion == LUAC_VERSION || luacVersion == LUAC_VERSION_53;
return luacVersion == LUAC_VERSION || luacVersion == LUAC_VERSION_54 || luacVersion == LUAC_VERSION_53;
}
/** Load a 4-byte int value from the input stream
@@ -369,7 +370,7 @@ public class LoadState {
LuaString varname = loadString();
int startpc = loadInt();
int endpc = loadInt();
if (luacVersion == LUAC_VERSION) {
if (luacVersion == LUAC_VERSION || luacVersion == LUAC_VERSION_54) {
int slot = loadInt();
boolean toclose = is.readByte() != 0;
boolean isconst = is.readByte() != 0;
@@ -405,6 +406,9 @@ public class LoadState {
f.numparams = is.readUnsignedByte();
f.is_vararg = is.readUnsignedByte();
f.maxstacksize = is.readUnsignedByte();
if (luacVersion == LUAC_VERSION) {
f.varargname = loadString();
}
f.code = loadIntArray();
if (isModernVersion()) {
loadConstants(f);

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.4";
public static final String _VERSION = "Lua 5.5";
/** use return values from previous op */
public static final int LUA_MULTRET = -1;

View File

@@ -257,6 +257,8 @@ public class LuaClosure extends LuaFunction implements PrototypeProvider {
// TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null;
boolean[] closedLocals = hasToCloseLocals()? new boolean[stack.length]: NOCLOSEVARS;
if (p.varargname != null && p.numparams < stack.length)
stack[p.numparams] = packVarargs(varargs);
// allow for debug hooks
if (globals != null && globals.debuglib != null)
@@ -633,6 +635,12 @@ public class LuaClosure extends LuaFunction implements PrototypeProvider {
}
}
private LuaTable packVarargs(Varargs varargs) {
LuaTable table = LuaValue.tableOf(varargs, 1);
table.set("n", LuaValue.valueOf(varargs.narg()));
return table;
}
private boolean hasToCloseLocals() {
if (p.locvars == null)
return false;

View File

@@ -99,6 +99,7 @@ public class Prototype {
public int lastlinedefined;
public int numparams;
public int is_vararg;
public LuaString varargname;
public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {};
private static final Prototype[] NOSUBPROTOS = {};

View File

@@ -276,6 +276,7 @@ public class DumpState {
dumpChar(f.numparams);
dumpChar(f.is_vararg);
dumpChar(f.maxstacksize);
dumpString(f.varargname);
dumpCode(f);
dumpConstants(f);
dumpUpvalues(f);

View File

@@ -21,6 +21,7 @@
******************************************************************************/
package org.luaj.vm2.compiler;
import java.util.ArrayList;
import java.util.Hashtable;
import org.luaj.vm2.LocVars;
@@ -45,6 +46,8 @@ public class FuncState extends Constants {
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 */
BlockCnt declPrevious; /* lexical declaration scope chain */
ArrayList globaldecls; /* explicit global declarations in this block */
};
Prototype f; /* current function header */
@@ -226,6 +229,9 @@ public class FuncState extends Constants {
bl.upval = false;
bl.toclose = false;
bl.previous = this.bl;
bl.declPrevious = ls.declbl;
bl.globaldecls = null;
ls.declbl = bl;
this.bl = bl;
_assert(this.freereg == this.nactvar);
}
@@ -241,6 +247,7 @@ public class FuncState extends Constants {
if (bl.isloop)
ls.breaklabel(); /* close pending breaks */
this.bl = bl.previous;
ls.declbl = bl.declPrevious;
this.removevars(bl.nactvar);
_assert(bl.nactvar == this.nactvar);
this.freereg = this.nactvar; /* free registers */

View File

@@ -23,6 +23,7 @@ package org.luaj.vm2.compiler;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import org.luaj.vm2.LocVars;
@@ -146,11 +147,12 @@ public class LexState extends Constants {
LuaString source; /* current source name */
LuaString envn; /* environment variable name */
byte decpoint; /* locale decimal point */
FuncState.BlockCnt declbl; /* lexical declaration scope chain */
/* ORDER RESERVED */
final static String luaX_tokens [] = {
"and", "break", "continue", "do", "else", "elseif",
"end", "false", "for", "function", "goto", "if",
"end", "false", "for", "function", "global", "goto", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "//", "<<", ">>", "==", ">=", "<=", "~=",
@@ -160,12 +162,12 @@ public class LexState extends Constants {
final static int
/* terminal symbols denoted by reserved words */
TK_AND=257, TK_BREAK=258, TK_CONTINUE=259, TK_DO=260, TK_ELSE=261, TK_ELSEIF=262,
TK_END=263, TK_FALSE=264, TK_FOR=265, TK_FUNCTION=266, TK_GOTO=267, TK_IF=268,
TK_IN=269, TK_LOCAL=270, TK_NIL=271, TK_NOT=272, TK_OR=273, TK_REPEAT=274,
TK_RETURN=275, TK_THEN=276, TK_TRUE=277, TK_UNTIL=278, TK_WHILE=279,
TK_END=263, TK_FALSE=264, TK_FOR=265, TK_FUNCTION=266, TK_GLOBAL=267, TK_GOTO=268, TK_IF=269,
TK_IN=270, TK_LOCAL=271, TK_NIL=272, TK_NOT=273, TK_OR=274, TK_REPEAT=275,
TK_RETURN=276, TK_THEN=277, TK_TRUE=278, TK_UNTIL=279, TK_WHILE=280,
/* other terminal symbols */
TK_CONCAT=280, TK_DOTS=281, TK_IDIV=282, TK_SHL=283, TK_SHR=284, TK_EQ=285, TK_GE=286, TK_LE=287, TK_NE=288,
TK_DBCOLON=289, TK_EOS=290, TK_NUMBER=291, TK_NAME=292, TK_STRING=293;
TK_CONCAT=281, TK_DOTS=282, TK_IDIV=283, TK_SHL=284, TK_SHR=285, TK_EQ=286, TK_GE=287, TK_LE=288, TK_NE=289,
TK_DBCOLON=290, TK_EOS=291, TK_NUMBER=292, TK_NAME=293, TK_STRING=294;
final static int FIRST_RESERVED = TK_AND;
final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED;
@@ -868,6 +870,18 @@ public class LexState extends Constants {
}
};
static final class GlobalDecl {
final LuaString name;
final boolean isconst;
final boolean wildcard;
GlobalDecl(LuaString name, boolean isconst, boolean wildcard) {
this.name = name;
this.isconst = isconst;
this.wildcard = wildcard;
}
};
/* description of pending goto statements and label statements */
static class Labeldesc {
@@ -1028,14 +1042,48 @@ public class LexState extends Constants {
LuaString varname = this.str_checkname();
FuncState fs = this.fs;
if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */
expdesc key = new expdesc();
FuncState.singlevaraux(fs, this.envn, var, 1); /* get environment variable */
_assert(var.k == VLOCAL || var.k == VUPVAL);
this.codestring(key, varname); /* key is variable name */
fs.indexed(var, key); /* env[varname] */
if (!resolveglobal(varname, var))
this.semerror("variable '" + varname.tojstring() + "' is not declared");
}
}
private void makeglobalvar(LuaString varname, expdesc var) {
expdesc key = new expdesc();
FuncState.singlevaraux(this.fs, this.envn, var, 1);
_assert(var.k == VLOCAL || var.k == VUPVAL);
this.codestring(key, varname);
this.fs.indexed(var, key);
}
private boolean resolveglobal(LuaString varname, expdesc var) {
boolean explicitSeen = false;
for (FuncState.BlockCnt block = declbl; block != null; block = block.declPrevious) {
if (block.globaldecls == null)
continue;
explicitSeen = true;
for (int i = block.globaldecls.size() - 1; i >= 0; i--) {
GlobalDecl decl = (GlobalDecl) block.globaldecls.get(i);
if (decl.wildcard || decl.name.eq_b(varname)) {
makeglobalvar(varname, var);
var.isconst = decl.isconst;
return true;
}
}
}
if (!explicitSeen) {
makeglobalvar(varname, var);
var.isconst = false;
return true;
}
return false;
}
private void registerglobaldecl(LuaString name, boolean isconst, boolean wildcard) {
if (declbl.globaldecls == null)
declbl.globaldecls = new ArrayList();
declbl.globaldecls.add(new GlobalDecl(name, isconst, wildcard));
}
void adjust_assign(int nvars, int nexps, expdesc e) {
FuncState fs = this.fs;
int extra = nvars - nexps;
@@ -1337,7 +1385,9 @@ public class LexState extends Constants {
/* parlist -> [ param { `,' param } ] */
FuncState fs = this.fs;
Prototype f = fs.f;
int firstparam = fs.nactvar;
int nparams = 0;
LuaString varargname = null;
f.is_vararg = 0;
if (this.t.token != ')') { /* is `parlist' not empty? */
do {
@@ -1350,14 +1400,19 @@ public class LexState extends Constants {
case TK_DOTS: { /* param . `...' */
this.next();
f.is_vararg = 1;
if (this.t.token == TK_NAME) {
varargname = this.str_checkname();
this.new_localvar(varargname);
}
break;
}
default: this.syntaxerror("<name> or " + LUA_QL("...") + " expected");
}
} while ((f.is_vararg==0) && this.testnext(','));
}
this.adjustlocalvars(nparams);
f.numparams = fs.nactvar;
this.adjustlocalvars(nparams + (varargname != null ? 1 : 0));
f.numparams = firstparam + nparams;
f.varargname = varargname;
fs.reserveregs(fs.nactvar); /* reserve register for parameters */
}
@@ -1764,7 +1819,7 @@ public class LexState extends Constants {
}
void check_readonly(expdesc v) {
if ((v.k == VLOCAL || v.k == VUPVAL) && v.isconst)
if (v.isconst)
this.semerror("attempt to assign to const variable");
}
@@ -1948,7 +2003,7 @@ public class LexState extends Constants {
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP);
this.new_localvar(varname);
this.new_localvar(varname, false, true);
this.checknext('=');
this.exp1(); /* initial value */
this.checknext(',');
@@ -1975,9 +2030,9 @@ public class LexState extends Constants {
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL);
/* create declared variables */
this.new_localvar(indexname);
this.new_localvar(indexname, false, true);
while (this.testnext(',')) {
this.new_localvar(this.str_checkname());
this.new_localvar(this.str_checkname(), false, true);
++nvars;
}
this.checknext(TK_IN);
@@ -2100,6 +2155,70 @@ public class LexState extends Constants {
this.adjustlocalvars(nvars);
}
private boolean parseGlobalAttribute() {
this.checknext('<');
LuaString attr = this.str_checkname();
this.checknext('>');
if (attr.eq_b(LuaValue.valueOf("const")))
return true;
if (attr.eq_b(LuaValue.valueOf("close")))
this.syntaxerror("global variables do not support attribute 'close'");
this.syntaxerror("unknown attribute '" + attr.tojstring() + "'");
return false;
}
private void storeDeclaredGlobals(ArrayList names, expdesc e, int nvars, int nexps) {
ArrayList vars = new ArrayList(nvars);
for (int i = 0; i < nvars; i++) {
expdesc var = new expdesc();
makeglobalvar((LuaString) names.get(i), var);
vars.add(var);
}
if (nexps == nvars) {
fs.setoneret(e);
fs.storevar((expdesc) vars.get(nvars - 1), e);
nvars--;
}
for (int i = nvars - 1; i >= 0; i--) {
expdesc value = new expdesc();
value.init(VNONRELOC, this.fs.freereg - 1);
fs.storevar((expdesc) vars.get(i), value);
}
}
void globalstat() {
int nvars = 0;
int nexps = 0;
expdesc e = new expdesc();
boolean defaultConst = false;
ArrayList names = new ArrayList();
ArrayList consts = new ArrayList();
this.next(); /* skip GLOBAL */
if (this.t.token == '<')
defaultConst = parseGlobalAttribute();
if (this.t.token == '*') {
this.next();
registerglobaldecl(null, defaultConst, true);
return;
}
do {
LuaString name = this.str_checkname();
boolean isconst = defaultConst;
if (this.t.token == '<')
isconst = parseGlobalAttribute();
names.add(name);
consts.add(Boolean.valueOf(isconst));
nvars++;
} while (this.testnext(','));
if (this.testnext('=')) {
nexps = this.explist(e);
this.adjust_assign(nvars, nexps, e);
storeDeclaredGlobals(names, e, nvars, nexps);
}
for (int i = 0; i < nvars; i++)
registerglobaldecl((LuaString) names.get(i), ((Boolean) consts.get(i)).booleanValue(), false);
}
boolean funcname(expdesc v) {
/* funcname -> NAME {field} [`:' NAME] */
@@ -2216,6 +2335,10 @@ public class LexState extends Constants {
this.localstat();
break;
}
case TK_GLOBAL: {
this.globalstat();
break;
}
case TK_DBCOLON: { /* stat -> label */
next(); /* skip double colon */
labelstat(str_checkname(), line);

View File

@@ -65,6 +65,7 @@ public class TableLib extends TwoArgFunction {
public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable();
table.set("concat", new concat());
table.set("create", new create());
table.set("insert", new insert());
table.set("move", new move());
table.set("pack", new pack());
@@ -125,6 +126,25 @@ public class TableLib extends TwoArgFunction {
}
}
// "create" (size [, value]) -> table
static class create extends VarArgFunction {
public Varargs invoke(Varargs args) {
int size = args.checkint(1);
if (size < 0) {
argerror(1, "size out of range");
}
LuaTable table = LuaValue.tableOf(size, 0);
if (!args.isnoneornil(2)) {
LuaValue value = args.arg(2);
table.presize(size);
for (int i = 1; i <= size; i++) {
table.set(i, value);
}
}
return table;
}
}
// "move" (a1, f, e, t [,a2]) -> a2
static class move extends VarArgFunction {
public Varargs invoke(Varargs args) {

View File

@@ -133,7 +133,8 @@ public class Utf8Lib extends TwoArgFunction {
if (i <= len && isContinuation(s.luaByte(i - 1))) {
throw new LuaError("initial position is a continuation byte");
}
return LuaValue.valueOf(i);
Decoded decoded = decode(s, i - 1);
return LuaValue.varargsOf(new LuaValue[] { LuaValue.valueOf(i), LuaValue.valueOf(decoded.next) });
}
int pos = i - 1;
if (n > 0) {
@@ -147,7 +148,8 @@ public class Utf8Lib extends TwoArgFunction {
return NIL;
}
}
return LuaValue.valueOf(pos + 1);
Decoded decoded = decode(s, pos);
return LuaValue.varargsOf(new LuaValue[] { LuaValue.valueOf(pos + 1), LuaValue.valueOf(decoded.next) });
}
while (n < 0) {
if (pos <= 0) {
@@ -162,7 +164,8 @@ public class Utf8Lib extends TwoArgFunction {
}
n++;
}
return LuaValue.valueOf(pos + 1);
Decoded decoded = decode(s, pos);
return LuaValue.varargsOf(new LuaValue[] { LuaValue.valueOf(pos + 1), LuaValue.valueOf(decoded.next) });
}
}