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

@@ -2,7 +2,7 @@
LuaJ is a Java implementation of Lua with JSE and JME targets. LuaJ is a Java implementation of Lua with JSE and JME targets.
This fork is maintained by Open Autonomous Connection and is built with Maven only. The current codebase targets Lua 5.4 semantics in the supported runtime paths, including the recent work around `<close>`, `<const>`, updated weak-table behavior, binary chunk compatibility, and library alignment. This fork is maintained by Open Autonomous Connection and is built with Maven only. The current codebase targets Lua 5.5 semantics in the supported runtime paths, including `global`, named vararg tables, read-only `for` variables, `table.create`, updated `utf8.offset`, binary chunk compatibility, and the existing Lua 5.4 `<close>` and `<const>` behavior.
## Modules ## Modules
@@ -80,8 +80,9 @@ For the core VM without the JSE platform layer:
- LuaJ runs on the host JVM garbage collector. Resource cleanup is not equivalent to native Lua finalization. - LuaJ runs on the host JVM garbage collector. Resource cleanup is not equivalent to native Lua finalization.
- Explicit close is still the correct pattern for files, iterators, and host-backed resources. - Explicit close is still the correct pattern for files, iterators, and host-backed resources.
- `LuaJC` is available on JSE and covered by the current Maven test path. - `LuaJC` is available on JSE and covered by the current Maven test path. For 5.5-only constructs that the legacy bytecode generator does not model directly, it falls back to generated delegate wrappers over `LuaClosure`.
- JME support remains in the Maven build, with environment-specific limitations depending on available Java ME APIs. - JME support remains in the Maven build, with environment-specific limitations depending on available Java ME APIs.
- The repository currently passes `.\mvnw.cmd -q clean test`.
## Examples ## Examples
@@ -92,10 +93,6 @@ Relevant examples are in:
- `examples/lua` - `examples/lua`
- `examples/maven` - `examples/maven`
## Status
Current implementation notes for the Lua 5.4 work are tracked in `LUA54_STATUS.md`.
## License ## License
This project is distributed under the terms in `LICENSE`. This project is distributed under the terms in `LICENSE`.

View File

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

View File

@@ -30,7 +30,7 @@ package org.luaj.vm2;
*/ */
public class Lua { public class Lua {
/** version is supplied by ant build task */ /** 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 */ /** use return values from previous op */
public static final int LUA_MULTRET = -1; public static final int LUA_MULTRET = -1;

View File

@@ -257,6 +257,8 @@ public class LuaClosure extends LuaFunction implements PrototypeProvider {
// TODO: use linked list. // TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null; UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null;
boolean[] closedLocals = hasToCloseLocals()? new boolean[stack.length]: NOCLOSEVARS; 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 // allow for debug hooks
if (globals != null && globals.debuglib != null) 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() { private boolean hasToCloseLocals() {
if (p.locvars == null) if (p.locvars == null)
return false; return false;

View File

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

View File

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

View File

@@ -21,6 +21,7 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2.compiler; package org.luaj.vm2.compiler;
import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import org.luaj.vm2.LocVars; 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 upval; /* true if some variable in the block is an upvalue */
boolean toclose; /* true if some variable in the block needs __close handling */ boolean toclose; /* true if some variable in the block needs __close handling */
boolean isloop; /* true if `block' is a loop */ 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 */ Prototype f; /* current function header */
@@ -226,6 +229,9 @@ public class FuncState extends Constants {
bl.upval = false; bl.upval = false;
bl.toclose = false; bl.toclose = false;
bl.previous = this.bl; bl.previous = this.bl;
bl.declPrevious = ls.declbl;
bl.globaldecls = null;
ls.declbl = bl;
this.bl = bl; this.bl = bl;
_assert(this.freereg == this.nactvar); _assert(this.freereg == this.nactvar);
} }
@@ -241,6 +247,7 @@ public class FuncState extends Constants {
if (bl.isloop) if (bl.isloop)
ls.breaklabel(); /* close pending breaks */ ls.breaklabel(); /* close pending breaks */
this.bl = bl.previous; this.bl = bl.previous;
ls.declbl = bl.declPrevious;
this.removevars(bl.nactvar); this.removevars(bl.nactvar);
_assert(bl.nactvar == this.nactvar); _assert(bl.nactvar == this.nactvar);
this.freereg = this.nactvar; /* free registers */ this.freereg = this.nactvar; /* free registers */

View File

@@ -23,6 +23,7 @@ package org.luaj.vm2.compiler;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable; import java.util.Hashtable;
import org.luaj.vm2.LocVars; import org.luaj.vm2.LocVars;
@@ -146,11 +147,12 @@ public class LexState extends Constants {
LuaString source; /* current source name */ LuaString source; /* current source name */
LuaString envn; /* environment variable name */ LuaString envn; /* environment variable name */
byte decpoint; /* locale decimal point */ byte decpoint; /* locale decimal point */
FuncState.BlockCnt declbl; /* lexical declaration scope chain */
/* ORDER RESERVED */ /* ORDER RESERVED */
final static String luaX_tokens [] = { final static String luaX_tokens [] = {
"and", "break", "continue", "do", "else", "elseif", "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", "in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while", "return", "then", "true", "until", "while",
"..", "...", "//", "<<", ">>", "==", ">=", "<=", "~=", "..", "...", "//", "<<", ">>", "==", ">=", "<=", "~=",
@@ -160,12 +162,12 @@ public class LexState extends Constants {
final static int final static int
/* terminal symbols denoted by reserved words */ /* 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_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_END=263, TK_FALSE=264, TK_FOR=265, TK_FUNCTION=266, TK_GLOBAL=267, TK_GOTO=268, TK_IF=269,
TK_IN=269, TK_LOCAL=270, TK_NIL=271, TK_NOT=272, TK_OR=273, TK_REPEAT=274, TK_IN=270, TK_LOCAL=271, TK_NIL=272, TK_NOT=273, TK_OR=274, TK_REPEAT=275,
TK_RETURN=275, TK_THEN=276, TK_TRUE=277, TK_UNTIL=278, TK_WHILE=279, TK_RETURN=276, TK_THEN=277, TK_TRUE=278, TK_UNTIL=279, TK_WHILE=280,
/* other terminal symbols */ /* 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_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=289, TK_EOS=290, TK_NUMBER=291, TK_NAME=292, TK_STRING=293; 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 FIRST_RESERVED = TK_AND;
final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED; 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 */ /* description of pending goto statements and label statements */
static class Labeldesc { static class Labeldesc {
@@ -1028,14 +1042,48 @@ public class LexState extends Constants {
LuaString varname = this.str_checkname(); LuaString varname = this.str_checkname();
FuncState fs = this.fs; FuncState fs = this.fs;
if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ if (FuncState.singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */
expdesc key = new expdesc(); if (!resolveglobal(varname, var))
FuncState.singlevaraux(fs, this.envn, var, 1); /* get environment variable */ this.semerror("variable '" + varname.tojstring() + "' is not declared");
_assert(var.k == VLOCAL || var.k == VUPVAL);
this.codestring(key, varname); /* key is variable name */
fs.indexed(var, key); /* env[varname] */
} }
} }
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) { void adjust_assign(int nvars, int nexps, expdesc e) {
FuncState fs = this.fs; FuncState fs = this.fs;
int extra = nvars - nexps; int extra = nvars - nexps;
@@ -1337,7 +1385,9 @@ public class LexState extends Constants {
/* parlist -> [ param { `,' param } ] */ /* parlist -> [ param { `,' param } ] */
FuncState fs = this.fs; FuncState fs = this.fs;
Prototype f = fs.f; Prototype f = fs.f;
int firstparam = fs.nactvar;
int nparams = 0; int nparams = 0;
LuaString varargname = null;
f.is_vararg = 0; f.is_vararg = 0;
if (this.t.token != ')') { /* is `parlist' not empty? */ if (this.t.token != ')') { /* is `parlist' not empty? */
do { do {
@@ -1350,14 +1400,19 @@ public class LexState extends Constants {
case TK_DOTS: { /* param . `...' */ case TK_DOTS: { /* param . `...' */
this.next(); this.next();
f.is_vararg = 1; f.is_vararg = 1;
if (this.t.token == TK_NAME) {
varargname = this.str_checkname();
this.new_localvar(varargname);
}
break; break;
} }
default: this.syntaxerror("<name> or " + LUA_QL("...") + " expected"); default: this.syntaxerror("<name> or " + LUA_QL("...") + " expected");
} }
} while ((f.is_vararg==0) && this.testnext(',')); } while ((f.is_vararg==0) && this.testnext(','));
} }
this.adjustlocalvars(nparams); this.adjustlocalvars(nparams + (varargname != null ? 1 : 0));
f.numparams = fs.nactvar; f.numparams = firstparam + nparams;
f.varargname = varargname;
fs.reserveregs(fs.nactvar); /* reserve register for parameters */ fs.reserveregs(fs.nactvar); /* reserve register for parameters */
} }
@@ -1764,7 +1819,7 @@ public class LexState extends Constants {
} }
void check_readonly(expdesc v) { 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"); 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_INDEX);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP);
this.new_localvar(varname); this.new_localvar(varname, false, true);
this.checknext('='); this.checknext('=');
this.exp1(); /* initial value */ this.exp1(); /* initial value */
this.checknext(','); 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_STATE);
this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL); this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL);
/* create declared variables */ /* create declared variables */
this.new_localvar(indexname); this.new_localvar(indexname, false, true);
while (this.testnext(',')) { while (this.testnext(',')) {
this.new_localvar(this.str_checkname()); this.new_localvar(this.str_checkname(), false, true);
++nvars; ++nvars;
} }
this.checknext(TK_IN); this.checknext(TK_IN);
@@ -2100,6 +2155,70 @@ public class LexState extends Constants {
this.adjustlocalvars(nvars); 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) { boolean funcname(expdesc v) {
/* funcname -> NAME {field} [`:' NAME] */ /* funcname -> NAME {field} [`:' NAME] */
@@ -2216,6 +2335,10 @@ public class LexState extends Constants {
this.localstat(); this.localstat();
break; break;
} }
case TK_GLOBAL: {
this.globalstat();
break;
}
case TK_DBCOLON: { /* stat -> label */ case TK_DBCOLON: { /* stat -> label */
next(); /* skip double colon */ next(); /* skip double colon */
labelstat(str_checkname(), line); labelstat(str_checkname(), line);

View File

@@ -65,6 +65,7 @@ public class TableLib extends TwoArgFunction {
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable(); LuaTable table = new LuaTable();
table.set("concat", new concat()); table.set("concat", new concat());
table.set("create", new create());
table.set("insert", new insert()); table.set("insert", new insert());
table.set("move", new move()); table.set("move", new move());
table.set("pack", new pack()); 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 // "move" (a1, f, e, t [,a2]) -> a2
static class move extends VarArgFunction { static class move extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {

View File

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

View File

@@ -128,7 +128,7 @@ public class LuaJC implements Globals.Loader {
} }
private boolean requiresInterpreterDelegate(Prototype p) { private boolean requiresInterpreterDelegate(Prototype p) {
return hasToCloseLocals(p) || usesInterpreterOnlyRuntimeSemantics(p); return hasToCloseLocals(p) || hasNamedVarargTables(p) || usesInterpreterOnlyRuntimeSemantics(p);
} }
private boolean hasToCloseLocals(Prototype p) { private boolean hasToCloseLocals(Prototype p) {
@@ -148,6 +148,20 @@ public class LuaJC implements Globals.Loader {
return false; return false;
} }
private boolean hasNamedVarargTables(Prototype p) {
if (p.varargname != null) {
return true;
}
if (p.p != null) {
for (int i = 0; i < p.p.length; i++) {
if (p.p[i] != null && hasNamedVarargTables(p.p[i])) {
return true;
}
}
}
return false;
}
private boolean usesInterpreterOnlyRuntimeSemantics(Prototype p) { private boolean usesInterpreterOnlyRuntimeSemantics(Prototype p) {
if (prototypeReferences(p, "xpcall") || prototypeReferences(p, "debug")) { if (prototypeReferences(p, "xpcall") || prototypeReferences(p, "debug")) {
return true; return true;

View File

@@ -51,7 +51,7 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
private static final String __NAME__ = "Luaj"; private static final String __NAME__ = "Luaj";
private static final String __SHORT_NAME__ = "Luaj"; private static final String __SHORT_NAME__ = "Luaj";
private static final String __LANGUAGE__ = "lua"; private static final String __LANGUAGE__ = "lua";
private static final String __LANGUAGE_VERSION__ = "5.4"; private static final String __LANGUAGE_VERSION__ = "5.5";
private static final String __ARGV__ = "arg"; private static final String __ARGV__ = "arg";
private static final String __FILENAME__ = "?"; private static final String __FILENAME__ = "?";

View File

@@ -499,7 +499,8 @@ public class FragmentsTest extends TestSuite {
LuaValue.valueOf(2), LuaValue.valueOf(2),
LuaValue.valueOf(228), LuaValue.valueOf(228),
LuaValue.valueOf(97), LuaValue.valueOf(97),
LuaValue.valueOf(2) LuaValue.valueOf(2),
LuaValue.valueOf(3)
}), }),
"local s = utf8.char(97, 228)\nlocal iter, state, var = utf8.codes(s)\nlocal _, cp = iter(state, var)\nreturn utf8.len(s), utf8.codepoint(s, 2), cp, utf8.offset(s, 2)\n"); "local s = utf8.char(97, 228)\nlocal iter, state, var = utf8.codes(s)\nlocal _, cp = iter(state, var)\nreturn utf8.len(s), utf8.codepoint(s, 2), cp, utf8.offset(s, 2)\n");
} }
@@ -550,7 +551,7 @@ public class FragmentsTest extends TestSuite {
runFragment( runFragment(
LuaValue.varargsOf(new LuaValue[] { LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(123), LuaValue.valueOf(123),
LuaValue.valueOf(84) LuaValue.valueOf(85)
}), }),
"local dumped = string.dump(function() return 123 end)\n" + "local dumped = string.dump(function() return 123 end)\n" +
"local loaded = assert(load(dumped, 'dumped', 'b'))\n" + "local loaded = assert(load(dumped, 'dumped', 'b'))\n" +
@@ -565,11 +566,21 @@ public class FragmentsTest extends TestSuite {
LuaValue.FALSE, LuaValue.FALSE,
LuaValue.valueOf(Long.MAX_VALUE), LuaValue.valueOf(Long.MAX_VALUE),
LuaValue.valueOf(Long.MIN_VALUE), LuaValue.valueOf(Long.MIN_VALUE),
LuaValue.valueOf("Lua 5.4") LuaValue.valueOf("Lua 5.5")
}), }),
"return math.type(3), math.tointeger(3.0), math.ult(-1, 1), math.maxinteger, math.mininteger, _VERSION\n"); "return math.type(3), math.tointeger(3.0), math.ult(-1, 1), math.maxinteger, math.mininteger, _VERSION\n");
} }
public void testTableCreate55() {
runFragment(
LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(3),
LuaValue.valueOf("x"),
LuaValue.valueOf("x")
}),
"local t = table.create(3, 'x')\nreturn #t, t[1], t[3]\n");
}
public void testHexFloatLiteralAndTonumber() { public void testHexFloatLiteralAndTonumber() {
runFragment( runFragment(
LuaValue.varargsOf(new LuaValue[] { LuaValue.varargsOf(new LuaValue[] {
@@ -734,6 +745,101 @@ public class FragmentsTest extends TestSuite {
} }
} }
public void testGlobalDeclaration55() {
runFragment(
LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(7),
LuaValue.valueOf(7)
}),
"global answer = 7\nlocal function f() return answer end\nreturn f(), answer\n");
}
public void testGlobalDeclarationRejectsUndeclaredName() {
Globals globals = JsePlatform.debugGlobals();
try {
switch (TEST_TYPE) {
case TEST_TYPE_LUAJC:
LuaJC.install(globals);
globals.load(new StringReader("global answer = 7\nreturn missing\n"), getName());
break;
default:
globals.compilePrototype(new StringReader("global answer = 7\nreturn missing\n"), getName());
break;
}
fail("expected LuaError");
} catch (LuaError e) {
assertTrue(e.getMessage().indexOf("is not declared") >= 0);
} catch (Exception e) {
fail(e.toString());
}
}
public void testGlobalConstRejectsAssignment55() {
Globals globals = JsePlatform.debugGlobals();
try {
switch (TEST_TYPE) {
case TEST_TYPE_LUAJC:
LuaJC.install(globals);
globals.load(new StringReader("global <const> answer = 7\nanswer = 8\n"), getName());
break;
default:
globals.compilePrototype(new StringReader("global <const> answer = 7\nanswer = 8\n"), getName());
break;
}
fail("expected LuaError");
} catch (LuaError e) {
assertTrue(e.getMessage().indexOf("attempt to assign to const variable") >= 0);
} catch (Exception e) {
fail(e.toString());
}
}
public void testNamedVarargTable55() {
runFragment(
LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(1),
LuaValue.valueOf(2),
LuaValue.valueOf(2),
LuaValue.valueOf(3),
LuaValue.valueOf(2)
}),
"local function f(a, ...rest)\n" +
" return a, rest[1], rest.n, rest[2], select('#', ...)\n" +
"end\n" +
"return f(1, 2, 3)\n");
}
public void testNamedVarargDumpRoundTrip55() {
runFragment(
LuaValue.varargsOf(new LuaValue[] {
LuaValue.valueOf(2),
LuaValue.valueOf(5)
}),
"local dumped = string.dump(function(...rest) return rest.n, rest[2] end)\n" +
"local loaded = assert(load(dumped, 'dumped', 'b'))\n" +
"return loaded(4, 5)\n");
}
public void testForLoopVariableRejectsAssignment55() {
Globals globals = JsePlatform.debugGlobals();
try {
switch (TEST_TYPE) {
case TEST_TYPE_LUAJC:
LuaJC.install(globals);
globals.load(new StringReader("for i = 1, 3 do i = 7 end\n"), getName());
break;
default:
globals.compilePrototype(new StringReader("for i = 1, 3 do i = 7 end\n"), getName());
break;
}
fail("expected LuaError");
} catch (LuaError e) {
assertTrue(e.getMessage().indexOf("attempt to assign to const variable") >= 0);
} catch (Exception e) {
fail(e.toString());
}
}
public void testCoroutineClose54() { public void testCoroutineClose54() {
runFragment( runFragment(
LuaValue.varargsOf(new LuaValue[] { LuaValue.varargsOf(new LuaValue[] {
@@ -1024,9 +1130,9 @@ public class FragmentsTest extends TestSuite {
public void testNumericForUpvalues() { public void testNumericForUpvalues() {
runFragment( LuaValue.valueOf(8), runFragment( LuaValue.valueOf(8),
"for i = 3,4 do\n"+ "for i = 3,4 do\n"+
" i = i + 5\n"+ " local j = i + 5\n"+
" local a = function()\n"+ " local a = function()\n"+
" return i\n"+ " return j\n"+
" end\n" + " end\n" +
" return a()\n"+ " return a()\n"+
"end\n"); "end\n");

View File

@@ -83,9 +83,9 @@ public class ScriptEngineTests extends TestSuite {
ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj"); ScriptEngine e = new ScriptEngineManager().getEngineByName("luaj");
ScriptEngineFactory f = e.getFactory(); ScriptEngineFactory f = e.getFactory();
assertEquals("Luaj", f.getEngineName()); assertEquals("Luaj", f.getEngineName());
assertEquals("Lua 5.4", f.getEngineVersion()); assertEquals("Lua 5.5", f.getEngineVersion());
assertEquals("lua", f.getLanguageName()); assertEquals("lua", f.getLanguageName());
assertEquals("5.4", f.getLanguageVersion()); assertEquals("5.5", f.getLanguageVersion());
} }
} }