From 493b055a26b0f47bfd1b31d85f57fd66a92ea927 Mon Sep 17 00:00:00 2001 From: UnlegitDqrk Date: Sun, 1 Mar 2026 21:42:19 +0100 Subject: [PATCH] Started with upgrading to Lua 5.3 --- .../src/main/java/org/luaj/vm2/LoadState.java | 8 +- core/src/main/java/org/luaj/vm2/Lua.java | 60 +- .../main/java/org/luaj/vm2/LuaClosure.java | 24 + .../src/main/java/org/luaj/vm2/LuaDouble.java | 20 +- .../main/java/org/luaj/vm2/LuaInteger.java | 122 +++-- .../src/main/java/org/luaj/vm2/LuaString.java | 38 +- core/src/main/java/org/luaj/vm2/LuaTable.java | 12 +- core/src/main/java/org/luaj/vm2/LuaValue.java | 81 +++ core/src/main/java/org/luaj/vm2/Print.java | 5 + .../java/org/luaj/vm2/compiler/FuncState.java | 49 +- .../java/org/luaj/vm2/compiler/LexState.java | 126 ++--- .../java/org/luaj/vm2/libs/CoroutineLib.java | 10 +- .../main/java/org/luaj/vm2/libs/DebugLib.java | 6 + .../main/java/org/luaj/vm2/libs/MathLib.java | 60 +- .../java/org/luaj/vm2/libs/StringLib.java | 513 ++++++++++++++++++ .../main/java/org/luaj/vm2/libs/TableLib.java | 29 + grammar/Lua52.jj | 2 +- grammar/LuaParser.jj | 6 + .../org/luaj/vm2/libs/jme/JmePlatform.java | 5 +- jse/src/main/java/org/luaj/vm2/ast/Exp.java | 14 +- .../luaj/vm2/libs/jse/CoerceJavaToLua.java | 8 +- .../luaj/vm2/libs/jse/CoerceLuaToJava.java | 12 +- .../org/luaj/vm2/libs/jse/JsePlatform.java | 5 +- .../java/org/luaj/vm2/luajc/JavaBuilder.java | 6 + .../main/java/org/luaj/vm2/luajc/JavaGen.java | 6 + .../java/org/luaj/vm2/luajc/ProtoInfo.java | 6 + .../java/org/luaj/vm2/parser/LuaParser.java | 24 + .../luaj/vm2/parser/LuaParserConstants.java | 15 + .../vm2/parser/LuaParserTokenManager.java | 19 +- .../org/luaj/vm2/script/LuaScriptEngine.java | 6 +- .../test/java/org/luaj/vm2/FragmentsTest.java | 121 +++++ .../vm2/libs/jse/LuaJavaCoercionTest.java | 14 + 32 files changed, 1245 insertions(+), 187 deletions(-) diff --git a/core/src/main/java/org/luaj/vm2/LoadState.java b/core/src/main/java/org/luaj/vm2/LoadState.java index e353020c..2010eed8 100644 --- a/core/src/main/java/org/luaj/vm2/LoadState.java +++ b/core/src/main/java/org/luaj/vm2/LoadState.java @@ -237,12 +237,12 @@ public class LoadState { int e = (int)((bits >> 52) & 0x7ffL) - 1023; - if ( e >= 0 && e < 31 ) { + if ( e >= 0 && e < 63 ) { long f = bits & 0xFFFFFFFFFFFFFL; int shift = 52 - e; - long intPrecMask = ( 1L << shift ) - 1; - if ( ( f & intPrecMask ) == 0 ) { - int intValue = (int)( f >> shift ) | ( 1 << e ); + long intPrecMask = shift > 0 ? ( 1L << shift ) - 1 : 0; + if ( shift <= 52 && ( f & intPrecMask ) == 0 ) { + long intValue = shift >= 0 ? (f >> shift) | (1L << e) : (f << (-shift)) | (1L << e); return LuaInteger.valueOf( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue ); } } diff --git a/core/src/main/java/org/luaj/vm2/Lua.java b/core/src/main/java/org/luaj/vm2/Lua.java index a4148b51..2e55b55e 100644 --- a/core/src/main/java/org/luaj/vm2/Lua.java +++ b/core/src/main/java/org/luaj/vm2/Lua.java @@ -30,7 +30,7 @@ package org.luaj.vm2; */ public class Lua { /** version is supplied by ant build task */ - public static final String _VERSION = "Luaj 0.0"; + public static final String _VERSION = "Lua 5.3"; /** use return values from previous op */ public static final int LUA_MULTRET = -1; @@ -201,39 +201,45 @@ public class Lua { public static final int OP_MUL = 15; /* A B C R(A) := RK(B) * RK(C) */ public static final int OP_DIV = 16; /* A B C R(A) := RK(B) / RK(C) */ public static final int OP_IDIV = 17; /* A B C R(A) := RK(B) // RK(C) */ - public static final int OP_MOD = 18; /* A B C R(A) := RK(B) % RK(C) */ - public static final int OP_POW = 19; /* A B C R(A) := RK(B) ^ RK(C) */ - public static final int OP_UNM = 20; /* A B R(A) := -R(B) */ - public static final int OP_NOT = 21; /* A B R(A) := not R(B) */ - public static final int OP_LEN = 22; /* A B R(A) := length of R(B) */ + public static final int OP_BAND = 18; /* A B C R(A) := RK(B) & RK(C) */ + public static final int OP_BOR = 19; /* A B C R(A) := RK(B) | RK(C) */ + public static final int OP_BXOR = 20; /* A B C R(A) := RK(B) ~ RK(C) */ + public static final int OP_SHL = 21; /* A B C R(A) := RK(B) << RK(C) */ + public static final int OP_SHR = 22; /* A B C R(A) := RK(B) >> RK(C) */ + public static final int OP_MOD = 23; /* A B C R(A) := RK(B) % RK(C) */ + public static final int OP_POW = 24; /* A B C R(A) := RK(B) ^ RK(C) */ + public static final int OP_UNM = 25; /* A B R(A) := -R(B) */ + public static final int OP_BNOT = 26; /* A B R(A) := ~R(B) */ + public static final int OP_NOT = 27; /* A B R(A) := not R(B) */ + public static final int OP_LEN = 28; /* A B R(A) := length of R(B) */ - public static final int OP_CONCAT = 23; /* A B C R(A) := R(B).. ... ..R(C) */ + public static final int OP_CONCAT = 29; /* A B C R(A) := R(B).. ... ..R(C) */ - public static final int OP_JMP = 24; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ - public static final int OP_EQ = 25; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ - public static final int OP_LT = 26; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ - public static final int OP_LE = 27; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + public static final int OP_JMP = 30; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ + public static final int OP_EQ = 31; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + public static final int OP_LT = 32; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + public static final int OP_LE = 33; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ - public static final int OP_TEST = 28; /* A C if not (R(A) <=> C) then pc++ */ - public static final int OP_TESTSET = 29; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + public static final int OP_TEST = 34; /* A C if not (R(A) <=> C) then pc++ */ + public static final int OP_TESTSET = 35; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ - public static final int OP_CALL = 30; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ - public static final int OP_TAILCALL = 31; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ - public static final int OP_RETURN = 32; /* A B return R(A), ... ,R(A+B-2) (see note) */ + public static final int OP_CALL = 36; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 37; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 38; /* A B return R(A), ... ,R(A+B-2) (see note) */ - public static final int OP_FORLOOP = 33; /* A sBx R(A)+=R(A+2); + public static final int OP_FORLOOP = 39; /* A sBx R(A)+=R(A+2); if R(A) >>23)>0xff? k[b&0x0ff]: stack[b]).idiv((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); continue; + case Lua.OP_BAND: /* A B C R(A):= RK(B) & RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).band((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_BOR: /* A B C R(A):= RK(B) | RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).bor((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_BXOR: /* A B C R(A):= RK(B) ~ RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).bxor((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_SHL: /* A B C R(A):= RK(B) << RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).shl((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_SHR: /* A B C R(A):= RK(B) >> RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).shr((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mod((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); continue; @@ -364,6 +384,10 @@ public class LuaClosure extends LuaFunction { stack[a] = stack[i>>>23].neg(); continue; + case Lua.OP_BNOT: /* A B R(A):= ~R(B) */ + stack[a] = stack[i>>>23].bnot(); + continue; + case Lua.OP_NOT: /* A B R(A):= not R(B) */ stack[a] = stack[i>>>23].not(); continue; diff --git a/core/src/main/java/org/luaj/vm2/LuaDouble.java b/core/src/main/java/org/luaj/vm2/LuaDouble.java index e38daf67..d9980bde 100644 --- a/core/src/main/java/org/luaj/vm2/LuaDouble.java +++ b/core/src/main/java/org/luaj/vm2/LuaDouble.java @@ -75,8 +75,13 @@ public class LuaDouble extends LuaNumber { final double v; public static LuaNumber valueOf(double d) { - int id = (int) d; - return d==id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d); + if (!Double.isNaN(d) && !Double.isInfinite(d) && d >= Long.MIN_VALUE && d <= Long.MAX_VALUE) { + long ld = (long) d; + if (d == ld) { + return LuaInteger.valueOf(ld); + } + } + return new LuaDouble(d); } /** Don't allow ints to be boxed by DoubleValues */ @@ -103,13 +108,14 @@ public class LuaDouble extends LuaNumber { public double optdouble(double defval) { return v; } public int optint(int defval) { return (int) (long) v; } - public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long)v); } + public LuaInteger optinteger(LuaInteger defval) { return islong()? LuaInteger.valueOf((long) v): defval; } public long optlong(long defval) { return (long) v; } - public LuaInteger checkinteger() { return LuaInteger.valueOf( (int) (long) v ); } + public LuaInteger checkinteger() { if (!islong()) argerror("integer"); return LuaInteger.valueOf((long) v); } // unary operators public LuaValue neg() { return valueOf(-v); } + public LuaValue bnot() { if (!islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(~((long) v)); } // object equality, used for key comparison public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; } @@ -122,6 +128,7 @@ public class LuaDouble extends LuaNumber { public boolean raweq( LuaValue val ) { return val.raweq(v); } public boolean raweq( double val ) { return v == val; } public boolean raweq( int val ) { return v == val; } + public boolean raweq( long val ) { return v == val; } // basic binary arithmetic public LuaValue add( LuaValue rhs ) { return rhs.add(v); } @@ -146,6 +153,11 @@ public class LuaDouble extends LuaNumber { public LuaValue idiv( double rhs ) { return LuaDouble.didiv(v,rhs); } public LuaValue idiv( int rhs ) { return LuaDouble.didiv(v,rhs); } public LuaValue idivInto( double lhs ) { return LuaDouble.didiv(lhs,v); } + public LuaValue band( LuaValue rhs ) { if (!islong()) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.band(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) v) & n.tolong()); } + public LuaValue bor( LuaValue rhs ) { if (!islong()) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bor(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) v) | n.tolong()); } + public LuaValue bxor( LuaValue rhs ) { if (!islong()) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bxor(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) v) ^ n.tolong()); } + public LuaValue shl( LuaValue rhs ) { if (!islong()) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shl(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(LuaInteger.luaShiftLeft((long) v, n.tolong())); } + public LuaValue shr( LuaValue rhs ) { if (!islong()) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shr(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(LuaInteger.luaShiftRight((long) v, n.tolong())); } public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); } public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); } diff --git a/core/src/main/java/org/luaj/vm2/LuaInteger.java b/core/src/main/java/org/luaj/vm2/LuaInteger.java index 36f7e5d9..240ff72b 100644 --- a/core/src/main/java/org/luaj/vm2/LuaInteger.java +++ b/core/src/main/java/org/luaj/vm2/LuaInteger.java @@ -24,7 +24,7 @@ package org.luaj.vm2; import org.luaj.vm2.libs.MathLib; /** - * Extension of {@link LuaNumber} which can hold a Java int as its value. + * Extension of {@link LuaNumber} which can hold a Java long as its value. *

* These instance are not instantiated directly by clients, but indirectly * via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)} @@ -59,25 +59,23 @@ public class LuaInteger extends LuaNumber { * @see LuaValue#valueOf(int) * @see LuaValue#valueOf(double) */ - public static LuaNumber valueOf(long l) { + public static LuaInteger valueOf(long l) { int i = (int) l; - return l==i? (i<=255 && i>=-256? intValues[i+256]: - (LuaNumber) new LuaInteger(i)): - (LuaNumber) LuaDouble.valueOf(l); + return l == i && i <= 255 && i >= -256 ? intValues[i+256] : new LuaInteger(l); } /** The value being held by this instance. */ - public final int v; + public final long v; /** * Package protected constructor. * @see LuaValue#valueOf(int) **/ - LuaInteger(int i) { + LuaInteger(long i) { this.v = i; } - public boolean isint() { return true; } + public boolean isint() { return v == (int) v; } public boolean isinttype() { return true; } public boolean islong() { return true; } @@ -85,33 +83,33 @@ public class LuaInteger extends LuaNumber { public char tochar() { return (char) v; } public double todouble() { return v; } public float tofloat() { return v; } - public int toint() { return v; } + public int toint() { return (int) v; } public long tolong() { return v; } public short toshort() { return (short) v; } public double optdouble(double defval) { return v; } - public int optint(int defval) { return v; } + public int optint(int defval) { return (int) v; } public LuaInteger optinteger(LuaInteger defval) { return this; } public long optlong(long defval) { return v; } public String tojstring() { - return Integer.toString(v); + return Long.toString(v); } public LuaString strvalue() { - return LuaString.valueOf(Integer.toString(v)); + return LuaString.valueOf(Long.toString(v)); } public LuaString optstring(LuaString defval) { - return LuaString.valueOf(Integer.toString(v)); + return LuaString.valueOf(Long.toString(v)); } public LuaValue tostring() { - return LuaString.valueOf(Integer.toString(v)); + return LuaString.valueOf(Long.toString(v)); } public String optjstring(String defval) { - return Integer.toString(v); + return Long.toString(v); } public LuaInteger checkinteger() { @@ -123,15 +121,16 @@ public class LuaInteger extends LuaNumber { } public int hashCode() { - return v; + return hashCode(v); } - public static int hashCode(int x) { - return x; + public static int hashCode(long x) { + return (int) (x ^ (x >>> 32)); } // unary operators public LuaValue neg() { return valueOf(-(long)v); } + public LuaValue bnot() { return valueOf(~v); } // object equality, used for key comparison public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)o).v == v: false; } @@ -144,60 +143,66 @@ public class LuaInteger extends LuaNumber { public boolean raweq( LuaValue val ) { return val.raweq(v); } public boolean raweq( double val ) { return v == val; } public boolean raweq( int val ) { return v == val; } + public boolean raweq( long val ) { return v == val; } // arithmetic operators - public LuaValue add( LuaValue rhs ) { return rhs.add(v); } + public LuaValue add( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.add(rhs): (n.isinttype()? LuaInteger.valueOf(v + n.tolong()): LuaDouble.valueOf(v + n.todouble())); } public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); } - public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + (long)v); } - public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); } + public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + v); } + public LuaValue sub( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.sub(rhs): (n.isinttype()? LuaInteger.valueOf(v - n.tolong()): LuaDouble.valueOf(v - n.todouble())); } public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); } - public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); } + public LuaValue sub( int rhs ) { return LuaInteger.valueOf(v - rhs); } public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); } - public LuaValue subFrom( int lhs ) { return LuaInteger.valueOf(lhs - (long)v); } - public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); } + public LuaValue subFrom( int lhs ) { return LuaInteger.valueOf(lhs - v); } + public LuaValue mul( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.mul(rhs): (n.isinttype()? LuaInteger.valueOf(v * n.tolong()): LuaDouble.valueOf(v * n.todouble())); } public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); } - public LuaValue mul( int lhs ) { return LuaInteger.valueOf(lhs * (long)v); } - public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); } + public LuaValue mul( int lhs ) { return LuaInteger.valueOf(lhs * v); } + public LuaValue pow( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.pow(rhs): MathLib.dpow(v, n.todouble()); } public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); } public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); } public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); } public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); } - public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); } - public LuaValue idiv( LuaValue rhs ) { return rhs.idivInto(v); } + public LuaValue div( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.div(rhs): LuaDouble.ddiv(v, n.todouble()); } + public LuaValue idiv( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.idiv(rhs): (n.isinttype()? LuaInteger.valueOf(luaFloorDiv(v, n.tolong())): LuaDouble.didiv(v, n.todouble())); } public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); } public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); } public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); } public LuaValue idiv( double rhs ) { return LuaDouble.didiv(v,rhs); } - public LuaValue idiv( int rhs ) { return LuaDouble.didiv(v,rhs); } + public LuaValue idiv( int rhs ) { return LuaInteger.valueOf(luaFloorDiv(v, rhs)); } public LuaValue idivInto( double lhs ) { return LuaDouble.didiv(lhs,v); } - public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } + public LuaValue band( LuaValue rhs ) { LuaValue n = rhs.tonumber(); if (n.isnil()) return super.band(rhs); if (!n.islong()) throw bitwiseError(); return LuaInteger.valueOf(v & n.tolong()); } + public LuaValue bor( LuaValue rhs ) { LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bor(rhs); if (!n.islong()) throw bitwiseError(); return LuaInteger.valueOf(v | n.tolong()); } + public LuaValue bxor( LuaValue rhs ) { LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bxor(rhs); if (!n.islong()) throw bitwiseError(); return LuaInteger.valueOf(v ^ n.tolong()); } + public LuaValue shl( LuaValue rhs ) { LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shl(rhs); if (!n.islong()) throw bitwiseError(); return LuaInteger.valueOf(luaShiftLeft(v, n.tolong())); } + public LuaValue shr( LuaValue rhs ) { LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shr(rhs); if (!n.islong()) throw bitwiseError(); return LuaInteger.valueOf(luaShiftRight(v, n.tolong())); } + public LuaValue mod( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.mod(rhs): (n.isinttype()? LuaInteger.valueOf(luaFloorMod(v, n.tolong())): LuaDouble.dmod(v, n.todouble())); } public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); } - public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); } + public LuaValue mod( int rhs ) { return LuaInteger.valueOf(luaFloorMod(v, rhs)); } public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); } // relational operators - public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); } + public LuaValue lt( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.lt(rhs): (n.isinttype()? (v < n.tolong()? TRUE: FALSE): (v < n.todouble()? TRUE: FALSE)); } public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; } public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; } - public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); } + public boolean lt_b( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.lt_b(rhs): (n.isinttype()? v < n.tolong(): v < n.todouble()); } public boolean lt_b( int rhs ) { return v < rhs; } public boolean lt_b( double rhs ) { return v < rhs; } - public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); } + public LuaValue lteq( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.lteq(rhs): (n.isinttype()? (v <= n.tolong()? TRUE: FALSE): (v <= n.todouble()? TRUE: FALSE)); } public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; } public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; } - public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); } + public boolean lteq_b( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.lteq_b(rhs): (n.isinttype()? v <= n.tolong(): v <= n.todouble()); } public boolean lteq_b( int rhs ) { return v <= rhs; } public boolean lteq_b( double rhs ) { return v <= rhs; } - public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); } + public LuaValue gt( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.gt(rhs): (n.isinttype()? (v > n.tolong()? TRUE: FALSE): (v > n.todouble()? TRUE: FALSE)); } public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; } public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; } - public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); } + public boolean gt_b( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.gt_b(rhs): (n.isinttype()? v > n.tolong(): v > n.todouble()); } public boolean gt_b( int rhs ) { return v > rhs; } public boolean gt_b( double rhs ) { return v > rhs; } - public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); } + public LuaValue gteq( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.gteq(rhs): (n.isinttype()? (v >= n.tolong()? TRUE: FALSE): (v >= n.todouble()? TRUE: FALSE)); } public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; } public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; } - public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); } + public boolean gteq_b( LuaValue rhs ) { LuaValue n = rhs.tonumber(); return n.isnil()? super.gteq_b(rhs): (n.isinttype()? v >= n.tolong(): v >= n.todouble()); } public boolean gteq_b( int rhs ) { return v >= rhs; } public boolean gteq_b( double rhs ) { return v >= rhs; } @@ -205,7 +210,7 @@ public class LuaInteger extends LuaNumber { public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; } public int checkint() { - return v; + return (int) v; } public long checklong() { return v; @@ -220,4 +225,41 @@ public class LuaInteger extends LuaNumber { return valueOf( String.valueOf(v) ); } + private static long luaFloorDiv(long lhs, long rhs) { + if (rhs == 0) { + throw new LuaError("attempt to divide by zero"); + } + long quotient = lhs / rhs; + long remainder = lhs % rhs; + if (remainder != 0 && ((lhs ^ rhs) < 0)) { + quotient--; + } + return quotient; + } + + private static long luaFloorMod(long lhs, long rhs) { + if (rhs == 0) { + throw new LuaError("attempt to divide by zero"); + } + return lhs - rhs * luaFloorDiv(lhs, rhs); + } + + static long luaShiftLeft(long lhs, long rhs) { + if (rhs < 0) { + return luaShiftRight(lhs, -rhs); + } + return rhs >= Long.SIZE ? 0L : lhs << rhs; + } + + static long luaShiftRight(long lhs, long rhs) { + if (rhs < 0) { + return luaShiftLeft(lhs, -rhs); + } + return rhs >= Long.SIZE ? 0L : lhs >>> rhs; + } + + private static LuaError bitwiseError() { + return new LuaError("number has no integer representation"); + } + } diff --git a/core/src/main/java/org/luaj/vm2/LuaString.java b/core/src/main/java/org/luaj/vm2/LuaString.java index 5182993d..e3198728 100644 --- a/core/src/main/java/org/luaj/vm2/LuaString.java +++ b/core/src/main/java/org/luaj/vm2/LuaString.java @@ -262,6 +262,7 @@ public class LuaString extends LuaValue { // unary operators public LuaValue neg() { double d = scannumber(); return Double.isNaN(d)? super.neg(): valueOf(-d); } + public LuaValue bnot() { double d = scannumber(); if (Double.isNaN(d)) return super.bnot(); if (d != (long) d) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(~((long) d)); } // basic binary arithmetic public LuaValue add( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(ADD,rhs): rhs.add(d); } @@ -287,6 +288,11 @@ public class LuaString extends LuaValue { public LuaValue idiv( double rhs ) { return LuaDouble.didiv(checkarith(),rhs); } public LuaValue idiv( int rhs ) { return LuaDouble.didiv(checkarith(),rhs); } public LuaValue idivInto( double lhs ) { return LuaDouble.didiv(lhs, checkarith()); } + public LuaValue band( LuaValue rhs ) { double d = scannumber(); if (Double.isNaN(d)) return super.band(rhs); if (d != (long) d) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.band(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) d) & n.tolong()); } + public LuaValue bor( LuaValue rhs ) { double d = scannumber(); if (Double.isNaN(d)) return super.bor(rhs); if (d != (long) d) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bor(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) d) | n.tolong()); } + public LuaValue bxor( LuaValue rhs ) { double d = scannumber(); if (Double.isNaN(d)) return super.bxor(rhs); if (d != (long) d) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.bxor(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(((long) d) ^ n.tolong()); } + public LuaValue shl( LuaValue rhs ) { double d = scannumber(); if (Double.isNaN(d)) return super.shl(rhs); if (d != (long) d) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shl(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(LuaInteger.luaShiftLeft((long) d, n.tolong())); } + public LuaValue shr( LuaValue rhs ) { double d = scannumber(); if (Double.isNaN(d)) return super.shr(rhs); if (d != (long) d) throw new LuaError("number has no integer representation"); LuaValue n = rhs.tonumber(); if (n.isnil()) return super.shr(rhs); if (!n.islong()) throw new LuaError("number has no integer representation"); return LuaInteger.valueOf(LuaInteger.luaShiftRight((long) d, n.tolong())); } public LuaValue mod( LuaValue rhs ) { double d = scannumber(); return Double.isNaN(d)? arithmt(MOD,rhs): rhs.modFrom(d); } public LuaValue mod( double rhs ) { return LuaDouble.dmod(checkarith(), rhs); } public LuaValue mod( int rhs ) { return LuaDouble.dmod(checkarith(), rhs); } @@ -344,10 +350,16 @@ public class LuaString extends LuaValue { return (int) (long) checkdouble(); } public LuaInteger checkinteger() { - return valueOf(checkint()); + double d = scannumber(); + if (Double.isNaN(d) || d != (long) d) + argerror("integer"); + return LuaInteger.valueOf((long) d); } public long checklong() { - return (long) checkdouble(); + double d = scannumber(); + if (Double.isNaN(d) || d != (long) d) + argerror("integer"); + return (long) d; } public double checkdouble() { double d = scannumber(); @@ -765,8 +777,11 @@ public class LuaString extends LuaValue { while ( i=j ) return Double.NaN; - if ( m_bytes[i]=='0' && i+1= end) + return Double.NaN; + for ( int i=start; i='0'&&m_bytes[i]<='9')? '0': m_bytes[i]>='A'&&m_bytes[i]<='Z'? ('A'-10): ('a'-10)); if ( digit < 0 || digit >= base ) diff --git a/core/src/main/java/org/luaj/vm2/LuaTable.java b/core/src/main/java/org/luaj/vm2/LuaTable.java index a35dcc3f..4a764829 100644 --- a/core/src/main/java/org/luaj/vm2/LuaTable.java +++ b/core/src/main/java/org/luaj/vm2/LuaTable.java @@ -236,7 +236,7 @@ public class LuaTable extends LuaValue implements Metatable { } public LuaValue rawget( LuaValue key ) { - if ( key.isinttype() ) { + if ( key.isint() ) { 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.isinttype() || !arrayset(key.toint(), value) ) + if ( !key.isint() || !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.isinttype() ) { + if ( key.isint() ) { i = key.toint(); if ( i>0 && i<=array.length ) { break; @@ -473,7 +473,7 @@ public class LuaTable extends LuaValue implements Metatable { } if ( checkLoadFactor() ) { if ( (m_metatable == null || !m_metatable.useWeakValues()) - && key.isinttype() && key.toint() > 0 ) { + && key.isint() && key.toint() > 0 ) { // a rehash might make room in the array portion for this key. rehash( key.toint() ); if ( arrayset(key.toint(), value) ) @@ -767,9 +767,9 @@ public class LuaTable extends LuaValue implements Metatable { } protected static Entry defaultEntry(LuaValue key, LuaValue value) { - if ( key.isinttype() ) { + if ( key.isint() ) { return new IntKeyEntry( key.toint(), value ); - } else if (value.type() == TNUMBER) { + } else if (value.type() == TNUMBER && !value.isinttype()) { return new NumberValueEntry( key, value.todouble() ); } else { return new NormalEntry( key, value ); diff --git a/core/src/main/java/org/luaj/vm2/LuaValue.java b/core/src/main/java/org/luaj/vm2/LuaValue.java index d13b413e..1862f206 100644 --- a/core/src/main/java/org/luaj/vm2/LuaValue.java +++ b/core/src/main/java/org/luaj/vm2/LuaValue.java @@ -216,6 +216,24 @@ public class LuaValue extends Varargs { /** LuaString constant with value "__idiv" for use as metatag */ public static final LuaString IDIV = valueOf("__idiv"); + /** LuaString constant with value "__band" for use as metatag */ + public static final LuaString BAND = valueOf("__band"); + + /** LuaString constant with value "__bor" for use as metatag */ + public static final LuaString BOR = valueOf("__bor"); + + /** LuaString constant with value "__bxor" for use as metatag */ + public static final LuaString BXOR = valueOf("__bxor"); + + /** LuaString constant with value "__shl" for use as metatag */ + public static final LuaString SHL = valueOf("__shl"); + + /** LuaString constant with value "__shr" for use as metatag */ + public static final LuaString SHR = valueOf("__shr"); + + /** LuaString constant with value "__bnot" for use as metatag */ + public static final LuaString BNOT = valueOf("__bnot"); + /** LuaString constant with value "__mul" for use as metatag */ public static final LuaString MUL = valueOf("__mul"); @@ -2010,6 +2028,13 @@ public class LuaValue extends Varargs { * @throws LuaError if {@code this} is not a table or string, and has no {@link #UNM} metatag */ public LuaValue neg() { return checkmetatag(UNM, "attempt to perform arithmetic on ").call(this); } + + /** Unary bitwise not: return bitwise inverse value {@code (~this)}. + * @return numeric inverse as {@link LuaNumber} if integer-coercible, + * or metatag processing result if {@link #BNOT} metatag is defined + * @throws LuaError if {@code this} cannot be represented as an integer + */ + public LuaValue bnot() { return checkmetatag(BNOT, "attempt to perform bitwise operation on ").call(this); } /** Length operator: return lua length of object {@code (#this)} including metatag processing as java int * @return length as defined by the lua # operator @@ -2141,6 +2166,15 @@ public class LuaValue extends Varargs { */ public boolean raweq( int val ) { return false; } + /** Equals: Perform direct equality comparison with a long value + * without metatag processing. + * @param val The long value to compare with. + * @return true if {@code this} is a {@link LuaNumber} + * whose value equals val, + * otherwise false + */ + public boolean raweq( long val ) { return false; } + /** Perform equality testing metatag processing * @param lhs left-hand-side of equality expression * @param lhsmt metatag value for left-hand-side @@ -2403,6 +2437,16 @@ public class LuaValue extends Varargs { public LuaValue div( LuaValue rhs ) { return arithmt(DIV,rhs); } public LuaValue idiv( LuaValue rhs ) { return arithmt(IDIV,rhs); } + + public LuaValue band( LuaValue rhs ) { return arithmt(BAND,rhs); } + + public LuaValue bor( LuaValue rhs ) { return arithmt(BOR,rhs); } + + public LuaValue bxor( LuaValue rhs ) { return arithmt(BXOR,rhs); } + + public LuaValue shl( LuaValue rhs ) { return arithmt(SHL,rhs); } + + public LuaValue shr( LuaValue rhs ) { return arithmt(SHR,rhs); } /** Divide: Perform numeric divide operation by another value * of double type without metatag processing @@ -2420,6 +2464,16 @@ public class LuaValue extends Varargs { public LuaValue div( double rhs ) { return aritherror("div"); } public LuaValue idiv( double rhs ) { return aritherror("idiv"); } + + public LuaValue band( double rhs ) { return aritherror("band"); } + + public LuaValue bor( double rhs ) { return aritherror("bor"); } + + public LuaValue bxor( double rhs ) { return aritherror("bxor"); } + + public LuaValue shl( double rhs ) { return aritherror("shl"); } + + public LuaValue shr( double rhs ) { return aritherror("shr"); } /** Divide: Perform numeric divide operation by another value * of int type without metatag processing @@ -2437,6 +2491,16 @@ public class LuaValue extends Varargs { public LuaValue div( int rhs ) { return aritherror("div"); } public LuaValue idiv( int rhs ) { return aritherror("idiv"); } + + public LuaValue band( int rhs ) { return aritherror("band"); } + + public LuaValue bor( int rhs ) { return aritherror("bor"); } + + public LuaValue bxor( int rhs ) { return aritherror("bxor"); } + + public LuaValue shl( int rhs ) { return aritherror("shl"); } + + public LuaValue shr( int rhs ) { return aritherror("shr"); } /** Reverse-divide: Perform numeric divide operation into another value * with metatag processing @@ -2454,6 +2518,16 @@ public class LuaValue extends Varargs { public LuaValue divInto(double lhs) { return arithmtwith(DIV,lhs); } public LuaValue idivInto(double lhs) { return arithmtwith(IDIV,lhs); } + + public LuaValue bandInto(double lhs) { return arithmtwith(BAND,lhs); } + + public LuaValue borInto(double lhs) { return arithmtwith(BOR,lhs); } + + public LuaValue bxorInto(double lhs) { return arithmtwith(BXOR,lhs); } + + public LuaValue shlInto(double lhs) { return arithmtwith(SHL,lhs); } + + public LuaValue shrInto(double lhs) { return arithmtwith(SHR,lhs); } /** Modulo: Perform numeric modulo operation with another value * of unknown type, @@ -3173,6 +3247,13 @@ public class LuaValue extends Varargs { * @return {@link LuaInteger} instance, possibly pooled, whose value is i */ public static LuaInteger valueOf(int i) { return LuaInteger.valueOf(i); } + + /** Convert java long to a {@link LuaValue}. + * + * @param l long value to convert + * @return {@link LuaInteger} instance, possibly pooled, whose value is l + */ + public static LuaInteger valueOf(long l) { return LuaInteger.valueOf(l); } /** Convert java double to a {@link LuaValue}. * This may return a {@link LuaInteger} or {@link LuaDouble} depending diff --git a/core/src/main/java/org/luaj/vm2/Print.java b/core/src/main/java/org/luaj/vm2/Print.java index 1965658d..b655bf8f 100644 --- a/core/src/main/java/org/luaj/vm2/Print.java +++ b/core/src/main/java/org/luaj/vm2/Print.java @@ -277,6 +277,11 @@ public class Print extends Lua { case OP_MUL: case OP_DIV: case OP_IDIV: + case OP_BAND: + case OP_BOR: + case OP_BXOR: + case OP_SHL: + case OP_SHR: case OP_POW: case OP_EQ: case OP_LT: diff --git a/core/src/main/java/org/luaj/vm2/compiler/FuncState.java b/core/src/main/java/org/luaj/vm2/compiler/FuncState.java index b0506963..9f2c4ddb 100644 --- a/core/src/main/java/org/luaj/vm2/compiler/FuncState.java +++ b/core/src/main/java/org/luaj/vm2/compiler/FuncState.java @@ -890,6 +890,21 @@ public class FuncState extends Constants { case OP_IDIV: r = v1.idiv(v2); break; + case OP_BAND: + r = v1.band(v2); + break; + case OP_BOR: + r = v1.bor(v2); + break; + case OP_BXOR: + r = v1.bxor(v2); + break; + case OP_SHL: + r = v1.shl(v2); + break; + case OP_SHR: + r = v1.shr(v2); + break; case OP_MOD: r = v1.mod(v2); break; @@ -899,6 +914,9 @@ public class FuncState extends Constants { case OP_UNM: r = v1.neg(); break; + case OP_BNOT: + r = v1.bnot(); + break; case OP_LEN: // r = v1.len(); // break; @@ -918,7 +936,7 @@ public class FuncState extends Constants { if (constfolding(op, e1, e2)) return; else { - int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) + int o2 = (op != OP_UNM && op != OP_BNOT && op != OP_LEN) ? this.exp2RK(e2) : 0; int o1 = this.exp2RK(e1); if (o1 > o2) { @@ -971,6 +989,15 @@ public class FuncState extends Constants { this.codearith(OP_LEN, e, e2, line); break; } + case LexState.OPR_BNOT: { + if (e.isnumeral()) + e.u.setNval(e.u.nval().bnot()); + else { + this.exp2anyreg(e); + this.codearith(OP_BNOT, e, e2, line); + } + break; + } default: _assert (false); } @@ -995,6 +1022,11 @@ public class FuncState extends Constants { case LexState.OPR_MUL: case LexState.OPR_DIV: case LexState.OPR_IDIV: + case LexState.OPR_BAND: + case LexState.OPR_BOR: + case LexState.OPR_BXOR: + case LexState.OPR_SHL: + case LexState.OPR_SHR: case LexState.OPR_MOD: case LexState.OPR_POW: { if (!v.isnumeral()) @@ -1057,6 +1089,21 @@ public class FuncState extends Constants { case LexState.OPR_IDIV: this.codearith(OP_IDIV, e1, e2, line); break; + case LexState.OPR_BAND: + this.codearith(OP_BAND, e1, e2, line); + break; + case LexState.OPR_BOR: + this.codearith(OP_BOR, e1, e2, line); + break; + case LexState.OPR_BXOR: + this.codearith(OP_BXOR, e1, e2, line); + break; + case LexState.OPR_SHL: + this.codearith(OP_SHL, e1, e2, line); + break; + case LexState.OPR_SHR: + this.codearith(OP_SHR, e1, e2, line); + break; case LexState.OPR_MOD: this.codearith(OP_MOD, e1, e2, line); break; diff --git a/core/src/main/java/org/luaj/vm2/compiler/LexState.java b/core/src/main/java/org/luaj/vm2/compiler/LexState.java index 3cfa0980..bc9b360f 100644 --- a/core/src/main/java/org/luaj/vm2/compiler/LexState.java +++ b/core/src/main/java/org/luaj/vm2/compiler/LexState.java @@ -85,15 +85,16 @@ public class LexState extends Constants { ** grep "ORDER OPR" if you change these enums */ static final int - OPR_ADD=0, OPR_SUB=1, OPR_MUL=2, OPR_DIV=3, OPR_IDIV=4, OPR_MOD=5, OPR_POW=6, - OPR_CONCAT=7, - OPR_NE=8, OPR_EQ=9, - OPR_LT=10, OPR_LE=11, OPR_GT=12, OPR_GE=13, - OPR_AND=14, OPR_OR=15, - OPR_NOBINOPR=16; + OPR_ADD=0, OPR_SUB=1, OPR_MUL=2, OPR_DIV=3, OPR_IDIV=4, OPR_BAND=5, OPR_BOR=6, + OPR_BXOR=7, OPR_SHL=8, OPR_SHR=9, OPR_MOD=10, OPR_POW=11, + OPR_CONCAT=12, + OPR_NE=13, OPR_EQ=14, + OPR_LT=15, OPR_LE=16, OPR_GT=17, OPR_GE=18, + OPR_AND=19, OPR_OR=20, + OPR_NOBINOPR=21; static final int - OPR_MINUS=0, OPR_NOT=1, OPR_LEN=2, OPR_NOUNOPR=3; + OPR_MINUS=0, OPR_NOT=1, OPR_LEN=2, OPR_BNOT=3, OPR_NOUNOPR=4; /* exp kind */ static final int @@ -149,7 +150,7 @@ public class LexState extends Constants { "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "//", "==", ">=", "<=", "~=", + "..", "...", "//", "<<", ">>", "==", ">=", "<=", "~=", "::", "", "", "", "", "", }; @@ -160,8 +161,8 @@ public class LexState extends Constants { TK_IN=268, TK_LOCAL=269, TK_NIL=270, TK_NOT=271, TK_OR=272, TK_REPEAT=273, TK_RETURN=274, TK_THEN=275, TK_TRUE=276, TK_UNTIL=277, TK_WHILE=278, /* other terminal symbols */ - TK_CONCAT=279, TK_DOTS=280, TK_IDIV=281, TK_EQ=282, TK_GE=283, TK_LE=284, TK_NE=285, - TK_DBCOLON=286, TK_EOS=287, TK_NUMBER=288, TK_NAME=289, TK_STRING=290; + TK_CONCAT=279, TK_DOTS=280, TK_IDIV=281, TK_SHL=282, TK_SHR=283, TK_EQ=284, TK_GE=285, TK_LE=286, TK_NE=287, + TK_DBCOLON=288, TK_EOS=289, TK_NUMBER=290, TK_NAME=291, TK_STRING=292; final static int FIRST_RESERVED = TK_AND; final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED; @@ -335,52 +336,27 @@ public class LexState extends Constants { } LuaValue strx2number(String str, SemInfo seminfo) { - char[] c = str.toCharArray(); - int s = 0; - while ( s < c.length && isspace(c[s])) - ++s; - // Check for negative sign - double sgn = 1.0; - if (s < c.length && c[s] == '-') { - sgn = -1.0; - ++s; - } - /* Check for "0x" */ - if (s + 2 >= c.length ) - return LuaValue.ZERO; - if (c[s++] != '0') - return LuaValue.ZERO; - if (c[s] != 'x' && c[s] != 'X') - return LuaValue.ZERO; - ++s; - - // read integer part. - double m = 0; - int e = 0; - while (s < c.length && isxdigit(c[s])) - m = (m * 16) + hexvalue(c[s++]); - if (s < c.length && c[s] == '.') { - ++s; // skip dot - while (s < c.length && isxdigit(c[s])) { - m = (m * 16) + hexvalue(c[s++]); - e -= 4; // Each fractional part shifts right by 2^4 + String trimmed = str.trim(); + try { + if (trimmed.indexOf('.') < 0 && trimmed.indexOf('p') < 0 && trimmed.indexOf('P') < 0) { + boolean negative = trimmed.startsWith("-"); + boolean positive = trimmed.startsWith("+"); + int prefix = negative || positive ? 3 : 2; + String digits = trimmed.substring(prefix); + long value = Long.parseUnsignedLong(digits, 16); + if (negative) { + if (value == Long.MIN_VALUE) { + return LuaValue.valueOf(Long.MIN_VALUE); + } + return LuaValue.valueOf(-value); + } + return LuaValue.valueOf(value); } + return LuaValue.valueOf(Double.parseDouble(trimmed)); + } catch (NumberFormatException e) { + lexerror("malformed number (" + e.getMessage() + ")", TK_NUMBER); + return LuaValue.NIL; } - if (s < c.length && (c[s] == 'p' || c[s] == 'P')) { - ++s; - int exp1 = 0; - boolean neg1 = false; - if (s < c.length && c[s] == '-') { - neg1 = true; - ++s; - } - while (s < c.length && isdigit(c[s])) - exp1 = exp1 * 10 + c[s++] - '0'; - if (neg1) - exp1 = -exp1; - e += exp1; - } - return LuaValue.valueOf(sgn * m * MathLib.dpow_d(2.0, e)); } boolean str2d(String str, SemInfo seminfo) { @@ -390,7 +366,12 @@ public class LexState extends Constants { seminfo.r = strx2number(str, seminfo); else { try { - seminfo.r = LuaValue.valueOf(Double.parseDouble(str.trim())); + String trimmed = str.trim(); + if (trimmed.indexOf('.') < 0 && trimmed.indexOf('e') < 0 && trimmed.indexOf('E') < 0) { + seminfo.r = LuaValue.valueOf(Long.parseLong(trimmed)); + } else { + seminfo.r = LuaValue.valueOf(Double.parseDouble(trimmed)); + } } catch (NumberFormatException e) { lexerror("malformed number (" + e.getMessage() + ")", TK_NUMBER); } @@ -640,7 +621,10 @@ public class LexState extends Constants { } case '<': { nextChar(); - if (current != '=') + if (current == '<') { + nextChar(); + return TK_SHL; + } else if (current != '=') return '<'; else { nextChar(); @@ -649,7 +633,10 @@ public class LexState extends Constants { } case '>': { nextChar(); - if (current != '=') + if (current == '>') { + nextChar(); + return TK_SHR; + } else if (current != '=') return '>'; else { nextChar(); @@ -1510,6 +1497,8 @@ public class LexState extends Constants { return OPR_MINUS; case '#': return OPR_LEN; + case '~': + return OPR_BNOT; default: return OPR_NOUNOPR; } @@ -1528,6 +1517,16 @@ public class LexState extends Constants { return OPR_DIV; case TK_IDIV: return OPR_IDIV; + case '&': + return OPR_BAND; + case '|': + return OPR_BOR; + case '~': + return OPR_BXOR; + case TK_SHL: + return OPR_SHL; + case TK_SHR: + return OPR_SHR; case '%': return OPR_MOD; case '^': @@ -1567,14 +1566,15 @@ public class LexState extends Constants { }; static Priority[] priority = { /* ORDER OPR */ - new Priority(6, 6), new Priority(6, 6), new Priority(7, 7), new Priority(7, 7), new Priority(7, 7), new Priority(7, 7), /* `+' `-' `*' `/' `//' `%' */ - new Priority(10, 9), new Priority(5, 4), /* power and concat (right associative) */ - new Priority(3, 3), new Priority(3, 3), /* equality and inequality */ - new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), /* order */ - new Priority(2, 2), new Priority(1, 1) /* logical (and/or) */ + new Priority(10, 10), new Priority(10, 10), new Priority(11, 11), new Priority(11, 11), new Priority(11, 11), new Priority(6, 6), new Priority(4, 4), + new Priority(5, 5), new Priority(7, 7), new Priority(7, 7), new Priority(11, 11), new Priority(13, 12), + new Priority(8, 7), + new Priority(3, 3), new Priority(3, 3), + new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), + new Priority(2, 2), new Priority(1, 1) }; - static final int UNARY_PRIORITY = 8; /* priority for unary operators */ + static final int UNARY_PRIORITY = 12; /* priority for unary operators */ /* diff --git a/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java b/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java index f70607ca..3b9f26c2 100644 --- a/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java +++ b/core/src/main/java/org/luaj/vm2/libs/CoroutineLib.java @@ -76,6 +76,7 @@ public class CoroutineLib extends TwoArgFunction { globals = env.checkglobals(); LuaTable coroutine = new LuaTable(); coroutine.set("create", new Create()); + coroutine.set("isyieldable", new IsYieldable()); coroutine.set("resume", new Resume()); coroutine.set("running", new Running()); coroutine.set("status", new Status()); @@ -99,6 +100,13 @@ public class CoroutineLib extends TwoArgFunction { } } + final class IsYieldable extends VarArgFunction { + public Varargs invoke(Varargs args) { + final LuaThread r = globals.running; + return valueOf(r != null && !r.isMainThread()); + } + } + final class Running extends VarArgFunction { public Varargs invoke(Varargs args) { final LuaThread r = globals.running; @@ -141,4 +149,4 @@ public class CoroutineLib extends TwoArgFunction { } } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/luaj/vm2/libs/DebugLib.java b/core/src/main/java/org/luaj/vm2/libs/DebugLib.java index da6fdb7b..3be24b01 100644 --- a/core/src/main/java/org/luaj/vm2/libs/DebugLib.java +++ b/core/src/main/java/org/luaj/vm2/libs/DebugLib.java @@ -817,9 +817,15 @@ public class DebugLib extends TwoArgFunction { case Lua.OP_MUL: tm = LuaValue.MUL; break; case Lua.OP_DIV: tm = LuaValue.DIV; break; case Lua.OP_IDIV: tm = LuaValue.IDIV; break; + case Lua.OP_BAND: tm = LuaValue.BAND; break; + case Lua.OP_BOR: tm = LuaValue.BOR; break; + case Lua.OP_BXOR: tm = LuaValue.BXOR; break; + case Lua.OP_SHL: tm = LuaValue.SHL; break; + case Lua.OP_SHR: tm = LuaValue.SHR; break; case Lua.OP_MOD: tm = LuaValue.MOD; break; case Lua.OP_POW: tm = LuaValue.POW; break; case Lua.OP_UNM: tm = LuaValue.UNM; break; + case Lua.OP_BNOT: tm = LuaValue.BNOT; break; case Lua.OP_LEN: tm = LuaValue.LEN; break; case Lua.OP_LT: tm = LuaValue.LT; break; case Lua.OP_LE: tm = LuaValue.LE; break; diff --git a/core/src/main/java/org/luaj/vm2/libs/MathLib.java b/core/src/main/java/org/luaj/vm2/libs/MathLib.java index d2dfba51..3b37b22d 100644 --- a/core/src/main/java/org/luaj/vm2/libs/MathLib.java +++ b/core/src/main/java/org/luaj/vm2/libs/MathLib.java @@ -113,7 +113,9 @@ public class MathLib extends TwoArgFunction { math.set("huge", LuaDouble.POSINF ); math.set("ldexp", new ldexp()); math.set("max", new max()); + math.set("maxinteger", LuaValue.valueOf(Long.MAX_VALUE)); math.set("min", new min()); + math.set("mininteger", LuaValue.valueOf(Long.MIN_VALUE)); math.set("modf", new modf()); math.set("pi", Math.PI ); math.set("pow", new pow()); @@ -124,6 +126,9 @@ public class MathLib extends TwoArgFunction { math.set("sin", new sin()); math.set("sqrt", new sqrt()); math.set("tan", new tan()); + math.set("tointeger", new tointeger()); + math.set("type", new type()); + math.set("ult", new ult()); env.set("math", math); if (!env.get("package").isnil()) env.get("package").get("loaded").set("math", math); return math; @@ -229,6 +234,34 @@ public class MathLib extends TwoArgFunction { return varargsOf( valueOf(intPart), valueOf(fracPart) ); } } + + static class tointeger extends OneArgFunction { + public LuaValue call(LuaValue arg) { + LuaValue n = arg.tonumber(); + if (n.isnil() || !n.islong()) { + return NIL; + } + return LuaValue.valueOf(n.tolong()); + } + } + + static class type extends OneArgFunction { + public LuaValue call(LuaValue arg) { + LuaValue n = arg.tonumber(); + if (n.isnil()) { + return NIL; + } + return LuaValue.valueOf(n.isinttype()? "integer": "float"); + } + } + + static class ult extends TwoArgFunction { + public LuaValue call(LuaValue x, LuaValue y) { + long a = x.checklong(); + long b = y.checklong(); + return valueOf(Long.compareUnsigned(a, b) < 0); + } + } static class random extends LibFunction { Random random = new Random(); @@ -236,17 +269,28 @@ public class MathLib extends TwoArgFunction { return valueOf( random.nextDouble() ); } public LuaValue call(LuaValue a) { - int m = a.checkint(); - if (m<1) argerror(1, "interval is empty"); - return valueOf( 1 + random.nextInt(m) ); + long upper = a.checklong(); + if (upper < 1) argerror(1, "interval is empty"); + return valueOf(nextLong(1, upper)); } public LuaValue call(LuaValue a, LuaValue b) { - int m = a.checkint(); - int n = b.checkint(); - if (n 0); + return value; + } + return lower + random.nextLong(bound); } - } static class randomseed extends OneArgFunction { diff --git a/core/src/main/java/org/luaj/vm2/libs/StringLib.java b/core/src/main/java/org/luaj/vm2/libs/StringLib.java index 86de6509..4b7f2d43 100644 --- a/core/src/main/java/org/luaj/vm2/libs/StringLib.java +++ b/core/src/main/java/org/luaj/vm2/libs/StringLib.java @@ -23,9 +23,11 @@ package org.luaj.vm2.libs; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.ByteOrder; import org.luaj.vm2.Buffer; import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; @@ -93,9 +95,12 @@ public class StringLib extends TwoArgFunction { string.set("len", new len()); string.set("lower", new lower()); string.set("match", new match()); + string.set("pack", new pack()); + string.set("packsize", new packsize()); string.set("rep", new rep()); string.set("reverse", new reverse()); string.set("sub", new sub()); + string.set("unpack", new unpack()); string.set("upper", new upper()); env.set("string", string); @@ -327,6 +332,149 @@ public class StringLib extends TwoArgFunction { } buf.append( (byte) '"' ); } + + static final class pack extends VarArgFunction { + public Varargs invoke(Varargs args) { + FormatState state = new FormatState(args.checkstring(1)); + ByteArrayOutput out = new ByteArrayOutput(); + int arg = 2; + while (state.hasMore()) { + FormatOption option = state.next(); + int padding = state.alignmentPadding(out.size(), option); + out.pad(padding); + switch (option.kind) { + case PADDING: + out.pad(option.size); + break; + case ALIGN: + out.pad(option.size); + break; + case SIGNED: + writeInteger(out, args.checklong(arg++), option.size, state.littleEndian, true); + break; + case UNSIGNED: + writeInteger(out, checkUnsigned(args.checklong(arg++), option.size), option.size, state.littleEndian, false); + break; + case FLOAT: + writeFloat(out, (float) args.checkdouble(arg++), state.littleEndian); + break; + case DOUBLE: + writeDouble(out, args.checkdouble(arg++), state.littleEndian); + break; + case FIXED_STRING: { + LuaString s = args.checkstring(arg++); + if (s.rawlen() > option.size) { + argerror(arg - 1, "string longer than given size"); + } + out.write(s); + out.pad(option.size - s.rawlen()); + break; + } + case ZERO_STRING: { + LuaString s = args.checkstring(arg++); + for (int i = 0; i < s.rawlen(); i++) { + if (s.luaByte(i) == 0) { + argerror(arg - 1, "string contains zeros"); + } + } + out.write(s); + out.writeByte(0); + break; + } + case SIZED_STRING: { + LuaString s = args.checkstring(arg++); + writeInteger(out, checkUnsigned(s.rawlen(), option.size), option.size, state.littleEndian, false); + out.write(s); + break; + } + default: + throw new LuaError("unsupported pack option"); + } + } + return LuaString.valueUsing(out.toByteArray()); + } + } + + static final class packsize extends OneArgFunction { + public LuaValue call(LuaValue arg) { + FormatState state = new FormatState(arg.checkstring()); + int offset = 0; + while (state.hasMore()) { + FormatOption option = state.next(); + offset += state.alignmentPadding(offset, option); + if (option.kind == OptionKind.ZERO_STRING || option.kind == OptionKind.SIZED_STRING) { + throw new LuaError("variable-length format"); + } + offset += option.size; + } + return valueOf(offset); + } + } + + static final class unpack extends VarArgFunction { + public Varargs invoke(Varargs args) { + FormatState state = new FormatState(args.checkstring(1)); + LuaString input = args.checkstring(2); + int position = posrelat(args.optint(3, 1), input.rawlen()); + if (position < 1) { + position = 1; + } + int offset = position - 1; + java.util.ArrayList values = new java.util.ArrayList(); + while (state.hasMore()) { + FormatOption option = state.next(); + offset += state.alignmentPadding(offset, option); + requireBytes(input, offset, option.kind == OptionKind.ZERO_STRING ? 1 : option.size); + switch (option.kind) { + case PADDING: + case ALIGN: + offset += option.size; + break; + case SIGNED: + values.add(LuaValue.valueOf(readInteger(input, offset, option.size, state.littleEndian, true))); + offset += option.size; + break; + case UNSIGNED: + values.add(LuaValue.valueOf(readUnsignedInteger(input, offset, option.size, state.littleEndian))); + offset += option.size; + break; + case FLOAT: + values.add(LuaValue.valueOf(readFloat(input, offset, state.littleEndian))); + offset += option.size; + break; + case DOUBLE: + values.add(LuaValue.valueOf(readDouble(input, offset, state.littleEndian))); + offset += option.size; + break; + case FIXED_STRING: + values.add(input.substring(offset, offset + option.size)); + offset += option.size; + break; + case ZERO_STRING: { + int end = findZero(input, offset); + values.add(input.substring(offset, end)); + offset = end + 1; + break; + } + case SIZED_STRING: { + long len = readUnsignedInteger(input, offset, option.size, state.littleEndian); + offset += option.size; + if (len > Integer.MAX_VALUE) { + throw new LuaError("string length does not fit"); + } + requireBytes(input, offset, (int) len); + values.add(input.substring(offset, offset + (int) len)); + offset += (int) len; + break; + } + default: + throw new LuaError("unsupported unpack option"); + } + } + values.add(LuaValue.valueOf(offset + 1)); + return LuaValue.varargsOf(values.toArray(new LuaValue[values.size()])); + } + } private static final String FLAGS = "-+ #0"; @@ -745,6 +893,371 @@ public class StringLib extends TwoArgFunction { return valueOf(arg.checkjstring().toUpperCase()); } } + + private static void requireBytes(LuaString input, int offset, int size) { + if (offset < 0 || offset + size > input.rawlen()) { + throw new LuaError("data string too short"); + } + } + + private static int findZero(LuaString input, int offset) { + for (int i = offset, n = input.rawlen(); i < n; i++) { + if (input.luaByte(i) == 0) { + return i; + } + } + throw new LuaError("unfinished string for format 'z'"); + } + + private static long checkUnsigned(long value, int size) { + if (value < 0) { + throw new LuaError("unsigned overflow"); + } + if (size < 8 && value >= (1L << (size * 8))) { + throw new LuaError("unsigned overflow"); + } + return value; + } + + private static void writeInteger(ByteArrayOutput out, long value, int size, boolean littleEndian, boolean signed) { + if (size <= 0 || size > 16) { + throw new LuaError("integral size out of limits"); + } + byte fill = (byte) ((signed && value < 0) ? 0xFF : 0x00); + byte[] bytes = new byte[size]; + long v = value; + for (int i = 0; i < size; i++) { + int index = littleEndian ? i : (size - 1 - i); + if (i < 8) { + bytes[index] = (byte) (v & 0xFF); + v >>= 8; + } else { + bytes[index] = fill; + } + } + out.write(bytes); + } + + private static long readInteger(LuaString input, int offset, int size, boolean littleEndian, boolean signed) { + if (!signed) { + return readUnsignedInteger(input, offset, size, littleEndian); + } + if (size <= 0 || size > 16) { + throw new LuaError("integral size out of limits"); + } + requireBytes(input, offset, size); + long result = 0L; + int bytesToRead = Math.min(size, 8); + for (int i = 0; i < bytesToRead; i++) { + int index = littleEndian ? (offset + i) : (offset + size - 1 - i); + result |= ((long) input.luaByte(index)) << (8 * i); + } + int signIndex = littleEndian ? (offset + size - 1) : offset; + boolean negative = (input.luaByte(signIndex) & 0x80) != 0; + if (size < 8 && negative) { + result |= (-1L) << (size * 8); + } + if (size > 8) { + for (int i = 8; i < size; i++) { + int index = littleEndian ? (offset + i) : (offset + size - 1 - i); + int b = input.luaByte(index); + if (b != (negative ? 0xFF : 0x00)) { + throw new LuaError("integer overflow"); + } + } + } + return result; + } + + private static long readUnsignedInteger(LuaString input, int offset, int size, boolean littleEndian) { + if (size <= 0 || size > 16) { + throw new LuaError("integral size out of limits"); + } + requireBytes(input, offset, size); + long result = 0L; + int bytesToRead = Math.min(size, 8); + for (int i = 0; i < bytesToRead; i++) { + int index = littleEndian ? (offset + i) : (offset + size - 1 - i); + result |= ((long) input.luaByte(index)) << (8 * i); + } + for (int i = 8; i < size; i++) { + int index = littleEndian ? (offset + i) : (offset + size - 1 - i); + if (input.luaByte(index) != 0) { + throw new LuaError("unsigned overflow"); + } + } + if (result < 0) { + throw new LuaError("unsigned overflow"); + } + return result; + } + + private static void writeFloat(ByteArrayOutput out, float value, boolean littleEndian) { + writeFixed(out, Float.floatToIntBits(value) & 0xFFFFFFFFL, 4, littleEndian); + } + + private static float readFloat(LuaString input, int offset, boolean littleEndian) { + return Float.intBitsToFloat((int) readFixed(input, offset, 4, littleEndian)); + } + + private static void writeDouble(ByteArrayOutput out, double value, boolean littleEndian) { + writeFixed(out, Double.doubleToLongBits(value), 8, littleEndian); + } + + private static double readDouble(LuaString input, int offset, boolean littleEndian) { + return Double.longBitsToDouble(readFixed(input, offset, 8, littleEndian)); + } + + private static void writeFixed(ByteArrayOutput out, long bits, int size, boolean littleEndian) { + byte[] bytes = new byte[size]; + for (int i = 0; i < size; i++) { + int index = littleEndian ? i : (size - 1 - i); + bytes[index] = (byte) ((bits >>> (8 * i)) & 0xFF); + } + out.write(bytes); + } + + private static long readFixed(LuaString input, int offset, int size, boolean littleEndian) { + requireBytes(input, offset, size); + long bits = 0L; + for (int i = 0; i < size; i++) { + int index = littleEndian ? (offset + i) : (offset + size - 1 - i); + bits |= ((long) input.luaByte(index)) << (8 * i); + } + return bits; + } + + private enum OptionKind { + PADDING, + ALIGN, + SIGNED, + UNSIGNED, + FLOAT, + DOUBLE, + FIXED_STRING, + ZERO_STRING, + SIZED_STRING + } + + private static final class FormatOption { + final OptionKind kind; + final int size; + final int alignment; + + FormatOption(OptionKind kind, int size, int alignment) { + this.kind = kind; + this.size = size; + this.alignment = alignment; + } + } + + private static final class FormatState { + private final LuaString format; + private int index; + boolean littleEndian; + private int maxAlign; + + FormatState(LuaString format) { + this.format = format; + this.littleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; + this.maxAlign = 1; + } + + boolean hasMore() { + skipSpaces(); + return index < format.rawlen(); + } + + FormatOption next() { + while (index < format.rawlen()) { + int c = format.luaByte(index++); + switch (c) { + case ' ': + case '\t': + case '\n': + case '\r': + continue; + case '<': + littleEndian = true; + continue; + case '>': + littleEndian = false; + continue; + case '=': + littleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN; + continue; + case '!': + maxAlign = readCount(); + if (maxAlign <= 0) { + throw new LuaError("invalid format option '!'"); + } + continue; + case 'x': + return new FormatOption(OptionKind.PADDING, 1, 1); + case 'X': { + FormatOption next = peekNextSizedOption(); + return new FormatOption(OptionKind.ALIGN, alignmentFor(next.size), 1); + } + case 'b': + return signed(1); + case 'B': + return unsigned(1); + case 'h': + return signed(2); + case 'H': + return unsigned(2); + case 'l': + return signed(8); + case 'L': + return unsigned(8); + case 'j': + return signed(8); + case 'J': + return unsigned(8); + case 'T': + return unsigned(8); + case 'i': + return signed(readOptionalCount(4)); + case 'I': + return unsigned(readOptionalCount(4)); + case 'f': + return new FormatOption(OptionKind.FLOAT, 4, alignmentFor(4)); + case 'd': + case 'n': + return new FormatOption(OptionKind.DOUBLE, 8, alignmentFor(8)); + case 'c': { + int size = readCount(); + if (size < 0) { + throw new LuaError("missing size for format option 'c'"); + } + return new FormatOption(OptionKind.FIXED_STRING, size, 1); + } + case 'z': + return new FormatOption(OptionKind.ZERO_STRING, 0, 1); + case 's': { + int size = readOptionalCount(8); + return new FormatOption(OptionKind.SIZED_STRING, size, alignmentFor(size)); + } + default: + throw new LuaError("invalid format option '" + (char) c + "'"); + } + } + throw new LuaError("unexpected end of format string"); + } + + int alignmentPadding(int offset, FormatOption option) { + int alignment = option.alignment; + if (alignment <= 1) { + return 0; + } + int mod = offset & (alignment - 1); + return mod == 0 ? 0 : (alignment - mod); + } + + private FormatOption signed(int size) { + validateIntegralSize(size); + return new FormatOption(OptionKind.SIGNED, size, alignmentFor(size)); + } + + private FormatOption unsigned(int size) { + validateIntegralSize(size); + return new FormatOption(OptionKind.UNSIGNED, size, alignmentFor(size)); + } + + private FormatOption peekNextSizedOption() { + int savedIndex = index; + boolean savedEndian = littleEndian; + int savedMaxAlign = maxAlign; + FormatOption option = next(); + index = savedIndex; + littleEndian = savedEndian; + maxAlign = savedMaxAlign; + if (option.kind == OptionKind.ZERO_STRING || option.kind == OptionKind.FIXED_STRING) { + throw new LuaError("invalid next option for option 'X'"); + } + return option; + } + + private int readCount() { + if (index >= format.rawlen() || !Character.isDigit((char) format.luaByte(index))) { + throw new LuaError("missing size for format option"); + } + int value = 0; + while (index < format.rawlen() && Character.isDigit((char) format.luaByte(index))) { + value = value * 10 + (format.luaByte(index++) - '0'); + } + return value; + } + + private int readOptionalCount(int defaultValue) { + if (index < format.rawlen() && Character.isDigit((char) format.luaByte(index))) { + int value = readCount(); + validateIntegralSize(value); + return value; + } + return defaultValue; + } + + private int alignmentFor(int size) { + int alignment = Math.min(size, maxAlign); + if (alignment <= 1) { + return 1; + } + if ((alignment & (alignment - 1)) != 0) { + throw new LuaError("format asks for alignment not power of 2"); + } + return alignment; + } + + private void validateIntegralSize(int size) { + if (size < 1 || size > 16) { + throw new LuaError("integral size out of limits"); + } + } + + private void skipSpaces() { + while (index < format.rawlen()) { + int c = format.luaByte(index); + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { + index++; + } else { + break; + } + } + } + } + + private static final class ByteArrayOutput { + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + void writeByte(int value) { + out.write(value); + } + + void write(byte[] bytes) { + out.write(bytes, 0, bytes.length); + } + + void write(LuaString s) { + byte[] bytes = new byte[s.rawlen()]; + s.copyInto(0, bytes, 0, bytes.length); + write(bytes); + } + + void pad(int count) { + for (int i = 0; i < count; i++) { + out.write(0); + } + } + + int size() { + return out.size(); + } + + byte[] toByteArray() { + return out.toByteArray(); + } + } /** * This utility method implements both string.find and string.match. diff --git a/core/src/main/java/org/luaj/vm2/libs/TableLib.java b/core/src/main/java/org/luaj/vm2/libs/TableLib.java index 4c58b853..26e1de59 100644 --- a/core/src/main/java/org/luaj/vm2/libs/TableLib.java +++ b/core/src/main/java/org/luaj/vm2/libs/TableLib.java @@ -21,6 +21,7 @@ ******************************************************************************/ package org.luaj.vm2.libs; +import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; @@ -65,6 +66,7 @@ public class TableLib extends TwoArgFunction { LuaTable table = new LuaTable(); table.set("concat", new concat()); table.set("insert", new insert()); + table.set("move", new move()); table.set("pack", new pack()); table.set("remove", new remove()); table.set("sort", new sort()); @@ -123,6 +125,33 @@ public class TableLib extends TwoArgFunction { } } + // "move" (a1, f, e, t [,a2]) -> a2 + static class move extends VarArgFunction { + public Varargs invoke(Varargs args) { + LuaTable source = args.checktable(1); + int from = args.checkint(2); + int to = args.checkint(3); + int target = args.checkint(4); + LuaTable dest = args.opttable(5, source); + if (to >= from) { + int count = to - from + 1; + if (count < 0) { + throw new LuaError("too many elements to move"); + } + if (target > from && target <= to && dest == source) { + for (int i = count - 1; i >= 0; i--) { + dest.set(target + i, source.get(from + i)); + } + } else { + for (int i = 0; i < count; i++) { + dest.set(target + i, source.get(from + i)); + } + } + } + return dest; + } + } + // "remove" (table [, pos]) -> removed-ele static class remove extends VarArgFunction { public Varargs invoke(Varargs args) { diff --git a/grammar/Lua52.jj b/grammar/Lua52.jj index c5e0c26e..24ee23f5 100644 --- a/grammar/Lua52.jj +++ b/grammar/Lua52.jj @@ -353,7 +353,7 @@ void FieldSep(): void Binop(): {} { - "+" | "-" | "*" | "/" | "//" | "^" | "%" | ".." | "<" | "<=" | ">" | ">=" | "==" | "~=" | | +"+" | "-" | "*" | "/" | "//" | "&" | "|" | "~" | "<<" | ">>" | "^" | "%" | ".." | "<" | "<=" | ">" | ">=" | "==" | "~=" | | } void Unop(): diff --git a/grammar/LuaParser.jj b/grammar/LuaParser.jj index 2dc3cfa3..05f00df7 100644 --- a/grammar/LuaParser.jj +++ b/grammar/LuaParser.jj @@ -510,6 +510,11 @@ int Binop(): | "*" { return Lua.OP_MUL; } | "/" { return Lua.OP_DIV; } | "//" { return Lua.OP_IDIV; } +| "&" { return Lua.OP_BAND; } +| "|" { return Lua.OP_BOR; } +| "~" { return Lua.OP_BXOR; } +| "<<" { return Lua.OP_SHL; } +| ">>" { return Lua.OP_SHR; } | "^" { return Lua.OP_POW; } | "%" { return Lua.OP_MOD; } | ".." { return Lua.OP_CONCAT; } @@ -529,4 +534,5 @@ int Unop(): "-" { return Lua.OP_UNM; } | { return Lua.OP_NOT; } | "#" { return Lua.OP_LEN; } +| "~" { return Lua.OP_BNOT; } } diff --git a/jme/src/main/java/org/luaj/vm2/libs/jme/JmePlatform.java b/jme/src/main/java/org/luaj/vm2/libs/jme/JmePlatform.java index 8619e068..4e27d7fe 100644 --- a/jme/src/main/java/org/luaj/vm2/libs/jme/JmePlatform.java +++ b/jme/src/main/java/org/luaj/vm2/libs/jme/JmePlatform.java @@ -25,7 +25,6 @@ import org.luaj.vm2.Globals; import org.luaj.vm2.LoadState; import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.libs.BaseLib; -import org.luaj.vm2.libs.Bit32Lib; import org.luaj.vm2.libs.CoroutineLib; import org.luaj.vm2.libs.DebugLib; import org.luaj.vm2.libs.MathLib; @@ -34,6 +33,7 @@ import org.luaj.vm2.libs.PackageLib; import org.luaj.vm2.libs.ResourceFinder; import org.luaj.vm2.libs.StringLib; import org.luaj.vm2.libs.TableLib; +import org.luaj.vm2.libs.Utf8Lib; /** The {@link JmePlatform} class is a convenience class to standardize * how globals tables are initialized for the JME platform. @@ -73,7 +73,6 @@ import org.luaj.vm2.libs.TableLib; *

  • {@link Globals}
  • *
  • {@link BaseLib}
  • *
  • {@link PackageLib}
  • - *
  • {@link Bit32Lib}
  • *
  • {@link TableLib}
  • *
  • {@link StringLib}
  • *
  • {@link CoroutineLib}
  • @@ -105,11 +104,11 @@ public class JmePlatform { Globals globals = new Globals(); globals.load(new BaseLib()); globals.load(new PackageLib()); - globals.load(new Bit32Lib()); globals.load(new OsLib()); globals.load(new MathLib()); globals.load(new TableLib()); globals.load(new StringLib()); + globals.load(new Utf8Lib()); globals.load(new CoroutineLib()); globals.load(new JmeIoLib()); LoadState.install(globals); diff --git a/jse/src/main/java/org/luaj/vm2/ast/Exp.java b/jse/src/main/java/org/luaj/vm2/ast/Exp.java index ed9afc09..0afaae03 100644 --- a/jse/src/main/java/org/luaj/vm2/ast/Exp.java +++ b/jse/src/main/java/org/luaj/vm2/ast/Exp.java @@ -89,11 +89,15 @@ public class Exp extends SyntaxElement { case Lua.OP_OR: return 0; case Lua.OP_AND: return 1; case Lua.OP_LT: case Lua.OP_GT: case Lua.OP_LE: case Lua.OP_GE: case Lua.OP_NEQ: case Lua.OP_EQ: return 2; - case Lua.OP_CONCAT: return 3; - case Lua.OP_ADD: case Lua.OP_SUB: return 4; - case Lua.OP_MUL: case Lua.OP_DIV: case Lua.OP_IDIV: case Lua.OP_MOD: return 5; - case Lua.OP_NOT: case Lua.OP_UNM: case Lua.OP_LEN: return 6; - case Lua.OP_POW: return 7; + case Lua.OP_BOR: return 3; + case Lua.OP_BXOR: return 4; + case Lua.OP_BAND: return 5; + case Lua.OP_SHL: case Lua.OP_SHR: return 6; + case Lua.OP_CONCAT: return 7; + case Lua.OP_ADD: case Lua.OP_SUB: return 8; + case Lua.OP_MUL: case Lua.OP_DIV: case Lua.OP_IDIV: case Lua.OP_MOD: return 9; + case Lua.OP_NOT: case Lua.OP_UNM: case Lua.OP_LEN: case Lua.OP_BNOT: return 10; + case Lua.OP_POW: return 11; default: throw new IllegalStateException("precedence of bad op "+op); } } diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java index 66f7558c..536ab793 100644 --- a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java +++ b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceJavaToLua.java @@ -78,7 +78,7 @@ public class CoerceJavaToLua { private static final class IntCoercion implements Coercion { public LuaValue coerce( Object javaValue ) { Number n = (Number) javaValue; - return LuaInteger.valueOf( n.intValue() ); + return LuaInteger.valueOf( n.longValue() ); } } @@ -149,7 +149,7 @@ public class CoerceJavaToLua { COERCIONS.put( Character.class, charCoercion ); COERCIONS.put( Short.class, intCoercion ); COERCIONS.put( Integer.class, intCoercion ); - COERCIONS.put( Long.class, doubleCoercion ); + COERCIONS.put( Long.class, intCoercion ); COERCIONS.put( Float.class, doubleCoercion ); COERCIONS.put( Double.class, doubleCoercion ); COERCIONS.put( String.class, stringCoercion ); @@ -160,9 +160,9 @@ public class CoerceJavaToLua { /** * Coerse a Java object to a corresponding lua value. *

    - * Integral types {@code boolean}, {@code byte}, {@code char}, and {@code int} + * Integral types {@code boolean}, {@code byte}, {@code char}, {@code int}, and {@code long} * will become {@link LuaInteger}; - * {@code long}, {@code float}, and {@code double} will become {@link LuaDouble}; + * {@code float} and {@code double} will become {@link LuaDouble}; * {@code String} and {@code byte[]} will become {@link LuaString}; * types inheriting from {@link LuaValue} will be returned without coercion; * other types will become {@link LuaUserdata}. diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java index b3380152..94c4ce18 100644 --- a/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java +++ b/jse/src/main/java/org/luaj/vm2/libs/jse/CoerceLuaToJava.java @@ -292,7 +292,10 @@ public class CoerceLuaToJava { public int score(LuaValue value) { switch ( value.type() ) { case LuaValue.TNUMBER: - return inheritanceLevels( targetType, value.isint()? Integer.class: Double.class ); + if (value.isinttype()) { + return inheritanceLevels( targetType, value.isint()? Integer.class: Long.class ); + } + return inheritanceLevels( targetType, Double.class ); case LuaValue.TBOOLEAN: return inheritanceLevels( targetType, Boolean.class ); case LuaValue.TSTRING: @@ -308,7 +311,10 @@ public class CoerceLuaToJava { public Object coerce(LuaValue value) { switch ( value.type() ) { case LuaValue.TNUMBER: - return value.isint()? (Object) Integer.valueOf(value.toint()): (Object) Double.valueOf(value.todouble()); + if (value.isinttype()) { + return value.isint()? (Object) Integer.valueOf(value.toint()): (Object) Long.valueOf(value.tolong()); + } + return Double.valueOf(value.todouble()); case LuaValue.TBOOLEAN: return value.toboolean()? Boolean.TRUE: Boolean.FALSE; case LuaValue.TSTRING: @@ -369,4 +375,4 @@ public class CoerceLuaToJava { COERCIONS.put( c, co ); return co; } -} \ No newline at end of file +} diff --git a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java index c1c82f9f..55cc03ef 100644 --- a/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java +++ b/jse/src/main/java/org/luaj/vm2/libs/jse/JsePlatform.java @@ -26,13 +26,13 @@ import org.luaj.vm2.LoadState; import org.luaj.vm2.LuaValue; import org.luaj.vm2.Varargs; import org.luaj.vm2.compiler.LuaC; -import org.luaj.vm2.libs.Bit32Lib; import org.luaj.vm2.libs.CoroutineLib; import org.luaj.vm2.libs.DebugLib; import org.luaj.vm2.libs.PackageLib; import org.luaj.vm2.libs.ResourceFinder; import org.luaj.vm2.libs.StringLib; import org.luaj.vm2.libs.TableLib; +import org.luaj.vm2.libs.Utf8Lib; /** The {@link JsePlatform} class is a convenience class to standardize * how globals tables are initialized for the JSE platform. @@ -63,7 +63,6 @@ import org.luaj.vm2.libs.TableLib; *

  • {@link Globals}
  • *
  • {@link JseBaseLib}
  • *
  • {@link PackageLib}
  • - *
  • {@link Bit32Lib}
  • *
  • {@link TableLib}
  • *
  • {@link StringLib}
  • *
  • {@link CoroutineLib}
  • @@ -95,9 +94,9 @@ public class JsePlatform { Globals globals = new Globals(); globals.load(new JseBaseLib()); globals.load(new PackageLib()); - globals.load(new Bit32Lib()); globals.load(new TableLib()); globals.load(new JseStringLib()); + globals.load(new Utf8Lib()); globals.load(new CoroutineLib()); globals.load(new JseMathLib()); globals.load(new JseIoLib()); diff --git a/jse/src/main/java/org/luaj/vm2/luajc/JavaBuilder.java b/jse/src/main/java/org/luaj/vm2/luajc/JavaBuilder.java index c9df8cff..889e05d1 100644 --- a/jse/src/main/java/org/luaj/vm2/luajc/JavaBuilder.java +++ b/jse/src/main/java/org/luaj/vm2/luajc/JavaBuilder.java @@ -520,6 +520,7 @@ public class JavaBuilder { switch (o) { default: case Lua.OP_UNM: op = "neg"; break; + case Lua.OP_BNOT: op = "bnot"; break; case Lua.OP_NOT: op = "not"; break; case Lua.OP_LEN: op = "len"; break; } @@ -535,6 +536,11 @@ public class JavaBuilder { case Lua.OP_MUL: op = "mul"; break; case Lua.OP_DIV: op = "div"; break; case Lua.OP_IDIV: op = "idiv"; break; + case Lua.OP_BAND: op = "band"; break; + case Lua.OP_BOR: op = "bor"; break; + case Lua.OP_BXOR: op = "bxor"; break; + case Lua.OP_SHL: op = "shl"; break; + case Lua.OP_SHR: op = "shr"; break; case Lua.OP_MOD: op = "mod"; break; case Lua.OP_POW: op = "pow"; break; } diff --git a/jse/src/main/java/org/luaj/vm2/luajc/JavaGen.java b/jse/src/main/java/org/luaj/vm2/luajc/JavaGen.java index 6fe2d021..380fd26d 100644 --- a/jse/src/main/java/org/luaj/vm2/luajc/JavaGen.java +++ b/jse/src/main/java/org/luaj/vm2/luajc/JavaGen.java @@ -112,6 +112,7 @@ public class JavaGen { break; case Lua.OP_UNM: /* A B R(A):= -R(B) */ + case Lua.OP_BNOT: /* A B R(A):= ~R(B) */ case Lua.OP_NOT: /* A B R(A):= not R(B) */ case Lua.OP_LEN: /* A B R(A):= length of R(B) */ builder.loadLocal( pc, b ); @@ -166,6 +167,11 @@ public class JavaGen { case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */ case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */ case Lua.OP_IDIV: /* A B C R(A):= RK(B) // RK(C) */ + case Lua.OP_BAND: /* A B C R(A):= RK(B) & RK(C) */ + case Lua.OP_BOR: /* A B C R(A):= RK(B) | RK(C) */ + case Lua.OP_BXOR: /* A B C R(A):= RK(B) ~ RK(C) */ + case Lua.OP_SHL: /* A B C R(A):= RK(B) << RK(C) */ + case Lua.OP_SHR: /* A B C R(A):= RK(B) >> RK(C) */ case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */ loadLocalOrConstant( p, builder, pc, b ); diff --git a/jse/src/main/java/org/luaj/vm2/luajc/ProtoInfo.java b/jse/src/main/java/org/luaj/vm2/luajc/ProtoInfo.java index 25c9e9f6..7f557cf0 100644 --- a/jse/src/main/java/org/luaj/vm2/luajc/ProtoInfo.java +++ b/jse/src/main/java/org/luaj/vm2/luajc/ProtoInfo.java @@ -176,6 +176,7 @@ public class ProtoInfo { case Lua.OP_MOVE:/* A B R(A) := R(B) */ case Lua.OP_UNM: /* A B R(A) := -R(B) */ + case Lua.OP_BNOT: /* A B R(A) := ~R(B) */ case Lua.OP_NOT: /* A B R(A) := not R(B) */ case Lua.OP_LEN: /* A B R(A) := length of R(B) */ case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ @@ -190,6 +191,11 @@ public class ProtoInfo { case Lua.OP_MUL: /* A B C R(A) := RK(B) * RK(C) */ case Lua.OP_DIV: /* A B C R(A) := RK(B) / RK(C) */ case Lua.OP_IDIV: /* A B C R(A) := RK(B) // RK(C) */ + case Lua.OP_BAND: /* A B C R(A) := RK(B) & RK(C) */ + case Lua.OP_BOR: /* A B C R(A) := RK(B) | RK(C) */ + case Lua.OP_BXOR: /* A B C R(A) := RK(B) ~ RK(C) */ + case Lua.OP_SHL: /* A B C R(A) := RK(B) << RK(C) */ + case Lua.OP_SHR: /* A B C R(A) := RK(B) >> RK(C) */ case Lua.OP_MOD: /* A B C R(A) := RK(B) % RK(C) */ case Lua.OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */ a = Lua.GETARG_A( ins ); diff --git a/jse/src/main/java/org/luaj/vm2/parser/LuaParser.java b/jse/src/main/java/org/luaj/vm2/parser/LuaParser.java index 1674a1c1..ffa31cca 100644 --- a/jse/src/main/java/org/luaj/vm2/parser/LuaParser.java +++ b/jse/src/main/java/org/luaj/vm2/parser/LuaParser.java @@ -980,6 +980,26 @@ public class LuaParser implements LuaParserConstants { jj_consume_token(85); {if (true) return Lua.OP_DIV;} break; + case BAND: + jj_consume_token(BAND); + {if (true) return Lua.OP_BAND;} + break; + case BOR: + jj_consume_token(BOR); + {if (true) return Lua.OP_BOR;} + break; + case BNOT: + jj_consume_token(BNOT); + {if (true) return Lua.OP_BXOR;} + break; + case SHL: + jj_consume_token(SHL); + {if (true) return Lua.OP_SHL;} + break; + case SHR: + jj_consume_token(SHR); + {if (true) return Lua.OP_SHR;} + break; case 86: jj_consume_token(86); {if (true) return Lua.OP_POW;} @@ -1046,6 +1066,10 @@ public class LuaParser implements LuaParserConstants { jj_consume_token(69); {if (true) return Lua.OP_LEN;} break; + case BNOT: + jj_consume_token(BNOT); + {if (true) return Lua.OP_BNOT;} + break; default: jj_la1[33] = jj_gen; jj_consume_token(-1); diff --git a/jse/src/main/java/org/luaj/vm2/parser/LuaParserConstants.java b/jse/src/main/java/org/luaj/vm2/parser/LuaParserConstants.java index 30c44c29..e0da5472 100644 --- a/jse/src/main/java/org/luaj/vm2/parser/LuaParserConstants.java +++ b/jse/src/main/java/org/luaj/vm2/parser/LuaParserConstants.java @@ -114,6 +114,16 @@ public interface LuaParserConstants { int CHAR = 67; /** RegularExpression Id. */ int LF = 68; + /** RegularExpression Id. */ + int BNOT = 95; + /** RegularExpression Id. */ + int BAND = 96; + /** RegularExpression Id. */ + int BOR = 97; + /** RegularExpression Id. */ + int SHL = 98; + /** RegularExpression Id. */ + int SHR = 99; /** Lexical state. */ int DEFAULT = 0; @@ -237,6 +247,11 @@ public interface LuaParserConstants { "\">=\"", "\"==\"", "\"~=\"", + "\"~\"", + "\"&\"", + "\"|\"", + "\"<<\"", + "\">>\"", }; } diff --git a/jse/src/main/java/org/luaj/vm2/parser/LuaParserTokenManager.java b/jse/src/main/java/org/luaj/vm2/parser/LuaParserTokenManager.java index ca130b2f..aed79893 100644 --- a/jse/src/main/java/org/luaj/vm2/parser/LuaParserTokenManager.java +++ b/jse/src/main/java/org/luaj/vm2/parser/LuaParserTokenManager.java @@ -450,6 +450,8 @@ private int jjMoveStringLiteralDfa0_0() { case 35: return jjStopAtPos(0, 69); + case 38: + return jjStopAtPos(0, 96); case 37: return jjStopAtPos(0, 87); case 40: @@ -478,13 +480,13 @@ private int jjMoveStringLiteralDfa0_0() return jjStopAtPos(0, 70); case 60: jjmatchedKind = 89; - return jjMoveStringLiteralDfa1_0(0x0L, 0x4000000L); + return jjMoveStringLiteralDfa1_0(0x0L, 0x84000000L); case 61: jjmatchedKind = 71; return jjMoveStringLiteralDfa1_0(0x0L, 0x20000000L); case 62: jjmatchedKind = 91; - return jjMoveStringLiteralDfa1_0(0x0L, 0x10000000L); + return jjMoveStringLiteralDfa1_0(0x0L, 0x110000000L); case 91: jjmatchedKind = 77; return jjMoveStringLiteralDfa1_0(0x7800L, 0x0L); @@ -525,6 +527,7 @@ private int jjMoveStringLiteralDfa0_0() case 125: return jjStopAtPos(0, 81); case 126: + jjmatchedKind = 95; return jjMoveStringLiteralDfa1_0(0x0L, 0x40000000L); default : return jjMoveNfa_0(8, 0); @@ -557,10 +560,18 @@ private int jjMoveStringLiteralDfa1_0(long active0, long active1) if ((active1 & 0x1L) != 0L) return jjStopAtPos(1, 28); break; + case 60: + if ((active1 & 0x80000000L) != 0L) + return jjStopAtPos(1, 98); + break; case 58: if ((active1 & 0x2L) != 0L) return jjStopAtPos(1, 65); break; + case 62: + if ((active1 & 0x100000000L) != 0L) + return jjStopAtPos(1, 99); + break; case 61: if ((active1 & 0x4000000L) != 0L) return jjStopAtPos(1, 90); @@ -1759,7 +1770,7 @@ public static final int[] jjnewLexState = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; static final long[] jjtoToken = { - 0x601fffffef800001L, 0x7fffffe2L, + 0x601fffffef800001L, 0xfffffffe2L, }; static final long[] jjtoSkip = { 0x7e003eL, 0x0L, @@ -2105,3 +2116,5 @@ private void jjCheckNAddStates(int start, int end) } } + case 124: + return jjStopAtPos(0, 97); diff --git a/jse/src/main/java/org/luaj/vm2/script/LuaScriptEngine.java b/jse/src/main/java/org/luaj/vm2/script/LuaScriptEngine.java index e8525c0e..297f480d 100644 --- a/jse/src/main/java/org/luaj/vm2/script/LuaScriptEngine.java +++ b/jse/src/main/java/org/luaj/vm2/script/LuaScriptEngine.java @@ -47,7 +47,7 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin private static final String __NAME__ = "Luaj"; private static final String __SHORT_NAME__ = "Luaj"; private static final String __LANGUAGE__ = "lua"; - private static final String __LANGUAGE_VERSION__ = "5.2"; + private static final String __LANGUAGE_VERSION__ = "5.3"; private static final String __ARGV__ = "arg"; private static final String __FILENAME__ = "?"; @@ -244,7 +244,7 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin case LuaValue.TSTRING: return luajValue.tojstring(); case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class); case LuaValue.TNUMBER: return luajValue.isinttype()? - (Object) Integer.valueOf(luajValue.toint()): + (Object) Long.valueOf(luajValue.tolong()): (Object) Double.valueOf(luajValue.todouble()); default: return luajValue; } @@ -263,4 +263,4 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin } } -} \ No newline at end of file +} diff --git a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java index 12a19bc5..be93e45f 100644 --- a/jse/src/test/java/org/luaj/vm2/FragmentsTest.java +++ b/jse/src/test/java/org/luaj/vm2/FragmentsTest.java @@ -116,6 +116,127 @@ public class FragmentsTest extends TestSuite { public void testFloorDivisionInExpression() { runFragment(LuaValue.TRUE, "local x=5 local width=10 return x==width//2\n"); } + + public void testLongIntegerLiteralPrecision() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(9007199254740993L), + LuaValue.valueOf(9007199254740994L), + LuaValue.valueOf(9007199254740992L) + }), + "local x = 9007199254740993\nreturn x, x + 1, x - 1\n"); + } + + public void testBitwiseOperators() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(2), + LuaValue.valueOf(7), + LuaValue.valueOf(5), + LuaValue.valueOf(12), + LuaValue.valueOf(1), + LuaValue.valueOf(-6) + }), + "return 6 & 3, 6 | 3, 6 ~ 3, 3 << 2, 6 >> 2, ~5\n"); + } + + public void testBitwisePrecedence() { + runFragment(LuaValue.valueOf(7), "return 1 | 2 & 6\n"); + } + + public void testTableMove() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(1), + LuaValue.valueOf(2), + LuaValue.valueOf(3), + LuaValue.valueOf(1), + LuaValue.valueOf(2), + LuaValue.valueOf(3) + }), + "local t = {1,2,3}\nlocal d = {}\ntable.move(t, 1, 3, 1, d)\nreturn d[1], d[2], d[3], table.move(t, 1, 3, 2)[2], t[3], t[4]\n"); + } + + public void testUtf8Library() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(2), + LuaValue.valueOf(2), + LuaValue.valueOf(97), + LuaValue.valueOf(228), + LuaValue.valueOf(2) + }), + "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"); + } + + public void testStringPackUnpack() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(-5), + LuaValue.valueOf(513), + LuaValue.valueOf("hi"), + LuaValue.valueOf(10), + LuaValue.valueOf(7) + }), + "local s = string.pack('c4', 'ab')\nreturn s, (string.unpack('>c4', s)), select(2, string.unpack('>c4', s))\n"); + } + + public void testMath53Helpers() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf("integer"), + LuaValue.valueOf(3), + LuaValue.TRUE, + LuaValue.valueOf(Long.MAX_VALUE), + LuaValue.valueOf(Long.MIN_VALUE), + LuaValue.valueOf("Lua 5.3") + }), + "return math.type(3), math.tointeger(3.0), math.ult(-1, 1), math.maxinteger, math.mininteger, _VERSION\n"); + } + + public void testHexFloatLiteralAndTonumber() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf(3.0), + LuaValue.valueOf(3.0), + LuaValue.valueOf(16) + }), + "return 0x1.8p1, tonumber('0x1.8p1'), tonumber('0x10')\n"); + } + + public void testCoroutineIsYieldable() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.FALSE, + LuaValue.TRUE + }), + "local co = coroutine.create(function() return coroutine.isyieldable() end)\n" + + "local ok, value = coroutine.resume(co)\n" + + "return coroutine.isyieldable(), value\n"); + } + + public void testMathRandomSupportsLongBounds() { + runFragment( + LuaValue.varargsOf(new LuaValue[] { + LuaValue.valueOf("integer"), + LuaValue.TRUE + }), + "math.randomseed(123)\nlocal v = math.random(9007199254740993, 9007199254740995)\nreturn math.type(v), v >= 9007199254740993 and v <= 9007199254740995\n"); + } + + public void testStandardGlobalsDoNotExposeBit32() { + runFragment(LuaValue.TRUE, "return bit32 == nil\n"); + } public void testForloopParamUpvalues() { runFragment( LuaValue.varargsOf(new LuaValue[] { diff --git a/jse/src/test/java/org/luaj/vm2/libs/jse/LuaJavaCoercionTest.java b/jse/src/test/java/org/luaj/vm2/libs/jse/LuaJavaCoercionTest.java index 16e536a9..64ac13fd 100644 --- a/jse/src/test/java/org/luaj/vm2/libs/jse/LuaJavaCoercionTest.java +++ b/jse/src/test/java/org/luaj/vm2/libs/jse/LuaJavaCoercionTest.java @@ -40,6 +40,20 @@ public class LuaJavaCoercionTest extends TestCase { assertEquals( Integer.class, o.getClass() ); assertEquals( new Integer(777), o ); } + + public void testJavaLongToLuaInteger() { + Long l = Long.valueOf(9007199254740993L); + LuaValue v = CoerceJavaToLua.coerce(l); + assertEquals( LuaInteger.class, v.getClass() ); + assertEquals( 9007199254740993L, v.tolong() ); + } + + public void testLuaIntegerToJavaObjectUsesLongWhenNeeded() { + LuaInteger i = LuaInteger.valueOf(9007199254740993L); + Object o = CoerceLuaToJava.coerce(i, Object.class); + assertEquals( Long.class, o.getClass() ); + assertEquals( Long.valueOf(9007199254740993L), o ); + } public void testJavaStringToLuaString() { String s = new String("777");