From 1d67e30a0a04a59cd4db58a1bb3dbad45c2e629b Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Fri, 12 Mar 2021 19:48:56 -0500 Subject: [PATCH 01/24] Added (preliminary) Lua 5.2 environment support Still needs a LOT of testing. It's probably pretty broken right now. --- .../squiddev/cobalt/compiler/FuncState.java | 35 +++++++++++++------ .../org/squiddev/cobalt/compiler/LuaC.java | 10 +++++- .../function/LuaInterpretedFunction.java | 15 ++++++-- .../cobalt/function/LuaInterpreter.java | 6 ++-- 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index a5866048..7ac29966 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -40,6 +40,7 @@ public class FuncState { class upvaldesc { short k; short info; + LuaString name; } static class BlockCnt { @@ -69,6 +70,7 @@ static class BlockCnt { short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ FuncState() { + } @@ -136,6 +138,14 @@ private int indexupvalue(LuaString name, expdesc v) throws CompileException { return f.nups++; } + private int searchupvalue(LuaString n) { + int i; + for (i = 0; i < f.nups; i++) { + if (f.upvalues[i] == n) return i; + } + return -1; /* not found */ + } + private int searchvar(LuaString n) { int i; for (i = nactvar - 1; i >= 0; i--) { @@ -164,17 +174,22 @@ int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { markupval(v); /* local will be used as an upval */ } return LexState.VLOCAL; - } else { /* not found at current level; try upper one */ - if (prev == null) { /* no more levels? */ - /* default is global variable */ - var.init(LexState.VGLOBAL, NO_REG); - return LexState.VGLOBAL; - } - if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) { - return LexState.VGLOBAL; + } else { /* not found at current level; try upvalues */ + int idx = searchupvalue(n); + if (idx < 0) { /* not found? */ + if (prev == null) { /* no more levels? */ + /* default is global variable */ + var.init(LexState.VGLOBAL, NO_REG); + return LexState.VGLOBAL; + } + if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) { + return LexState.VGLOBAL; + } + var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ + var.k = LexState.VUPVAL; /* upvalue in this level */ + return LexState.VUPVAL; } - var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ - var.k = LexState.VUPVAL; /* upvalue in this level */ + var.init(LexState.VUPVAL, idx); return LexState.VUPVAL; } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index ce934920..de478097 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -223,12 +223,20 @@ private static Prototype luaY_parser(int firstByte, InputStream z, LuaString nam /* main func. is always vararg */ funcstate.f.is_vararg = Lua.VARARG_ISVARARG; funcstate.f.source = name; + LexState.expdesc v = new LexState.expdesc(); + v.init(LexState.VLOCAL, 0); + funcstate.f.upvalues = new LuaString[1]; + funcstate.f.upvalues[0] = LuaString.valueOf("_ENV"); + funcstate.f.nups = 1; + funcstate.upvalues[0] = funcstate.new upvaldesc(); + funcstate.upvalues[0].k = LexState.VLOCAL; + funcstate.upvalues[0].info = (short)v.u.s.info; lexstate.nextToken(); /* read first token */ lexstate.chunk(); lexstate.check(LexState.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); - LuaC._assert(funcstate.f.nups == 0); + LuaC._assert(funcstate.f.nups == 1); LuaC._assert(lexstate.fs == null); return funcstate.f; } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index 7d3ccb44..db2f2ce7 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -99,7 +99,8 @@ public final class LuaInterpretedFunction extends LuaClosure implements Resumabl public LuaInterpretedFunction(Prototype p) { this.p = p; - this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + //this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + this.upvalues = new Upvalue[p.nups + 1]; } /** @@ -111,14 +112,16 @@ public LuaInterpretedFunction(Prototype p) { public LuaInterpretedFunction(Prototype p, LuaTable env) { super(env); this.p = p; - this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + //this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + this.upvalues = new Upvalue[p.nups + 1]; + this.upvalues[0] = new Upvalue(env); } public void nilUpvalues() { int nups = p.nups; if (nups > 0) { Upvalue[] upvalues = this.upvalues; - for (int i = 0; i < nups; i++) { + for (int i = 1; i < nups; i++) { upvalues[i] = new Upvalue(Constants.NIL); } } @@ -149,6 +152,12 @@ public final Varargs invoke(LuaState state, Varargs varargs) throws LuaError, Un return execute(state, setupCall(state, this, varargs, FLAG_FRESH), this); } + @Override + public LuaTable getfenv() { return (LuaTable)upvalues[0].getValue(); } + + @Override + public void setfenv(LuaTable env) { upvalues[0].setValue(env); } + @Override public Upvalue getUpvalue(int i) { return upvalues[i]; diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 1be3ee9b..89c63626 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -203,7 +203,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; case OP_GETGLOBAL: // A Bx R(A):= Gbl[Kst(Bx)] - stack[a] = OperationHelper.getTable(state, function.env, k[(i >>> POS_Bx) & MAXARG_Bx]); + stack[a] = OperationHelper.getTable(state, upvalues[0].getValue(), k[(i >>> POS_Bx) & MAXARG_Bx]); break; case OP_GETTABLE: { // A B C: R(A):= R(B)[RK(C)] @@ -214,7 +214,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } case OP_SETGLOBAL: // A Bx: Gbl[Kst(Bx)]:= R(A) - OperationHelper.setTable(state, function.env, k[(i >>> POS_Bx) & MAXARG_Bx], stack[a]); + OperationHelper.setTable(state, upvalues[0].getValue(), k[(i >>> POS_Bx) & MAXARG_Bx], stack[a]); break; case OP_SETUPVAL: // A B: UpValue[B]:= R(A) @@ -612,7 +612,7 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), case OP_CLOSURE: { // A Bx: R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) Prototype newp = p.p[(i >>> POS_Bx) & MAXARG_Bx]; - LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, function.env); + LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, (LuaTable)upvalues[0].getValue()); for (int j = 0, nup = newp.nups; j < nup; ++j) { i = code[pc++]; int b = (i >>> POS_B) & MAXARG_B; From c1ddfdc3af7a3b127f4ce9d66ed994fc5d1df03b Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Fri, 26 Mar 2021 14:27:17 -0400 Subject: [PATCH 02/24] Implemented support for Lua 5.2 bytecode Also fixed how environments are injected (I think?). Still generates 5.1 bytecode, so that'll have to be switched to 5.2. --- src/main/java/org/squiddev/cobalt/Lua52.java | 431 ++++++++++++++++++ .../java/org/squiddev/cobalt/LuaThread.java | 5 + src/main/java/org/squiddev/cobalt/Print.java | 49 +- .../java/org/squiddev/cobalt/Prototype.java | 2 + .../cobalt/compiler/BytecodeLoader.java | 39 +- .../function/LuaInterpretedFunction.java | 12 +- .../cobalt/function/LuaInterpreter.java | 95 +++- 7 files changed, 611 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/squiddev/cobalt/Lua52.java diff --git a/src/main/java/org/squiddev/cobalt/Lua52.java b/src/main/java/org/squiddev/cobalt/Lua52.java new file mode 100644 index 00000000..0a32571d --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/Lua52.java @@ -0,0 +1,431 @@ +/* + * The MIT License (MIT) + * + * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. + * Modifications: Copyright (c) 2015-2020 SquidDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.squiddev.cobalt; + + +/** + * Constants for lua limits and opcodes. + * + * This is a direct translation of C lua distribution header file constants + * for bytecode creation and processing. + */ +public class Lua52 { + /** + * Version is supplied by ant build task + */ + public static final String _VERSION = "Luaj 0.0"; + + /** + * Use return values from previous op + */ + public static final int LUA_MULTRET = -1; + + /** + * Masks for new-style vararg + */ + public static final int VARARG_HASARG = 1; + public static final int VARARG_ISVARARG = 2; + public static final int VARARG_NEEDSARG = 4; + + // from lopcodes.h + + /*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. + ===========================================================================*/ + + + /* basic instruction format */ + public static final int iABC = 0; + public static final int iABx = 1; + public static final int iAsBx = 2; + public static final int iAx = 3; + + + /** + * Size and position of opcode arguments. + */ + public static final int SIZE_C = 9; + public static final int SIZE_B = 9; + public static final int SIZE_Bx = (SIZE_C + SIZE_B); + public static final int SIZE_A = 8; + public static final int SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A); + + public static final int SIZE_OP = 6; + + public static final int POS_OP = 0; + public static final int POS_A = (POS_OP + SIZE_OP); + public static final int POS_C = (POS_A + SIZE_A); + public static final int POS_B = (POS_C + SIZE_C); + public static final int POS_Bx = POS_C; + public static final int POS_Ax = POS_A; + + + public static final int MAX_OP = ((1 << SIZE_OP) - 1); + public static final int MAXARG_A = ((1 << SIZE_A) - 1); + public static final int MAXARG_B = ((1 << SIZE_B) - 1); + public static final int MAXARG_C = ((1 << SIZE_C) - 1); + public static final int MAXARG_Bx = ((1 << SIZE_Bx) - 1); + public static final int MAXARG_sBx = (MAXARG_Bx >> 1); /* `sBx' is signed */ + public static final int MAXARG_Ax = ((1<> POS_OP) & MAX_OP; + } + + public static int GETARG_A(int i) { + return (i >> POS_A) & MAXARG_A; + } + + public static int GETARG_B(int i) { + return (i >> POS_B) & MAXARG_B; + } + + public static int GETARG_C(int i) { + return (i >> POS_C) & MAXARG_C; + } + + public static int GETARG_Bx(int i) { + return (i >> POS_Bx) & MAXARG_Bx; + } + + public static int GETARG_Ax(int i) { + return (i >> POS_Ax) & MAXARG_Ax; + } + + public static int GETARG_sBx(int i) { + return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + } + + + /* + ** Macros to operate RK indices + */ + + /** + * This bit 1 means constant (0 means register) + */ + public static final int BITRK = (1 << (SIZE_B - 1)); + + /** + * Test whether value is a constant + * + * @param x The part of the opcode + * @return If the opcode part is a constant + */ + public static boolean ISK(int x) { + return 0 != ((x) & BITRK); + } + + /** + * Gets the index of the constant + * + * @param r The part of the opcode + * @return The constant index + */ + public static int INDEXK(int r) { + return (r & ~BITRK); + } + + public static final int MAXINDEXRK = (BITRK - 1); + + /** + * code a constant index as a RK value + * + * @param x The constant index + * @return The part of the opcode + */ + public static int RKASK(int x) { + return ((x) | BITRK); + } + + + /** + * Invalid register that fits in 8 bits + */ + public static final int NO_REG = MAXARG_A; + + + /* + ** R(x) - register + ** Kst(x) - constant (in constant table) + ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x) + */ + + + /* + ** grep "ORDER OP" if you change these enums + */ + + /*---------------------------------------------------------------------- + name args description + ------------------------------------------------------------------------*/ + public static final int OP_MOVE = 0;/* A B R(A) := R(B) */ + public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */ + public static final int OP_LOADKX = 2;/* A R(A) := Kst(extra arg) */ + public static final int OP_LOADBOOL = 3;/* A B C R(A) := (Bool)B; if (C) pc++ */ + public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(B) := nil */ + public static final int OP_GETUPVAL = 5; /* A B R(A) := UpValue[B] */ + public static final int OP_GETTABUP = 6; /* A B C := UpValue[B][RK(C)] */ + public static final int OP_GETTABLE = 7; /* A B C R(A) := R(B)[RK(C)] */ + + public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */ + public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */ + public static final int OP_SETTABLE = 10;/* A B C R(A)[RK(B)] := RK(C) */ + + public static final int OP_NEWTABLE = 11;/* A B C R(A) := {} (size = B,C) */ + + public static final int OP_SELF = 12; /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ + + public static final int OP_ADD = 13; /* A B C R(A) := RK(B) + RK(C) */ + public static final int OP_SUB = 14; /* A B C R(A) := RK(B) - RK(C) */ + 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_MOD = 17; /* A B C R(A) := RK(B) % RK(C) */ + public static final int OP_POW = 18; /* A B C R(A) := RK(B) ^ RK(C) */ + public static final int OP_UNM = 19; /* A B R(A) := -R(B) */ + public static final int OP_NOT = 20; /* A B R(A) := not R(B) */ + public static final int OP_LEN = 21; /* A B R(A) := length of R(B) */ + + public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */ + + public static final int OP_JMP = 23; /* sBx pc+=sBx */ + + public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + + public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */ + public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + + public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 30;/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */ + + public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ + + /* pseudo-opcodes used in parsing only. */ + public static final int OP_GT = 63; // > + public static final int OP_GE = 62; // >= + public static final int OP_NEQ = 61; // ~= + public static final int OP_AND = 60; // and + public static final int OP_OR = 59; // or + + /*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump + ===========================================================================*/ + + + /* + ** masks for instruction properties. The format is: + ** bits 0-1: op mode + ** bits 2-3: C arg mode + ** bits 4-5: B arg mode + ** bit 6: instruction set register A + ** bit 7: operator is a test + */ + + public static final int OpArgN = 0; /* argument is not used */ + public static final int OpArgU = 1; /* argument is used */ + public static final int OpArgR = 2; /* argument is a register or a jump offset */ + public static final int OpArgK = 3; /* argument is a constant or register/constant */ + + public static final int[] luaP_opmodes = { + /* T A B C mode opcode */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iABC), /* OP_MOVE */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgN << 2) | (iABx), /* OP_LOADK */ + (0 << 7) | (1 << 6) | (OpArgN << 4) | (OpArgN << 2) | (iABx), /* OP_LOADKX */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_LOADBOOL */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iABC), /* OP_LOADNIL */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABC), /* OP_GETUPVAL */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgK << 2) | (iABC), /* OP_GETTABUP */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgK << 2) | (iABC), /* OP_GETTABLE */ + (0 << 7) | (0 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_SETTABUP */ + (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABC), /* OP_SETUPVAL */ + (0 << 7) | (0 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_SETTABLE */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_NEWTABLE */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgK << 2) | (iABC), /* OP_SELF */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_ADD */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_SUB */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_MUL */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_DIV */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_MOD */ + (0 << 7) | (1 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_POW */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iABC), /* OP_UNM */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iABC), /* OP_NOT */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iABC), /* OP_LEN */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgR << 2) | (iABC), /* OP_CONCAT */ + (0 << 7) | (0 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_JMP */ + (1 << 7) | (0 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_EQ */ + (1 << 7) | (0 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_LT */ + (1 << 7) | (0 << 6) | (OpArgK << 4) | (OpArgK << 2) | (iABC), /* OP_LE */ + (1 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgU << 2) | (iABC), /* OP_TEST */ + (1 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgU << 2) | (iABC), /* OP_TESTSET */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_CALL */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_TAILCALL */ + (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABC), /* OP_RETURN */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_FORLOOP */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_FORPREP */ + (0 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgU << 2) | (iABC), /* OP_TFORCALL */ + (1 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgU << 2) | (iABC), /* OP_TFORLOOP */ + (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_SETLIST */ + (0 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgN << 2) | (iABC), /* OP_CLOSE */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABx), /* OP_CLOSURE */ + (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABC), /* OP_VARARG */ + (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iAx), /* OP_EXTRAARG */ + }; + + public static int getOpMode(int m) { + return luaP_opmodes[m] & 3; + } + + public static int getBMode(int m) { + return (luaP_opmodes[m] >> 4) & 3; + } + + public static int getCMode(int m) { + return (luaP_opmodes[m] >> 2) & 3; + } + + public static boolean testAMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 6)); + } + + public static boolean testTMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 7)); + } + + /* number of list items to accumulate before a SETLIST instruction */ + public static final int LFIELDS_PER_FLUSH = 50; + + /* conversion from Lua 5.1 instructions => Lua 5.2 instructions */ + public static final int[] lua51opcodes = { + OP_MOVE, + OP_LOADK, + OP_LOADBOOL, + OP_LOADNIL, + OP_GETUPVAL, + OP_GETGLOBAL, + OP_GETTABLE, + OP_SETGLOBAL, + OP_SETUPVAL, + OP_SETTABLE, + OP_NEWTABLE, + OP_SELF, + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_POW, + OP_UNM, + OP_NOT, + OP_LEN, + OP_CONCAT, + OP_JMP, + OP_EQ, + OP_LT, + OP_LE, + OP_TEST, + OP_TESTSET, + OP_CALL, + OP_TAILCALL, + OP_RETURN, + OP_FORLOOP, + OP_FORPREP, + OP_TFORLOOP, + OP_SETLIST, + OP_CLOSE, + OP_CLOSURE, + OP_VARARG + }; + + /* Lua 5.2 bytecode error checking bytes */ + public static final byte[] error_check = {0x19, (byte)0x93, 0x0d, 0x0a, 0x1a, 0x0a}; + +} diff --git a/src/main/java/org/squiddev/cobalt/LuaThread.java b/src/main/java/org/squiddev/cobalt/LuaThread.java index 7954f6cc..edb1924b 100644 --- a/src/main/java/org/squiddev/cobalt/LuaThread.java +++ b/src/main/java/org/squiddev/cobalt/LuaThread.java @@ -28,6 +28,8 @@ import org.squiddev.cobalt.debug.DebugHandler; import org.squiddev.cobalt.debug.DebugState; import org.squiddev.cobalt.function.LuaFunction; +import org.squiddev.cobalt.function.LuaInterpretedFunction; +import org.squiddev.cobalt.function.Upvalue; import org.squiddev.cobalt.lib.CoroutineLib; import org.squiddev.cobalt.lib.jse.JsePlatform; @@ -459,6 +461,9 @@ public void run() { // Clear the function after the first run LuaFunction function = func; func = null; + if (function instanceof LuaInterpretedFunction) { + ((LuaInterpretedFunction)function).setUpvalue(0, new Upvalue(thread.env)); + } Varargs res = loop(state, state.currentThread, function, threader.unpack()); diff --git a/src/main/java/org/squiddev/cobalt/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index 738f16c9..095593f0 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -32,7 +32,7 @@ import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import static org.squiddev.cobalt.Lua.*; +import static org.squiddev.cobalt.Lua52.*; /** * Debug helper class to pretty-print lua bytecodes. @@ -83,6 +83,51 @@ public class Print { null, }; + public static final String[] OPNAMES_52 = { + "MOVE", + "LOADK", + "LOADKX", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETTABUP", + "GETTABLE", + "SETTABUP", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + "EXTRAARG", + null, + }; + static void printString(PrintStream ps, final LuaString s) { @@ -182,7 +227,7 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { } else { ps.print("[-] "); } - ps.print(OPNAMES[o] + " "); + ps.print((f.isLua52 ? OPNAMES_52[o] : OPNAMES[o]) + " "); switch (getOpMode(o)) { case iABC: ps.print(a); diff --git a/src/main/java/org/squiddev/cobalt/Prototype.java b/src/main/java/org/squiddev/cobalt/Prototype.java index e1b1eaf2..a7a35746 100644 --- a/src/main/java/org/squiddev/cobalt/Prototype.java +++ b/src/main/java/org/squiddev/cobalt/Prototype.java @@ -59,6 +59,8 @@ public final class Prototype { public int numparams; public int is_vararg; public int maxstacksize; + public boolean isLua52; + public int[] upvalue_info; public LuaString sourceShort() { return getShortName(source); diff --git a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java index a84cb686..314a571e 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java +++ b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java @@ -30,6 +30,7 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import static org.squiddev.cobalt.Constants.*; @@ -57,6 +58,11 @@ public final class BytecodeLoader { */ public static final int LUAC_VERSION = 0x51; + /** + * for header of binary files -- this is Lua 5.2 + */ + public static final int LUAC_VERSION_52 = 0x52; + /** * for header of binary files -- this is the official format */ @@ -71,6 +77,7 @@ public final class BytecodeLoader { private boolean luacLittleEndian; private int luacSizeofSizeT; private int luacNumberFormat; + private boolean isLua52; /** * input stream from which we are loading @@ -256,6 +263,10 @@ private void loadConstants(Prototype f) throws IOException { * @throws IOException if there is an i/o exception */ private void loadDebug(Prototype f) throws IOException { + if (isLua52) { + LuaString src = loadString(); + if (src != null) f.source = src; + } f.lineinfo = loadIntArray(); int n = loadInt(); f.locvars = n > 0 ? new LocalVariable[n] : NOLOCVARS; @@ -282,18 +293,29 @@ private void loadDebug(Prototype f) throws IOException { */ public Prototype loadFunction(LuaString p) throws IOException { Prototype f = new Prototype(); - f.source = loadString(); - if (f.source == null) { - f.source = p; + if (isLua52) f.source = p; + else { + f.source = loadString(); + if (f.source == null) { + f.source = p; + } } f.linedefined = loadInt(); f.lastlinedefined = loadInt(); - f.nups = is.readUnsignedByte(); + if (!isLua52) f.nups = is.readUnsignedByte(); f.numparams = is.readUnsignedByte(); f.is_vararg = is.readUnsignedByte(); f.maxstacksize = is.readUnsignedByte(); + f.isLua52 = isLua52; f.code = loadIntArray(); loadConstants(f); + if (isLua52) { + f.nups = loadInt(); + f.upvalue_info = new int[f.nups]; + for (int i = 0; i < f.nups; i++) { + f.upvalue_info[i] = is.readUnsignedByte() << 8 | is.readUnsignedByte(); + } + } loadDebug(f); // TODO: add check here, for debugging purposes, I believe @@ -311,7 +333,9 @@ public Prototype loadFunction(LuaString p) throws IOException { */ public void loadHeader() throws IOException, CompileException { int luacVersion = is.readByte(); - if (luacVersion != LUAC_VERSION) throw new CompileException("unsupported luac version"); + if (luacVersion == LUAC_VERSION) isLua52 = false; + else if (luacVersion == LUAC_VERSION_52) isLua52 = true; + else throw new CompileException("unsupported luac version"); int luacFormat = is.readByte(); luacLittleEndian = (0 != is.readByte()); @@ -320,6 +344,11 @@ public void loadHeader() throws IOException, CompileException { int luacSizeofInstruction = is.readByte(); int luacSizeofLuaNumber = is.readByte(); luacNumberFormat = is.readByte(); + if (isLua52) { + byte[] data = new byte[6]; + is.read(data); + if (!Arrays.equals(data, Lua52.error_check)) throw new CompileException("corrupted precompiled chunk"); + } // check format switch (luacNumberFormat) { diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index db2f2ce7..f2a07991 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -99,8 +99,8 @@ public final class LuaInterpretedFunction extends LuaClosure implements Resumabl public LuaInterpretedFunction(Prototype p) { this.p = p; - //this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; - this.upvalues = new Upvalue[p.nups + 1]; + if (p.isLua52) this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + else this.upvalues = new Upvalue[p.nups + 1]; } /** @@ -112,9 +112,11 @@ public LuaInterpretedFunction(Prototype p) { public LuaInterpretedFunction(Prototype p, LuaTable env) { super(env); this.p = p; - //this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; - this.upvalues = new Upvalue[p.nups + 1]; - this.upvalues[0] = new Upvalue(env); + if (p.isLua52) this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + else { + this.upvalues = new Upvalue[p.nups + 1]; + this.upvalues[0] = new Upvalue(env); + } } public void nilUpvalues() { diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 89c63626..16864e12 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -30,7 +30,7 @@ import org.squiddev.cobalt.debug.DebugState; import static org.squiddev.cobalt.Constants.*; -import static org.squiddev.cobalt.Lua.*; +import static org.squiddev.cobalt.Lua52.*; import static org.squiddev.cobalt.LuaDouble.valueOf; import static org.squiddev.cobalt.debug.DebugFrame.*; @@ -175,8 +175,12 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti int i = code[pc++]; int a = ((i >> POS_A) & MAXARG_A); + int op = ((i >> POS_OP) & MAX_OP); + + if (!p.isLua52) op = lua51opcodes[op]; + // process the instruction - switch (((i >> POS_OP) & MAX_OP)) { + switch (op) { case OP_MOVE: // A B: R(A):= R(B) stack[a] = stack[(i >>> POS_B) & MAXARG_B]; break; @@ -185,6 +189,11 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti stack[a] = k[(i >>> POS_Bx) & MAXARG_Bx]; break; + case OP_LOADKX: // A: R(A) := Kst(extra arg) + // assert(((code[pc] >> POS_OP) & MAX_OP) == OP_EXTRAARG); + stack[a] = k[GETARG_Ax(code[pc++])]; + break; + case OP_LOADBOOL: // A B C: R(A):= (Bool)B: if (C) pc++ stack[a] = ((i >>> POS_B) & MAXARG_B) != 0 ? TRUE : FALSE; if (((i >>> POS_C) & MAXARG_C) != 0) pc++; // skip next instruction (if C) @@ -202,6 +211,13 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti stack[a] = upvalues[((i >>> POS_B) & MAXARG_B)].getValue(); break; + case OP_GETTABUP: { // A B C: R(A) := UpValue[B][RK(C)] + int b = (i >>> POS_B) & MAXARG_B; + int c = (i >>> POS_C) & MAXARG_C; + stack[a] = OperationHelper.getTable(state, upvalues[b].getValue(), c > 0xff ? k[c & 0x0ff] : stack[c], b); + break; + } + case OP_GETGLOBAL: // A Bx R(A):= Gbl[Kst(Bx)] stack[a] = OperationHelper.getTable(state, upvalues[0].getValue(), k[(i >>> POS_Bx) & MAXARG_Bx]); break; @@ -217,6 +233,13 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti OperationHelper.setTable(state, upvalues[0].getValue(), k[(i >>> POS_Bx) & MAXARG_Bx], stack[a]); break; + case OP_SETTABUP: { // A B C: UpValue[A][RK(B)] := RK(C) + int b = (i >>> POS_B) & MAXARG_B; + int c = (i >>> POS_C) & MAXARG_C; + OperationHelper.setTable(state, upvalues[a].getValue(), b > 0xff ? k[b & 0x0ff] : stack[b], c > 0xff ? k[c & 0x0ff] : stack[c], a); + break; + } + case OP_SETUPVAL: // A B: UpValue[B]:= R(A) upvalues[(i >>> POS_B) & MAXARG_B].setValue(stack[a]); break; @@ -553,6 +576,29 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } break; + case OP_TFORCALL: { // A C: R(A+3), ..., R(A+2+C) := R(A)(R(A+1), R(A+2)); + int c = ((i >> POS_C) & MAXARG_C); + + LuaValue val = stack[a]; + if (val instanceof LuaInterpretedFunction) { + function = (LuaInterpretedFunction) val; + di = setupCall(state, function, stack[a + 1], stack[a + 2], 0); + continue newFrame; + } + + Varargs args = ValueFactory.varargsOf(stack, a + 1, a + 2); // exact arg count + Varargs v = OperationHelper.invoke(state, val, args.asImmutable(), a); + i = code[pc++]; + a = ((i >> POS_A) & MAXARG_A); + if (c > 0) { + while (--c > 0) stack[a + 3 + c] = v.arg(c); + v = NONE; + } else { + di.top = a + 3 + v.count(); + di.extras = v; + } + } + case OP_TFORLOOP: { /* A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), @@ -613,12 +659,23 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), case OP_CLOSURE: { // A Bx: R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) Prototype newp = p.p[(i >>> POS_Bx) & MAXARG_Bx]; LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, (LuaTable)upvalues[0].getValue()); - for (int j = 0, nup = newp.nups; j < nup; ++j) { - i = code[pc++]; - int b = (i >>> POS_B) & MAXARG_B; - newcl.upvalues[j] = (i & 4) != 0 - ? upvalues[b] // OP_GETUPVAL - : openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); // OP_MOVE + if (p.isLua52) { + for (int j = 0; j < newp.nups; ++j) { + DebugFrame frame = di; + for (int s = (newp.upvalue_info[j] >> 8) - 1; s > 0 && frame != null; --s) frame = frame.previous; + if (frame == null) { + // TODO: handle this + } + newcl.upvalues[j] = new Upvalue(frame.stack, newp.upvalue_info[j] & 0xFF); + } + } else { + for (int j = 0, nup = newp.nups; j < nup; ++j) { + i = code[pc++]; + int b = (i >>> POS_B) & MAXARG_B; + newcl.upvalues[j] = (i & 4) != 0 + ? upvalues[b] // OP_GETUPVAL + : openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); // OP_MOVE + } } stack[a] = newcl; break; @@ -634,7 +691,11 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), stack[a + j - 1] = varargs.arg(j); } } + break; } + + case OP_EXTRAARG: + // throw exception? } } } @@ -701,7 +762,7 @@ public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction switch (((i >> POS_OP) & MAX_OP)) { case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: case OP_POW: case OP_UNM: - case OP_GETTABLE: case OP_GETGLOBAL: case OP_SELF: { + case OP_GETTABLE: case OP_GETTABUP: case OP_GETGLOBAL: case OP_SELF: { di.stack[(i >> POS_A) & MAXARG_A] = varargs.first(); break; } @@ -723,6 +784,20 @@ public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction break; } + case OP_TFORCALL: { + int a = (i >>> POS_A) & MAXARG_A; + int c = (i >>> POS_C) & MAXARG_C; + if (c > 0) { + LuaValue[] stack = di.stack; + while (--c > 0) stack[a + 3 + c] = varargs.arg(c); + di.extras = NONE; + } else { + di.top = a + 3 + varargs.count(); + di.extras = varargs; + } + break; + } + case OP_CALL: case OP_TAILCALL: { int a = (i >>> POS_A) & MAXARG_A; int c = (i >>> POS_C) & MAXARG_C; @@ -737,7 +812,7 @@ public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction break; } - case OP_SETTABLE: case OP_SETGLOBAL: + case OP_SETTABLE: case OP_SETGLOBAL: case OP_SETTABUP: // Nothing to be done here break; From 0449528fbcaf735266a9e6aaa502e45ebaaa2ff7 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Tue, 30 Mar 2021 01:56:28 -0400 Subject: [PATCH 03/24] Made most things work again I had to revert the changes to the code generator back to pure 5.1, so _ENV is no longer special in code. However, the environment is still stored as an upvalue, even on 5.1 (which is the version the bytecode is generated for). It properly loads 5.2 bytecode for the most part, but there's still a few things broken with upvalues. (It's able to get CC to partially boot through the BIOS!) --- .../java/org/squiddev/cobalt/LuaError.java | 1 + .../cobalt/compiler/BytecodeLoader.java | 5 +- .../squiddev/cobalt/compiler/FuncState.java | 35 +- .../squiddev/cobalt/compiler/FuncState52.java | 1136 +++++++++ .../squiddev/cobalt/compiler/LexState52.java | 2043 +++++++++++++++++ .../squiddev/cobalt/compiler/LoadState.java | 4 + .../org/squiddev/cobalt/compiler/LuaC.java | 29 +- .../cobalt/function/LuaInterpreter.java | 45 +- .../java/org/squiddev/cobalt/lib/BaseLib.java | 5 + 9 files changed, 3251 insertions(+), 52 deletions(-) create mode 100644 src/main/java/org/squiddev/cobalt/compiler/FuncState52.java create mode 100644 src/main/java/org/squiddev/cobalt/compiler/LexState52.java diff --git a/src/main/java/org/squiddev/cobalt/LuaError.java b/src/main/java/org/squiddev/cobalt/LuaError.java index c8794d07..cb2fda8e 100644 --- a/src/main/java/org/squiddev/cobalt/LuaError.java +++ b/src/main/java/org/squiddev/cobalt/LuaError.java @@ -76,6 +76,7 @@ public LuaError(Throwable cause) { level = 1; calculateLevel = true; value = ValueFactory.valueOf("vm error: " + cause.toString()); + cause.printStackTrace(System.err); } /** diff --git a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java index 314a571e..b8734693 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java +++ b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java @@ -90,7 +90,6 @@ public final class BytecodeLoader { * @param stream The stream to read from */ public BytecodeLoader(InputStream stream) { - this.is = new DataInputStream(stream); } @@ -347,7 +346,9 @@ public void loadHeader() throws IOException, CompileException { if (isLua52) { byte[] data = new byte[6]; is.read(data); - if (!Arrays.equals(data, Lua52.error_check)) throw new CompileException("corrupted precompiled chunk"); + if (!Arrays.equals(data, Lua52.error_check)) { + throw new CompileException("corrupted precompiled chunk"); + } } // check format diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index 7ac29966..a5866048 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -40,7 +40,6 @@ public class FuncState { class upvaldesc { short k; short info; - LuaString name; } static class BlockCnt { @@ -70,7 +69,6 @@ static class BlockCnt { short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ FuncState() { - } @@ -138,14 +136,6 @@ private int indexupvalue(LuaString name, expdesc v) throws CompileException { return f.nups++; } - private int searchupvalue(LuaString n) { - int i; - for (i = 0; i < f.nups; i++) { - if (f.upvalues[i] == n) return i; - } - return -1; /* not found */ - } - private int searchvar(LuaString n) { int i; for (i = nactvar - 1; i >= 0; i--) { @@ -174,22 +164,17 @@ int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { markupval(v); /* local will be used as an upval */ } return LexState.VLOCAL; - } else { /* not found at current level; try upvalues */ - int idx = searchupvalue(n); - if (idx < 0) { /* not found? */ - if (prev == null) { /* no more levels? */ - /* default is global variable */ - var.init(LexState.VGLOBAL, NO_REG); - return LexState.VGLOBAL; - } - if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) { - return LexState.VGLOBAL; - } - var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ - var.k = LexState.VUPVAL; /* upvalue in this level */ - return LexState.VUPVAL; + } else { /* not found at current level; try upper one */ + if (prev == null) { /* no more levels? */ + /* default is global variable */ + var.init(LexState.VGLOBAL, NO_REG); + return LexState.VGLOBAL; + } + if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) { + return LexState.VGLOBAL; } - var.init(LexState.VUPVAL, idx); + var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ + var.k = LexState.VUPVAL; /* upvalue in this level */ return LexState.VUPVAL; } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java new file mode 100644 index 00000000..6b7fb842 --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java @@ -0,0 +1,1136 @@ +/* + * The MIT License (MIT) + * + * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. + * Modifications: Copyright (c) 2015-2020 SquidDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.squiddev.cobalt.compiler; + + +import org.squiddev.cobalt.*; +import org.squiddev.cobalt.compiler.LexState52.ConsControl; +import org.squiddev.cobalt.compiler.LexState52.expdesc; +import org.squiddev.cobalt.function.LocalVariable; + +import java.util.Hashtable; + +import static org.squiddev.cobalt.Constants.*; +import static org.squiddev.cobalt.Lua.*; +import static org.squiddev.cobalt.compiler.LuaC.*; + +public class FuncState52 { + class upvaldesc { + short k; + short info; + LuaString name; + } + + static class BlockCnt { + BlockCnt previous; /* chain */ + IntPtr breaklist = new IntPtr(); /* list of jumps out of this loop */ + short nactvar; /* # active locals outside the breakable structure */ + boolean upval; /* true if some variable in the block is an upvalue */ + boolean isbreakable; /* true if `block' is a loop */ + } + + Prototype f; /* current function header */ + // LTable h; /* table to find (and reuse) elements in `k' */ + Hashtable htable; /* table to find (and reuse) elements in `k' */ + FuncState52 prev; /* enclosing function */ + LexState52 ls; /* lexical state */ + LuaC L; /* compiler being invoked */ + BlockCnt bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* `pc' of last `jump target' */ + IntPtr jpc; /* list of pending jumps to `pc' */ + int freereg; /* first free register */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + short nlocvars; /* number of elements in `locvars' */ + short nactvar; /* number of active local variables */ + upvaldesc upvalues[] = new upvaldesc[LUAI_MAXUPVALUES]; /* upvalues */ + short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ + + FuncState52() { + + } + + + // ============================================================= + // from lcode.h + // ============================================================= + + InstructionPtr getcodePtr(expdesc e) { + return new InstructionPtr(f.code, e.u.s.info); + } + + int getcode(expdesc e) { + return f.code[e.u.s.info]; + } + + int codeAsBx(int o, int A, int sBx) throws CompileException { + return codeABx(o, A, sBx + MAXARG_sBx); + } + + void setmultret(expdesc e) throws CompileException { + setreturns(e, LUA_MULTRET); + } + + + // ============================================================= + // from lparser.c + // ============================================================= + + LocalVariable getlocvar(int i) { + return f.locvars[actvar[i]]; + } + + void checklimit(int v, int l, String msg) throws CompileException { + if (v > l) { + errorlimit(l, msg); + } + } + + private void errorlimit(int limit, String what) throws CompileException { + String msg = (f.linedefined == 0) ? + "main function has more than " + limit + " " + what : + "function at line " + f.linedefined + " has more than " + limit + " " + what; + throw ls.lexError(msg, 0); + } + + + private int indexupvalue(LuaString name, expdesc v) throws CompileException { + int i; + for (i = 0; i < f.nups; i++) { + if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) { + _assert(f.upvalues[i] == name); + return i; + } + } + /* new one */ + checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); + if (f.upvalues == null || f.nups + 1 > f.upvalues.length) { + f.upvalues = realloc(f.upvalues, f.nups * 2 + 1); + } + f.upvalues[f.nups] = name; + _assert(v.k == LexState52.VLOCAL || v.k == LexState52.VUPVAL); + upvalues[f.nups] = new upvaldesc(); + upvalues[f.nups].k = (short) (v.k); + upvalues[f.nups].info = (short) (v.u.s.info); + return f.nups++; + } + + private int searchupvalue(LuaString n) { + int i; + for (i = 0; i < f.nups; i++) { + if (f.upvalues[i] == n) return i; + } + return -1; /* not found */ + } + + private int searchvar(LuaString n) { + int i; + for (i = nactvar - 1; i >= 0; i--) { + if (n == getlocvar(i).name) { + return i; + } + } + return -1; /* not found */ + } + + private void markupval(int level) { + BlockCnt bl = this.bl; + while (bl != null && bl.nactvar > level) { + bl = bl.previous; + } + if (bl != null) { + bl.upval = true; + } + } + + int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { + int v = searchvar(n); /* look up at current level */ + if (v >= 0) { + var.init(LexState52.VLOCAL, v); + if (base == 0) { + markupval(v); /* local will be used as an upval */ + } + return LexState52.VLOCAL; + } else { /* not found at current level; try upvalues */ + int idx = searchupvalue(n); + if (idx < 0) { /* not found? */ + if (prev == null) { /* no more levels? */ + return LexState52.VVOID; + } + if (prev.singlevaraux(n, var, 0) == LexState52.VVOID) { + return LexState52.VVOID; + } + var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ + var.k = LexState52.VUPVAL; /* upvalue in this level */ + return LexState52.VUPVAL; + } + var.init(LexState52.VUPVAL, idx); + return LexState52.VUPVAL; + } + } + + void enterblock(BlockCnt bl, boolean isbreakable) throws CompileException { + bl.breaklist.i = LexState52.NO_JUMP; + bl.isbreakable = isbreakable; + bl.nactvar = this.nactvar; + bl.upval = false; + bl.previous = this.bl; + this.bl = bl; + _assert(this.freereg == this.nactvar); + } + + // +// void leaveblock (FuncState *fs) { +// BlockCnt *bl = this.bl; +// this.bl = bl.previous; +// removevars(this.ls, bl.nactvar); +// if (bl.upval) +// this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); +// /* a block either controls scope or breaks (never both) */ +// assert(!bl.isbreakable || !bl.upval); +// assert(bl.nactvar == this.nactvar); +// this.freereg = this.nactvar; /* free registers */ +// this.patchtohere(bl.breaklist); +// } + + void leaveblock() throws CompileException { + BlockCnt bl = this.bl; + this.bl = bl.previous; + ls.removevars(bl.nactvar); + if (bl.upval) { + this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); + } + /* a block either controls scope or breaks (never both) */ + _assert(!bl.isbreakable || !bl.upval); + _assert(bl.nactvar == this.nactvar); + this.freereg = this.nactvar; /* free registers */ + this.patchtohere(bl.breaklist.i); + } + + void closelistfield(ConsControl cc) throws CompileException { + if (cc.v.k == LexState52.VVOID) { + return; /* there is no list item */ + } + this.exp2nextreg(cc.v); + cc.v.k = LexState52.VVOID; + if (cc.tostore == LFIELDS_PER_FLUSH) { + this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */ + cc.tostore = 0; /* no more items pending */ + } + } + + private boolean hasmultret(int k) { + return ((k) == LexState52.VCALL || (k) == LexState52.VVARARG); + } + + void lastlistfield(ConsControl cc) throws CompileException { + if (cc.tostore == 0) return; + if (hasmultret(cc.v.k)) { + this.setmultret(cc.v); + this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET); + cc.na--; /* do not count last expression (unknown number of elements) */ + } else { + if (cc.v.k != LexState52.VVOID) { + this.exp2nextreg(cc.v); + } + this.setlist(cc.t.u.s.info, cc.na, cc.tostore); + } + } + + + // ============================================================= + // from lcode.c + // ============================================================= + + void nil(int from, int n) throws CompileException { + InstructionPtr previous; + if (this.pc > this.lasttarget) { /* no jumps to current position? */ + if (this.pc == 0) { /* function start? */ + if (from >= this.nactvar) { + return; /* positions are already clean */ + } + } else { + previous = new InstructionPtr(this.f.code, this.pc - 1); + if (GET_OPCODE(previous.get()) == OP_LOADNIL) { + int pfrom = GETARG_A(previous.get()); + int pto = GETARG_B(previous.get()); + if (pfrom <= from && from <= pto + 1) { /* can connect both? */ + if (from + n - 1 > pto) { + SETARG_B(previous, from + n - 1); + } + return; + } + } + } + } + /* else no optimization */ + this.codeABC(OP_LOADNIL, from, from + n - 1, 0); + } + + + int jump() throws CompileException { + int jpc = this.jpc.i; /* save list of jumps to here */ + this.jpc.i = LexState52.NO_JUMP; + IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState52.NO_JUMP)); + this.concat(j, jpc); /* keep them on hold */ + return j.i; + } + + void ret(int first, int nret) throws CompileException { + this.codeABC(OP_RETURN, first, nret + 1, 0); + } + + private int condjump(int /* OpCode */op, int A, int B, int C) throws CompileException { + this.codeABC(op, A, B, C); + return this.jump(); + } + + private void fixjump(int pc, int dest) throws CompileException { + InstructionPtr jmp = new InstructionPtr(this.f.code, pc); + int offset = dest - (pc + 1); + _assert(dest != LexState52.NO_JUMP); + if (Math.abs(offset) > MAXARG_sBx) { + throw ls.syntaxError("control structure too long"); + } + SETARG_sBx(jmp, offset); + } + + + /* + * * returns current `pc' and marks it as a jump target (to avoid wrong * + * optimizations with consecutive instructions not in the same basic block). + */ + int getlabel() { + this.lasttarget = this.pc; + return this.pc; + } + + + private int getjump(int pc) { + int offset = GETARG_sBx(this.f.code[pc]); + /* point to itself represents end of list */ + if (offset == LexState52.NO_JUMP) + /* end of list */ { + return LexState52.NO_JUMP; + } else + /* turn offset into absolute position */ { + return (pc + 1) + offset; + } + } + + + private InstructionPtr getjumpcontrol(int pc) { + InstructionPtr pi = new InstructionPtr(this.f.code, pc); + if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1]))) { + return new InstructionPtr(pi.code, pi.idx - 1); + } else { + return pi; + } + } + + + /* + * * check whether list has any jump that do not produce a value * (or + * produce an inverted value) + */ + private boolean need_value(int list) { + for (; list != LexState52.NO_JUMP; list = this.getjump(list)) { + int i = this.getjumpcontrol(list).get(); + if (GET_OPCODE(i) != OP_TESTSET) { + return true; + } + } + return false; /* not found */ + } + + + private boolean patchtestreg(int node, int reg) { + InstructionPtr i = this.getjumpcontrol(node); + if (GET_OPCODE(i.get()) != OP_TESTSET) + /* cannot patch other instructions */ { + return false; + } + if (reg != NO_REG && reg != GETARG_B(i.get())) { + SETARG_A(i, reg); + } else + /* no register to put value or register already has the value */ { + i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get()))); + } + + return true; + } + + + private void removevalues(int list) { + for (; list != LexState52.NO_JUMP; list = this.getjump(list)) { + this.patchtestreg(list, NO_REG); + } + } + + private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws CompileException { + while (list != LexState52.NO_JUMP) { + int next = this.getjump(list); + if (this.patchtestreg(list, reg)) { + this.fixjump(list, vtarget); + } else { + this.fixjump(list, dtarget); /* jump to default target */ + } + list = next; + } + } + + private void dischargejpc() throws CompileException { + this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); + this.jpc.i = LexState52.NO_JUMP; + } + + void patchlist(int list, int target) throws CompileException { + if (target == this.pc) { + this.patchtohere(list); + } else { + _assert(target < this.pc); + this.patchlistaux(list, target, NO_REG, target); + } + } + + void patchtohere(int list) throws CompileException { + this.getlabel(); + this.concat(this.jpc, list); + } + + void concat(IntPtr l1, int l2) throws CompileException { + if (l2 == LexState52.NO_JUMP) { + return; + } + if (l1.i == LexState52.NO_JUMP) { + l1.i = l2; + } else { + int list = l1.i; + int next; + while ((next = this.getjump(list)) != LexState52.NO_JUMP) + /* find last element */ { + list = next; + } + this.fixjump(list, l2); + } + } + + void checkstack(int n) throws CompileException { + int newstack = this.freereg + n; + if (newstack > this.f.maxstacksize) { + if (newstack >= MAXSTACK) { + throw ls.syntaxError("function or expression too complex"); + } + this.f.maxstacksize = newstack; + } + } + + void reserveregs(int n) throws CompileException { + this.checkstack(n); + this.freereg += n; + } + + private void freereg(int reg) throws CompileException { + if (!ISK(reg) && reg >= this.nactvar) { + this.freereg--; + _assert(reg == this.freereg); + } + } + + private void freeexp(expdesc e) throws CompileException { + if (e.k == LexState52.VNONRELOC) { + this.freereg(e.u.s.info); + } + } + + private int addk(LuaValue v) { + int idx; + if (this.htable.containsKey(v)) { + idx = htable.get(v); + } else { + idx = this.nk; + this.htable.put(v, idx); + final Prototype f = this.f; + if (f.k == null || nk + 1 >= f.k.length) { + f.k = realloc(f.k, nk * 2 + 1); + } + f.k[this.nk++] = v; + } + return idx; + } + + int stringK(LuaString s) { + return this.addk(s); + } + + int numberK(LuaValue r) { + if (r instanceof LuaDouble) { + double d = r.toDouble(); + int i = (int) d; + if (d == (double) i) { + r = LuaInteger.valueOf(i); + } + } + return this.addk(r); + } + + private int boolK(boolean b) { + return this.addk((b ? TRUE : FALSE)); + } + + private int nilK() { + return this.addk(NIL); + } + + void setreturns(expdesc e, int nresults) throws CompileException { + if (e.k == LexState52.VCALL) { /* expression is an open function call? */ + SETARG_C(this.getcodePtr(e), nresults + 1); + } else if (e.k == LexState52.VVARARG) { + SETARG_B(this.getcodePtr(e), nresults + 1); + SETARG_A(this.getcodePtr(e), this.freereg); + this.reserveregs(1); + } + } + + void setoneret(expdesc e) { + if (e.k == LexState52.VCALL) { /* expression is an open function call? */ + e.k = LexState52.VNONRELOC; + e.u.s.info = GETARG_A(this.getcode(e)); + } else if (e.k == LexState52.VVARARG) { + SETARG_B(this.getcodePtr(e), 2); + e.k = LexState52.VRELOCABLE; /* can relocate its simple result */ + } + } + + void dischargevars(expdesc e) throws CompileException { + switch (e.k) { + case LexState52.VLOCAL: { + e.k = LexState52.VNONRELOC; + break; + } + case LexState52.VUPVAL: { + e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0); + e.k = LexState52.VRELOCABLE; + break; + } + case LexState52.VGLOBAL: { + e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info); + e.k = LexState52.VRELOCABLE; + break; + } + case LexState52.VINDEXED: { + this.freereg(e.u.s.aux); + this.freereg(e.u.s.info); + e.u.s.info = this + .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux); + e.k = LexState52.VRELOCABLE; + break; + } + case LexState52.VVARARG: + case LexState52.VCALL: { + this.setoneret(e); + break; + } + default: + break; /* there is one value available (somewhere) */ + } + } + + private int code_label(int A, int b, int jump) throws CompileException { + this.getlabel(); /* those instructions may be jump targets */ + return this.codeABC(OP_LOADBOOL, A, b, jump); + } + + private void discharge2reg(expdesc e, int reg) throws CompileException { + this.dischargevars(e); + switch (e.k) { + case LexState52.VNIL: { + this.nil(reg, 1); + break; + } + case LexState52.VFALSE: + case LexState52.VTRUE: { + this.codeABC(OP_LOADBOOL, reg, (e.k == LexState52.VTRUE ? 1 : 0), + 0); + break; + } + case LexState52.VK: { + this.codeABx(OP_LOADK, reg, e.u.s.info); + break; + } + case LexState52.VKNUM: { + this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval())); + break; + } + case LexState52.VRELOCABLE: { + InstructionPtr pc = this.getcodePtr(e); + SETARG_A(pc, reg); + break; + } + case LexState52.VNONRELOC: { + if (reg != e.u.s.info) { + this.codeABC(OP_MOVE, reg, e.u.s.info, 0); + } + break; + } + default: { + _assert(e.k == LexState52.VVOID || e.k == LexState52.VJMP); + return; /* nothing to do... */ + } + } + e.u.s.info = reg; + e.k = LexState52.VNONRELOC; + } + + private void discharge2anyreg(expdesc e) throws CompileException { + if (e.k != LexState52.VNONRELOC) { + this.reserveregs(1); + this.discharge2reg(e, this.freereg - 1); + } + } + + private void exp2reg(expdesc e, int reg) throws CompileException { + this.discharge2reg(e, reg); + if (e.k == LexState52.VJMP) { + this.concat(e.t, e.u.s.info); /* put this jump in `t' list */ + } + if (e.hasjumps()) { + int _final; /* position after whole expression */ + int p_f = LexState52.NO_JUMP; /* position of an eventual LOAD false */ + int p_t = LexState52.NO_JUMP; /* position of an eventual LOAD true */ + if (this.need_value(e.t.i) || this.need_value(e.f.i)) { + int fj = (e.k == LexState52.VJMP) ? LexState52.NO_JUMP : this + .jump(); + p_f = this.code_label(reg, 0, 1); + p_t = this.code_label(reg, 1, 0); + this.patchtohere(fj); + } + _final = this.getlabel(); + this.patchlistaux(e.f.i, _final, reg, p_f); + this.patchlistaux(e.t.i, _final, reg, p_t); + } + e.f.i = e.t.i = LexState52.NO_JUMP; + e.u.s.info = reg; + e.k = LexState52.VNONRELOC; + } + + void exp2nextreg(expdesc e) throws CompileException { + this.dischargevars(e); + this.freeexp(e); + this.reserveregs(1); + this.exp2reg(e, this.freereg - 1); + } + + int exp2anyreg(expdesc e) throws CompileException { + this.dischargevars(e); + if (e.k == LexState52.VNONRELOC) { + if (!e.hasjumps()) { + return e.u.s.info; /* exp is already in a register */ + } + if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */ + this.exp2reg(e, e.u.s.info); /* put value on it */ + return e.u.s.info; + } + } + this.exp2nextreg(e); /* default */ + return e.u.s.info; + } + + void exp2val(expdesc e) throws CompileException { + if (e.hasjumps()) { + this.exp2anyreg(e); + } else { + this.dischargevars(e); + } + } + + int exp2RK(expdesc e) throws CompileException { + this.exp2val(e); + switch (e.k) { + case LexState52.VKNUM: + case LexState52.VTRUE: + case LexState52.VFALSE: + case LexState52.VNIL: { + if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e.u.s.info = (e.k == LexState52.VNIL) ? this.nilK() + : (e.k == LexState52.VKNUM) ? this.numberK(e.u.nval()) + : this.boolK((e.k == LexState52.VTRUE)); + e.k = LexState52.VK; + return RKASK(e.u.s.info); + } else { + break; + } + } + case LexState52.VK: { + if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */ { + return RKASK(e.u.s.info); + } else { + break; + } + } + default: + break; + } + /* not a constant in the right range: put it in a register */ + return this.exp2anyreg(e); + } + + void storevar(expdesc var, expdesc ex) throws CompileException { + switch (var.k) { + case LexState52.VLOCAL: { + this.freeexp(ex); + this.exp2reg(ex, var.u.s.info); + return; + } + case LexState52.VUPVAL: { + int e = this.exp2anyreg(ex); + this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0); + break; + } + case LexState52.VGLOBAL: { + int e = this.exp2anyreg(ex); + this.codeABx(OP_SETGLOBAL, e, var.u.s.info); + break; + } + case LexState52.VINDEXED: { + int e = this.exp2RK(ex); + this.codeABC(OP_SETTABLE, var.u.s.info, var.u.s.aux, e); + break; + } + default: { + _assert(false); /* invalid var kind to store */ + break; + } + } + this.freeexp(ex); + } + + void self(expdesc e, expdesc key) throws CompileException { + int func; + this.exp2anyreg(e); + this.freeexp(e); + func = this.freereg; + this.reserveregs(2); + this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key)); + this.freeexp(key); + e.u.s.info = func; + e.k = LexState52.VNONRELOC; + } + + private void invertjump(expdesc e) throws CompileException { + InstructionPtr pc = this.getjumpcontrol(e.u.s.info); + _assert(testTMode(GET_OPCODE(pc.get())) + && GET_OPCODE(pc.get()) != OP_TESTSET && Lua + .GET_OPCODE(pc.get()) != OP_TEST); + // SETARG_A(pc, !(GETARG_A(pc.get()))); + int a = GETARG_A(pc.get()); + int nota = (a != 0 ? 0 : 1); + SETARG_A(pc, nota); + } + + private int jumponcond(expdesc e, int cond) throws CompileException { + if (e.k == LexState52.VRELOCABLE) { + int ie = this.getcode(e); + if (GET_OPCODE(ie) == OP_NOT) { + this.pc--; /* remove previous OP_NOT */ + return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond != 0 ? 0 : 1)); + } + /* else go through */ + } + this.discharge2anyreg(e); + this.freeexp(e); + return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond); + } + + void goiftrue(expdesc e) throws CompileException { + int pc; /* pc of last jump */ + this.dischargevars(e); + switch (e.k) { + case LexState52.VK: + case LexState52.VKNUM: + case LexState52.VTRUE: { + pc = LexState52.NO_JUMP; /* always true; do nothing */ + break; + } + case LexState52.VFALSE: { + pc = this.jump(); /* always jump */ + break; + } + case LexState52.VJMP: { + this.invertjump(e); + pc = e.u.s.info; + break; + } + default: { + pc = this.jumponcond(e, 0); + break; + } + } + this.concat(e.f, pc); /* insert last jump in `f' list */ + this.patchtohere(e.t.i); + e.t.i = LexState52.NO_JUMP; + } + + private void goiffalse(expdesc e) throws CompileException { + int pc; /* pc of last jump */ + this.dischargevars(e); + switch (e.k) { + case LexState52.VNIL: + case LexState52.VFALSE: { + pc = LexState52.NO_JUMP; /* always false; do nothing */ + break; + } + case LexState52.VTRUE: { + pc = this.jump(); /* always jump */ + break; + } + case LexState52.VJMP: { + pc = e.u.s.info; + break; + } + default: { + pc = this.jumponcond(e, 1); + break; + } + } + this.concat(e.t, pc); /* insert last jump in `t' list */ + this.patchtohere(e.f.i); + e.f.i = LexState52.NO_JUMP; + } + + private void codenot(expdesc e) throws CompileException { + this.dischargevars(e); + switch (e.k) { + case LexState52.VNIL: + case LexState52.VFALSE: { + e.k = LexState52.VTRUE; + break; + } + case LexState52.VK: + case LexState52.VKNUM: + case LexState52.VTRUE: { + e.k = LexState52.VFALSE; + break; + } + case LexState52.VJMP: { + this.invertjump(e); + break; + } + case LexState52.VRELOCABLE: + case LexState52.VNONRELOC: { + this.discharge2anyreg(e); + this.freeexp(e); + e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0); + e.k = LexState52.VRELOCABLE; + break; + } + default: { + _assert(false); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { + int temp = e.f.i; + e.f.i = e.t.i; + e.t.i = temp; + } + this.removevalues(e.f.i); + this.removevalues(e.t.i); + } + + void indexed(expdesc t, expdesc k) throws CompileException { + t.u.s.aux = this.exp2RK(k); + t.k = LexState52.VINDEXED; + } + + private boolean constfolding(int op, expdesc e1, expdesc e2) throws CompileException { + LuaValue v1, v2, r; + if (!e1.isnumeral() || !e2.isnumeral()) { + return false; + } + v1 = e1.u.nval(); + v2 = e2.u.nval(); + try { + switch (op) { + case OP_ADD: + r = OperationHelper.add(null, v1, v2); + break; + case OP_SUB: + r = OperationHelper.sub(null, v1, v2); + break; + case OP_MUL: + r = OperationHelper.mul(null, v1, v2); + break; + case OP_DIV: + r = OperationHelper.div(null, v1, v2); + break; + case OP_MOD: + r = OperationHelper.mod(null, v1, v2); + break; + case OP_POW: + r = OperationHelper.pow(null, v1, v2); + break; + case OP_UNM: + r = OperationHelper.neg(null, v1); + break; + case OP_LEN: + // r = v1.len(); + // break; + return false; /* no constant folding for 'len' */ + default: + _assert(false); + r = null; + break; + } + } catch (UnwindThrowable | LuaError e) { + return false; + } + + if (Double.isNaN(r.toDouble())) { + return false; /* do not attempt to produce NaN */ + } + e1.u.setNval(r); + return true; + } + + private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { + if (constfolding(op, e1, e2)) { + } else { + int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) + : 0; + int o1 = this.exp2RK(e1); + if (o1 > o2) { + this.freeexp(e1); + this.freeexp(e2); + } else { + this.freeexp(e2); + this.freeexp(e1); + } + e1.u.s.info = this.codeABC(op, 0, o1, o2); + e1.k = LexState52.VRELOCABLE; + } + } + + private void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) throws CompileException { + int o1 = this.exp2RK(e1); + int o2 = this.exp2RK(e2); + this.freeexp(e2); + this.freeexp(e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; + o1 = o2; + o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1.u.s.info = this.condjump(op, cond, o1, o2); + e1.k = LexState52.VJMP; + } + + void prefix(int /* UnOpr */op, expdesc e) throws CompileException { + expdesc e2 = new expdesc(); + e2.init(LexState52.VKNUM, 0); + switch (op) { + case LexState52.OPR_MINUS: { + if (e.k == LexState52.VK) { + this.exp2anyreg(e); /* cannot operate on non-numeric constants */ + } + this.codearith(OP_UNM, e, e2); + break; + } + case LexState52.OPR_NOT: + this.codenot(e); + break; + case LexState52.OPR_LEN: { + this.exp2anyreg(e); /* cannot operate on constants */ + this.codearith(OP_LEN, e, e2); + break; + } + default: + _assert(false); + } + } + + void infix(int /* BinOpr */op, expdesc v) throws CompileException { + switch (op) { + case LexState52.OPR_AND: { + this.goiftrue(v); + break; + } + case LexState52.OPR_OR: { + this.goiffalse(v); + break; + } + case LexState52.OPR_CONCAT: { + this.exp2nextreg(v); /* operand must be on the `stack' */ + break; + } + case LexState52.OPR_ADD: + case LexState52.OPR_SUB: + case LexState52.OPR_MUL: + case LexState52.OPR_DIV: + case LexState52.OPR_MOD: + case LexState52.OPR_POW: { + if (!v.isnumeral()) { + this.exp2RK(v); + } + break; + } + default: { + this.exp2RK(v); + break; + } + } + } + + + void posfix(int op, expdesc e1, expdesc e2) throws CompileException { + switch (op) { + case LexState52.OPR_AND: { + _assert(e1.t.i == LexState52.NO_JUMP); /* list must be closed */ + this.dischargevars(e2); + this.concat(e2.f, e1.f.i); + // *e1 = *e2; + e1.setvalue(e2); + break; + } + case LexState52.OPR_OR: { + _assert(e1.f.i == LexState52.NO_JUMP); /* list must be closed */ + this.dischargevars(e2); + this.concat(e2.t, e1.t.i); + // *e1 = *e2; + e1.setvalue(e2); + break; + } + case LexState52.OPR_CONCAT: { + this.exp2val(e2); + if (e2.k == LexState52.VRELOCABLE + && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { + _assert(e1.u.s.info == GETARG_B(this.getcode(e2)) - 1); + this.freeexp(e1); + SETARG_B(this.getcodePtr(e2), e1.u.s.info); + e1.k = LexState52.VRELOCABLE; + e1.u.s.info = e2.u.s.info; + } else { + this.exp2nextreg(e2); /* operand must be on the 'stack' */ + this.codearith(OP_CONCAT, e1, e2); + } + break; + } + case LexState52.OPR_ADD: + this.codearith(OP_ADD, e1, e2); + break; + case LexState52.OPR_SUB: + this.codearith(OP_SUB, e1, e2); + break; + case LexState52.OPR_MUL: + this.codearith(OP_MUL, e1, e2); + break; + case LexState52.OPR_DIV: + this.codearith(OP_DIV, e1, e2); + break; + case LexState52.OPR_MOD: + this.codearith(OP_MOD, e1, e2); + break; + case LexState52.OPR_POW: + this.codearith(OP_POW, e1, e2); + break; + case LexState52.OPR_EQ: + this.codecomp(OP_EQ, 1, e1, e2); + break; + case LexState52.OPR_NE: + this.codecomp(OP_EQ, 0, e1, e2); + break; + case LexState52.OPR_LT: + this.codecomp(OP_LT, 1, e1, e2); + break; + case LexState52.OPR_LE: + this.codecomp(OP_LE, 1, e1, e2); + break; + case LexState52.OPR_GT: + this.codecomp(OP_LT, 0, e1, e2); + break; + case LexState52.OPR_GE: + this.codecomp(OP_LE, 0, e1, e2); + break; + default: + _assert(false); + } + } + + + void fixline(int line) { + this.f.lineinfo[this.pc - 1] = line; + } + + + private int code(int instruction, int line) throws CompileException { + Prototype f = this.f; + this.dischargejpc(); /* `pc' will change */ + /* put new instruction in code array */ + if (f.code == null || this.pc + 1 > f.code.length) { + f.code = LuaC.realloc(f.code, this.pc * 2 + 1); + } + f.code[this.pc] = instruction; + /* save corresponding line information */ + if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length) { + f.lineinfo = LuaC.realloc(f.lineinfo, + this.pc * 2 + 1); + } + f.lineinfo[this.pc] = line; + return this.pc++; + } + + + int codeABC(int o, int a, int b, int c) throws CompileException { + _assert(getOpMode(o) == iABC); + _assert(getBMode(o) != OpArgN || b == 0); + _assert(getCMode(o) != OpArgN || c == 0); + return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline); + } + + + int codeABx(int o, int a, int bc) throws CompileException { + _assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + _assert(getCMode(o) == OpArgN); + return this.code(CREATE_ABx(o, a, bc), this.ls.lastline); + } + + + private void setlist(int base, int nelems, int tostore) throws CompileException { + int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + _assert(tostore != 0); + if (c <= MAXARG_C) { + this.codeABC(OP_SETLIST, base, b, c); + } else { + this.codeABC(OP_SETLIST, base, b, 0); + this.code(c, this.ls.lastline); + } + this.freereg = base + 1; /* free registers with list values */ + } +} diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java new file mode 100644 index 00000000..71c4977e --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -0,0 +1,2043 @@ +/* + * The MIT License (MIT) + * + * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. + * Modifications: Copyright (c) 2015-2020 SquidDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.squiddev.cobalt.compiler; + +import org.squiddev.cobalt.*; +import org.squiddev.cobalt.function.LocalVariable; +import org.squiddev.cobalt.lib.Utf8Lib; +import org.squiddev.cobalt.compiler.FuncState52; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Hashtable; + +public class LexState52 { + + private static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)"; + private static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)"; + private static final String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)"; + private static final String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)"; + private static final String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)"; + private static final String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)"; + + // keywords array + private static final String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[]{ + RESERVED_LOCAL_VAR_FOR_CONTROL, + RESERVED_LOCAL_VAR_FOR_GENERATOR, + RESERVED_LOCAL_VAR_FOR_INDEX, + RESERVED_LOCAL_VAR_FOR_LIMIT, + RESERVED_LOCAL_VAR_FOR_STATE, + RESERVED_LOCAL_VAR_FOR_STEP + }; + private static final Hashtable RESERVED_LOCAL_VAR_KEYWORDS_TABLE = new Hashtable<>(); + + static { + for (String RESERVED_LOCAL_VAR_KEYWORD : RESERVED_LOCAL_VAR_KEYWORDS) { + RESERVED_LOCAL_VAR_KEYWORDS_TABLE.put(RESERVED_LOCAL_VAR_KEYWORD, Boolean.TRUE); + } + } + + private static final int EOZ = -1; + private static final int MAXSRC = 80; + private static final int MAX_INT = Integer.MAX_VALUE - 2; + private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX? + private static final int LUAI_MAXCCALLS = 200; + + private static String LUA_QS(String s) { + return "'" + s + "'"; + } + + private static String LUA_QL(Object o) { + return LUA_QS(String.valueOf(o)); + } + + private static final int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior + private static final boolean LUA_COMPAT_VARARG = true; + + public static boolean isReservedKeyword(String varName) { + return RESERVED_LOCAL_VAR_KEYWORDS_TABLE.containsKey(varName); + } + + /* + ** Marks the end of a patch list. It is an invalid value both as an absolute + ** address, and as a list link (would link an element to itself). + */ + static final int NO_JUMP = -1; + + /* + ** grep "ORDER OPR" if you change these enums + */ + static final int + OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5, + OPR_CONCAT = 6, + OPR_NE = 7, OPR_EQ = 8, + OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12, + OPR_AND = 13, OPR_OR = 14, + OPR_NOBINOPR = 15; + + static final int + OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3; + + /* exp kind */ + static final int + VVOID = 0, /* no value */ + VNIL = 1, + VTRUE = 2, + VFALSE = 3, + VK = 4, /* info = index of constant in `k' */ + VKNUM = 5, /* nval = numerical value */ + VLOCAL = 6, /* info = local register */ + VUPVAL = 7, /* info = index of upvalue in `upvalues' */ + VGLOBAL = 8, /* info = index of table, aux = index of global name in `k' */ + VINDEXED = 9, /* info = table register, aux = index register (or `k') */ + VJMP = 10, /* info = instruction pc */ + VRELOCABLE = 11, /* info = instruction pc */ + VNONRELOC = 12, /* info = result register */ + VCALL = 13, /* info = instruction pc */ + VVARARG = 14; /* info = instruction pc */ + + /* semantics information */ + private static class SemInfo { + LuaValue r; + LuaString ts; + } + + private static class Token { + int token; + final SemInfo seminfo = new SemInfo(); + + public void set(Token other) { + this.token = other.token; + this.seminfo.r = other.seminfo.r; + this.seminfo.ts = other.seminfo.ts; + } + } + + private int current; /* current character (charint) */ + private int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + private final Token t = new Token(); /* current token */ + private final Token lookahead = new Token(); /* look ahead token */ + FuncState52 fs; /* `FuncState52' is private to the parser */ + private InputStream z; /* input stream */ + private byte[] buff; /* buffer for tokens */ + private int nbuff; /* length of buffer */ + private LuaString source; /* current source name */ + private byte decpoint; /* locale decimal point */ + public int nCcalls; + private final HashMap strings = new HashMap<>(); + + /* ORDER RESERVED */ + private final static String[] luaX_tokens = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", + "", + "", "", "", + }; + + final static int + // terminal symbols denoted by reserved words + TK_AND = 257, TK_BREAK = 258, TK_DO = 259, TK_ELSE = 260, TK_ELSEIF = 261, + TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_IF = 266, + TK_IN = 267, TK_LOCAL = 268, TK_NIL = 269, TK_NOT = 270, TK_OR = 271, TK_REPEAT = 272, + TK_RETURN = 273, TK_THEN = 274, TK_TRUE = 275, TK_UNTIL = 276, TK_WHILE = 277, + // other terminal symbols + TK_CONCAT = 278, TK_DOTS = 279, TK_EQ = 280, TK_GE = 281, TK_LE = 282, TK_NE = 283, + TK_EOS = 284, + TK_NUMBER = 285, TK_NAME = 286, TK_STRING = 287; + + private final static int FIRST_RESERVED = TK_AND; + private final static int NUM_RESERVED = TK_WHILE + 1 - FIRST_RESERVED; + + private final static Hashtable RESERVED = new Hashtable<>(); + + static { + for (int i = 0; i < NUM_RESERVED; i++) { + LuaString ts = ValueFactory.valueOf(luaX_tokens[i]); + RESERVED.put(ts, FIRST_RESERVED + i); + } + } + + private static boolean isAlphaNum(int c) { + return c >= '0' && c <= '9' + || c >= 'a' && c <= 'z' + || c >= 'A' && c <= 'Z' + || c == '_'; + } + + private static boolean isAlpha(int c) { + return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; + } + + private static boolean isDigit(int c) { + return c >= '0' && c <= '9'; + } + + private static boolean isSpace(int c) { + return c <= ' '; + } + + private static boolean isHex(int c) { + return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'; + } + + public LexState52(InputStream stream) { + this.z = stream; + this.buff = new byte[32]; + } + + private void nextChar() { + try { + current = z.read(); + } catch (IOException e) { + e.printStackTrace(); + current = EOZ; + } + } + + private boolean currIsNewline() { + return current == '\n' || current == '\r'; + } + + private void save_and_next() { + save(current); + nextChar(); + } + + private void save(int c) { + if (buff == null || nbuff + 1 > buff.length) { + buff = LuaC.realloc(buff, nbuff * 2 + 1); + } + buff[nbuff++] = (byte) c; + } + + + private String token2str(int token) { + if (token < FIRST_RESERVED) { + return LUA_QS(iscntrl(token) ? "char(" + token + ")" : String.valueOf((char) token)); + } else { + String name = luaX_tokens[token - FIRST_RESERVED]; + return token < TK_EOS ? LUA_QS(name) : name; + } + } + + private static boolean iscntrl(int token) { + return token < ' '; + } + + private String txtToken(int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + return LUA_QS(new String(buff, 0, nbuff)); + default: + return token2str(token); + } + } + + CompileException lexError(String msg, int token) { + String cid = chunkId(source.toString()); // TODO: get source name from source + String message = cid + ":" + linenumber + ": " + msg; + if (token != 0) message += " near " + txtToken(token); + return new CompileException(message); + } + + String chunkId(String source) { + if (source.startsWith("=")) { + return source.substring(1); + } + String end = ""; + if (source.startsWith("@")) { + source = source.substring(1); + } else { + source = "[string \"" + source; + end = "\"]"; + } + int n = source.length() + end.length(); + if (n > MAXSRC) { + source = source.substring(0, MAXSRC - end.length() - 3) + "..."; + } + return source + end; + } + + CompileException syntaxError(String msg) { + return lexError(msg, t.token); + } + + // look up and keep at most one copy of each string + public LuaString newString(byte[] bytes, int offset, int len) { + LuaString tmp = LuaString.valueOf(bytes, offset, len); + LuaString v = strings.get(tmp); + if (v == null) { + // must copy bytes, since bytes could be from reusable buffer + byte[] copy = new byte[len]; + System.arraycopy(bytes, offset, copy, 0, len); + v = LuaString.valueOf(copy); + strings.put(v, v); + } + return v; + } + + // only called by new_localvarliteral() for var names. + private LuaString newString(String s) { + byte[] b = s.getBytes(); + return newString(b, 0, b.length); + } + + private void inclineNumber() throws CompileException { + int old = current; + LuaC._assert(currIsNewline()); + nextChar(); /* skip '\n' or '\r' */ + if (currIsNewline() && current != old) { + nextChar(); /* skip '\n\r' or '\r\n' */ + } + if (++linenumber >= MAX_INT) { + throw syntaxError("chunk has too many lines"); + } + } + + void setinput(int firstByte, InputStream z, LuaString source) { + this.decpoint = '.'; + this.lookahead.token = TK_EOS; /* no look-ahead token */ + this.z = z; + this.fs = null; + this.linenumber = 1; + this.lastline = 1; + this.source = source; + this.nbuff = 0; /* initialize buffer */ + this.current = firstByte; /* read first char */ + this.skipShebang(); + } + + private void skipShebang() { + if (current == '#') { + while (!currIsNewline() && current != EOZ) { + nextChar(); + } + } + } + + + + /* + ** ======================================================= + ** LEXICAL ANALYZER + ** ======================================================= + */ + + + private boolean check_next(String set) { + if (set.indexOf(current) < 0) { + return false; + } + save_and_next(); + return true; + } + + private void buffreplace(byte from, byte to) { + int n = nbuff; + byte[] p = buff; + while (--n >= 0) { + if (p[n] == from) { + p[n] = to; + } + } + } + + private void str2d(String str, SemInfo seminfo) throws CompileException { + // If we're a hex string, try to parse as a long. Java's handling of hex floats + // is much more limited than C's version. + if (str.startsWith("0x") || str.startsWith("0X")) { + try { + seminfo.r = ValueFactory.valueOf(Long.valueOf(str.substring(2), 16)); + return; + } catch (NumberFormatException ignored) { + } + } + + try { + seminfo.r = ValueFactory.valueOf(Double.parseDouble(str)); + return; + } catch (NumberFormatException ignored) { + } + + throw lexError("malformed number", TK_NUMBER); + } + + private void read_numeral(SemInfo seminfo) throws CompileException { + LuaC._assert(isDigit(current)); + + int first = current; + save_and_next(); + String exp = "Ee"; + if (first == '0' && check_next("xX")) exp = "Pp"; + + while (true) { + // Exponential and sign + if (check_next(exp)) check_next("+-"); + + if (isHex(current) || current == '.') { + save_and_next(); + } else { + break; + } + } + + String str = new String(buff, 0, nbuff); + str2d(str, seminfo); + } + + private int skip_sep() throws CompileException { + int count = 0; + int s = current; + LuaC._assert(s == '[' || s == ']'); + save_and_next(); + while (current == '=') { + save_and_next(); + count++; + } + return current == s ? count : -count - 1; + } + + private void read_long_string(SemInfo seminfo, int sep) throws CompileException { + int cont = 0; + save_and_next(); /* skip 2nd `[' */ + if (currIsNewline()) /* string starts with a newline? */ { + inclineNumber(); /* skip it */ + } + for (boolean endloop = false; !endloop; ) { + switch (current) { + case EOZ: + String msg = seminfo != null ? "unfinished long string" + : "unfinished long comment"; + throw lexError(msg, TK_EOS); + case '[': { + if (skip_sep() == sep) { + save_and_next(); /* skip 2nd `[' */ + cont++; + if (LUA_COMPAT_LSTR == 1) { + if (sep == 0) { + throw lexError("nesting of [[...]] is deprecated", (int) '['); + } + } + } + break; + } + case ']': { + if (skip_sep() == sep) { + save_and_next(); /* skip 2nd `]' */ + if (LUA_COMPAT_LSTR == 2) { + cont--; + if (sep == 0 && cont >= 0) { + break; + } + } + endloop = true; + } + break; + } + case '\n': + case '\r': { + save('\n'); + inclineNumber(); + if (seminfo == null) { + nbuff = 0; /* avoid wasting space */ + } + break; + } + default: { + if (seminfo != null) { + save_and_next(); + } else { + nextChar(); + } + } + } + } + if (seminfo != null) { + seminfo.ts = newString(buff, 2 + sep, nbuff - 2 * (2 + sep)); + } + } + + private void read_string(int del, SemInfo seminfo) throws CompileException { + save_and_next(); + while (current != del) { + switch (current) { + case EOZ: + throw lexError("unfinished string", TK_EOS); + case '\n': + case '\r': + throw lexError("unfinished string", TK_STRING); + case '\\': { + save_and_next(); /* do not save the `\' */ + switch (current) { + case 'a': /* bell */ + saveEscape('\u0007'); + break; + case 'b': /* backspace */ + saveEscape('\b'); + break; + case 'f': /* form feed */ + saveEscape('\f'); + break; + case 'n': /* newline */ + saveEscape('\n'); + break; + case 'r': /* carriage return */ + saveEscape('\r'); + break; + case 't': /* tab */ + saveEscape('\t'); + break; + case 'v': /* vertical tab */ + saveEscape('\u000B'); + break; + case 'x': + saveEscape(readHexEsc()); + break; + case 'u': + readUtf8Esc(); + break; + case '\n': /* go through */ + case '\r': + nbuff--; + save('\n'); + inclineNumber(); + break; + case EOZ: + break; /* will raise an error next loop */ + case 'z': { // "zap" following span of spaces + nbuff--; + nextChar(); // Skip z and remove '\\' + while (isSpace(current)) { + if (currIsNewline()) inclineNumber(); + nextChar(); + } + break; + } + default: { + if (!isDigit(current)) { + nbuff--; + save_and_next(); /* handles \\, \", \', and \? */ + } else { /* \xxx */ + int c = readDecEsc(); + nbuff--; + save(c); + } + break; + } + } + break; + } + default: + save_and_next(); + } + } + save_and_next(); /* skip delimiter */ + seminfo.ts = newString(buff, 1, nbuff - 2); + } + + private void saveEscape(int character) { + nextChar(); + nbuff--; + save(character); + } + + private int readHexEsc() throws CompileException { + int r = (readHex() << 4) | readHex(); + nbuff -= 2; + return r; + } + + private int readDecEsc() throws CompileException { + int i = 0; + int result = 0; + for (; i < 3 && isDigit(current); i++) { + result = 10 * result + current - '0'; + save_and_next(); + } + + if (result > UCHAR_MAX) throw escapeError("escape sequence too large"); + nbuff -= i; + + return result; + } + + private void readUtf8Esc() throws CompileException { + save_and_next(); + if (current != '{') throw escapeError("mising '{'"); + + int i = 4; + long codepoint = readHex(); + while (true) { + save_and_next(); + if (!isHex(current)) break; + + i++; + codepoint = (codepoint << 4) | hexValue(current); + if (codepoint > Utf8Lib.MAX_UNICODE) throw escapeError("UTF-8 value too large"); + } + if (current != '}') throw escapeError("missing '}'"); + nextChar(); + + nbuff -= i; + if (codepoint < 0x80) { + save((int) codepoint); + } else { + byte[] buffer = new byte[8]; + int j = Utf8Lib.buildCharacter(buffer, codepoint); + for (; j > 0; j--) save(buffer[8 - j]); + } + } + + private int readHex() throws CompileException { + save_and_next(); + if (!isHex(current)) throw escapeError("hexadecimal digit expected"); + return hexValue(current); + } + + private static int hexValue(int c) { + // Terrible bit twiddling right here: + // 'A'..'F' corresponds to 0x41..0x46, and 'a'..'f' to 0x61..0x66. So bitwise and with 0xf + // gives us the last digit, +9 to map from 1..6 to 10..15. + return c <= '9' ? c - '0' : (c & 0xf) + 9; + } + + private CompileException escapeError(String message) { + if (current != EOZ) save_and_next(); + return lexError(message, TK_STRING); + } + + private int llex(SemInfo seminfo) throws CompileException { + nbuff = 0; + while (true) { + switch (current) { + case '\n': + case '\r': { + inclineNumber(); + continue; + } + case '-': { + nextChar(); + if (current != '-') { + return '-'; + } + /* else is a comment */ + nextChar(); + if (current == '[') { + int sep = skip_sep(); + nbuff = 0; /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(null, sep); /* long comment */ + nbuff = 0; + continue; + } + } + /* else short comment */ + while (!currIsNewline() && current != EOZ) { + nextChar(); + } + continue; + } + case '[': { + int sep = skip_sep(); + if (sep >= 0) { + read_long_string(seminfo, sep); + return TK_STRING; + } else if (sep == -1) { + return '['; + } else { + throw lexError("invalid long string delimiter", TK_STRING); + } + } + case '=': { + nextChar(); + if (current != '=') { + return '='; + } else { + nextChar(); + return TK_EQ; + } + } + case '<': { + nextChar(); + if (current != '=') { + return '<'; + } else { + nextChar(); + return TK_LE; + } + } + case '>': { + nextChar(); + if (current != '=') { + return '>'; + } else { + nextChar(); + return TK_GE; + } + } + case '~': { + nextChar(); + if (current != '=') { + return '~'; + } else { + nextChar(); + return TK_NE; + } + } + case '"': + case '\'': { + read_string(current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(); + if (check_next(".")) { + if (check_next(".")) { + return TK_DOTS; /* ... */ + } else { + return TK_CONCAT; /* .. */ + } + } else if (!isDigit(current)) { + return '.'; + } else { + read_numeral(seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isSpace(current)) { + LuaC._assert(!currIsNewline()); + nextChar(); + } else if (isDigit(current)) { + read_numeral(seminfo); + return TK_NUMBER; + } else if (isAlpha(current) || current == '_') { + /* identifier or reserved word */ + LuaString ts; + do { + save_and_next(); + } while (isAlphaNum(current) || current == '_'); + ts = newString(buff, 0, nbuff); + if (RESERVED.containsKey(ts)) { + return RESERVED.get(ts); + } else { + seminfo.ts = ts; + return TK_NAME; + } + } else { + int c = current; + nextChar(); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } + } + + void nextToken() throws CompileException { + lastline = linenumber; + if (lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + t.set(lookahead); /* use this one */ + lookahead.token = TK_EOS; /* and discharge it */ + } else { + t.token = llex(t.seminfo); /* read next token */ + } + } + + private void lookahead() throws CompileException { + LuaC._assert(lookahead.token == TK_EOS); + lookahead.token = llex(lookahead.seminfo); + } + + // ============================================================= + // from lcode.h + // ============================================================= + + + // ============================================================= + // from lparser.c + // ============================================================= + + static class expdesc { + int k; // expkind, from enumerated list, above + + static class U { // originally a union + static class S { + int info, aux; + } + + final S s = new S(); + private LuaValue _nval; + + public void setNval(LuaValue r) { + _nval = r; + } + + public LuaValue nval() { + return _nval == null ? LuaInteger.valueOf(s.info) : _nval; + } + } + + final U u = new U(); + final IntPtr t = new IntPtr(); /* patch list of `exit when true' */ + final IntPtr f = new IntPtr(); /* patch list of `exit when false' */ + + void init(int k, int i) { + this.f.i = NO_JUMP; + this.t.i = NO_JUMP; + this.k = k; + this.u.s.info = i; + } + + boolean hasjumps() { + return t.i != f.i; + } + + boolean isnumeral() { + return k == VKNUM && t.i == NO_JUMP && f.i == NO_JUMP; + } + + public void setvalue(expdesc other) { + this.k = other.k; + this.u._nval = other.u._nval; + this.u.s.info = other.u.s.info; + this.u.s.aux = other.u.s.aux; + this.t.i = other.t.i; + this.f.i = other.f.i; + } + } + + private boolean hasmultret(int k) { + return k == VCALL || k == VVARARG; + } + + /*---------------------------------------------------------------------- + name args description + ------------------------------------------------------------------------*/ + + /* + * * prototypes for recursive non-terminal functions + */ + + private void error_expected(int token) throws CompileException { + throw syntaxError(token2str(token) + " expected"); + } + + private boolean testnext(int c) throws CompileException { + if (t.token == c) { + nextToken(); + return true; + } else { + return false; + } + } + + void check(int c) throws CompileException { + if (t.token != c) { + error_expected(c); + } + } + + private void checknext(int c) throws CompileException { + check(c); + nextToken(); + } + + private void check_condition(boolean c, String msg) throws CompileException { + if (!c) { + throw syntaxError(msg); + } + } + + + private void check_match(int what, int who, int where) throws CompileException { + if (!testnext(what)) { + if (where == linenumber) { + error_expected(what); + } else { + throw syntaxError(LUA_QS(token2str(what)) + + " expected " + "(to close " + LUA_QS(token2str(who)) + + " at line " + where + ")"); + } + } + } + + private LuaString str_checkname() throws CompileException { + LuaString ts; + check(TK_NAME); + ts = t.seminfo.ts; + nextToken(); + return ts; + } + + private void codestring(expdesc e, LuaString s) { + e.init(VK, fs.stringK(s)); + } + + private void checkname(expdesc e) throws CompileException { + codestring(e, str_checkname()); + } + + + private int registerlocalvar(LuaString varname) { + FuncState52 fs = this.fs; + Prototype f = fs.f; + if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length) { + f.locvars = LuaC.realloc(f.locvars, fs.nlocvars * 2 + 1); + } + f.locvars[fs.nlocvars] = new LocalVariable(varname, 0, 0); + return fs.nlocvars++; + } + + + // +// #define new_localvarliteral(ls,v,n) \ +// this.new_localvar(luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) +// + private void new_localvarliteral(String v, int n) throws CompileException { + LuaString ts = newString(v); + new_localvar(ts, n); + } + + private void new_localvar(LuaString name, int n) throws CompileException { + FuncState52 fs = this.fs; + fs.checklimit(fs.nactvar + n + 1, LuaC.LUAI_MAXVARS, "local variables"); + fs.actvar[fs.nactvar + n] = (short) registerlocalvar(name); + } + + private void adjustlocalvars(int nvars) { + FuncState52 fs = this.fs; + fs.nactvar = (short) (fs.nactvar + nvars); + for (; nvars > 0; nvars--) { + fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc; + } + } + + void removevars(int tolevel) { + FuncState52 fs = this.fs; + while (fs.nactvar > tolevel) { + fs.getlocvar(--fs.nactvar).endpc = fs.pc; + } + } + + private void singlevar(expdesc var) throws CompileException { + LuaString varname = str_checkname(); + FuncState52 fs = this.fs; + if (fs.singlevaraux(varname, var, 1) == VGLOBAL) { + expdesc key = new expdesc(); + fs.singlevaraux(LuaString.valueOf("_ENV"), var, 1); + codestring(key, varname); + // todo + } + } + + private void adjust_assign(int nvars, int nexps, expdesc e) throws CompileException { + FuncState52 fs = this.fs; + int extra = nvars - nexps; + if (hasmultret(e.k)) { + /* includes call itself */ + extra++; + if (extra < 0) { + extra = 0; + } + /* last exp. provides the difference */ + fs.setreturns(e, extra); + if (extra > 1) { + fs.reserveregs(extra - 1); + } + } else { + /* close last expression */ + if (e.k != VVOID) { + fs.exp2nextreg(e); + } + if (extra > 0) { + int reg = fs.freereg; + fs.reserveregs(extra); + fs.nil(reg, extra); + } + } + } + + private void enterlevel() throws CompileException { + if (++nCcalls > LUAI_MAXCCALLS) { + throw lexError("chunk has too many syntax levels", 0); + } + } + + private void leavelevel() { + nCcalls--; + } + + private void pushclosure(FuncState52 func, expdesc v) throws CompileException { + FuncState52 fs = this.fs; + Prototype f = fs.f; + if (f.p == null || fs.np + 1 > f.p.length) { + f.p = LuaC.realloc(f.p, fs.np * 2 + 1); + } + f.p[fs.np++] = func.f; + v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1)); + for (int i = 0; i < func.f.nups; i++) { + int o = func.upvalues[i].k == VLOCAL ? Lua.OP_MOVE + : Lua.OP_GETUPVAL; + fs.codeABC(o, 0, func.upvalues[i].info, 0); + } + } + + void open_func(FuncState52 fs) { + Prototype f = new Prototype(); + if (this.fs != null) { + f.source = this.fs.f.source; + } + f.isLua52 = false; // temporary! + fs.f = f; + fs.prev = this.fs; /* linked list of FuncState52s */ + fs.ls = this; + this.fs = fs; + fs.pc = 0; + fs.lasttarget = -1; + fs.jpc = new IntPtr(NO_JUMP); + fs.freereg = 0; + fs.nk = 0; + fs.np = 0; + fs.nlocvars = 0; + fs.nactvar = 0; + fs.bl = null; + f.maxstacksize = 2; /* registers 0/1 are always valid */ + //fs.h = new LTable(); + fs.htable = new Hashtable<>(); + } + + void close_func() throws CompileException { + FuncState52 fs = this.fs; + Prototype f = fs.f; + this.removevars(0); + fs.ret(0, 0); /* final return */ + f.code = LuaC.realloc(f.code, fs.pc); + f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); + // f.sizelineinfo = fs.pc; + f.k = LuaC.realloc(f.k, fs.nk); + f.p = LuaC.realloc(f.p, fs.np); + f.locvars = LuaC.realloc(f.locvars, fs.nlocvars); + // f.sizelocvars = fs.nlocvars; + f.upvalues = LuaC.realloc(f.upvalues, f.nups); + // LuaC._assert (CheckCode.checkcode(f)); + LuaC._assert(fs.bl == null); + this.fs = fs.prev; +// L.top -= 2; /* remove table and prototype from the stack */ + // /* last token read was anchored in defunct function; must reanchor it + // */ + // if (fs!=null) ls.anchor_token(); + } + + /*============================================================*/ + /* GRAMMAR RULES */ + /*============================================================*/ + + private void field(expdesc v) throws CompileException { + /* field -> ['.' | ':'] NAME */ + FuncState52 fs = this.fs; + expdesc key = new expdesc(); + fs.exp2anyreg(v); + this.nextToken(); /* skip the dot or colon */ + this.checkname(key); + fs.indexed(v, key); + } + + private void yindex(expdesc v) throws CompileException { + /* index -> '[' expr ']' */ + this.nextToken(); /* skip the '[' */ + this.expr(v); + this.fs.exp2val(v); + this.checknext(']'); + } + + + /* + ** {====================================================================== + ** Rules for Constructors + ** ======================================================================= + */ + + + static class ConsControl { + expdesc v = new expdesc(); /* last list item read */ + expdesc t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ + } + + + private void recfield(ConsControl cc) throws CompileException { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState52 fs = this.fs; + int reg = this.fs.freereg; + expdesc key = new expdesc(); + expdesc val = new expdesc(); + int rkkey; + if (this.t.token == TK_NAME) { + fs.checklimit(cc.nh, MAX_INT, "items in a constructor"); + this.checkname(key); + } else + /* this.t.token == '[' */ { + this.yindex(key); + } + cc.nh++; + this.checknext('='); + rkkey = fs.exp2RK(key); + this.expr(val); + fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val)); + fs.freereg = reg; /* free registers */ + } + + private void listfield(ConsControl cc) throws CompileException { + this.expr(cc.v); + fs.checklimit(cc.na, MAX_INT, "items in a constructor"); + cc.na++; + cc.tostore++; + } + + + private void constructor(expdesc t) throws CompileException { + /* constructor -> ?? */ + FuncState52 fs = this.fs; + int line = this.linenumber; + int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0); + ConsControl cc = new ConsControl(); + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + t.init(VRELOCABLE, pc); + cc.v.init(VVOID, 0); /* no value (yet) */ + fs.exp2nextreg(t); /* fix it at stack top (for gc) */ + this.checknext('{'); + do { + LuaC._assert(cc.v.k == VVOID || cc.tostore > 0); + if (this.t.token == '}') { + break; + } + fs.closelistfield(cc); + switch (this.t.token) { + case TK_NAME: { /* may be listfields or recfields */ + this.lookahead(); + if (this.lookahead.token != '=') /* expression? */ { + this.listfield(cc); + } else { + this.recfield(cc); + } + break; + } + case '[': { /* constructor_item -> recfield */ + this.recfield(cc); + break; + } + default: { /* constructor_part -> listfield */ + this.listfield(cc); + break; + } + } + } while (this.testnext(',') || this.testnext(';')); + this.check_match('}', '{', line); + fs.lastlistfield(cc); + InstructionPtr i = new InstructionPtr(fs.f.code, pc); + LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */ + LuaC.SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */ + } + + /* + ** converts an integer to a "floating point byte", represented as + ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if + ** eeeee != 0 and (xxx) otherwise. + */ + private static int luaO_int2fb(int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = x + 1 >> 1; + e++; + } + if (x < 8) { + return x; + } else { + return e + 1 << 3 | x - 8; + } + } + + + /* }====================================================================== */ + + private void parlist() throws CompileException { + /* parlist -> [ param { `,' param } ] */ + FuncState52 fs = this.fs; + Prototype f = fs.f; + int nparams = 0; + f.is_vararg = 0; + if (this.t.token != ')') { /* is `parlist' not empty? */ + do { + switch (this.t.token) { + case TK_NAME: { /* param . NAME */ + this.new_localvar(this.str_checkname(), nparams++); + break; + } + case TK_DOTS: { /* param . `...' */ + this.nextToken(); + if (LUA_COMPAT_VARARG) { + /* use `arg' as default name */ + this.new_localvarliteral("arg", nparams++); + f.is_vararg = Lua.VARARG_HASARG | Lua.VARARG_NEEDSARG; + } + f.is_vararg |= Lua.VARARG_ISVARARG; + break; + } + default: + throw syntaxError(" or " + LUA_QL("...") + " expected"); + } + } while (f.is_vararg == 0 && this.testnext(',')); + } + this.adjustlocalvars(nparams); + f.numparams = fs.nactvar - (f.is_vararg & Lua.VARARG_HASARG); + fs.reserveregs(fs.nactvar); /* reserve register for parameters */ + } + + + private void body(expdesc e, boolean needself, int line) throws CompileException { + /* body -> `(' parlist `)' chunk END */ + FuncState52 new_fs = new FuncState52(); + open_func(new_fs); + new_fs.f.linedefined = line; + this.checknext('('); + if (needself) { + new_localvarliteral("self", 0); + adjustlocalvars(1); + } + this.parlist(); + this.checknext(')'); + this.chunk(); + new_fs.f.lastlinedefined = this.linenumber; + this.check_match(TK_END, TK_FUNCTION, line); + this.close_func(); + this.pushclosure(new_fs, e); + } + + private int explist1(expdesc v) throws CompileException { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + this.expr(v); + while (this.testnext(',')) { + fs.exp2nextreg(v); + this.expr(v); + n++; + } + return n; + } + + + private void funcargs(expdesc f) throws CompileException { + FuncState52 fs = this.fs; + expdesc args = new expdesc(); + int base, nparams; + int line = this.linenumber; + switch (this.t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != this.lastline) { + throw syntaxError("ambiguous syntax (function call x new statement)"); + } + this.nextToken(); + if (this.t.token == ')') /* arg list is empty? */ { + args.k = VVOID; + } else { + this.explist1(args); + fs.setmultret(args); + } + this.check_match(')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + this.constructor(args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + this.codestring(args, this.t.seminfo.ts); + this.nextToken(); /* must use `seminfo' before `next' */ + break; + } + default: { + throw syntaxError("function arguments expected"); + } + } + LuaC._assert(f.k == VNONRELOC); + base = f.u.s.info; /* base register for call */ + if (hasmultret(args.k)) { + nparams = Lua.LUA_MULTRET; /* open call */ + } else { + if (args.k != VVOID) { + fs.exp2nextreg(args); /* close last argument */ + } + nparams = fs.freereg - (base + 1); + } + f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2)); + fs.fixline(line); + fs.freereg = base + 1; /* call remove function and arguments and leaves + * (unless changed) one result */ + } + + + /* + ** {====================================================================== + ** Expression parsing + ** ======================================================================= + */ + + private void prefixexp(expdesc v) throws CompileException { + /* prefixexp -> NAME | '(' expr ')' */ + switch (this.t.token) { + case '(': { + int line = this.linenumber; + this.nextToken(); + this.expr(v); + this.check_match(')', '(', line); + fs.dischargevars(v); + return; + } + case TK_NAME: { + this.singlevar(v); + return; + } + default: { + throw syntaxError("unexpected symbol"); + } + } + } + + + private void primaryexp(expdesc v) throws CompileException { + /* + * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | + * funcargs } + */ + FuncState52 fs = this.fs; + this.prefixexp(v); + for (; ; ) { + switch (this.t.token) { + case '.': { /* field */ + this.field(v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key = new expdesc(); + fs.exp2anyreg(v); + this.yindex(key); + fs.indexed(v, key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key = new expdesc(); + this.nextToken(); + this.checkname(key); + fs.self(v, key); + this.funcargs(v); + break; + } + case '(': + case TK_STRING: + case '{': { /* funcargs */ + fs.exp2nextreg(v); + this.funcargs(v); + break; + } + default: + return; + } + } + } + + + private void simpleexp(expdesc v) throws CompileException { + /* + * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | + * FUNCTION body | primaryexp + */ + switch (this.t.token) { + case TK_NUMBER: { + v.init(VKNUM, 0); + v.u.setNval(this.t.seminfo.r); + break; + } + case TK_STRING: { + this.codestring(v, this.t.seminfo.ts); + break; + } + case TK_NIL: { + v.init(VNIL, 0); + break; + } + case TK_TRUE: { + v.init(VTRUE, 0); + break; + } + case TK_FALSE: { + v.init(VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState52 fs = this.fs; + this.check_condition(fs.f.is_vararg != 0, "cannot use " + LUA_QL("...") + + " outside a vararg function"); + fs.f.is_vararg &= ~Lua.VARARG_NEEDSARG; /* don't need 'arg' */ + v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + this.constructor(v); + return; + } + case TK_FUNCTION: { + this.nextToken(); + this.body(v, false, this.linenumber); + return; + } + default: { + this.primaryexp(v); + return; + } + } + this.nextToken(); + } + + + private int getunopr(int op) { + switch (op) { + case TK_NOT: + return OPR_NOT; + case '-': + return OPR_MINUS; + case '#': + return OPR_LEN; + default: + return OPR_NOUNOPR; + } + } + + + private int getbinopr(int op) { + switch (op) { + case '+': + return OPR_ADD; + case '-': + return OPR_SUB; + case '*': + return OPR_MUL; + case '/': + return OPR_DIV; + case '%': + return OPR_MOD; + case '^': + return OPR_POW; + case TK_CONCAT: + return OPR_CONCAT; + case TK_NE: + return OPR_NE; + case TK_EQ: + return OPR_EQ; + case '<': + return OPR_LT; + case TK_LE: + return OPR_LE; + case '>': + return OPR_GT; + case TK_GE: + return OPR_GE; + case TK_AND: + return OPR_AND; + case TK_OR: + return OPR_OR; + default: + return OPR_NOBINOPR; + } + } + + static class Priority { + final byte left; /* left priority for each binary operator */ + + final byte right; /* right priority */ + + public Priority(int i, int j) { + left = (byte) i; + right = (byte) j; + } + } + + private 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(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) */ + }; + + private static final int UNARY_PRIORITY = 8; /* priority for unary operators */ + + + /* + ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } + ** where `binop' is any binary operator with a priority higher than `limit' + */ + private int subexpr(expdesc v, int limit) throws CompileException { + int op; + int uop; + this.enterlevel(); + uop = getunopr(this.t.token); + if (uop != OPR_NOUNOPR) { + this.nextToken(); + this.subexpr(v, UNARY_PRIORITY); + fs.prefix(uop, v); + } else { + this.simpleexp(v); + } + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(this.t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2 = new expdesc(); + int nextop; + this.nextToken(); + fs.infix(op, v); + /* read sub-expression with higher priority */ + nextop = this.subexpr(v2, priority[op].right); + fs.posfix(op, v, v2); + op = nextop; + } + this.leavelevel(); + return op; /* return first untreated operator */ + } + + private void expr(expdesc v) throws CompileException { + this.subexpr(v, 0); + } + + /* }==================================================================== */ + + + + /* + ** {====================================================================== + ** Rules for Statements + ** ======================================================================= + */ + + + private boolean block_follow(int token) { + switch (token) { + case TK_ELSE: + case TK_ELSEIF: + case TK_END: + case TK_UNTIL: + case TK_EOS: + return true; + default: + return false; + } + } + + + private void block() throws CompileException { + /* block -> chunk */ + FuncState52 fs = this.fs; + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + fs.enterblock(bl, false); + this.chunk(); + LuaC._assert(bl.breaklist.i == NO_JUMP); + fs.leaveblock(); + } + + + /* + ** structure to chain all variables in the left-hand side of an + ** assignment + */ + static class LHS_assign { + LHS_assign prev; + /* variable (global, local, upvalue, or indexed) */ + expdesc v = new expdesc(); + } + + + /* + ** check whether, in an assignment to a local variable, the local variable + ** is needed in a previous assignment (to a table). If so, save original + ** local value in a safe place and use this safe copy in the previous + ** assignment. + */ + private void check_conflict(LHS_assign lh, expdesc v) throws CompileException { + FuncState52 fs = this.fs; + int extra = fs.freereg; /* eventual position to save local variable */ + boolean conflict = false; + for (; lh != null; lh = lh.prev) { + if (lh.v.k == VINDEXED) { + if (lh.v.u.s.info == v.u.s.info) { /* conflict? */ + conflict = true; + lh.v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh.v.u.s.aux == v.u.s.info) { /* conflict? */ + conflict = true; + lh.v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */ + fs.reserveregs(1); + } + } + + + private void assignment(LHS_assign lh, int nvars) throws CompileException { + expdesc e = new expdesc(); + this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED, + "syntax error"); + if (this.testnext(',')) { /* assignment -> `,' primaryexp assignment */ + LHS_assign nv = new LHS_assign(); + nv.prev = lh; + this.primaryexp(nv.v); + if (nv.v.k == VLOCAL) { + this.check_conflict(lh, nv.v); + } + this.assignment(nv, nvars + 1); + } else { /* assignment . `=' explist1 */ + int nexps; + this.checknext('='); + nexps = this.explist1(e); + if (nexps != nvars) { + this.adjust_assign(nvars, nexps, e); + if (nexps > nvars) { + this.fs.freereg -= nexps - nvars; /* remove extra values */ + } + } else { + fs.setoneret(e); /* close last expression */ + fs.storevar(lh.v, e); + return; /* avoid default */ + } + } + e.init(VNONRELOC, this.fs.freereg - 1); /* default assignment */ + fs.storevar(lh.v, e); + } + + + private int cond() throws CompileException { + /* cond -> exp */ + expdesc v = new expdesc(); + /* read condition */ + this.expr(v); + /* `falses' are all equal here */ + if (v.k == VNIL) { + v.k = VFALSE; + } + fs.goiftrue(v); + return v.f.i; + } + + + private void breakstat() throws CompileException { + FuncState52 fs = this.fs; + FuncState52.BlockCnt bl = fs.bl; + boolean upval = false; + while (bl != null && !bl.isbreakable) { + upval |= bl.upval; + bl = bl.previous; + } + if (bl == null) { + throw syntaxError("no loop to break"); + } + if (upval) { + fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0); + } + fs.concat(bl.breaklist, fs.jump()); + } + + + private void whilestat(int line) throws CompileException { + /* whilestat -> WHILE cond DO block END */ + FuncState52 fs = this.fs; + int whileinit; + int condexit; + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + this.nextToken(); /* skip WHILE */ + whileinit = fs.getlabel(); + condexit = this.cond(); + fs.enterblock(bl, true); + this.checknext(TK_DO); + this.block(); + fs.patchlist(fs.jump(), whileinit); + this.check_match(TK_END, TK_WHILE, line); + fs.leaveblock(); + fs.patchtohere(condexit); /* false conditions finish the loop */ + } + + private void repeatstat(int line) throws CompileException { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState52 fs = this.fs; + int repeat_init = fs.getlabel(); + FuncState52.BlockCnt bl1 = new FuncState52.BlockCnt(); + FuncState52.BlockCnt bl2 = new FuncState52.BlockCnt(); + fs.enterblock(bl1, true); /* loop block */ + fs.enterblock(bl2, false); /* scope block */ + this.nextToken(); /* skip REPEAT */ + this.chunk(); + this.check_match(TK_UNTIL, TK_REPEAT, line); + condexit = this.cond(); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + fs.leaveblock(); /* finish scope */ + fs.patchlist(condexit, repeat_init); /* close the loop */ + } else { /* complete semantics when there are upvalues */ + this.breakstat(); /* if condition then break */ + fs.patchtohere(condexit); /* else... */ + fs.leaveblock(); /* finish scope... */ + fs.patchlist(fs.jump(), repeat_init); /* and repeat */ + } + fs.leaveblock(); /* finish loop */ + } + + + private int exp1() throws CompileException { + expdesc e = new expdesc(); + int k; + this.expr(e); + k = e.k; + fs.exp2nextreg(e); + return k; + } + + + private void forbody(int base, int line, int nvars, boolean isnum) throws CompileException { + /* forbody -> DO block */ + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + FuncState52 fs = this.fs; + int prep, endfor; + this.adjustlocalvars(3); /* control variables */ + this.checknext(TK_DO); + prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump(); + fs.enterblock(bl, false); /* scope for declared variables */ + this.adjustlocalvars(nvars); + fs.reserveregs(nvars); + this.block(); + fs.leaveblock(); /* end of scope for declared variables */ + fs.patchtohere(prep); + endfor = isnum ? fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP) : fs + .codeABC(Lua.OP_TFORLOOP, base, 0, nvars); + fs.fixline(line); /* pretend that `Lua.OP_FOR' starts the loop */ + fs.patchlist(isnum ? endfor : fs.jump(), prep + 1); + } + + + private void fornum(LuaString varname, int line) throws CompileException { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState52 fs = this.fs; + int base = fs.freereg; + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2); + this.new_localvar(varname, 3); + this.checknext('='); + this.exp1(); /* initial value */ + this.checknext(','); + this.exp1(); /* limit */ + if (this.testnext(',')) { + this.exp1(); /* optional step */ + } else { /* default step = 1 */ + fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); + fs.reserveregs(1); + } + this.forbody(base, line, 1, true); + } + + + private void forlist(LuaString indexname) throws CompileException { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState52 fs = this.fs; + expdesc e = new expdesc(); + int nvars = 0; + int line; + int base = fs.freereg; + /* create control variables */ + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++); + /* create declared variables */ + this.new_localvar(indexname, nvars++); + while (this.testnext(',')) { + this.new_localvar(this.str_checkname(), nvars++); + } + this.checknext(TK_IN); + line = this.linenumber; + this.adjust_assign(3, this.explist1(e), e); + fs.checkstack(3); /* extra space to call generator */ + this.forbody(base, line, nvars - 3, false); + } + + + private void forstat(int line) throws CompileException { + /* forstat -> FOR (fornum | forlist) END */ + FuncState52 fs = this.fs; + LuaString varname; + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + fs.enterblock(bl, true); /* scope for loop and control variables */ + this.nextToken(); /* skip `for' */ + varname = this.str_checkname(); /* first variable name */ + switch (this.t.token) { + case '=': + this.fornum(varname, line); + break; + case ',': + case TK_IN: + this.forlist(varname); + break; + default: + throw syntaxError(LUA_QL("=") + " or " + LUA_QL("in") + " expected"); + } + this.check_match(TK_END, TK_FOR, line); + fs.leaveblock(); /* loop scope (`break' jumps to this point) */ + } + + + private int test_then_block() throws CompileException { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + this.nextToken(); /* skip IF or ELSEIF */ + condexit = this.cond(); + this.checknext(TK_THEN); + this.block(); /* `then' part */ + return condexit; + } + + + private void ifstat(int line) throws CompileException { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] + * END */ + FuncState52 fs = this.fs; + int flist; + IntPtr escapelist = new IntPtr(NO_JUMP); + flist = test_then_block(); /* IF cond THEN block */ + while (this.t.token == TK_ELSEIF) { + fs.concat(escapelist, fs.jump()); + fs.patchtohere(flist); + flist = test_then_block(); /* ELSEIF cond THEN block */ + } + if (this.t.token == TK_ELSE) { + fs.concat(escapelist, fs.jump()); + fs.patchtohere(flist); + this.nextToken(); /* skip ELSE (after patch, for correct line info) */ + this.block(); /* `else' part */ + } else { + fs.concat(escapelist, flist); + } + fs.patchtohere(escapelist.i); + this.check_match(TK_END, TK_IF, line); + } + + private void localfunc() throws CompileException { + expdesc v = new expdesc(); + expdesc b = new expdesc(); + FuncState52 fs = this.fs; + this.new_localvar(this.str_checkname(), 0); + v.init(VLOCAL, fs.freereg); + fs.reserveregs(1); + this.adjustlocalvars(1); + this.body(b, false, this.linenumber); + fs.storevar(v, b); + /* debug information will only see the variable after this point! */ + fs.getlocvar(fs.nactvar - 1).startpc = fs.pc; + } + + + private void localstat() throws CompileException { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e = new expdesc(); + do { + this.new_localvar(this.str_checkname(), nvars++); + } while (this.testnext(',')); + if (this.testnext('=')) { + nexps = this.explist1(e); + } else { + e.k = VVOID; + nexps = 0; + } + this.adjust_assign(nvars, nexps, e); + this.adjustlocalvars(nvars); + } + + + private boolean funcname(expdesc v) throws CompileException { + /* funcname -> NAME {field} [`:' NAME] */ + boolean needself = false; + this.singlevar(v); + while (this.t.token == '.') { + this.field(v); + } + if (this.t.token == ':') { + needself = true; + this.field(v); + } + return needself; + } + + + private void funcstat(int line) throws CompileException { + /* funcstat -> FUNCTION funcname body */ + boolean needself; + expdesc v = new expdesc(); + expdesc b = new expdesc(); + this.nextToken(); /* skip FUNCTION */ + needself = this.funcname(v); + this.body(b, needself, line); + fs.storevar(v, b); + fs.fixline(line); /* definition `happens' in the first line */ + } + + + private void exprstat() throws CompileException { + /* stat -> func | assignment */ + FuncState52 fs = this.fs; + LHS_assign v = new LHS_assign(); + this.primaryexp(v.v); + if (v.v.k == VCALL) /* stat -> func */ { + LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ + } else { /* stat -> assignment */ + v.prev = null; + this.assignment(v, 1); + } + } + + private void retstat() throws CompileException { + /* stat -> RETURN explist */ + FuncState52 fs = this.fs; + expdesc e = new expdesc(); + int first, nret; /* registers with returned values */ + this.nextToken(); /* skip RETURN */ + if (block_follow(this.t.token) || this.t.token == ';') { + first = nret = 0; /* return no values */ + } else { + nret = this.explist1(e); /* optional return values */ + if (hasmultret(e.k)) { + fs.setmultret(e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); + LuaC._assert(Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); + } + first = fs.nactvar; + nret = Lua.LUA_MULTRET; /* return all values */ + } else { + if (nret == 1) /* only one single value? */ { + first = fs.exp2anyreg(e); + } else { + fs.exp2nextreg(e); /* values must go to the `stack' */ + first = fs.nactvar; /* return all `active' values */ + LuaC._assert(nret == fs.freereg - first); + } + } + } + fs.ret(first, nret); + } + + + private boolean statement() throws CompileException { + int line = this.linenumber; /* may be needed for error messages */ + switch (this.t.token) { + case TK_IF: { /* stat -> ifstat */ + this.ifstat(line); + return false; + } + case TK_WHILE: { /* stat -> whilestat */ + this.whilestat(line); + return false; + } + case TK_DO: { /* stat -> DO block END */ + this.nextToken(); /* skip DO */ + this.block(); + this.check_match(TK_END, TK_DO, line); + return false; + } + case TK_FOR: { /* stat -> forstat */ + this.forstat(line); + return false; + } + case TK_REPEAT: { /* stat -> repeatstat */ + this.repeatstat(line); + return false; + } + case TK_FUNCTION: { + this.funcstat(line); /* stat -> funcstat */ + return false; + } + case TK_LOCAL: { /* stat -> localstat */ + this.nextToken(); /* skip LOCAL */ + if (this.testnext(TK_FUNCTION)) /* local function? */ { + this.localfunc(); + } else { + this.localstat(); + } + return false; + } + case TK_RETURN: { /* stat -> retstat */ + this.retstat(); + return true; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + this.nextToken(); /* skip BREAK */ + this.breakstat(); + return true; /* must be last statement */ + } + default: { + this.exprstat(); + return false; /* to avoid warnings */ + } + } + } + + void chunk() throws CompileException { + /* chunk -> { stat [`;'] } */ + boolean islast = false; + this.enterlevel(); + while (!islast && !block_follow(this.t.token)) { + islast = this.statement(); + this.testnext(';'); + LuaC._assert(this.fs.f.maxstacksize >= this.fs.freereg + && this.fs.freereg >= this.fs.nactvar); + this.fs.freereg = this.fs.nactvar; /* free registers */ + } + this.leavelevel(); + } + + /* }====================================================================== */ +} diff --git a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java index 94e13577..1763b0fc 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java @@ -31,6 +31,7 @@ import org.squiddev.cobalt.function.LuaClosure; import org.squiddev.cobalt.function.LuaFunction; import org.squiddev.cobalt.function.LuaInterpretedFunction; +import org.squiddev.cobalt.function.Upvalue; import org.squiddev.cobalt.lib.jse.JsePlatform; import java.io.IOException; @@ -129,6 +130,9 @@ public static LuaFunction load(LuaState state, InputStream stream, LuaString nam Prototype p = loadBinaryChunk(firstByte, stream, name); LuaInterpretedFunction closure = new LuaInterpretedFunction(p, env); closure.nilUpvalues(); + if (p.isLua52 && p.nups == 1) { + closure.setUpvalue(0, new Upvalue(env)); + } return closure; } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index de478097..bd549a48 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -30,6 +30,7 @@ import org.squiddev.cobalt.function.LocalVariable; import org.squiddev.cobalt.function.LuaFunction; import org.squiddev.cobalt.function.LuaInterpretedFunction; +import org.squiddev.cobalt.function.Upvalue; import org.squiddev.cobalt.lib.BaseLib; import org.squiddev.cobalt.lib.jse.JsePlatform; @@ -73,6 +74,10 @@ public class LuaC implements LuaCompiler { protected static void _assert(boolean b) throws CompileException { if (!b) { // So technically this should fire a runtime exception but... + System.err.println("compiler assert failed\nstack trace:"); + for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { + System.err.println(ste); + } throw new CompileException("compiler assert failed"); } } @@ -180,6 +185,9 @@ public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaT Prototype p = compile(stream, name, mode); LuaInterpretedFunction closure = new LuaInterpretedFunction(p, env); closure.nilUpvalues(); + if (p.isLua52 && p.nups == 1) { + closure.setUpvalue(0, new Upvalue(env)); + } return closure; } @@ -220,23 +228,26 @@ private static Prototype luaY_parser(int firstByte, InputStream z, LuaString nam // lexstate.buff = buff; lexstate.setinput(firstByte, z, name); lexstate.open_func(funcstate); + funcstate.f.isLua52 = false; // temporary! /* main func. is always vararg */ funcstate.f.is_vararg = Lua.VARARG_ISVARARG; funcstate.f.source = name; - LexState.expdesc v = new LexState.expdesc(); - v.init(LexState.VLOCAL, 0); - funcstate.f.upvalues = new LuaString[1]; - funcstate.f.upvalues[0] = LuaString.valueOf("_ENV"); - funcstate.f.nups = 1; - funcstate.upvalues[0] = funcstate.new upvaldesc(); - funcstate.upvalues[0].k = LexState.VLOCAL; - funcstate.upvalues[0].info = (short)v.u.s.info; + if (funcstate.f.isLua52) { + LexState.expdesc v = new LexState.expdesc(); + v.init(LexState.VLOCAL, 0); + funcstate.f.upvalues = new LuaString[1]; + funcstate.f.upvalues[0] = LuaString.valueOf("_ENV"); + funcstate.f.nups = 1; + funcstate.upvalues[0] = funcstate.new upvaldesc(); + funcstate.upvalues[0].k = LexState.VLOCAL; + funcstate.upvalues[0].info = (short) v.u.s.info; + } lexstate.nextToken(); /* read first token */ lexstate.chunk(); lexstate.check(LexState.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); - LuaC._assert(funcstate.f.nups == 1); + LuaC._assert(funcstate.f.nups == (funcstate.f.isLua52 ? 1 : 0)); LuaC._assert(lexstate.fs == null); return funcstate.f; } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 16864e12..1c9efc9c 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -208,7 +208,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } case OP_GETUPVAL: // A B: R(A):= UpValue[B] - stack[a] = upvalues[((i >>> POS_B) & MAXARG_B)].getValue(); + stack[a] = upvalues[((i >>> POS_B) & MAXARG_B) + (p.isLua52 ? 0 : 1)].getValue(); break; case OP_GETTABUP: { // A B C: R(A) := UpValue[B][RK(C)] @@ -241,7 +241,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } case OP_SETUPVAL: // A B: UpValue[B]:= R(A) - upvalues[(i >>> POS_B) & MAXARG_B].setValue(stack[a]); + upvalues[((i >>> POS_B) & MAXARG_B) + (p.isLua52 ? 0 : 1)].setValue(stack[a]); break; case OP_SETTABLE: { // A B C: R(A)[RK(B)]:= RK(C) @@ -586,8 +586,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti continue newFrame; } - Varargs args = ValueFactory.varargsOf(stack, a + 1, a + 2); // exact arg count - Varargs v = OperationHelper.invoke(state, val, args.asImmutable(), a); + Varargs v = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); i = code[pc++]; a = ((i >> POS_A) & MAXARG_A); if (c > 0) { @@ -605,8 +604,11 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), R(A+2)): if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */ - Varargs v = di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); - LuaValue val = v.first(); + LuaValue val; + Varargs v; + if (!p.isLua52) di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); + v = di.extras; + val = v.first(); if (val.isNil()) { pc++; } else { @@ -658,23 +660,32 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), case OP_CLOSURE: { // A Bx: R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) Prototype newp = p.p[(i >>> POS_Bx) & MAXARG_Bx]; - LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, (LuaTable)upvalues[0].getValue()); - if (p.isLua52) { + LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, newp.isLua52 ? null : (LuaTable)upvalues[0].getValue()); + if (newp.isLua52) { for (int j = 0; j < newp.nups; ++j) { - DebugFrame frame = di; - for (int s = (newp.upvalue_info[j] >> 8) - 1; s > 0 && frame != null; --s) frame = frame.previous; - if (frame == null) { - // TODO: handle this + int b = newp.upvalue_info[j]; + if ((b >> 8) != 0) { + DebugFrame frame = di; + for (int s = (b >> 8) - 1; s > 0 && frame != null; --s) frame = frame.previous; + if (frame == null) { + throw new IllegalStateException("Upvalue stack index out of range"); + } + //System.out.println("Found upvalue index " + (newp.upvalue_info[j] & 0xFF) + " (named " + (frame.func instanceof LuaInterpretedFunction ? frame.closure.getPrototype().upvalues[newp.upvalue_info[j] & 0xFF] : "?") + ") with type " + frame.closure.getUpvalue(newp.upvalue_info[j] & 0xFF).getClass().getName()); + newcl.upvalues[j] = new Upvalue(frame.stack, b & 0xFF); + //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(frame.stack, b & 0xFF)); + //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); + } else { + newcl.upvalues[j] = upvalues[b & 0xFF]; } - newcl.upvalues[j] = new Upvalue(frame.stack, newp.upvalue_info[j] & 0xFF); } } else { - for (int j = 0, nup = newp.nups; j < nup; ++j) { + for (int j = 1, nup = newp.nups; j <= nup; ++j) { i = code[pc++]; int b = (i >>> POS_B) & MAXARG_B; newcl.upvalues[j] = (i & 4) != 0 - ? upvalues[b] // OP_GETUPVAL + ? upvalues[b+1] // OP_GETUPVAL : openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); // OP_MOVE + //System.out.println("Added upvalue " + j + " as " + ((i & 4) != 0 ? di.closure.getPrototype().upvalues[b] : "?")); } } stack[a] = newcl; @@ -759,8 +770,10 @@ public static void closeAll(Upvalue[] upvalues) { public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction function, Varargs varargs) throws LuaError, UnwindThrowable { Prototype p = function.p; int i = p.code[di.pc++]; + int op = ((i >> POS_OP) & MAX_OP); + if (!p.isLua52) op = lua51opcodes[op]; - switch (((i >> POS_OP) & MAX_OP)) { + switch (op) { case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: case OP_POW: case OP_UNM: case OP_GETTABLE: case OP_GETTABUP: case OP_GETGLOBAL: case OP_SELF: { di.stack[(i >> POS_A) & MAXARG_A] = varargs.first(); diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 937e6f85..1d69e0c7 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -24,6 +24,7 @@ */ package org.squiddev.cobalt.lib; +import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode; import org.squiddev.cobalt.*; import org.squiddev.cobalt.compiler.LoadState; import org.squiddev.cobalt.debug.DebugFrame; @@ -91,6 +92,7 @@ public class BaseLib implements LuaLibrary { "next", // "next" ( table, [index] ) -> next-index, next-value "__inext", // "inext" ( table, [int-index] ) -> next-index, next-value "rawlen", // "rawlen" ( table | string ) -> int + "__internal_print", }; private static final String[] LIBR_KEYS = { "pcall", // (f, arg1, ...) -> status, result1, ... @@ -321,6 +323,9 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError, UnwindThrow throw ErrorFactory.argError(1, "table or string expected"); } } + case 21: // __internal_print (TODO: delete this) + System.out.println(args.arg(1).checkLuaString().toString()); + return Constants.NONE; } return Constants.NONE; } From 29a757361d77e297aefea6d4269bf6faa70f3112 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Wed, 31 Mar 2021 00:32:49 -0400 Subject: [PATCH 04/24] Lua 5.2 bytecode works properly now Boots CraftOS! (when precompiled to 5.2) Now to write the compiler... --- .../cobalt/function/LuaInterpreter.java | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 1c9efc9c..3948b1db 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -200,10 +200,17 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; case OP_LOADNIL: { // A B: R(A):= ...:= R(B):= nil - int b = ((i >>> POS_B) & MAXARG_B); - do { - stack[b--] = NIL; - } while (b >= a); + if (p.isLua52) { + int b = ((i >>> POS_B) & MAXARG_B); + while (b >= 0) { + stack[a+(b--)] = NIL; + } + } else { + int b = ((i >>> POS_B) & MAXARG_B); + do { + stack[b--] = NIL; + } while (b >= a); + } break; } @@ -577,7 +584,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; case OP_TFORCALL: { // A C: R(A+3), ..., R(A+2+C) := R(A)(R(A+1), R(A+2)); - int c = ((i >> POS_C) & MAXARG_C); + /*int c = ((i >> POS_C) & MAXARG_C); LuaValue val = stack[a]; if (val instanceof LuaInterpretedFunction) { @@ -595,7 +602,13 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } else { di.top = a + 3 + v.count(); di.extras = v; + }*/ + di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); + for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { + stack[a + 2 + c] = di.extras.arg(c); } + i = code[pc++]; + a = ((i >> POS_A) & MAXARG_A); } case OP_TFORLOOP: { @@ -604,19 +617,28 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), R(A+2)): if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */ - LuaValue val; - Varargs v; - if (!p.isLua52) di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); - v = di.extras; - val = v.first(); - if (val.isNil()) { - pc++; + if (p.isLua52) { + Varargs v = di.extras; + LuaValue val = v.first(); + if (!val.isNil()) { + stack[a] = stack[a + 1] = val; + di.extras = NONE; + pc += ((i >>> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + } } else { - stack[a + 2] = stack[a + 3] = val; - for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { - stack[a + 2 + c] = v.arg(c); + di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); + Varargs v = di.extras; + LuaValue val = v.first(); + if (val.isNil()) { + pc++; + } else { + for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { + stack[a + 2 + c] = v.arg(c); + } + if (p.isLua52) a = ((i >> POS_A) & MAXARG_A); + stack[a + 2] = stack[a + 3] = val; + di.extras = NONE; } - di.extras = NONE; } break; } From 9fb8a9d9faa9c79aa4244e1c09b003afc664e02c Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Wed, 31 Mar 2021 17:58:54 -0400 Subject: [PATCH 05/24] Added 5.2 bytecode generation This is complete enough to fully boot CraftOS in Lua 5.2; no precompilation necessary. --- src/main/java/org/squiddev/cobalt/Lua52.java | 5 +- src/main/java/org/squiddev/cobalt/Print.java | 1 - .../squiddev/cobalt/compiler/DumpState.java | 45 ++++-- .../squiddev/cobalt/compiler/FuncState52.java | 146 +++++++++--------- .../squiddev/cobalt/compiler/LexState52.java | 132 +++++++++------- .../org/squiddev/cobalt/compiler/LuaC.java | 45 ++++-- .../cobalt/function/LuaInterpreter.java | 1 + .../java/org/squiddev/cobalt/lib/BaseLib.java | 3 +- 8 files changed, 208 insertions(+), 170 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/Lua52.java b/src/main/java/org/squiddev/cobalt/Lua52.java index 0a32571d..4260d103 100644 --- a/src/main/java/org/squiddev/cobalt/Lua52.java +++ b/src/main/java/org/squiddev/cobalt/Lua52.java @@ -262,7 +262,7 @@ public static int RKASK(int x) { if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */ public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ - public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx]) */ public static final int OP_VARARG = 38; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ public static final int OP_EXTRAARG = 39;/* Ax extra (larger) argument for previous opcode */ @@ -352,9 +352,8 @@ set top (like in OP_CALL with C == 0). (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_FORLOOP */ (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_FORPREP */ (0 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgU << 2) | (iABC), /* OP_TFORCALL */ - (1 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgU << 2) | (iABC), /* OP_TFORLOOP */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_TFORLOOP */ (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_SETLIST */ - (0 << 7) | (0 << 6) | (OpArgN << 4) | (OpArgN << 2) | (iABC), /* OP_CLOSE */ (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABx), /* OP_CLOSURE */ (0 << 7) | (1 << 6) | (OpArgU << 4) | (OpArgN << 2) | (iABC), /* OP_VARARG */ (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iAx), /* OP_EXTRAARG */ diff --git a/src/main/java/org/squiddev/cobalt/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index 095593f0..bfa237d7 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -121,7 +121,6 @@ public class Print { "TFORCALL", "TFORLOOP", "SETLIST", - "CLOSE", "CLOSURE", "VARARG", "EXTRAARG", diff --git a/src/main/java/org/squiddev/cobalt/compiler/DumpState.java b/src/main/java/org/squiddev/cobalt/compiler/DumpState.java index 6cc666f4..b3c77deb 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/DumpState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/DumpState.java @@ -24,10 +24,7 @@ */ package org.squiddev.cobalt.compiler; -import org.squiddev.cobalt.Constants; -import org.squiddev.cobalt.LuaString; -import org.squiddev.cobalt.LuaValue; -import org.squiddev.cobalt.Prototype; +import org.squiddev.cobalt.*; import org.squiddev.cobalt.function.LocalVariable; import java.io.DataOutputStream; @@ -46,6 +43,11 @@ public class DumpState { */ public static final int LUAC_VERSION = 0x51; + /** + * for header of binary files -- this is Lua 5.1 + */ + public static final int LUAC_VERSION_52 = 0x52; + /** * for header of binary files -- this is the official format */ @@ -227,25 +229,39 @@ void dumpDebug(final Prototype f) throws IOException { } void dumpFunction(final Prototype f, final LuaString string) throws IOException { - if (f.source == null || f.source.equals(string) || strip) { - dumpInt(0); - } else { - dumpString(f.source); + if (!f.isLua52) { + if (f.source == null || f.source.equals(string) || strip) { + dumpInt(0); + } else { + dumpString(f.source); + } } dumpInt(f.linedefined); dumpInt(f.lastlinedefined); - dumpChar(f.nups); + if (!f.isLua52) dumpChar(f.nups); dumpChar(f.numparams); dumpChar(f.is_vararg); dumpChar(f.maxstacksize); dumpCode(f); dumpConstants(f); + if (f.isLua52) { + dumpInt(f.nups); + for (int i = 0; i < f.nups; i++) { + dumpChar(f.upvalue_info[i] >> 8); + dumpChar(f.upvalue_info[i] & 0xFF); + } + if (f.source == null || f.source.equals(string) || strip) { + dumpInt(0); + } else { + dumpString(f.source); + } + } dumpDebug(f); } - void dumpHeader() throws IOException { + void dumpHeader(boolean isLua52) throws IOException { writer.write(LUAC_HEADER_SIGNATURE); - writer.write(LUAC_VERSION); + writer.write(isLua52 ? LUAC_VERSION_52 : LUAC_VERSION); writer.write(LUAC_FORMAT); writer.write(IS_LITTLE_ENDIAN ? 1 : 0); writer.write(SIZEOF_INT); @@ -253,6 +269,9 @@ void dumpHeader() throws IOException { writer.write(SIZEOF_INSTRUCTION); writer.write(SIZEOF_LUA_NUMBER); writer.write(NUMBER_FORMAT); + if (isLua52) + for (int i = 0; i < 6; i++) + writer.write(Lua52.error_check[i]); } /* @@ -260,7 +279,7 @@ void dumpHeader() throws IOException { */ public static int dump(Prototype f, OutputStream w, boolean strip) throws IOException { DumpState D = new DumpState(w, strip); - D.dumpHeader(); + D.dumpHeader(f.isLua52); D.dumpFunction(f, null); return D.status; } @@ -288,7 +307,7 @@ public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numb D.IS_LITTLE_ENDIAN = littleendian; D.NUMBER_FORMAT = numberFormat; D.SIZEOF_LUA_NUMBER = (numberFormat == NUMBER_FORMAT_INTS_ONLY ? 4 : 8); - D.dumpHeader(); + D.dumpHeader(f.isLua52); D.dumpFunction(f, null); return D.status; } diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java index 6b7fb842..d95217c5 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java @@ -33,13 +33,13 @@ import java.util.Hashtable; import static org.squiddev.cobalt.Constants.*; -import static org.squiddev.cobalt.Lua.*; +import static org.squiddev.cobalt.Lua52.*; import static org.squiddev.cobalt.compiler.LuaC.*; public class FuncState52 { class upvaldesc { - short k; - short info; + boolean instack; + short idx; LuaString name; } @@ -79,11 +79,11 @@ static class BlockCnt { // ============================================================= InstructionPtr getcodePtr(expdesc e) { - return new InstructionPtr(f.code, e.u.s.info); + return new InstructionPtr(f.code, e.u.info); } int getcode(expdesc e) { - return f.code[e.u.s.info]; + return f.code[e.u.info]; } int codeAsBx(int o, int A, int sBx) throws CompileException { @@ -117,31 +117,29 @@ private void errorlimit(int limit, String what) throws CompileException { } - private int indexupvalue(LuaString name, expdesc v) throws CompileException { - int i; - for (i = 0; i < f.nups; i++) { - if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) { - _assert(f.upvalues[i] == name); - return i; - } - } + int newupvalue(LuaString name, expdesc v) throws CompileException { /* new one */ checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); if (f.upvalues == null || f.nups + 1 > f.upvalues.length) { f.upvalues = realloc(f.upvalues, f.nups * 2 + 1); } + if (f.upvalue_info == null || f.nups + 1 > f.upvalue_info.length) { + f.upvalue_info = realloc(f.upvalue_info, f.nups * 2 + 1); + } f.upvalues[f.nups] = name; + f.upvalue_info[f.nups] = ((v.k == LexState52.VLOCAL ? 1 : 0) << 8) | v.u.info; _assert(v.k == LexState52.VLOCAL || v.k == LexState52.VUPVAL); upvalues[f.nups] = new upvaldesc(); - upvalues[f.nups].k = (short) (v.k); - upvalues[f.nups].info = (short) (v.u.s.info); + upvalues[f.nups].instack = (v.k == LexState52.VLOCAL); + upvalues[f.nups].idx = (short) (v.u.info); + upvalues[f.nups].name = name; return f.nups++; } private int searchupvalue(LuaString n) { int i; for (i = 0; i < f.nups; i++) { - if (f.upvalues[i] == n) return i; + if (f.upvalues[i].equals(n)) return i; } return -1; /* not found */ } @@ -183,9 +181,7 @@ int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { if (prev.singlevaraux(n, var, 0) == LexState52.VVOID) { return LexState52.VVOID; } - var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ - var.k = LexState52.VUPVAL; /* upvalue in this level */ - return LexState52.VUPVAL; + idx = this.newupvalue(n, var); /* else was LOCAL or UPVAL */ } var.init(LexState52.VUPVAL, idx); return LexState52.VUPVAL; @@ -220,9 +216,9 @@ void leaveblock() throws CompileException { BlockCnt bl = this.bl; this.bl = bl.previous; ls.removevars(bl.nactvar); - if (bl.upval) { + /*if (bl.upval) { this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); - } + }*/ /* a block either controls scope or breaks (never both) */ _assert(!bl.isbreakable || !bl.upval); _assert(bl.nactvar == this.nactvar); @@ -237,7 +233,7 @@ void closelistfield(ConsControl cc) throws CompileException { this.exp2nextreg(cc.v); cc.v.k = LexState52.VVOID; if (cc.tostore == LFIELDS_PER_FLUSH) { - this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */ + this.setlist(cc.t.u.info, cc.na, cc.tostore); /* flush */ cc.tostore = 0; /* no more items pending */ } } @@ -250,13 +246,13 @@ void lastlistfield(ConsControl cc) throws CompileException { if (cc.tostore == 0) return; if (hasmultret(cc.v.k)) { this.setmultret(cc.v); - this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET); + this.setlist(cc.t.u.info, cc.na, LUA_MULTRET); cc.na--; /* do not count last expression (unknown number of elements) */ } else { if (cc.v.k != LexState52.VVOID) { this.exp2nextreg(cc.v); } - this.setlist(cc.t.u.s.info, cc.na, cc.tostore); + this.setlist(cc.t.u.info, cc.na, cc.tostore); } } @@ -276,10 +272,10 @@ void nil(int from, int n) throws CompileException { previous = new InstructionPtr(this.f.code, this.pc - 1); if (GET_OPCODE(previous.get()) == OP_LOADNIL) { int pfrom = GETARG_A(previous.get()); - int pto = GETARG_B(previous.get()); - if (pfrom <= from && from <= pto + 1) { /* can connect both? */ - if (from + n - 1 > pto) { - SETARG_B(previous, from + n - 1); + int pn = GETARG_B(previous.get()); + if (pfrom <= from && from <= pfrom + pn) { /* can connect both? */ + if (from + n - 1 > pfrom + n - 1) { + SETARG_B(previous, n); } return; } @@ -287,7 +283,7 @@ void nil(int from, int n) throws CompileException { } } /* else no optimization */ - this.codeABC(OP_LOADNIL, from, from + n - 1, 0); + this.codeABC(OP_LOADNIL, from, n, 0); } @@ -462,7 +458,7 @@ private void freereg(int reg) throws CompileException { private void freeexp(expdesc e) throws CompileException { if (e.k == LexState52.VNONRELOC) { - this.freereg(e.u.s.info); + this.freereg(e.u.info); } } @@ -518,7 +514,7 @@ void setreturns(expdesc e, int nresults) throws CompileException { void setoneret(expdesc e) { if (e.k == LexState52.VCALL) { /* expression is an open function call? */ e.k = LexState52.VNONRELOC; - e.u.s.info = GETARG_A(this.getcode(e)); + e.u.info = GETARG_A(this.getcode(e)); } else if (e.k == LexState52.VVARARG) { SETARG_B(this.getcodePtr(e), 2); e.k = LexState52.VRELOCABLE; /* can relocate its simple result */ @@ -532,20 +528,18 @@ void dischargevars(expdesc e) throws CompileException { break; } case LexState52.VUPVAL: { - e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0); - e.k = LexState52.VRELOCABLE; - break; - } - case LexState52.VGLOBAL: { - e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info); + e.u.info = this.codeABC(OP_GETUPVAL, 0, e.u.info, 0); e.k = LexState52.VRELOCABLE; break; } case LexState52.VINDEXED: { - this.freereg(e.u.s.aux); - this.freereg(e.u.s.info); - e.u.s.info = this - .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux); + int op = Lua52.OP_GETTABUP; + this.freereg(e.u.ind.idx); + if (e.u.ind.vt == LexState52.VLOCAL) { + this.freereg(e.u.ind.t); + op = Lua52.OP_GETTABLE; + } + e.u.info = this.codeABC(op, 0, e.u.ind.t, e.u.ind.idx); e.k = LexState52.VRELOCABLE; break; } @@ -578,7 +572,7 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState52.VK: { - this.codeABx(OP_LOADK, reg, e.u.s.info); + this.codeABx(OP_LOADK, reg, e.u.info); break; } case LexState52.VKNUM: { @@ -591,8 +585,8 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState52.VNONRELOC: { - if (reg != e.u.s.info) { - this.codeABC(OP_MOVE, reg, e.u.s.info, 0); + if (reg != e.u.info) { + this.codeABC(OP_MOVE, reg, e.u.info, 0); } break; } @@ -601,7 +595,7 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { return; /* nothing to do... */ } } - e.u.s.info = reg; + e.u.info = reg; e.k = LexState52.VNONRELOC; } @@ -615,7 +609,7 @@ private void discharge2anyreg(expdesc e) throws CompileException { private void exp2reg(expdesc e, int reg) throws CompileException { this.discharge2reg(e, reg); if (e.k == LexState52.VJMP) { - this.concat(e.t, e.u.s.info); /* put this jump in `t' list */ + this.concat(e.t, e.u.info); /* put this jump in `t' list */ } if (e.hasjumps()) { int _final; /* position after whole expression */ @@ -633,7 +627,7 @@ private void exp2reg(expdesc e, int reg) throws CompileException { this.patchlistaux(e.t.i, _final, reg, p_t); } e.f.i = e.t.i = LexState52.NO_JUMP; - e.u.s.info = reg; + e.u.info = reg; e.k = LexState52.VNONRELOC; } @@ -648,15 +642,15 @@ int exp2anyreg(expdesc e) throws CompileException { this.dischargevars(e); if (e.k == LexState52.VNONRELOC) { if (!e.hasjumps()) { - return e.u.s.info; /* exp is already in a register */ + return e.u.info; /* exp is already in a register */ } - if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */ - this.exp2reg(e, e.u.s.info); /* put value on it */ - return e.u.s.info; + if (e.u.info >= this.nactvar) { /* reg. is not a local? */ + this.exp2reg(e, e.u.info); /* put value on it */ + return e.u.info; } } this.exp2nextreg(e); /* default */ - return e.u.s.info; + return e.u.info; } void exp2val(expdesc e) throws CompileException { @@ -675,18 +669,18 @@ int exp2RK(expdesc e) throws CompileException { case LexState52.VFALSE: case LexState52.VNIL: { if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ - e.u.s.info = (e.k == LexState52.VNIL) ? this.nilK() + e.u.info = (e.k == LexState52.VNIL) ? this.nilK() : (e.k == LexState52.VKNUM) ? this.numberK(e.u.nval()) : this.boolK((e.k == LexState52.VTRUE)); e.k = LexState52.VK; - return RKASK(e.u.s.info); + return RKASK(e.u.info); } else { break; } } case LexState52.VK: { - if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */ { - return RKASK(e.u.s.info); + if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ { + return RKASK(e.u.info); } else { break; } @@ -702,22 +696,18 @@ void storevar(expdesc var, expdesc ex) throws CompileException { switch (var.k) { case LexState52.VLOCAL: { this.freeexp(ex); - this.exp2reg(ex, var.u.s.info); + this.exp2reg(ex, var.u.info); return; } case LexState52.VUPVAL: { int e = this.exp2anyreg(ex); - this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0); - break; - } - case LexState52.VGLOBAL: { - int e = this.exp2anyreg(ex); - this.codeABx(OP_SETGLOBAL, e, var.u.s.info); + this.codeABC(OP_SETUPVAL, e, var.u.info, 0); break; } case LexState52.VINDEXED: { + int op = var.u.ind.vt == LexState52.VLOCAL ? Lua52.OP_SETTABLE : Lua52.OP_SETTABUP; int e = this.exp2RK(ex); - this.codeABC(OP_SETTABLE, var.u.s.info, var.u.s.aux, e); + this.codeABC(op, var.u.ind.t, var.u.ind.idx, e); break; } default: { @@ -734,14 +724,14 @@ void self(expdesc e, expdesc key) throws CompileException { this.freeexp(e); func = this.freereg; this.reserveregs(2); - this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key)); + this.codeABC(OP_SELF, func, e.u.info, this.exp2RK(key)); this.freeexp(key); - e.u.s.info = func; + e.u.info = func; e.k = LexState52.VNONRELOC; } private void invertjump(expdesc e) throws CompileException { - InstructionPtr pc = this.getjumpcontrol(e.u.s.info); + InstructionPtr pc = this.getjumpcontrol(e.u.info); _assert(testTMode(GET_OPCODE(pc.get())) && GET_OPCODE(pc.get()) != OP_TESTSET && Lua .GET_OPCODE(pc.get()) != OP_TEST); @@ -762,7 +752,7 @@ private int jumponcond(expdesc e, int cond) throws CompileException { } this.discharge2anyreg(e); this.freeexp(e); - return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond); + return this.condjump(OP_TESTSET, NO_REG, e.u.info, cond); } void goiftrue(expdesc e) throws CompileException { @@ -781,7 +771,7 @@ void goiftrue(expdesc e) throws CompileException { } case LexState52.VJMP: { this.invertjump(e); - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -808,7 +798,7 @@ private void goiffalse(expdesc e) throws CompileException { break; } case LexState52.VJMP: { - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -843,7 +833,7 @@ private void codenot(expdesc e) throws CompileException { case LexState52.VNONRELOC: { this.discharge2anyreg(e); this.freeexp(e); - e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0); + e.u.info = this.codeABC(OP_NOT, 0, e.u.info, 0); e.k = LexState52.VRELOCABLE; break; } @@ -863,7 +853,9 @@ private void codenot(expdesc e) throws CompileException { } void indexed(expdesc t, expdesc k) throws CompileException { - t.u.s.aux = this.exp2RK(k); + t.u.ind.t = t.u.info; + t.u.ind.idx = this.exp2RK(k); + t.u.ind.vt = t.k == LexState52.VUPVAL ? LexState52.VUPVAL : LexState52.VLOCAL; t.k = LexState52.VINDEXED; } @@ -930,7 +922,7 @@ private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { this.freeexp(e2); this.freeexp(e1); } - e1.u.s.info = this.codeABC(op, 0, o1, o2); + e1.u.info = this.codeABC(op, 0, o1, o2); e1.k = LexState52.VRELOCABLE; } } @@ -947,7 +939,7 @@ private void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) thro o2 = temp; /* o1 <==> o2 */ cond = 1; } - e1.u.s.info = this.condjump(op, cond, o1, o2); + e1.u.info = this.condjump(op, cond, o1, o2); e1.k = LexState52.VJMP; } @@ -1030,11 +1022,11 @@ void posfix(int op, expdesc e1, expdesc e2) throws CompileException { this.exp2val(e2); if (e2.k == LexState52.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { - _assert(e1.u.s.info == GETARG_B(this.getcode(e2)) - 1); + _assert(e1.u.info == GETARG_B(this.getcode(e2)) - 1); this.freeexp(e1); - SETARG_B(this.getcodePtr(e2), e1.u.s.info); + SETARG_B(this.getcodePtr(e2), e1.u.info); e1.k = LexState52.VRELOCABLE; - e1.u.s.info = e2.u.s.info; + e1.u.info = e2.u.info; } else { this.exp2nextreg(e2); /* operand must be on the 'stack' */ this.codearith(OP_CONCAT, e1, e2); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index 71c4977e..7b9a1a95 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -111,7 +111,6 @@ public static boolean isReservedKeyword(String varName) { VKNUM = 5, /* nval = numerical value */ VLOCAL = 6, /* info = local register */ VUPVAL = 7, /* info = index of upvalue in `upvalues' */ - VGLOBAL = 8, /* info = index of table, aux = index of global name in `k' */ VINDEXED = 9, /* info = table register, aux = index register (or `k') */ VJMP = 10, /* info = instruction pc */ VRELOCABLE = 11, /* info = instruction pc */ @@ -313,7 +312,7 @@ private LuaString newString(String s) { private void inclineNumber() throws CompileException { int old = current; - LuaC._assert(currIsNewline()); + LuaC._assert(currIsNewline(), linenumber); nextChar(); /* skip '\n' or '\r' */ if (currIsNewline() && current != old) { nextChar(); /* skip '\n\r' or '\r\n' */ @@ -392,7 +391,7 @@ private void str2d(String str, SemInfo seminfo) throws CompileException { } private void read_numeral(SemInfo seminfo) throws CompileException { - LuaC._assert(isDigit(current)); + LuaC._assert(isDigit(current), linenumber); int first = current; save_and_next(); @@ -417,7 +416,7 @@ private void read_numeral(SemInfo seminfo) throws CompileException { private int skip_sep() throws CompileException { int count = 0; int s = current; - LuaC._assert(s == '[' || s == ']'); + LuaC._assert(s == '[' || s == ']', linenumber); save_and_next(); while (current == '=') { save_and_next(); @@ -738,7 +737,7 @@ private int llex(SemInfo seminfo) throws CompileException { } default: { if (isSpace(current)) { - LuaC._assert(!currIsNewline()); + LuaC._assert(!currIsNewline(), linenumber); nextChar(); } else if (isDigit(current)) { read_numeral(seminfo); @@ -777,7 +776,7 @@ void nextToken() throws CompileException { } private void lookahead() throws CompileException { - LuaC._assert(lookahead.token == TK_EOS); + LuaC._assert(lookahead.token == TK_EOS, linenumber); lookahead.token = llex(lookahead.seminfo); } @@ -795,10 +794,13 @@ static class expdesc { static class U { // originally a union static class S { - int info, aux; + int idx; + int t; + int vt; } - final S s = new S(); + final S ind = new S(); + int info; private LuaValue _nval; public void setNval(LuaValue r) { @@ -806,7 +808,7 @@ public void setNval(LuaValue r) { } public LuaValue nval() { - return _nval == null ? LuaInteger.valueOf(s.info) : _nval; + return _nval == null ? LuaInteger.valueOf(info) : _nval; } } @@ -818,7 +820,7 @@ void init(int k, int i) { this.f.i = NO_JUMP; this.t.i = NO_JUMP; this.k = k; - this.u.s.info = i; + this.u.info = i; } boolean hasjumps() { @@ -832,8 +834,10 @@ boolean isnumeral() { public void setvalue(expdesc other) { this.k = other.k; this.u._nval = other.u._nval; - this.u.s.info = other.u.s.info; - this.u.s.aux = other.u.s.aux; + this.u.info = other.u.info; + this.u.ind.idx = other.u.ind.idx; + this.u.ind.t = other.u.ind.t; + this.u.ind.vt = other.u.ind.vt; this.t.i = other.t.i; this.f.i = other.f.i; } @@ -955,11 +959,12 @@ void removevars(int tolevel) { private void singlevar(expdesc var) throws CompileException { LuaString varname = str_checkname(); FuncState52 fs = this.fs; - if (fs.singlevaraux(varname, var, 1) == VGLOBAL) { + if (fs.singlevaraux(varname, var, 1) == VVOID) { expdesc key = new expdesc(); fs.singlevaraux(LuaString.valueOf("_ENV"), var, 1); + LuaC._assert(var.k == LexState52.VLOCAL || var.k == LexState52.VUPVAL, linenumber); codestring(key, varname); - // todo + fs.indexed(var, key); } } @@ -1000,28 +1005,28 @@ private void leavelevel() { nCcalls--; } - private void pushclosure(FuncState52 func, expdesc v) throws CompileException { - FuncState52 fs = this.fs; + Prototype addprototype() { + Prototype clp; Prototype f = fs.f; - if (f.p == null || fs.np + 1 > f.p.length) { + if (f.p == null || fs.np >= f.p.length) { f.p = LuaC.realloc(f.p, fs.np * 2 + 1); } - f.p[fs.np++] = func.f; - v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1)); - for (int i = 0; i < func.f.nups; i++) { - int o = func.upvalues[i].k == VLOCAL ? Lua.OP_MOVE - : Lua.OP_GETUPVAL; - fs.codeABC(o, 0, func.upvalues[i].info, 0); - } + f.p[fs.np++] = clp = new Prototype(); + return clp; + } + + private void codeclosure(expdesc v) throws CompileException { + FuncState52 fs = this.fs.prev; + v.init(LexState52.VRELOCABLE, fs.codeABx(Lua52.OP_CLOSURE, 0, fs.np - 1)); + fs.exp2nextreg(v); } void open_func(FuncState52 fs) { - Prototype f = new Prototype(); + Prototype f = fs.f; if (this.fs != null) { f.source = this.fs.f.source; } - f.isLua52 = false; // temporary! - fs.f = f; + f.isLua52 = true; fs.prev = this.fs; /* linked list of FuncState52s */ fs.ls = this; this.fs = fs; @@ -1053,7 +1058,7 @@ void close_func() throws CompileException { // f.sizelocvars = fs.nlocvars; f.upvalues = LuaC.realloc(f.upvalues, f.nups); // LuaC._assert (CheckCode.checkcode(f)); - LuaC._assert(fs.bl == null); + LuaC._assert(fs.bl == null, linenumber); this.fs = fs.prev; // L.top -= 2; /* remove table and prototype from the stack */ // /* last token read was anchored in defunct function; must reanchor it @@ -1118,7 +1123,7 @@ private void recfield(ConsControl cc) throws CompileException { this.checknext('='); rkkey = fs.exp2RK(key); this.expr(val); - fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val)); + fs.codeABC(Lua52.OP_SETTABLE, cc.t.u.info, rkkey, fs.exp2RK(val)); fs.freereg = reg; /* free registers */ } @@ -1134,7 +1139,7 @@ private void constructor(expdesc t) throws CompileException { /* constructor -> ?? */ FuncState52 fs = this.fs; int line = this.linenumber; - int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0); + int pc = fs.codeABC(Lua52.OP_NEWTABLE, 0, 0, 0); ConsControl cc = new ConsControl(); cc.na = cc.nh = cc.tostore = 0; cc.t = t; @@ -1143,7 +1148,7 @@ private void constructor(expdesc t) throws CompileException { fs.exp2nextreg(t); /* fix it at stack top (for gc) */ this.checknext('{'); do { - LuaC._assert(cc.v.k == VVOID || cc.tostore > 0); + LuaC._assert(cc.v.k == VVOID || cc.tostore > 0, linenumber); if (this.t.token == '}') { break; } @@ -1214,9 +1219,9 @@ private void parlist() throws CompileException { if (LUA_COMPAT_VARARG) { /* use `arg' as default name */ this.new_localvarliteral("arg", nparams++); - f.is_vararg = Lua.VARARG_HASARG | Lua.VARARG_NEEDSARG; + f.is_vararg = Lua52.VARARG_HASARG | Lua52.VARARG_NEEDSARG; } - f.is_vararg |= Lua.VARARG_ISVARARG; + f.is_vararg |= Lua52.VARARG_ISVARARG; break; } default: @@ -1225,7 +1230,7 @@ private void parlist() throws CompileException { } while (f.is_vararg == 0 && this.testnext(',')); } this.adjustlocalvars(nparams); - f.numparams = fs.nactvar - (f.is_vararg & Lua.VARARG_HASARG); + f.numparams = fs.nactvar - (f.is_vararg & Lua52.VARARG_HASARG); fs.reserveregs(fs.nactvar); /* reserve register for parameters */ } @@ -1233,8 +1238,9 @@ private void parlist() throws CompileException { private void body(expdesc e, boolean needself, int line) throws CompileException { /* body -> `(' parlist `)' chunk END */ FuncState52 new_fs = new FuncState52(); - open_func(new_fs); + new_fs.f = this.addprototype(); new_fs.f.linedefined = line; + open_func(new_fs); this.checknext('('); if (needself) { new_localvarliteral("self", 0); @@ -1245,8 +1251,8 @@ private void body(expdesc e, boolean needself, int line) throws CompileException this.chunk(); new_fs.f.lastlinedefined = this.linenumber; this.check_match(TK_END, TK_FUNCTION, line); + this.codeclosure(e); this.close_func(); - this.pushclosure(new_fs, e); } private int explist1(expdesc v) throws CompileException { @@ -1295,17 +1301,17 @@ private void funcargs(expdesc f) throws CompileException { throw syntaxError("function arguments expected"); } } - LuaC._assert(f.k == VNONRELOC); - base = f.u.s.info; /* base register for call */ + LuaC._assert(f.k == VNONRELOC, linenumber); + base = f.u.info; /* base register for call */ if (hasmultret(args.k)) { - nparams = Lua.LUA_MULTRET; /* open call */ + nparams = Lua52.LUA_MULTRET; /* open call */ } else { if (args.k != VVOID) { fs.exp2nextreg(args); /* close last argument */ } nparams = fs.freereg - (base + 1); } - f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2)); + f.init(VCALL, fs.codeABC(Lua52.OP_CALL, base, nparams + 1, 2)); fs.fixline(line); fs.freereg = base + 1; /* call remove function and arguments and leaves * (unless changed) one result */ @@ -1413,8 +1419,8 @@ private void simpleexp(expdesc v) throws CompileException { FuncState52 fs = this.fs; this.check_condition(fs.f.is_vararg != 0, "cannot use " + LUA_QL("...") + " outside a vararg function"); - fs.f.is_vararg &= ~Lua.VARARG_NEEDSARG; /* don't need 'arg' */ - v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0)); + fs.f.is_vararg &= ~Lua52.VARARG_NEEDSARG; /* don't need 'arg' */ + v.init(VVARARG, fs.codeABC(Lua52.OP_VARARG, 0, 1, 0)); break; } case '{': { /* constructor */ @@ -1575,7 +1581,7 @@ private void block() throws CompileException { FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); fs.enterblock(bl, false); this.chunk(); - LuaC._assert(bl.breaklist.i == NO_JUMP); + LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); fs.leaveblock(); } @@ -1603,18 +1609,19 @@ private void check_conflict(LHS_assign lh, expdesc v) throws CompileException { boolean conflict = false; for (; lh != null; lh = lh.prev) { if (lh.v.k == VINDEXED) { - if (lh.v.u.s.info == v.u.s.info) { /* conflict? */ + if (lh.v.u.ind.vt == v.k && lh.v.u.ind.t == v.u.info) { /* conflict? */ conflict = true; - lh.v.u.s.info = extra; /* previous assignment will use safe copy */ + lh.v.u.ind.vt = VLOCAL; + lh.v.u.ind.t = extra; /* previous assignment will use safe copy */ } - if (lh.v.u.s.aux == v.u.s.info) { /* conflict? */ + if (v.k == VLOCAL && lh.v.u.ind.idx == v.u.info) { /* conflict? */ conflict = true; - lh.v.u.s.aux = extra; /* previous assignment will use safe copy */ + lh.v.u.ind.idx = extra; /* previous assignment will use safe copy */ } } } if (conflict) { - fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */ + fs.codeABC(Lua52.OP_MOVE, fs.freereg, v.u.info, 0); /* make copy */ fs.reserveregs(1); } } @@ -1678,7 +1685,7 @@ private void breakstat() throws CompileException { throw syntaxError("no loop to break"); } if (upval) { - fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0); + fs.codeABC(Lua52.OP_CLOSE, bl.nactvar, 0, 0); } fs.concat(bl.breaklist, fs.jump()); } @@ -1745,17 +1752,22 @@ private void forbody(int base, int line, int nvars, boolean isnum) throws Compil int prep, endfor; this.adjustlocalvars(3); /* control variables */ this.checknext(TK_DO); - prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump(); + prep = isnum ? fs.codeAsBx(Lua52.OP_FORPREP, base, NO_JUMP) : fs.jump(); fs.enterblock(bl, false); /* scope for declared variables */ this.adjustlocalvars(nvars); fs.reserveregs(nvars); this.block(); fs.leaveblock(); /* end of scope for declared variables */ fs.patchtohere(prep); - endfor = isnum ? fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP) : fs - .codeABC(Lua.OP_TFORLOOP, base, 0, nvars); - fs.fixline(line); /* pretend that `Lua.OP_FOR' starts the loop */ - fs.patchlist(isnum ? endfor : fs.jump(), prep + 1); + if (isnum) /* numeric for? */ + endfor = fs.codeAsBx(Lua52.OP_FORLOOP, base, NO_JUMP); + else { /* generic for */ + fs.codeABC(Lua52.OP_TFORCALL, base, 0, nvars); + fs.fixline(line); + endfor = fs.codeAsBx(Lua52.OP_TFORLOOP, base + 2, NO_JUMP); + } + fs.patchlist(endfor, prep + 1); + fs.fixline(line); /* pretend that `Lua52.OP_FOR' starts the loop */ } @@ -1774,7 +1786,7 @@ private void fornum(LuaString varname, int line) throws CompileException { if (this.testnext(',')) { this.exp1(); /* optional step */ } else { /* default step = 1 */ - fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); + fs.codeABx(Lua52.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); fs.reserveregs(1); } this.forbody(base, line, 1, true); @@ -1952,18 +1964,18 @@ private void retstat() throws CompileException { if (hasmultret(e.k)) { fs.setmultret(e); if (e.k == VCALL && nret == 1) { /* tail call? */ - LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); - LuaC._assert(Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); + LuaC.SET_OPCODE(fs.getcodePtr(e), Lua52.OP_TAILCALL); + LuaC._assert(Lua52.GETARG_A(fs.getcode(e)) == fs.nactvar, linenumber); } first = fs.nactvar; - nret = Lua.LUA_MULTRET; /* return all values */ + nret = Lua52.LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ { first = fs.exp2anyreg(e); } else { fs.exp2nextreg(e); /* values must go to the `stack' */ first = fs.nactvar; /* return all `active' values */ - LuaC._assert(nret == fs.freereg - first); + LuaC._assert(nret == fs.freereg - first, linenumber); } } } @@ -2033,7 +2045,7 @@ void chunk() throws CompileException { islast = this.statement(); this.testnext(';'); LuaC._assert(this.fs.f.maxstacksize >= this.fs.freereg - && this.fs.freereg >= this.fs.nactvar); + && this.fs.freereg >= this.fs.nactvar, linenumber); this.fs.freereg = this.fs.nactvar; /* free registers */ } this.leavelevel(); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index bd549a48..b6ac16b4 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -71,10 +71,12 @@ public class LuaC implements LuaCompiler { public static final LuaC INSTANCE = new LuaC(); - protected static void _assert(boolean b) throws CompileException { + protected static void _assert(boolean b) throws CompileException { _assert(b, 0); } + + protected static void _assert(boolean b, int line) throws CompileException { if (!b) { // So technically this should fire a runtime exception but... - System.err.println("compiler assert failed\nstack trace:"); + System.err.println("compiler assert failed on line " + line + "\nstack trace:"); for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { System.err.println(ste); } @@ -85,6 +87,7 @@ protected static void _assert(boolean b) throws CompileException { public static final int MAXSTACK = 250; public static final int LUAI_MAXUPVALUES = 60; public static final int LUAI_MAXVARS = 200; + public static boolean isLua52 = true; public static void SET_OPCODE(InstructionPtr i, int o) { @@ -215,7 +218,7 @@ public static Prototype compile(InputStream stream, LuaString name, LuaString mo return LoadState.loadBinaryChunk(firstByte, stream, name); } else { checkMode(mode, "text"); - return luaY_parser(firstByte, stream, name); + return isLua52 ? luaY_parser52(firstByte, stream, name) : luaY_parser(firstByte, stream, name); } } @@ -228,26 +231,38 @@ private static Prototype luaY_parser(int firstByte, InputStream z, LuaString nam // lexstate.buff = buff; lexstate.setinput(firstByte, z, name); lexstate.open_func(funcstate); - funcstate.f.isLua52 = false; // temporary! /* main func. is always vararg */ funcstate.f.is_vararg = Lua.VARARG_ISVARARG; funcstate.f.source = name; - if (funcstate.f.isLua52) { - LexState.expdesc v = new LexState.expdesc(); - v.init(LexState.VLOCAL, 0); - funcstate.f.upvalues = new LuaString[1]; - funcstate.f.upvalues[0] = LuaString.valueOf("_ENV"); - funcstate.f.nups = 1; - funcstate.upvalues[0] = funcstate.new upvaldesc(); - funcstate.upvalues[0].k = LexState.VLOCAL; - funcstate.upvalues[0].info = (short) v.u.s.info; - } lexstate.nextToken(); /* read first token */ lexstate.chunk(); lexstate.check(LexState.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); - LuaC._assert(funcstate.f.nups == (funcstate.f.isLua52 ? 1 : 0)); + LuaC._assert(funcstate.f.nups == 0); + LuaC._assert(lexstate.fs == null); + return funcstate.f; + } + + private static Prototype luaY_parser52(int firstByte, InputStream z, LuaString name) throws CompileException { + LexState52 lexstate = new LexState52(z); + FuncState52 funcstate = new FuncState52(); + // lexstate.buff = buff; + lexstate.setinput(firstByte, z, name); + funcstate.f = new Prototype(); + lexstate.open_func(funcstate); + /* main func. is always vararg */ + funcstate.f.is_vararg = Lua.VARARG_ISVARARG; + funcstate.f.source = name; + LexState52.expdesc v = new LexState52.expdesc(); + v.init(LexState52.VLOCAL, 0); + funcstate.newupvalue(LuaString.valueOf("_ENV"), v); + lexstate.nextToken(); /* read first token */ + lexstate.chunk(); + lexstate.check(LexState.TK_EOS); + lexstate.close_func(); + LuaC._assert(funcstate.prev == null); + LuaC._assert(funcstate.f.nups == 1); LuaC._assert(lexstate.fs == null); return funcstate.f; } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 3948b1db..5df9cf78 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -685,6 +685,7 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, newp.isLua52 ? null : (LuaTable)upvalues[0].getValue()); if (newp.isLua52) { for (int j = 0; j < newp.nups; ++j) { + int b = newp.upvalue_info[j]; if ((b >> 8) != 0) { DebugFrame frame = di; diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 1d69e0c7..365c083b 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -27,6 +27,7 @@ import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode; import org.squiddev.cobalt.*; import org.squiddev.cobalt.compiler.LoadState; +import org.squiddev.cobalt.compiler.LuaC; import org.squiddev.cobalt.debug.DebugFrame; import org.squiddev.cobalt.debug.DebugHandler; import org.squiddev.cobalt.debug.DebugState; @@ -112,7 +113,7 @@ public LuaValue add(LuaState state, LuaTable env) { next = env.rawget("next"); inext = env.rawget("__inext"); - env.rawset("_VERSION", valueOf("Lua 5.1")); + env.rawset("_VERSION", valueOf(LuaC.isLua52 ? "Lua 5.2" : "Lua 5.1")); return env; } From af40c7adef4aeeb88fd798393c4e46a555e451d3 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 01:27:03 -0400 Subject: [PATCH 06/24] Added goto statement & labels This should theoretically be the last thing to implement. I think there's also something with weak tables to fix, as well as adding tail call hooks, but those are much more minor details. If you don't like goto, you can disable it with LuaC.blockGoto. --- .../squiddev/cobalt/compiler/FuncState52.java | 115 +++++++--- .../squiddev/cobalt/compiler/LexState52.java | 207 ++++++++++++++---- .../org/squiddev/cobalt/compiler/LuaC.java | 24 +- .../java/org/squiddev/cobalt/lib/BaseLib.java | 4 - .../org/squiddev/cobalt/lib/CoroutineLib.java | 1 + 5 files changed, 277 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java index d95217c5..243e7f5f 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java @@ -34,6 +34,7 @@ import static org.squiddev.cobalt.Constants.*; import static org.squiddev.cobalt.Lua52.*; +import static org.squiddev.cobalt.compiler.LexState52.NO_JUMP; import static org.squiddev.cobalt.compiler.LuaC.*; public class FuncState52 { @@ -45,10 +46,11 @@ class upvaldesc { static class BlockCnt { BlockCnt previous; /* chain */ - IntPtr breaklist = new IntPtr(); /* list of jumps out of this loop */ + short firstlabel; /* index of first label in this block */ + short firstgoto; /* index of first pending goto in this block */ short nactvar; /* # active locals outside the breakable structure */ boolean upval; /* true if some variable in the block is an upvalue */ - boolean isbreakable; /* true if `block' is a loop */ + boolean isloop; /* true if `block' is a loop */ } Prototype f; /* current function header */ @@ -188,10 +190,11 @@ int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { } } - void enterblock(BlockCnt bl, boolean isbreakable) throws CompileException { - bl.breaklist.i = LexState52.NO_JUMP; - bl.isbreakable = isbreakable; + void enterblock(BlockCnt bl, boolean isloop) throws CompileException { + bl.isloop = isloop; bl.nactvar = this.nactvar; + bl.firstlabel = ls.dyd.nlabel; + bl.firstgoto = ls.dyd.ngt; bl.upval = false; bl.previous = this.bl; this.bl = bl; @@ -214,16 +217,30 @@ void enterblock(BlockCnt bl, boolean isbreakable) throws CompileException { void leaveblock() throws CompileException { BlockCnt bl = this.bl; + if (bl.previous != null && bl.upval) { + /* create a 'jump to here' to close upvalues */ + int j = jump(); + patchclose(j, bl.nactvar); + patchtohere(j); + } + if (bl.isloop) { + ls.breaklabel(); + } this.bl = bl.previous; ls.removevars(bl.nactvar); /*if (bl.upval) { this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); }*/ /* a block either controls scope or breaks (never both) */ - _assert(!bl.isbreakable || !bl.upval); + _assert(!bl.isloop || !bl.upval); _assert(bl.nactvar == this.nactvar); this.freereg = this.nactvar; /* free registers */ - this.patchtohere(bl.breaklist.i); + ls.dyd.nlabel = bl.firstlabel; + if (bl.previous != null) { + movegotosout(bl); + } else if (bl.firstgoto < ls.dyd.ngt) { + ls.undefgoto(ls.dyd.gt[bl.firstgoto]); + } } void closelistfield(ConsControl cc) throws CompileException { @@ -289,8 +306,8 @@ void nil(int from, int n) throws CompileException { int jump() throws CompileException { int jpc = this.jpc.i; /* save list of jumps to here */ - this.jpc.i = LexState52.NO_JUMP; - IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState52.NO_JUMP)); + this.jpc.i = NO_JUMP; + IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, NO_JUMP)); this.concat(j, jpc); /* keep them on hold */ return j.i; } @@ -307,7 +324,7 @@ private int condjump(int /* OpCode */op, int A, int B, int C) throws CompileExce private void fixjump(int pc, int dest) throws CompileException { InstructionPtr jmp = new InstructionPtr(this.f.code, pc); int offset = dest - (pc + 1); - _assert(dest != LexState52.NO_JUMP); + _assert(dest != NO_JUMP); if (Math.abs(offset) > MAXARG_sBx) { throw ls.syntaxError("control structure too long"); } @@ -328,9 +345,9 @@ int getlabel() { private int getjump(int pc) { int offset = GETARG_sBx(this.f.code[pc]); /* point to itself represents end of list */ - if (offset == LexState52.NO_JUMP) + if (offset == NO_JUMP) /* end of list */ { - return LexState52.NO_JUMP; + return NO_JUMP; } else /* turn offset into absolute position */ { return (pc + 1) + offset; @@ -353,7 +370,7 @@ private InstructionPtr getjumpcontrol(int pc) { * produce an inverted value) */ private boolean need_value(int list) { - for (; list != LexState52.NO_JUMP; list = this.getjump(list)) { + for (; list != NO_JUMP; list = this.getjump(list)) { int i = this.getjumpcontrol(list).get(); if (GET_OPCODE(i) != OP_TESTSET) { return true; @@ -381,13 +398,13 @@ private boolean patchtestreg(int node, int reg) { private void removevalues(int list) { - for (; list != LexState52.NO_JUMP; list = this.getjump(list)) { + for (; list != NO_JUMP; list = this.getjump(list)) { this.patchtestreg(list, NO_REG); } } private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws CompileException { - while (list != LexState52.NO_JUMP) { + while (list != NO_JUMP) { int next = this.getjump(list); if (this.patchtestreg(list, reg)) { this.fixjump(list, vtarget); @@ -400,7 +417,7 @@ private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws Co private void dischargejpc() throws CompileException { this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); - this.jpc.i = LexState52.NO_JUMP; + this.jpc.i = NO_JUMP; } void patchlist(int list, int target) throws CompileException { @@ -412,21 +429,33 @@ void patchlist(int list, int target) throws CompileException { } } + void patchclose(int list, int level) throws CompileException { + level++; /* argument is +1 to reserve 0 as non-op */ + while (list != NO_JUMP) { + int next = getjump(list); + LuaC._assert(GET_OPCODE(f.code[list]) == OP_JMP && + GETARG_A(f.code[list]) == 0 || + GETARG_A(f.code[list]) >= level); + f.code[list] = (f.code[list] & MASK_NOT_A) | (level << POS_A); + list = next; + } + } + void patchtohere(int list) throws CompileException { this.getlabel(); this.concat(this.jpc, list); } void concat(IntPtr l1, int l2) throws CompileException { - if (l2 == LexState52.NO_JUMP) { + if (l2 == NO_JUMP) { return; } - if (l1.i == LexState52.NO_JUMP) { + if (l1.i == NO_JUMP) { l1.i = l2; } else { int list = l1.i; int next; - while ((next = this.getjump(list)) != LexState52.NO_JUMP) + while ((next = this.getjump(list)) != NO_JUMP) /* find last element */ { list = next; } @@ -613,10 +642,10 @@ private void exp2reg(expdesc e, int reg) throws CompileException { } if (e.hasjumps()) { int _final; /* position after whole expression */ - int p_f = LexState52.NO_JUMP; /* position of an eventual LOAD false */ - int p_t = LexState52.NO_JUMP; /* position of an eventual LOAD true */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (this.need_value(e.t.i) || this.need_value(e.f.i)) { - int fj = (e.k == LexState52.VJMP) ? LexState52.NO_JUMP : this + int fj = (e.k == LexState52.VJMP) ? NO_JUMP : this .jump(); p_f = this.code_label(reg, 0, 1); p_t = this.code_label(reg, 1, 0); @@ -626,7 +655,7 @@ private void exp2reg(expdesc e, int reg) throws CompileException { this.patchlistaux(e.f.i, _final, reg, p_f); this.patchlistaux(e.t.i, _final, reg, p_t); } - e.f.i = e.t.i = LexState52.NO_JUMP; + e.f.i = e.t.i = NO_JUMP; e.u.info = reg; e.k = LexState52.VNONRELOC; } @@ -762,7 +791,7 @@ void goiftrue(expdesc e) throws CompileException { case LexState52.VK: case LexState52.VKNUM: case LexState52.VTRUE: { - pc = LexState52.NO_JUMP; /* always true; do nothing */ + pc = NO_JUMP; /* always true; do nothing */ break; } case LexState52.VFALSE: { @@ -781,7 +810,7 @@ void goiftrue(expdesc e) throws CompileException { } this.concat(e.f, pc); /* insert last jump in `f' list */ this.patchtohere(e.t.i); - e.t.i = LexState52.NO_JUMP; + e.t.i = NO_JUMP; } private void goiffalse(expdesc e) throws CompileException { @@ -790,7 +819,7 @@ private void goiffalse(expdesc e) throws CompileException { switch (e.k) { case LexState52.VNIL: case LexState52.VFALSE: { - pc = LexState52.NO_JUMP; /* always false; do nothing */ + pc = NO_JUMP; /* always false; do nothing */ break; } case LexState52.VTRUE: { @@ -808,7 +837,7 @@ private void goiffalse(expdesc e) throws CompileException { } this.concat(e.t, pc); /* insert last jump in `t' list */ this.patchtohere(e.f.i); - e.f.i = LexState52.NO_JUMP; + e.f.i = NO_JUMP; } private void codenot(expdesc e) throws CompileException { @@ -1003,7 +1032,7 @@ void infix(int /* BinOpr */op, expdesc v) throws CompileException { void posfix(int op, expdesc e1, expdesc e2) throws CompileException { switch (op) { case LexState52.OPR_AND: { - _assert(e1.t.i == LexState52.NO_JUMP); /* list must be closed */ + _assert(e1.t.i == NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.f, e1.f.i); // *e1 = *e2; @@ -1011,7 +1040,7 @@ void posfix(int op, expdesc e1, expdesc e2) throws CompileException { break; } case LexState52.OPR_OR: { - _assert(e1.f.i == LexState52.NO_JUMP); /* list must be closed */ + _assert(e1.f.i == NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.t, e1.t.i); // *e1 = *e2; @@ -1125,4 +1154,32 @@ private void setlist(int base, int nelems, int tostore) throws CompileException } this.freereg = base + 1; /* free registers with list values */ } + + private void movegotosout(BlockCnt bl) throws CompileException { + int i = bl.firstgoto; + LexState52.LabelDescription[] gl = ls.dyd.gt; + /* correct pending gotos to current block and try to close it + with visible labels */ + while (i < ls.dyd.ngt) { + LexState52.LabelDescription gt = gl[i]; + if (gt.nactvar > bl.nactvar) { + if (bl.upval) { + patchclose(gt.pc, bl.nactvar); + } + gt.nactvar = bl.nactvar; + } + if (!ls.findlabel(i)) { + i++; /* move to the next one */ + } + } + } + + void checkrepeated(LexState52.LabelDescription[] ll, int nll, LuaString label) throws CompileException { + for (int i = bl.firstlabel; i < nll; i++) { + if (label.equals(ll[i].name)) { + throw new CompileException(String.format("label %s already defined on line %d", + label.toString(), ll[i].line)); + } + } + } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index 7b9a1a95..fd0576b4 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -24,10 +24,10 @@ */ package org.squiddev.cobalt.compiler; +import com.sun.deploy.xml.XMLable; import org.squiddev.cobalt.*; import org.squiddev.cobalt.function.LocalVariable; import org.squiddev.cobalt.lib.Utf8Lib; -import org.squiddev.cobalt.compiler.FuncState52; import java.io.IOException; import java.io.InputStream; @@ -148,14 +148,15 @@ public void set(Token other) { private byte decpoint; /* locale decimal point */ public int nCcalls; private final HashMap strings = new HashMap<>(); + DynamicData dyd; /* ORDER RESERVED */ private final static String[] luaX_tokens = { "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "if", + "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", + "..", "...", "==", ">=", "<=", "~=", "::", "", "", "", "", }; @@ -163,13 +164,13 @@ public void set(Token other) { final static int // terminal symbols denoted by reserved words TK_AND = 257, TK_BREAK = 258, TK_DO = 259, TK_ELSE = 260, TK_ELSEIF = 261, - TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_IF = 266, - TK_IN = 267, TK_LOCAL = 268, TK_NIL = 269, TK_NOT = 270, TK_OR = 271, TK_REPEAT = 272, - TK_RETURN = 273, TK_THEN = 274, TK_TRUE = 275, TK_UNTIL = 276, TK_WHILE = 277, + TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_GOTO = 266, TK_IF = 267, + 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 = 278, TK_DOTS = 279, TK_EQ = 280, TK_GE = 281, TK_LE = 282, TK_NE = 283, - TK_EOS = 284, - TK_NUMBER = 285, TK_NAME = 286, TK_STRING = 287; + TK_CONCAT = 279, TK_DOTS = 280, TK_EQ = 281, TK_GE = 282, TK_LE = 283, TK_NE = 284, TK_DBCOLON = 285, + TK_EOS = 286, + TK_NUMBER = 287, TK_NAME = 288, TK_STRING = 289; private final static int FIRST_RESERVED = TK_AND; private final static int NUM_RESERVED = TK_WHILE + 1 - FIRST_RESERVED; @@ -712,6 +713,15 @@ private int llex(SemInfo seminfo) throws CompileException { return TK_NE; } } + case ':': { + nextChar(); + if (current != ':') { + return ':'; + } else { + nextChar(); + return TK_DBCOLON; + } + } case '"': case '\'': { read_string(current, seminfo); @@ -843,6 +853,28 @@ public void setvalue(expdesc other) { } } + static class LabelDescription { + LuaString name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + short nactvar; /* local level where it appears in current block */ + } + + static class DynamicData { + short[] actvar; + int nactvar; + LabelDescription[] gt; + short ngt; + LabelDescription[] label; + short nlabel; + + DynamicData() { + actvar = null; nactvar = 0; + gt = null; ngt = 0; + label = null; nlabel = 0; + } + } + private boolean hasmultret(int k) { return k == VCALL || k == VVARARG; } @@ -1005,6 +1037,89 @@ private void leavelevel() { nCcalls--; } + private void closegoto(int g, LabelDescription label) throws CompileException { + LabelDescription gt = dyd.gt[g]; + LuaC._assert(gt.name.equals(label.name), linenumber); + if (gt.nactvar < label.nactvar) { + throw new CompileException(String.format(" at line %d jumps into the scope of local %s", + gt.name.toString(), gt.line, fs.getlocvar(gt.nactvar).name.toString())); + } + fs.patchlist(gt.pc, label.pc); + for (int i = g; i < dyd.ngt - 1; i++) { + dyd.gt[i] = dyd.gt[i+1]; + } + dyd.ngt--; + } + + boolean findlabel(int g) throws CompileException { + LabelDescription gt = dyd.gt[g]; + for (int i = fs.bl.firstlabel; i < dyd.nlabel; i++) { + LabelDescription lb = dyd.label[i]; + if (lb.name.equals(gt.name)) { + if (gt.nactvar > lb.nactvar && (fs.bl.upval || dyd.nlabel > fs.bl.firstlabel)) { + fs.patchclose(gt.pc, lb.nactvar); + } + closegoto(g, lb); + return true; + } + } + return false; + } + + private int newlabelentry(DynamicData dyd, boolean islabel, LuaString name, int line, int pc) { + if (islabel) { + if (dyd.label == null || dyd.label.length < dyd.nlabel + 1) { + dyd.label = LuaC.realloc(dyd.label, dyd.nlabel * 2 + 1); + } + dyd.label[dyd.nlabel] = new LabelDescription(); + dyd.label[dyd.nlabel].name = name; + dyd.label[dyd.nlabel].line = line; + dyd.label[dyd.nlabel].nactvar = fs.nactvar; + dyd.label[dyd.nlabel].pc = pc; + return dyd.nlabel++; + } else { + if (dyd.gt == null || dyd.gt.length < dyd.ngt + 1) { + dyd.gt = LuaC.realloc(dyd.gt, dyd.ngt * 2 + 1); + } + dyd.gt[dyd.ngt] = new LabelDescription(); + dyd.gt[dyd.ngt].name = name; + dyd.gt[dyd.ngt].line = line; + dyd.gt[dyd.ngt].nactvar = fs.nactvar; + dyd.gt[dyd.ngt].pc = pc; + return dyd.ngt++; + } + } + + private void findgotos(LabelDescription lb) throws CompileException { + LabelDescription[] gl = dyd.gt; + int i = fs.bl.firstgoto; + while (i < dyd.ngt) { + if (gl[i].name.equals(lb.name)) { + closegoto(i, lb); + } else { + i++; + } + } + } + + /* + ** create a label named "break" to resolve break statements + */ + void breaklabel() throws CompileException { + int l = newlabelentry(dyd, true, LuaString.valueOf("break"), 0, fs.pc); + findgotos(dyd.label[l]); + } + + /* + ** generates an error for an undefined 'goto'; choose appropriate + ** message when label name is a reserved word (which can only be 'break') + */ + void /* no return */ undefgoto(LabelDescription gt) throws CompileException { + String msg = String.format(isReservedKeyword(gt.name.toString()) ? "<%s> at line %d not inside a loop" : "no visible label %s for at line %d", + gt.name.toString(), gt.line); + throw new CompileException(msg); + } + Prototype addprototype() { Prototype clp; Prototype f = fs.f; @@ -1021,7 +1136,7 @@ private void codeclosure(expdesc v) throws CompileException { fs.exp2nextreg(v); } - void open_func(FuncState52 fs) { + void open_func(FuncState52 fs, FuncState52.BlockCnt bl) throws CompileException { Prototype f = fs.f; if (this.fs != null) { f.source = this.fs.f.source; @@ -1042,6 +1157,7 @@ void open_func(FuncState52 fs) { f.maxstacksize = 2; /* registers 0/1 are always valid */ //fs.h = new LTable(); fs.htable = new Hashtable<>(); + fs.enterblock(bl, false); } void close_func() throws CompileException { @@ -1049,6 +1165,7 @@ void close_func() throws CompileException { Prototype f = fs.f; this.removevars(0); fs.ret(0, 0); /* final return */ + fs.leaveblock(); f.code = LuaC.realloc(f.code, fs.pc); f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); // f.sizelineinfo = fs.pc; @@ -1238,9 +1355,10 @@ private void parlist() throws CompileException { private void body(expdesc e, boolean needself, int line) throws CompileException { /* body -> `(' parlist `)' chunk END */ FuncState52 new_fs = new FuncState52(); + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); new_fs.f = this.addprototype(); new_fs.f.linedefined = line; - open_func(new_fs); + open_func(new_fs, bl); this.checknext('('); if (needself) { new_localvarliteral("self", 0); @@ -1581,7 +1699,7 @@ private void block() throws CompileException { FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); fs.enterblock(bl, false); this.chunk(); - LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); + //LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); fs.leaveblock(); } @@ -1672,25 +1790,36 @@ private int cond() throws CompileException { return v.f.i; } - - private void breakstat() throws CompileException { - FuncState52 fs = this.fs; - FuncState52.BlockCnt bl = fs.bl; - boolean upval = false; - while (bl != null && !bl.isbreakable) { - upval |= bl.upval; - bl = bl.previous; + private void gotostat(int pc) throws CompileException { + LuaString label; + if (testnext(TK_GOTO)) { + label = str_checkname(); + } else { + this.nextToken(); + label = LuaString.valueOf("break"); } - if (bl == null) { - throw syntaxError("no loop to break"); + int g = newlabelentry(dyd, false, label, linenumber, pc); + findlabel(g); + } + + + + private void labelstat(LuaString label, int line) throws CompileException { + /* label -> '::' NAME '::' */ + fs.checkrepeated(dyd.label, dyd.nlabel, label); /* check for repeated labels */ + checknext(TK_DBCOLON); /* skip double colon */ + /* create new entry for this label */ + int l = newlabelentry(dyd, true, label, line, fs.pc); /* index of new label being created */ + while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ + statement(); } - if (upval) { - fs.codeABC(Lua52.OP_CLOSE, bl.nactvar, 0, 0); + if (block_follow(0)) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + dyd.label[l].nactvar = fs.bl.nactvar; } - fs.concat(bl.breaklist, fs.jump()); + findgotos(dyd.label[l]); } - private void whilestat(int line) throws CompileException { /* whilestat -> WHILE cond DO block END */ FuncState52 fs = this.fs; @@ -1722,15 +1851,11 @@ private void repeatstat(int line) throws CompileException { this.chunk(); this.check_match(TK_UNTIL, TK_REPEAT, line); condexit = this.cond(); /* read condition (inside scope block) */ - if (!bl2.upval) { /* no upvalues? */ - fs.leaveblock(); /* finish scope */ - fs.patchlist(condexit, repeat_init); /* close the loop */ - } else { /* complete semantics when there are upvalues */ - this.breakstat(); /* if condition then break */ - fs.patchtohere(condexit); /* else... */ - fs.leaveblock(); /* finish scope... */ - fs.patchlist(fs.jump(), repeat_init); /* and repeat */ + if (bl2.upval) { /* upvalues? */ + fs.patchclose(condexit, bl2.nactvar); } + fs.leaveblock(); /* finish scope */ + fs.patchlist(condexit, repeat_init); /* close the loop */ fs.leaveblock(); /* finish loop */ } @@ -2021,13 +2146,21 @@ private boolean statement() throws CompileException { } return false; } + case TK_DBCOLON: { /* stat -> label */ + this.nextToken(); + this.labelstat(str_checkname(), line); + return false; + } case TK_RETURN: { /* stat -> retstat */ this.retstat(); return true; /* must be last statement */ } - case TK_BREAK: { /* stat -> breakstat */ - this.nextToken(); /* skip BREAK */ - this.breakstat(); + case TK_BREAK: /* stat -> breakstat */ + case TK_GOTO: { /* stat -> 'goto' NAME */ + if (this.t.token == TK_GOTO && LuaC.blockGoto) { + throw lexError("illegal jump statement", this.t.token); + } + this.gotostat(fs.jump()); return true; /* must be last statement */ } default: { diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index b6ac16b4..b21f38ef 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -88,6 +88,7 @@ protected static void _assert(boolean b, int line) throws CompileException { public static final int LUAI_MAXUPVALUES = 60; public static final int LUAI_MAXVARS = 200; public static boolean isLua52 = true; + public static boolean blockGoto = false; public static void SET_OPCODE(InstructionPtr i, int o) { @@ -161,6 +162,14 @@ public static LocalVariable[] realloc(LocalVariable[] v, int n) { return a; } + public static LexState52.LabelDescription[] realloc(LexState52.LabelDescription[] v, int n) { + LexState52.LabelDescription[] a = new LexState52.LabelDescription[n]; + if (v != null) { + System.arraycopy(v, 0, a, 0, Math.min(v.length, n)); + } + return a; + } + public static int[] realloc(int[] v, int n) { int[] a = new int[n]; if (v != null) { @@ -218,7 +227,12 @@ public static Prototype compile(InputStream stream, LuaString name, LuaString mo return LoadState.loadBinaryChunk(firstByte, stream, name); } else { checkMode(mode, "text"); - return isLua52 ? luaY_parser52(firstByte, stream, name) : luaY_parser(firstByte, stream, name); + if (isLua52) { + LexState52.DynamicData dyd = new LexState52.DynamicData(); + return luaY_parser52(firstByte, stream, dyd, name); + } else { + return luaY_parser(firstByte, stream, name); + } } } @@ -244,13 +258,15 @@ private static Prototype luaY_parser(int firstByte, InputStream z, LuaString nam return funcstate.f; } - private static Prototype luaY_parser52(int firstByte, InputStream z, LuaString name) throws CompileException { + private static Prototype luaY_parser52(int firstByte, InputStream z, LexState52.DynamicData dyd, LuaString name) throws CompileException { LexState52 lexstate = new LexState52(z); FuncState52 funcstate = new FuncState52(); + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); // lexstate.buff = buff; + lexstate.dyd = dyd; lexstate.setinput(firstByte, z, name); funcstate.f = new Prototype(); - lexstate.open_func(funcstate); + lexstate.open_func(funcstate, bl); /* main func. is always vararg */ funcstate.f.is_vararg = Lua.VARARG_ISVARARG; funcstate.f.source = name; @@ -259,7 +275,7 @@ private static Prototype luaY_parser52(int firstByte, InputStream z, LuaString n funcstate.newupvalue(LuaString.valueOf("_ENV"), v); lexstate.nextToken(); /* read first token */ lexstate.chunk(); - lexstate.check(LexState.TK_EOS); + lexstate.check(LexState52.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); LuaC._assert(funcstate.f.nups == 1); diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 365c083b..03caf2ce 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -93,7 +93,6 @@ public class BaseLib implements LuaLibrary { "next", // "next" ( table, [index] ) -> next-index, next-value "__inext", // "inext" ( table, [int-index] ) -> next-index, next-value "rawlen", // "rawlen" ( table | string ) -> int - "__internal_print", }; private static final String[] LIBR_KEYS = { "pcall", // (f, arg1, ...) -> status, result1, ... @@ -324,9 +323,6 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError, UnwindThrow throw ErrorFactory.argError(1, "table or string expected"); } } - case 21: // __internal_print (TODO: delete this) - System.out.println(args.arg(1).checkLuaString().toString()); - return Constants.NONE; } return Constants.NONE; } diff --git a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java index 366830b3..8f7b28c2 100644 --- a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java @@ -93,6 +93,7 @@ public Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws LuaErr Varargs result = LuaThread.resume(state, thread, args.subargs(2)); return varargsOf(Constants.TRUE, result); } catch (LuaError le) { + le.printStackTrace(System.out); return varargsOf(Constants.FALSE, le.value); } } From 3f7109ea6ff579411cefeecd40e6217006b1e573 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 01:48:04 -0400 Subject: [PATCH 07/24] Added tail call hook --- .../java/org/squiddev/cobalt/debug/DebugHook.java | 13 ++++++++++++- .../java/org/squiddev/cobalt/debug/DebugState.java | 14 ++++++++++++++ .../org/squiddev/cobalt/function/LuaFunction.java | 5 +++++ .../squiddev/cobalt/function/LuaInterpreter.java | 4 +++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/debug/DebugHook.java b/src/main/java/org/squiddev/cobalt/debug/DebugHook.java index 2e6a392a..18e527bc 100644 --- a/src/main/java/org/squiddev/cobalt/debug/DebugHook.java +++ b/src/main/java/org/squiddev/cobalt/debug/DebugHook.java @@ -36,10 +36,10 @@ */ public interface DebugHook { LuaString CALL = valueOf("call"); + LuaString TAILCALL = valueOf("tail call"); LuaString LINE = valueOf("line"); LuaString COUNT = valueOf("count"); LuaString RETURN = valueOf("return"); - LuaString TAILRETURN = valueOf("tail return"); /** * Called after entering a function @@ -52,6 +52,17 @@ public interface DebugHook { */ void onCall(LuaState state, DebugState ds, DebugFrame frame) throws LuaError, UnwindThrowable; + /** + * Called after entering a function by means of tail call + * + * @param state Current lua state + * @param ds The current debug state + * @param frame The current frame + * @throws LuaError On a runtime error. + * @throws UnwindThrowable If this hook transfers control to another coroutine. + */ + void onTailCall(LuaState state, DebugState ds, DebugFrame frame) throws LuaError, UnwindThrowable; + /** * Called before exiting a function * diff --git a/src/main/java/org/squiddev/cobalt/debug/DebugState.java b/src/main/java/org/squiddev/cobalt/debug/DebugState.java index 4743a03b..9e3dbf2f 100644 --- a/src/main/java/org/squiddev/cobalt/debug/DebugState.java +++ b/src/main/java/org/squiddev/cobalt/debug/DebugState.java @@ -227,6 +227,20 @@ public void hookCall(DebugFrame frame) throws LuaError, UnwindThrowable { frame.flags &= ~FLAG_HOOKED; } + public void hookTailCall(DebugFrame frame) throws LuaError, UnwindThrowable { + inhook = true; + frame.flags |= FLAG_HOOKED; + try { + hookfunc.onTailCall(state, this, frame); + } catch (LuaError | RuntimeException e) { + inhook = false; + throw e; + } + + inhook = false; + frame.flags &= ~FLAG_HOOKED; + } + void hookReturn(DebugFrame frame) throws LuaError, UnwindThrowable { inhook = true; frame.flags |= FLAG_HOOKED; diff --git a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java index 56a05fff..a3be3241 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java @@ -199,6 +199,11 @@ public void onCall(LuaState state, DebugState ds, DebugFrame frame) throws LuaEr call(state, CALL); } + @Override + public void onTailCall(LuaState state, DebugState ds, DebugFrame frame) throws LuaError, UnwindThrowable { + call(state, TAILCALL); + } + @Override public void onReturn(LuaState state, DebugState ds, DebugFrame frame) throws LuaError, UnwindThrowable { call(state, RETURN); diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 5df9cf78..3d5bce84 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -141,7 +141,8 @@ private static DebugFrame setupCall(LuaState state, LuaInterpretedFunction funct if (!ds.inhook && ds.hookcall) { // Pretend we are at the first instruction for the hook. - ds.hookCall(di); + if ((flags & FLAG_TAIL) != 0) ds.hookTailCall(di); + else ds.hookCall(di); } di.top = 0; @@ -695,6 +696,7 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), } //System.out.println("Found upvalue index " + (newp.upvalue_info[j] & 0xFF) + " (named " + (frame.func instanceof LuaInterpretedFunction ? frame.closure.getPrototype().upvalues[newp.upvalue_info[j] & 0xFF] : "?") + ") with type " + frame.closure.getUpvalue(newp.upvalue_info[j] & 0xFF).getClass().getName()); newcl.upvalues[j] = new Upvalue(frame.stack, b & 0xFF); + // TODO: Add proper upvalue closing (maybe this is easy? still have to look at how the stack flag works) //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(frame.stack, b & 0xFF)); //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); } else { From 07d89d435346c25eca8718eefd4c5a7ee9bfce42 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 03:51:21 -0400 Subject: [PATCH 08/24] Added official Lua 5.2 goto tests Also fixed some bugs that made the tests fail. --- README.md | 4 +- .../squiddev/cobalt/compiler/FuncState52.java | 4 +- .../squiddev/cobalt/compiler/LexState52.java | 123 +++++++------ .../org/squiddev/cobalt/compiler/LuaC.java | 3 +- .../cobalt/function/LuaInterpreter.java | 21 ++- src/test/resources/assert/lua5.2/goto.lua | 173 ++++++++++++++++++ 6 files changed, 266 insertions(+), 62 deletions(-) create mode 100644 src/test/resources/assert/lua5.2/goto.lua diff --git a/README.md b/README.md index 30daec48..b263bf5b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Cobalt [![Current build status](https://github.com/SquidDev/Cobalt/workflows/Build/badge.svg)](https://github.com/SquidDev/Cobalt/actions "Current build status") ## What? -Cobalt is a fork of LuaJ 2.0 (Lua 5.1) with many features of LuaJ 3.0 backported. +Cobalt is a fork of LuaJ 2.0 (Lua 5.1) + Lua 5.2 with many features of LuaJ 3.0 backported. It allows multiple Lua instances to be run at once, with no shared metatables. ## Why? LuaJ 2.0 had too many bugs, mostly minor but annoying. Cobalt is an attempt to slim down LuaJ (only the JSE will be supported) and fix most of the bugs. -## But Lua 5.1 is outdated! +## But Lua 5.2 is outdated! I am considering having a separate Lua 5.3 branch but that is not an immediate priority right now. diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java index 243e7f5f..a204082a 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java @@ -813,7 +813,7 @@ void goiftrue(expdesc e) throws CompileException { e.t.i = NO_JUMP; } - private void goiffalse(expdesc e) throws CompileException { + void goiffalse(expdesc e) throws CompileException { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { @@ -1177,7 +1177,7 @@ private void movegotosout(BlockCnt bl) throws CompileException { void checkrepeated(LexState52.LabelDescription[] ll, int nll, LuaString label) throws CompileException { for (int i = bl.firstlabel; i < nll; i++) { if (label.equals(ll[i].name)) { - throw new CompileException(String.format("label %s already defined on line %d", + throw new CompileException(String.format("label '%s' already defined on line %d", label.toString(), ll[i].line)); } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index fd0576b4..ae6ffe90 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -1041,7 +1041,7 @@ private void closegoto(int g, LabelDescription label) throws CompileException { LabelDescription gt = dyd.gt[g]; LuaC._assert(gt.name.equals(label.name), linenumber); if (gt.nactvar < label.nactvar) { - throw new CompileException(String.format(" at line %d jumps into the scope of local %s", + throw new CompileException(String.format(" at line %d jumps into the scope of local '%s'", gt.name.toString(), gt.line, fs.getlocvar(gt.nactvar).name.toString())); } fs.patchlist(gt.pc, label.pc); @@ -1091,10 +1091,9 @@ private int newlabelentry(DynamicData dyd, boolean islabel, LuaString name, int } private void findgotos(LabelDescription lb) throws CompileException { - LabelDescription[] gl = dyd.gt; int i = fs.bl.firstgoto; while (i < dyd.ngt) { - if (gl[i].name.equals(lb.name)) { + if (dyd.gt[i].name.equals(lb.name)) { closegoto(i, lb); } else { i++; @@ -1115,7 +1114,7 @@ void breaklabel() throws CompileException { ** message when label name is a reserved word (which can only be 'break') */ void /* no return */ undefgoto(LabelDescription gt) throws CompileException { - String msg = String.format(isReservedKeyword(gt.name.toString()) ? "<%s> at line %d not inside a loop" : "no visible label %s for at line %d", + String msg = String.format(isReservedKeyword(gt.name.toString()) ? "<%s> at line %d not inside a loop" : "no visible label '%s' for at line %d", gt.name.toString(), gt.line); throw new CompileException(msg); } @@ -1366,7 +1365,7 @@ private void body(expdesc e, boolean needself, int line) throws CompileException } this.parlist(); this.checknext(')'); - this.chunk(); + this.statlist(); new_fs.f.lastlinedefined = this.linenumber; this.check_match(TK_END, TK_FUNCTION, line); this.codeclosure(e); @@ -1679,14 +1678,15 @@ private void expr(expdesc v) throws CompileException { */ - private boolean block_follow(int token) { - switch (token) { + private boolean block_follow(boolean withUntil) { + switch (t.token) { case TK_ELSE: case TK_ELSEIF: case TK_END: - case TK_UNTIL: case TK_EOS: return true; + case TK_UNTIL: + return withUntil; default: return false; } @@ -1698,7 +1698,7 @@ private void block() throws CompileException { FuncState52 fs = this.fs; FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); fs.enterblock(bl, false); - this.chunk(); + this.statlist(); //LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); fs.leaveblock(); } @@ -1813,7 +1813,7 @@ private void labelstat(LuaString label, int line) throws CompileException { while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ statement(); } - if (block_follow(0)) { /* label is last no-op statement in the block? */ + if (block_follow(false)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ dyd.label[l].nactvar = fs.bl.nactvar; } @@ -1848,7 +1848,7 @@ private void repeatstat(int line) throws CompileException { fs.enterblock(bl1, true); /* loop block */ fs.enterblock(bl2, false); /* scope block */ this.nextToken(); /* skip REPEAT */ - this.chunk(); + this.statlist(); this.check_match(TK_UNTIL, TK_REPEAT, line); condexit = this.cond(); /* read condition (inside scope block) */ if (bl2.upval) { /* upvalues? */ @@ -1966,14 +1966,38 @@ private void forstat(int line) throws CompileException { } - private int test_then_block() throws CompileException { + private void test_then_block(IntPtr escapelist) throws CompileException { /* test_then_block -> [IF | ELSEIF] cond THEN block */ - int condexit; + FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + int jf; this.nextToken(); /* skip IF or ELSEIF */ - condexit = this.cond(); + expdesc v = new expdesc(); + expr(v); this.checknext(TK_THEN); - this.block(); /* `then' part */ - return condexit; + if (t.token == TK_GOTO || t.token == TK_BREAK) { + fs.goiffalse(v); + fs.enterblock(bl, false); + gotostat(v.t.i); + while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ + statement(); + } + if (block_follow(false)) { /* 'goto' is the entire block? */ + fs.leaveblock(); + return; + } else { + jf = fs.jump(); + } + } else { + fs.goiftrue(v); + fs.enterblock(bl, false); + jf = v.f.i; + } + this.statlist(); /* `then' part */ + fs.leaveblock(); + if (t.token == TK_ELSE || t.token == TK_ELSEIF) { + fs.concat(escapelist, fs.jump()); + } + fs.patchtohere(jf); } @@ -1981,24 +2005,16 @@ private void ifstat(int line) throws CompileException { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] * END */ FuncState52 fs = this.fs; - int flist; IntPtr escapelist = new IntPtr(NO_JUMP); - flist = test_then_block(); /* IF cond THEN block */ + test_then_block(escapelist); /* IF cond THEN block */ while (this.t.token == TK_ELSEIF) { - fs.concat(escapelist, fs.jump()); - fs.patchtohere(flist); - flist = test_then_block(); /* ELSEIF cond THEN block */ + test_then_block(escapelist); /* ELSEIF cond THEN block */ } - if (this.t.token == TK_ELSE) { - fs.concat(escapelist, fs.jump()); - fs.patchtohere(flist); - this.nextToken(); /* skip ELSE (after patch, for correct line info) */ + if (testnext(TK_ELSE)) { this.block(); /* `else' part */ - } else { - fs.concat(escapelist, flist); } - fs.patchtohere(escapelist.i); this.check_match(TK_END, TK_IF, line); + fs.patchtohere(escapelist.i); } private void localfunc() throws CompileException { @@ -2082,7 +2098,7 @@ private void retstat() throws CompileException { expdesc e = new expdesc(); int first, nret; /* registers with returned values */ this.nextToken(); /* skip RETURN */ - if (block_follow(this.t.token) || this.t.token == ';') { + if (block_follow(true) || this.t.token == ';') { first = nret = 0; /* return no values */ } else { nret = this.explist1(e); /* optional return values */ @@ -2108,34 +2124,39 @@ private void retstat() throws CompileException { } - private boolean statement() throws CompileException { + private void statement() throws CompileException { int line = this.linenumber; /* may be needed for error messages */ + enterlevel(); switch (this.t.token) { + case ';': { /* stat -> ';' (empty statement) */ + nextToken(); /* skip ';' */ + break; + } case TK_IF: { /* stat -> ifstat */ this.ifstat(line); - return false; + break; } case TK_WHILE: { /* stat -> whilestat */ this.whilestat(line); - return false; + break; } case TK_DO: { /* stat -> DO block END */ this.nextToken(); /* skip DO */ this.block(); this.check_match(TK_END, TK_DO, line); - return false; + break; } case TK_FOR: { /* stat -> forstat */ this.forstat(line); - return false; + break; } case TK_REPEAT: { /* stat -> repeatstat */ this.repeatstat(line); - return false; + break; } case TK_FUNCTION: { this.funcstat(line); /* stat -> funcstat */ - return false; + break; } case TK_LOCAL: { /* stat -> localstat */ this.nextToken(); /* skip LOCAL */ @@ -2144,16 +2165,16 @@ private boolean statement() throws CompileException { } else { this.localstat(); } - return false; + break; } case TK_DBCOLON: { /* stat -> label */ this.nextToken(); this.labelstat(str_checkname(), line); - return false; + break; } case TK_RETURN: { /* stat -> retstat */ this.retstat(); - return true; /* must be last statement */ + break; } case TK_BREAK: /* stat -> breakstat */ case TK_GOTO: { /* stat -> 'goto' NAME */ @@ -2161,27 +2182,27 @@ private boolean statement() throws CompileException { throw lexError("illegal jump statement", this.t.token); } this.gotostat(fs.jump()); - return true; /* must be last statement */ + break; } default: { this.exprstat(); - return false; /* to avoid warnings */ + break; } } + LuaC._assert(fs.f.maxstacksize >= fs.freereg && fs.freereg >= fs.nactvar, linenumber); + fs.freereg = fs.nactvar; /* free registers */ + leavelevel(); } - void chunk() throws CompileException { + void statlist() throws CompileException { /* chunk -> { stat [`;'] } */ - boolean islast = false; - this.enterlevel(); - while (!islast && !block_follow(this.t.token)) { - islast = this.statement(); - this.testnext(';'); - LuaC._assert(this.fs.f.maxstacksize >= this.fs.freereg - && this.fs.freereg >= this.fs.nactvar, linenumber); - this.fs.freereg = this.fs.nactvar; /* free registers */ + while (!block_follow(true)) { + if (this.t.token == TK_RETURN) { + statement(); + return; /* 'return' must be last statement */ + } + this.statement(); } - this.leavelevel(); } /* }====================================================================== */ diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index b21f38ef..9df9c6d2 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -264,6 +264,7 @@ private static Prototype luaY_parser52(int firstByte, InputStream z, LexState52. FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); // lexstate.buff = buff; lexstate.dyd = dyd; + dyd.nactvar = dyd.ngt = dyd.nlabel = 0; lexstate.setinput(firstByte, z, name); funcstate.f = new Prototype(); lexstate.open_func(funcstate, bl); @@ -274,7 +275,7 @@ private static Prototype luaY_parser52(int firstByte, InputStream z, LexState52. v.init(LexState52.VLOCAL, 0); funcstate.newupvalue(LuaString.valueOf("_ENV"), v); lexstate.nextToken(); /* read first token */ - lexstate.chunk(); + lexstate.statlist(); lexstate.check(LexState52.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 3d5bce84..2d4380ca 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -340,7 +340,16 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; } - case OP_JMP: // sBx: pc+=sBx + case OP_JMP: // A sBx: pc+=sBx; if (A) close all upvalues >= R(A - 1) + if (p.isLua52 && a > 0) { + for (int x = openups.length; --x >= a - 1; ) { + Upvalue upvalue = openups[x]; + if (upvalue != null) { + upvalue.close(); + openups[x] = null; + } + } + } pc += ((i >>> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; break; @@ -689,15 +698,15 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), int b = newp.upvalue_info[j]; if ((b >> 8) != 0) { - DebugFrame frame = di; + /*DebugFrame frame = di; for (int s = (b >> 8) - 1; s > 0 && frame != null; --s) frame = frame.previous; if (frame == null) { throw new IllegalStateException("Upvalue stack index out of range"); - } + }*/ //System.out.println("Found upvalue index " + (newp.upvalue_info[j] & 0xFF) + " (named " + (frame.func instanceof LuaInterpretedFunction ? frame.closure.getPrototype().upvalues[newp.upvalue_info[j] & 0xFF] : "?") + ") with type " + frame.closure.getUpvalue(newp.upvalue_info[j] & 0xFF).getClass().getName()); - newcl.upvalues[j] = new Upvalue(frame.stack, b & 0xFF); - // TODO: Add proper upvalue closing (maybe this is easy? still have to look at how the stack flag works) - //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(frame.stack, b & 0xFF)); + //newcl.upvalues[j] = new Upvalue(frame.stack, b & 0xFF); + // TODO: Make sure this is actually correct + newcl.upvalues[j] = openups[b & 0xFF] != null ? openups[b & 0xFF] : (openups[b & 0xFF] = new Upvalue(di.stack, b & 0xFF)); //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); } else { newcl.upvalues[j] = upvalues[b & 0xFF]; diff --git a/src/test/resources/assert/lua5.2/goto.lua b/src/test/resources/assert/lua5.2/goto.lua new file mode 100644 index 00000000..e4f8e2d4 --- /dev/null +++ b/src/test/resources/assert/lua5.2/goto.lua @@ -0,0 +1,173 @@ +local function errmsg (code, m) + local st, msg = load(code) + assert(not st and string.find(msg, m), "expected '" .. m .. "' in '" .. msg .. "'") +end + +-- cannot see label inside block +errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'") +errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'") + +-- repeated label +errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") + + +-- undefined label +errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'") + +-- jumping over variable definition +errmsg([[ +do local bb, cc; goto l1; end +local aa +::l1:: print(3) +]], "local 'aa'") + +-- jumping into a block +errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'") +errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'") + +-- cannot continue a repeat-until with variables +errmsg([[ + repeat + if x then goto cont end + local xuxu = 10 + ::cont:: + until xuxu < x +]], "local 'xuxu'") + +-- simple gotos +local x +do + local y = 12 + goto l1 + ::l2:: x = x + 1; goto l3 + ::l1:: x = y; goto l2 +end +::l3:: ::l3_1:: assert(x == 13) + + +-- long labels +do + local prog = [[ + do + local a = 1 + goto l%sa; a = a + 1 + ::l%sa:: a = a + 10 + goto l%sb; a = a + 2 + ::l%sb:: a = a + 20 + return a + end + ]] + local label = string.rep("0123456789", 40) + prog = string.format(prog, label, label, label, label) + assert(assert(load(prog))() == 31) +end + +-- goto to correct label when nested +do goto l3; ::l3:: end -- does not loop jumping to previous label 'l3' + +-- ok to jump over local dec. to end of block +do + goto l1 + local a = 23 + x = a + ::l1::; +end + +while true do + goto l4 + goto l1 -- ok to jump over local dec. to end of block + goto l1 -- multiple uses of same label + local x = 45 + ::l1:: ;;; +end +::l4:: assert(x == 13) + +if print then + goto l1 -- ok to jump over local dec. to end of block + error("should not be here") + goto l2 -- ok to jump over local dec. to end of block + local x + ::l1:: ; ::l2:: ;; +else end + +-- to repeat a label in a different function is OK +local function foo () + local a = {} + goto l3 + ::l1:: a[#a + 1] = 1; goto l2; + ::l2:: a[#a + 1] = 2; goto l5; + ::l3:: + ::l3a:: a[#a + 1] = 3; goto l1; + ::l4:: a[#a + 1] = 4; goto l6; + ::l5:: a[#a + 1] = 5; goto l4; + ::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and + a[4] == 5 and a[5] == 4) + if not a[6] then a[6] = true; goto l3a end -- do it twice +end + +::l6:: foo() + + + +-------------------------------------------------------------------------------- +-- testing closing of upvalues + +local function foo () + local a = {} + do + local i = 1 + local k = 0 + a[0] = function (y) k = y end + ::l1:: do + local x + if i > 2 then goto l2 end + a[i] = function (y) if y then x = y else return x + k end end + i = i + 1 + goto l1 + end + end + ::l2:: return a +end + +local a = foo() +a[1](10); a[2](20) +assert(a[1]() == 10 and a[2]() == 20 and a[3] == nil) +a[0](13) +assert(a[1]() == 23 and a[2]() == 33) + +-------------------------------------------------------------------------------- +-- testing if x goto optimizations + +local function testG (a) + if a == 1 then + goto l1 + error("should never be here!") + elseif a == 2 then goto l2 + elseif a == 3 then goto l3 + elseif a == 4 then + goto l1 -- go to inside the block + error("should never be here!") + ::l1:: a = a + 1 -- must go to 'if' end + else + goto l4 + ::l4a:: a = a * 2; goto l4b + error("should never be here!") + ::l4:: goto l4a + error("should never be here!") + ::l4b:: + end + do return a end + ::l2:: do return "2" end + ::l3:: do return "3" end + ::l1:: return "1" +end + +assert(testG(1) == "1") +assert(testG(2) == "2") +assert(testG(3) == "3") +assert(testG(4) == 5) +assert(testG(5) == 10) +-------------------------------------------------------------------------------- + + +print'OK' From da4a30575ee956b5518a73c69ff71c29e4f3015e Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 11:23:21 -0400 Subject: [PATCH 09/24] Fixed 'return;', made goto context-sensitive goto can now be used as a variable name again, for old programs that used it. (Don't do this anymore.) --- src/main/java/org/squiddev/cobalt/compiler/LexState52.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index ae6ffe90..cf3e6fc0 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -759,7 +759,7 @@ private int llex(SemInfo seminfo) throws CompileException { save_and_next(); } while (isAlphaNum(current) || current == '_'); ts = newString(buff, 0, nbuff); - if (RESERVED.containsKey(ts)) { + if (RESERVED.containsKey(ts) && !ts.equals(LuaString.valueOf("goto"))) { return RESERVED.get(ts); } else { seminfo.ts = ts; @@ -2121,12 +2121,16 @@ private void retstat() throws CompileException { } } fs.ret(first, nret); + testnext(';'); } private void statement() throws CompileException { int line = this.linenumber; /* may be needed for error messages */ enterlevel(); + if (this.t.token == TK_NAME && this.t.seminfo.ts.equals(LuaString.valueOf("goto"))) { + this.t.token = TK_GOTO; + } switch (this.t.token) { case ';': { /* stat -> ';' (empty statement) */ nextToken(); /* skip ';' */ From 51a4053ee95075a077627a5e2a4c1b08fa17c0c6 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 11:30:09 -0400 Subject: [PATCH 10/24] Fixed goto-as-name when made the object of assignment (read: `goto = 2` works) --- src/main/java/org/squiddev/cobalt/compiler/LexState52.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index cf3e6fc0..11ad5bdf 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -2129,7 +2129,10 @@ private void statement() throws CompileException { int line = this.linenumber; /* may be needed for error messages */ enterlevel(); if (this.t.token == TK_NAME && this.t.seminfo.ts.equals(LuaString.valueOf("goto"))) { - this.t.token = TK_GOTO; + lookahead(); + if (lookahead.token == TK_NAME) { + this.t.token = TK_GOTO; + } } switch (this.t.token) { case ';': { /* stat -> ';' (empty statement) */ From 1dfb12c5019abf8b57ad93c5f1c6311e703a0e64 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 16:38:23 -0400 Subject: [PATCH 11/24] Added uservalues, removed environments Also fixed a bunch of stuff to make CC tests pass. --- .../org/squiddev/cobalt/ErrorFactory.java | 7 +- .../java/org/squiddev/cobalt/LuaState.java | 6 + .../java/org/squiddev/cobalt/LuaUserdata.java | 1 + .../squiddev/cobalt/compiler/FuncState52.java | 15 +- .../squiddev/cobalt/compiler/LoadState.java | 16 +- .../org/squiddev/cobalt/compiler/LuaC.java | 19 +- .../org/squiddev/cobalt/debug/DebugFrame.java | 2 +- .../squiddev/cobalt/debug/DebugHelpers52.java | 259 ++++++++++++++++++ .../squiddev/cobalt/function/LibFunction.java | 1 - .../squiddev/cobalt/function/LuaFunction.java | 8 +- .../cobalt/function/LuaInterpreter.java | 5 +- .../java/org/squiddev/cobalt/lib/BaseLib.java | 8 +- .../org/squiddev/cobalt/lib/DebugLib.java | 32 ++- .../org/squiddev/cobalt/lib/PackageLib.java | 2 - .../squiddev/cobalt/vm/LuaOperationsTest.java | 88 +++--- 15 files changed, 389 insertions(+), 80 deletions(-) create mode 100644 src/main/java/org/squiddev/cobalt/debug/DebugHelpers52.java diff --git a/src/main/java/org/squiddev/cobalt/ErrorFactory.java b/src/main/java/org/squiddev/cobalt/ErrorFactory.java index 1aa3c20d..fa92e433 100644 --- a/src/main/java/org/squiddev/cobalt/ErrorFactory.java +++ b/src/main/java/org/squiddev/cobalt/ErrorFactory.java @@ -27,6 +27,7 @@ import org.squiddev.cobalt.debug.DebugFrame; import org.squiddev.cobalt.debug.DebugHandler; import org.squiddev.cobalt.debug.DebugHelpers; +import org.squiddev.cobalt.debug.DebugHelpers52; /** * Factory class for errors @@ -106,9 +107,9 @@ public static LuaError operandError(LuaState state, LuaValue operand, String ver DebugFrame info = DebugHandler.getDebugState(state).getStack(); if (info != null && info.closure != null) { if (stack < info.closure.getPrototype().maxstacksize) { - kind = DebugHelpers.getObjectName(info, stack); - } - } + kind = info.closure.getPrototype().isLua52 ? DebugHelpers52.getObjectName(info, stack) : DebugHelpers.getObjectName(info, stack); + } + } } if (kind != null) { diff --git a/src/main/java/org/squiddev/cobalt/LuaState.java b/src/main/java/org/squiddev/cobalt/LuaState.java index b5bcbe2a..d6c5d726 100644 --- a/src/main/java/org/squiddev/cobalt/LuaState.java +++ b/src/main/java/org/squiddev/cobalt/LuaState.java @@ -134,6 +134,11 @@ public final class LuaState { */ boolean abandoned; + /** + * Whether to compile for Lua 5.1 or 5.2. + */ + public boolean useLua52; + public LuaState() { this(new LuaState.Builder()); } @@ -153,6 +158,7 @@ private LuaState(Builder builder) { this.debug = builder.debug; this.timezone = builder.timezone; this.threader = new YieldThreader(builder.coroutineExecutor); + this.useLua52 = LuaC.defaultLua52; } /** diff --git a/src/main/java/org/squiddev/cobalt/LuaUserdata.java b/src/main/java/org/squiddev/cobalt/LuaUserdata.java index 84e34e75..e3264e48 100644 --- a/src/main/java/org/squiddev/cobalt/LuaUserdata.java +++ b/src/main/java/org/squiddev/cobalt/LuaUserdata.java @@ -28,6 +28,7 @@ public class LuaUserdata extends LuaValue { public final Object instance; public LuaTable metatable; + public LuaTable uservalue; public LuaUserdata(Object obj) { super(Constants.TUSERDATA); diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java index a204082a..49d6afaf 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java @@ -280,6 +280,7 @@ void lastlistfield(ConsControl cc) throws CompileException { void nil(int from, int n) throws CompileException { InstructionPtr previous; + int l = from + n - 1; if (this.pc > this.lasttarget) { /* no jumps to current position? */ if (this.pc == 0) { /* function start? */ if (from >= this.nactvar) { @@ -289,18 +290,20 @@ void nil(int from, int n) throws CompileException { previous = new InstructionPtr(this.f.code, this.pc - 1); if (GET_OPCODE(previous.get()) == OP_LOADNIL) { int pfrom = GETARG_A(previous.get()); - int pn = GETARG_B(previous.get()); - if (pfrom <= from && from <= pfrom + pn) { /* can connect both? */ - if (from + n - 1 > pfrom + n - 1) { - SETARG_B(previous, n); - } + int pl = pfrom + GETARG_B(previous.get()); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; + if (pl > l) l = pl; + SETARG_A(previous, from); + SETARG_B(previous, l - from); return; } } } } /* else no optimization */ - this.codeABC(OP_LOADNIL, from, n, 0); + this.codeABC(OP_LOADNIL, from, n - 1, 0); } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java index 1763b0fc..9056b5c5 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java @@ -75,6 +75,20 @@ public final class LoadState { */ public interface LuaCompiler { + /** + * Load into a Closure or LuaFunction from a Stream and initializes the environment + * + * @param stream Stream to read + * @param filename Name of chunk + * @param mode + * @param env Environment to load + * @param useLua52 Whether to load as Lua 5.2 + * @return The loaded function + * @throws IOException On stream read error + * @throws CompileException If the stream cannot be loaded. + */ + LuaFunction load(InputStream stream, LuaString filename, LuaString mode, LuaTable env, boolean useLua52) throws IOException, CompileException; + /** * Load into a Closure or LuaFunction from a Stream and initializes the environment * @@ -121,7 +135,7 @@ public static LuaFunction load(LuaState state, InputStream stream, LuaString nam } public static LuaFunction load(LuaState state, InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { - if (state.compiler != null) return state.compiler.load(stream, name, mode, env); + if (state.compiler != null) return state.compiler.load(stream, name, mode, env, state.useLua52); int firstByte = stream.read(); if (firstByte != LUA_SIGNATURE[0]) throw new CompileException("no compiler"); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index 9df9c6d2..098278b2 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -87,7 +87,7 @@ protected static void _assert(boolean b, int line) throws CompileException { public static final int MAXSTACK = 250; public static final int LUAI_MAXUPVALUES = 60; public static final int LUAI_MAXVARS = 200; - public static boolean isLua52 = true; + public static boolean defaultLua52 = false; public static boolean blockGoto = false; @@ -193,16 +193,21 @@ private LuaC() { * Load into a Closure or LuaFunction, with the supplied initial environment */ @Override - public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { - Prototype p = compile(stream, name, mode); + public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env, boolean useLua52) throws IOException, CompileException { + Prototype p = compile(stream, name, mode, useLua52); LuaInterpretedFunction closure = new LuaInterpretedFunction(p, env); closure.nilUpvalues(); - if (p.isLua52 && p.nups == 1) { + if (useLua52 && p.nups == 1) { closure.setUpvalue(0, new Upvalue(env)); } return closure; } + @Override + public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { + return load(stream, name, mode, env, defaultLua52); + } + public static Prototype compile(InputStream stream, String name) throws IOException, CompileException { return compile(stream, valueOf(name)); } @@ -221,13 +226,17 @@ public static Prototype compile(InputStream stream, LuaString name) throws IOExc } public static Prototype compile(InputStream stream, LuaString name, LuaString mode) throws IOException, CompileException { + return compile(stream, name, mode, defaultLua52); + } + + public static Prototype compile(InputStream stream, LuaString name, LuaString mode, boolean lua52) throws IOException, CompileException { int firstByte = stream.read(); if (firstByte == '\033') { checkMode(mode, "binary"); return LoadState.loadBinaryChunk(firstByte, stream, name); } else { checkMode(mode, "text"); - if (isLua52) { + if (lua52) { LexState52.DynamicData dyd = new LexState52.DynamicData(); return luaY_parser52(firstByte, stream, dyd, name); } else { diff --git a/src/main/java/org/squiddev/cobalt/debug/DebugFrame.java b/src/main/java/org/squiddev/cobalt/debug/DebugFrame.java index 4d4f0745..7c9a9aef 100644 --- a/src/main/java/org/squiddev/cobalt/debug/DebugFrame.java +++ b/src/main/java/org/squiddev/cobalt/debug/DebugFrame.java @@ -220,7 +220,7 @@ public LuaString[] getFuncKind() { if (previous == null || previous.closure == null || previous.pc < 0) return null; int stackpos = (previous.closure.getPrototype().code[previous.pc] >> 6) & 0xff; - return DebugHelpers.getFuncName(previous, stackpos); + return previous.closure.getPrototype().isLua52 ? DebugHelpers52.getFuncName(previous, stackpos) : DebugHelpers.getFuncName(previous, stackpos); } public String sourceLine() { diff --git a/src/main/java/org/squiddev/cobalt/debug/DebugHelpers52.java b/src/main/java/org/squiddev/cobalt/debug/DebugHelpers52.java new file mode 100644 index 00000000..65a42025 --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/debug/DebugHelpers52.java @@ -0,0 +1,259 @@ +/* + * The MIT License (MIT) + * + * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. + * Modifications: Copyright (c) 2015-2020 SquidDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.squiddev.cobalt.debug; + +import org.squiddev.cobalt.*; +import org.squiddev.cobalt.compiler.LuaC; +import org.squiddev.cobalt.lib.DebugLib; + +import static org.squiddev.cobalt.Lua52.*; +import static org.squiddev.cobalt.ValueFactory.valueOf; +import static org.squiddev.cobalt.debug.DebugFrame.FLAG_HOOKED; +import static org.squiddev.cobalt.debug.DebugFrame.FLAG_TAIL; + +/** + * Helper methods for the debug library + */ +public final class DebugHelpers52 { + private static final LuaString GLOBAL = valueOf("global"); + private static final LuaString LOCAL = valueOf("local"); + private static final LuaString METHOD = valueOf("method"); + private static final LuaString UPVALUE = valueOf("upvalue"); + private static final LuaString FIELD = valueOf("field"); + private static final LuaString QUESTION = valueOf("?"); + private static final LuaString HOOK = valueOf("hook"); + private static final LuaString METAMETHOD = valueOf("metamethod"); + private static final LuaString CONSTANT = valueOf("constant"); + + /** + * Size of the first part of the stack + */ + private static final int LEVELS1 = 10; + + /** + * Size of the second part of the stack + */ + private static final int LEVELS2 = 11; + + private DebugHelpers52() { + } + + private static LuaString[] fromMetamethod(String name) { + return new LuaString[]{valueOf("__" + name), METAMETHOD}; + } + + static LuaString[] getFuncName(DebugFrame di, int stackpos) { + if (di.closure == null) return null; + if ((di.flags & FLAG_HOOKED) != 0) return new LuaString[]{QUESTION, HOOK}; + + Prototype p = di.closure.getPrototype(); + int pc = di.pc; // currentpc(L, ci); + int i = p.code[pc]; + switch (GET_OPCODE(i)) { + case OP_CALL: case OP_TAILCALL: return getObjectName(di, GETARG_A(i)); + case OP_SELF: case OP_GETTABLE: return fromMetamethod("index"); + case OP_SETTABLE: return fromMetamethod("newindex"); + case OP_ADD: return fromMetamethod("add"); + case OP_SUB: return fromMetamethod("sub"); + case OP_MUL: return fromMetamethod("mul"); + case OP_DIV: return fromMetamethod("div"); + case OP_POW: return fromMetamethod("pow"); + case OP_MOD: return fromMetamethod("mod"); + case OP_UNM: return fromMetamethod("unm"); + case OP_EQ: return fromMetamethod("eq"); + case OP_LE: return fromMetamethod("le"); + case OP_LT: return fromMetamethod("lt"); + case OP_LEN: return fromMetamethod("len"); + case OP_CONCAT: return fromMetamethod("concat"); + default: return null; + } + } + + // return StrValue[] { name, namewhat } if found, null if not + public static LuaString[] getObjectName(DebugFrame di, int stackpos) { + if (di.closure == null) return null; + if ((di.flags & FLAG_HOOKED) != 0) return new LuaString[]{QUESTION, HOOK}; + + Prototype p = di.closure.getPrototype(); + int pc = di.pc; // currentpc(L, ci); + LuaString name = p.getlocalname(stackpos + 1, pc); + + // is a local? + if (name != null) return new LuaString[]{name, LOCAL}; + + pc = findsetreg(p, di.pc, stackpos); + if (pc != -1) { + int i = p.code[pc]; + int op = GET_OPCODE(i); + switch (op) { + case OP_MOVE: { + int a = GETARG_A(i); + int b = GETARG_B(i); /* move from `b' to `a' */ + if (b < a) return getObjectName(di, b); /* get name for `b' */ + break; + } + case OP_GETTABUP: + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + int t = GETARG_B(i); /* table index */ + LuaString vn = op == OP_GETTABLE ? constantName(p, k) : p.upvalues[t]; + return new LuaString[]{constantName(p, k), vn.equals(LuaString.valueOf("_ENV")) ? GLOBAL : FIELD}; + } + case OP_GETUPVAL: { + int u = GETARG_B(i); /* upvalue index */ + return new LuaString[]{u < p.upvalues.length ? p.upvalues[u] : DebugLib.QMARK, UPVALUE}; + } + case OP_LOADK: + case OP_LOADKX: { + int b = op == OP_LOADK ? GETARG_Bx(i) : GETARG_Ax(i); + if (p.k[b].isString()) return new LuaString[]{(LuaString)p.k[b], CONSTANT}; + break; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + return new LuaString[]{constantName(p, k), METHOD}; + } + } + } + + return null; // no useful name found + } + + private static int filterpc(int pc, int jmptarget) { + if (pc < jmptarget) return -1; + else return pc; + } + + // return last instruction, or 0 if error + private static int findsetreg(Prototype p, int lastpc, int reg) { + int setreg = -1; + int jmptarget = 0; + for (int pc = 0; pc < lastpc; pc++) { + int i = p.code[pc]; + int op = GET_OPCODE(i); + int a = GETARG_A(i); + switch (op) { + case OP_LOADNIL: { + int b = GETARG_B(i); + if (a <= reg && reg <= a + b) { + setreg = filterpc(pc, jmptarget); + } + break; + } + case OP_TFORCALL: { + if (reg >= a + 2) { + setreg = filterpc(pc, jmptarget); + } + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (reg >= a) { + setreg = filterpc(pc, jmptarget); + } + break; + } + case OP_JMP: { + int b = GETARG_sBx(i); + int dest = pc + 1 + b; + /* jump is forward and do not skip `lastpc'? */ + if (pc < dest && dest <= lastpc) { + if (dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + } + break; + } + case OP_TEST: { + if (reg == a) { + setreg = filterpc(pc, jmptarget); + } + break; + } + default: { + if (testAMode(op) && reg == a) { + setreg = filterpc(pc, jmptarget); + } + break; + } + } + } + return setreg; + } + + private static boolean precheck(Prototype pt) { + if (!(pt.maxstacksize <= LuaC.MAXSTACK)) return false; + lua_assert(pt.numparams + (pt.is_vararg & VARARG_HASARG) <= pt.maxstacksize); + lua_assert((pt.is_vararg & VARARG_NEEDSARG) == 0 + || (pt.is_vararg & VARARG_HASARG) != 0); + return pt.upvalues.length <= pt.nups && (pt.lineinfo.length == pt.code.length || pt.lineinfo.length == 0) && GET_OPCODE(pt.code[pt.code.length - 1]) == OP_RETURN; + } + + private static boolean checkArgMode(Prototype pt, int val, int mode) { + switch (mode) { + case OpArgN: + if (!(val == 0)) return false; + break; + case OpArgU: + break; + case OpArgR: + checkRegister(pt, val); + break; + case OpArgK: + if (!(ISK(val) ? INDEXK(val) < pt.k.length : val < pt.maxstacksize)) return false; + break; + } + return true; + } + + private static boolean checkRegister(Prototype proto, int reg) { + return (reg < proto.maxstacksize); + } + + private static boolean checkOpenUp(Prototype proto, int pc) { + int i = proto.code[(pc) + 1]; + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_RETURN: + case OP_SETLIST: { + return GETARG_B(i) == 0; + } + default: + return false; /* invalid instruction after an open call */ + } + } + + private static LuaString constantName(Prototype proto, int index) { + if (ISK(index) && proto.k[INDEXK(index)].isString()) { + return (LuaString) proto.k[INDEXK(index)].toLuaString(); + } else { + return DebugLib.QMARK; + } + } + + private static void lua_assert(boolean x) { + if (!x) throw new RuntimeException("lua_assert failed"); + } +} diff --git a/src/main/java/org/squiddev/cobalt/function/LibFunction.java b/src/main/java/org/squiddev/cobalt/function/LibFunction.java index ce1d64e0..3d154216 100644 --- a/src/main/java/org/squiddev/cobalt/function/LibFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LibFunction.java @@ -188,7 +188,6 @@ public static void bind(LuaTable env, Supplier factory, String[] na LibFunction f = factory.get(); f.opcode = firstOpcode + i; f.name = names[i]; - f.env = env; env.rawset(f.name, f); } } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java index a3be3241..4e8cb333 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java @@ -42,16 +42,12 @@ * @see LuaInterpretedFunction */ public abstract class LuaFunction extends LuaValue implements DebugHook { - protected LuaTable env; - public LuaFunction() { super(Constants.TFUNCTION); - this.env = null; } public LuaFunction(LuaTable env) { super(Constants.TFUNCTION); - this.env = env; } @Override @@ -71,12 +67,12 @@ public LuaTable getMetatable(LuaState state) { @Override public LuaTable getfenv() { - return env; + return null; } @Override public void setfenv(LuaTable env) { - this.env = env != null ? env : null; + } public String debugName() { diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 2d4380ca..4ec9a60c 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -200,14 +200,13 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti if (((i >>> POS_C) & MAXARG_C) != 0) pc++; // skip next instruction (if C) break; - case OP_LOADNIL: { // A B: R(A):= ...:= R(B):= nil + case OP_LOADNIL: { // A B: R(A):= ...:= R([A+]B):= nil + int b = ((i >>> POS_B) & MAXARG_B); if (p.isLua52) { - int b = ((i >>> POS_B) & MAXARG_B); while (b >= 0) { stack[a+(b--)] = NIL; } } else { - int b = ((i >>> POS_B) & MAXARG_B); do { stack[b--] = NIL; } while (b >= a); diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 03caf2ce..a32a763d 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -112,7 +112,7 @@ public LuaValue add(LuaState state, LuaTable env) { next = env.rawget("next"); inext = env.rawget("__inext"); - env.rawset("_VERSION", valueOf(LuaC.isLua52 ? "Lua 5.2" : "Lua 5.1")); + env.rawset("_VERSION", valueOf(state.useLua52 ? "Lua 5.2" : "Lua 5.1")); return env; } @@ -339,6 +339,12 @@ protected Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws Lua case 2: // "load", // ( func|str [,chunkname[, mode[, env]]] ) -> chunk | nil, msg { + // We'd typically rely on the argument checks in LuaValue, but those don't give us argument numbers so we do arg checks ahead of time. + Varargs.argCheck(args.arg(1).isFunction() || args.arg(1).isString(), 1, "expected function or string, got " + args.arg(1).typeName()); + Varargs.argCheck(args.isNoneOrNil(2) || args.arg(2).isString(), 2, "expected string, got " + args.arg(2).typeName()); + Varargs.argCheck(args.isNoneOrNil(3) || args.arg(3).isString(), 3, "expected string, got " + args.arg(3).typeName()); + Varargs.argCheck(args.isNoneOrNil(4) || args.arg(4).isTable(), 4, "expected table, got " + args.arg(4).typeName()); + LuaValue scriptGen = args.arg(1); LuaString chunkName = args.arg(2).optLuaString(null); LuaString mode = args.arg(3).optLuaString(LOAD_MODE); diff --git a/src/main/java/org/squiddev/cobalt/lib/DebugLib.java b/src/main/java/org/squiddev/cobalt/lib/DebugLib.java index 919e14d1..713177b8 100644 --- a/src/main/java/org/squiddev/cobalt/lib/DebugLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/DebugLib.java @@ -58,11 +58,13 @@ public class DebugLib extends VarArgFunction implements LuaLibrary { "getmetatable", "getregistry", "getupvalue", + "getuservalue", "setfenv", "sethook", "setlocal", "setmetatable", "setupvalue", + "setuservalue", "traceback", "upvalueid", "upvaluejoin", @@ -119,20 +121,24 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError { case 7: return _getupvalue(args); case 8: - return _setfenv(args); + return _getuservalue(args); case 9: - return _sethook(state, args); + return _setfenv(args); case 10: - return _setlocal(state, args); + return _sethook(state, args); case 11: - return _setmetatable(state, args); + return _setlocal(state, args); case 12: - return _setupvalue(args); + return _setmetatable(state, args); case 13: - return _traceback(state, args); + return _setupvalue(args); case 14: - return upvalueId(args); + return _setuservalue(args); case 15: + return _traceback(state, args); + case 16: + return upvalueId(args); + case 17: return upvalueJoin(args); default: return NONE; @@ -471,4 +477,16 @@ private static Varargs upvalueJoin(Varargs args) throws LuaError { closure1.setUpvalue(upvalue1, closure2.getUpvalue(upvalue2)); return NONE; } + + private static LuaTable _getuservalue(Varargs args) throws LuaError { + args.arg(1).checkUserdata(); + return ((LuaUserdata)args.arg(1)).uservalue; + } + + private static LuaValue _setuservalue(Varargs args) throws LuaError { + LuaValue val = args.arg(1); + val.checkUserdata(); + ((LuaUserdata)val).uservalue = args.arg(2).checkTable(); + return val; + } } diff --git a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java index affe81fc..4cef0854 100644 --- a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java @@ -96,7 +96,6 @@ static final class PkgLib1 extends OneArgFunction { PackageLib lib; public PkgLib1(LuaTable env, String name, int opcode, PackageLib lib) { - this.env = env; this.name = name; this.opcode = opcode; this.lib = lib; @@ -126,7 +125,6 @@ static final class PkgLibV extends VarArgFunction { PackageLib lib; public PkgLibV(LuaTable env, String name, int opcode, PackageLib lib) { - this.env = env; this.name = name; this.opcode = opcode; this.lib = lib; diff --git a/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java b/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java index 709ab4c6..6091c683 100644 --- a/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java +++ b/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java @@ -64,20 +64,20 @@ public class LuaOperationsTest { private final LuaValue stringlong = valueOf(samplestringlong); private final LuaValue stringdouble = valueOf(samplestringdouble); private final LuaTable table = ValueFactory.listOf(new LuaValue[]{valueOf("aaa"), valueOf("bbb")}); - private final LuaFunction somefunc = new ZeroArgFunction() { - { - env = table; - } - - @Override - public LuaValue call(LuaState state) { - return Constants.NONE; - } - }; +// private final LuaFunction somefunc = new ZeroArgFunction() { +// { +// env = table; +// } +// +// @Override +// public LuaValue call(LuaState state) { +// return Constants.NONE; +// } +// }; private final LuaState state = new LuaState(); - private final LuaThread thread = new LuaThread(state, somefunc, table); +// private final LuaThread thread = new LuaThread(state, somefunc, table); private final Prototype proto = new Prototype(); - private final LuaClosure someclosure = new LuaInterpretedFunction(proto, table); +// private final LuaClosure someclosure = new LuaInterpretedFunction(proto, table); private final LuaUserdata userdataobj = ValueFactory.userdataOf(sampleobject); private final LuaUserdata userdatacls = ValueFactory.userdataOf(sampledata); @@ -146,9 +146,9 @@ public void testLength() throws LuaError, UnwindThrowable { assertEquals(samplestringlong.length(), OperationHelper.length(state, stringlong).toInteger()); assertEquals(samplestringdouble.length(), OperationHelper.length(state, stringdouble).toInteger()); assertEquals(2, table.length()); - throwsLuaError("length", somefunc); - throwsLuaError("length", thread); - throwsLuaError("length", someclosure); +// throwsLuaError("length", somefunc); +// throwsLuaError("length", thread); +// throwsLuaError("length", someclosure); throwsLuaError("length", userdataobj); throwsLuaError("length", userdatacls); } @@ -167,9 +167,9 @@ public void testGetfenv() { throwsLuaError("getfenv", stringlong); throwsLuaError("getfenv", stringdouble); throwsLuaError("getfenv", table); - assertSame(table, thread.getfenv()); - assertSame(table, someclosure.getfenv()); - assertSame(table, somefunc.getfenv()); +// assertSame(table, thread.getfenv()); +// assertSame(table, someclosure.getfenv()); +// assertSame(table, somefunc.getfenv()); throwsLuaError("getfenv", userdataobj); throwsLuaError("getfenv", userdatacls); } @@ -189,15 +189,15 @@ public void testSetfenv() { setfenvThrowsLuaError("setfenv", stringlong, table2); setfenvThrowsLuaError("setfenv", stringdouble, table2); setfenvThrowsLuaError("setfenv", table, table2); - thread.setfenv(table2); - assertSame(table2, thread.getfenv()); - assertSame(table, someclosure.getfenv()); - assertSame(table, somefunc.getfenv()); - someclosure.setfenv(table2); - assertSame(table2, someclosure.getfenv()); - assertSame(table, somefunc.getfenv()); - somefunc.setfenv(table2); - assertSame(table2, somefunc.getfenv()); +// thread.setfenv(table2); +// assertSame(table2, thread.getfenv()); +// assertSame(table, someclosure.getfenv()); +// assertSame(table, somefunc.getfenv()); +// someclosure.setfenv(table2); +// assertSame(table2, someclosure.getfenv()); +// assertSame(table, somefunc.getfenv()); +// somefunc.setfenv(table2); +// assertSame(table2, somefunc.getfenv()); setfenvThrowsLuaError("setfenv", userdataobj, table2); setfenvThrowsLuaError("setfenv", userdatacls, table2); } @@ -227,23 +227,23 @@ public void testFunctionClosureThreadEnv() throws LuaError, UnwindThrowable, Int OperationHelper.setTable(state, _G, valueOf("a"), aaa); OperationHelper.setTable(state, newenv, valueOf("a"), eee); - // function tests - { - LuaFunction f = new ZeroArgFunction() { - { - env = _G; - } - - @Override - public LuaValue call(LuaState state) throws LuaError { - return OperationHelper.noUnwind(state, () -> OperationHelper.getTable(state, env, valueOf("a"))); - } - }; - assertEquals(aaa, f.call(state)); - f.setfenv(newenv); - assertEquals(newenv, f.getfenv()); - assertEquals(eee, f.call(state)); - } +// // function tests +// { +// LuaFunction f = new ZeroArgFunction() { +// { +// env = _G; +// } +// +// @Override +// public LuaValue call(LuaState state) throws LuaError { +// return OperationHelper.noUnwind(state, () -> OperationHelper.getTable(state, env, valueOf("a"))); +// } +// }; +// assertEquals(aaa, f.call(state)); +// f.setfenv(newenv); +// assertEquals(newenv, f.getfenv()); +// assertEquals(eee, f.call(state)); +// } // closure tests { From 32b5f6b8a065a9f7811ddd91095bb890be79ac9b Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Thu, 1 Apr 2021 19:38:25 -0400 Subject: [PATCH 12/24] Fixed most of the tests There's still a couple more to finish before it'll pass. --- .../java/org/squiddev/cobalt/LuaState.java | 14 +- .../java/org/squiddev/cobalt/LuaThread.java | 2 +- src/main/java/org/squiddev/cobalt/Print.java | 6 +- .../java/org/squiddev/cobalt/Print52.java | 477 ++++++++++++++++++ .../squiddev/cobalt/compiler/LexState52.java | 1 - .../org/squiddev/cobalt/compiler/LuaC.java | 4 +- .../function/LuaInterpretedFunction.java | 4 +- .../java/org/squiddev/cobalt/lib/BaseLib.java | 28 +- .../org/squiddev/cobalt/lib/DebugLib.java | 4 +- .../java/org/squiddev/cobalt/AssertTests.java | 1 + .../cobalt/compiler/CompileTestHelper.java | 4 +- .../resources/compare/errors/baselibargs.lua | 3 +- .../resources/compare/errors/baselibargs.out | 3 - 13 files changed, 529 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/squiddev/cobalt/Print52.java diff --git a/src/main/java/org/squiddev/cobalt/LuaState.java b/src/main/java/org/squiddev/cobalt/LuaState.java index d6c5d726..95efd1e0 100644 --- a/src/main/java/org/squiddev/cobalt/LuaState.java +++ b/src/main/java/org/squiddev/cobalt/LuaState.java @@ -158,7 +158,7 @@ private LuaState(Builder builder) { this.debug = builder.debug; this.timezone = builder.timezone; this.threader = new YieldThreader(builder.coroutineExecutor); - this.useLua52 = LuaC.defaultLua52; + this.useLua52 = builder.useLua52; } /** @@ -236,6 +236,7 @@ public static class Builder { private DebugHandler debug = DebugHandler.INSTANCE; private TimeZone timezone = TimeZone.getDefault(); private Executor coroutineExecutor = defaultCoroutineExecutor; + private boolean useLua52 = LuaC.defaultLua52; /** * Build a Lua state from this builder @@ -412,5 +413,16 @@ public Builder coroutineExecutor(Executor coroutineExecutor) { this.coroutineExecutor = coroutineExecutor; return this; } + + /** + * Set whether the state generates Lua 5.2 bytecode. + * + * @param lua52 Whether to use Lua 5.2 + * @return This builder + */ + public Builder useLua52(boolean lua52) { + this.useLua52 = lua52; + return this; + } } } diff --git a/src/main/java/org/squiddev/cobalt/LuaThread.java b/src/main/java/org/squiddev/cobalt/LuaThread.java index edb1924b..d12bbecc 100644 --- a/src/main/java/org/squiddev/cobalt/LuaThread.java +++ b/src/main/java/org/squiddev/cobalt/LuaThread.java @@ -462,7 +462,7 @@ public void run() { LuaFunction function = func; func = null; if (function instanceof LuaInterpretedFunction) { - ((LuaInterpretedFunction)function).setUpvalue(0, new Upvalue(thread.env)); + ((LuaInterpretedFunction)function).setUpvalue(((LuaInterpretedFunction) function).p.isLua52 ? 0 : -1, new Upvalue(thread.env)); } Varargs res = loop(state, state.currentThread, function, threader.unpack()); diff --git a/src/main/java/org/squiddev/cobalt/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index bfa237d7..ace3cbc3 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -32,7 +32,7 @@ import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import static org.squiddev.cobalt.Lua52.*; +import static org.squiddev.cobalt.Lua.*; /** * Debug helper class to pretty-print lua bytecodes. @@ -211,6 +211,10 @@ public static void printCode(PrintStream ps, Prototype f) { * @param pc the program counter to look up and print */ public static void printOpcode(PrintStream ps, Prototype f, int pc) { + if (f.isLua52) { + Print52.printOpcode(ps, f, pc); + return; + } int[] code = f.code; int i = code[pc]; int o = GET_OPCODE(i); diff --git a/src/main/java/org/squiddev/cobalt/Print52.java b/src/main/java/org/squiddev/cobalt/Print52.java new file mode 100644 index 00000000..5e31e34f --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -0,0 +1,477 @@ +/* + * The MIT License (MIT) + * + * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. + * Modifications: Copyright (c) 2015-2020 SquidDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.squiddev.cobalt; + +import org.squiddev.cobalt.function.LuaClosure; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; + +import static org.squiddev.cobalt.Lua52.*; + +/** + * Debug helper class to pretty-print lua bytecodes. + * + * @see Prototype + * @see LuaClosure + */ +public class Print52 { + public static final String[] OPNAMES = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + null, + }; + + public static final String[] OPNAMES_52 = { + "MOVE", + "LOADK", + "LOADKX", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETTABUP", + "GETTABLE", + "SETTABUP", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSURE", + "VARARG", + "EXTRAARG", + null, + }; + + + static void printString(PrintStream ps, final LuaString s) { + + ps.print('"'); + for (int i = 0, n = s.length; i < n; i++) { + int c = s.bytes[s.offset + i]; + if (c >= ' ' && c <= '~' && c != '\"' && c != '\\') { + ps.print((char) c); + } else { + switch (c) { + case '"': + ps.print("\\\""); + break; + case '\\': + ps.print("\\\\"); + break; + case 0x0007: /* bell */ + ps.print("\\a"); + break; + case '\b': /* backspace */ + ps.print("\\b"); + break; + case '\f': /* form feed */ + ps.print("\\f"); + break; + case '\t': /* tab */ + ps.print("\\t"); + break; + case '\r': /* carriage return */ + ps.print("\\r"); + break; + case '\n': /* newline */ + ps.print("\\n"); + break; + case 0x000B: /* vertical tab */ + ps.print("\\v"); + break; + default: + ps.print('\\'); + ps.print(Integer.toString(1000 + 0xff & c).substring(1)); + break; + } + } + } + ps.print('"'); + } + + private static void printValue(PrintStream ps, LuaValue v) { + switch (v.type()) { + case Constants.TSTRING: + printString(ps, (LuaString) v); + break; + default: + ps.print(v.toString()); + + } + } + + private static void printConstant(PrintStream ps, Prototype f, int i) { + printValue(ps, f.k[i]); + } + + /** + * Print the code in a prototype + * + * @param f the {@link Prototype} + */ + public static void printCode(PrintStream ps, Prototype f) { + int[] code = f.code; + int pc, n = code.length; + for (pc = 0; pc < n; pc++) { + printOpcode(ps, f, pc); + ps.println(); + } + } + + /** + * Print an opcode in a prototype + * + * @param ps the {@link PrintStream} to print to + * @param f the {@link Prototype} + * @param pc the program counter to look up and print + */ + public static void printOpcode(PrintStream ps, Prototype f, int pc) { + int[] code = f.code; + int i = code[pc]; + int o = GET_OPCODE(i); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + int bx = GETARG_Bx(i); + int sbx = GETARG_sBx(i); + int line = getline(f, pc); + ps.print(" " + (pc + 1) + " "); + if (line > 0) { + ps.print("[" + line + "] "); + } else { + ps.print("[-] "); + } + ps.print((f.isLua52 ? OPNAMES_52[o] : OPNAMES[o]) + " "); + switch (getOpMode(o)) { + case iABC: + ps.print(a); + if (getBMode(o) != OpArgN) { + ps.print(" " + (ISK(b) ? (-1 - INDEXK(b)) : b)); + } + if (getCMode(o) != OpArgN) { + ps.print(" " + (ISK(c) ? (-1 - INDEXK(c)) : c)); + } + break; + case iABx: + if (getBMode(o) == OpArgK) { + ps.print(a + " " + (-1 - bx)); + } else { + ps.print(a + " " + (bx)); + } + break; + case iAsBx: + if (o == OP_JMP) { + ps.print(sbx); + } else { + ps.print(a + " " + sbx); + } + break; + } + switch (o) { + case OP_LOADK: + ps.print(" ; "); + printConstant(ps, f, bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + ps.print(" ; "); + if (f.upvalues.length > b) { + printValue(ps, f.upvalues[b]); + } else { + ps.print("-"); + } + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + ps.print(" ; "); + printConstant(ps, f, bx); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { + ps.print(" ; "); + printConstant(ps, f, INDEXK(c)); + } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) { + ps.print(" ; "); + if (ISK(b)) { + printConstant(ps, f, INDEXK(b)); + } else { + ps.print("-"); + } + ps.print(" "); + if (ISK(c)) { + printConstant(ps, f, INDEXK(c)); + } else { + ps.print("-"); + } + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + ps.print(" ; to " + (sbx + pc + 2)); + break; + case OP_CLOSURE: + ps.print(" ; " + f.p[bx].getClass().getName()); + break; + case OP_SETLIST: + if (c == 0) { + ps.print(" ; " + code[++pc]); + } else { + ps.print(" ; " + c); + } + break; + case OP_VARARG: + ps.print(" ; is_vararg=" + f.is_vararg); + break; + default: + break; + } + } + + private static int getline(Prototype f, int pc) { + return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length ? f.lineinfo[pc] : -1; + } + + private static void printHeader(PrintStream ps, Prototype f) { + String s = String.valueOf(f.source); + if (s.startsWith("@") || s.startsWith("=")) { + s = s.substring(1); + } else if ("\033Lua".equals(s)) { + s = "(bstring)"; + } else { + s = "(string)"; + } + String a = (f.linedefined == 0) ? "main" : "function"; + ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," + + f.lastlinedefined + "> (" + f.code.length + " instructions, " + + f.code.length * 4 + " bytes at " + id(f) + ")\n"); + ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " + + f.upvalues.length + " upvalue, "); + ps.print(f.locvars.length + " local, " + f.k.length + + " constant, " + f.p.length + " function\n"); + } + + private static void printConstants(PrintStream ps, Prototype f) { + int i, n = f.k.length; + ps.print("constants (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.print(" " + (i + 1) + " "); + printValue(ps, f.k[i]); + ps.print("\n"); + } + } + + private static void printLocals(PrintStream ps, Prototype f) { + int i, n = f.locvars.length; + ps.print("locals (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.println(" " + i + " " + f.locvars[i].name + " " + (f.locvars[i].startpc + 1) + " " + (f.locvars[i].endpc + 1)); + } + } + + private static void printUpValues(PrintStream ps, Prototype f) { + int i, n = f.upvalues.length; + ps.print("upvalues (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.print(" " + i + " " + f.upvalues[i] + "\n"); + } + } + + public static String show(Prototype p) { + return showWith(ps -> printFunction(ps, p, true)); + } + + public static void printFunction(PrintStream ps, Prototype f, boolean full) { + int i, n = f.p.length; + printHeader(ps, f); + printCode(ps, f); + if (full) { + printConstants(ps, f); + printLocals(ps, f); + printUpValues(ps, f); + } + for (i = 0; i < n; i++) { + printFunction(ps, f.p[i], full); + } + } + + private static void format(PrintStream ps, String s, int maxcols) { + int n = s.length(); + if (n > maxcols) { + ps.print(s.substring(0, maxcols)); + } else { + ps.print(s); + for (int i = maxcols - n; --i >= 0; ) { + ps.print(' '); + } + } + } + + private static String id(Prototype f) { + return f.sourceShort() + ":" + f.linedefined; + } + + /** + * Print the state of a {@link LuaClosure} that is being executed + * + * @param cl the {@link LuaClosure} + * @param pc the program counter + * @param stack the stack of {@link LuaValue} + * @param top the top of the stack + * @param varargs any {@link Varargs} value that may apply + */ + public static void printState(PrintStream ps, LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { + // print opcode into buffer + format(ps, showWith(p -> printOpcode(p, cl.getPrototype(), pc)), 50); + + // print stack + ps.print('['); + for (int i = 0; i < stack.length; i++) { + LuaValue v = stack[i]; + if (v == null) { + ps.print("null"); + } else { + switch (v.type()) { + case Constants.TSTRING: + LuaString s = (LuaString) v; + ps.print(s.length() < 48 ? + s.toString() : + s.substring(0, 32).toString() + "...+" + (s.length() - 32) + "b"); + break; + case Constants.TFUNCTION: + ps.print((v instanceof LuaClosure) ? + ((LuaClosure) v).getPrototype().toString() : v.toString()); + break; + case Constants.TUSERDATA: + Object o = v.toUserdata(); + if (o != null) { + String n = o.getClass().getName(); + n = n.substring(n.lastIndexOf('.') + 1); + ps.print(n + ": " + Integer.toHexString(o.hashCode())); + } else { + ps.print(v.toString()); + } + break; + default: + ps.print(v.toString()); + } + } + if (i + 1 == top) { + ps.print(']'); + } + ps.print(" | "); + } + ps.print(varargs); + ps.println(); + } + + private static String showWith(Consumer f) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + PrintStream ps; + try { + ps = new PrintStream(output, false, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("UTF-8 should exist", e); + } + f.accept(ps); + ps.flush(); + return new String(output.toByteArray(), StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java index 11ad5bdf..74568100 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java @@ -24,7 +24,6 @@ */ package org.squiddev.cobalt.compiler; -import com.sun.deploy.xml.XMLable; import org.squiddev.cobalt.*; import org.squiddev.cobalt.function.LocalVariable; import org.squiddev.cobalt.lib.Utf8Lib; diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index 098278b2..91a16080 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -87,7 +87,7 @@ protected static void _assert(boolean b, int line) throws CompileException { public static final int MAXSTACK = 250; public static final int LUAI_MAXUPVALUES = 60; public static final int LUAI_MAXVARS = 200; - public static boolean defaultLua52 = false; + public static boolean defaultLua52 = true; public static boolean blockGoto = false; @@ -199,6 +199,8 @@ public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaT closure.nilUpvalues(); if (useLua52 && p.nups == 1) { closure.setUpvalue(0, new Upvalue(env)); + } else if (!useLua52 && p.nups == 0) { + closure.setfenv(env); } return closure; } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index f2a07991..cfd62f41 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -162,12 +162,12 @@ public final Varargs invoke(LuaState state, Varargs varargs) throws LuaError, Un @Override public Upvalue getUpvalue(int i) { - return upvalues[i]; + return upvalues[i+(p.isLua52 ? 0 : 1)]; } @Override public void setUpvalue(int i, Upvalue upvalue) { - upvalues[i] = upvalue; + upvalues[i+(p.isLua52 ? 0 : 1)] = upvalue; } @Override diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index a32a763d..193e986f 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -219,15 +219,27 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError, UnwindThrow case 6: // "print", // (...) -> void { return noUnwind(state, () -> { - LuaValue tostring = OperationHelper.getTable(state, state.getCurrentThread().getfenv(), valueOf("tostring")); - for (int i = 1, n = args.count(); i <= n; i++) { - if (i > 1) state.stdout.write('\t'); - LuaString s = OperationHelper.call(state, tostring, args.arg(i)).strvalue(); - int z = s.indexOf((byte) 0, 0); - state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); + LuaTable env = state.getCurrentThread().getfenv(); + if (env != null) { + LuaValue tostring = OperationHelper.getTable(state, env, valueOf("tostring")); + for (int i = 1, n = args.count(); i <= n; i++) { + if (i > 1) state.stdout.write('\t'); + LuaString s = OperationHelper.call(state, tostring, args.arg(i)).strvalue(); + int z = s.indexOf((byte) 0, 0); + state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); + } + state.stdout.println(); + return Constants.NONE; + } else { + for (int i = 1, n = args.count(); i <= n; i++) { + if (i > 1) state.stdout.write('\t'); + LuaString s = OperationHelper.toString(state, args.arg(i)).strvalue(); + int z = s.indexOf((byte) 0, 0); + state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); + } + state.stdout.println(); + return Constants.NONE; } - state.stdout.println(); - return Constants.NONE; }); } case 7: // "select", // (f, ...) -> value1, ... diff --git a/src/main/java/org/squiddev/cobalt/lib/DebugLib.java b/src/main/java/org/squiddev/cobalt/lib/DebugLib.java index 713177b8..2c823a61 100644 --- a/src/main/java/org/squiddev/cobalt/lib/DebugLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/DebugLib.java @@ -400,8 +400,8 @@ private static Varargs _getregistry(Varargs args) { private static LuaString findupvalue(LuaClosure c, int up) { Prototype p = c.getPrototype(); - if (up > 0 && p.upvalues != null && up <= p.upvalues.length) { - return p.upvalues[up - 1]; + if (up > 0 && p.upvalues != null && up + (p.isLua52 ? 0 : 1) <= p.upvalues.length) { + return p.upvalues[up - 1 + (p.isLua52 ? 0 : 1)]; } else { return null; } diff --git a/src/test/java/org/squiddev/cobalt/AssertTests.java b/src/test/java/org/squiddev/cobalt/AssertTests.java index f13a0ce6..fa6a559d 100644 --- a/src/test/java/org/squiddev/cobalt/AssertTests.java +++ b/src/test/java/org/squiddev/cobalt/AssertTests.java @@ -130,6 +130,7 @@ public LuaValue call(LuaState state) { @ParameterizedTest(name = ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER) @ValueSource(strings = { "bitwise", + "goto", }) public void lua52(String name) throws Exception { ScriptHelper helpers = new ScriptHelper("/assert/lua5.2/"); diff --git a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java index e58fd52e..af31d385 100644 --- a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java +++ b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java @@ -24,6 +24,7 @@ */ package org.squiddev.cobalt.compiler; +import org.squiddev.cobalt.LuaString; import org.squiddev.cobalt.Print; import org.squiddev.cobalt.Prototype; @@ -44,7 +45,8 @@ public class CompileTestHelper { */ public static void compareResults(String dir, String file) throws IOException, CompileException { // Compile in memory - String sourceBytecode = Print.show(LuaC.compile(new ByteArrayInputStream(bytesFromJar(dir + file + ".lua")), "@" + file + ".lua")); + // Force Lua 5.1 output since 5.2 tests haven't been written yet + String sourceBytecode = Print.show(LuaC.compile(new ByteArrayInputStream(bytesFromJar(dir + file + ".lua")), LuaString.valueOf("@" + file + ".lua"), null, false)); // Load expected value from jar Prototype expectedPrototype = loadFromBytes(bytesFromJar(dir + file + ".lc"), file + ".lua"); diff --git a/src/test/resources/compare/errors/baselibargs.lua b/src/test/resources/compare/errors/baselibargs.lua index dfb0531f..ac4ce4d0 100644 --- a/src/test/resources/compare/errors/baselibargs.lua +++ b/src/test/resources/compare/errors/baselibargs.lua @@ -29,7 +29,8 @@ checkallerrors('error', { { 123 }, { nil, 1, 2, n = 3 } }, 123) -- getfenv banner('getfenv') -checkallpass('getfenv', { { nil, print, function() end, 0, 1, 2, n = 5 } }) +--checkallpass('getfenv', { { nil, print, function() end, 0, 1, 2, n = 4 } }) +checkallpass('getfenv', { { function() end, 0 } }) -- Java functions no longer have environments (including pcall, which was position 1) checkallerrors('getfenv', { { true, {}, 'abc' } }, 'bad argument') -- getmetatable diff --git a/src/test/resources/compare/errors/baselibargs.out b/src/test/resources/compare/errors/baselibargs.out index dd3b81c9..bb3ed03f 100644 --- a/src/test/resources/compare/errors/baselibargs.out +++ b/src/test/resources/compare/errors/baselibargs.out @@ -54,11 +54,8 @@ - error(123,2) ...123... ====== getfenv ====== --- checkallpass -- getfenv(nil) -- getfenv()
- getfenv()
- getfenv(0)
-- getfenv(1)
--- checkallerrors - getfenv(true) ...bad argument... - getfenv(
) ...bad argument... From b572f476e5de49facfc4e51c3e142c006d0b5275 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Mon, 5 Apr 2021 19:47:30 -0400 Subject: [PATCH 13/24] Removed Lua 5.1 compiler, fixed Print a bit --- .../java/org/squiddev/cobalt/LuaState.java | 18 - src/main/java/org/squiddev/cobalt/Print.java | 46 +- .../java/org/squiddev/cobalt/Print52.java | 222 +- .../cobalt/compiler/BytecodeLoader.java | 14 +- .../squiddev/cobalt/compiler/FuncState.java | 297 ++- .../squiddev/cobalt/compiler/FuncState52.java | 1188 --------- .../squiddev/cobalt/compiler/LexState.java | 453 +++- .../squiddev/cobalt/compiler/LexState52.java | 2215 ----------------- .../squiddev/cobalt/compiler/LoadState.java | 17 +- .../org/squiddev/cobalt/compiler/LuaC.java | 96 +- .../java/org/squiddev/cobalt/lib/BaseLib.java | 2 +- .../cobalt/compiler/CompileTestHelper.java | 4 +- 12 files changed, 554 insertions(+), 4018 deletions(-) delete mode 100644 src/main/java/org/squiddev/cobalt/compiler/FuncState52.java delete mode 100644 src/main/java/org/squiddev/cobalt/compiler/LexState52.java diff --git a/src/main/java/org/squiddev/cobalt/LuaState.java b/src/main/java/org/squiddev/cobalt/LuaState.java index 95efd1e0..b5bcbe2a 100644 --- a/src/main/java/org/squiddev/cobalt/LuaState.java +++ b/src/main/java/org/squiddev/cobalt/LuaState.java @@ -134,11 +134,6 @@ public final class LuaState { */ boolean abandoned; - /** - * Whether to compile for Lua 5.1 or 5.2. - */ - public boolean useLua52; - public LuaState() { this(new LuaState.Builder()); } @@ -158,7 +153,6 @@ private LuaState(Builder builder) { this.debug = builder.debug; this.timezone = builder.timezone; this.threader = new YieldThreader(builder.coroutineExecutor); - this.useLua52 = builder.useLua52; } /** @@ -236,7 +230,6 @@ public static class Builder { private DebugHandler debug = DebugHandler.INSTANCE; private TimeZone timezone = TimeZone.getDefault(); private Executor coroutineExecutor = defaultCoroutineExecutor; - private boolean useLua52 = LuaC.defaultLua52; /** * Build a Lua state from this builder @@ -413,16 +406,5 @@ public Builder coroutineExecutor(Executor coroutineExecutor) { this.coroutineExecutor = coroutineExecutor; return this; } - - /** - * Set whether the state generates Lua 5.2 bytecode. - * - * @param lua52 Whether to use Lua 5.2 - * @return This builder - */ - public Builder useLua52(boolean lua52) { - this.useLua52 = lua52; - return this; - } } } diff --git a/src/main/java/org/squiddev/cobalt/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index ace3cbc3..b5df08be 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -83,50 +83,6 @@ public class Print { null, }; - public static final String[] OPNAMES_52 = { - "MOVE", - "LOADK", - "LOADKX", - "LOADBOOL", - "LOADNIL", - "GETUPVAL", - "GETTABUP", - "GETTABLE", - "SETTABUP", - "SETUPVAL", - "SETTABLE", - "NEWTABLE", - "SELF", - "ADD", - "SUB", - "MUL", - "DIV", - "MOD", - "POW", - "UNM", - "NOT", - "LEN", - "CONCAT", - "JMP", - "EQ", - "LT", - "LE", - "TEST", - "TESTSET", - "CALL", - "TAILCALL", - "RETURN", - "FORLOOP", - "FORPREP", - "TFORCALL", - "TFORLOOP", - "SETLIST", - "CLOSURE", - "VARARG", - "EXTRAARG", - null, - }; - static void printString(PrintStream ps, final LuaString s) { @@ -230,7 +186,7 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { } else { ps.print("[-] "); } - ps.print((f.isLua52 ? OPNAMES_52[o] : OPNAMES[o]) + " "); + ps.print(OPNAMES[o] + " "); switch (getOpMode(o)) { case iABC: ps.print(a); diff --git a/src/main/java/org/squiddev/cobalt/Print52.java b/src/main/java/org/squiddev/cobalt/Print52.java index 5e31e34f..122f859a 100644 --- a/src/main/java/org/squiddev/cobalt/Print52.java +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -42,48 +42,6 @@ */ public class Print52 { public static final String[] OPNAMES = { - "MOVE", - "LOADK", - "LOADBOOL", - "LOADNIL", - "GETUPVAL", - "GETGLOBAL", - "GETTABLE", - "SETGLOBAL", - "SETUPVAL", - "SETTABLE", - "NEWTABLE", - "SELF", - "ADD", - "SUB", - "MUL", - "DIV", - "MOD", - "POW", - "UNM", - "NOT", - "LEN", - "CONCAT", - "JMP", - "EQ", - "LT", - "LE", - "TEST", - "TESTSET", - "CALL", - "TAILCALL", - "RETURN", - "FORLOOP", - "FORPREP", - "TFORLOOP", - "SETLIST", - "CLOSE", - "CLOSURE", - "VARARG", - null, - }; - - public static final String[] OPNAMES_52 = { "MOVE", "LOADK", "LOADKX", @@ -189,20 +147,6 @@ private static void printConstant(PrintStream ps, Prototype f, int i) { printValue(ps, f.k[i]); } - /** - * Print the code in a prototype - * - * @param f the {@link Prototype} - */ - public static void printCode(PrintStream ps, Prototype f) { - int[] code = f.code; - int pc, n = code.length; - for (pc = 0; pc < n; pc++) { - printOpcode(ps, f, pc); - ps.println(); - } - } - /** * Print an opcode in a prototype * @@ -226,7 +170,7 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { } else { ps.print("[-] "); } - ps.print((f.isLua52 ? OPNAMES_52[o] : OPNAMES[o]) + " "); + ps.print(OPNAMES[o] + " "); switch (getOpMode(o)) { case iABC: ps.print(a); @@ -257,6 +201,10 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { ps.print(" ; "); printConstant(ps, f, bx); break; + case OP_LOADKX: + ps.print(" ; "); + printConstant(ps, f, GETARG_Ax(code[pc+1])); + break; case OP_GETUPVAL: case OP_SETUPVAL: ps.print(" ; "); @@ -266,10 +214,17 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { ps.print("-"); } break; - case OP_GETGLOBAL: - case OP_SETGLOBAL: + case OP_GETTABUP: + case OP_SETTABUP: ps.print(" ; "); - printConstant(ps, f, bx); + if (f.upvalues.length > b) { + printValue(ps, f.upvalues[b]); + } else { + ps.print("-"); + } + ps.print("["); + printConstant(ps, f, c); + ps.print("]"); break; case OP_GETTABLE: case OP_SELF: @@ -305,6 +260,7 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { case OP_JMP: case OP_FORLOOP: case OP_FORPREP: + case OP_TFORLOOP: ps.print(" ; to " + (sbx + pc + 2)); break; case OP_CLOSURE: @@ -328,150 +284,4 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { private static int getline(Prototype f, int pc) { return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length ? f.lineinfo[pc] : -1; } - - private static void printHeader(PrintStream ps, Prototype f) { - String s = String.valueOf(f.source); - if (s.startsWith("@") || s.startsWith("=")) { - s = s.substring(1); - } else if ("\033Lua".equals(s)) { - s = "(bstring)"; - } else { - s = "(string)"; - } - String a = (f.linedefined == 0) ? "main" : "function"; - ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," - + f.lastlinedefined + "> (" + f.code.length + " instructions, " - + f.code.length * 4 + " bytes at " + id(f) + ")\n"); - ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " - + f.upvalues.length + " upvalue, "); - ps.print(f.locvars.length + " local, " + f.k.length - + " constant, " + f.p.length + " function\n"); - } - - private static void printConstants(PrintStream ps, Prototype f) { - int i, n = f.k.length; - ps.print("constants (" + n + ") for " + id(f) + ":\n"); - for (i = 0; i < n; i++) { - ps.print(" " + (i + 1) + " "); - printValue(ps, f.k[i]); - ps.print("\n"); - } - } - - private static void printLocals(PrintStream ps, Prototype f) { - int i, n = f.locvars.length; - ps.print("locals (" + n + ") for " + id(f) + ":\n"); - for (i = 0; i < n; i++) { - ps.println(" " + i + " " + f.locvars[i].name + " " + (f.locvars[i].startpc + 1) + " " + (f.locvars[i].endpc + 1)); - } - } - - private static void printUpValues(PrintStream ps, Prototype f) { - int i, n = f.upvalues.length; - ps.print("upvalues (" + n + ") for " + id(f) + ":\n"); - for (i = 0; i < n; i++) { - ps.print(" " + i + " " + f.upvalues[i] + "\n"); - } - } - - public static String show(Prototype p) { - return showWith(ps -> printFunction(ps, p, true)); - } - - public static void printFunction(PrintStream ps, Prototype f, boolean full) { - int i, n = f.p.length; - printHeader(ps, f); - printCode(ps, f); - if (full) { - printConstants(ps, f); - printLocals(ps, f); - printUpValues(ps, f); - } - for (i = 0; i < n; i++) { - printFunction(ps, f.p[i], full); - } - } - - private static void format(PrintStream ps, String s, int maxcols) { - int n = s.length(); - if (n > maxcols) { - ps.print(s.substring(0, maxcols)); - } else { - ps.print(s); - for (int i = maxcols - n; --i >= 0; ) { - ps.print(' '); - } - } - } - - private static String id(Prototype f) { - return f.sourceShort() + ":" + f.linedefined; - } - - /** - * Print the state of a {@link LuaClosure} that is being executed - * - * @param cl the {@link LuaClosure} - * @param pc the program counter - * @param stack the stack of {@link LuaValue} - * @param top the top of the stack - * @param varargs any {@link Varargs} value that may apply - */ - public static void printState(PrintStream ps, LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { - // print opcode into buffer - format(ps, showWith(p -> printOpcode(p, cl.getPrototype(), pc)), 50); - - // print stack - ps.print('['); - for (int i = 0; i < stack.length; i++) { - LuaValue v = stack[i]; - if (v == null) { - ps.print("null"); - } else { - switch (v.type()) { - case Constants.TSTRING: - LuaString s = (LuaString) v; - ps.print(s.length() < 48 ? - s.toString() : - s.substring(0, 32).toString() + "...+" + (s.length() - 32) + "b"); - break; - case Constants.TFUNCTION: - ps.print((v instanceof LuaClosure) ? - ((LuaClosure) v).getPrototype().toString() : v.toString()); - break; - case Constants.TUSERDATA: - Object o = v.toUserdata(); - if (o != null) { - String n = o.getClass().getName(); - n = n.substring(n.lastIndexOf('.') + 1); - ps.print(n + ": " + Integer.toHexString(o.hashCode())); - } else { - ps.print(v.toString()); - } - break; - default: - ps.print(v.toString()); - } - } - if (i + 1 == top) { - ps.print(']'); - } - ps.print(" | "); - } - ps.print(varargs); - ps.println(); - } - - private static String showWith(Consumer f) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - PrintStream ps; - try { - ps = new PrintStream(output, false, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 should exist", e); - } - f.accept(ps); - ps.flush(); - return new String(output.toByteArray(), StandardCharsets.UTF_8); - } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java index b8734693..f2728076 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java +++ b/src/main/java/org/squiddev/cobalt/compiler/BytecodeLoader.java @@ -39,17 +39,17 @@ */ public final class BytecodeLoader { /** - * format corresponding to non-number-patched lua, all numbers are floats or doubles + * format corresponding to non-number-patched Lua, all numbers are floats or doubles */ public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; /** - * format corresponding to non-number-patched lua, all numbers are ints + * format corresponding to non-number-patched Lua, all numbers are ints */ public static final int NUMBER_FORMAT_INTS_ONLY = 1; /** - * format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints + * format corresponding to number-patched Lua, all numbers are 32-bit (4 byte) ints */ public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; @@ -161,9 +161,9 @@ private long loadInt64() throws IOException { } /** - * Load a lua strin gvalue from the input stream + * Load a Lua string value from the input stream * - * @return the {@link LuaString} value laoded. + * @return the {@link LuaString} value loaded. */ private LuaString loadString() throws IOException { int size = this.luacSizeofSizeT == 8 ? (int) loadInt64() : loadInt(); @@ -256,7 +256,7 @@ private void loadConstants(Prototype f) throws IOException { } /** - * Load the debug infor for a function prototype + * Load the debug info for a function prototype * * @param f the function Prototype * @throws IOException if there is an i/o exception @@ -325,7 +325,7 @@ public Prototype loadFunction(LuaString p) throws IOException { } /** - * Load the lua chunk header values. + * Load the Lua chunk header values. * * @throws IOException if an i/o exception occurs. * @throws CompileException If the bytecode is invalid. diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index a5866048..1449d407 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -33,21 +33,24 @@ import java.util.Hashtable; import static org.squiddev.cobalt.Constants.*; -import static org.squiddev.cobalt.Lua.*; +import static org.squiddev.cobalt.Lua52.*; +import static org.squiddev.cobalt.compiler.LexState.NO_JUMP; import static org.squiddev.cobalt.compiler.LuaC.*; public class FuncState { class upvaldesc { - short k; - short info; + boolean instack; + short idx; + LuaString name; } static class BlockCnt { BlockCnt previous; /* chain */ - IntPtr breaklist = new IntPtr(); /* list of jumps out of this loop */ + short firstlabel; /* index of first label in this block */ + short firstgoto; /* index of first pending goto in this block */ short nactvar; /* # active locals outside the breakable structure */ boolean upval; /* true if some variable in the block is an upvalue */ - boolean isbreakable; /* true if `block' is a loop */ + boolean isloop; /* true if `block' is a loop */ } Prototype f; /* current function header */ @@ -69,6 +72,7 @@ static class BlockCnt { short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ FuncState() { + } @@ -77,11 +81,11 @@ static class BlockCnt { // ============================================================= InstructionPtr getcodePtr(expdesc e) { - return new InstructionPtr(f.code, e.u.s.info); + return new InstructionPtr(f.code, e.u.info); } int getcode(expdesc e) { - return f.code[e.u.s.info]; + return f.code[e.u.info]; } int codeAsBx(int o, int A, int sBx) throws CompileException { @@ -115,27 +119,33 @@ private void errorlimit(int limit, String what) throws CompileException { } - private int indexupvalue(LuaString name, expdesc v) throws CompileException { - int i; - for (i = 0; i < f.nups; i++) { - if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) { - _assert(f.upvalues[i] == name); - return i; - } - } + int newupvalue(LuaString name, expdesc v) throws CompileException { /* new one */ checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); if (f.upvalues == null || f.nups + 1 > f.upvalues.length) { f.upvalues = realloc(f.upvalues, f.nups * 2 + 1); } + if (f.upvalue_info == null || f.nups + 1 > f.upvalue_info.length) { + f.upvalue_info = realloc(f.upvalue_info, f.nups * 2 + 1); + } f.upvalues[f.nups] = name; + f.upvalue_info[f.nups] = ((v.k == LexState.VLOCAL ? 1 : 0) << 8) | v.u.info; _assert(v.k == LexState.VLOCAL || v.k == LexState.VUPVAL); upvalues[f.nups] = new upvaldesc(); - upvalues[f.nups].k = (short) (v.k); - upvalues[f.nups].info = (short) (v.u.s.info); + upvalues[f.nups].instack = (v.k == LexState.VLOCAL); + upvalues[f.nups].idx = (short) (v.u.info); + upvalues[f.nups].name = name; return f.nups++; } + private int searchupvalue(LuaString n) { + int i; + for (i = 0; i < f.nups; i++) { + if (f.upvalues[i].equals(n)) return i; + } + return -1; /* not found */ + } + private int searchvar(LuaString n) { int i; for (i = nactvar - 1; i >= 0; i--) { @@ -164,25 +174,27 @@ int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { markupval(v); /* local will be used as an upval */ } return LexState.VLOCAL; - } else { /* not found at current level; try upper one */ - if (prev == null) { /* no more levels? */ - /* default is global variable */ - var.init(LexState.VGLOBAL, NO_REG); - return LexState.VGLOBAL; - } - if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) { - return LexState.VGLOBAL; + } else { /* not found at current level; try upvalues */ + int idx = searchupvalue(n); + if (idx < 0) { /* not found? */ + if (prev == null) { /* no more levels? */ + return LexState.VVOID; + } + if (prev.singlevaraux(n, var, 0) == LexState.VVOID) { + return LexState.VVOID; + } + idx = this.newupvalue(n, var); /* else was LOCAL or UPVAL */ } - var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ - var.k = LexState.VUPVAL; /* upvalue in this level */ + var.init(LexState.VUPVAL, idx); return LexState.VUPVAL; } } - void enterblock(BlockCnt bl, boolean isbreakable) throws CompileException { - bl.breaklist.i = LexState.NO_JUMP; - bl.isbreakable = isbreakable; + void enterblock(BlockCnt bl, boolean isloop) throws CompileException { + bl.isloop = isloop; bl.nactvar = this.nactvar; + bl.firstlabel = ls.dyd.nlabel; + bl.firstgoto = ls.dyd.ngt; bl.upval = false; bl.previous = this.bl; this.bl = bl; @@ -205,16 +217,30 @@ void enterblock(BlockCnt bl, boolean isbreakable) throws CompileException { void leaveblock() throws CompileException { BlockCnt bl = this.bl; + if (bl.previous != null && bl.upval) { + /* create a 'jump to here' to close upvalues */ + int j = jump(); + patchclose(j, bl.nactvar); + patchtohere(j); + } + if (bl.isloop) { + ls.breaklabel(); + } this.bl = bl.previous; ls.removevars(bl.nactvar); - if (bl.upval) { + /*if (bl.upval) { this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); - } + }*/ /* a block either controls scope or breaks (never both) */ - _assert(!bl.isbreakable || !bl.upval); + _assert(!bl.isloop || !bl.upval); _assert(bl.nactvar == this.nactvar); this.freereg = this.nactvar; /* free registers */ - this.patchtohere(bl.breaklist.i); + ls.dyd.nlabel = bl.firstlabel; + if (bl.previous != null) { + movegotosout(bl); + } else if (bl.firstgoto < ls.dyd.ngt) { + ls.undefgoto(ls.dyd.gt[bl.firstgoto]); + } } void closelistfield(ConsControl cc) throws CompileException { @@ -224,7 +250,7 @@ void closelistfield(ConsControl cc) throws CompileException { this.exp2nextreg(cc.v); cc.v.k = LexState.VVOID; if (cc.tostore == LFIELDS_PER_FLUSH) { - this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */ + this.setlist(cc.t.u.info, cc.na, cc.tostore); /* flush */ cc.tostore = 0; /* no more items pending */ } } @@ -237,13 +263,13 @@ void lastlistfield(ConsControl cc) throws CompileException { if (cc.tostore == 0) return; if (hasmultret(cc.v.k)) { this.setmultret(cc.v); - this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET); + this.setlist(cc.t.u.info, cc.na, LUA_MULTRET); cc.na--; /* do not count last expression (unknown number of elements) */ } else { if (cc.v.k != LexState.VVOID) { this.exp2nextreg(cc.v); } - this.setlist(cc.t.u.s.info, cc.na, cc.tostore); + this.setlist(cc.t.u.info, cc.na, cc.tostore); } } @@ -254,6 +280,7 @@ void lastlistfield(ConsControl cc) throws CompileException { void nil(int from, int n) throws CompileException { InstructionPtr previous; + int l = from + n - 1; if (this.pc > this.lasttarget) { /* no jumps to current position? */ if (this.pc == 0) { /* function start? */ if (from >= this.nactvar) { @@ -263,25 +290,27 @@ void nil(int from, int n) throws CompileException { previous = new InstructionPtr(this.f.code, this.pc - 1); if (GET_OPCODE(previous.get()) == OP_LOADNIL) { int pfrom = GETARG_A(previous.get()); - int pto = GETARG_B(previous.get()); - if (pfrom <= from && from <= pto + 1) { /* can connect both? */ - if (from + n - 1 > pto) { - SETARG_B(previous, from + n - 1); - } + int pl = pfrom + GETARG_B(previous.get()); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; + if (pl > l) l = pl; + SETARG_A(previous, from); + SETARG_B(previous, l - from); return; } } } } /* else no optimization */ - this.codeABC(OP_LOADNIL, from, from + n - 1, 0); + this.codeABC(OP_LOADNIL, from, n - 1, 0); } int jump() throws CompileException { int jpc = this.jpc.i; /* save list of jumps to here */ - this.jpc.i = LexState.NO_JUMP; - IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState.NO_JUMP)); + this.jpc.i = NO_JUMP; + IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, NO_JUMP)); this.concat(j, jpc); /* keep them on hold */ return j.i; } @@ -298,7 +327,7 @@ private int condjump(int /* OpCode */op, int A, int B, int C) throws CompileExce private void fixjump(int pc, int dest) throws CompileException { InstructionPtr jmp = new InstructionPtr(this.f.code, pc); int offset = dest - (pc + 1); - _assert(dest != LexState.NO_JUMP); + _assert(dest != NO_JUMP); if (Math.abs(offset) > MAXARG_sBx) { throw ls.syntaxError("control structure too long"); } @@ -319,9 +348,9 @@ int getlabel() { private int getjump(int pc) { int offset = GETARG_sBx(this.f.code[pc]); /* point to itself represents end of list */ - if (offset == LexState.NO_JUMP) + if (offset == NO_JUMP) /* end of list */ { - return LexState.NO_JUMP; + return NO_JUMP; } else /* turn offset into absolute position */ { return (pc + 1) + offset; @@ -344,7 +373,7 @@ private InstructionPtr getjumpcontrol(int pc) { * produce an inverted value) */ private boolean need_value(int list) { - for (; list != LexState.NO_JUMP; list = this.getjump(list)) { + for (; list != NO_JUMP; list = this.getjump(list)) { int i = this.getjumpcontrol(list).get(); if (GET_OPCODE(i) != OP_TESTSET) { return true; @@ -364,7 +393,7 @@ private boolean patchtestreg(int node, int reg) { SETARG_A(i, reg); } else /* no register to put value or register already has the value */ { - i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get()))); + i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua52.GETARG_C(i.get()))); } return true; @@ -372,13 +401,13 @@ private boolean patchtestreg(int node, int reg) { private void removevalues(int list) { - for (; list != LexState.NO_JUMP; list = this.getjump(list)) { + for (; list != NO_JUMP; list = this.getjump(list)) { this.patchtestreg(list, NO_REG); } } private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws CompileException { - while (list != LexState.NO_JUMP) { + while (list != NO_JUMP) { int next = this.getjump(list); if (this.patchtestreg(list, reg)) { this.fixjump(list, vtarget); @@ -391,7 +420,7 @@ private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws Co private void dischargejpc() throws CompileException { this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); - this.jpc.i = LexState.NO_JUMP; + this.jpc.i = NO_JUMP; } void patchlist(int list, int target) throws CompileException { @@ -403,21 +432,33 @@ void patchlist(int list, int target) throws CompileException { } } + void patchclose(int list, int level) throws CompileException { + level++; /* argument is +1 to reserve 0 as non-op */ + while (list != NO_JUMP) { + int next = getjump(list); + LuaC._assert(GET_OPCODE(f.code[list]) == OP_JMP && + GETARG_A(f.code[list]) == 0 || + GETARG_A(f.code[list]) >= level); + f.code[list] = (f.code[list] & MASK_NOT_A) | (level << POS_A); + list = next; + } + } + void patchtohere(int list) throws CompileException { this.getlabel(); this.concat(this.jpc, list); } void concat(IntPtr l1, int l2) throws CompileException { - if (l2 == LexState.NO_JUMP) { + if (l2 == NO_JUMP) { return; } - if (l1.i == LexState.NO_JUMP) { + if (l1.i == NO_JUMP) { l1.i = l2; } else { int list = l1.i; int next; - while ((next = this.getjump(list)) != LexState.NO_JUMP) + while ((next = this.getjump(list)) != NO_JUMP) /* find last element */ { list = next; } @@ -449,7 +490,7 @@ private void freereg(int reg) throws CompileException { private void freeexp(expdesc e) throws CompileException { if (e.k == LexState.VNONRELOC) { - this.freereg(e.u.s.info); + this.freereg(e.u.info); } } @@ -505,7 +546,7 @@ void setreturns(expdesc e, int nresults) throws CompileException { void setoneret(expdesc e) { if (e.k == LexState.VCALL) { /* expression is an open function call? */ e.k = LexState.VNONRELOC; - e.u.s.info = GETARG_A(this.getcode(e)); + e.u.info = GETARG_A(this.getcode(e)); } else if (e.k == LexState.VVARARG) { SETARG_B(this.getcodePtr(e), 2); e.k = LexState.VRELOCABLE; /* can relocate its simple result */ @@ -519,20 +560,18 @@ void dischargevars(expdesc e) throws CompileException { break; } case LexState.VUPVAL: { - e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0); - e.k = LexState.VRELOCABLE; - break; - } - case LexState.VGLOBAL: { - e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info); + e.u.info = this.codeABC(OP_GETUPVAL, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } case LexState.VINDEXED: { - this.freereg(e.u.s.aux); - this.freereg(e.u.s.info); - e.u.s.info = this - .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux); + int op = Lua52.OP_GETTABUP; + this.freereg(e.u.ind.idx); + if (e.u.ind.vt == LexState.VLOCAL) { + this.freereg(e.u.ind.t); + op = Lua52.OP_GETTABLE; + } + e.u.info = this.codeABC(op, 0, e.u.ind.t, e.u.ind.idx); e.k = LexState.VRELOCABLE; break; } @@ -565,7 +604,7 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState.VK: { - this.codeABx(OP_LOADK, reg, e.u.s.info); + this.codeABx(OP_LOADK, reg, e.u.info); break; } case LexState.VKNUM: { @@ -578,8 +617,8 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState.VNONRELOC: { - if (reg != e.u.s.info) { - this.codeABC(OP_MOVE, reg, e.u.s.info, 0); + if (reg != e.u.info) { + this.codeABC(OP_MOVE, reg, e.u.info, 0); } break; } @@ -588,7 +627,7 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { return; /* nothing to do... */ } } - e.u.s.info = reg; + e.u.info = reg; e.k = LexState.VNONRELOC; } @@ -602,14 +641,14 @@ private void discharge2anyreg(expdesc e) throws CompileException { private void exp2reg(expdesc e, int reg) throws CompileException { this.discharge2reg(e, reg); if (e.k == LexState.VJMP) { - this.concat(e.t, e.u.s.info); /* put this jump in `t' list */ + this.concat(e.t, e.u.info); /* put this jump in `t' list */ } if (e.hasjumps()) { int _final; /* position after whole expression */ - int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */ - int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (this.need_value(e.t.i) || this.need_value(e.f.i)) { - int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this + int fj = (e.k == LexState.VJMP) ? NO_JUMP : this .jump(); p_f = this.code_label(reg, 0, 1); p_t = this.code_label(reg, 1, 0); @@ -619,8 +658,8 @@ private void exp2reg(expdesc e, int reg) throws CompileException { this.patchlistaux(e.f.i, _final, reg, p_f); this.patchlistaux(e.t.i, _final, reg, p_t); } - e.f.i = e.t.i = LexState.NO_JUMP; - e.u.s.info = reg; + e.f.i = e.t.i = NO_JUMP; + e.u.info = reg; e.k = LexState.VNONRELOC; } @@ -635,15 +674,15 @@ int exp2anyreg(expdesc e) throws CompileException { this.dischargevars(e); if (e.k == LexState.VNONRELOC) { if (!e.hasjumps()) { - return e.u.s.info; /* exp is already in a register */ + return e.u.info; /* exp is already in a register */ } - if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */ - this.exp2reg(e, e.u.s.info); /* put value on it */ - return e.u.s.info; + if (e.u.info >= this.nactvar) { /* reg. is not a local? */ + this.exp2reg(e, e.u.info); /* put value on it */ + return e.u.info; } } this.exp2nextreg(e); /* default */ - return e.u.s.info; + return e.u.info; } void exp2val(expdesc e) throws CompileException { @@ -662,18 +701,18 @@ int exp2RK(expdesc e) throws CompileException { case LexState.VFALSE: case LexState.VNIL: { if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ - e.u.s.info = (e.k == LexState.VNIL) ? this.nilK() + e.u.info = (e.k == LexState.VNIL) ? this.nilK() : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval()) : this.boolK((e.k == LexState.VTRUE)); e.k = LexState.VK; - return RKASK(e.u.s.info); + return RKASK(e.u.info); } else { break; } } case LexState.VK: { - if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */ { - return RKASK(e.u.s.info); + if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ { + return RKASK(e.u.info); } else { break; } @@ -689,22 +728,18 @@ void storevar(expdesc var, expdesc ex) throws CompileException { switch (var.k) { case LexState.VLOCAL: { this.freeexp(ex); - this.exp2reg(ex, var.u.s.info); + this.exp2reg(ex, var.u.info); return; } case LexState.VUPVAL: { int e = this.exp2anyreg(ex); - this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0); - break; - } - case LexState.VGLOBAL: { - int e = this.exp2anyreg(ex); - this.codeABx(OP_SETGLOBAL, e, var.u.s.info); + this.codeABC(OP_SETUPVAL, e, var.u.info, 0); break; } case LexState.VINDEXED: { + int op = var.u.ind.vt == LexState.VLOCAL ? Lua52.OP_SETTABLE : Lua52.OP_SETTABUP; int e = this.exp2RK(ex); - this.codeABC(OP_SETTABLE, var.u.s.info, var.u.s.aux, e); + this.codeABC(op, var.u.ind.t, var.u.ind.idx, e); break; } default: { @@ -721,16 +756,16 @@ void self(expdesc e, expdesc key) throws CompileException { this.freeexp(e); func = this.freereg; this.reserveregs(2); - this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key)); + this.codeABC(OP_SELF, func, e.u.info, this.exp2RK(key)); this.freeexp(key); - e.u.s.info = func; + e.u.info = func; e.k = LexState.VNONRELOC; } private void invertjump(expdesc e) throws CompileException { - InstructionPtr pc = this.getjumpcontrol(e.u.s.info); + InstructionPtr pc = this.getjumpcontrol(e.u.info); _assert(testTMode(GET_OPCODE(pc.get())) - && GET_OPCODE(pc.get()) != OP_TESTSET && Lua + && GET_OPCODE(pc.get()) != OP_TESTSET && Lua52 .GET_OPCODE(pc.get()) != OP_TEST); // SETARG_A(pc, !(GETARG_A(pc.get()))); int a = GETARG_A(pc.get()); @@ -749,7 +784,7 @@ private int jumponcond(expdesc e, int cond) throws CompileException { } this.discharge2anyreg(e); this.freeexp(e); - return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond); + return this.condjump(OP_TESTSET, NO_REG, e.u.info, cond); } void goiftrue(expdesc e) throws CompileException { @@ -759,7 +794,7 @@ void goiftrue(expdesc e) throws CompileException { case LexState.VK: case LexState.VKNUM: case LexState.VTRUE: { - pc = LexState.NO_JUMP; /* always true; do nothing */ + pc = NO_JUMP; /* always true; do nothing */ break; } case LexState.VFALSE: { @@ -768,7 +803,7 @@ void goiftrue(expdesc e) throws CompileException { } case LexState.VJMP: { this.invertjump(e); - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -778,16 +813,16 @@ void goiftrue(expdesc e) throws CompileException { } this.concat(e.f, pc); /* insert last jump in `f' list */ this.patchtohere(e.t.i); - e.t.i = LexState.NO_JUMP; + e.t.i = NO_JUMP; } - private void goiffalse(expdesc e) throws CompileException { + void goiffalse(expdesc e) throws CompileException { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { case LexState.VNIL: case LexState.VFALSE: { - pc = LexState.NO_JUMP; /* always false; do nothing */ + pc = NO_JUMP; /* always false; do nothing */ break; } case LexState.VTRUE: { @@ -795,7 +830,7 @@ private void goiffalse(expdesc e) throws CompileException { break; } case LexState.VJMP: { - pc = e.u.s.info; + pc = e.u.info; break; } default: { @@ -805,7 +840,7 @@ private void goiffalse(expdesc e) throws CompileException { } this.concat(e.t, pc); /* insert last jump in `t' list */ this.patchtohere(e.f.i); - e.f.i = LexState.NO_JUMP; + e.f.i = NO_JUMP; } private void codenot(expdesc e) throws CompileException { @@ -830,7 +865,7 @@ private void codenot(expdesc e) throws CompileException { case LexState.VNONRELOC: { this.discharge2anyreg(e); this.freeexp(e); - e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0); + e.u.info = this.codeABC(OP_NOT, 0, e.u.info, 0); e.k = LexState.VRELOCABLE; break; } @@ -850,7 +885,9 @@ private void codenot(expdesc e) throws CompileException { } void indexed(expdesc t, expdesc k) throws CompileException { - t.u.s.aux = this.exp2RK(k); + t.u.ind.t = t.u.info; + t.u.ind.idx = this.exp2RK(k); + t.u.ind.vt = t.k == LexState.VUPVAL ? LexState.VUPVAL : LexState.VLOCAL; t.k = LexState.VINDEXED; } @@ -917,7 +954,7 @@ private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { this.freeexp(e2); this.freeexp(e1); } - e1.u.s.info = this.codeABC(op, 0, o1, o2); + e1.u.info = this.codeABC(op, 0, o1, o2); e1.k = LexState.VRELOCABLE; } } @@ -934,7 +971,7 @@ private void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) thro o2 = temp; /* o1 <==> o2 */ cond = 1; } - e1.u.s.info = this.condjump(op, cond, o1, o2); + e1.u.info = this.condjump(op, cond, o1, o2); e1.k = LexState.VJMP; } @@ -998,7 +1035,7 @@ void infix(int /* BinOpr */op, expdesc v) throws CompileException { void posfix(int op, expdesc e1, expdesc e2) throws CompileException { switch (op) { case LexState.OPR_AND: { - _assert(e1.t.i == LexState.NO_JUMP); /* list must be closed */ + _assert(e1.t.i == NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.f, e1.f.i); // *e1 = *e2; @@ -1006,7 +1043,7 @@ void posfix(int op, expdesc e1, expdesc e2) throws CompileException { break; } case LexState.OPR_OR: { - _assert(e1.f.i == LexState.NO_JUMP); /* list must be closed */ + _assert(e1.f.i == NO_JUMP); /* list must be closed */ this.dischargevars(e2); this.concat(e2.t, e1.t.i); // *e1 = *e2; @@ -1017,11 +1054,11 @@ void posfix(int op, expdesc e1, expdesc e2) throws CompileException { this.exp2val(e2); if (e2.k == LexState.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { - _assert(e1.u.s.info == GETARG_B(this.getcode(e2)) - 1); + _assert(e1.u.info == GETARG_B(this.getcode(e2)) - 1); this.freeexp(e1); - SETARG_B(this.getcodePtr(e2), e1.u.s.info); + SETARG_B(this.getcodePtr(e2), e1.u.info); e1.k = LexState.VRELOCABLE; - e1.u.s.info = e2.u.s.info; + e1.u.info = e2.u.info; } else { this.exp2nextreg(e2); /* operand must be on the 'stack' */ this.codearith(OP_CONCAT, e1, e2); @@ -1120,4 +1157,32 @@ private void setlist(int base, int nelems, int tostore) throws CompileException } this.freereg = base + 1; /* free registers with list values */ } + + private void movegotosout(BlockCnt bl) throws CompileException { + int i = bl.firstgoto; + LexState.LabelDescription[] gl = ls.dyd.gt; + /* correct pending gotos to current block and try to close it + with visible labels */ + while (i < ls.dyd.ngt) { + LexState.LabelDescription gt = gl[i]; + if (gt.nactvar > bl.nactvar) { + if (bl.upval) { + patchclose(gt.pc, bl.nactvar); + } + gt.nactvar = bl.nactvar; + } + if (!ls.findlabel(i)) { + i++; /* move to the next one */ + } + } + } + + void checkrepeated(LexState.LabelDescription[] ll, int nll, LuaString label) throws CompileException { + for (int i = bl.firstlabel; i < nll; i++) { + if (label.equals(ll[i].name)) { + throw new CompileException(String.format("label '%s' already defined on line %d", + label.toString(), ll[i].line)); + } + } + } } diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java deleted file mode 100644 index 49d6afaf..00000000 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState52.java +++ /dev/null @@ -1,1188 +0,0 @@ -/* - * The MIT License (MIT) - * - * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. - * Modifications: Copyright (c) 2015-2020 SquidDev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.squiddev.cobalt.compiler; - - -import org.squiddev.cobalt.*; -import org.squiddev.cobalt.compiler.LexState52.ConsControl; -import org.squiddev.cobalt.compiler.LexState52.expdesc; -import org.squiddev.cobalt.function.LocalVariable; - -import java.util.Hashtable; - -import static org.squiddev.cobalt.Constants.*; -import static org.squiddev.cobalt.Lua52.*; -import static org.squiddev.cobalt.compiler.LexState52.NO_JUMP; -import static org.squiddev.cobalt.compiler.LuaC.*; - -public class FuncState52 { - class upvaldesc { - boolean instack; - short idx; - LuaString name; - } - - static class BlockCnt { - BlockCnt previous; /* chain */ - short firstlabel; /* index of first label in this block */ - short firstgoto; /* index of first pending goto in this block */ - short nactvar; /* # active locals outside the breakable structure */ - boolean upval; /* true if some variable in the block is an upvalue */ - boolean isloop; /* true if `block' is a loop */ - } - - Prototype f; /* current function header */ - // LTable h; /* table to find (and reuse) elements in `k' */ - Hashtable htable; /* table to find (and reuse) elements in `k' */ - FuncState52 prev; /* enclosing function */ - LexState52 ls; /* lexical state */ - LuaC L; /* compiler being invoked */ - BlockCnt bl; /* chain of current blocks */ - int pc; /* next position to code (equivalent to `ncode') */ - int lasttarget; /* `pc' of last `jump target' */ - IntPtr jpc; /* list of pending jumps to `pc' */ - int freereg; /* first free register */ - int nk; /* number of elements in `k' */ - int np; /* number of elements in `p' */ - short nlocvars; /* number of elements in `locvars' */ - short nactvar; /* number of active local variables */ - upvaldesc upvalues[] = new upvaldesc[LUAI_MAXUPVALUES]; /* upvalues */ - short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ - - FuncState52() { - - } - - - // ============================================================= - // from lcode.h - // ============================================================= - - InstructionPtr getcodePtr(expdesc e) { - return new InstructionPtr(f.code, e.u.info); - } - - int getcode(expdesc e) { - return f.code[e.u.info]; - } - - int codeAsBx(int o, int A, int sBx) throws CompileException { - return codeABx(o, A, sBx + MAXARG_sBx); - } - - void setmultret(expdesc e) throws CompileException { - setreturns(e, LUA_MULTRET); - } - - - // ============================================================= - // from lparser.c - // ============================================================= - - LocalVariable getlocvar(int i) { - return f.locvars[actvar[i]]; - } - - void checklimit(int v, int l, String msg) throws CompileException { - if (v > l) { - errorlimit(l, msg); - } - } - - private void errorlimit(int limit, String what) throws CompileException { - String msg = (f.linedefined == 0) ? - "main function has more than " + limit + " " + what : - "function at line " + f.linedefined + " has more than " + limit + " " + what; - throw ls.lexError(msg, 0); - } - - - int newupvalue(LuaString name, expdesc v) throws CompileException { - /* new one */ - checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); - if (f.upvalues == null || f.nups + 1 > f.upvalues.length) { - f.upvalues = realloc(f.upvalues, f.nups * 2 + 1); - } - if (f.upvalue_info == null || f.nups + 1 > f.upvalue_info.length) { - f.upvalue_info = realloc(f.upvalue_info, f.nups * 2 + 1); - } - f.upvalues[f.nups] = name; - f.upvalue_info[f.nups] = ((v.k == LexState52.VLOCAL ? 1 : 0) << 8) | v.u.info; - _assert(v.k == LexState52.VLOCAL || v.k == LexState52.VUPVAL); - upvalues[f.nups] = new upvaldesc(); - upvalues[f.nups].instack = (v.k == LexState52.VLOCAL); - upvalues[f.nups].idx = (short) (v.u.info); - upvalues[f.nups].name = name; - return f.nups++; - } - - private int searchupvalue(LuaString n) { - int i; - for (i = 0; i < f.nups; i++) { - if (f.upvalues[i].equals(n)) return i; - } - return -1; /* not found */ - } - - private int searchvar(LuaString n) { - int i; - for (i = nactvar - 1; i >= 0; i--) { - if (n == getlocvar(i).name) { - return i; - } - } - return -1; /* not found */ - } - - private void markupval(int level) { - BlockCnt bl = this.bl; - while (bl != null && bl.nactvar > level) { - bl = bl.previous; - } - if (bl != null) { - bl.upval = true; - } - } - - int singlevaraux(LuaString n, expdesc var, int base) throws CompileException { - int v = searchvar(n); /* look up at current level */ - if (v >= 0) { - var.init(LexState52.VLOCAL, v); - if (base == 0) { - markupval(v); /* local will be used as an upval */ - } - return LexState52.VLOCAL; - } else { /* not found at current level; try upvalues */ - int idx = searchupvalue(n); - if (idx < 0) { /* not found? */ - if (prev == null) { /* no more levels? */ - return LexState52.VVOID; - } - if (prev.singlevaraux(n, var, 0) == LexState52.VVOID) { - return LexState52.VVOID; - } - idx = this.newupvalue(n, var); /* else was LOCAL or UPVAL */ - } - var.init(LexState52.VUPVAL, idx); - return LexState52.VUPVAL; - } - } - - void enterblock(BlockCnt bl, boolean isloop) throws CompileException { - bl.isloop = isloop; - bl.nactvar = this.nactvar; - bl.firstlabel = ls.dyd.nlabel; - bl.firstgoto = ls.dyd.ngt; - bl.upval = false; - bl.previous = this.bl; - this.bl = bl; - _assert(this.freereg == this.nactvar); - } - - // -// void leaveblock (FuncState *fs) { -// BlockCnt *bl = this.bl; -// this.bl = bl.previous; -// removevars(this.ls, bl.nactvar); -// if (bl.upval) -// this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); -// /* a block either controls scope or breaks (never both) */ -// assert(!bl.isbreakable || !bl.upval); -// assert(bl.nactvar == this.nactvar); -// this.freereg = this.nactvar; /* free registers */ -// this.patchtohere(bl.breaklist); -// } - - void leaveblock() throws CompileException { - BlockCnt bl = this.bl; - if (bl.previous != null && bl.upval) { - /* create a 'jump to here' to close upvalues */ - int j = jump(); - patchclose(j, bl.nactvar); - patchtohere(j); - } - if (bl.isloop) { - ls.breaklabel(); - } - this.bl = bl.previous; - ls.removevars(bl.nactvar); - /*if (bl.upval) { - this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); - }*/ - /* a block either controls scope or breaks (never both) */ - _assert(!bl.isloop || !bl.upval); - _assert(bl.nactvar == this.nactvar); - this.freereg = this.nactvar; /* free registers */ - ls.dyd.nlabel = bl.firstlabel; - if (bl.previous != null) { - movegotosout(bl); - } else if (bl.firstgoto < ls.dyd.ngt) { - ls.undefgoto(ls.dyd.gt[bl.firstgoto]); - } - } - - void closelistfield(ConsControl cc) throws CompileException { - if (cc.v.k == LexState52.VVOID) { - return; /* there is no list item */ - } - this.exp2nextreg(cc.v); - cc.v.k = LexState52.VVOID; - if (cc.tostore == LFIELDS_PER_FLUSH) { - this.setlist(cc.t.u.info, cc.na, cc.tostore); /* flush */ - cc.tostore = 0; /* no more items pending */ - } - } - - private boolean hasmultret(int k) { - return ((k) == LexState52.VCALL || (k) == LexState52.VVARARG); - } - - void lastlistfield(ConsControl cc) throws CompileException { - if (cc.tostore == 0) return; - if (hasmultret(cc.v.k)) { - this.setmultret(cc.v); - this.setlist(cc.t.u.info, cc.na, LUA_MULTRET); - cc.na--; /* do not count last expression (unknown number of elements) */ - } else { - if (cc.v.k != LexState52.VVOID) { - this.exp2nextreg(cc.v); - } - this.setlist(cc.t.u.info, cc.na, cc.tostore); - } - } - - - // ============================================================= - // from lcode.c - // ============================================================= - - void nil(int from, int n) throws CompileException { - InstructionPtr previous; - int l = from + n - 1; - if (this.pc > this.lasttarget) { /* no jumps to current position? */ - if (this.pc == 0) { /* function start? */ - if (from >= this.nactvar) { - return; /* positions are already clean */ - } - } else { - previous = new InstructionPtr(this.f.code, this.pc - 1); - if (GET_OPCODE(previous.get()) == OP_LOADNIL) { - int pfrom = GETARG_A(previous.get()); - int pl = pfrom + GETARG_B(previous.get()); - if ((pfrom <= from && from <= pl + 1) || - (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ - if (pfrom < from) from = pfrom; - if (pl > l) l = pl; - SETARG_A(previous, from); - SETARG_B(previous, l - from); - return; - } - } - } - } - /* else no optimization */ - this.codeABC(OP_LOADNIL, from, n - 1, 0); - } - - - int jump() throws CompileException { - int jpc = this.jpc.i; /* save list of jumps to here */ - this.jpc.i = NO_JUMP; - IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, NO_JUMP)); - this.concat(j, jpc); /* keep them on hold */ - return j.i; - } - - void ret(int first, int nret) throws CompileException { - this.codeABC(OP_RETURN, first, nret + 1, 0); - } - - private int condjump(int /* OpCode */op, int A, int B, int C) throws CompileException { - this.codeABC(op, A, B, C); - return this.jump(); - } - - private void fixjump(int pc, int dest) throws CompileException { - InstructionPtr jmp = new InstructionPtr(this.f.code, pc); - int offset = dest - (pc + 1); - _assert(dest != NO_JUMP); - if (Math.abs(offset) > MAXARG_sBx) { - throw ls.syntaxError("control structure too long"); - } - SETARG_sBx(jmp, offset); - } - - - /* - * * returns current `pc' and marks it as a jump target (to avoid wrong * - * optimizations with consecutive instructions not in the same basic block). - */ - int getlabel() { - this.lasttarget = this.pc; - return this.pc; - } - - - private int getjump(int pc) { - int offset = GETARG_sBx(this.f.code[pc]); - /* point to itself represents end of list */ - if (offset == NO_JUMP) - /* end of list */ { - return NO_JUMP; - } else - /* turn offset into absolute position */ { - return (pc + 1) + offset; - } - } - - - private InstructionPtr getjumpcontrol(int pc) { - InstructionPtr pi = new InstructionPtr(this.f.code, pc); - if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1]))) { - return new InstructionPtr(pi.code, pi.idx - 1); - } else { - return pi; - } - } - - - /* - * * check whether list has any jump that do not produce a value * (or - * produce an inverted value) - */ - private boolean need_value(int list) { - for (; list != NO_JUMP; list = this.getjump(list)) { - int i = this.getjumpcontrol(list).get(); - if (GET_OPCODE(i) != OP_TESTSET) { - return true; - } - } - return false; /* not found */ - } - - - private boolean patchtestreg(int node, int reg) { - InstructionPtr i = this.getjumpcontrol(node); - if (GET_OPCODE(i.get()) != OP_TESTSET) - /* cannot patch other instructions */ { - return false; - } - if (reg != NO_REG && reg != GETARG_B(i.get())) { - SETARG_A(i, reg); - } else - /* no register to put value or register already has the value */ { - i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get()))); - } - - return true; - } - - - private void removevalues(int list) { - for (; list != NO_JUMP; list = this.getjump(list)) { - this.patchtestreg(list, NO_REG); - } - } - - private void patchlistaux(int list, int vtarget, int reg, int dtarget) throws CompileException { - while (list != NO_JUMP) { - int next = this.getjump(list); - if (this.patchtestreg(list, reg)) { - this.fixjump(list, vtarget); - } else { - this.fixjump(list, dtarget); /* jump to default target */ - } - list = next; - } - } - - private void dischargejpc() throws CompileException { - this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); - this.jpc.i = NO_JUMP; - } - - void patchlist(int list, int target) throws CompileException { - if (target == this.pc) { - this.patchtohere(list); - } else { - _assert(target < this.pc); - this.patchlistaux(list, target, NO_REG, target); - } - } - - void patchclose(int list, int level) throws CompileException { - level++; /* argument is +1 to reserve 0 as non-op */ - while (list != NO_JUMP) { - int next = getjump(list); - LuaC._assert(GET_OPCODE(f.code[list]) == OP_JMP && - GETARG_A(f.code[list]) == 0 || - GETARG_A(f.code[list]) >= level); - f.code[list] = (f.code[list] & MASK_NOT_A) | (level << POS_A); - list = next; - } - } - - void patchtohere(int list) throws CompileException { - this.getlabel(); - this.concat(this.jpc, list); - } - - void concat(IntPtr l1, int l2) throws CompileException { - if (l2 == NO_JUMP) { - return; - } - if (l1.i == NO_JUMP) { - l1.i = l2; - } else { - int list = l1.i; - int next; - while ((next = this.getjump(list)) != NO_JUMP) - /* find last element */ { - list = next; - } - this.fixjump(list, l2); - } - } - - void checkstack(int n) throws CompileException { - int newstack = this.freereg + n; - if (newstack > this.f.maxstacksize) { - if (newstack >= MAXSTACK) { - throw ls.syntaxError("function or expression too complex"); - } - this.f.maxstacksize = newstack; - } - } - - void reserveregs(int n) throws CompileException { - this.checkstack(n); - this.freereg += n; - } - - private void freereg(int reg) throws CompileException { - if (!ISK(reg) && reg >= this.nactvar) { - this.freereg--; - _assert(reg == this.freereg); - } - } - - private void freeexp(expdesc e) throws CompileException { - if (e.k == LexState52.VNONRELOC) { - this.freereg(e.u.info); - } - } - - private int addk(LuaValue v) { - int idx; - if (this.htable.containsKey(v)) { - idx = htable.get(v); - } else { - idx = this.nk; - this.htable.put(v, idx); - final Prototype f = this.f; - if (f.k == null || nk + 1 >= f.k.length) { - f.k = realloc(f.k, nk * 2 + 1); - } - f.k[this.nk++] = v; - } - return idx; - } - - int stringK(LuaString s) { - return this.addk(s); - } - - int numberK(LuaValue r) { - if (r instanceof LuaDouble) { - double d = r.toDouble(); - int i = (int) d; - if (d == (double) i) { - r = LuaInteger.valueOf(i); - } - } - return this.addk(r); - } - - private int boolK(boolean b) { - return this.addk((b ? TRUE : FALSE)); - } - - private int nilK() { - return this.addk(NIL); - } - - void setreturns(expdesc e, int nresults) throws CompileException { - if (e.k == LexState52.VCALL) { /* expression is an open function call? */ - SETARG_C(this.getcodePtr(e), nresults + 1); - } else if (e.k == LexState52.VVARARG) { - SETARG_B(this.getcodePtr(e), nresults + 1); - SETARG_A(this.getcodePtr(e), this.freereg); - this.reserveregs(1); - } - } - - void setoneret(expdesc e) { - if (e.k == LexState52.VCALL) { /* expression is an open function call? */ - e.k = LexState52.VNONRELOC; - e.u.info = GETARG_A(this.getcode(e)); - } else if (e.k == LexState52.VVARARG) { - SETARG_B(this.getcodePtr(e), 2); - e.k = LexState52.VRELOCABLE; /* can relocate its simple result */ - } - } - - void dischargevars(expdesc e) throws CompileException { - switch (e.k) { - case LexState52.VLOCAL: { - e.k = LexState52.VNONRELOC; - break; - } - case LexState52.VUPVAL: { - e.u.info = this.codeABC(OP_GETUPVAL, 0, e.u.info, 0); - e.k = LexState52.VRELOCABLE; - break; - } - case LexState52.VINDEXED: { - int op = Lua52.OP_GETTABUP; - this.freereg(e.u.ind.idx); - if (e.u.ind.vt == LexState52.VLOCAL) { - this.freereg(e.u.ind.t); - op = Lua52.OP_GETTABLE; - } - e.u.info = this.codeABC(op, 0, e.u.ind.t, e.u.ind.idx); - e.k = LexState52.VRELOCABLE; - break; - } - case LexState52.VVARARG: - case LexState52.VCALL: { - this.setoneret(e); - break; - } - default: - break; /* there is one value available (somewhere) */ - } - } - - private int code_label(int A, int b, int jump) throws CompileException { - this.getlabel(); /* those instructions may be jump targets */ - return this.codeABC(OP_LOADBOOL, A, b, jump); - } - - private void discharge2reg(expdesc e, int reg) throws CompileException { - this.dischargevars(e); - switch (e.k) { - case LexState52.VNIL: { - this.nil(reg, 1); - break; - } - case LexState52.VFALSE: - case LexState52.VTRUE: { - this.codeABC(OP_LOADBOOL, reg, (e.k == LexState52.VTRUE ? 1 : 0), - 0); - break; - } - case LexState52.VK: { - this.codeABx(OP_LOADK, reg, e.u.info); - break; - } - case LexState52.VKNUM: { - this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval())); - break; - } - case LexState52.VRELOCABLE: { - InstructionPtr pc = this.getcodePtr(e); - SETARG_A(pc, reg); - break; - } - case LexState52.VNONRELOC: { - if (reg != e.u.info) { - this.codeABC(OP_MOVE, reg, e.u.info, 0); - } - break; - } - default: { - _assert(e.k == LexState52.VVOID || e.k == LexState52.VJMP); - return; /* nothing to do... */ - } - } - e.u.info = reg; - e.k = LexState52.VNONRELOC; - } - - private void discharge2anyreg(expdesc e) throws CompileException { - if (e.k != LexState52.VNONRELOC) { - this.reserveregs(1); - this.discharge2reg(e, this.freereg - 1); - } - } - - private void exp2reg(expdesc e, int reg) throws CompileException { - this.discharge2reg(e, reg); - if (e.k == LexState52.VJMP) { - this.concat(e.t, e.u.info); /* put this jump in `t' list */ - } - if (e.hasjumps()) { - int _final; /* position after whole expression */ - int p_f = NO_JUMP; /* position of an eventual LOAD false */ - int p_t = NO_JUMP; /* position of an eventual LOAD true */ - if (this.need_value(e.t.i) || this.need_value(e.f.i)) { - int fj = (e.k == LexState52.VJMP) ? NO_JUMP : this - .jump(); - p_f = this.code_label(reg, 0, 1); - p_t = this.code_label(reg, 1, 0); - this.patchtohere(fj); - } - _final = this.getlabel(); - this.patchlistaux(e.f.i, _final, reg, p_f); - this.patchlistaux(e.t.i, _final, reg, p_t); - } - e.f.i = e.t.i = NO_JUMP; - e.u.info = reg; - e.k = LexState52.VNONRELOC; - } - - void exp2nextreg(expdesc e) throws CompileException { - this.dischargevars(e); - this.freeexp(e); - this.reserveregs(1); - this.exp2reg(e, this.freereg - 1); - } - - int exp2anyreg(expdesc e) throws CompileException { - this.dischargevars(e); - if (e.k == LexState52.VNONRELOC) { - if (!e.hasjumps()) { - return e.u.info; /* exp is already in a register */ - } - if (e.u.info >= this.nactvar) { /* reg. is not a local? */ - this.exp2reg(e, e.u.info); /* put value on it */ - return e.u.info; - } - } - this.exp2nextreg(e); /* default */ - return e.u.info; - } - - void exp2val(expdesc e) throws CompileException { - if (e.hasjumps()) { - this.exp2anyreg(e); - } else { - this.dischargevars(e); - } - } - - int exp2RK(expdesc e) throws CompileException { - this.exp2val(e); - switch (e.k) { - case LexState52.VKNUM: - case LexState52.VTRUE: - case LexState52.VFALSE: - case LexState52.VNIL: { - if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ - e.u.info = (e.k == LexState52.VNIL) ? this.nilK() - : (e.k == LexState52.VKNUM) ? this.numberK(e.u.nval()) - : this.boolK((e.k == LexState52.VTRUE)); - e.k = LexState52.VK; - return RKASK(e.u.info); - } else { - break; - } - } - case LexState52.VK: { - if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ { - return RKASK(e.u.info); - } else { - break; - } - } - default: - break; - } - /* not a constant in the right range: put it in a register */ - return this.exp2anyreg(e); - } - - void storevar(expdesc var, expdesc ex) throws CompileException { - switch (var.k) { - case LexState52.VLOCAL: { - this.freeexp(ex); - this.exp2reg(ex, var.u.info); - return; - } - case LexState52.VUPVAL: { - int e = this.exp2anyreg(ex); - this.codeABC(OP_SETUPVAL, e, var.u.info, 0); - break; - } - case LexState52.VINDEXED: { - int op = var.u.ind.vt == LexState52.VLOCAL ? Lua52.OP_SETTABLE : Lua52.OP_SETTABUP; - int e = this.exp2RK(ex); - this.codeABC(op, var.u.ind.t, var.u.ind.idx, e); - break; - } - default: { - _assert(false); /* invalid var kind to store */ - break; - } - } - this.freeexp(ex); - } - - void self(expdesc e, expdesc key) throws CompileException { - int func; - this.exp2anyreg(e); - this.freeexp(e); - func = this.freereg; - this.reserveregs(2); - this.codeABC(OP_SELF, func, e.u.info, this.exp2RK(key)); - this.freeexp(key); - e.u.info = func; - e.k = LexState52.VNONRELOC; - } - - private void invertjump(expdesc e) throws CompileException { - InstructionPtr pc = this.getjumpcontrol(e.u.info); - _assert(testTMode(GET_OPCODE(pc.get())) - && GET_OPCODE(pc.get()) != OP_TESTSET && Lua - .GET_OPCODE(pc.get()) != OP_TEST); - // SETARG_A(pc, !(GETARG_A(pc.get()))); - int a = GETARG_A(pc.get()); - int nota = (a != 0 ? 0 : 1); - SETARG_A(pc, nota); - } - - private int jumponcond(expdesc e, int cond) throws CompileException { - if (e.k == LexState52.VRELOCABLE) { - int ie = this.getcode(e); - if (GET_OPCODE(ie) == OP_NOT) { - this.pc--; /* remove previous OP_NOT */ - return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond != 0 ? 0 : 1)); - } - /* else go through */ - } - this.discharge2anyreg(e); - this.freeexp(e); - return this.condjump(OP_TESTSET, NO_REG, e.u.info, cond); - } - - void goiftrue(expdesc e) throws CompileException { - int pc; /* pc of last jump */ - this.dischargevars(e); - switch (e.k) { - case LexState52.VK: - case LexState52.VKNUM: - case LexState52.VTRUE: { - pc = NO_JUMP; /* always true; do nothing */ - break; - } - case LexState52.VFALSE: { - pc = this.jump(); /* always jump */ - break; - } - case LexState52.VJMP: { - this.invertjump(e); - pc = e.u.info; - break; - } - default: { - pc = this.jumponcond(e, 0); - break; - } - } - this.concat(e.f, pc); /* insert last jump in `f' list */ - this.patchtohere(e.t.i); - e.t.i = NO_JUMP; - } - - void goiffalse(expdesc e) throws CompileException { - int pc; /* pc of last jump */ - this.dischargevars(e); - switch (e.k) { - case LexState52.VNIL: - case LexState52.VFALSE: { - pc = NO_JUMP; /* always false; do nothing */ - break; - } - case LexState52.VTRUE: { - pc = this.jump(); /* always jump */ - break; - } - case LexState52.VJMP: { - pc = e.u.info; - break; - } - default: { - pc = this.jumponcond(e, 1); - break; - } - } - this.concat(e.t, pc); /* insert last jump in `t' list */ - this.patchtohere(e.f.i); - e.f.i = NO_JUMP; - } - - private void codenot(expdesc e) throws CompileException { - this.dischargevars(e); - switch (e.k) { - case LexState52.VNIL: - case LexState52.VFALSE: { - e.k = LexState52.VTRUE; - break; - } - case LexState52.VK: - case LexState52.VKNUM: - case LexState52.VTRUE: { - e.k = LexState52.VFALSE; - break; - } - case LexState52.VJMP: { - this.invertjump(e); - break; - } - case LexState52.VRELOCABLE: - case LexState52.VNONRELOC: { - this.discharge2anyreg(e); - this.freeexp(e); - e.u.info = this.codeABC(OP_NOT, 0, e.u.info, 0); - e.k = LexState52.VRELOCABLE; - break; - } - default: { - _assert(false); /* cannot happen */ - break; - } - } - /* interchange true and false lists */ - { - int temp = e.f.i; - e.f.i = e.t.i; - e.t.i = temp; - } - this.removevalues(e.f.i); - this.removevalues(e.t.i); - } - - void indexed(expdesc t, expdesc k) throws CompileException { - t.u.ind.t = t.u.info; - t.u.ind.idx = this.exp2RK(k); - t.u.ind.vt = t.k == LexState52.VUPVAL ? LexState52.VUPVAL : LexState52.VLOCAL; - t.k = LexState52.VINDEXED; - } - - private boolean constfolding(int op, expdesc e1, expdesc e2) throws CompileException { - LuaValue v1, v2, r; - if (!e1.isnumeral() || !e2.isnumeral()) { - return false; - } - v1 = e1.u.nval(); - v2 = e2.u.nval(); - try { - switch (op) { - case OP_ADD: - r = OperationHelper.add(null, v1, v2); - break; - case OP_SUB: - r = OperationHelper.sub(null, v1, v2); - break; - case OP_MUL: - r = OperationHelper.mul(null, v1, v2); - break; - case OP_DIV: - r = OperationHelper.div(null, v1, v2); - break; - case OP_MOD: - r = OperationHelper.mod(null, v1, v2); - break; - case OP_POW: - r = OperationHelper.pow(null, v1, v2); - break; - case OP_UNM: - r = OperationHelper.neg(null, v1); - break; - case OP_LEN: - // r = v1.len(); - // break; - return false; /* no constant folding for 'len' */ - default: - _assert(false); - r = null; - break; - } - } catch (UnwindThrowable | LuaError e) { - return false; - } - - if (Double.isNaN(r.toDouble())) { - return false; /* do not attempt to produce NaN */ - } - e1.u.setNval(r); - return true; - } - - private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { - if (constfolding(op, e1, e2)) { - } else { - int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) - : 0; - int o1 = this.exp2RK(e1); - if (o1 > o2) { - this.freeexp(e1); - this.freeexp(e2); - } else { - this.freeexp(e2); - this.freeexp(e1); - } - e1.u.info = this.codeABC(op, 0, o1, o2); - e1.k = LexState52.VRELOCABLE; - } - } - - private void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) throws CompileException { - int o1 = this.exp2RK(e1); - int o2 = this.exp2RK(e2); - this.freeexp(e2); - this.freeexp(e1); - if (cond == 0 && op != OP_EQ) { - int temp; /* exchange args to replace by `<' or `<=' */ - temp = o1; - o1 = o2; - o2 = temp; /* o1 <==> o2 */ - cond = 1; - } - e1.u.info = this.condjump(op, cond, o1, o2); - e1.k = LexState52.VJMP; - } - - void prefix(int /* UnOpr */op, expdesc e) throws CompileException { - expdesc e2 = new expdesc(); - e2.init(LexState52.VKNUM, 0); - switch (op) { - case LexState52.OPR_MINUS: { - if (e.k == LexState52.VK) { - this.exp2anyreg(e); /* cannot operate on non-numeric constants */ - } - this.codearith(OP_UNM, e, e2); - break; - } - case LexState52.OPR_NOT: - this.codenot(e); - break; - case LexState52.OPR_LEN: { - this.exp2anyreg(e); /* cannot operate on constants */ - this.codearith(OP_LEN, e, e2); - break; - } - default: - _assert(false); - } - } - - void infix(int /* BinOpr */op, expdesc v) throws CompileException { - switch (op) { - case LexState52.OPR_AND: { - this.goiftrue(v); - break; - } - case LexState52.OPR_OR: { - this.goiffalse(v); - break; - } - case LexState52.OPR_CONCAT: { - this.exp2nextreg(v); /* operand must be on the `stack' */ - break; - } - case LexState52.OPR_ADD: - case LexState52.OPR_SUB: - case LexState52.OPR_MUL: - case LexState52.OPR_DIV: - case LexState52.OPR_MOD: - case LexState52.OPR_POW: { - if (!v.isnumeral()) { - this.exp2RK(v); - } - break; - } - default: { - this.exp2RK(v); - break; - } - } - } - - - void posfix(int op, expdesc e1, expdesc e2) throws CompileException { - switch (op) { - case LexState52.OPR_AND: { - _assert(e1.t.i == NO_JUMP); /* list must be closed */ - this.dischargevars(e2); - this.concat(e2.f, e1.f.i); - // *e1 = *e2; - e1.setvalue(e2); - break; - } - case LexState52.OPR_OR: { - _assert(e1.f.i == NO_JUMP); /* list must be closed */ - this.dischargevars(e2); - this.concat(e2.t, e1.t.i); - // *e1 = *e2; - e1.setvalue(e2); - break; - } - case LexState52.OPR_CONCAT: { - this.exp2val(e2); - if (e2.k == LexState52.VRELOCABLE - && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { - _assert(e1.u.info == GETARG_B(this.getcode(e2)) - 1); - this.freeexp(e1); - SETARG_B(this.getcodePtr(e2), e1.u.info); - e1.k = LexState52.VRELOCABLE; - e1.u.info = e2.u.info; - } else { - this.exp2nextreg(e2); /* operand must be on the 'stack' */ - this.codearith(OP_CONCAT, e1, e2); - } - break; - } - case LexState52.OPR_ADD: - this.codearith(OP_ADD, e1, e2); - break; - case LexState52.OPR_SUB: - this.codearith(OP_SUB, e1, e2); - break; - case LexState52.OPR_MUL: - this.codearith(OP_MUL, e1, e2); - break; - case LexState52.OPR_DIV: - this.codearith(OP_DIV, e1, e2); - break; - case LexState52.OPR_MOD: - this.codearith(OP_MOD, e1, e2); - break; - case LexState52.OPR_POW: - this.codearith(OP_POW, e1, e2); - break; - case LexState52.OPR_EQ: - this.codecomp(OP_EQ, 1, e1, e2); - break; - case LexState52.OPR_NE: - this.codecomp(OP_EQ, 0, e1, e2); - break; - case LexState52.OPR_LT: - this.codecomp(OP_LT, 1, e1, e2); - break; - case LexState52.OPR_LE: - this.codecomp(OP_LE, 1, e1, e2); - break; - case LexState52.OPR_GT: - this.codecomp(OP_LT, 0, e1, e2); - break; - case LexState52.OPR_GE: - this.codecomp(OP_LE, 0, e1, e2); - break; - default: - _assert(false); - } - } - - - void fixline(int line) { - this.f.lineinfo[this.pc - 1] = line; - } - - - private int code(int instruction, int line) throws CompileException { - Prototype f = this.f; - this.dischargejpc(); /* `pc' will change */ - /* put new instruction in code array */ - if (f.code == null || this.pc + 1 > f.code.length) { - f.code = LuaC.realloc(f.code, this.pc * 2 + 1); - } - f.code[this.pc] = instruction; - /* save corresponding line information */ - if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length) { - f.lineinfo = LuaC.realloc(f.lineinfo, - this.pc * 2 + 1); - } - f.lineinfo[this.pc] = line; - return this.pc++; - } - - - int codeABC(int o, int a, int b, int c) throws CompileException { - _assert(getOpMode(o) == iABC); - _assert(getBMode(o) != OpArgN || b == 0); - _assert(getCMode(o) != OpArgN || c == 0); - return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline); - } - - - int codeABx(int o, int a, int bc) throws CompileException { - _assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); - _assert(getCMode(o) == OpArgN); - return this.code(CREATE_ABx(o, a, bc), this.ls.lastline); - } - - - private void setlist(int base, int nelems, int tostore) throws CompileException { - int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; - int b = (tostore == LUA_MULTRET) ? 0 : tostore; - _assert(tostore != 0); - if (c <= MAXARG_C) { - this.codeABC(OP_SETLIST, base, b, c); - } else { - this.codeABC(OP_SETLIST, base, b, 0); - this.code(c, this.ls.lastline); - } - this.freereg = base + 1; /* free registers with list values */ - } - - private void movegotosout(BlockCnt bl) throws CompileException { - int i = bl.firstgoto; - LexState52.LabelDescription[] gl = ls.dyd.gt; - /* correct pending gotos to current block and try to close it - with visible labels */ - while (i < ls.dyd.ngt) { - LexState52.LabelDescription gt = gl[i]; - if (gt.nactvar > bl.nactvar) { - if (bl.upval) { - patchclose(gt.pc, bl.nactvar); - } - gt.nactvar = bl.nactvar; - } - if (!ls.findlabel(i)) { - i++; /* move to the next one */ - } - } - } - - void checkrepeated(LexState52.LabelDescription[] ll, int nll, LuaString label) throws CompileException { - for (int i = bl.firstlabel; i < nll; i++) { - if (label.equals(ll[i].name)) { - throw new CompileException(String.format("label '%s' already defined on line %d", - label.toString(), ll[i].line)); - } - } - } -} diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState.java b/src/main/java/org/squiddev/cobalt/compiler/LexState.java index be6ace05..987a068a 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState.java @@ -110,7 +110,6 @@ public static boolean isReservedKeyword(String varName) { VKNUM = 5, /* nval = numerical value */ VLOCAL = 6, /* info = local register */ VUPVAL = 7, /* info = index of upvalue in `upvalues' */ - VGLOBAL = 8, /* info = index of table, aux = index of global name in `k' */ VINDEXED = 9, /* info = table register, aux = index register (or `k') */ VJMP = 10, /* info = instruction pc */ VRELOCABLE = 11, /* info = instruction pc */ @@ -148,14 +147,15 @@ public void set(Token other) { private byte decpoint; /* locale decimal point */ public int nCcalls; private final HashMap strings = new HashMap<>(); + DynamicData dyd; /* ORDER RESERVED */ private final static String[] luaX_tokens = { "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "if", + "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", + "..", "...", "==", ">=", "<=", "~=", "::", "", "", "", "", }; @@ -163,13 +163,13 @@ public void set(Token other) { final static int // terminal symbols denoted by reserved words TK_AND = 257, TK_BREAK = 258, TK_DO = 259, TK_ELSE = 260, TK_ELSEIF = 261, - TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_IF = 266, - TK_IN = 267, TK_LOCAL = 268, TK_NIL = 269, TK_NOT = 270, TK_OR = 271, TK_REPEAT = 272, - TK_RETURN = 273, TK_THEN = 274, TK_TRUE = 275, TK_UNTIL = 276, TK_WHILE = 277, + TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_GOTO = 266, TK_IF = 267, + 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 = 278, TK_DOTS = 279, TK_EQ = 280, TK_GE = 281, TK_LE = 282, TK_NE = 283, - TK_EOS = 284, - TK_NUMBER = 285, TK_NAME = 286, TK_STRING = 287; + TK_CONCAT = 279, TK_DOTS = 280, TK_EQ = 281, TK_GE = 282, TK_LE = 283, TK_NE = 284, TK_DBCOLON = 285, + TK_EOS = 286, + TK_NUMBER = 287, TK_NAME = 288, TK_STRING = 289; private final static int FIRST_RESERVED = TK_AND; private final static int NUM_RESERVED = TK_WHILE + 1 - FIRST_RESERVED; @@ -312,7 +312,7 @@ private LuaString newString(String s) { private void inclineNumber() throws CompileException { int old = current; - LuaC._assert(currIsNewline()); + LuaC._assert(currIsNewline(), linenumber); nextChar(); /* skip '\n' or '\r' */ if (currIsNewline() && current != old) { nextChar(); /* skip '\n\r' or '\r\n' */ @@ -391,7 +391,7 @@ private void str2d(String str, SemInfo seminfo) throws CompileException { } private void read_numeral(SemInfo seminfo) throws CompileException { - LuaC._assert(isDigit(current)); + LuaC._assert(isDigit(current), linenumber); int first = current; save_and_next(); @@ -416,7 +416,7 @@ private void read_numeral(SemInfo seminfo) throws CompileException { private int skip_sep() throws CompileException { int count = 0; int s = current; - LuaC._assert(s == '[' || s == ']'); + LuaC._assert(s == '[' || s == ']', linenumber); save_and_next(); while (current == '=') { save_and_next(); @@ -712,6 +712,15 @@ private int llex(SemInfo seminfo) throws CompileException { return TK_NE; } } + case ':': { + nextChar(); + if (current != ':') { + return ':'; + } else { + nextChar(); + return TK_DBCOLON; + } + } case '"': case '\'': { read_string(current, seminfo); @@ -737,7 +746,7 @@ private int llex(SemInfo seminfo) throws CompileException { } default: { if (isSpace(current)) { - LuaC._assert(!currIsNewline()); + LuaC._assert(!currIsNewline(), linenumber); nextChar(); } else if (isDigit(current)) { read_numeral(seminfo); @@ -749,7 +758,7 @@ private int llex(SemInfo seminfo) throws CompileException { save_and_next(); } while (isAlphaNum(current) || current == '_'); ts = newString(buff, 0, nbuff); - if (RESERVED.containsKey(ts)) { + if (RESERVED.containsKey(ts) && !ts.equals(LuaString.valueOf("goto"))) { return RESERVED.get(ts); } else { seminfo.ts = ts; @@ -776,7 +785,7 @@ void nextToken() throws CompileException { } private void lookahead() throws CompileException { - LuaC._assert(lookahead.token == TK_EOS); + LuaC._assert(lookahead.token == TK_EOS, linenumber); lookahead.token = llex(lookahead.seminfo); } @@ -794,10 +803,13 @@ static class expdesc { static class U { // originally a union static class S { - int info, aux; + int idx; + int t; + int vt; } - final S s = new S(); + final S ind = new S(); + int info; private LuaValue _nval; public void setNval(LuaValue r) { @@ -805,7 +817,7 @@ public void setNval(LuaValue r) { } public LuaValue nval() { - return _nval == null ? LuaInteger.valueOf(s.info) : _nval; + return _nval == null ? LuaInteger.valueOf(info) : _nval; } } @@ -817,7 +829,7 @@ void init(int k, int i) { this.f.i = NO_JUMP; this.t.i = NO_JUMP; this.k = k; - this.u.s.info = i; + this.u.info = i; } boolean hasjumps() { @@ -831,13 +843,37 @@ boolean isnumeral() { public void setvalue(expdesc other) { this.k = other.k; this.u._nval = other.u._nval; - this.u.s.info = other.u.s.info; - this.u.s.aux = other.u.s.aux; + this.u.info = other.u.info; + this.u.ind.idx = other.u.ind.idx; + this.u.ind.t = other.u.ind.t; + this.u.ind.vt = other.u.ind.vt; this.t.i = other.t.i; this.f.i = other.f.i; } } + static class LabelDescription { + LuaString name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + short nactvar; /* local level where it appears in current block */ + } + + static class DynamicData { + short[] actvar; + int nactvar; + LabelDescription[] gt; + short ngt; + LabelDescription[] label; + short nlabel; + + DynamicData() { + actvar = null; nactvar = 0; + gt = null; ngt = 0; + label = null; nlabel = 0; + } + } + private boolean hasmultret(int k) { return k == VCALL || k == VVARARG; } @@ -954,8 +990,12 @@ void removevars(int tolevel) { private void singlevar(expdesc var) throws CompileException { LuaString varname = str_checkname(); FuncState fs = this.fs; - if (fs.singlevaraux(varname, var, 1) == VGLOBAL) { - var.u.s.info = fs.stringK(varname); /* info points to global name */ + if (fs.singlevaraux(varname, var, 1) == VVOID) { + expdesc key = new expdesc(); + fs.singlevaraux(LuaString.valueOf("_ENV"), var, 1); + LuaC._assert(var.k == LexState.VLOCAL || var.k == LexState.VUPVAL, linenumber); + codestring(key, varname); + fs.indexed(var, key); } } @@ -996,28 +1036,111 @@ private void leavelevel() { nCcalls--; } - private void pushclosure(FuncState func, expdesc v) throws CompileException { - FuncState fs = this.fs; + private void closegoto(int g, LabelDescription label) throws CompileException { + LabelDescription gt = dyd.gt[g]; + LuaC._assert(gt.name.equals(label.name), linenumber); + if (gt.nactvar < label.nactvar) { + throw new CompileException(String.format(" at line %d jumps into the scope of local '%s'", + gt.name.toString(), gt.line, fs.getlocvar(gt.nactvar).name.toString())); + } + fs.patchlist(gt.pc, label.pc); + for (int i = g; i < dyd.ngt - 1; i++) { + dyd.gt[i] = dyd.gt[i+1]; + } + dyd.ngt--; + } + + boolean findlabel(int g) throws CompileException { + LabelDescription gt = dyd.gt[g]; + for (int i = fs.bl.firstlabel; i < dyd.nlabel; i++) { + LabelDescription lb = dyd.label[i]; + if (lb.name.equals(gt.name)) { + if (gt.nactvar > lb.nactvar && (fs.bl.upval || dyd.nlabel > fs.bl.firstlabel)) { + fs.patchclose(gt.pc, lb.nactvar); + } + closegoto(g, lb); + return true; + } + } + return false; + } + + private int newlabelentry(DynamicData dyd, boolean islabel, LuaString name, int line, int pc) { + if (islabel) { + if (dyd.label == null || dyd.label.length < dyd.nlabel + 1) { + dyd.label = LuaC.realloc(dyd.label, dyd.nlabel * 2 + 1); + } + dyd.label[dyd.nlabel] = new LabelDescription(); + dyd.label[dyd.nlabel].name = name; + dyd.label[dyd.nlabel].line = line; + dyd.label[dyd.nlabel].nactvar = fs.nactvar; + dyd.label[dyd.nlabel].pc = pc; + return dyd.nlabel++; + } else { + if (dyd.gt == null || dyd.gt.length < dyd.ngt + 1) { + dyd.gt = LuaC.realloc(dyd.gt, dyd.ngt * 2 + 1); + } + dyd.gt[dyd.ngt] = new LabelDescription(); + dyd.gt[dyd.ngt].name = name; + dyd.gt[dyd.ngt].line = line; + dyd.gt[dyd.ngt].nactvar = fs.nactvar; + dyd.gt[dyd.ngt].pc = pc; + return dyd.ngt++; + } + } + + private void findgotos(LabelDescription lb) throws CompileException { + int i = fs.bl.firstgoto; + while (i < dyd.ngt) { + if (dyd.gt[i].name.equals(lb.name)) { + closegoto(i, lb); + } else { + i++; + } + } + } + + /* + ** create a label named "break" to resolve break statements + */ + void breaklabel() throws CompileException { + int l = newlabelentry(dyd, true, LuaString.valueOf("break"), 0, fs.pc); + findgotos(dyd.label[l]); + } + + /* + ** generates an error for an undefined 'goto'; choose appropriate + ** message when label name is a reserved word (which can only be 'break') + */ + void /* no return */ undefgoto(LabelDescription gt) throws CompileException { + String msg = String.format(isReservedKeyword(gt.name.toString()) ? "<%s> at line %d not inside a loop" : "no visible label '%s' for at line %d", + gt.name.toString(), gt.line); + throw new CompileException(msg); + } + + Prototype addprototype() { + Prototype clp; Prototype f = fs.f; - if (f.p == null || fs.np + 1 > f.p.length) { + if (f.p == null || fs.np >= f.p.length) { f.p = LuaC.realloc(f.p, fs.np * 2 + 1); } - f.p[fs.np++] = func.f; - v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1)); - for (int i = 0; i < func.f.nups; i++) { - int o = func.upvalues[i].k == VLOCAL ? Lua.OP_MOVE - : Lua.OP_GETUPVAL; - fs.codeABC(o, 0, func.upvalues[i].info, 0); - } + f.p[fs.np++] = clp = new Prototype(); + return clp; } - void open_func(FuncState fs) { - Prototype f = new Prototype(); + private void codeclosure(expdesc v) throws CompileException { + FuncState fs = this.fs.prev; + v.init(LexState.VRELOCABLE, fs.codeABx(Lua52.OP_CLOSURE, 0, fs.np - 1)); + fs.exp2nextreg(v); + } + + void open_func(FuncState fs, FuncState.BlockCnt bl) throws CompileException { + Prototype f = fs.f; if (this.fs != null) { f.source = this.fs.f.source; } - fs.f = f; - fs.prev = this.fs; /* linked list of funcstates */ + f.isLua52 = true; + fs.prev = this.fs; /* linked list of FuncStates */ fs.ls = this; this.fs = fs; fs.pc = 0; @@ -1032,6 +1155,7 @@ void open_func(FuncState fs) { f.maxstacksize = 2; /* registers 0/1 are always valid */ //fs.h = new LTable(); fs.htable = new Hashtable<>(); + fs.enterblock(bl, false); } void close_func() throws CompileException { @@ -1039,6 +1163,7 @@ void close_func() throws CompileException { Prototype f = fs.f; this.removevars(0); fs.ret(0, 0); /* final return */ + fs.leaveblock(); f.code = LuaC.realloc(f.code, fs.pc); f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); // f.sizelineinfo = fs.pc; @@ -1048,7 +1173,7 @@ void close_func() throws CompileException { // f.sizelocvars = fs.nlocvars; f.upvalues = LuaC.realloc(f.upvalues, f.nups); // LuaC._assert (CheckCode.checkcode(f)); - LuaC._assert(fs.bl == null); + LuaC._assert(fs.bl == null, linenumber); this.fs = fs.prev; // L.top -= 2; /* remove table and prototype from the stack */ // /* last token read was anchored in defunct function; must reanchor it @@ -1113,7 +1238,7 @@ private void recfield(ConsControl cc) throws CompileException { this.checknext('='); rkkey = fs.exp2RK(key); this.expr(val); - fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val)); + fs.codeABC(Lua52.OP_SETTABLE, cc.t.u.info, rkkey, fs.exp2RK(val)); fs.freereg = reg; /* free registers */ } @@ -1129,7 +1254,7 @@ private void constructor(expdesc t) throws CompileException { /* constructor -> ?? */ FuncState fs = this.fs; int line = this.linenumber; - int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0); + int pc = fs.codeABC(Lua52.OP_NEWTABLE, 0, 0, 0); ConsControl cc = new ConsControl(); cc.na = cc.nh = cc.tostore = 0; cc.t = t; @@ -1138,7 +1263,7 @@ private void constructor(expdesc t) throws CompileException { fs.exp2nextreg(t); /* fix it at stack top (for gc) */ this.checknext('{'); do { - LuaC._assert(cc.v.k == VVOID || cc.tostore > 0); + LuaC._assert(cc.v.k == VVOID || cc.tostore > 0, linenumber); if (this.t.token == '}') { break; } @@ -1209,9 +1334,9 @@ private void parlist() throws CompileException { if (LUA_COMPAT_VARARG) { /* use `arg' as default name */ this.new_localvarliteral("arg", nparams++); - f.is_vararg = Lua.VARARG_HASARG | Lua.VARARG_NEEDSARG; + f.is_vararg = Lua52.VARARG_HASARG | Lua52.VARARG_NEEDSARG; } - f.is_vararg |= Lua.VARARG_ISVARARG; + f.is_vararg |= Lua52.VARARG_ISVARARG; break; } default: @@ -1220,7 +1345,7 @@ private void parlist() throws CompileException { } while (f.is_vararg == 0 && this.testnext(',')); } this.adjustlocalvars(nparams); - f.numparams = fs.nactvar - (f.is_vararg & Lua.VARARG_HASARG); + f.numparams = fs.nactvar - (f.is_vararg & Lua52.VARARG_HASARG); fs.reserveregs(fs.nactvar); /* reserve register for parameters */ } @@ -1228,8 +1353,10 @@ private void parlist() throws CompileException { private void body(expdesc e, boolean needself, int line) throws CompileException { /* body -> `(' parlist `)' chunk END */ FuncState new_fs = new FuncState(); - open_func(new_fs); + FuncState.BlockCnt bl = new FuncState.BlockCnt(); + new_fs.f = this.addprototype(); new_fs.f.linedefined = line; + open_func(new_fs, bl); this.checknext('('); if (needself) { new_localvarliteral("self", 0); @@ -1237,11 +1364,11 @@ private void body(expdesc e, boolean needself, int line) throws CompileException } this.parlist(); this.checknext(')'); - this.chunk(); + this.statlist(); new_fs.f.lastlinedefined = this.linenumber; this.check_match(TK_END, TK_FUNCTION, line); + this.codeclosure(e); this.close_func(); - this.pushclosure(new_fs, e); } private int explist1(expdesc v) throws CompileException { @@ -1290,17 +1417,17 @@ private void funcargs(expdesc f) throws CompileException { throw syntaxError("function arguments expected"); } } - LuaC._assert(f.k == VNONRELOC); - base = f.u.s.info; /* base register for call */ + LuaC._assert(f.k == VNONRELOC, linenumber); + base = f.u.info; /* base register for call */ if (hasmultret(args.k)) { - nparams = Lua.LUA_MULTRET; /* open call */ + nparams = Lua52.LUA_MULTRET; /* open call */ } else { if (args.k != VVOID) { fs.exp2nextreg(args); /* close last argument */ } nparams = fs.freereg - (base + 1); } - f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2)); + f.init(VCALL, fs.codeABC(Lua52.OP_CALL, base, nparams + 1, 2)); fs.fixline(line); fs.freereg = base + 1; /* call remove function and arguments and leaves * (unless changed) one result */ @@ -1408,8 +1535,8 @@ private void simpleexp(expdesc v) throws CompileException { FuncState fs = this.fs; this.check_condition(fs.f.is_vararg != 0, "cannot use " + LUA_QL("...") + " outside a vararg function"); - fs.f.is_vararg &= ~Lua.VARARG_NEEDSARG; /* don't need 'arg' */ - v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0)); + fs.f.is_vararg &= ~Lua52.VARARG_NEEDSARG; /* don't need 'arg' */ + v.init(VVARARG, fs.codeABC(Lua52.OP_VARARG, 0, 1, 0)); break; } case '{': { /* constructor */ @@ -1550,14 +1677,15 @@ private void expr(expdesc v) throws CompileException { */ - private boolean block_follow(int token) { - switch (token) { + private boolean block_follow(boolean withUntil) { + switch (t.token) { case TK_ELSE: case TK_ELSEIF: case TK_END: - case TK_UNTIL: case TK_EOS: return true; + case TK_UNTIL: + return withUntil; default: return false; } @@ -1569,8 +1697,8 @@ private void block() throws CompileException { FuncState fs = this.fs; FuncState.BlockCnt bl = new FuncState.BlockCnt(); fs.enterblock(bl, false); - this.chunk(); - LuaC._assert(bl.breaklist.i == NO_JUMP); + this.statlist(); + //LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); fs.leaveblock(); } @@ -1598,18 +1726,19 @@ private void check_conflict(LHS_assign lh, expdesc v) throws CompileException { boolean conflict = false; for (; lh != null; lh = lh.prev) { if (lh.v.k == VINDEXED) { - if (lh.v.u.s.info == v.u.s.info) { /* conflict? */ + if (lh.v.u.ind.vt == v.k && lh.v.u.ind.t == v.u.info) { /* conflict? */ conflict = true; - lh.v.u.s.info = extra; /* previous assignment will use safe copy */ + lh.v.u.ind.vt = VLOCAL; + lh.v.u.ind.t = extra; /* previous assignment will use safe copy */ } - if (lh.v.u.s.aux == v.u.s.info) { /* conflict? */ + if (v.k == VLOCAL && lh.v.u.ind.idx == v.u.info) { /* conflict? */ conflict = true; - lh.v.u.s.aux = extra; /* previous assignment will use safe copy */ + lh.v.u.ind.idx = extra; /* previous assignment will use safe copy */ } } } if (conflict) { - fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */ + fs.codeABC(Lua52.OP_MOVE, fs.freereg, v.u.info, 0); /* make copy */ fs.reserveregs(1); } } @@ -1660,25 +1789,36 @@ private int cond() throws CompileException { return v.f.i; } - - private void breakstat() throws CompileException { - FuncState fs = this.fs; - FuncState.BlockCnt bl = fs.bl; - boolean upval = false; - while (bl != null && !bl.isbreakable) { - upval |= bl.upval; - bl = bl.previous; + private void gotostat(int pc) throws CompileException { + LuaString label; + if (testnext(TK_GOTO)) { + label = str_checkname(); + } else { + this.nextToken(); + label = LuaString.valueOf("break"); } - if (bl == null) { - throw syntaxError("no loop to break"); + int g = newlabelentry(dyd, false, label, linenumber, pc); + findlabel(g); + } + + + + private void labelstat(LuaString label, int line) throws CompileException { + /* label -> '::' NAME '::' */ + fs.checkrepeated(dyd.label, dyd.nlabel, label); /* check for repeated labels */ + checknext(TK_DBCOLON); /* skip double colon */ + /* create new entry for this label */ + int l = newlabelentry(dyd, true, label, line, fs.pc); /* index of new label being created */ + while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ + statement(); } - if (upval) { - fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0); + if (block_follow(false)) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + dyd.label[l].nactvar = fs.bl.nactvar; } - fs.concat(bl.breaklist, fs.jump()); + findgotos(dyd.label[l]); } - private void whilestat(int line) throws CompileException { /* whilestat -> WHILE cond DO block END */ FuncState fs = this.fs; @@ -1707,18 +1847,14 @@ private void repeatstat(int line) throws CompileException { fs.enterblock(bl1, true); /* loop block */ fs.enterblock(bl2, false); /* scope block */ this.nextToken(); /* skip REPEAT */ - this.chunk(); + this.statlist(); this.check_match(TK_UNTIL, TK_REPEAT, line); condexit = this.cond(); /* read condition (inside scope block) */ - if (!bl2.upval) { /* no upvalues? */ - fs.leaveblock(); /* finish scope */ - fs.patchlist(condexit, repeat_init); /* close the loop */ - } else { /* complete semantics when there are upvalues */ - this.breakstat(); /* if condition then break */ - fs.patchtohere(condexit); /* else... */ - fs.leaveblock(); /* finish scope... */ - fs.patchlist(fs.jump(), repeat_init); /* and repeat */ + if (bl2.upval) { /* upvalues? */ + fs.patchclose(condexit, bl2.nactvar); } + fs.leaveblock(); /* finish scope */ + fs.patchlist(condexit, repeat_init); /* close the loop */ fs.leaveblock(); /* finish loop */ } @@ -1740,17 +1876,22 @@ private void forbody(int base, int line, int nvars, boolean isnum) throws Compil int prep, endfor; this.adjustlocalvars(3); /* control variables */ this.checknext(TK_DO); - prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump(); + prep = isnum ? fs.codeAsBx(Lua52.OP_FORPREP, base, NO_JUMP) : fs.jump(); fs.enterblock(bl, false); /* scope for declared variables */ this.adjustlocalvars(nvars); fs.reserveregs(nvars); this.block(); fs.leaveblock(); /* end of scope for declared variables */ fs.patchtohere(prep); - endfor = isnum ? fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP) : fs - .codeABC(Lua.OP_TFORLOOP, base, 0, nvars); - fs.fixline(line); /* pretend that `Lua.OP_FOR' starts the loop */ - fs.patchlist(isnum ? endfor : fs.jump(), prep + 1); + if (isnum) /* numeric for? */ + endfor = fs.codeAsBx(Lua52.OP_FORLOOP, base, NO_JUMP); + else { /* generic for */ + fs.codeABC(Lua52.OP_TFORCALL, base, 0, nvars); + fs.fixline(line); + endfor = fs.codeAsBx(Lua52.OP_TFORLOOP, base + 2, NO_JUMP); + } + fs.patchlist(endfor, prep + 1); + fs.fixline(line); /* pretend that `Lua52.OP_FOR' starts the loop */ } @@ -1769,7 +1910,7 @@ private void fornum(LuaString varname, int line) throws CompileException { if (this.testnext(',')) { this.exp1(); /* optional step */ } else { /* default step = 1 */ - fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); + fs.codeABx(Lua52.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); fs.reserveregs(1); } this.forbody(base, line, 1, true); @@ -1824,14 +1965,38 @@ private void forstat(int line) throws CompileException { } - private int test_then_block() throws CompileException { + private void test_then_block(IntPtr escapelist) throws CompileException { /* test_then_block -> [IF | ELSEIF] cond THEN block */ - int condexit; + FuncState.BlockCnt bl = new FuncState.BlockCnt(); + int jf; this.nextToken(); /* skip IF or ELSEIF */ - condexit = this.cond(); + expdesc v = new expdesc(); + expr(v); this.checknext(TK_THEN); - this.block(); /* `then' part */ - return condexit; + if (t.token == TK_GOTO || t.token == TK_BREAK) { + fs.goiffalse(v); + fs.enterblock(bl, false); + gotostat(v.t.i); + while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ + statement(); + } + if (block_follow(false)) { /* 'goto' is the entire block? */ + fs.leaveblock(); + return; + } else { + jf = fs.jump(); + } + } else { + fs.goiftrue(v); + fs.enterblock(bl, false); + jf = v.f.i; + } + this.statlist(); /* `then' part */ + fs.leaveblock(); + if (t.token == TK_ELSE || t.token == TK_ELSEIF) { + fs.concat(escapelist, fs.jump()); + } + fs.patchtohere(jf); } @@ -1839,24 +2004,16 @@ private void ifstat(int line) throws CompileException { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] * END */ FuncState fs = this.fs; - int flist; IntPtr escapelist = new IntPtr(NO_JUMP); - flist = test_then_block(); /* IF cond THEN block */ + test_then_block(escapelist); /* IF cond THEN block */ while (this.t.token == TK_ELSEIF) { - fs.concat(escapelist, fs.jump()); - fs.patchtohere(flist); - flist = test_then_block(); /* ELSEIF cond THEN block */ + test_then_block(escapelist); /* ELSEIF cond THEN block */ } - if (this.t.token == TK_ELSE) { - fs.concat(escapelist, fs.jump()); - fs.patchtohere(flist); - this.nextToken(); /* skip ELSE (after patch, for correct line info) */ + if (testnext(TK_ELSE)) { this.block(); /* `else' part */ - } else { - fs.concat(escapelist, flist); } - fs.patchtohere(escapelist.i); this.check_match(TK_END, TK_IF, line); + fs.patchtohere(escapelist.i); } private void localfunc() throws CompileException { @@ -1940,60 +2097,72 @@ private void retstat() throws CompileException { expdesc e = new expdesc(); int first, nret; /* registers with returned values */ this.nextToken(); /* skip RETURN */ - if (block_follow(this.t.token) || this.t.token == ';') { + if (block_follow(true) || this.t.token == ';') { first = nret = 0; /* return no values */ } else { nret = this.explist1(e); /* optional return values */ if (hasmultret(e.k)) { fs.setmultret(e); if (e.k == VCALL && nret == 1) { /* tail call? */ - LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); - LuaC._assert(Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); + LuaC.SET_OPCODE(fs.getcodePtr(e), Lua52.OP_TAILCALL); + LuaC._assert(Lua52.GETARG_A(fs.getcode(e)) == fs.nactvar, linenumber); } first = fs.nactvar; - nret = Lua.LUA_MULTRET; /* return all values */ + nret = Lua52.LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ { first = fs.exp2anyreg(e); } else { fs.exp2nextreg(e); /* values must go to the `stack' */ first = fs.nactvar; /* return all `active' values */ - LuaC._assert(nret == fs.freereg - first); + LuaC._assert(nret == fs.freereg - first, linenumber); } } } fs.ret(first, nret); + testnext(';'); } - private boolean statement() throws CompileException { + private void statement() throws CompileException { int line = this.linenumber; /* may be needed for error messages */ + enterlevel(); + if (this.t.token == TK_NAME && this.t.seminfo.ts.equals(LuaString.valueOf("goto"))) { + lookahead(); + if (lookahead.token == TK_NAME) { + this.t.token = TK_GOTO; + } + } switch (this.t.token) { + case ';': { /* stat -> ';' (empty statement) */ + nextToken(); /* skip ';' */ + break; + } case TK_IF: { /* stat -> ifstat */ this.ifstat(line); - return false; + break; } case TK_WHILE: { /* stat -> whilestat */ this.whilestat(line); - return false; + break; } case TK_DO: { /* stat -> DO block END */ this.nextToken(); /* skip DO */ this.block(); this.check_match(TK_END, TK_DO, line); - return false; + break; } case TK_FOR: { /* stat -> forstat */ this.forstat(line); - return false; + break; } case TK_REPEAT: { /* stat -> repeatstat */ this.repeatstat(line); - return false; + break; } case TK_FUNCTION: { this.funcstat(line); /* stat -> funcstat */ - return false; + break; } case TK_LOCAL: { /* stat -> localstat */ this.nextToken(); /* skip LOCAL */ @@ -2002,36 +2171,44 @@ private boolean statement() throws CompileException { } else { this.localstat(); } - return false; + break; + } + case TK_DBCOLON: { /* stat -> label */ + this.nextToken(); + this.labelstat(str_checkname(), line); + break; } case TK_RETURN: { /* stat -> retstat */ this.retstat(); - return true; /* must be last statement */ + break; } - case TK_BREAK: { /* stat -> breakstat */ - this.nextToken(); /* skip BREAK */ - this.breakstat(); - return true; /* must be last statement */ + case TK_BREAK: /* stat -> breakstat */ + case TK_GOTO: { /* stat -> 'goto' NAME */ + if (this.t.token == TK_GOTO && LuaC.blockGoto) { + throw lexError("illegal jump statement", this.t.token); + } + this.gotostat(fs.jump()); + break; } default: { this.exprstat(); - return false; /* to avoid warnings */ + break; } } + LuaC._assert(fs.f.maxstacksize >= fs.freereg && fs.freereg >= fs.nactvar, linenumber); + fs.freereg = fs.nactvar; /* free registers */ + leavelevel(); } - void chunk() throws CompileException { + void statlist() throws CompileException { /* chunk -> { stat [`;'] } */ - boolean islast = false; - this.enterlevel(); - while (!islast && !block_follow(this.t.token)) { - islast = this.statement(); - this.testnext(';'); - LuaC._assert(this.fs.f.maxstacksize >= this.fs.freereg - && this.fs.freereg >= this.fs.nactvar); - this.fs.freereg = this.fs.nactvar; /* free registers */ + while (!block_follow(true)) { + if (this.t.token == TK_RETURN) { + statement(); + return; /* 'return' must be last statement */ + } + this.statement(); } - this.leavelevel(); } /* }====================================================================== */ diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java b/src/main/java/org/squiddev/cobalt/compiler/LexState52.java deleted file mode 100644 index 74568100..00000000 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState52.java +++ /dev/null @@ -1,2215 +0,0 @@ -/* - * The MIT License (MIT) - * - * Original Source: Copyright (c) 2009-2011 Luaj.org. All rights reserved. - * Modifications: Copyright (c) 2015-2020 SquidDev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.squiddev.cobalt.compiler; - -import org.squiddev.cobalt.*; -import org.squiddev.cobalt.function.LocalVariable; -import org.squiddev.cobalt.lib.Utf8Lib; - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Hashtable; - -public class LexState52 { - - private static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)"; - private static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)"; - private static final String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)"; - private static final String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)"; - private static final String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)"; - private static final String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)"; - - // keywords array - private static final String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[]{ - RESERVED_LOCAL_VAR_FOR_CONTROL, - RESERVED_LOCAL_VAR_FOR_GENERATOR, - RESERVED_LOCAL_VAR_FOR_INDEX, - RESERVED_LOCAL_VAR_FOR_LIMIT, - RESERVED_LOCAL_VAR_FOR_STATE, - RESERVED_LOCAL_VAR_FOR_STEP - }; - private static final Hashtable RESERVED_LOCAL_VAR_KEYWORDS_TABLE = new Hashtable<>(); - - static { - for (String RESERVED_LOCAL_VAR_KEYWORD : RESERVED_LOCAL_VAR_KEYWORDS) { - RESERVED_LOCAL_VAR_KEYWORDS_TABLE.put(RESERVED_LOCAL_VAR_KEYWORD, Boolean.TRUE); - } - } - - private static final int EOZ = -1; - private static final int MAXSRC = 80; - private static final int MAX_INT = Integer.MAX_VALUE - 2; - private static final int UCHAR_MAX = 255; // TODO, convert to unicode CHAR_MAX? - private static final int LUAI_MAXCCALLS = 200; - - private static String LUA_QS(String s) { - return "'" + s + "'"; - } - - private static String LUA_QL(Object o) { - return LUA_QS(String.valueOf(o)); - } - - private static final int LUA_COMPAT_LSTR = 1; // 1 for compatibility, 2 for old behavior - private static final boolean LUA_COMPAT_VARARG = true; - - public static boolean isReservedKeyword(String varName) { - return RESERVED_LOCAL_VAR_KEYWORDS_TABLE.containsKey(varName); - } - - /* - ** Marks the end of a patch list. It is an invalid value both as an absolute - ** address, and as a list link (would link an element to itself). - */ - static final int NO_JUMP = -1; - - /* - ** grep "ORDER OPR" if you change these enums - */ - static final int - OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5, - OPR_CONCAT = 6, - OPR_NE = 7, OPR_EQ = 8, - OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12, - OPR_AND = 13, OPR_OR = 14, - OPR_NOBINOPR = 15; - - static final int - OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3; - - /* exp kind */ - static final int - VVOID = 0, /* no value */ - VNIL = 1, - VTRUE = 2, - VFALSE = 3, - VK = 4, /* info = index of constant in `k' */ - VKNUM = 5, /* nval = numerical value */ - VLOCAL = 6, /* info = local register */ - VUPVAL = 7, /* info = index of upvalue in `upvalues' */ - VINDEXED = 9, /* info = table register, aux = index register (or `k') */ - VJMP = 10, /* info = instruction pc */ - VRELOCABLE = 11, /* info = instruction pc */ - VNONRELOC = 12, /* info = result register */ - VCALL = 13, /* info = instruction pc */ - VVARARG = 14; /* info = instruction pc */ - - /* semantics information */ - private static class SemInfo { - LuaValue r; - LuaString ts; - } - - private static class Token { - int token; - final SemInfo seminfo = new SemInfo(); - - public void set(Token other) { - this.token = other.token; - this.seminfo.r = other.seminfo.r; - this.seminfo.ts = other.seminfo.ts; - } - } - - private int current; /* current character (charint) */ - private int linenumber; /* input line counter */ - int lastline; /* line of last token `consumed' */ - private final Token t = new Token(); /* current token */ - private final Token lookahead = new Token(); /* look ahead token */ - FuncState52 fs; /* `FuncState52' is private to the parser */ - private InputStream z; /* input stream */ - private byte[] buff; /* buffer for tokens */ - private int nbuff; /* length of buffer */ - private LuaString source; /* current source name */ - private byte decpoint; /* locale decimal point */ - public int nCcalls; - private final HashMap strings = new HashMap<>(); - DynamicData dyd; - - /* ORDER RESERVED */ - private final static String[] luaX_tokens = { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", "if", - "in", "local", "nil", "not", "or", "repeat", - "return", "then", "true", "until", "while", - "..", "...", "==", ">=", "<=", "~=", "::", - "", - "", "", "", - }; - - final static int - // terminal symbols denoted by reserved words - TK_AND = 257, TK_BREAK = 258, TK_DO = 259, TK_ELSE = 260, TK_ELSEIF = 261, - TK_END = 262, TK_FALSE = 263, TK_FOR = 264, TK_FUNCTION = 265, TK_GOTO = 266, TK_IF = 267, - 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_EQ = 281, TK_GE = 282, TK_LE = 283, TK_NE = 284, TK_DBCOLON = 285, - TK_EOS = 286, - TK_NUMBER = 287, TK_NAME = 288, TK_STRING = 289; - - private final static int FIRST_RESERVED = TK_AND; - private final static int NUM_RESERVED = TK_WHILE + 1 - FIRST_RESERVED; - - private final static Hashtable RESERVED = new Hashtable<>(); - - static { - for (int i = 0; i < NUM_RESERVED; i++) { - LuaString ts = ValueFactory.valueOf(luaX_tokens[i]); - RESERVED.put(ts, FIRST_RESERVED + i); - } - } - - private static boolean isAlphaNum(int c) { - return c >= '0' && c <= '9' - || c >= 'a' && c <= 'z' - || c >= 'A' && c <= 'Z' - || c == '_'; - } - - private static boolean isAlpha(int c) { - return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; - } - - private static boolean isDigit(int c) { - return c >= '0' && c <= '9'; - } - - private static boolean isSpace(int c) { - return c <= ' '; - } - - private static boolean isHex(int c) { - return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F'; - } - - public LexState52(InputStream stream) { - this.z = stream; - this.buff = new byte[32]; - } - - private void nextChar() { - try { - current = z.read(); - } catch (IOException e) { - e.printStackTrace(); - current = EOZ; - } - } - - private boolean currIsNewline() { - return current == '\n' || current == '\r'; - } - - private void save_and_next() { - save(current); - nextChar(); - } - - private void save(int c) { - if (buff == null || nbuff + 1 > buff.length) { - buff = LuaC.realloc(buff, nbuff * 2 + 1); - } - buff[nbuff++] = (byte) c; - } - - - private String token2str(int token) { - if (token < FIRST_RESERVED) { - return LUA_QS(iscntrl(token) ? "char(" + token + ")" : String.valueOf((char) token)); - } else { - String name = luaX_tokens[token - FIRST_RESERVED]; - return token < TK_EOS ? LUA_QS(name) : name; - } - } - - private static boolean iscntrl(int token) { - return token < ' '; - } - - private String txtToken(int token) { - switch (token) { - case TK_NAME: - case TK_STRING: - case TK_NUMBER: - return LUA_QS(new String(buff, 0, nbuff)); - default: - return token2str(token); - } - } - - CompileException lexError(String msg, int token) { - String cid = chunkId(source.toString()); // TODO: get source name from source - String message = cid + ":" + linenumber + ": " + msg; - if (token != 0) message += " near " + txtToken(token); - return new CompileException(message); - } - - String chunkId(String source) { - if (source.startsWith("=")) { - return source.substring(1); - } - String end = ""; - if (source.startsWith("@")) { - source = source.substring(1); - } else { - source = "[string \"" + source; - end = "\"]"; - } - int n = source.length() + end.length(); - if (n > MAXSRC) { - source = source.substring(0, MAXSRC - end.length() - 3) + "..."; - } - return source + end; - } - - CompileException syntaxError(String msg) { - return lexError(msg, t.token); - } - - // look up and keep at most one copy of each string - public LuaString newString(byte[] bytes, int offset, int len) { - LuaString tmp = LuaString.valueOf(bytes, offset, len); - LuaString v = strings.get(tmp); - if (v == null) { - // must copy bytes, since bytes could be from reusable buffer - byte[] copy = new byte[len]; - System.arraycopy(bytes, offset, copy, 0, len); - v = LuaString.valueOf(copy); - strings.put(v, v); - } - return v; - } - - // only called by new_localvarliteral() for var names. - private LuaString newString(String s) { - byte[] b = s.getBytes(); - return newString(b, 0, b.length); - } - - private void inclineNumber() throws CompileException { - int old = current; - LuaC._assert(currIsNewline(), linenumber); - nextChar(); /* skip '\n' or '\r' */ - if (currIsNewline() && current != old) { - nextChar(); /* skip '\n\r' or '\r\n' */ - } - if (++linenumber >= MAX_INT) { - throw syntaxError("chunk has too many lines"); - } - } - - void setinput(int firstByte, InputStream z, LuaString source) { - this.decpoint = '.'; - this.lookahead.token = TK_EOS; /* no look-ahead token */ - this.z = z; - this.fs = null; - this.linenumber = 1; - this.lastline = 1; - this.source = source; - this.nbuff = 0; /* initialize buffer */ - this.current = firstByte; /* read first char */ - this.skipShebang(); - } - - private void skipShebang() { - if (current == '#') { - while (!currIsNewline() && current != EOZ) { - nextChar(); - } - } - } - - - - /* - ** ======================================================= - ** LEXICAL ANALYZER - ** ======================================================= - */ - - - private boolean check_next(String set) { - if (set.indexOf(current) < 0) { - return false; - } - save_and_next(); - return true; - } - - private void buffreplace(byte from, byte to) { - int n = nbuff; - byte[] p = buff; - while (--n >= 0) { - if (p[n] == from) { - p[n] = to; - } - } - } - - private void str2d(String str, SemInfo seminfo) throws CompileException { - // If we're a hex string, try to parse as a long. Java's handling of hex floats - // is much more limited than C's version. - if (str.startsWith("0x") || str.startsWith("0X")) { - try { - seminfo.r = ValueFactory.valueOf(Long.valueOf(str.substring(2), 16)); - return; - } catch (NumberFormatException ignored) { - } - } - - try { - seminfo.r = ValueFactory.valueOf(Double.parseDouble(str)); - return; - } catch (NumberFormatException ignored) { - } - - throw lexError("malformed number", TK_NUMBER); - } - - private void read_numeral(SemInfo seminfo) throws CompileException { - LuaC._assert(isDigit(current), linenumber); - - int first = current; - save_and_next(); - String exp = "Ee"; - if (first == '0' && check_next("xX")) exp = "Pp"; - - while (true) { - // Exponential and sign - if (check_next(exp)) check_next("+-"); - - if (isHex(current) || current == '.') { - save_and_next(); - } else { - break; - } - } - - String str = new String(buff, 0, nbuff); - str2d(str, seminfo); - } - - private int skip_sep() throws CompileException { - int count = 0; - int s = current; - LuaC._assert(s == '[' || s == ']', linenumber); - save_and_next(); - while (current == '=') { - save_and_next(); - count++; - } - return current == s ? count : -count - 1; - } - - private void read_long_string(SemInfo seminfo, int sep) throws CompileException { - int cont = 0; - save_and_next(); /* skip 2nd `[' */ - if (currIsNewline()) /* string starts with a newline? */ { - inclineNumber(); /* skip it */ - } - for (boolean endloop = false; !endloop; ) { - switch (current) { - case EOZ: - String msg = seminfo != null ? "unfinished long string" - : "unfinished long comment"; - throw lexError(msg, TK_EOS); - case '[': { - if (skip_sep() == sep) { - save_and_next(); /* skip 2nd `[' */ - cont++; - if (LUA_COMPAT_LSTR == 1) { - if (sep == 0) { - throw lexError("nesting of [[...]] is deprecated", (int) '['); - } - } - } - break; - } - case ']': { - if (skip_sep() == sep) { - save_and_next(); /* skip 2nd `]' */ - if (LUA_COMPAT_LSTR == 2) { - cont--; - if (sep == 0 && cont >= 0) { - break; - } - } - endloop = true; - } - break; - } - case '\n': - case '\r': { - save('\n'); - inclineNumber(); - if (seminfo == null) { - nbuff = 0; /* avoid wasting space */ - } - break; - } - default: { - if (seminfo != null) { - save_and_next(); - } else { - nextChar(); - } - } - } - } - if (seminfo != null) { - seminfo.ts = newString(buff, 2 + sep, nbuff - 2 * (2 + sep)); - } - } - - private void read_string(int del, SemInfo seminfo) throws CompileException { - save_and_next(); - while (current != del) { - switch (current) { - case EOZ: - throw lexError("unfinished string", TK_EOS); - case '\n': - case '\r': - throw lexError("unfinished string", TK_STRING); - case '\\': { - save_and_next(); /* do not save the `\' */ - switch (current) { - case 'a': /* bell */ - saveEscape('\u0007'); - break; - case 'b': /* backspace */ - saveEscape('\b'); - break; - case 'f': /* form feed */ - saveEscape('\f'); - break; - case 'n': /* newline */ - saveEscape('\n'); - break; - case 'r': /* carriage return */ - saveEscape('\r'); - break; - case 't': /* tab */ - saveEscape('\t'); - break; - case 'v': /* vertical tab */ - saveEscape('\u000B'); - break; - case 'x': - saveEscape(readHexEsc()); - break; - case 'u': - readUtf8Esc(); - break; - case '\n': /* go through */ - case '\r': - nbuff--; - save('\n'); - inclineNumber(); - break; - case EOZ: - break; /* will raise an error next loop */ - case 'z': { // "zap" following span of spaces - nbuff--; - nextChar(); // Skip z and remove '\\' - while (isSpace(current)) { - if (currIsNewline()) inclineNumber(); - nextChar(); - } - break; - } - default: { - if (!isDigit(current)) { - nbuff--; - save_and_next(); /* handles \\, \", \', and \? */ - } else { /* \xxx */ - int c = readDecEsc(); - nbuff--; - save(c); - } - break; - } - } - break; - } - default: - save_and_next(); - } - } - save_and_next(); /* skip delimiter */ - seminfo.ts = newString(buff, 1, nbuff - 2); - } - - private void saveEscape(int character) { - nextChar(); - nbuff--; - save(character); - } - - private int readHexEsc() throws CompileException { - int r = (readHex() << 4) | readHex(); - nbuff -= 2; - return r; - } - - private int readDecEsc() throws CompileException { - int i = 0; - int result = 0; - for (; i < 3 && isDigit(current); i++) { - result = 10 * result + current - '0'; - save_and_next(); - } - - if (result > UCHAR_MAX) throw escapeError("escape sequence too large"); - nbuff -= i; - - return result; - } - - private void readUtf8Esc() throws CompileException { - save_and_next(); - if (current != '{') throw escapeError("mising '{'"); - - int i = 4; - long codepoint = readHex(); - while (true) { - save_and_next(); - if (!isHex(current)) break; - - i++; - codepoint = (codepoint << 4) | hexValue(current); - if (codepoint > Utf8Lib.MAX_UNICODE) throw escapeError("UTF-8 value too large"); - } - if (current != '}') throw escapeError("missing '}'"); - nextChar(); - - nbuff -= i; - if (codepoint < 0x80) { - save((int) codepoint); - } else { - byte[] buffer = new byte[8]; - int j = Utf8Lib.buildCharacter(buffer, codepoint); - for (; j > 0; j--) save(buffer[8 - j]); - } - } - - private int readHex() throws CompileException { - save_and_next(); - if (!isHex(current)) throw escapeError("hexadecimal digit expected"); - return hexValue(current); - } - - private static int hexValue(int c) { - // Terrible bit twiddling right here: - // 'A'..'F' corresponds to 0x41..0x46, and 'a'..'f' to 0x61..0x66. So bitwise and with 0xf - // gives us the last digit, +9 to map from 1..6 to 10..15. - return c <= '9' ? c - '0' : (c & 0xf) + 9; - } - - private CompileException escapeError(String message) { - if (current != EOZ) save_and_next(); - return lexError(message, TK_STRING); - } - - private int llex(SemInfo seminfo) throws CompileException { - nbuff = 0; - while (true) { - switch (current) { - case '\n': - case '\r': { - inclineNumber(); - continue; - } - case '-': { - nextChar(); - if (current != '-') { - return '-'; - } - /* else is a comment */ - nextChar(); - if (current == '[') { - int sep = skip_sep(); - nbuff = 0; /* `skip_sep' may dirty the buffer */ - if (sep >= 0) { - read_long_string(null, sep); /* long comment */ - nbuff = 0; - continue; - } - } - /* else short comment */ - while (!currIsNewline() && current != EOZ) { - nextChar(); - } - continue; - } - case '[': { - int sep = skip_sep(); - if (sep >= 0) { - read_long_string(seminfo, sep); - return TK_STRING; - } else if (sep == -1) { - return '['; - } else { - throw lexError("invalid long string delimiter", TK_STRING); - } - } - case '=': { - nextChar(); - if (current != '=') { - return '='; - } else { - nextChar(); - return TK_EQ; - } - } - case '<': { - nextChar(); - if (current != '=') { - return '<'; - } else { - nextChar(); - return TK_LE; - } - } - case '>': { - nextChar(); - if (current != '=') { - return '>'; - } else { - nextChar(); - return TK_GE; - } - } - case '~': { - nextChar(); - if (current != '=') { - return '~'; - } else { - nextChar(); - return TK_NE; - } - } - case ':': { - nextChar(); - if (current != ':') { - return ':'; - } else { - nextChar(); - return TK_DBCOLON; - } - } - case '"': - case '\'': { - read_string(current, seminfo); - return TK_STRING; - } - case '.': { - save_and_next(); - if (check_next(".")) { - if (check_next(".")) { - return TK_DOTS; /* ... */ - } else { - return TK_CONCAT; /* .. */ - } - } else if (!isDigit(current)) { - return '.'; - } else { - read_numeral(seminfo); - return TK_NUMBER; - } - } - case EOZ: { - return TK_EOS; - } - default: { - if (isSpace(current)) { - LuaC._assert(!currIsNewline(), linenumber); - nextChar(); - } else if (isDigit(current)) { - read_numeral(seminfo); - return TK_NUMBER; - } else if (isAlpha(current) || current == '_') { - /* identifier or reserved word */ - LuaString ts; - do { - save_and_next(); - } while (isAlphaNum(current) || current == '_'); - ts = newString(buff, 0, nbuff); - if (RESERVED.containsKey(ts) && !ts.equals(LuaString.valueOf("goto"))) { - return RESERVED.get(ts); - } else { - seminfo.ts = ts; - return TK_NAME; - } - } else { - int c = current; - nextChar(); - return c; /* single-char tokens (+ - / ...) */ - } - } - } - } - } - - void nextToken() throws CompileException { - lastline = linenumber; - if (lookahead.token != TK_EOS) { /* is there a look-ahead token? */ - t.set(lookahead); /* use this one */ - lookahead.token = TK_EOS; /* and discharge it */ - } else { - t.token = llex(t.seminfo); /* read next token */ - } - } - - private void lookahead() throws CompileException { - LuaC._assert(lookahead.token == TK_EOS, linenumber); - lookahead.token = llex(lookahead.seminfo); - } - - // ============================================================= - // from lcode.h - // ============================================================= - - - // ============================================================= - // from lparser.c - // ============================================================= - - static class expdesc { - int k; // expkind, from enumerated list, above - - static class U { // originally a union - static class S { - int idx; - int t; - int vt; - } - - final S ind = new S(); - int info; - private LuaValue _nval; - - public void setNval(LuaValue r) { - _nval = r; - } - - public LuaValue nval() { - return _nval == null ? LuaInteger.valueOf(info) : _nval; - } - } - - final U u = new U(); - final IntPtr t = new IntPtr(); /* patch list of `exit when true' */ - final IntPtr f = new IntPtr(); /* patch list of `exit when false' */ - - void init(int k, int i) { - this.f.i = NO_JUMP; - this.t.i = NO_JUMP; - this.k = k; - this.u.info = i; - } - - boolean hasjumps() { - return t.i != f.i; - } - - boolean isnumeral() { - return k == VKNUM && t.i == NO_JUMP && f.i == NO_JUMP; - } - - public void setvalue(expdesc other) { - this.k = other.k; - this.u._nval = other.u._nval; - this.u.info = other.u.info; - this.u.ind.idx = other.u.ind.idx; - this.u.ind.t = other.u.ind.t; - this.u.ind.vt = other.u.ind.vt; - this.t.i = other.t.i; - this.f.i = other.f.i; - } - } - - static class LabelDescription { - LuaString name; /* label identifier */ - int pc; /* position in code */ - int line; /* line where it appeared */ - short nactvar; /* local level where it appears in current block */ - } - - static class DynamicData { - short[] actvar; - int nactvar; - LabelDescription[] gt; - short ngt; - LabelDescription[] label; - short nlabel; - - DynamicData() { - actvar = null; nactvar = 0; - gt = null; ngt = 0; - label = null; nlabel = 0; - } - } - - private boolean hasmultret(int k) { - return k == VCALL || k == VVARARG; - } - - /*---------------------------------------------------------------------- - name args description - ------------------------------------------------------------------------*/ - - /* - * * prototypes for recursive non-terminal functions - */ - - private void error_expected(int token) throws CompileException { - throw syntaxError(token2str(token) + " expected"); - } - - private boolean testnext(int c) throws CompileException { - if (t.token == c) { - nextToken(); - return true; - } else { - return false; - } - } - - void check(int c) throws CompileException { - if (t.token != c) { - error_expected(c); - } - } - - private void checknext(int c) throws CompileException { - check(c); - nextToken(); - } - - private void check_condition(boolean c, String msg) throws CompileException { - if (!c) { - throw syntaxError(msg); - } - } - - - private void check_match(int what, int who, int where) throws CompileException { - if (!testnext(what)) { - if (where == linenumber) { - error_expected(what); - } else { - throw syntaxError(LUA_QS(token2str(what)) - + " expected " + "(to close " + LUA_QS(token2str(who)) - + " at line " + where + ")"); - } - } - } - - private LuaString str_checkname() throws CompileException { - LuaString ts; - check(TK_NAME); - ts = t.seminfo.ts; - nextToken(); - return ts; - } - - private void codestring(expdesc e, LuaString s) { - e.init(VK, fs.stringK(s)); - } - - private void checkname(expdesc e) throws CompileException { - codestring(e, str_checkname()); - } - - - private int registerlocalvar(LuaString varname) { - FuncState52 fs = this.fs; - Prototype f = fs.f; - if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length) { - f.locvars = LuaC.realloc(f.locvars, fs.nlocvars * 2 + 1); - } - f.locvars[fs.nlocvars] = new LocalVariable(varname, 0, 0); - return fs.nlocvars++; - } - - - // -// #define new_localvarliteral(ls,v,n) \ -// this.new_localvar(luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) -// - private void new_localvarliteral(String v, int n) throws CompileException { - LuaString ts = newString(v); - new_localvar(ts, n); - } - - private void new_localvar(LuaString name, int n) throws CompileException { - FuncState52 fs = this.fs; - fs.checklimit(fs.nactvar + n + 1, LuaC.LUAI_MAXVARS, "local variables"); - fs.actvar[fs.nactvar + n] = (short) registerlocalvar(name); - } - - private void adjustlocalvars(int nvars) { - FuncState52 fs = this.fs; - fs.nactvar = (short) (fs.nactvar + nvars); - for (; nvars > 0; nvars--) { - fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc; - } - } - - void removevars(int tolevel) { - FuncState52 fs = this.fs; - while (fs.nactvar > tolevel) { - fs.getlocvar(--fs.nactvar).endpc = fs.pc; - } - } - - private void singlevar(expdesc var) throws CompileException { - LuaString varname = str_checkname(); - FuncState52 fs = this.fs; - if (fs.singlevaraux(varname, var, 1) == VVOID) { - expdesc key = new expdesc(); - fs.singlevaraux(LuaString.valueOf("_ENV"), var, 1); - LuaC._assert(var.k == LexState52.VLOCAL || var.k == LexState52.VUPVAL, linenumber); - codestring(key, varname); - fs.indexed(var, key); - } - } - - private void adjust_assign(int nvars, int nexps, expdesc e) throws CompileException { - FuncState52 fs = this.fs; - int extra = nvars - nexps; - if (hasmultret(e.k)) { - /* includes call itself */ - extra++; - if (extra < 0) { - extra = 0; - } - /* last exp. provides the difference */ - fs.setreturns(e, extra); - if (extra > 1) { - fs.reserveregs(extra - 1); - } - } else { - /* close last expression */ - if (e.k != VVOID) { - fs.exp2nextreg(e); - } - if (extra > 0) { - int reg = fs.freereg; - fs.reserveregs(extra); - fs.nil(reg, extra); - } - } - } - - private void enterlevel() throws CompileException { - if (++nCcalls > LUAI_MAXCCALLS) { - throw lexError("chunk has too many syntax levels", 0); - } - } - - private void leavelevel() { - nCcalls--; - } - - private void closegoto(int g, LabelDescription label) throws CompileException { - LabelDescription gt = dyd.gt[g]; - LuaC._assert(gt.name.equals(label.name), linenumber); - if (gt.nactvar < label.nactvar) { - throw new CompileException(String.format(" at line %d jumps into the scope of local '%s'", - gt.name.toString(), gt.line, fs.getlocvar(gt.nactvar).name.toString())); - } - fs.patchlist(gt.pc, label.pc); - for (int i = g; i < dyd.ngt - 1; i++) { - dyd.gt[i] = dyd.gt[i+1]; - } - dyd.ngt--; - } - - boolean findlabel(int g) throws CompileException { - LabelDescription gt = dyd.gt[g]; - for (int i = fs.bl.firstlabel; i < dyd.nlabel; i++) { - LabelDescription lb = dyd.label[i]; - if (lb.name.equals(gt.name)) { - if (gt.nactvar > lb.nactvar && (fs.bl.upval || dyd.nlabel > fs.bl.firstlabel)) { - fs.patchclose(gt.pc, lb.nactvar); - } - closegoto(g, lb); - return true; - } - } - return false; - } - - private int newlabelentry(DynamicData dyd, boolean islabel, LuaString name, int line, int pc) { - if (islabel) { - if (dyd.label == null || dyd.label.length < dyd.nlabel + 1) { - dyd.label = LuaC.realloc(dyd.label, dyd.nlabel * 2 + 1); - } - dyd.label[dyd.nlabel] = new LabelDescription(); - dyd.label[dyd.nlabel].name = name; - dyd.label[dyd.nlabel].line = line; - dyd.label[dyd.nlabel].nactvar = fs.nactvar; - dyd.label[dyd.nlabel].pc = pc; - return dyd.nlabel++; - } else { - if (dyd.gt == null || dyd.gt.length < dyd.ngt + 1) { - dyd.gt = LuaC.realloc(dyd.gt, dyd.ngt * 2 + 1); - } - dyd.gt[dyd.ngt] = new LabelDescription(); - dyd.gt[dyd.ngt].name = name; - dyd.gt[dyd.ngt].line = line; - dyd.gt[dyd.ngt].nactvar = fs.nactvar; - dyd.gt[dyd.ngt].pc = pc; - return dyd.ngt++; - } - } - - private void findgotos(LabelDescription lb) throws CompileException { - int i = fs.bl.firstgoto; - while (i < dyd.ngt) { - if (dyd.gt[i].name.equals(lb.name)) { - closegoto(i, lb); - } else { - i++; - } - } - } - - /* - ** create a label named "break" to resolve break statements - */ - void breaklabel() throws CompileException { - int l = newlabelentry(dyd, true, LuaString.valueOf("break"), 0, fs.pc); - findgotos(dyd.label[l]); - } - - /* - ** generates an error for an undefined 'goto'; choose appropriate - ** message when label name is a reserved word (which can only be 'break') - */ - void /* no return */ undefgoto(LabelDescription gt) throws CompileException { - String msg = String.format(isReservedKeyword(gt.name.toString()) ? "<%s> at line %d not inside a loop" : "no visible label '%s' for at line %d", - gt.name.toString(), gt.line); - throw new CompileException(msg); - } - - Prototype addprototype() { - Prototype clp; - Prototype f = fs.f; - if (f.p == null || fs.np >= f.p.length) { - f.p = LuaC.realloc(f.p, fs.np * 2 + 1); - } - f.p[fs.np++] = clp = new Prototype(); - return clp; - } - - private void codeclosure(expdesc v) throws CompileException { - FuncState52 fs = this.fs.prev; - v.init(LexState52.VRELOCABLE, fs.codeABx(Lua52.OP_CLOSURE, 0, fs.np - 1)); - fs.exp2nextreg(v); - } - - void open_func(FuncState52 fs, FuncState52.BlockCnt bl) throws CompileException { - Prototype f = fs.f; - if (this.fs != null) { - f.source = this.fs.f.source; - } - f.isLua52 = true; - fs.prev = this.fs; /* linked list of FuncState52s */ - fs.ls = this; - this.fs = fs; - fs.pc = 0; - fs.lasttarget = -1; - fs.jpc = new IntPtr(NO_JUMP); - fs.freereg = 0; - fs.nk = 0; - fs.np = 0; - fs.nlocvars = 0; - fs.nactvar = 0; - fs.bl = null; - f.maxstacksize = 2; /* registers 0/1 are always valid */ - //fs.h = new LTable(); - fs.htable = new Hashtable<>(); - fs.enterblock(bl, false); - } - - void close_func() throws CompileException { - FuncState52 fs = this.fs; - Prototype f = fs.f; - this.removevars(0); - fs.ret(0, 0); /* final return */ - fs.leaveblock(); - f.code = LuaC.realloc(f.code, fs.pc); - f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); - // f.sizelineinfo = fs.pc; - f.k = LuaC.realloc(f.k, fs.nk); - f.p = LuaC.realloc(f.p, fs.np); - f.locvars = LuaC.realloc(f.locvars, fs.nlocvars); - // f.sizelocvars = fs.nlocvars; - f.upvalues = LuaC.realloc(f.upvalues, f.nups); - // LuaC._assert (CheckCode.checkcode(f)); - LuaC._assert(fs.bl == null, linenumber); - this.fs = fs.prev; -// L.top -= 2; /* remove table and prototype from the stack */ - // /* last token read was anchored in defunct function; must reanchor it - // */ - // if (fs!=null) ls.anchor_token(); - } - - /*============================================================*/ - /* GRAMMAR RULES */ - /*============================================================*/ - - private void field(expdesc v) throws CompileException { - /* field -> ['.' | ':'] NAME */ - FuncState52 fs = this.fs; - expdesc key = new expdesc(); - fs.exp2anyreg(v); - this.nextToken(); /* skip the dot or colon */ - this.checkname(key); - fs.indexed(v, key); - } - - private void yindex(expdesc v) throws CompileException { - /* index -> '[' expr ']' */ - this.nextToken(); /* skip the '[' */ - this.expr(v); - this.fs.exp2val(v); - this.checknext(']'); - } - - - /* - ** {====================================================================== - ** Rules for Constructors - ** ======================================================================= - */ - - - static class ConsControl { - expdesc v = new expdesc(); /* last list item read */ - expdesc t; /* table descriptor */ - int nh; /* total number of `record' elements */ - int na; /* total number of array elements */ - int tostore; /* number of array elements pending to be stored */ - } - - - private void recfield(ConsControl cc) throws CompileException { - /* recfield -> (NAME | `['exp1`]') = exp1 */ - FuncState52 fs = this.fs; - int reg = this.fs.freereg; - expdesc key = new expdesc(); - expdesc val = new expdesc(); - int rkkey; - if (this.t.token == TK_NAME) { - fs.checklimit(cc.nh, MAX_INT, "items in a constructor"); - this.checkname(key); - } else - /* this.t.token == '[' */ { - this.yindex(key); - } - cc.nh++; - this.checknext('='); - rkkey = fs.exp2RK(key); - this.expr(val); - fs.codeABC(Lua52.OP_SETTABLE, cc.t.u.info, rkkey, fs.exp2RK(val)); - fs.freereg = reg; /* free registers */ - } - - private void listfield(ConsControl cc) throws CompileException { - this.expr(cc.v); - fs.checklimit(cc.na, MAX_INT, "items in a constructor"); - cc.na++; - cc.tostore++; - } - - - private void constructor(expdesc t) throws CompileException { - /* constructor -> ?? */ - FuncState52 fs = this.fs; - int line = this.linenumber; - int pc = fs.codeABC(Lua52.OP_NEWTABLE, 0, 0, 0); - ConsControl cc = new ConsControl(); - cc.na = cc.nh = cc.tostore = 0; - cc.t = t; - t.init(VRELOCABLE, pc); - cc.v.init(VVOID, 0); /* no value (yet) */ - fs.exp2nextreg(t); /* fix it at stack top (for gc) */ - this.checknext('{'); - do { - LuaC._assert(cc.v.k == VVOID || cc.tostore > 0, linenumber); - if (this.t.token == '}') { - break; - } - fs.closelistfield(cc); - switch (this.t.token) { - case TK_NAME: { /* may be listfields or recfields */ - this.lookahead(); - if (this.lookahead.token != '=') /* expression? */ { - this.listfield(cc); - } else { - this.recfield(cc); - } - break; - } - case '[': { /* constructor_item -> recfield */ - this.recfield(cc); - break; - } - default: { /* constructor_part -> listfield */ - this.listfield(cc); - break; - } - } - } while (this.testnext(',') || this.testnext(';')); - this.check_match('}', '{', line); - fs.lastlistfield(cc); - InstructionPtr i = new InstructionPtr(fs.f.code, pc); - LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */ - LuaC.SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */ - } - - /* - ** converts an integer to a "floating point byte", represented as - ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if - ** eeeee != 0 and (xxx) otherwise. - */ - private static int luaO_int2fb(int x) { - int e = 0; /* expoent */ - while (x >= 16) { - x = x + 1 >> 1; - e++; - } - if (x < 8) { - return x; - } else { - return e + 1 << 3 | x - 8; - } - } - - - /* }====================================================================== */ - - private void parlist() throws CompileException { - /* parlist -> [ param { `,' param } ] */ - FuncState52 fs = this.fs; - Prototype f = fs.f; - int nparams = 0; - f.is_vararg = 0; - if (this.t.token != ')') { /* is `parlist' not empty? */ - do { - switch (this.t.token) { - case TK_NAME: { /* param . NAME */ - this.new_localvar(this.str_checkname(), nparams++); - break; - } - case TK_DOTS: { /* param . `...' */ - this.nextToken(); - if (LUA_COMPAT_VARARG) { - /* use `arg' as default name */ - this.new_localvarliteral("arg", nparams++); - f.is_vararg = Lua52.VARARG_HASARG | Lua52.VARARG_NEEDSARG; - } - f.is_vararg |= Lua52.VARARG_ISVARARG; - break; - } - default: - throw syntaxError(" or " + LUA_QL("...") + " expected"); - } - } while (f.is_vararg == 0 && this.testnext(',')); - } - this.adjustlocalvars(nparams); - f.numparams = fs.nactvar - (f.is_vararg & Lua52.VARARG_HASARG); - fs.reserveregs(fs.nactvar); /* reserve register for parameters */ - } - - - private void body(expdesc e, boolean needself, int line) throws CompileException { - /* body -> `(' parlist `)' chunk END */ - FuncState52 new_fs = new FuncState52(); - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - new_fs.f = this.addprototype(); - new_fs.f.linedefined = line; - open_func(new_fs, bl); - this.checknext('('); - if (needself) { - new_localvarliteral("self", 0); - adjustlocalvars(1); - } - this.parlist(); - this.checknext(')'); - this.statlist(); - new_fs.f.lastlinedefined = this.linenumber; - this.check_match(TK_END, TK_FUNCTION, line); - this.codeclosure(e); - this.close_func(); - } - - private int explist1(expdesc v) throws CompileException { - /* explist1 -> expr { `,' expr } */ - int n = 1; /* at least one expression */ - this.expr(v); - while (this.testnext(',')) { - fs.exp2nextreg(v); - this.expr(v); - n++; - } - return n; - } - - - private void funcargs(expdesc f) throws CompileException { - FuncState52 fs = this.fs; - expdesc args = new expdesc(); - int base, nparams; - int line = this.linenumber; - switch (this.t.token) { - case '(': { /* funcargs -> `(' [ explist1 ] `)' */ - if (line != this.lastline) { - throw syntaxError("ambiguous syntax (function call x new statement)"); - } - this.nextToken(); - if (this.t.token == ')') /* arg list is empty? */ { - args.k = VVOID; - } else { - this.explist1(args); - fs.setmultret(args); - } - this.check_match(')', '(', line); - break; - } - case '{': { /* funcargs -> constructor */ - this.constructor(args); - break; - } - case TK_STRING: { /* funcargs -> STRING */ - this.codestring(args, this.t.seminfo.ts); - this.nextToken(); /* must use `seminfo' before `next' */ - break; - } - default: { - throw syntaxError("function arguments expected"); - } - } - LuaC._assert(f.k == VNONRELOC, linenumber); - base = f.u.info; /* base register for call */ - if (hasmultret(args.k)) { - nparams = Lua52.LUA_MULTRET; /* open call */ - } else { - if (args.k != VVOID) { - fs.exp2nextreg(args); /* close last argument */ - } - nparams = fs.freereg - (base + 1); - } - f.init(VCALL, fs.codeABC(Lua52.OP_CALL, base, nparams + 1, 2)); - fs.fixline(line); - fs.freereg = base + 1; /* call remove function and arguments and leaves - * (unless changed) one result */ - } - - - /* - ** {====================================================================== - ** Expression parsing - ** ======================================================================= - */ - - private void prefixexp(expdesc v) throws CompileException { - /* prefixexp -> NAME | '(' expr ')' */ - switch (this.t.token) { - case '(': { - int line = this.linenumber; - this.nextToken(); - this.expr(v); - this.check_match(')', '(', line); - fs.dischargevars(v); - return; - } - case TK_NAME: { - this.singlevar(v); - return; - } - default: { - throw syntaxError("unexpected symbol"); - } - } - } - - - private void primaryexp(expdesc v) throws CompileException { - /* - * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | - * funcargs } - */ - FuncState52 fs = this.fs; - this.prefixexp(v); - for (; ; ) { - switch (this.t.token) { - case '.': { /* field */ - this.field(v); - break; - } - case '[': { /* `[' exp1 `]' */ - expdesc key = new expdesc(); - fs.exp2anyreg(v); - this.yindex(key); - fs.indexed(v, key); - break; - } - case ':': { /* `:' NAME funcargs */ - expdesc key = new expdesc(); - this.nextToken(); - this.checkname(key); - fs.self(v, key); - this.funcargs(v); - break; - } - case '(': - case TK_STRING: - case '{': { /* funcargs */ - fs.exp2nextreg(v); - this.funcargs(v); - break; - } - default: - return; - } - } - } - - - private void simpleexp(expdesc v) throws CompileException { - /* - * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | - * FUNCTION body | primaryexp - */ - switch (this.t.token) { - case TK_NUMBER: { - v.init(VKNUM, 0); - v.u.setNval(this.t.seminfo.r); - break; - } - case TK_STRING: { - this.codestring(v, this.t.seminfo.ts); - break; - } - case TK_NIL: { - v.init(VNIL, 0); - break; - } - case TK_TRUE: { - v.init(VTRUE, 0); - break; - } - case TK_FALSE: { - v.init(VFALSE, 0); - break; - } - case TK_DOTS: { /* vararg */ - FuncState52 fs = this.fs; - this.check_condition(fs.f.is_vararg != 0, "cannot use " + LUA_QL("...") - + " outside a vararg function"); - fs.f.is_vararg &= ~Lua52.VARARG_NEEDSARG; /* don't need 'arg' */ - v.init(VVARARG, fs.codeABC(Lua52.OP_VARARG, 0, 1, 0)); - break; - } - case '{': { /* constructor */ - this.constructor(v); - return; - } - case TK_FUNCTION: { - this.nextToken(); - this.body(v, false, this.linenumber); - return; - } - default: { - this.primaryexp(v); - return; - } - } - this.nextToken(); - } - - - private int getunopr(int op) { - switch (op) { - case TK_NOT: - return OPR_NOT; - case '-': - return OPR_MINUS; - case '#': - return OPR_LEN; - default: - return OPR_NOUNOPR; - } - } - - - private int getbinopr(int op) { - switch (op) { - case '+': - return OPR_ADD; - case '-': - return OPR_SUB; - case '*': - return OPR_MUL; - case '/': - return OPR_DIV; - case '%': - return OPR_MOD; - case '^': - return OPR_POW; - case TK_CONCAT: - return OPR_CONCAT; - case TK_NE: - return OPR_NE; - case TK_EQ: - return OPR_EQ; - case '<': - return OPR_LT; - case TK_LE: - return OPR_LE; - case '>': - return OPR_GT; - case TK_GE: - return OPR_GE; - case TK_AND: - return OPR_AND; - case TK_OR: - return OPR_OR; - default: - return OPR_NOBINOPR; - } - } - - static class Priority { - final byte left; /* left priority for each binary operator */ - - final byte right; /* right priority */ - - public Priority(int i, int j) { - left = (byte) i; - right = (byte) j; - } - } - - private 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(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) */ - }; - - private static final int UNARY_PRIORITY = 8; /* priority for unary operators */ - - - /* - ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } - ** where `binop' is any binary operator with a priority higher than `limit' - */ - private int subexpr(expdesc v, int limit) throws CompileException { - int op; - int uop; - this.enterlevel(); - uop = getunopr(this.t.token); - if (uop != OPR_NOUNOPR) { - this.nextToken(); - this.subexpr(v, UNARY_PRIORITY); - fs.prefix(uop, v); - } else { - this.simpleexp(v); - } - /* expand while operators have priorities higher than `limit' */ - op = getbinopr(this.t.token); - while (op != OPR_NOBINOPR && priority[op].left > limit) { - expdesc v2 = new expdesc(); - int nextop; - this.nextToken(); - fs.infix(op, v); - /* read sub-expression with higher priority */ - nextop = this.subexpr(v2, priority[op].right); - fs.posfix(op, v, v2); - op = nextop; - } - this.leavelevel(); - return op; /* return first untreated operator */ - } - - private void expr(expdesc v) throws CompileException { - this.subexpr(v, 0); - } - - /* }==================================================================== */ - - - - /* - ** {====================================================================== - ** Rules for Statements - ** ======================================================================= - */ - - - private boolean block_follow(boolean withUntil) { - switch (t.token) { - case TK_ELSE: - case TK_ELSEIF: - case TK_END: - case TK_EOS: - return true; - case TK_UNTIL: - return withUntil; - default: - return false; - } - } - - - private void block() throws CompileException { - /* block -> chunk */ - FuncState52 fs = this.fs; - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - fs.enterblock(bl, false); - this.statlist(); - //LuaC._assert(bl.breaklist.i == NO_JUMP, linenumber); - fs.leaveblock(); - } - - - /* - ** structure to chain all variables in the left-hand side of an - ** assignment - */ - static class LHS_assign { - LHS_assign prev; - /* variable (global, local, upvalue, or indexed) */ - expdesc v = new expdesc(); - } - - - /* - ** check whether, in an assignment to a local variable, the local variable - ** is needed in a previous assignment (to a table). If so, save original - ** local value in a safe place and use this safe copy in the previous - ** assignment. - */ - private void check_conflict(LHS_assign lh, expdesc v) throws CompileException { - FuncState52 fs = this.fs; - int extra = fs.freereg; /* eventual position to save local variable */ - boolean conflict = false; - for (; lh != null; lh = lh.prev) { - if (lh.v.k == VINDEXED) { - if (lh.v.u.ind.vt == v.k && lh.v.u.ind.t == v.u.info) { /* conflict? */ - conflict = true; - lh.v.u.ind.vt = VLOCAL; - lh.v.u.ind.t = extra; /* previous assignment will use safe copy */ - } - if (v.k == VLOCAL && lh.v.u.ind.idx == v.u.info) { /* conflict? */ - conflict = true; - lh.v.u.ind.idx = extra; /* previous assignment will use safe copy */ - } - } - } - if (conflict) { - fs.codeABC(Lua52.OP_MOVE, fs.freereg, v.u.info, 0); /* make copy */ - fs.reserveregs(1); - } - } - - - private void assignment(LHS_assign lh, int nvars) throws CompileException { - expdesc e = new expdesc(); - this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED, - "syntax error"); - if (this.testnext(',')) { /* assignment -> `,' primaryexp assignment */ - LHS_assign nv = new LHS_assign(); - nv.prev = lh; - this.primaryexp(nv.v); - if (nv.v.k == VLOCAL) { - this.check_conflict(lh, nv.v); - } - this.assignment(nv, nvars + 1); - } else { /* assignment . `=' explist1 */ - int nexps; - this.checknext('='); - nexps = this.explist1(e); - if (nexps != nvars) { - this.adjust_assign(nvars, nexps, e); - if (nexps > nvars) { - this.fs.freereg -= nexps - nvars; /* remove extra values */ - } - } else { - fs.setoneret(e); /* close last expression */ - fs.storevar(lh.v, e); - return; /* avoid default */ - } - } - e.init(VNONRELOC, this.fs.freereg - 1); /* default assignment */ - fs.storevar(lh.v, e); - } - - - private int cond() throws CompileException { - /* cond -> exp */ - expdesc v = new expdesc(); - /* read condition */ - this.expr(v); - /* `falses' are all equal here */ - if (v.k == VNIL) { - v.k = VFALSE; - } - fs.goiftrue(v); - return v.f.i; - } - - private void gotostat(int pc) throws CompileException { - LuaString label; - if (testnext(TK_GOTO)) { - label = str_checkname(); - } else { - this.nextToken(); - label = LuaString.valueOf("break"); - } - int g = newlabelentry(dyd, false, label, linenumber, pc); - findlabel(g); - } - - - - private void labelstat(LuaString label, int line) throws CompileException { - /* label -> '::' NAME '::' */ - fs.checkrepeated(dyd.label, dyd.nlabel, label); /* check for repeated labels */ - checknext(TK_DBCOLON); /* skip double colon */ - /* create new entry for this label */ - int l = newlabelentry(dyd, true, label, line, fs.pc); /* index of new label being created */ - while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ - statement(); - } - if (block_follow(false)) { /* label is last no-op statement in the block? */ - /* assume that locals are already out of scope */ - dyd.label[l].nactvar = fs.bl.nactvar; - } - findgotos(dyd.label[l]); - } - - private void whilestat(int line) throws CompileException { - /* whilestat -> WHILE cond DO block END */ - FuncState52 fs = this.fs; - int whileinit; - int condexit; - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - this.nextToken(); /* skip WHILE */ - whileinit = fs.getlabel(); - condexit = this.cond(); - fs.enterblock(bl, true); - this.checknext(TK_DO); - this.block(); - fs.patchlist(fs.jump(), whileinit); - this.check_match(TK_END, TK_WHILE, line); - fs.leaveblock(); - fs.patchtohere(condexit); /* false conditions finish the loop */ - } - - private void repeatstat(int line) throws CompileException { - /* repeatstat -> REPEAT block UNTIL cond */ - int condexit; - FuncState52 fs = this.fs; - int repeat_init = fs.getlabel(); - FuncState52.BlockCnt bl1 = new FuncState52.BlockCnt(); - FuncState52.BlockCnt bl2 = new FuncState52.BlockCnt(); - fs.enterblock(bl1, true); /* loop block */ - fs.enterblock(bl2, false); /* scope block */ - this.nextToken(); /* skip REPEAT */ - this.statlist(); - this.check_match(TK_UNTIL, TK_REPEAT, line); - condexit = this.cond(); /* read condition (inside scope block) */ - if (bl2.upval) { /* upvalues? */ - fs.patchclose(condexit, bl2.nactvar); - } - fs.leaveblock(); /* finish scope */ - fs.patchlist(condexit, repeat_init); /* close the loop */ - fs.leaveblock(); /* finish loop */ - } - - - private int exp1() throws CompileException { - expdesc e = new expdesc(); - int k; - this.expr(e); - k = e.k; - fs.exp2nextreg(e); - return k; - } - - - private void forbody(int base, int line, int nvars, boolean isnum) throws CompileException { - /* forbody -> DO block */ - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - FuncState52 fs = this.fs; - int prep, endfor; - this.adjustlocalvars(3); /* control variables */ - this.checknext(TK_DO); - prep = isnum ? fs.codeAsBx(Lua52.OP_FORPREP, base, NO_JUMP) : fs.jump(); - fs.enterblock(bl, false); /* scope for declared variables */ - this.adjustlocalvars(nvars); - fs.reserveregs(nvars); - this.block(); - fs.leaveblock(); /* end of scope for declared variables */ - fs.patchtohere(prep); - if (isnum) /* numeric for? */ - endfor = fs.codeAsBx(Lua52.OP_FORLOOP, base, NO_JUMP); - else { /* generic for */ - fs.codeABC(Lua52.OP_TFORCALL, base, 0, nvars); - fs.fixline(line); - endfor = fs.codeAsBx(Lua52.OP_TFORLOOP, base + 2, NO_JUMP); - } - fs.patchlist(endfor, prep + 1); - fs.fixline(line); /* pretend that `Lua52.OP_FOR' starts the loop */ - } - - - private void fornum(LuaString varname, int line) throws CompileException { - /* fornum -> NAME = exp1,exp1[,exp1] forbody */ - FuncState52 fs = this.fs; - int base = fs.freereg; - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0); - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1); - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2); - this.new_localvar(varname, 3); - this.checknext('='); - this.exp1(); /* initial value */ - this.checknext(','); - this.exp1(); /* limit */ - if (this.testnext(',')) { - this.exp1(); /* optional step */ - } else { /* default step = 1 */ - fs.codeABx(Lua52.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); - fs.reserveregs(1); - } - this.forbody(base, line, 1, true); - } - - - private void forlist(LuaString indexname) throws CompileException { - /* forlist -> NAME {,NAME} IN explist1 forbody */ - FuncState52 fs = this.fs; - expdesc e = new expdesc(); - int nvars = 0; - int line; - int base = fs.freereg; - /* create control variables */ - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++); - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++); - this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++); - /* create declared variables */ - this.new_localvar(indexname, nvars++); - while (this.testnext(',')) { - this.new_localvar(this.str_checkname(), nvars++); - } - this.checknext(TK_IN); - line = this.linenumber; - this.adjust_assign(3, this.explist1(e), e); - fs.checkstack(3); /* extra space to call generator */ - this.forbody(base, line, nvars - 3, false); - } - - - private void forstat(int line) throws CompileException { - /* forstat -> FOR (fornum | forlist) END */ - FuncState52 fs = this.fs; - LuaString varname; - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - fs.enterblock(bl, true); /* scope for loop and control variables */ - this.nextToken(); /* skip `for' */ - varname = this.str_checkname(); /* first variable name */ - switch (this.t.token) { - case '=': - this.fornum(varname, line); - break; - case ',': - case TK_IN: - this.forlist(varname); - break; - default: - throw syntaxError(LUA_QL("=") + " or " + LUA_QL("in") + " expected"); - } - this.check_match(TK_END, TK_FOR, line); - fs.leaveblock(); /* loop scope (`break' jumps to this point) */ - } - - - private void test_then_block(IntPtr escapelist) throws CompileException { - /* test_then_block -> [IF | ELSEIF] cond THEN block */ - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); - int jf; - this.nextToken(); /* skip IF or ELSEIF */ - expdesc v = new expdesc(); - expr(v); - this.checknext(TK_THEN); - if (t.token == TK_GOTO || t.token == TK_BREAK) { - fs.goiffalse(v); - fs.enterblock(bl, false); - gotostat(v.t.i); - while (t.token == ';' || t.token == TK_DBCOLON) { /* skip other no-op statements */ - statement(); - } - if (block_follow(false)) { /* 'goto' is the entire block? */ - fs.leaveblock(); - return; - } else { - jf = fs.jump(); - } - } else { - fs.goiftrue(v); - fs.enterblock(bl, false); - jf = v.f.i; - } - this.statlist(); /* `then' part */ - fs.leaveblock(); - if (t.token == TK_ELSE || t.token == TK_ELSEIF) { - fs.concat(escapelist, fs.jump()); - } - fs.patchtohere(jf); - } - - - private void ifstat(int line) throws CompileException { - /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] - * END */ - FuncState52 fs = this.fs; - IntPtr escapelist = new IntPtr(NO_JUMP); - test_then_block(escapelist); /* IF cond THEN block */ - while (this.t.token == TK_ELSEIF) { - test_then_block(escapelist); /* ELSEIF cond THEN block */ - } - if (testnext(TK_ELSE)) { - this.block(); /* `else' part */ - } - this.check_match(TK_END, TK_IF, line); - fs.patchtohere(escapelist.i); - } - - private void localfunc() throws CompileException { - expdesc v = new expdesc(); - expdesc b = new expdesc(); - FuncState52 fs = this.fs; - this.new_localvar(this.str_checkname(), 0); - v.init(VLOCAL, fs.freereg); - fs.reserveregs(1); - this.adjustlocalvars(1); - this.body(b, false, this.linenumber); - fs.storevar(v, b); - /* debug information will only see the variable after this point! */ - fs.getlocvar(fs.nactvar - 1).startpc = fs.pc; - } - - - private void localstat() throws CompileException { - /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ - int nvars = 0; - int nexps; - expdesc e = new expdesc(); - do { - this.new_localvar(this.str_checkname(), nvars++); - } while (this.testnext(',')); - if (this.testnext('=')) { - nexps = this.explist1(e); - } else { - e.k = VVOID; - nexps = 0; - } - this.adjust_assign(nvars, nexps, e); - this.adjustlocalvars(nvars); - } - - - private boolean funcname(expdesc v) throws CompileException { - /* funcname -> NAME {field} [`:' NAME] */ - boolean needself = false; - this.singlevar(v); - while (this.t.token == '.') { - this.field(v); - } - if (this.t.token == ':') { - needself = true; - this.field(v); - } - return needself; - } - - - private void funcstat(int line) throws CompileException { - /* funcstat -> FUNCTION funcname body */ - boolean needself; - expdesc v = new expdesc(); - expdesc b = new expdesc(); - this.nextToken(); /* skip FUNCTION */ - needself = this.funcname(v); - this.body(b, needself, line); - fs.storevar(v, b); - fs.fixline(line); /* definition `happens' in the first line */ - } - - - private void exprstat() throws CompileException { - /* stat -> func | assignment */ - FuncState52 fs = this.fs; - LHS_assign v = new LHS_assign(); - this.primaryexp(v.v); - if (v.v.k == VCALL) /* stat -> func */ { - LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ - } else { /* stat -> assignment */ - v.prev = null; - this.assignment(v, 1); - } - } - - private void retstat() throws CompileException { - /* stat -> RETURN explist */ - FuncState52 fs = this.fs; - expdesc e = new expdesc(); - int first, nret; /* registers with returned values */ - this.nextToken(); /* skip RETURN */ - if (block_follow(true) || this.t.token == ';') { - first = nret = 0; /* return no values */ - } else { - nret = this.explist1(e); /* optional return values */ - if (hasmultret(e.k)) { - fs.setmultret(e); - if (e.k == VCALL && nret == 1) { /* tail call? */ - LuaC.SET_OPCODE(fs.getcodePtr(e), Lua52.OP_TAILCALL); - LuaC._assert(Lua52.GETARG_A(fs.getcode(e)) == fs.nactvar, linenumber); - } - first = fs.nactvar; - nret = Lua52.LUA_MULTRET; /* return all values */ - } else { - if (nret == 1) /* only one single value? */ { - first = fs.exp2anyreg(e); - } else { - fs.exp2nextreg(e); /* values must go to the `stack' */ - first = fs.nactvar; /* return all `active' values */ - LuaC._assert(nret == fs.freereg - first, linenumber); - } - } - } - fs.ret(first, nret); - testnext(';'); - } - - - private void statement() throws CompileException { - int line = this.linenumber; /* may be needed for error messages */ - enterlevel(); - if (this.t.token == TK_NAME && this.t.seminfo.ts.equals(LuaString.valueOf("goto"))) { - lookahead(); - if (lookahead.token == TK_NAME) { - this.t.token = TK_GOTO; - } - } - switch (this.t.token) { - case ';': { /* stat -> ';' (empty statement) */ - nextToken(); /* skip ';' */ - break; - } - case TK_IF: { /* stat -> ifstat */ - this.ifstat(line); - break; - } - case TK_WHILE: { /* stat -> whilestat */ - this.whilestat(line); - break; - } - case TK_DO: { /* stat -> DO block END */ - this.nextToken(); /* skip DO */ - this.block(); - this.check_match(TK_END, TK_DO, line); - break; - } - case TK_FOR: { /* stat -> forstat */ - this.forstat(line); - break; - } - case TK_REPEAT: { /* stat -> repeatstat */ - this.repeatstat(line); - break; - } - case TK_FUNCTION: { - this.funcstat(line); /* stat -> funcstat */ - break; - } - case TK_LOCAL: { /* stat -> localstat */ - this.nextToken(); /* skip LOCAL */ - if (this.testnext(TK_FUNCTION)) /* local function? */ { - this.localfunc(); - } else { - this.localstat(); - } - break; - } - case TK_DBCOLON: { /* stat -> label */ - this.nextToken(); - this.labelstat(str_checkname(), line); - break; - } - case TK_RETURN: { /* stat -> retstat */ - this.retstat(); - break; - } - case TK_BREAK: /* stat -> breakstat */ - case TK_GOTO: { /* stat -> 'goto' NAME */ - if (this.t.token == TK_GOTO && LuaC.blockGoto) { - throw lexError("illegal jump statement", this.t.token); - } - this.gotostat(fs.jump()); - break; - } - default: { - this.exprstat(); - break; - } - } - LuaC._assert(fs.f.maxstacksize >= fs.freereg && fs.freereg >= fs.nactvar, linenumber); - fs.freereg = fs.nactvar; /* free registers */ - leavelevel(); - } - - void statlist() throws CompileException { - /* chunk -> { stat [`;'] } */ - while (!block_follow(true)) { - if (this.t.token == TK_RETURN) { - statement(); - return; /* 'return' must be last statement */ - } - this.statement(); - } - } - - /* }====================================================================== */ -} diff --git a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java index 9056b5c5..1a5ede7f 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LoadState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java @@ -74,21 +74,6 @@ public final class LoadState { * @see #load(InputStream, LuaString, LuaString, LuaTable) */ public interface LuaCompiler { - - /** - * Load into a Closure or LuaFunction from a Stream and initializes the environment - * - * @param stream Stream to read - * @param filename Name of chunk - * @param mode - * @param env Environment to load - * @param useLua52 Whether to load as Lua 5.2 - * @return The loaded function - * @throws IOException On stream read error - * @throws CompileException If the stream cannot be loaded. - */ - LuaFunction load(InputStream stream, LuaString filename, LuaString mode, LuaTable env, boolean useLua52) throws IOException, CompileException; - /** * Load into a Closure or LuaFunction from a Stream and initializes the environment * @@ -135,7 +120,7 @@ public static LuaFunction load(LuaState state, InputStream stream, LuaString nam } public static LuaFunction load(LuaState state, InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { - if (state.compiler != null) return state.compiler.load(stream, name, mode, env, state.useLua52); + if (state.compiler != null) return state.compiler.load(stream, name, mode, env); int firstByte = stream.read(); if (firstByte != LUA_SIGNATURE[0]) throw new CompileException("no compiler"); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index 91a16080..8d7cd712 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -43,14 +43,14 @@ /** * Compiler for Lua. * - * Compiles lua source files into lua bytecode within a {@link Prototype}, - * loads lua binary files directly into a{@link Prototype}, + * Compiles Lua source files into Lua bytecode within a {@link Prototype}, + * loads Lua binary files directly into a{@link Prototype}, * and optionaly instantiates a {@link LuaInterpretedFunction} around the result * using a user-supplied environment. * * Implements the {@link LuaCompiler} interface for loading * initialized chunks, which is an interface common to - * lua bytecode compiling and java bytecode compiling. + * Lua bytecode compiling and java bytecode compiling. * * The {@link LuaC} compiler is installed by default by the * {@link JsePlatform} class @@ -87,45 +87,44 @@ protected static void _assert(boolean b, int line) throws CompileException { public static final int MAXSTACK = 250; public static final int LUAI_MAXUPVALUES = 60; public static final int LUAI_MAXVARS = 200; - public static boolean defaultLua52 = true; public static boolean blockGoto = false; public static void SET_OPCODE(InstructionPtr i, int o) { - i.set((i.get() & (Lua.MASK_NOT_OP)) | ((o << Lua.POS_OP) & Lua.MASK_OP)); + i.set((i.get() & (Lua52.MASK_NOT_OP)) | ((o << Lua52.POS_OP) & Lua52.MASK_OP)); } public static void SETARG_A(InstructionPtr i, int u) { - i.set((i.get() & (Lua.MASK_NOT_A)) | ((u << Lua.POS_A) & Lua.MASK_A)); + i.set((i.get() & (Lua52.MASK_NOT_A)) | ((u << Lua52.POS_A) & Lua52.MASK_A)); } public static void SETARG_B(InstructionPtr i, int u) { - i.set((i.get() & (Lua.MASK_NOT_B)) | ((u << Lua.POS_B) & Lua.MASK_B)); + i.set((i.get() & (Lua52.MASK_NOT_B)) | ((u << Lua52.POS_B) & Lua52.MASK_B)); } public static void SETARG_C(InstructionPtr i, int u) { - i.set((i.get() & (Lua.MASK_NOT_C)) | ((u << Lua.POS_C) & Lua.MASK_C)); + i.set((i.get() & (Lua52.MASK_NOT_C)) | ((u << Lua52.POS_C) & Lua52.MASK_C)); } public static void SETARG_Bx(InstructionPtr i, int u) { - i.set((i.get() & (Lua.MASK_NOT_Bx)) | ((u << Lua.POS_Bx) & Lua.MASK_Bx)); + i.set((i.get() & (Lua52.MASK_NOT_Bx)) | ((u << Lua52.POS_Bx) & Lua52.MASK_Bx)); } public static void SETARG_sBx(InstructionPtr i, int u) { - SETARG_Bx(i, u + Lua.MAXARG_sBx); + SETARG_Bx(i, u + Lua52.MAXARG_sBx); } public static int CREATE_ABC(int o, int a, int b, int c) { - return ((o << Lua.POS_OP) & Lua.MASK_OP) | - ((a << Lua.POS_A) & Lua.MASK_A) | - ((b << Lua.POS_B) & Lua.MASK_B) | - ((c << Lua.POS_C) & Lua.MASK_C); + return ((o << Lua52.POS_OP) & Lua52.MASK_OP) | + ((a << Lua52.POS_A) & Lua52.MASK_A) | + ((b << Lua52.POS_B) & Lua52.MASK_B) | + ((c << Lua52.POS_C) & Lua52.MASK_C); } public static int CREATE_ABx(int o, int a, int bc) { - return ((o << Lua.POS_OP) & Lua.MASK_OP) | - ((a << Lua.POS_A) & Lua.MASK_A) | - ((bc << Lua.POS_Bx) & Lua.MASK_Bx); + return ((o << Lua52.POS_OP) & Lua52.MASK_OP) | + ((a << Lua52.POS_A) & Lua52.MASK_A) | + ((bc << Lua52.POS_Bx) & Lua52.MASK_Bx); } // vector reallocation @@ -162,8 +161,8 @@ public static LocalVariable[] realloc(LocalVariable[] v, int n) { return a; } - public static LexState52.LabelDescription[] realloc(LexState52.LabelDescription[] v, int n) { - LexState52.LabelDescription[] a = new LexState52.LabelDescription[n]; + public static LexState.LabelDescription[] realloc(LexState.LabelDescription[] v, int n) { + LexState.LabelDescription[] a = new LexState.LabelDescription[n]; if (v != null) { System.arraycopy(v, 0, a, 0, Math.min(v.length, n)); } @@ -193,23 +192,18 @@ private LuaC() { * Load into a Closure or LuaFunction, with the supplied initial environment */ @Override - public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env, boolean useLua52) throws IOException, CompileException { - Prototype p = compile(stream, name, mode, useLua52); + public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { + Prototype p = compile(stream, name, mode); LuaInterpretedFunction closure = new LuaInterpretedFunction(p, env); closure.nilUpvalues(); - if (useLua52 && p.nups == 1) { + if (p.isLua52 && p.nups == 1) { closure.setUpvalue(0, new Upvalue(env)); - } else if (!useLua52 && p.nups == 0) { + } else if (!p.isLua52 && p.nups == 0) { closure.setfenv(env); } return closure; } - @Override - public LuaFunction load(InputStream stream, LuaString name, LuaString mode, LuaTable env) throws IOException, CompileException { - return load(stream, name, mode, env, defaultLua52); - } - public static Prototype compile(InputStream stream, String name) throws IOException, CompileException { return compile(stream, valueOf(name)); } @@ -228,51 +222,21 @@ public static Prototype compile(InputStream stream, LuaString name) throws IOExc } public static Prototype compile(InputStream stream, LuaString name, LuaString mode) throws IOException, CompileException { - return compile(stream, name, mode, defaultLua52); - } - - public static Prototype compile(InputStream stream, LuaString name, LuaString mode, boolean lua52) throws IOException, CompileException { int firstByte = stream.read(); if (firstByte == '\033') { checkMode(mode, "binary"); return LoadState.loadBinaryChunk(firstByte, stream, name); } else { checkMode(mode, "text"); - if (lua52) { - LexState52.DynamicData dyd = new LexState52.DynamicData(); - return luaY_parser52(firstByte, stream, dyd, name); - } else { - return luaY_parser(firstByte, stream, name); - } + LexState.DynamicData dyd = new LexState.DynamicData(); + return luaY_parser(firstByte, stream, dyd, name); } } - /** - * Parse the input - */ - private static Prototype luaY_parser(int firstByte, InputStream z, LuaString name) throws CompileException { + private static Prototype luaY_parser(int firstByte, InputStream z, LexState.DynamicData dyd, LuaString name) throws CompileException { LexState lexstate = new LexState(z); FuncState funcstate = new FuncState(); - // lexstate.buff = buff; - lexstate.setinput(firstByte, z, name); - lexstate.open_func(funcstate); - /* main func. is always vararg */ - funcstate.f.is_vararg = Lua.VARARG_ISVARARG; - funcstate.f.source = name; - lexstate.nextToken(); /* read first token */ - lexstate.chunk(); - lexstate.check(LexState.TK_EOS); - lexstate.close_func(); - LuaC._assert(funcstate.prev == null); - LuaC._assert(funcstate.f.nups == 0); - LuaC._assert(lexstate.fs == null); - return funcstate.f; - } - - private static Prototype luaY_parser52(int firstByte, InputStream z, LexState52.DynamicData dyd, LuaString name) throws CompileException { - LexState52 lexstate = new LexState52(z); - FuncState52 funcstate = new FuncState52(); - FuncState52.BlockCnt bl = new FuncState52.BlockCnt(); + FuncState.BlockCnt bl = new FuncState.BlockCnt(); // lexstate.buff = buff; lexstate.dyd = dyd; dyd.nactvar = dyd.ngt = dyd.nlabel = 0; @@ -280,14 +244,14 @@ private static Prototype luaY_parser52(int firstByte, InputStream z, LexState52. funcstate.f = new Prototype(); lexstate.open_func(funcstate, bl); /* main func. is always vararg */ - funcstate.f.is_vararg = Lua.VARARG_ISVARARG; + funcstate.f.is_vararg = Lua52.VARARG_ISVARARG; funcstate.f.source = name; - LexState52.expdesc v = new LexState52.expdesc(); - v.init(LexState52.VLOCAL, 0); + LexState.expdesc v = new LexState.expdesc(); + v.init(LexState.VLOCAL, 0); funcstate.newupvalue(LuaString.valueOf("_ENV"), v); lexstate.nextToken(); /* read first token */ lexstate.statlist(); - lexstate.check(LexState52.TK_EOS); + lexstate.check(LexState.TK_EOS); lexstate.close_func(); LuaC._assert(funcstate.prev == null); LuaC._assert(funcstate.f.nups == 1); diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 193e986f..b7e9ff95 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -112,7 +112,7 @@ public LuaValue add(LuaState state, LuaTable env) { next = env.rawget("next"); inext = env.rawget("__inext"); - env.rawset("_VERSION", valueOf(state.useLua52 ? "Lua 5.2" : "Lua 5.1")); + env.rawset("_VERSION", valueOf("Lua 5.2")); return env; } diff --git a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java index af31d385..992eef32 100644 --- a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java +++ b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java @@ -45,8 +45,8 @@ public class CompileTestHelper { */ public static void compareResults(String dir, String file) throws IOException, CompileException { // Compile in memory - // Force Lua 5.1 output since 5.2 tests haven't been written yet - String sourceBytecode = Print.show(LuaC.compile(new ByteArrayInputStream(bytesFromJar(dir + file + ".lua")), LuaString.valueOf("@" + file + ".lua"), null, false)); + // This is going to fail until Lua 5.2 bytecode tests are written + String sourceBytecode = Print.show(LuaC.compile(new ByteArrayInputStream(bytesFromJar(dir + file + ".lua")), LuaString.valueOf("@" + file + ".lua"), null)); // Load expected value from jar Prototype expectedPrototype = loadFromBytes(bytesFromJar(dir + file + ".lc"), file + ".lua"); From 3903aa077d2bc2715aff51e29882114a82013690 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Tue, 6 Apr 2021 00:30:57 -0400 Subject: [PATCH 14/24] Removed environments from threads, fixed a few tests --- build.gradle | 1 + .../java/org/squiddev/cobalt/LuaState.java | 26 +++++++++++-- .../java/org/squiddev/cobalt/LuaThread.java | 25 ++++-------- .../java/org/squiddev/cobalt/LuaValue.java | 6 +++ .../java/org/squiddev/cobalt/Print52.java | 13 ++++++- .../squiddev/cobalt/compiler/LexState.java | 4 +- .../org/squiddev/cobalt/compiler/LuaC.java | 2 +- .../squiddev/cobalt/function/LuaFunction.java | 4 +- .../function/LuaInterpretedFunction.java | 39 ++++++++++++++++--- .../java/org/squiddev/cobalt/lib/BaseLib.java | 30 +++++--------- .../org/squiddev/cobalt/lib/CoroutineLib.java | 6 +-- .../org/squiddev/cobalt/lib/PackageLib.java | 2 +- .../java/org/squiddev/cobalt/lib/Utf8Lib.java | 1 - .../squiddev/cobalt/CoroutineLoopTest.java | 2 +- .../org/squiddev/cobalt/CoroutineTest.java | 2 +- .../squiddev/cobalt/OrphanedThreadTest.java | 4 +- .../squiddev/cobalt/vm/LuaOperationsTest.java | 14 +------ .../org/squiddev/cobalt/vm/MetatableTest.java | 2 +- .../java/org/squiddev/cobalt/vm/TypeTest.java | 2 +- src/test/resources/assert/lua5.1/errors.lua | 4 +- .../resources/compare/errors/baselibargs.lua | 2 +- .../compare/errors/stringlibargs.out | 4 +- src/test/resources/compare/stringlib.out | 2 +- 23 files changed, 115 insertions(+), 82 deletions(-) diff --git a/build.gradle b/build.gradle index ed8a5bec..0364c34e 100644 --- a/build.gradle +++ b/build.gradle @@ -113,4 +113,5 @@ test { testLogging { events "skipped", "failed" } + exclude "org/squiddev/cobalt/compiler/**" // excluded until Lua 5.2 tests are written } diff --git a/src/main/java/org/squiddev/cobalt/LuaState.java b/src/main/java/org/squiddev/cobalt/LuaState.java index b5bcbe2a..031f90af 100644 --- a/src/main/java/org/squiddev/cobalt/LuaState.java +++ b/src/main/java/org/squiddev/cobalt/LuaState.java @@ -134,6 +134,11 @@ public final class LuaState { */ boolean abandoned; + /** + * The global table for the state + */ + public LuaTable globalTable; + public LuaState() { this(new LuaState.Builder()); } @@ -153,6 +158,7 @@ private LuaState(Builder builder) { this.debug = builder.debug; this.timezone = builder.timezone; this.threader = new YieldThreader(builder.coroutineExecutor); + this.globalTable = builder.globalTable; } /** @@ -187,18 +193,19 @@ public LuaThread getCurrentThread() { /** * Setup the main thread * - * @param environment The main thread to use + * @param globals The global table to use * @see #getMainThread() * @see #getCurrentThread() */ - public void setupThread(LuaTable environment) { + public void setupThread(LuaTable globals) { if (mainThread != null && mainThread.isAlive()) { throw new IllegalStateException("State already has main thread"); } - LuaThread thread = new LuaThread(this, environment); + LuaThread thread = new LuaThread(this); mainThread = thread; currentThread = thread; + globalTable = globals; } public static LuaState.Builder builder() { @@ -230,6 +237,7 @@ public static class Builder { private DebugHandler debug = DebugHandler.INSTANCE; private TimeZone timezone = TimeZone.getDefault(); private Executor coroutineExecutor = defaultCoroutineExecutor; + private LuaTable globalTable = new LuaTable(); /** * Build a Lua state from this builder @@ -406,5 +414,17 @@ public Builder coroutineExecutor(Executor coroutineExecutor) { this.coroutineExecutor = coroutineExecutor; return this; } + + /** + * Set the global table for this state. + * + * @param table The new global table + * @return This builder + */ + public Builder globalTable(LuaTable table) { + if (table == null) throw new NullPointerException("table cannot be null"); + this.globalTable = table; + return this; + } } } diff --git a/src/main/java/org/squiddev/cobalt/LuaThread.java b/src/main/java/org/squiddev/cobalt/LuaThread.java index d12bbecc..79aa75c7 100644 --- a/src/main/java/org/squiddev/cobalt/LuaThread.java +++ b/src/main/java/org/squiddev/cobalt/LuaThread.java @@ -107,11 +107,6 @@ public class LuaThread extends LuaValue { */ private final LuaState luaState; - /** - * The environment this thread has. - */ - private LuaTable env; - /** * The function called when handling errors */ @@ -131,14 +126,12 @@ public class LuaThread extends LuaValue { * Constructor for main thread only * * @param state The current lua state - * @param env The thread's environment */ - public LuaThread(LuaState state, LuaTable env) { + public LuaThread(LuaState state) { super(Constants.TTHREAD); this.state = new State(this, STATUS_RUNNING); this.luaState = state; this.debugState = new DebugState(state); - this.env = env; this.function = null; } @@ -147,15 +140,13 @@ public LuaThread(LuaState state, LuaTable env) { * * @param state The current lua state * @param func The function to execute - * @param env The environment to apply to the thread */ - public LuaThread(LuaState state, LuaFunction func, LuaTable env) { + public LuaThread(LuaState state, LuaFunction func) { super(Constants.TTHREAD); if (func == null) throw new IllegalArgumentException("function cannot be null"); this.state = new State(this, STATUS_INITIAL); this.luaState = state; this.debugState = new DebugState(state); - this.env = env; this.function = func; } @@ -174,15 +165,13 @@ public LuaTable getMetatable(LuaState state) { return state.threadMetatable; } - @Override + @Override @Deprecated public LuaTable getfenv() { - return env; + return luaState.globalTable; } - @Override - public void setfenv(LuaTable env) { - this.env = env; - } + @Override @Deprecated + public void setfenv(LuaTable env) { } public String getStatus() { return STATUS_NAMES[state.status]; @@ -462,7 +451,7 @@ public void run() { LuaFunction function = func; func = null; if (function instanceof LuaInterpretedFunction) { - ((LuaInterpretedFunction)function).setUpvalue(((LuaInterpretedFunction) function).p.isLua52 ? 0 : -1, new Upvalue(thread.env)); + ((LuaInterpretedFunction)function).setUpvalue(((LuaInterpretedFunction) function).p.isLua52 ? 0 : -1, new Upvalue(state.globalTable)); } Varargs res = loop(state, state.currentThread, function, threader.unpack()); diff --git a/src/main/java/org/squiddev/cobalt/LuaValue.java b/src/main/java/org/squiddev/cobalt/LuaValue.java index f294bff1..956dd90f 100644 --- a/src/main/java/org/squiddev/cobalt/LuaValue.java +++ b/src/main/java/org/squiddev/cobalt/LuaValue.java @@ -1120,7 +1120,10 @@ public void setMetatable(LuaState state, LuaTable metatable) throws LuaError { * * @return {@link LuaValue} currently set as the instances environent. * @throws LuaError If this has no environment. + * + * @deprecated This will no longer work on anything except Lua functions. Look for the _ENV upvalue instead. */ + @Deprecated public LuaTable getfenv() throws LuaError { throw ErrorFactory.typeError(this, "function or thread"); } @@ -1137,7 +1140,10 @@ public LuaTable getfenv() throws LuaError { * @param env {@link LuaValue} (typically a {@link LuaTable}) containing the environment. * @throws LuaError If this has no environment. * @see JsePlatform + * + * @deprecated This will no longer work on anything except Lua functions. Use load with an environment argument instead. */ + @Deprecated public void setfenv(LuaTable env) throws LuaError { throw ErrorFactory.typeError(this, "function or thread"); } diff --git a/src/main/java/org/squiddev/cobalt/Print52.java b/src/main/java/org/squiddev/cobalt/Print52.java index 122f859a..893388f0 100644 --- a/src/main/java/org/squiddev/cobalt/Print52.java +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -215,7 +215,6 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { } break; case OP_GETTABUP: - case OP_SETTABUP: ps.print(" ; "); if (f.upvalues.length > b) { printValue(ps, f.upvalues[b]); @@ -226,6 +225,18 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { printConstant(ps, f, c); ps.print("]"); break; + case OP_SETTABUP: + ps.print(" ; "); + if (f.upvalues.length > a) { + printValue(ps, f.upvalues[a]); + } else { + ps.print("-"); + } + ps.print("["); + printConstant(ps, f, b); + ps.print("] = "); + printConstant(ps, f, c); + break; case OP_GETTABLE: case OP_SELF: if (ISK(c)) { diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState.java b/src/main/java/org/squiddev/cobalt/compiler/LexState.java index 987a068a..28bf2aa2 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState.java @@ -59,6 +59,8 @@ public class LexState { } } + public static final LuaString ENV = LuaString.valueOf("_ENV"); + private static final int EOZ = -1; private static final int MAXSRC = 80; private static final int MAX_INT = Integer.MAX_VALUE - 2; @@ -992,7 +994,7 @@ private void singlevar(expdesc var) throws CompileException { FuncState fs = this.fs; if (fs.singlevaraux(varname, var, 1) == VVOID) { expdesc key = new expdesc(); - fs.singlevaraux(LuaString.valueOf("_ENV"), var, 1); + fs.singlevaraux(LexState.ENV, var, 1); LuaC._assert(var.k == LexState.VLOCAL || var.k == LexState.VUPVAL, linenumber); codestring(key, varname); fs.indexed(var, key); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index 8d7cd712..a93467bd 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -248,7 +248,7 @@ private static Prototype luaY_parser(int firstByte, InputStream z, LexState.Dyna funcstate.f.source = name; LexState.expdesc v = new LexState.expdesc(); v.init(LexState.VLOCAL, 0); - funcstate.newupvalue(LuaString.valueOf("_ENV"), v); + funcstate.newupvalue(LexState.ENV, v); lexstate.nextToken(); /* read first token */ lexstate.statlist(); lexstate.check(LexState.TK_EOS); diff --git a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java index 4e8cb333..928292a5 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaFunction.java @@ -65,12 +65,12 @@ public LuaTable getMetatable(LuaState state) { return state.functionMetatable; } - @Override + @Override @Deprecated public LuaTable getfenv() { return null; } - @Override + @Override @Deprecated public void setfenv(LuaTable env) { } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index cfd62f41..ff99336a 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -25,6 +25,7 @@ package org.squiddev.cobalt.function; import org.squiddev.cobalt.*; +import org.squiddev.cobalt.compiler.LexState; import org.squiddev.cobalt.compiler.LoadState; import org.squiddev.cobalt.compiler.LuaC; import org.squiddev.cobalt.debug.DebugFrame; @@ -112,8 +113,16 @@ public LuaInterpretedFunction(Prototype p) { public LuaInterpretedFunction(Prototype p, LuaTable env) { super(env); this.p = p; - if (p.isLua52) this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; - else { + if (p.isLua52) { + this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + if (env != null) { + for (int i = 0; i < p.nups; i++) { + if (p.upvalues[i].equals(LuaString.valueOf("_ENV"))) { + this.upvalues[i] = new Upvalue(env); + } + } + } + } else { this.upvalues = new Upvalue[p.nups + 1]; this.upvalues[0] = new Upvalue(env); } @@ -123,7 +132,7 @@ public void nilUpvalues() { int nups = p.nups; if (nups > 0) { Upvalue[] upvalues = this.upvalues; - for (int i = 1; i < nups; i++) { + for (int i = p.isLua52 ? 0 : 1; i < nups; i++) { upvalues[i] = new Upvalue(Constants.NIL); } } @@ -155,10 +164,30 @@ public final Varargs invoke(LuaState state, Varargs varargs) throws LuaError, Un } @Override - public LuaTable getfenv() { return (LuaTable)upvalues[0].getValue(); } + public LuaTable getfenv() { + if (!p.isLua52) return (LuaTable)upvalues[0].getValue(); + else { + for (int i = 0; i < p.nups; i++) { + if (p.upvalues[i].equals(LexState.ENV)) { + return (LuaTable)upvalues[i].getValue(); + } + } + return null; + } + } @Override - public void setfenv(LuaTable env) { upvalues[0].setValue(env); } + public void setfenv(LuaTable env) { + if (env == null) throw new NullPointerException("environment must not be null"); + if (!p.isLua52) upvalues[0].setValue(env); + else { + for (int i = 0; i < p.nups; i++) { + if (p.upvalues[i].equals(LexState.ENV)) { + upvalues[i].setValue(env); + } + } + } + } @Override public Upvalue getUpvalue(int i) { diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index b7e9ff95..037279cc 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -219,27 +219,15 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError, UnwindThrow case 6: // "print", // (...) -> void { return noUnwind(state, () -> { - LuaTable env = state.getCurrentThread().getfenv(); - if (env != null) { - LuaValue tostring = OperationHelper.getTable(state, env, valueOf("tostring")); - for (int i = 1, n = args.count(); i <= n; i++) { - if (i > 1) state.stdout.write('\t'); - LuaString s = OperationHelper.call(state, tostring, args.arg(i)).strvalue(); - int z = s.indexOf((byte) 0, 0); - state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); - } - state.stdout.println(); - return Constants.NONE; - } else { - for (int i = 1, n = args.count(); i <= n; i++) { - if (i > 1) state.stdout.write('\t'); - LuaString s = OperationHelper.toString(state, args.arg(i)).strvalue(); - int z = s.indexOf((byte) 0, 0); - state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); - } - state.stdout.println(); - return Constants.NONE; + LuaValue tostring = OperationHelper.getTable(state, state.globalTable, valueOf("tostring")); + for (int i = 1, n = args.count(); i <= n; i++) { + if (i > 1) state.stdout.write('\t'); + LuaString s = OperationHelper.call(state, tostring, args.arg(i)).strvalue(); + int z = s.indexOf((byte) 0, 0); + state.stdout.write(s.bytes, s.offset, z >= 0 ? z : s.length); } + state.stdout.println(); + return Constants.NONE; }); } case 7: // "select", // (f, ...) -> value1, ... @@ -511,7 +499,7 @@ private static Varargs loadStream(LuaState state, InputStream is, LuaString chun if (is == null) { return varargsOf(Constants.NIL, valueOf("not found: " + chunkname)); } - return LoadState.load(state, is, chunkname, state.getCurrentThread().getfenv()); + return LoadState.load(state, is, chunkname, state.globalTable); } catch (Exception e) { return varargsOf(Constants.NIL, LuaError.getMessage(e)); } diff --git a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java index 8f7b28c2..2f469615 100644 --- a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java @@ -84,7 +84,7 @@ public Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws LuaErr switch (opcode) { case CREATE: { final LuaFunction func = args.arg(1).checkFunction(); - return new LuaThread(state, func, state.getCurrentThread().getfenv()); + return new LuaThread(state, func); } case RESUME: { di.flags |= FLAG_YPCALL; @@ -108,10 +108,8 @@ public Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws LuaErr return LuaThread.yield(state, args); case WRAP: { final LuaFunction func = args.arg(1).checkFunction(); - final LuaTable env = func.getfenv(); - final LuaThread thread = new LuaThread(state, func, env); + final LuaThread thread = new LuaThread(state, func); CoroutineLib cl = new CoroutineLib(thread); - cl.setfenv(env); cl.name = "wrapped"; cl.opcode = WRAPPED; return cl; diff --git a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java index 4cef0854..173f31fe 100644 --- a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java @@ -207,7 +207,7 @@ private Varargs module(LuaState state, Varargs args) throws LuaError, UnwindThro LuaTable module; if (!value.isTable()) { /* not found? */ /* try global variable (and create one if it does not exist) */ - LuaTable globals = state.getCurrentThread().getfenv(); + LuaTable globals = state.globalTable; module = findtable(state, globals, modname); if (module == null) { throw new LuaError("name conflict for module '" + modname + "'"); diff --git a/src/main/java/org/squiddev/cobalt/lib/Utf8Lib.java b/src/main/java/org/squiddev/cobalt/lib/Utf8Lib.java index 872a2c1c..4c60b9fc 100644 --- a/src/main/java/org/squiddev/cobalt/lib/Utf8Lib.java +++ b/src/main/java/org/squiddev/cobalt/lib/Utf8Lib.java @@ -36,7 +36,6 @@ public LuaValue add(LuaState state, LuaTable environment) { state.loadedPackages.rawset("utf8", t); codesIter = new Utf8CodesIter(); - codesIter.setfenv(environment); return t; } diff --git a/src/test/java/org/squiddev/cobalt/CoroutineLoopTest.java b/src/test/java/org/squiddev/cobalt/CoroutineLoopTest.java index 7195aff4..30f96398 100644 --- a/src/test/java/org/squiddev/cobalt/CoroutineLoopTest.java +++ b/src/test/java/org/squiddev/cobalt/CoroutineLoopTest.java @@ -50,7 +50,7 @@ public Varargs invoke(LuaState state, Varargs args) throws LuaError { } }); - LuaThread thread = new LuaThread(helpers.state, helpers.loadScript(name), helpers.globals); + LuaThread thread = new LuaThread(helpers.state, helpers.loadScript(name)); int i = 0; while (!thread.getStatus().equals("dead")) { diff --git a/src/test/java/org/squiddev/cobalt/CoroutineTest.java b/src/test/java/org/squiddev/cobalt/CoroutineTest.java index a736c2b4..6c2f8593 100644 --- a/src/test/java/org/squiddev/cobalt/CoroutineTest.java +++ b/src/test/java/org/squiddev/cobalt/CoroutineTest.java @@ -141,7 +141,7 @@ public Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws LuaErr LuaThread.suspend(state); return Constants.NONE; case 1: { // run - LuaThread thread = new LuaThread(state, args.first().checkFunction(), getfenv()); + LuaThread thread = new LuaThread(state, args.first().checkFunction()); di.state = thread; Varargs value = Constants.NONE; while (thread.isAlive()) value = LuaThread.resume(state, thread, value); diff --git a/src/test/java/org/squiddev/cobalt/OrphanedThreadTest.java b/src/test/java/org/squiddev/cobalt/OrphanedThreadTest.java index 2ff5d935..d486ce88 100644 --- a/src/test/java/org/squiddev/cobalt/OrphanedThreadTest.java +++ b/src/test/java/org/squiddev/cobalt/OrphanedThreadTest.java @@ -181,7 +181,7 @@ public void testAbandon() throws Exception { function = LoadState.load(state, new ByteArrayInputStream(script.getBytes()), "script", env); - LuaThread thread = new LuaThread(state, function, env); + LuaThread thread = new LuaThread(state, function); LuaThread.run(thread, NONE); @@ -195,7 +195,7 @@ public void testAbandon() throws Exception { } private void doTest(boolean secondOk, LuaValue secondValue) throws Exception { - LuaThread thread = new LuaThread(state, function, env); + LuaThread thread = new LuaThread(state, function); WeakReference luaThreadRef = new WeakReference<>(thread); WeakReference luaFuncRef = new WeakReference<>(function); assertNotNull(luaThreadRef.get()); diff --git a/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java b/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java index 6091c683..5ed43562 100644 --- a/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java +++ b/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java @@ -259,27 +259,17 @@ public void testFunctionClosureThreadEnv() throws LuaError, UnwindThrowable, Int // those closures created not in any other function get the thread's enviroment Prototype p2 = createPrototype("return loadstring('return a')", "threadtester"); { - LuaThread t = new LuaThread(state, new LuaInterpretedFunction(p2, _G), _G); + LuaThread t = new LuaThread(state, new LuaInterpretedFunction(p2, _G)); Varargs v = LuaThread.run(t, Constants.NONE); LuaValue f = v.arg(1); assertEquals(Constants.TFUNCTION, f.type()); assertEquals(aaa, OperationHelper.call(state, f)); assertEquals(_G, f.getfenv()); } - { - // change the thread environment after creation! - LuaThread t = new LuaThread(state, new LuaInterpretedFunction(p2, _G), _G); - t.setfenv(newenv); - Varargs v = LuaThread.run(t, Constants.NONE); - LuaValue f = v.arg(1); - assertEquals(Constants.TFUNCTION, f.type()); - assertEquals(eee, OperationHelper.call(state, f)); - assertEquals(newenv, f.getfenv()); - } { // let the closure have a different environment from the thread Prototype p3 = createPrototype("return function() return a end", "envtester"); - LuaThread t = new LuaThread(state, new LuaInterpretedFunction(p3, newenv), _G); + LuaThread t = new LuaThread(state, new LuaInterpretedFunction(p3, newenv)); Varargs v = LuaThread.run(t, Constants.NONE); LuaValue f = v.arg(1); assertEquals(Constants.TFUNCTION, f.type()); diff --git a/src/test/java/org/squiddev/cobalt/vm/MetatableTest.java b/src/test/java/org/squiddev/cobalt/vm/MetatableTest.java index f1446f79..77dd8059 100644 --- a/src/test/java/org/squiddev/cobalt/vm/MetatableTest.java +++ b/src/test/java/org/squiddev/cobalt/vm/MetatableTest.java @@ -49,7 +49,7 @@ public LuaValue call(LuaState state) { } }; private final LuaState state = new LuaState(); - private final LuaThread thread = new LuaThread(state, function, table); + private final LuaThread thread = new LuaThread(state, function); private final LuaClosure closure = new LuaInterpretedFunction(new Prototype()); private final LuaUserdata userdata = ValueFactory.userdataOf(sampleobject); private final LuaUserdata userdatamt = ValueFactory.userdataOf(sampledata, table); diff --git a/src/test/java/org/squiddev/cobalt/vm/TypeTest.java b/src/test/java/org/squiddev/cobalt/vm/TypeTest.java index 310a7b03..7139cd48 100644 --- a/src/test/java/org/squiddev/cobalt/vm/TypeTest.java +++ b/src/test/java/org/squiddev/cobalt/vm/TypeTest.java @@ -67,7 +67,7 @@ public LuaValue call(LuaState state) { return NONE; } }; - private final LuaThread thread = new LuaThread(new LuaState(), somefunc, table); + private final LuaThread thread = new LuaThread(new LuaState(), somefunc); private final LuaClosure someclosure = new LuaInterpretedFunction(new Prototype()); private final LuaUserdata userdataobj = userdataOf(sampleobject); private final LuaUserdata userdatacls = userdataOf(sampledata); diff --git a/src/test/resources/assert/lua5.1/errors.lua b/src/test/resources/assert/lua5.1/errors.lua index aaee6a05..6d656383 100644 --- a/src/test/resources/assert/lua5.1/errors.lua +++ b/src/test/resources/assert/lua5.1/errors.lua @@ -37,8 +37,8 @@ assert(not doit("tostring(1)") and doit("tostring()")) assert(doit "tonumber()") assert(doit "repeat until 1; a") --checksyntax("break label", "", "label", 1) -assert(doit ";") -assert(doit "a=1;;") +--assert(doit ";") +--assert(doit "a=1;;") assert(doit "return;;") assert(doit "assert(false)") assert(doit "assert(nil)") diff --git a/src/test/resources/compare/errors/baselibargs.lua b/src/test/resources/compare/errors/baselibargs.lua index ac4ce4d0..a7bdcd1b 100644 --- a/src/test/resources/compare/errors/baselibargs.lua +++ b/src/test/resources/compare/errors/baselibargs.lua @@ -30,7 +30,7 @@ checkallerrors('error', { { 123 }, { nil, 1, 2, n = 3 } }, 123) -- getfenv banner('getfenv') --checkallpass('getfenv', { { nil, print, function() end, 0, 1, 2, n = 4 } }) -checkallpass('getfenv', { { function() end, 0 } }) -- Java functions no longer have environments (including pcall, which was position 1) +checkallpass('getfenv', { { function() return _ENV end, 0 } }) -- Java functions no longer have environments (including pcall, which was position 1) checkallerrors('getfenv', { { true, {}, 'abc' } }, 'bad argument') -- getmetatable diff --git a/src/test/resources/compare/errors/stringlibargs.out b/src/test/resources/compare/errors/stringlibargs.out index 4eba8d29..609b7c79 100644 --- a/src/test/resources/compare/errors/stringlibargs.out +++ b/src/test/resources/compare/errors/stringlibargs.out @@ -107,9 +107,9 @@ needcheck string_char(3) 3 - string_char(6.7,) ...bad argument... ====== string.dump ====== --- checkallpass -- string.dump() 'LuaQ +- string.dump() 'LuaR --- checkallpass -- string.dump() 'LuaQ +- string.dump() 'LuaR --- checkallerrors - string.dump(nil) ...bad argument... - string.dump('abc') ...bad argument... diff --git a/src/test/resources/compare/stringlib.out b/src/test/resources/compare/stringlib.out index dc89417a..077a7a76 100644 --- a/src/test/resources/compare/stringlib.out +++ b/src/test/resources/compare/stringlib.out @@ -320,7 +320,7 @@ char good \<=] char empty char table false-string char nil false-string -dump good LuaQ +dump good LuaR dump empty false-string dump table false-string dump nil false-string From 52b7a3b5fe3fdf0999fb80fd0e7b7a358d756f41 Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Tue, 6 Apr 2021 02:30:43 -0400 Subject: [PATCH 15/24] Fixed some more tests --- src/main/java/org/squiddev/cobalt/ErrorFactory.java | 4 ++-- src/main/java/org/squiddev/cobalt/Print52.java | 9 ++++++--- .../squiddev/cobalt/function/LuaInterpretedFunction.java | 4 ++-- src/main/java/org/squiddev/cobalt/lib/PackageLib.java | 2 +- src/test/resources/assert/debug.lua | 2 +- src/test/resources/assert/lua5.1/attrib.lua | 3 ++- src/test/resources/compare/debuglib.out | 8 ++++---- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/ErrorFactory.java b/src/main/java/org/squiddev/cobalt/ErrorFactory.java index fa92e433..ba82e6de 100644 --- a/src/main/java/org/squiddev/cobalt/ErrorFactory.java +++ b/src/main/java/org/squiddev/cobalt/ErrorFactory.java @@ -108,8 +108,8 @@ public static LuaError operandError(LuaState state, LuaValue operand, String ver if (info != null && info.closure != null) { if (stack < info.closure.getPrototype().maxstacksize) { kind = info.closure.getPrototype().isLua52 ? DebugHelpers52.getObjectName(info, stack) : DebugHelpers.getObjectName(info, stack); - } - } + } + } } if (kind != null) { diff --git a/src/main/java/org/squiddev/cobalt/Print52.java b/src/main/java/org/squiddev/cobalt/Print52.java index 893388f0..bf224280 100644 --- a/src/main/java/org/squiddev/cobalt/Print52.java +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -222,7 +222,8 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { ps.print("-"); } ps.print("["); - printConstant(ps, f, c); + if (c > 0xff) printConstant(ps, f, c & 0x0ff); + else ps.print("-"); ps.print("]"); break; case OP_SETTABUP: @@ -233,9 +234,11 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { ps.print("-"); } ps.print("["); - printConstant(ps, f, b); + if (b > 0xff) printConstant(ps, f, b & 0x0ff); + else ps.print("-"); ps.print("] = "); - printConstant(ps, f, c); + if (c > 0xff) printConstant(ps, f, c & 0x0ff); + else ps.print("-"); break; case OP_GETTABLE: case OP_SELF: diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index ff99336a..63447908 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -179,11 +179,11 @@ public LuaTable getfenv() { @Override public void setfenv(LuaTable env) { if (env == null) throw new NullPointerException("environment must not be null"); - if (!p.isLua52) upvalues[0].setValue(env); + if (!p.isLua52) upvalues[0] = new Upvalue(env); else { for (int i = 0; i < p.nups; i++) { if (p.upvalues[i].equals(LexState.ENV)) { - upvalues[i].setValue(env); + upvalues[i] = new Upvalue(env); } } } diff --git a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java index 173f31fe..c97829ea 100644 --- a/src/main/java/org/squiddev/cobalt/lib/PackageLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/PackageLib.java @@ -113,7 +113,7 @@ public LuaValue call(LuaState state, LuaValue arg) throws LuaError { t.setMetatable(state, m = ValueFactory.tableOf()); } LuaTable mt = m; - noUnwind(state, () -> OperationHelper.setTable(state, mt, Constants.INDEX, state.getCurrentThread().getfenv())); + noUnwind(state, () -> OperationHelper.setTable(state, mt, Constants.INDEX, state.globalTable)); return Constants.NONE; } } diff --git a/src/test/resources/assert/debug.lua b/src/test/resources/assert/debug.lua index 380ae0e4..ea5ef46a 100644 --- a/src/test/resources/assert/debug.lua +++ b/src/test/resources/assert/debug.lua @@ -79,7 +79,7 @@ local function foo() assertEquals(getfenv(1), getfenv(foo)) assertEquals(getfenv(2), _G) end -setfenv(foo, { getfenv = getfenv, print = print, _G = _G }) +setfenv(foo, { getfenv = getfenv, print = print, _G = _G, tostring = tostring, pcall = pcall, error = error, assert = assert }) foo() local function tracebackVerbose(x) diff --git a/src/test/resources/assert/lua5.1/attrib.lua b/src/test/resources/assert/lua5.1/attrib.lua index b14d6866..edf3b0de 100644 --- a/src/test/resources/assert/lua5.1/attrib.lua +++ b/src/test/resources/assert/lua5.1/attrib.lua @@ -142,6 +142,7 @@ end local function import(...) local f = {...} + local _G = _G return function (m) for i=1, #f do m[f[i]] = _G[f[i]] end end @@ -203,7 +204,7 @@ else module("lib1.sub", package.seeall) assert(_M == fs) setfenv(1, _G) - + end f, err, when = package.loadlib("donotexist", p.."xuxu") assert(not f and type(err) == "string" and (when == "open" or when == "absent")) diff --git a/src/test/resources/compare/debuglib.out b/src/test/resources/compare/debuglib.out index f9f44ceb..0a1f3be3 100644 --- a/src/test/resources/compare/debuglib.out +++ b/src/test/resources/compare/debuglib.out @@ -83,7 +83,7 @@ debug.getinfo(1) currentline: 162 linedefined: 159 lastlinedefined: 179 - nups: 4 + nups: 5 func: function debug.getinfo(1,"") debug.getinfo(1,"l") @@ -98,7 +98,7 @@ debug.getinfo(2) currentline: 182 linedefined: 152 lastlinedefined: 184 - nups: 5 + nups: 6 func: function debug.getinfo(2,"l") currentline: 182 @@ -162,7 +162,7 @@ true currentline: -1 linedefined: 159 lastlinedefined: 179 - nups: 4 + nups: 5 func: function debug.getinfo(test) true @@ -172,6 +172,6 @@ true currentline: -1 linedefined: 152 lastlinedefined: 184 - nups: 5 + nups: 6 func: function ----- debug.sethook, debug.gethook From c50253831c02b5d9c116b63305fa87a8b39dd62e Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Tue, 6 Apr 2021 23:37:14 -0400 Subject: [PATCH 16/24] Fixed all tests --- .../java/org/squiddev/cobalt/Print52.java | 6 +- .../squiddev/cobalt/compiler/DumpState.java | 2 +- .../function/LuaInterpretedFunction.java | 8 +-- .../cobalt/function/LuaInterpreter.java | 58 ++++++++----------- src/test/resources/assert/lua5.1/closure.lua | 9 +-- src/test/resources/assert/lua5.1/db.lua | 11 ++-- src/test/resources/assert/lua5.1/locals.lua | 9 +-- src/test/resources/assert/lua5.3/db.lua | 2 +- 8 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/Print52.java b/src/main/java/org/squiddev/cobalt/Print52.java index bf224280..473eb215 100644 --- a/src/main/java/org/squiddev/cobalt/Print52.java +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -189,11 +189,7 @@ public static void printOpcode(PrintStream ps, Prototype f, int pc) { } break; case iAsBx: - if (o == OP_JMP) { - ps.print(sbx); - } else { - ps.print(a + " " + sbx); - } + ps.print(a + " " + sbx); break; } switch (o) { diff --git a/src/main/java/org/squiddev/cobalt/compiler/DumpState.java b/src/main/java/org/squiddev/cobalt/compiler/DumpState.java index b3c77deb..7020fa4e 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/DumpState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/DumpState.java @@ -250,7 +250,7 @@ void dumpFunction(final Prototype f, final LuaString string) throws IOException dumpChar(f.upvalue_info[i] >> 8); dumpChar(f.upvalue_info[i] & 0xFF); } - if (f.source == null || f.source.equals(string) || strip) { + if (f.source == null || strip) { dumpInt(0); } else { dumpString(f.source); diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index 63447908..519889f6 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java @@ -116,7 +116,7 @@ public LuaInterpretedFunction(Prototype p, LuaTable env) { if (p.isLua52) { this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; if (env != null) { - for (int i = 0; i < p.nups; i++) { + for (int i = 0; i < p.nups && i < p.upvalues.length; i++) { if (p.upvalues[i].equals(LuaString.valueOf("_ENV"))) { this.upvalues[i] = new Upvalue(env); } @@ -167,12 +167,12 @@ public final Varargs invoke(LuaState state, Varargs varargs) throws LuaError, Un public LuaTable getfenv() { if (!p.isLua52) return (LuaTable)upvalues[0].getValue(); else { - for (int i = 0; i < p.nups; i++) { + for (int i = 0; i < p.nups && i < p.upvalues.length; i++) { if (p.upvalues[i].equals(LexState.ENV)) { return (LuaTable)upvalues[i].getValue(); } } - return null; + return null; // TODO: maybe this should return _G (state.globalTable) instead? } } @@ -181,7 +181,7 @@ public void setfenv(LuaTable env) { if (env == null) throw new NullPointerException("environment must not be null"); if (!p.isLua52) upvalues[0] = new Upvalue(env); else { - for (int i = 0; i < p.nups; i++) { + for (int i = 0; i < p.nups && i < p.upvalues.length; i++) { if (p.upvalues[i].equals(LexState.ENV)) { upvalues[i] = new Upvalue(env); } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 4ec9a60c..48246841 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -149,6 +149,19 @@ private static DebugFrame setupCall(LuaState state, LuaInterpretedFunction funct return di; } + private static int jumpStatement(Prototype p, Upvalue[] openups, int pc, int a, int b) { + if (p.isLua52 && a > 0) { + for (int x = openups.length; --x >= a - 1;) { + Upvalue upvalue = openups[x]; + if (upvalue != null) { + upvalue.close(); + openups[x] = null; + } + } + } + return pc + b; + } + static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFunction function) throws LuaError, UnwindThrowable { final DebugState ds = DebugHandler.getDebugState(state); final DebugHandler handler = state.debug; @@ -340,16 +353,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti } case OP_JMP: // A sBx: pc+=sBx; if (A) close all upvalues >= R(A - 1) - if (p.isLua52 && a > 0) { - for (int x = openups.length; --x >= a - 1; ) { - Upvalue upvalue = openups[x]; - if (upvalue != null) { - upvalue.close(); - openups[x] = null; - } - } - } - pc += ((i >>> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, a, GETARG_sBx(i)); break; case OP_EQ: { // A B C: if ((RK(B) == RK(C)) ~= A) then pc++ @@ -357,7 +361,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti int c = (i >> POS_C) & MAXARG_C; if (OperationHelper.eq(state, b > 0xff ? k[b & 0x0ff] : stack[b], c > 0xff ? k[c & 0x0ff] : stack[c]) == (a != 0)) { // We assume the next instruction is a jump and read the branch from there. - pc += ((code[pc] >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, GETARG_A(code[pc]), GETARG_sBx(code[pc])); } pc++; break; @@ -367,7 +371,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti int b = (i >>> POS_B) & MAXARG_B; int c = (i >> POS_C) & MAXARG_C; if (OperationHelper.lt(state, b > 0xff ? k[b & 0x0ff] : stack[b], c > 0xff ? k[c & 0x0ff] : stack[c]) == (a != 0)) { - pc += ((code[pc] >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, GETARG_A(code[pc]), GETARG_sBx(code[pc])); } pc++; break; @@ -377,7 +381,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti int b = (i >>> POS_B) & MAXARG_B; int c = (i >> POS_C) & MAXARG_C; if (OperationHelper.le(state, b > 0xff ? k[b & 0x0ff] : stack[b], c > 0xff ? k[c & 0x0ff] : stack[c]) == (a != 0)) { - pc += ((code[pc] >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, GETARG_A(code[pc]), GETARG_sBx(code[pc])); } pc++; break; @@ -385,7 +389,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti case OP_TEST: // A C: if not (R(A) <=> C) then pc++ if (stack[a].toBoolean() == (((i >> POS_C) & MAXARG_C) != 0)) { - pc += ((code[pc] >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, GETARG_A(code[pc]), GETARG_sBx(code[pc])); } pc++; break; @@ -397,7 +401,7 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti LuaValue val = stack[b]; if (val.toBoolean() == (c != 0)) { stack[a] = val; - pc += ((code[pc] >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + pc = jumpStatement(p, openups, pc, GETARG_A(code[pc]), GETARG_sBx(code[pc])); } pc++; break; @@ -694,19 +698,9 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), LuaInterpretedFunction newcl = new LuaInterpretedFunction(newp, newp.isLua52 ? null : (LuaTable)upvalues[0].getValue()); if (newp.isLua52) { for (int j = 0; j < newp.nups; ++j) { - int b = newp.upvalue_info[j]; if ((b >> 8) != 0) { - /*DebugFrame frame = di; - for (int s = (b >> 8) - 1; s > 0 && frame != null; --s) frame = frame.previous; - if (frame == null) { - throw new IllegalStateException("Upvalue stack index out of range"); - }*/ - //System.out.println("Found upvalue index " + (newp.upvalue_info[j] & 0xFF) + " (named " + (frame.func instanceof LuaInterpretedFunction ? frame.closure.getPrototype().upvalues[newp.upvalue_info[j] & 0xFF] : "?") + ") with type " + frame.closure.getUpvalue(newp.upvalue_info[j] & 0xFF).getClass().getName()); - //newcl.upvalues[j] = new Upvalue(frame.stack, b & 0xFF); - // TODO: Make sure this is actually correct - newcl.upvalues[j] = openups[b & 0xFF] != null ? openups[b & 0xFF] : (openups[b & 0xFF] = new Upvalue(di.stack, b & 0xFF)); - //newcl.upvalues[j] = openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); + newcl.upvalues[j] = openups[b & 0xFF] != null ? openups[b & 0xFF] : (openups[b & 0xFF] = new Upvalue(stack, b & 0xFF)); } else { newcl.upvalues[j] = upvalues[b & 0xFF]; } @@ -718,7 +712,6 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), newcl.upvalues[j] = (i & 4) != 0 ? upvalues[b+1] // OP_GETUPVAL : openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); // OP_MOVE - //System.out.println("Added upvalue " + j + " as " + ((i & 4) != 0 ? di.closure.getPrototype().upvalues[b] : "?")); } } stack[a] = newcl; @@ -832,14 +825,9 @@ public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction case OP_TFORCALL: { int a = (i >>> POS_A) & MAXARG_A; - int c = (i >>> POS_C) & MAXARG_C; - if (c > 0) { - LuaValue[] stack = di.stack; - while (--c > 0) stack[a + 3 + c] = varargs.arg(c); - di.extras = NONE; - } else { - di.top = a + 3 + varargs.count(); - di.extras = varargs; + di.extras = varargs; + for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { + di.stack[a + 2 + c] = varargs.arg(c); } break; } diff --git a/src/test/resources/assert/lua5.1/closure.lua b/src/test/resources/assert/lua5.1/closure.lua index 94f72ae4..b56b49c1 100644 --- a/src/test/resources/assert/lua5.1/closure.lua +++ b/src/test/resources/assert/lua5.1/closure.lua @@ -160,7 +160,7 @@ assert(coroutine.running() == nil) -- tests for global environment - +--[[ local function foo (a) setfenv(0, a) coroutine.yield(getfenv()) @@ -175,7 +175,7 @@ local a = {} assert(f(a) == _G) local a,b = pcall(f) assert(a and b == _G) - +]] -- tests for multiple yield/resume arguments @@ -367,7 +367,7 @@ if not T then else local turn - + function fact (t, x) assert(turn == t) if x == 0 then return 1 @@ -407,6 +407,7 @@ _X() -- coroutine environments +--[[ co = coroutine.create(function () coroutine.yield(getfenv(0)) return loadstring("return a")() @@ -416,7 +417,7 @@ a = {a = 15} debug.setfenv(co, a) assert(debug.getfenv(co) == a) assert(select(2, coroutine.resume(co)) == a) -assert(select(2, coroutine.resume(co)) == a.a) +assert(select(2, coroutine.resume(co)) == a.a)]] print'OK' diff --git a/src/test/resources/assert/lua5.1/db.lua b/src/test/resources/assert/lua5.1/db.lua index a9252684..3466fa20 100644 --- a/src/test/resources/assert/lua5.1/db.lua +++ b/src/test/resources/assert/lua5.1/db.lua @@ -106,7 +106,7 @@ then else a=2 end -]], { 2, 4, 7 }) +]], { 2, 3, 4, 7 }) test([[-- if nil then @@ -140,7 +140,7 @@ test([[while math.sin(1) do break end end -a=1]], { 1, 2, 4, 7 }) +a=1]], { 1, 2, 3, 7 }) test([[for i=1,3 do a=i @@ -204,8 +204,11 @@ function f(a, b) assert(debug.setlocal(2, 3, "pera") == "AA" .. "AA") assert(debug.setlocal(2, 4, "ma��") == "B") x = debug.getinfo(2) - assert(x.func == g and x.what == "Lua" and x.name == 'g' and - x.nups == 0 and string.find(x.source, "^@.*db%.lua")) + assert(x.func == g) + assert(x.what == "Lua") + assert(x.name == 'g') + assert(x.nups == 1) + assert(string.find(x.source, "^@.*db%.lua")) glob = glob + 1 assert(debug.getinfo(1, "l").currentline == L + 1) assert(debug.getinfo(1, "l").currentline == L + 2) diff --git a/src/test/resources/assert/lua5.1/locals.lua b/src/test/resources/assert/lua5.1/locals.lua index b0fc556f..7225b0f5 100644 --- a/src/test/resources/assert/lua5.1/locals.lua +++ b/src/test/resources/assert/lua5.1/locals.lua @@ -65,23 +65,24 @@ do assert(b.A == 11) -- `real' global local g local function f () assert(setfenv(2, {a='10'}) == g) end - g = function () f(); _G.assert(_G.getfenv(1).a == '10') end + g = function () f(); _G.assert(_G.getfenv(1).a == '10'); return _ENV end g(); assert(getfenv(g).a == '10') end -- test for global table of loaded chunks +--[[ local function foo (s) return loadstring(s) end assert(getfenv(foo("")) == _G) -local a = {loadstring = loadstring} +local a = {loadstring = loadstring} setfenv(foo, a) assert(getfenv(foo("")) == _G) setfenv(0, a) -- change global environment assert(getfenv(foo("")) == a) setfenv(0, _G) - +]] -- testing limits for special instructions @@ -109,7 +110,7 @@ print'+' if rawget(_G, "querytab") then -- testing clearing of dead elements from tables collectgarbage("stop") -- stop GC - local a = {[{}] = 4, [3] = 0, alo = 1, + local a = {[{}] = 4, [3] = 0, alo = 1, a1234567890123456789012345678901234567890 = 10} local t = querytab(a) diff --git a/src/test/resources/assert/lua5.3/db.lua b/src/test/resources/assert/lua5.3/db.lua index 2a695b34..8554177d 100644 --- a/src/test/resources/assert/lua5.3/db.lua +++ b/src/test/resources/assert/lua5.3/db.lua @@ -830,7 +830,7 @@ do -- tests for 'source' in binary dumps local p = assert(load(prog, name)) -- load 'p' as a binary chunk with debug information local c = string.dump(p) - assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump + --assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump (this is only true for every version except Lua 5.2! 5.2 repeats the source, but not 5.1 or 5.3+.) local f = assert(load(c)) local g = f() local h = g(3) From 66a1274fa0d55f9fcf04d98c823d7984b4c1566c Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Wed, 7 Apr 2021 03:19:50 -0400 Subject: [PATCH 17/24] Re-added bytecode tests I had to delete any lines that used varargs, as Cobalt provides Lua 5.0-style arg tables, which does not exist in any form under Lua 5.2. Maybe this should be refactored/ adjusted so vararg compilation can be properly tested? --- build.gradle | 1 - .../squiddev/cobalt/compiler/FuncState.java | 96 ++- .../squiddev/cobalt/compiler/LexState.java | 53 +- .../cobalt/compiler/CompileTestHelper.java | 2 +- .../cobalt/compiler/CompilerUnitTests.java | 8 +- src/test/resources/assert/lua5.1/db.lua | 2 +- src/test/resources/assert/lua5.1/errors.lua | 2 +- .../bytecode-compiler/lua5.2/all.lua | 137 ++++ .../bytecode-compiler/lua5.2/all.luac | Bin 0 -> 5690 bytes .../bytecode-compiler/lua5.2/api.lua | 711 ++++++++++++++++++ .../bytecode-compiler/lua5.2/api.luac | Bin 0 -> 36216 bytes .../bytecode-compiler/lua5.2/attrib.lua | 339 +++++++++ .../bytecode-compiler/lua5.2/attrib.luac | Bin 0 -> 17324 bytes .../bytecode-compiler/lua5.2/big.lua | 381 ++++++++++ .../bytecode-compiler/lua5.2/big.luac | Bin 0 -> 16607 bytes .../bytecode-compiler/lua5.2/calls.lua | 294 ++++++++ .../bytecode-compiler/lua5.2/calls.luac | Bin 0 -> 16237 bytes .../bytecode-compiler/lua5.2/checktable.lua | 77 ++ .../bytecode-compiler/lua5.2/checktable.luac | Bin 0 -> 3750 bytes .../bytecode-compiler/lua5.2/closure.lua | 422 +++++++++++ .../bytecode-compiler/lua5.2/closure.luac | Bin 0 -> 20774 bytes .../bytecode-compiler/lua5.2/code.lua | 143 ++++ .../bytecode-compiler/lua5.2/code.luac | Bin 0 -> 5952 bytes .../bytecode-compiler/lua5.2/constructs.lua | 240 ++++++ .../bytecode-compiler/lua5.2/constructs.luac | Bin 0 -> 13414 bytes .../resources/bytecode-compiler/lua5.2/db.lua | 499 ++++++++++++ .../bytecode-compiler/lua5.2/db.luac | Bin 0 -> 25376 bytes .../bytecode-compiler/lua5.2/errors.lua | 250 ++++++ .../bytecode-compiler/lua5.2/errors.luac | Bin 0 -> 11713 bytes .../bytecode-compiler/lua5.2/events.lua | 360 +++++++++ .../bytecode-compiler/lua5.2/events.luac | Bin 0 -> 21321 bytes .../bytecode-compiler/lua5.2/files.lua | 324 ++++++++ .../bytecode-compiler/lua5.2/files.luac | Bin 0 -> 18040 bytes .../resources/bytecode-compiler/lua5.2/gc.lua | 312 ++++++++ .../bytecode-compiler/lua5.2/gc.luac | Bin 0 -> 14596 bytes .../bytecode-compiler/lua5.2/literals.lua | 176 +++++ .../bytecode-compiler/lua5.2/literals.luac | Bin 0 -> 9185 bytes .../bytecode-compiler/lua5.2/locals.lua | 127 ++++ .../bytecode-compiler/lua5.2/locals.luac | Bin 0 -> 5885 bytes .../bytecode-compiler/lua5.2/main.lua | 160 ++++ .../bytecode-compiler/lua5.2/main.luac | Bin 0 -> 4957 bytes .../bytecode-compiler/lua5.2/math.lua | 208 +++++ .../bytecode-compiler/lua5.2/math.luac | Bin 0 -> 11819 bytes .../bytecode-compiler/lua5.2/nextvar.lua | 396 ++++++++++ .../bytecode-compiler/lua5.2/nextvar.luac | Bin 0 -> 22086 bytes .../resources/bytecode-compiler/lua5.2/pm.lua | 273 +++++++ .../bytecode-compiler/lua5.2/pm.luac | Bin 0 -> 21413 bytes .../bytecode-compiler/lua5.2/sort.lua | 74 ++ .../bytecode-compiler/lua5.2/sort.luac | Bin 0 -> 4673 bytes .../bytecode-compiler/lua5.2/strings.lua | 176 +++++ .../bytecode-compiler/lua5.2/strings.luac | Bin 0 -> 14068 bytes .../bytecode-compiler/lua5.2/verybig.lua | 100 +++ .../bytecode-compiler/lua5.2/verybig.luac | Bin 0 -> 3122 bytes .../bytecode-compiler/regressions/bigattr.lc | Bin 206 -> 0 bytes .../regressions/bigattr.luac | Bin 0 -> 218 bytes .../regressions/comparators.lc | Bin 230 -> 0 bytes .../regressions/comparators.luac | Bin 0 -> 226 bytes .../regressions/construct.lc | Bin 162 -> 0 bytes .../regressions/construct.luac | Bin 0 -> 166 bytes .../regressions/controlchars.lc | Bin 225 -> 0 bytes .../regressions/controlchars.lua | 2 +- .../regressions/controlchars.luac | Bin 0 -> 227 bytes .../regressions/mathrandomseed.lc | Bin 159 -> 0 bytes .../regressions/mathrandomseed.luac | Bin 0 -> 167 bytes .../bytecode-compiler/regressions/modulo.lc | Bin 220 -> 0 bytes .../bytecode-compiler/regressions/modulo.luac | Bin 0 -> 188 bytes .../bytecode-compiler/regressions/varargs.lc | Bin 2000 -> 0 bytes .../bytecode-compiler/regressions/varargs.lua | 29 - 68 files changed, 6255 insertions(+), 119 deletions(-) create mode 100644 src/test/resources/bytecode-compiler/lua5.2/all.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/all.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/api.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/api.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/attrib.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/attrib.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/big.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/big.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/calls.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/calls.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/checktable.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/checktable.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/closure.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/closure.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/code.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/code.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/constructs.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/constructs.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/db.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/db.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/errors.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/errors.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/events.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/events.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/files.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/files.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/gc.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/gc.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/literals.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/literals.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/locals.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/locals.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/main.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/main.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/math.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/math.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/nextvar.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/nextvar.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/pm.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/pm.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/sort.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/sort.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/strings.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/strings.luac create mode 100644 src/test/resources/bytecode-compiler/lua5.2/verybig.lua create mode 100644 src/test/resources/bytecode-compiler/lua5.2/verybig.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/bigattr.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/bigattr.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/comparators.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/comparators.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/construct.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/construct.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/controlchars.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/controlchars.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/mathrandomseed.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/mathrandomseed.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/modulo.lc create mode 100644 src/test/resources/bytecode-compiler/regressions/modulo.luac delete mode 100644 src/test/resources/bytecode-compiler/regressions/varargs.lc delete mode 100644 src/test/resources/bytecode-compiler/regressions/varargs.lua diff --git a/build.gradle b/build.gradle index 0364c34e..ed8a5bec 100644 --- a/build.gradle +++ b/build.gradle @@ -113,5 +113,4 @@ test { testLogging { events "skipped", "failed" } - exclude "org/squiddev/cobalt/compiler/**" // excluded until Lua 5.2 tests are written } diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index 1449d407..fe532e70 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -35,6 +35,7 @@ import static org.squiddev.cobalt.Constants.*; import static org.squiddev.cobalt.Lua52.*; import static org.squiddev.cobalt.compiler.LexState.NO_JUMP; +import static org.squiddev.cobalt.compiler.LexState.VUPVAL; import static org.squiddev.cobalt.compiler.LuaC.*; public class FuncState { @@ -282,23 +283,17 @@ void nil(int from, int n) throws CompileException { InstructionPtr previous; int l = from + n - 1; if (this.pc > this.lasttarget) { /* no jumps to current position? */ - if (this.pc == 0) { /* function start? */ - if (from >= this.nactvar) { - return; /* positions are already clean */ - } - } else { - previous = new InstructionPtr(this.f.code, this.pc - 1); - if (GET_OPCODE(previous.get()) == OP_LOADNIL) { - int pfrom = GETARG_A(previous.get()); - int pl = pfrom + GETARG_B(previous.get()); - if ((pfrom <= from && from <= pl + 1) || - (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ - if (pfrom < from) from = pfrom; - if (pl > l) l = pl; - SETARG_A(previous, from); - SETARG_B(previous, l - from); - return; - } + previous = new InstructionPtr(this.f.code, this.pc - 1); + if (GET_OPCODE(previous.get()) == OP_LOADNIL) { + int pfrom = GETARG_A(previous.get()); + int pl = pfrom + GETARG_B(previous.get()); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; + if (pl > l) l = pl; + SETARG_A(previous, from); + SETARG_B(previous, l - from); + return; } } } @@ -685,6 +680,12 @@ int exp2anyreg(expdesc e) throws CompileException { return e.u.info; } + void exp2anyregup(expdesc e) throws CompileException { + if (e.k != VUPVAL || e.hasjumps()) { + this.exp2anyreg(e); + } + } + void exp2val(expdesc e) throws CompileException { if (e.hasjumps()) { this.exp2anyreg(e); @@ -696,13 +697,11 @@ void exp2val(expdesc e) throws CompileException { int exp2RK(expdesc e) throws CompileException { this.exp2val(e); switch (e.k) { - case LexState.VKNUM: case LexState.VTRUE: case LexState.VFALSE: case LexState.VNIL: { if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ e.u.info = (e.k == LexState.VNIL) ? this.nilK() - : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval()) : this.boolK((e.k == LexState.VTRUE)); e.k = LexState.VK; return RKASK(e.u.info); @@ -710,6 +709,11 @@ int exp2RK(expdesc e) throws CompileException { break; } } + case LexState.VKNUM: { + e.u.info = this.numberK(e.u.nval()); + e.k = LexState.VK; + /* go through */ + } case LexState.VK: { if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ { return RKASK(e.u.info); @@ -791,21 +795,17 @@ void goiftrue(expdesc e) throws CompileException { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { + case LexState.VJMP: { + this.invertjump(e); + pc = e.u.info; + break; + } case LexState.VK: case LexState.VKNUM: case LexState.VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } - case LexState.VFALSE: { - pc = this.jump(); /* always jump */ - break; - } - case LexState.VJMP: { - this.invertjump(e); - pc = e.u.info; - break; - } default: { pc = this.jumponcond(e, 0); break; @@ -820,19 +820,15 @@ void goiffalse(expdesc e) throws CompileException { int pc; /* pc of last jump */ this.dischargevars(e); switch (e.k) { + case LexState.VJMP: { + pc = e.u.info; + break; + } case LexState.VNIL: case LexState.VFALSE: { pc = NO_JUMP; /* always false; do nothing */ break; } - case LexState.VTRUE: { - pc = this.jump(); /* always jump */ - break; - } - case LexState.VJMP: { - pc = e.u.info; - break; - } default: { pc = this.jumponcond(e, 1); break; @@ -934,14 +930,11 @@ private boolean constfolding(int op, expdesc e1, expdesc e2) throws CompileExcep return false; } - if (Double.isNaN(r.toDouble())) { - return false; /* do not attempt to produce NaN */ - } e1.u.setNval(r); return true; } - private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { + private void codearith(int op, expdesc e1, expdesc e2, int line) throws CompileException { if (constfolding(op, e1, e2)) { } else { int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) @@ -956,6 +949,7 @@ private void codearith(int op, expdesc e1, expdesc e2) throws CompileException { } e1.u.info = this.codeABC(op, 0, o1, o2); e1.k = LexState.VRELOCABLE; + fixline(line); } } @@ -975,7 +969,7 @@ private void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) thro e1.k = LexState.VJMP; } - void prefix(int /* UnOpr */op, expdesc e) throws CompileException { + void prefix(int /* UnOpr */op, expdesc e, int line) throws CompileException { expdesc e2 = new expdesc(); e2.init(LexState.VKNUM, 0); switch (op) { @@ -983,7 +977,7 @@ void prefix(int /* UnOpr */op, expdesc e) throws CompileException { if (e.k == LexState.VK) { this.exp2anyreg(e); /* cannot operate on non-numeric constants */ } - this.codearith(OP_UNM, e, e2); + this.codearith(OP_UNM, e, e2, line); break; } case LexState.OPR_NOT: @@ -991,7 +985,7 @@ void prefix(int /* UnOpr */op, expdesc e) throws CompileException { break; case LexState.OPR_LEN: { this.exp2anyreg(e); /* cannot operate on constants */ - this.codearith(OP_LEN, e, e2); + this.codearith(OP_LEN, e, e2, line); break; } default: @@ -1032,7 +1026,7 @@ void infix(int /* BinOpr */op, expdesc v) throws CompileException { } - void posfix(int op, expdesc e1, expdesc e2) throws CompileException { + void posfix(int op, expdesc e1, expdesc e2, int line) throws CompileException { switch (op) { case LexState.OPR_AND: { _assert(e1.t.i == NO_JUMP); /* list must be closed */ @@ -1061,27 +1055,27 @@ && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { e1.u.info = e2.u.info; } else { this.exp2nextreg(e2); /* operand must be on the 'stack' */ - this.codearith(OP_CONCAT, e1, e2); + this.codearith(OP_CONCAT, e1, e2, line); } break; } case LexState.OPR_ADD: - this.codearith(OP_ADD, e1, e2); + this.codearith(OP_ADD, e1, e2, line); break; case LexState.OPR_SUB: - this.codearith(OP_SUB, e1, e2); + this.codearith(OP_SUB, e1, e2, line); break; case LexState.OPR_MUL: - this.codearith(OP_MUL, e1, e2); + this.codearith(OP_MUL, e1, e2, line); break; case LexState.OPR_DIV: - this.codearith(OP_DIV, e1, e2); + this.codearith(OP_DIV, e1, e2, line); break; case LexState.OPR_MOD: - this.codearith(OP_MOD, e1, e2); + this.codearith(OP_MOD, e1, e2, line); break; case LexState.OPR_POW: - this.codearith(OP_POW, e1, e2); + this.codearith(OP_POW, e1, e2, line); break; case LexState.OPR_EQ: this.codecomp(OP_EQ, 1, e1, e2); diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState.java b/src/main/java/org/squiddev/cobalt/compiler/LexState.java index 28bf2aa2..5f275b75 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState.java @@ -984,6 +984,7 @@ private void adjustlocalvars(int nvars) { void removevars(int tolevel) { FuncState fs = this.fs; + fs.ls.dyd.nactvar -= (fs.nactvar - tolevel); while (fs.nactvar > tolevel) { fs.getlocvar(--fs.nactvar).endpc = fs.pc; } @@ -1146,7 +1147,7 @@ void open_func(FuncState fs, FuncState.BlockCnt bl) throws CompileException { fs.ls = this; this.fs = fs; fs.pc = 0; - fs.lasttarget = -1; + fs.lasttarget = 0; fs.jpc = new IntPtr(NO_JUMP); fs.freereg = 0; fs.nk = 0; @@ -1163,7 +1164,6 @@ void open_func(FuncState fs, FuncState.BlockCnt bl) throws CompileException { void close_func() throws CompileException { FuncState fs = this.fs; Prototype f = fs.f; - this.removevars(0); fs.ret(0, 0); /* final return */ fs.leaveblock(); f.code = LuaC.realloc(f.code, fs.pc); @@ -1191,7 +1191,7 @@ private void field(expdesc v) throws CompileException { /* field -> ['.' | ':'] NAME */ FuncState fs = this.fs; expdesc key = new expdesc(); - fs.exp2anyreg(v); + fs.exp2anyregup(v); this.nextToken(); /* skip the dot or colon */ this.checkname(key); fs.indexed(v, key); @@ -1386,16 +1386,12 @@ private int explist1(expdesc v) throws CompileException { } - private void funcargs(expdesc f) throws CompileException { + private void funcargs(expdesc f, int line) throws CompileException { FuncState fs = this.fs; expdesc args = new expdesc(); int base, nparams; - int line = this.linenumber; switch (this.t.token) { case '(': { /* funcargs -> `(' [ explist1 ] `)' */ - if (line != this.lastline) { - throw syntaxError("ambiguous syntax (function call x new statement)"); - } this.nextToken(); if (this.t.token == ')') /* arg list is empty? */ { args.k = VVOID; @@ -1442,8 +1438,8 @@ private void funcargs(expdesc f) throws CompileException { ** ======================================================================= */ - private void prefixexp(expdesc v) throws CompileException { - /* prefixexp -> NAME | '(' expr ')' */ + private void primaryexp(expdesc v) throws CompileException { + /* primaryexp -> NAME | '(' expr ')' */ switch (this.t.token) { case '(': { int line = this.linenumber; @@ -1464,13 +1460,14 @@ private void prefixexp(expdesc v) throws CompileException { } - private void primaryexp(expdesc v) throws CompileException { + private void suffixedexp(expdesc v) throws CompileException { /* - * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | + * suffixedexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | * funcargs } */ FuncState fs = this.fs; - this.prefixexp(v); + int line = linenumber; + this.primaryexp(v); for (; ; ) { switch (this.t.token) { case '.': { /* field */ @@ -1479,7 +1476,7 @@ private void primaryexp(expdesc v) throws CompileException { } case '[': { /* `[' exp1 `]' */ expdesc key = new expdesc(); - fs.exp2anyreg(v); + fs.exp2anyregup(v); this.yindex(key); fs.indexed(v, key); break; @@ -1489,14 +1486,14 @@ private void primaryexp(expdesc v) throws CompileException { this.nextToken(); this.checkname(key); fs.self(v, key); - this.funcargs(v); + this.funcargs(v, line); break; } case '(': case TK_STRING: case '{': { /* funcargs */ fs.exp2nextreg(v); - this.funcargs(v); + this.funcargs(v, line); break; } default: @@ -1509,7 +1506,7 @@ private void primaryexp(expdesc v) throws CompileException { private void simpleexp(expdesc v) throws CompileException { /* * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | - * FUNCTION body | primaryexp + * FUNCTION body | suffixedexp */ switch (this.t.token) { case TK_NUMBER: { @@ -1551,7 +1548,7 @@ private void simpleexp(expdesc v) throws CompileException { return; } default: { - this.primaryexp(v); + this.suffixedexp(v); return; } } @@ -1642,9 +1639,10 @@ private int subexpr(expdesc v, int limit) throws CompileException { this.enterlevel(); uop = getunopr(this.t.token); if (uop != OPR_NOUNOPR) { + int line = linenumber; this.nextToken(); this.subexpr(v, UNARY_PRIORITY); - fs.prefix(uop, v); + fs.prefix(uop, v, line); } else { this.simpleexp(v); } @@ -1653,11 +1651,12 @@ private int subexpr(expdesc v, int limit) throws CompileException { while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2 = new expdesc(); int nextop; + int line = linenumber; this.nextToken(); fs.infix(op, v); /* read sub-expression with higher priority */ nextop = this.subexpr(v2, priority[op].right); - fs.posfix(op, v, v2); + fs.posfix(op, v, v2, line); op = nextop; } this.leavelevel(); @@ -1750,10 +1749,10 @@ private void assignment(LHS_assign lh, int nvars) throws CompileException { expdesc e = new expdesc(); this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED, "syntax error"); - if (this.testnext(',')) { /* assignment -> `,' primaryexp assignment */ + if (this.testnext(',')) { /* assignment -> `,' suffixedexp assignment */ LHS_assign nv = new LHS_assign(); nv.prev = lh; - this.primaryexp(nv.v); + this.suffixedexp(nv.v); if (nv.v.k == VLOCAL) { this.check_conflict(lh, nv.v); } @@ -1863,11 +1862,9 @@ private void repeatstat(int line) throws CompileException { private int exp1() throws CompileException { expdesc e = new expdesc(); - int k; this.expr(e); - k = e.k; fs.exp2nextreg(e); - return k; + return e.u.info; } @@ -2019,15 +2016,11 @@ private void ifstat(int line) throws CompileException { } private void localfunc() throws CompileException { - expdesc v = new expdesc(); expdesc b = new expdesc(); FuncState fs = this.fs; this.new_localvar(this.str_checkname(), 0); - v.init(VLOCAL, fs.freereg); - fs.reserveregs(1); this.adjustlocalvars(1); this.body(b, false, this.linenumber); - fs.storevar(v, b); /* debug information will only see the variable after this point! */ fs.getlocvar(fs.nactvar - 1).startpc = fs.pc; } @@ -2084,7 +2077,7 @@ private void exprstat() throws CompileException { /* stat -> func | assignment */ FuncState fs = this.fs; LHS_assign v = new LHS_assign(); - this.primaryexp(v.v); + this.suffixedexp(v.v); if (v.v.k == VCALL) /* stat -> func */ { LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ } else { /* stat -> assignment */ diff --git a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java index 992eef32..b0fe192f 100644 --- a/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java +++ b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java @@ -49,7 +49,7 @@ public static void compareResults(String dir, String file) throws IOException, C String sourceBytecode = Print.show(LuaC.compile(new ByteArrayInputStream(bytesFromJar(dir + file + ".lua")), LuaString.valueOf("@" + file + ".lua"), null)); // Load expected value from jar - Prototype expectedPrototype = loadFromBytes(bytesFromJar(dir + file + ".lc"), file + ".lua"); + Prototype expectedPrototype = loadFromBytes(bytesFromJar(dir + file + ".luac"), file + ".lua"); String expectedBytecode = Print.show(expectedPrototype); // compare results diff --git a/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java b/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java index 4d2f53a7..fef369b0 100644 --- a/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java +++ b/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java @@ -45,15 +45,15 @@ public void setup() { @ParameterizedTest(name = ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER) @ValueSource(strings = { "all", "api", "attrib", "big", "calls", "checktable", "closure", "code", "constructs", "db", "errors", - "events", "files", "gc", "literals", "locals", "main", "math", "nextvar", "pm", "sort", "strings", "vararg", + "events", "files", "gc", "literals", "locals", "main", "math", "nextvar", "pm", "sort", "strings", }) - public void lua51(String filename) throws IOException, CompileException { - CompileTestHelper.compareResults("/bytecode-compiler/lua5.1/", filename); + public void lua52(String filename) throws IOException, CompileException { + CompileTestHelper.compareResults("/bytecode-compiler/lua5.2/", filename); } @ParameterizedTest(name = ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER) @ValueSource(strings = { - "modulo", "construct", "bigattr", "controlchars", "comparators", "mathrandomseed", "varargs", + "modulo", "construct", "bigattr", "controlchars", "comparators", "mathrandomseed", }) public void regression(String filename) throws IOException, CompileException { CompileTestHelper.compareResults("/bytecode-compiler/regressions/", filename); diff --git a/src/test/resources/assert/lua5.1/db.lua b/src/test/resources/assert/lua5.1/db.lua index 3466fa20..5463b959 100644 --- a/src/test/resources/assert/lua5.1/db.lua +++ b/src/test/resources/assert/lua5.1/db.lua @@ -132,7 +132,7 @@ a=1 while a<=3 do a=a+1 end -]], { 2, 3, 4, 3, 4, 3, 4, 3, 5 }) +]], { 1, 2, 3, 4, 3, 4, 3, 4, 3, 5 }) test([[while math.sin(1) do if math.sin(1) diff --git a/src/test/resources/assert/lua5.1/errors.lua b/src/test/resources/assert/lua5.1/errors.lua index 6d656383..35a2a21a 100644 --- a/src/test/resources/assert/lua5.1/errors.lua +++ b/src/test/resources/assert/lua5.1/errors.lua @@ -42,7 +42,7 @@ assert(doit "repeat until 1; a") assert(doit "return;;") assert(doit "assert(false)") assert(doit "assert(nil)") -assert(doit "a=math.sin\n(3)") +--assert(doit "a=math.sin\n(3)") assert(doit("function a (... , ...) end")) assert(doit("function a (, ...) end")) diff --git a/src/test/resources/bytecode-compiler/lua5.2/all.lua b/src/test/resources/bytecode-compiler/lua5.2/all.lua new file mode 100644 index 00000000..8c4aface --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/all.lua @@ -0,0 +1,137 @@ +#!../lua + +math.randomseed(0) + +collectgarbage("setstepmul", 180) +collectgarbage("setpause", 190) + + +--[=[ + example of a long [comment], + [[spanning several [lines]]] + +]=] + +print("current path:\n " .. string.gsub(package.path, ";", "\n ")) + + +local msgs = {} +function Message (m) + print(m) + msgs[#msgs+1] = string.sub(m, 3, -3) +end + + +local c = os.clock() + +assert(os.setlocale"C") + +local T,print,gcinfo,format,write,assert,type = + T,print,gcinfo,string.format,io.write,assert,type + +local function formatmem (m) + if m < 1024 then return m + else + m = m/1024 - m/1024%1 + if m < 1024 then return m.."K" + else + m = m/1024 - m/1024%1 + return m.."M" + end + end +end + +local showmem = function () + if not T then + print(format(" ---- total memory: %s ----\n", formatmem(gcinfo()))) + else + T.checkmemory() + local a,b,c = T.totalmem() + local d,e = gcinfo() + print(format( + "\n ---- total memory: %s (%dK), max use: %s, blocks: %d\n", + formatmem(a), d, formatmem(c), b)) + end +end + + +-- +-- redefine dofile to run files through dump/undump +-- +dofile = function (n) + showmem() + local f = assert(loadfile(n)) + local b = string.dump(f) + f = assert(loadstring(b)) + return f() +end + +dofile('main.lua') + +do + local u = newproxy(true) + local newproxy, stderr = newproxy, io.stderr + getmetatable(u).__gc = function (o) + stderr:write'.' + newproxy(o) + end +end + +local f = assert(loadfile('gc.lua')) +f() +dofile('db.lua') +assert(dofile('calls.lua') == deep and deep) +dofile('strings.lua') +dofile('literals.lua') +assert(dofile('attrib.lua') == 27) +assert(dofile('locals.lua') == 5) +dofile('constructs.lua') +dofile('code.lua') +do + local f = coroutine.wrap(assert(loadfile('big.lua'))) + assert(f() == 'b') + assert(f() == 'a') +end +dofile('nextvar.lua') +dofile('pm.lua') +dofile('api.lua') +assert(dofile('events.lua') == 12) +dofile('vararg.lua') +dofile('closure.lua') +dofile('errors.lua') +dofile('math.lua') +dofile('sort.lua') +assert(dofile('verybig.lua') == 10); collectgarbage() +dofile('files.lua') + +if #msgs > 0 then + print("\ntests not performed:") + for i=1,#msgs do + print(msgs[i]) + end + print() +end + +print("final OK !!!") +print('cleaning all!!!!') + +debug.sethook(function (a) assert(type(a) == 'string') end, "cr") + +local _G, collectgarbage, showmem, print, format, clock = + _G, collectgarbage, showmem, print, format, os.clock + +local a={} +for n in pairs(_G) do a[n] = 1 end +a.tostring = nil +a.___Glob = nil +for n in pairs(a) do _G[n] = nil end + +a = nil +collectgarbage() +collectgarbage() +collectgarbage() +collectgarbage() +collectgarbage() +collectgarbage();showmem() + +print(format("\n\ntotal time: %.2f\n", clock()-c)) diff --git a/src/test/resources/bytecode-compiler/lua5.2/all.luac b/src/test/resources/bytecode-compiler/lua5.2/all.luac new file mode 100644 index 0000000000000000000000000000000000000000..03de65edbe450d9b583d214b18c00babf55403a4 GIT binary patch literal 5690 zcmb7I>2Djy6@SBBQWAZpIULtuLnBFR2e#3s7wWY2Qj{nswo}`+T^FsOuE-TlnB)q( zOCRXrp=ryl0|YMmSs&BCV--4b(E{y1Q1u9!4{cGPe?imVn?qblw&f7|ao#m=-n@Bp zPzO%;{PQGS*Bz(*zu8s%e39%&?9U6L0(ms%c~p@^C67cQ1x(`vsl_@HGSfgN)=!4rtpL__vY3}{i z+zg587_Ci`SiL<%jw5O9Hs&#yp<1;@Gs8!PCFdlij?6C)NPOZ5x<4E|*f;$2R+^Je zVQ`Wv!*MD(B+!otcBQJMf-^%EXM##qB*T$T4Sc^kL9_6&GO0hZwV&c9KSkx|viyBvA8qWrKGx9K&>yzgD4*3Gqp=2t9-g8x=XAkCyi1tZ zN_8hKV_vfk)^l>P-%)pdHBCuY|Fk>pP}xfXy-W{TlE}`4bByUD)z{bW!N*$ zd8#1>gzt569ngiB_l16XmI}<_f`UaR1KP9Uo5-)$^a;y8rO}2f%NQ z&4jfn4$rUK+<}kOm4&q_`59UoGxlQ`d%QCJJjuKqU%o5p{b|}!%1g0^7+xVs?-G%l z$5+Qsu8_;yIP$xVuoDMCgODu!ardCDhwXMyPnv!-?>7T&90y691dE+s+Zf9Q&z*oV z6pMZ@4vaW$dTs&Y#i-Ry$bF98^`a=~Cgnwpe7smLlRL)!5?C~KOwG7APdLrc&97fo zV{)k6f!rLZx5!0)NOfkjGeI0HGY&kANex+VhxM!4xgWPg)l;L$Yq}`$aospqgH~B98Gd_5fq2s2p3xHj;G)8Tis*rp0BxXaD6cf zZ`{;&ancB)h}>PUYX(UtNc_Z~=br3uZmwC^z1pGQ&>q=)v#$NAk>)kc^S~6^v1Yk@ zBM27B-3dx((x`Z>He!x^Ys^dSC&;$;WtRFaPt}0RjP7a_FL+HlSMU-MMe@Ol_0 zFTt>w1aT6VyCKRrh`3UM#&NrC{(C|#@j|QXx69uZ`mr|`ip0Y_4{+#$3cJzN2!|KiS&zgtsEfcEFU1Az;nMd+3Y`#wDixtF{F73u zgM(dVcC^V7rp(J{H?hk!_OQ{8dzMI{AT9uogJVC0H^SPN61o6<}ssb%)SrVMU-p5Fmk z1#r^v4xR>PQ4oB}ERjB0mZ6=ML`$A0?n#zm(Ip8K78O=J;h|7Uz#43@LzVt7(ESy@ z?AB80-*{Kr^M@mE^g6hJ_y%AbdtQR&Eh&lu0dLrJ9O`!sPN3Zk53(#!p7<5D6_b!z za29yD9MA>yk;dGDN*IsH+&pod%(1n)39f|KZCcIig(o$~=h-_T_~a1!^ymDZrj%AZ zm1lLDOUPzD5}Yd@Ap3Y>o(oeCEp~~W8}h)TpW1}e;4SpXx^oD*j+dQs379tydt86~ zTm!>Eyj(LJ?_I=|qUANejG9nCQ#|}kf!xL|pwHeaVAWj2m>Ay>F*~(mY}c|=j@1Wn z8~`@*OqSKtjx9IcQT$_?7bpu}l`IP2v)~8xv*L5GeF@;JdVdB7KtB#&fP6c=0UJIR zss@~Wi<6)&-Ue;)4rmKrZi};^H{%>+4E)?#$M->h7F>Y6#YNB-mq2faAHZfiT!xLs z4?$b}2=vqNW9Xj-zV@DmIp{5Z0@{L$&te|*cBsR~q5;|>0Bx}V+M)^C;tJ?zMGLkT zS3z5}L0fb{XTiss1nZ3%ciUiqmT|}6 zE?UNt!9BED{0jVbcojBzKs_?~Da8i`pDT5|fJWQ}^}J#o9}xOJJ!14@XwCJR6`8Bn zinyQt32G(W&zxLV#F3~I%nJCF8(q{#!Ep@ZXk&BB@O5Bg?$I~Eod70)^+WtyM#ux2 zL*9ybrVBKMZ|h@$c-h_qb^?bmc)8VW1UC-T5d815@^-7!N=EV>)}H^nFcR|+EqaLW zM}Pz1SJ9u*@Q9E3AB9zACuwD)>t4ToMZgJ4@UCcwog6mCQ;ZPY1ns{{*LdDfyep&C*=PDHv6{nt-h^B`~DaD N{u@B(s#MiO;{VP+ZH52< literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/api.lua b/src/test/resources/bytecode-compiler/lua5.2/api.lua new file mode 100644 index 00000000..3a8bf0bf --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/api.lua @@ -0,0 +1,711 @@ + +if T==nil then + (Message or print)('\a\n >>> testC not active: skipping API tests <<<\n\a') + return +end + + + +function tcheck (t1, t2) + table.remove(t1, 1) -- remove code + assert(table.getn(t1) == table.getn(t2)) + for i=1,table.getn(t1) do assert(t1[i] == t2[i]) end +end + +--function pack(...) return {...} end + + +print('testing C API') + +-- testing allignment +a = T.d2s(12458954321123) +assert(string.len(a) == 8) -- sizeof(double) +assert(T.s2d(a) == 12458954321123) + +a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2") +assert(a == 2 and b == 3 and not c) + +-- test that all trues are equal +a,b,c = T.testC("pushbool 1; pushbool 2; pushbool 0; return 3") +assert(a == b and a == true and c == false) +a,b,c = T.testC"pushbool 0; pushbool 10; pushnil;\ + tobool -3; tobool -3; tobool -3; return 3" +assert(a==0 and b==1 and c==0) + + +a,b,c = T.testC("gettop; return 2", 10, 20, 30, 40) +assert(a == 40 and b == 5 and not c) + +t = pack(T.testC("settop 5; gettop; return .", 2, 3)) +tcheck(t, {n=4,2,3}) + +t = pack(T.testC("settop 0; settop 15; return 10", 3, 1, 23)) +assert(t.n == 10 and t[1] == nil and t[10] == nil) + +t = pack(T.testC("remove -2; gettop; return .", 2, 3, 4)) +tcheck(t, {n=2,2,4}) + +t = pack(T.testC("insert -1; gettop; return .", 2, 3)) +tcheck(t, {n=2,2,3}) + +t = pack(T.testC("insert 3; gettop; return .", 2, 3, 4, 5)) +tcheck(t, {n=4,2,5,3,4}) + +t = pack(T.testC("replace 2; gettop; return .", 2, 3, 4, 5)) +tcheck(t, {n=3,5,3,4}) + +t = pack(T.testC("replace -2; gettop; return .", 2, 3, 4, 5)) +tcheck(t, {n=3,2,3,5}) + +t = pack(T.testC("remove 3; gettop; return .", 2, 3, 4, 5)) +tcheck(t, {n=3,2,4,5}) + +t = pack(T.testC("insert 3; pushvalue 3; remove 3; pushvalue 2; remove 2; \ + insert 2; pushvalue 1; remove 1; insert 1; \ + insert -2; pushvalue -2; remove -3; gettop; return .", + 2, 3, 4, 5, 10, 40, 90)) +tcheck(t, {n=7,2,3,4,5,10,40,90}) + +t = pack(T.testC("concat 5; gettop; return .", "alo", 2, 3, "joao", 12)) +tcheck(t, {n=1,"alo23joao12"}) + +-- testing MULTRET +t = pack(T.testC("rawcall 2,-1; gettop; return .", + function (a,b) return 1,2,3,4,a,b end, "alo", "joao")) +tcheck(t, {n=6,1,2,3,4,"alo", "joao"}) + +do -- test returning more results than fit in the caller stack + local a = {} + for i=1,1000 do a[i] = true end; a[999] = 10 + local b = T.testC([[call 1 -1; pop 1; tostring -1; return 1]], unpack, a) + assert(b == "10") +end + + +-- testing lessthan +assert(T.testC("lessthan 2 5, return 1", 3, 2, 2, 4, 2, 2)) +assert(T.testC("lessthan 5 2, return 1", 4, 2, 2, 3, 2, 2)) +assert(not T.testC("lessthan 2 -3, return 1", "4", "2", "2", "3", "2", "2")) +assert(not T.testC("lessthan -3 2, return 1", "3", "2", "2", "4", "2", "2")) + +local b = {__lt = function (a,b) return a[1] < b[1] end} +local a1,a3,a4 = setmetatable({1}, b), + setmetatable({3}, b), + setmetatable({4}, b) +assert(T.testC("lessthan 2 5, return 1", a3, 2, 2, a4, 2, 2)) +assert(T.testC("lessthan 5 -6, return 1", a4, 2, 2, a3, 2, 2)) +a,b = T.testC("lessthan 5 -6, return 2", a1, 2, 2, a3, 2, 20) +assert(a == 20 and b == false) + + +-- testing lua_is + +function count (x, n) + n = n or 2 + local prog = [[ + isnumber %d; + isstring %d; + isfunction %d; + iscfunction %d; + istable %d; + isuserdata %d; + isnil %d; + isnull %d; + return 8 + ]] + prog = string.format(prog, n, n, n, n, n, n, n, n) + local a,b,c,d,e,f,g,h = T.testC(prog, x) + return a+b+c+d+e+f+g+(100*h) +end + +assert(count(3) == 2) +assert(count('alo') == 1) +assert(count('32') == 2) +assert(count({}) == 1) +assert(count(print) == 2) +assert(count(function () end) == 1) +assert(count(nil) == 1) +assert(count(io.stdin) == 1) +assert(count(nil, 15) == 100) + +-- testing lua_to... + +function to (s, x, n) + n = n or 2 + return T.testC(string.format("%s %d; return 1", s, n), x) +end + +assert(to("tostring", {}) == nil) +assert(to("tostring", "alo") == "alo") +assert(to("tostring", 12) == "12") +assert(to("tostring", 12, 3) == nil) +assert(to("objsize", {}) == 0) +assert(to("objsize", "alo\0\0a") == 6) +assert(to("objsize", T.newuserdata(0)) == 0) +assert(to("objsize", T.newuserdata(101)) == 101) +assert(to("objsize", 12) == 2) +assert(to("objsize", 12, 3) == 0) +assert(to("tonumber", {}) == 0) +assert(to("tonumber", "12") == 12) +assert(to("tonumber", "s2") == 0) +assert(to("tonumber", 1, 20) == 0) +a = to("tocfunction", math.deg) +assert(a(3) == math.deg(3) and a ~= math.deg) + + +-- testing errors + +a = T.testC([[ + loadstring 2; call 0,1; + pushvalue 3; insert -2; call 1, 1; + call 0, 0; + return 1 +]], "x=150", function (a) assert(a==nil); return 3 end) + +assert(type(a) == 'string' and x == 150) + +--[[function check3(p, ...) + assert(select("#", ...) == 3) + assert(string.find(select(3, ...), p)) +end]] +check3(":1:", T.testC("loadstring 2; gettop; return .", "x=")) +check3("cannot read", T.testC("loadfile 2; gettop; return .", ".")) +check3("cannot open xxxx", T.testC("loadfile 2; gettop; return .", "xxxx")) + +-- testing table access + +a = {x=0, y=12} +x, y = T.testC("gettable 2; pushvalue 4; gettable 2; return 2", + a, 3, "y", 4, "x") +assert(x == 0 and y == 12) +T.testC("settable -5", a, 3, 4, "x", 15) +assert(a.x == 15) +a[a] = print +x = T.testC("gettable 2; return 1", a) -- table and key are the same object! +assert(x == print) +T.testC("settable 2", a, "x") -- table and key are the same object! +assert(a[a] == "x") + +b = setmetatable({p = a}, {}) +getmetatable(b).__index = function (t, i) return t.p[i] end +k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x") +assert(x == 15 and k == 35) +getmetatable(b).__index = function (t, i) return a[i] end +getmetatable(b).__newindex = function (t, i,v ) a[i] = v end +y = T.testC("insert 2; gettable -5; return 1", 2, 3, 4, "y", b) +assert(y == 12) +k = T.testC("settable -5, return 1", b, 3, 4, "x", 16) +assert(a.x == 16 and k == 4) +a[b] = 'xuxu' +y = T.testC("gettable 2, return 1", b) +assert(y == 'xuxu') +T.testC("settable 2", b, 19) +assert(a[b] == 19) + +-- testing next +a = {} +t = pack(T.testC("next; gettop; return .", a, nil)) +tcheck(t, {n=1,a}) +a = {a=3} +t = pack(T.testC("next; gettop; return .", a, nil)) +tcheck(t, {n=3,a,'a',3}) +t = pack(T.testC("next; pop 1; next; gettop; return .", a, nil)) +tcheck(t, {n=1,a}) + + + +-- testing upvalues + +do + local A = T.testC[[ pushnum 10; pushnum 20; pushcclosure 2; return 1]] + t, b, c = A([[pushvalue U0; pushvalue U1; pushvalue U2; return 3]]) + assert(b == 10 and c == 20 and type(t) == 'table') + a, b = A([[tostring U3; tonumber U4; return 2]]) + assert(a == nil and b == 0) + A([[pushnum 100; pushnum 200; replace U2; replace U1]]) + b, c = A([[pushvalue U1; pushvalue U2; return 2]]) + assert(b == 100 and c == 200) + A([[replace U2; replace U1]], {x=1}, {x=2}) + b, c = A([[pushvalue U1; pushvalue U2; return 2]]) + assert(b.x == 1 and c.x == 2) + T.checkmemory() +end + +local f = T.testC[[ pushnum 10; pushnum 20; pushcclosure 2; return 1]] +assert(T.upvalue(f, 1) == 10 and + T.upvalue(f, 2) == 20 and + T.upvalue(f, 3) == nil) +T.upvalue(f, 2, "xuxu") +assert(T.upvalue(f, 2) == "xuxu") + + +-- testing environments + +assert(T.testC"pushvalue G; return 1" == _G) +assert(T.testC"pushvalue E; return 1" == _G) +local a = {} +T.testC("replace E; return 1", a) +assert(T.testC"pushvalue G; return 1" == _G) +assert(T.testC"pushvalue E; return 1" == a) +assert(debug.getfenv(T.testC) == a) +assert(debug.getfenv(T.upvalue) == _G) +-- userdata inherit environment +local u = T.testC"newuserdata 0; return 1" +assert(debug.getfenv(u) == a) +-- functions inherit environment +u = T.testC"pushcclosure 0; return 1" +assert(debug.getfenv(u) == a) +debug.setfenv(T.testC, _G) +assert(T.testC"pushvalue E; return 1" == _G) + +local b = newproxy() +assert(debug.getfenv(b) == _G) +assert(debug.setfenv(b, a)) +assert(debug.getfenv(b) == a) + + + +-- testing locks (refs) + +-- reuse of references +local i = T.ref{} +T.unref(i) +assert(T.ref{} == i) + +Arr = {} +Lim = 100 +for i=1,Lim do -- lock many objects + Arr[i] = T.ref({}) +end + +assert(T.ref(nil) == -1 and T.getref(-1) == nil) +T.unref(-1); T.unref(-1) + +for i=1,Lim do -- unlock all them + T.unref(Arr[i]) +end + +function printlocks () + local n = T.testC("gettable R; return 1", "n") + print("n", n) + for i=0,n do + print(i, T.testC("gettable R; return 1", i)) + end +end + + +for i=1,Lim do -- lock many objects + Arr[i] = T.ref({}) +end + +for i=1,Lim,2 do -- unlock half of them + T.unref(Arr[i]) +end + +assert(type(T.getref(Arr[2])) == 'table') + + +assert(T.getref(-1) == nil) + + +a = T.ref({}) + +collectgarbage() + +assert(type(T.getref(a)) == 'table') + + +-- colect in cl the `val' of all collected userdata +tt = {} +cl = {n=0} +A = nil; B = nil +local F +F = function (x) + local udval = T.udataval(x) + table.insert(cl, udval) + local d = T.newuserdata(100) -- cria lixo + d = nil + assert(debug.getmetatable(x).__gc == F) + loadstring("table.insert({}, {})")() -- cria mais lixo + collectgarbage() -- forca coleta de lixo durante coleta! + assert(debug.getmetatable(x).__gc == F) -- coleta anterior nao melou isso? + local dummy = {} -- cria lixo durante coleta + if A ~= nil then + assert(type(A) == "userdata") + assert(T.udataval(A) == B) + debug.getmetatable(A) -- just acess it + end + A = x -- ressucita userdata + B = udval + return 1,2,3 +end +tt.__gc = F + +-- test whether udate collection frees memory in the right time +do + collectgarbage(); + collectgarbage(); + local x = collectgarbage("count"); + local a = T.newuserdata(5001) + assert(T.testC("objsize 2; return 1", a) == 5001) + assert(collectgarbage("count") >= x+4) + a = nil + collectgarbage(); + assert(collectgarbage("count") <= x+1) + -- udata without finalizer + x = collectgarbage("count") + collectgarbage("stop") + for i=1,1000 do newproxy(false) end + assert(collectgarbage("count") > x+10) + collectgarbage() + assert(collectgarbage("count") <= x+1) + -- udata with finalizer + x = collectgarbage("count") + collectgarbage() + collectgarbage("stop") + a = newproxy(true) + getmetatable(a).__gc = function () end + for i=1,1000 do newproxy(a) end + assert(collectgarbage("count") >= x+10) + collectgarbage() -- this collection only calls TM, without freeing memory + assert(collectgarbage("count") >= x+10) + collectgarbage() -- now frees memory + assert(collectgarbage("count") <= x+1) +end + + +collectgarbage("stop") + +-- create 3 userdatas with tag `tt' +a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a) +b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b) +c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c) + +-- create userdata without meta table +x = T.newuserdata(4) +y = T.newuserdata(0) + +assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil) + +d=T.ref(a); +e=T.ref(b); +f=T.ref(c); +t = {T.getref(d), T.getref(e), T.getref(f)} +assert(t[1] == a and t[2] == b and t[3] == c) + +t=nil; a=nil; c=nil; +T.unref(e); T.unref(f) + +collectgarbage() + +-- check that unref objects have been collected +assert(table.getn(cl) == 1 and cl[1] == nc) + +x = T.getref(d) +assert(type(x) == 'userdata' and debug.getmetatable(x) == tt) +x =nil +tt.b = b -- create cycle +tt=nil -- frees tt for GC +A = nil +b = nil +T.unref(d); +n5 = T.newuserdata(0) +debug.setmetatable(n5, {__gc=F}) +n5 = T.udataval(n5) +collectgarbage() +assert(table.getn(cl) == 4) +-- check order of collection +assert(cl[2] == n5 and cl[3] == nb and cl[4] == na) + + +a, na = {}, {} +for i=30,1,-1 do + a[i] = T.newuserdata(0) + debug.setmetatable(a[i], {__gc=F}) + na[i] = T.udataval(a[i]) +end +cl = {} +a = nil; collectgarbage() +assert(table.getn(cl) == 30) +for i=1,30 do assert(cl[i] == na[i]) end +na = nil + + +for i=2,Lim,2 do -- unlock the other half + T.unref(Arr[i]) +end + +x = T.newuserdata(41); debug.setmetatable(x, {__gc=F}) +assert(T.testC("objsize 2; return 1", x) == 41) +cl = {} +a = {[x] = 1} +x = T.udataval(x) +collectgarbage() +-- old `x' cannot be collected (`a' still uses it) +assert(table.getn(cl) == 0) +for n in pairs(a) do a[n] = nil end +collectgarbage() +assert(table.getn(cl) == 1 and cl[1] == x) -- old `x' must be collected + +-- testing lua_equal +assert(T.testC("equal 2 4; return 1", print, 1, print, 20)) +assert(T.testC("equal 3 2; return 1", 'alo', "alo")) +assert(T.testC("equal 2 3; return 1", nil, nil)) +assert(not T.testC("equal 2 3; return 1", {}, {})) +assert(not T.testC("equal 2 3; return 1")) +assert(not T.testC("equal 2 3; return 1", 3)) + +-- testing lua_equal with fallbacks +do + local map = {} + local t = {__eq = function (a,b) return map[a] == map[b] end} + local function f(x) + local u = T.newuserdata(0) + debug.setmetatable(u, t) + map[u] = x + return u + end + assert(f(10) == f(10)) + assert(f(10) ~= f(11)) + assert(T.testC("equal 2 3; return 1", f(10), f(10))) + assert(not T.testC("equal 2 3; return 1", f(10), f(20))) + t.__eq = nil + assert(f(10) ~= f(10)) +end + +print'+' + + + +------------------------------------------------------------------------- +do -- testing errors during GC + local a = {} + for i=1,20 do + a[i] = T.newuserdata(i) -- creates several udata + end + for i=1,20,2 do -- mark half of them to raise error during GC + debug.setmetatable(a[i], {__gc = function (x) error("error inside gc") end}) + end + for i=2,20,2 do -- mark the other half to count and to create more garbage + debug.setmetatable(a[i], {__gc = function (x) loadstring("A=A+1")() end}) + end + _G.A = 0 + a = 0 + while 1 do + if xpcall(collectgarbage, function (s) a=a+1 end) then + break -- stop if no more errors + end + end + assert(a == 10) -- number of errors + assert(A == 10) -- number of normal collections +end +------------------------------------------------------------------------- +-- test for userdata vals +do + local a = {}; local lim = 30 + for i=0,lim do a[i] = T.pushuserdata(i) end + for i=0,lim do assert(T.udataval(a[i]) == i) end + for i=0,lim do assert(T.pushuserdata(i) == a[i]) end + for i=0,lim do a[a[i]] = i end + for i=0,lim do a[T.pushuserdata(i)] = i end + assert(type(tostring(a[1])) == "string") +end + + +------------------------------------------------------------------------- +-- testing multiple states +T.closestate(T.newstate()); +L1 = T.newstate() +assert(L1) +assert(pack(T.doremote(L1, "function f () return 'alo', 3 end; f()")).n == 0) + +a, b = T.doremote(L1, "return f()") +assert(a == 'alo' and b == '3') + +T.doremote(L1, "_ERRORMESSAGE = nil") +-- error: `sin' is not defined +a, b = T.doremote(L1, "return sin(1)") +assert(a == nil and b == 2) -- 2 == run-time error + +-- error: syntax error +a, b, c = T.doremote(L1, "return a+") +assert(a == nil and b == 3 and type(c) == "string") -- 3 == syntax error + +T.loadlib(L1) +a, b = T.doremote(L1, [[ + a = strlibopen() + a = packageopen() + a = baselibopen(); assert(a == _G and require("_G") == a) + a = iolibopen(); assert(type(a.read) == "function") + assert(require("io") == a) + a = tablibopen(); assert(type(a.insert) == "function") + a = dblibopen(); assert(type(a.getlocal) == "function") + a = mathlibopen(); assert(type(a.sin) == "function") + return string.sub('okinama', 1, 2) +]]) +assert(a == "ok") + +T.closestate(L1); + +L1 = T.newstate() +T.loadlib(L1) +T.doremote(L1, "a = {}") +T.testC(L1, [[pushstring a; gettable G; pushstring x; pushnum 1; + settable -3]]) +assert(T.doremote(L1, "return a.x") == "1") + +T.closestate(L1) + +L1 = nil + +print('+') + +------------------------------------------------------------------------- +-- testing memory limits +------------------------------------------------------------------------- +collectgarbage() +T.totalmem(T.totalmem()+5000) -- set low memory limit (+5k) +assert(not pcall(loadstring"local a={}; for i=1,100000 do a[i]=i end")) +T.totalmem(1000000000) -- restore high limit + + +local function stack(x) if x>0 then stack(x-1) end end + +-- test memory errors; increase memory limit in small steps, so that +-- we get memory errors in different parts of a given task, up to there +-- is enough memory to complete the task without errors +function testamem (s, f) + collectgarbage() + stack(10) -- ensure minimum stack size + local M = T.totalmem() + local oldM = M + local a,b = nil + while 1 do + M = M+3 -- increase memory limit in small steps + T.totalmem(M) + a, b = pcall(f) + if a and b then break end -- stop when no more errors + collectgarbage() + if not a and not string.find(b, "memory") then -- `real' error? + T.totalmem(1000000000) -- restore high limit + error(b, 0) + end + end + T.totalmem(1000000000) -- restore high limit + print("\nlimit for " .. s .. ": " .. M-oldM) + return b +end + + +-- testing memory errors when creating a new state + +b = testamem("state creation", T.newstate) +T.closestate(b); -- close new state + + +-- testing threads + +function expand (n,s) + if n==0 then return "" end + local e = string.rep("=", n) + return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n", + e, s, expand(n-1,s), e) +end + +G=0; collectgarbage(); a =collectgarbage("count") +loadstring(expand(20,"G=G+1"))() +assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1) + +testamem("thread creation", function () + return T.doonnewstack("x=1") == 0 -- try to create thread +end) + + +-- testing memory x compiler + +testamem("loadstring", function () + return loadstring("x=1") -- try to do a loadstring +end) + + +local testprog = [[ +local function foo () return end +local t = {"x"} +a = "aaa" +for _, v in ipairs(t) do a=a..v end +return true +]] + +-- testing memory x dofile +_G.a = nil +local t =os.tmpname() +local f = assert(io.open(t, "w")) +f:write(testprog) +f:close() +testamem("dofile", function () + local a = loadfile(t) + return a and a() +end) +assert(os.remove(t)) +assert(_G.a == "aaax") + + +-- other generic tests + +testamem("string creation", function () + local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end) + return (a == 'ablo ablo') +end) + +testamem("dump/undump", function () + local a = loadstring(testprog) + local b = a and string.dump(a) + a = b and loadstring(b) + return a and a() +end) + +local t = os.tmpname() +testamem("file creation", function () + local f = assert(io.open(t, 'w')) + assert (not io.open"nomenaoexistente") + io.close(f); + return not loadfile'nomenaoexistente' +end) +assert(os.remove(t)) + +testamem("table creation", function () + local a, lim = {}, 10 + for i=1,lim do a[i] = i; a[i..'a'] = {} end + return (type(a[lim..'a']) == 'table' and a[lim] == lim) +end) + +local a = 1 +close = nil +testamem("closure creation", function () + function close (b,c) + return function (x) return a+b+c+x end + end + return (close(2,3)(4) == 10) +end) + +testamem("coroutines", function () + local a = coroutine.wrap(function () + coroutine.yield(string.rep("a", 10)) + return {} + end) + assert(string.len(a()) == 10) + return a() +end) + +print'+' + +-- testing some auxlib functions +assert(T.gsub("alo.alo.uhuh.", ".", "//") == "alo//alo//uhuh//") +assert(T.gsub("alo.alo.uhuh.", "alo", "//") == "//.//.uhuh.") +assert(T.gsub("", "alo", "//") == "") +assert(T.gsub("...", ".", "/.") == "/././.") +assert(T.gsub("...", "...", "") == "") + + +print'OK' + diff --git a/src/test/resources/bytecode-compiler/lua5.2/api.luac b/src/test/resources/bytecode-compiler/lua5.2/api.luac new file mode 100644 index 0000000000000000000000000000000000000000..05214f8d42edd9aed56fa6019c0f6264f6c59224 GIT binary patch literal 36216 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 K$cp-}JpMn6W>> cannot load dynamic library <<<\n\a') + print(err, when) +else + f() -- open library + assert(require("lib1") == lib1) + collectgarbage() + assert(lib1.id("x") == "x") + f = assert(package.loadlib("libs/lib1.so", p.."anotherfunc")) + assert(f(10, 20) == "1020\n") + f, err, when = package.loadlib("libs/lib1.so", p.."xuxu") + assert(not f and type(err) == "string" and when == "init") + package.cpath = "libs/?.so" + require"lib2" + assert(lib2.id("x") == "x") + local fs = require"lib1.sub" + assert(fs == lib1.sub and next(lib1.sub) == nil) + module("lib2", package.seeall) + f = require"-lib2" + assert(f.id("x") == "x" and _M == f and _NAME == "lib2") + module("lib1.sub", package.seeall) + assert(_M == fs) + setfenv(1, _G) + +end +f, err, when = package.loadlib("donotexist", p.."xuxu") +assert(not f and type(err) == "string" and (when == "open" or when == "absent")) + + +-- testing preload + +do + local p = package + package = {} + --[[p.preload.pl = function (...) + module(...) + function xuxu (x) return x+20 end + end]] + + require"pl" + assert(require"pl" == pl) + assert(pl.xuxu(10) == 30) + + package = p + assert(type(package.path) == "string") +end + + + +end --] + +print('+') + +print("testing assignments, logical operators, and constructors") + +local res, res2 = 27 + +a, b = 1, 2+3 +assert(a==1 and b==5) +a={} +function f() return 10, 11, 12 end +a.x, b, a[1] = 1, 2, f() +assert(a.x==1 and b==2 and a[1]==10) +a[f()], b, a[f()+3] = f(), a, 'x' +assert(a[10] == 10 and b == a and a[13] == 'x') + +do + local f = function (n) local x = {}; for i=1,n do x[i]=i end; + return unpack(x) end; + local a,b,c + a,b = 0, f(1) + assert(a == 0 and b == 1) + A,b = 0, f(1) + assert(A == 0 and b == 1) + a,b,c = 0,5,f(4) + assert(a==0 and b==5 and c==1) + a,b,c = 0,5,f(0) + assert(a==0 and b==5 and c==nil) +end + + +a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6 +assert(not a and b and c and d==6) + +d = 20 +a, b, c, d = f() +assert(a==10 and b==11 and c==12 and d==nil) +a,b = f(), 1, 2, 3, f() +assert(a==10 and b==1) + +assert(ab == true) +assert((10 and 2) == 2) +assert((10 or 2) == 10) +assert((10 or assert(nil)) == 10) +assert(not (nil and assert(nil))) +assert((nil or "alo") == "alo") +assert((nil and 10) == nil) +assert((false and 10) == false) +assert((true or 10) == true) +assert((false or 10) == 10) +assert(false ~= nil) +assert(nil ~= false) +assert(not nil == true) +assert(not not nil == false) +assert(not not 1 == true) +assert(not not a == true) +assert(not not (6 or nil) == true) +assert(not not (nil and 56) == false) +assert(not not (nil and true) == false) +print('+') + +a = {} +a[true] = 20 +a[false] = 10 +assert(a[1<2] == 20 and a[1>2] == 10) + +function f(a) return a end + +local a = {} +for i=3000,-3000,-1 do a[i] = i; end +a[10e30] = "alo"; a[true] = 10; a[false] = 20 +assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10) +for i=3000,-3000,-1 do assert(a[i] == i); end +a[print] = assert +a[f] = print +a[a] = a +assert(a[a][a][a][a][print] == assert) +a[print](a[a[f]] == a[print]) +a = nil + +a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'} +a, a.x, a.y = a, a[-3] +assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y) +a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2 +a[1].alo(a[2]==10 and b==10 and c==print) + +a[2^31] = 10; a[2^31+1] = 11; a[-2^31] = 12; +a[2^32] = 13; a[-2^32] = 14; a[2^32+1] = 15; a[10^33] = 16; + +assert(a[2^31] == 10 and a[2^31+1] == 11 and a[-2^31] == 12 and + a[2^32] == 13 and a[-2^32] == 14 and a[2^32+1] == 15 and + a[10^33] == 16) + +a = nil + + +do + local a,i,j,b + a = {'a', 'b'}; i=1; j=2; b=a + i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i + assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and + b[3] == 1) +end + +print('OK') + +return res diff --git a/src/test/resources/bytecode-compiler/lua5.2/attrib.luac b/src/test/resources/bytecode-compiler/lua5.2/attrib.luac new file mode 100644 index 0000000000000000000000000000000000000000..93ada7caf282983a31124e18a6ab8bf2072e3564 GIT binary patch literal 17324 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/big.lua b/src/test/resources/bytecode-compiler/lua5.2/big.lua new file mode 100644 index 00000000..92612880 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/big.lua @@ -0,0 +1,381 @@ +print "testing string length overflow" + +local longs = string.rep("\0", 2^25) +local function catter (i) + return assert(loadstring( + string.format("return function(a) return a%s end", + string.rep("..a", i-1))))() +end +rep129 = catter(129) +local a, b = pcall(rep129, longs) +assert(not a and string.find(b, "overflow")) +print('+') + + +require "checktable" + +--[[ lots of empty lines (to force SETLINEW) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +--]] + + +a,b = nil,nil +while not b do +if a then +b = { -- lots of strings (to force JMPW and PUSHCONSTANTW) +"n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", +"n11", "n12", "j301", "j302", "j303", "j304", "j305", "j306", "j307", "j308", +"j309", "a310", "n311", "n312", "n313", "n314", "n315", "n316", "n317", "n318", +"n319", "n320", "n321", "n322", "n323", "n324", "n325", "n326", "n327", "n328", +"a329", "n330", "n331", "n332", "n333", "n334", "n335", "n336", "n337", "n338", +"n339", "n340", "n341", "z342", "n343", "n344", "n345", "n346", "n347", "n348", +"n349", "n350", "n351", "n352", "r353", "n354", "n355", "n356", "n357", "n358", +"n359", "n360", "n361", "n362", "n363", "n364", "n365", "n366", "z367", "n368", +"n369", "n370", "n371", "n372", "n373", "n374", "n375", "a376", "n377", "n378", +"n379", "n380", "n381", "n382", "n383", "n384", "n385", "n386", "n387", "n388", +"n389", "n390", "n391", "n392", "n393", "n394", "n395", "n396", "n397", "n398", +"n399", "n400", "n13", "n14", "n15", "n16", "n17", "n18", "n19", "n20", +"n21", "n22", "n23", "a24", "n25", "n26", "n27", "n28", "n29", "j30", +"n31", "n32", "n33", "n34", "n35", "n36", "n37", "n38", "n39", "n40", +"n41", "n42", "n43", "n44", "n45", "n46", "n47", "n48", "n49", "n50", +"n51", "n52", "n53", "n54", "n55", "n56", "n57", "n58", "n59", "n60", +"n61", "n62", "n63", "n64", "n65", "a66", "z67", "n68", "n69", "n70", +"n71", "n72", "n73", "n74", "n75", "n76", "n77", "n78", "n79", "n80", +"n81", "n82", "n83", "n84", "n85", "n86", "n87", "n88", "n89", "n90", +"n91", "n92", "n93", "n94", "n95", "n96", "n97", "n98", "n99", "n100", +"n201", "n202", "n203", "n204", "n205", "n206", "n207", "n208", "n209", "n210", +"n211", "n212", "n213", "n214", "n215", "n216", "n217", "n218", "n219", "n220", +"n221", "n222", "n223", "n224", "n225", "n226", "n227", "n228", "n229", "n230", +"n231", "n232", "n233", "n234", "n235", "n236", "n237", "n238", "n239", "a240", +"a241", "a242", "a243", "a244", "a245", "a246", "a247", "a248", "a249", "n250", +"n251", "n252", "n253", "n254", "n255", "n256", "n257", "n258", "n259", "n260", +"n261", "n262", "n263", "n264", "n265", "n266", "n267", "n268", "n269", "n270", +"n271", "n272", "n273", "n274", "n275", "n276", "n277", "n278", "n279", "n280", +"n281", "n282", "n283", "n284", "n285", "n286", "n287", "n288", "n289", "n290", +"n291", "n292", "n293", "n294", "n295", "n296", "n297", "n298", "n299" +; x=23} +else a = 1 end + + +end + +assert(b.x == 23) +print('+') + +stat(b) + +repeat +a = { +n1 = 1.5, n2 = 2.5, n3 = 3.5, n4 = 4.5, n5 = 5.5, n6 = 6.5, n7 = 7.5, +n8 = 8.5, n9 = 9.5, n10 = 10.5, n11 = 11.5, n12 = 12.5, +j301 = 301.5, j302 = 302.5, j303 = 303.5, j304 = 304.5, j305 = 305.5, +j306 = 306.5, j307 = 307.5, j308 = 308.5, j309 = 309.5, a310 = 310.5, +n311 = 311.5, n312 = 312.5, n313 = 313.5, n314 = 314.5, n315 = 315.5, +n316 = 316.5, n317 = 317.5, n318 = 318.5, n319 = 319.5, n320 = 320.5, +n321 = 321.5, n322 = 322.5, n323 = 323.5, n324 = 324.5, n325 = 325.5, +n326 = 326.5, n327 = 327.5, n328 = 328.5, a329 = 329.5, n330 = 330.5, +n331 = 331.5, n332 = 332.5, n333 = 333.5, n334 = 334.5, n335 = 335.5, +n336 = 336.5, n337 = 337.5, n338 = 338.5, n339 = 339.5, n340 = 340.5, +n341 = 341.5, z342 = 342.5, n343 = 343.5, n344 = 344.5, n345 = 345.5, +n346 = 346.5, n347 = 347.5, n348 = 348.5, n349 = 349.5, n350 = 350.5, +n351 = 351.5, n352 = 352.5, r353 = 353.5, n354 = 354.5, n355 = 355.5, +n356 = 356.5, n357 = 357.5, n358 = 358.5, n359 = 359.5, n360 = 360.5, +n361 = 361.5, n362 = 362.5, n363 = 363.5, n364 = 364.5, n365 = 365.5, +n366 = 366.5, z367 = 367.5, n368 = 368.5, n369 = 369.5, n370 = 370.5, +n371 = 371.5, n372 = 372.5, n373 = 373.5, n374 = 374.5, n375 = 375.5, +a376 = 376.5, n377 = 377.5, n378 = 378.5, n379 = 379.5, n380 = 380.5, +n381 = 381.5, n382 = 382.5, n383 = 383.5, n384 = 384.5, n385 = 385.5, +n386 = 386.5, n387 = 387.5, n388 = 388.5, n389 = 389.5, n390 = 390.5, +n391 = 391.5, n392 = 392.5, n393 = 393.5, n394 = 394.5, n395 = 395.5, +n396 = 396.5, n397 = 397.5, n398 = 398.5, n399 = 399.5, n400 = 400.5, +n13 = 13.5, n14 = 14.5, n15 = 15.5, n16 = 16.5, n17 = 17.5, +n18 = 18.5, n19 = 19.5, n20 = 20.5, n21 = 21.5, n22 = 22.5, +n23 = 23.5, a24 = 24.5, n25 = 25.5, n26 = 26.5, n27 = 27.5, +n28 = 28.5, n29 = 29.5, j30 = 30.5, n31 = 31.5, n32 = 32.5, +n33 = 33.5, n34 = 34.5, n35 = 35.5, n36 = 36.5, n37 = 37.5, +n38 = 38.5, n39 = 39.5, n40 = 40.5, n41 = 41.5, n42 = 42.5, +n43 = 43.5, n44 = 44.5, n45 = 45.5, n46 = 46.5, n47 = 47.5, +n48 = 48.5, n49 = 49.5, n50 = 50.5, n51 = 51.5, n52 = 52.5, +n53 = 53.5, n54 = 54.5, n55 = 55.5, n56 = 56.5, n57 = 57.5, +n58 = 58.5, n59 = 59.5, n60 = 60.5, n61 = 61.5, n62 = 62.5, +n63 = 63.5, n64 = 64.5, n65 = 65.5, a66 = 66.5, z67 = 67.5, +n68 = 68.5, n69 = 69.5, n70 = 70.5, n71 = 71.5, n72 = 72.5, +n73 = 73.5, n74 = 74.5, n75 = 75.5, n76 = 76.5, n77 = 77.5, +n78 = 78.5, n79 = 79.5, n80 = 80.5, n81 = 81.5, n82 = 82.5, +n83 = 83.5, n84 = 84.5, n85 = 85.5, n86 = 86.5, n87 = 87.5, +n88 = 88.5, n89 = 89.5, n90 = 90.5, n91 = 91.5, n92 = 92.5, +n93 = 93.5, n94 = 94.5, n95 = 95.5, n96 = 96.5, n97 = 97.5, +n98 = 98.5, n99 = 99.5, n100 = 100.5, n201 = 201.5, n202 = 202.5, +n203 = 203.5, n204 = 204.5, n205 = 205.5, n206 = 206.5, n207 = 207.5, +n208 = 208.5, n209 = 209.5, n210 = 210.5, n211 = 211.5, n212 = 212.5, +n213 = 213.5, n214 = 214.5, n215 = 215.5, n216 = 216.5, n217 = 217.5, +n218 = 218.5, n219 = 219.5, n220 = 220.5, n221 = 221.5, n222 = 222.5, +n223 = 223.5, n224 = 224.5, n225 = 225.5, n226 = 226.5, n227 = 227.5, +n228 = 228.5, n229 = 229.5, n230 = 230.5, n231 = 231.5, n232 = 232.5, +n233 = 233.5, n234 = 234.5, n235 = 235.5, n236 = 236.5, n237 = 237.5, +n238 = 238.5, n239 = 239.5, a240 = 240.5, a241 = 241.5, a242 = 242.5, +a243 = 243.5, a244 = 244.5, a245 = 245.5, a246 = 246.5, a247 = 247.5, +a248 = 248.5, a249 = 249.5, n250 = 250.5, n251 = 251.5, n252 = 252.5, +n253 = 253.5, n254 = 254.5, n255 = 255.5, n256 = 256.5, n257 = 257.5, +n258 = 258.5, n259 = 259.5, n260 = 260.5, n261 = 261.5, n262 = 262.5, +n263 = 263.5, n264 = 264.5, n265 = 265.5, n266 = 266.5, n267 = 267.5, +n268 = 268.5, n269 = 269.5, n270 = 270.5, n271 = 271.5, n272 = 272.5, +n273 = 273.5, n274 = 274.5, n275 = 275.5, n276 = 276.5, n277 = 277.5, +n278 = 278.5, n279 = 279.5, n280 = 280.5, n281 = 281.5, n282 = 282.5, +n283 = 283.5, n284 = 284.5, n285 = 285.5, n286 = 286.5, n287 = 287.5, +n288 = 288.5, n289 = 289.5, n290 = 290.5, n291 = 291.5, n292 = 292.5, +n293 = 293.5, n294 = 294.5, n295 = 295.5, n296 = 296.5, n297 = 297.5, +n298 = 298.5, n299 = 299.5, j300 = 300} or 1 +until 1 + +assert(a.n299 == 299.5) +xxx = 1 +assert(xxx == 1) + +stat(a) + +function a:findfield (f) + local i,v = next(self, nil) + while i ~= f do + if not i then return end + i,v = next(self, i) + end + return v +end + +local ii = 0 +i = 1 +while b[i] do + local r = a:findfield(b[i]); + assert(a[b[i]] == r) + ii = math.max(ii,i) + i = i+1 +end + +assert(ii == 299) + +function xxxx (x) coroutine.yield('b'); return ii+x end + +assert(xxxx(10) == 309) + +a = nil +b = nil +a1 = nil + +print("tables with table indices:") +i = 1; a={} +while i <= 1023 do a[{}] = i; i=i+1 end +stat(a) +a = nil + +print("tables with function indices:") +a={} +for i=1,511 do local x; a[function () return x end] = i end +stat(a) +a = nil + +print'OK' + +return 'a' diff --git a/src/test/resources/bytecode-compiler/lua5.2/big.luac b/src/test/resources/bytecode-compiler/lua5.2/big.luac new file mode 100644 index 0000000000000000000000000000000000000000..e0db0a00b93052a68a7f45e52b2bf7e954067137 GIT binary patch literal 16607 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/calls.lua b/src/test/resources/bytecode-compiler/lua5.2/calls.lua new file mode 100644 index 00000000..ebd1e483 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/calls.lua @@ -0,0 +1,294 @@ +print("testing functions and calls") + +-- get the opportunity to test 'type' too ;) + +assert(type(1<2) == 'boolean') +assert(type(true) == 'boolean' and type(false) == 'boolean') +assert(type(nil) == 'nil' and type(-3) == 'number' and type'x' == 'string' and + type{} == 'table' and type(type) == 'function') + +assert(type(assert) == type(print)) +f = nil +function f (x) return a:x (x) end +assert(type(f) == 'function') + + +-- testing local-function recursion +fact = false +do + local res = 1 + local function fact (n) + if n==0 then return res + else return n*fact(n-1) + end + end + assert(fact(5) == 120) +end +assert(fact == false) + +-- testing declarations +a = {i = 10} +self = 20 +function a:x (x) return x+self.i end +function a.y (x) return x+self end + +assert(a:x(1)+10 == a.y(1)) + +a.t = {i=-100} +a["t"].x = function (self, a,b) return self.i+a+b end + +assert(a.t:x(2,3) == -95) + +do + local a = {x=0} + function a:add (x) self.x, a.y = self.x+x, 20; return self end + assert(a:add(10):add(20):add(30).x == 60 and a.y == 20) +end + +local a = {b={c={}}} + +function a.b.c.f1 (x) return x+1 end +function a.b.c:f2 (x,y) self[x] = y end +assert(a.b.c.f1(4) == 5) +a.b.c:f2('k', 12); assert(a.b.c.k == 12) + +print('+') + +t = nil -- 'declare' t +function f(a,b,c) local d = 'a'; t={a,b,c,d} end + +f( -- this line change must be valid + 1,2) +assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a') +f(1,2, -- this one too + 3,4) +assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a') + +function fat(x) + if x <= 1 then return 1 + else return x*loadstring("return fat(" .. x-1 .. ")")() + end +end + +assert(loadstring "loadstring 'assert(fat(6)==720)' () ")() +a = loadstring('return fat(5), 3') +a,b = a() +assert(a == 120 and b == 3) +print('+') + +function err_on_n (n) + if n==0 then error(); exit(1); + else err_on_n (n-1); exit(1); + end +end + +do + function dummy (n) + if n > 0 then + assert(not pcall(err_on_n, n)) + dummy(n-1) + end + end +end + +dummy(10) + +function deep (n) + if n>0 then deep(n-1) end +end +deep(10) +deep(200) + +-- testing tail call +function deep (n) if n>0 then return deep(n-1) else return 101 end end +assert(deep(30000) == 101) +a = {} +function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end +assert(a:deep(30000) == 101) + +print('+') + + +a = nil +(function (x) a=x end)(23) +assert(a == 23 and (function (x) return x*2 end)(20) == 40) + + +local x,y,z,a +a = {}; lim = 2000 +for i=1, lim do a[i]=i end +assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim) +x = unpack(a) +assert(x == 1) +x = {unpack(a)} +assert(table.getn(x) == lim and x[1] == 1 and x[lim] == lim) +x = {unpack(a, lim-2)} +assert(table.getn(x) == 3 and x[1] == lim-2 and x[3] == lim) +x = {unpack(a, 10, 6)} +assert(next(x) == nil) -- no elements +x = {unpack(a, 11, 10)} +assert(next(x) == nil) -- no elements +x,y = unpack(a, 10, 10) +assert(x == 10 and y == nil) +x,y,z = unpack(a, 10, 11) +assert(x == 10 and y == 11 and z == nil) +a,x = unpack{1} +assert(a==1 and x==nil) +a,x = unpack({1,2}, 1, 1) +assert(a==1 and x==nil) + + +-- testing closures + +-- fixed-point operator +Y = function (le) + local function a (f) + return le(function (x) return f(f)(x) end) + end + return a(a) + end + + +-- non-recursive factorial + +F = function (f) + return function (n) + if n == 0 then return 1 + else return n*f(n-1) end + end + end + +fat = Y(F) + +assert(fat(0) == 1 and fat(4) == 24 and Y(F)(5)==5*Y(F)(4)) + +local function g (z) + local function f (a,b,c,d) + return function (x,y) return a+b+c+d+a+x+y+z end + end + return f(z,z+1,z+2,z+3) +end + +f = g(10) +assert(f(9, 16) == 10+11+12+13+10+9+16+10) + +Y, F, f = nil +print('+') + +-- testing multiple returns + +function unlpack (t, i) + i = i or 1 + if (i <= table.getn(t)) then + return t[i], unlpack(t, i+1) + end +end + +function equaltab (t1, t2) + assert(table.getn(t1) == table.getn(t2)) + for i,v1 in ipairs(t1) do + assert(v1 == t2[i]) + end +end + +--[[local function pack (...) + local x = {...} + x.n = select('#', ...) + return x +end]] + +function f() return 1,2,30,4 end +function ret2 (a,b) return a,b end + +local a,b,c,d = unlpack{1,2,3} +assert(a==1 and b==2 and c==3 and d==nil) +a = {1,2,3,4,false,10,'alo',false,assert} +equaltab(pack(unlpack(a)), a) +equaltab(pack(unlpack(a), -1), {1,-1}) +a,b,c,d = ret2(f()), ret2(f()) +assert(a==1 and b==1 and c==2 and d==nil) +a,b,c,d = unlpack(pack(ret2(f()), ret2(f()))) +assert(a==1 and b==1 and c==2 and d==nil) +a,b,c,d = unlpack(pack(ret2(f()), (ret2(f())))) +assert(a==1 and b==1 and c==nil and d==nil) + +a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}} +assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b") + + +-- testing calls with 'incorrect' arguments +rawget({}, "x", 1) +rawset({}, "x", 1, 2) +assert(math.sin(1,2) == math.sin(1)) +table.sort({10,9,8,4,19,23,0,0}, function (a,b) return abWYWvG7RWFZ(lqU)Gp)-X()5-oonEHxB$GbR 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" then + assert(val==nil) + else + assert(t[key] == val) + local mp = T.hash(key, t) + if l[i] then + assert(l[i] == mp) + elseif mp ~= i then + l[i] = mp + else -- list head + l[mp] = {mp} -- first element + while next do + assert(ff <= next and next < hsize) + if l[next] then assert(l[next] == mp) else l[next] = mp end + table.insert(l[mp], next) + key,val,next = T.querytab(t, next) + assert(key) + end + end + end + end + l.asize = asize; l.hsize = hsize; l.ff = ff + return l +end + +function mostra (t) + local asize, hsize, ff = T.querytab(t) + print(asize, hsize, ff) + print'------' + for i=0,asize-1 do + local _, v = T.querytab(t, i) + print(string.format("[%d] -", i), v) + end + print'------' + for i=0,hsize-1 do + print(i, T.querytab(t, i+asize)) + end + print'-------------' +end + +function stat (t) + t = checktable(t) + local nelem, nlist = 0, 0 + local maxlist = {} + for i=0,t.hsize-1 do + if type(t[i]) == 'table' then + local n = table.getn(t[i]) + nlist = nlist+1 + nelem = nelem + n + if not maxlist[n] then maxlist[n] = 0 end + maxlist[n] = maxlist[n]+1 + end + end + print(string.format("hsize=%d elements=%d load=%.2f med.len=%.2f (asize=%d)", + t.hsize, nelem, nelem/t.hsize, nelem/nlist, t.asize)) + for i=1,table.getn(maxlist) do + local n = maxlist[i] or 0 + print(string.format("%5d %10d %.2f%%", i, n, n*100/nlist)) + end +end + diff --git a/src/test/resources/bytecode-compiler/lua5.2/checktable.luac b/src/test/resources/bytecode-compiler/lua5.2/checktable.luac new file mode 100644 index 0000000000000000000000000000000000000000..4c34862994f9cb23c3376c705b17af0630ca9db4 GIT binary patch literal 3750 zcmb7HTaVL56h31+4rI%%w=B>tMrz7J%NFQOZMlpSHR%OVRaMm@6hTV7Y($b>HdzXN z*>NJ4r?yCa=D*lM0*R`>pz3p9dFo54)bEV#tm7_Qi1gWWzL`1ac;=illl^B`8()*K zENdI>{bfgCUxAXJu!?BfB&jMTtt&}kKvY*s%u`6{Qx!>6Aw@Hige_f~CYOqeR0;F8 zk@63quY@t%h=wJlxnU{T=mSqy9*;(^=dJXk_DbXXs~*}W_)9gia(MUpjXs7NXnsT1 z6kb;ry(`!HjqloCG^?}J>#sD(LKjb95E(4;2e^{7lk5;Hv1~k+S6W@zc?HeO*H^uj zTTpk%={A;}M!$7q2{e(=&)s;+4U2Q_)k#Ym7NZ#~U;6a?x5SG%3kceM9j_`}smTCN zU>!+_DX4dSqMt%3!Vt^gPOGYcG3CHg@bkAaS#`q~(-d08z)*I@rJ89NknJMWV7+ri zNecUs)B91uewf&g3W=h6Nlh!vQ$o+1=dpx6OOKIzBApe3=GDN?_S>eU{C(G!rm__F zV`iuX+rNl*F%YT>f9^n@Co|7;htNHQ{kpb41-}fN{VNC)eiYP~=z|`+Sy5DQP5XCz zW0AINJ^O-z;IS8!(tf6O)$V6(Lf+ISd?<)vu4MrM?t;V=q*N(n2O_8H_8MK&_=KI1Hzk|i&2wRFD&4(Rb+Cc zaf+nz0%Z0)jh&FQ*aeQk5&t~cgYk?Px4_Hjdm1#W4o*eQUo182&QY>Bog)qL6Y^H2f|vNM4Bm#E#Zhnx z=Fm>#9mrW61Gg1;KHE<4F4}3l2YD;-*bh?Rh~aO?PQ>ZLnkUmGVK&l*nN+&4m`xC_ zgUFW zJ!Hd%qS`>xSLFfS9VldFnd$OP6cFH_}5zStkzf=a56Tv|8E z$jEXoG)(NQGNnn@&y|XGcSw0;r%}f^M?6{MjO1|1-Xt+A`lZg?n0tIiUqt)2mg5sS zKC^R}fa_{^;!4ShyjiM#3|@!dsZ!H%ytdczy8WIO+Dnb*snXnu1;^=l&AGPMjigz9 z3BZuUk%jn9z-M#m{iahoehd#xE0vO(aY3Sw1V3~HW-qQ-17b(g_yjWJByegIoCcT0 z8E|Px$k;Pt{b^JnCt%&x3~G=!Lo)6&^gj)sqc4pwAU_LOALqnm&0w!>pvya>TVtl( z>h;GApPHSgrN4QG;R=e17tR zXH6u0#zaC#Cb-y1ATq(VlqHTDj%fYog$4X^!802|d=uk|&jJhM60m9pFY+wFry`f` OBLHtlQmW9Lbmd>+xAa~B literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/closure.lua b/src/test/resources/bytecode-compiler/lua5.2/closure.lua new file mode 100644 index 00000000..cfabcc97 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/closure.lua @@ -0,0 +1,422 @@ +print "testing closures and coroutines" + +local A,B = 0,{g=10} +function f(x) + local a = {} + for i=1,1000 do + local y = 0 + do + a[i] = function () B.g = B.g+1; y = y+x; return y+A end + end + end + local dummy = function () return a[A] end + collectgarbage() + A = 1; assert(dummy() == a[1]); A = 0; + assert(a[1]() == x) + assert(a[3]() == x) + collectgarbage() + assert(B.g == 12) + return a +end + +a = f(10) +-- force a GC in this level +local x = {[1] = {}} -- to detect a GC +setmetatable(x, {__mode = 'kv'}) +while x[1] do -- repeat until GC + local a = A..A..A..A -- create garbage + A = A+1 +end +assert(a[1]() == 20+A) +assert(a[1]() == 30+A) +assert(a[2]() == 10+A) +collectgarbage() +assert(a[2]() == 20+A) +assert(a[2]() == 30+A) +assert(a[3]() == 20+A) +assert(a[8]() == 10+A) +assert(getmetatable(x).__mode == 'kv') +assert(B.g == 19) + +-- testing closures with 'for' control variable +a = {} +for i=1,10 do + a[i] = {set = function(x) i=x end, get = function () return i end} + if i == 3 then break end +end +assert(a[4] == nil) +a[1].set(10) +assert(a[2].get() == 2) +a[2].set('a') +assert(a[3].get() == 3) +assert(a[2].get() == 'a') + +a = {} +for i, k in pairs{'a', 'b'} do + a[i] = {set = function(x, y) i=x; k=y end, + get = function () return i, k end} + if i == 2 then break end +end +a[1].set(10, 20) +local r,s = a[2].get() +assert(r == 2 and s == 'b') +r,s = a[1].get() +assert(r == 10 and s == 20) +a[2].set('a', 'b') +r,s = a[2].get() +assert(r == "a" and s == "b") + + +-- testing closures with 'for' control variable x break +for i=1,3 do + f = function () return i end + break +end +assert(f() == 1) + +for k, v in pairs{"a", "b"} do + f = function () return k, v end + break +end +assert(({f()})[1] == 1) +assert(({f()})[2] == "a") + + +-- testing closure x break x return x errors + +local b +function f(x) + local first = 1 + while 1 do + if x == 3 and not first then return end + local a = 'xuxu' + b = function (op, y) + if op == 'set' then + a = x+y + else + return a + end + end + if x == 1 then do break end + elseif x == 2 then return + else if x ~= 3 then error() end + end + first = nil + end +end + +for i=1,3 do + f(i) + assert(b('get') == 'xuxu') + b('set', 10); assert(b('get') == 10+i) + b = nil +end + +pcall(f, 4); +assert(b('get') == 'xuxu') +b('set', 10); assert(b('get') == 14) + + +local w +-- testing multi-level closure +function f(x) + return function (y) + return function (z) return w+x+y+z end + end +end + +y = f(10) +w = 1.345 +assert(y(20)(30) == 60+w) + +-- testing closures x repeat-until + +local a = {} +local i = 1 +repeat + local x = i + a[i] = function () i = x+1; return x end +until i > 10 or a[i]() ~= x +assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4) + +print'+' + + +-- test for correctly closing upvalues in tail calls of vararg functions +local function t () + local function c(a,b) assert(a=="test" and b=="OK") end + local function v(f) c("test", f() ~= 1 and "FAILED" or "OK") end + local x = 1 + return v(function() return x end) +end +t() + + +-- coroutine tests + +local f + +assert(coroutine.running() == nil) + + +-- tests for global environment + +local function foo (a) + setfenv(0, a) + coroutine.yield(getfenv()) + assert(getfenv(0) == a) + assert(getfenv(1) == _G) + assert(getfenv(loadstring"") == a) + return getfenv() +end + +f = coroutine.wrap(foo) +local a = {} +assert(f(a) == _G) +local a,b = pcall(f) +assert(a and b == _G) + + +-- tests for multiple yield/resume arguments + +local function eqtab (t1, t2) + assert(table.getn(t1) == table.getn(t2)) + for i,v in ipairs(t1) do + assert(t2[i] == v) + end +end + +_G.x = nil -- declare x +--[[function foo (a, ...) + assert(coroutine.running() == f) + assert(coroutine.status(f) == "running") + local arg = {...} + for i=1,table.getn(arg) do + _G.x = {coroutine.yield(unpack(arg[i]))} + end + return unpack(a) +end]] + +f = coroutine.create(foo) +assert(type(f) == "thread" and coroutine.status(f) == "suspended") +assert(string.find(tostring(f), "thread")) +local s,a,b,c,d +s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'}) +assert(s and a == nil and coroutine.status(f) == "suspended") +s,a,b,c,d = coroutine.resume(f) +eqtab(_G.x, {}) +assert(s and a == 1 and b == nil) +s,a,b,c,d = coroutine.resume(f, 1, 2, 3) +eqtab(_G.x, {1, 2, 3}) +assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil) +s,a,b,c,d = coroutine.resume(f, "xuxu") +eqtab(_G.x, {"xuxu"}) +assert(s and a == 1 and b == 2 and c == 3 and d == nil) +assert(coroutine.status(f) == "dead") +s, a = coroutine.resume(f, "xuxu") +assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") + + +-- yields in tail calls +local function foo (i) return coroutine.yield(i) end +f = coroutine.wrap(function () + for i=1,10 do + assert(foo(i) == _G.x) + end + return 'a' +end) +for i=1,10 do _G.x = i; assert(f(i) == i) end +_G.x = 'xuxu'; assert(f('xuxu') == 'a') + +-- recursive +function pf (n, i) + coroutine.yield(n) + pf(n*i, i+1) +end + +f = coroutine.wrap(pf) +local s=1 +for i=1,10 do + assert(f(1, 1) == s) + s = s*i +end + +-- sieve +function gen (n) + return coroutine.wrap(function () + for i=2,n do coroutine.yield(i) end + end) +end + + +function filter (p, g) + return coroutine.wrap(function () + while 1 do + local n = g() + if n == nil then return end + if math.mod(n, p) ~= 0 then coroutine.yield(n) end + end + end) +end + +local x = gen(100) +local a = {} +while 1 do + local n = x() + if n == nil then break end + table.insert(a, n) + x = filter(n, x) +end + +assert(table.getn(a) == 25 and a[table.getn(a)] == 97) + + +-- errors in coroutines +function foo () + assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1) + assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined) + coroutine.yield(3) + error(foo) +end + +function goo() foo() end +x = coroutine.wrap(goo) +assert(x() == 3) +local a,b = pcall(x) +assert(not a and b == foo) + +x = coroutine.create(goo) +a,b = coroutine.resume(x) +assert(a and b == 3) +a,b = coroutine.resume(x) +assert(not a and b == foo and coroutine.status(x) == "dead") +a,b = coroutine.resume(x) +assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") + + +-- co-routines x for loop +function all (a, n, k) + if k == 0 then coroutine.yield(a) + else + for i=1,n do + a[k] = i + all(a, n, k-1) + end + end +end + +local a = 0 +for t in coroutine.wrap(function () all({}, 5, 4) end) do + a = a+1 +end +assert(a == 5^4) + + +-- access to locals of collected corroutines +local C = {}; setmetatable(C, {__mode = "kv"}) +local x = coroutine.wrap (function () + local a = 10 + local function f () a = a+10; return a end + while true do + a = a+1 + coroutine.yield(f) + end + end) + +C[1] = x; + +local f = x() +assert(f() == 21 and x()() == 32 and x() == f) +x = nil +collectgarbage() +assert(C[1] == nil) +assert(f() == 43 and f() == 53) + + +-- old bug: attempt to resume itself + +function co_func (current_co) + assert(coroutine.running() == current_co) + assert(coroutine.resume(current_co) == false) + assert(coroutine.resume(current_co) == false) + return 10 +end + +local co = coroutine.create(co_func) +local a,b = coroutine.resume(co, co) +assert(a == true and b == 10) +assert(coroutine.resume(co, co) == false) +assert(coroutine.resume(co, co) == false) + +-- access to locals of erroneous coroutines +local x = coroutine.create (function () + local a = 10 + _G.f = function () a=a+1; return a end + error('x') + end) + +assert(not coroutine.resume(x)) +-- overwrite previous position of local `a' +assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) +assert(_G.f() == 11) +assert(_G.f() == 12) + + +if not T then + (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a') +else + + local turn + + function fact (t, x) + assert(turn == t) + if x == 0 then return 1 + else return x*fact(t, x-1) + end + end + + local A,B,a,b = 0,0,0,0 + + local x = coroutine.create(function () + T.setyhook("", 2) + A = fact("A", 10) + end) + + local y = coroutine.create(function () + T.setyhook("", 3) + B = fact("B", 11) + end) + + while A==0 or B==0 do + if A==0 then turn = "A"; T.resume(x) end + if B==0 then turn = "B"; T.resume(y) end + end + + assert(B/A == 11) +end + + +-- leaving a pending coroutine open +_X = coroutine.wrap(function () + local a = 10 + local x = function () a = a+1 end + coroutine.yield() + end) + +_X() + + +-- coroutine environments +co = coroutine.create(function () + coroutine.yield(getfenv(0)) + return loadstring("return a")() + end) + +a = {a = 15} +debug.setfenv(co, a) +assert(debug.getfenv(co) == a) +assert(select(2, coroutine.resume(co)) == a) +assert(select(2, coroutine.resume(co)) == a.a) + + +print'OK' diff --git a/src/test/resources/bytecode-compiler/lua5.2/closure.luac b/src/test/resources/bytecode-compiler/lua5.2/closure.luac new file mode 100644 index 0000000000000000000000000000000000000000..7ad5a57e34b5d0b792cdabd84a2727cf3134ae2d GIT binary patch literal 20774 zcmcIs3v`@Eb-w>T^>l2GU1qMIwN)k$> zQ;FwkkpWz9l|uF?#G168%c+DE#FM-huSv`NL#RK4x+A`xFG^BSePKAW z44-P{^xAUSZl`aqzP@hg)l$;CBHy6i^Vs)#oc*=+B}#Iu_{sS6Fm_&NR=jv385-2d zy77q@d>Qn6YU(&nO}@+*d?@ieNfbQ!o(kzA;@&zZlELt`Btu6sxNb?#9XaiV!h_Ni zemWug>B0-M&q&!gWqU(|es7J9qE5&Ozu=va<1pb>ju~PbPxP924C?OsZS|6Eo2_b# zIF{Nsy82 z*fg>T6Cw$u;$L)mOxim)3?_DLcX zmQM6Fk+kKux$MJ(Diks6F8Uotzn9Vc4D>N(DkPDk$&j4Rh9dV!XztOFrx!zW;rtx( zK!u`@&OJZ-C~_o)SZFuFlrnNdOOQdE%Oe_gJRR$SyhMzA zxCb^PunGCIVJJjh%Tv9N#16v0r}~>z1hx+v+fA{jl9GyS6&;c25O|x_=_bY_1=+rX zr<)vq&<7uR*A3m>(B0W1o^3lIeLZKIat8;*hmJgS6na`@D55>cNF*Ym)B#;=p9@Pi7LgzHYOghhdwNVu7W`c)Ef2-CSIBwd^~EF+Z<8le+8bKH0v6S6 zu+gR?x>ZG_RU*12bv!Abj0$`X>COIe<9iPF78ka*`mmQ@K>qq$TTL8$5yvRv*o!!p z`2^iwJ>4p* z+D2bBZNmqZYUcX2S-#vH_|mebFAG?Q7qSiXSHtHR$ET{_jB{XHcL%ngTHQ9rr)E6V zt!s`)fj%DBUilbbhFt29u+BvXWU+SuW2>9IRGZ`%wD$>&O^5I4M(kvI!`7c@f9u(W zFxm^}U~3j*RET1nkb_Swgmf;|FGo_bM{-51g)xarT(>RfkV`7p&iG;)$`W!mpAzr! z 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 I^J|3v2b(5|u>b%7 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/code.lua b/src/test/resources/bytecode-compiler/lua5.2/code.lua new file mode 100644 index 00000000..03fb8c1a --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/code.lua @@ -0,0 +1,143 @@ + +if T==nil then + (Message or print)('\a\n >>> testC not active: skipping opcode tests <<<\n\a') + return +end +print "testing code generation and optimizations" + + +-- this code gave an error for the code checker +do + local function f (a) + for k,v,w in a do end + end +end + + +--[[function check (f, ...) + local c = T.listcode(f) + for i=1, select("#", ...) do + -- print(arg[i], c[i]) + assert(string.find(c[i], '- '..select(i, ...)..' *%d')) + end + assert(c[select("#", ...)+2] == nil) +end]] + + +function checkequal (a, b) + a = T.listcode(a) + b = T.listcode(b) + for i = 1, table.getn(a) do + a[i] = string.gsub(a[i], '%b()', '') -- remove line number + b[i] = string.gsub(b[i], '%b()', '') -- remove line number + assert(a[i] == b[i]) + end +end + + +-- some basic instructions +check(function () + (function () end){f()} +end, 'CLOSURE', 'NEWTABLE', 'GETGLOBAL', 'CALL', 'SETLIST', 'CALL', 'RETURN') + + +-- sequence of LOADNILs +check(function () + local a,b,c + local d; local e; + a = nil; d=nil +end, 'RETURN') + + +-- single return +check (function (a,b,c) return a end, 'RETURN') + + +-- infinite loops +check(function () while true do local a = -1 end end, +'LOADK', 'JMP', 'RETURN') + +check(function () while 1 do local a = -1 end end, +'LOADK', 'JMP', 'RETURN') + +check(function () repeat local x = 1 until false end, +'LOADK', 'JMP', 'RETURN') + +check(function () repeat local x until nil end, +'LOADNIL', 'JMP', 'RETURN') + +check(function () repeat local x = 1 until true end, +'LOADK', 'RETURN') + + +-- concat optimization +check(function (a,b,c,d) return a..b..c..d end, + 'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN') + +-- not +check(function () return not not nil end, 'LOADBOOL', 'RETURN') +check(function () return not not false end, 'LOADBOOL', 'RETURN') +check(function () return not not true end, 'LOADBOOL', 'RETURN') +check(function () return not not 1 end, 'LOADBOOL', 'RETURN') + +-- direct access to locals +check(function () + local a,b,c,d + a = b*2 + c[4], a[b] = -((a + d/-20.5 - a[b]) ^ a.x), b +end, + 'MUL', + 'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETTABLE', 'POW', + 'UNM', 'SETTABLE', 'SETTABLE', 'RETURN') + + +-- direct access to constants +check(function () + local a,b + a.x = 0 + a.x = b + a[b] = 'y' + a = 1 - a + b = 1/a + b = 5+4 + a[true] = false +end, + 'SETTABLE', 'SETTABLE', 'SETTABLE', 'SUB', 'DIV', 'LOADK', + 'SETTABLE', 'RETURN') + +local function f () return -((2^8 + -(-1)) % 8)/2 * 4 - 3 end + +check(f, 'LOADK', 'RETURN') +assert(f() == -5) + +check(function () + local a,b,c + b[c], a = c, b + b[a], a = c, b + a, b = c, a + a = a +end, + 'MOVE', 'MOVE', 'SETTABLE', + 'MOVE', 'MOVE', 'MOVE', 'SETTABLE', + 'MOVE', 'MOVE', 'MOVE', + -- no code for a = a + 'RETURN') + + +-- x == nil , x ~= nil +checkequal(function () if (a==nil) then a=1 end; if a~=nil then a=1 end end, + function () if (a==9) then a=1 end; if a~=9 then a=1 end end) + +check(function () if a==nil then a=1 end end, +'GETGLOBAL', 'EQ', 'JMP', 'LOADK', 'SETGLOBAL', 'RETURN') + +-- de morgan +checkequal(function () local a; if not (a or b) then b=a end end, + function () local a; if (not a and not b) then b=a end end) + +checkequal(function (l) local a; return 0 <= a and a <= l end, + function (l) local a; return not (not(a >= 0) or not(a <= l)) end) + + +print 'OK' + diff --git a/src/test/resources/bytecode-compiler/lua5.2/code.luac b/src/test/resources/bytecode-compiler/lua5.2/code.luac new file mode 100644 index 0000000000000000000000000000000000000000..28a9b40753918fd447b205b1adaa43b857ce9265 GIT binary patch literal 5952 zcmb_g&u<&Y6@Ei5DTS?Fw+*V+ZfYih5UW4~1ZWNoTzi%jC0Le3$Chk2LEuspEwffh zS)?2%D3Dnzhzl3!rDvnpfBff@k9(KO5iXATBm^8n%E60||jBf^63pmp5!;+At^QC)sCp zn19C3jM2r=Ne=OHs8XM4v+jRN2-cpVSMv#n1z)2RrygA>u@+ z7i>dYYst-HFH1NV9E6VMFcDGoocbj(`?-VQ6T}q6p zTfg99tW~cfINi$C^>T<1T$U!KY85v0T-$Wa;)Sb;CR|)h43Uw`kA_z^OUeAD>a~Q~ zTwhBJk)bo>TD4B6vEP*}Sd5qBfL!I>WS21YyQI2$QBH!eYR3R2Gdwg6e2Sx;i@*6~ zD;D`mfAa~CdU&v8ng{ac?m-RhA@cn(U{L17{-^=G)YVv34CO%-X1@$E^|_KTlaWWbb_*uT4CbQL z+=`tm34LZtkzAMiSw8h&gd$%ktcxrDAM&*=J4@}JFxx=8%}$q<_ecEnynWQVwXIz| zbu8jequWXvuhYc?xZ|{)gRO*{-#W7(0XLgixt5`9m6ASP7V76?@CtOJ_yu&6a2j)y zP{iCMyo$ML;Ceh$_7=~-28b|gN?R1H)bFikYrAo8A?ZWR_4T{BO1c9*9}CxujlF@I zL7VbJe7*rFfHl8SS-)x(TvsH^wx0(s0_>+SeIIm`CMk~O{wp}V#b}b_4g+K|JqudB3DAiZpj@+?AxCVbqdv_BrF?*4I)14NVh~s=gAW?9L65K{ntShbY)Tv} z4uQ<_!C_FXj2I;v_UEDlqt0fXt&&Z)+nDB{HllkBk-M(2hCb81w7cUf}nV zX?&);o9k@?SAlCs0Mq1pNsF*E8G%xAk9sM&N4=EXqm(#q0Jkicc?ksDm4eo$pv4qS zEXTz}mw*rrUNF7;+#Kso*de_JJEX+1>wr0~%q4xJlXuy?9D}zboe8IX1X4flfcel0IhG5hR5fQj|bf!ddz2n_M<5_5UIrjL}M zUuzm=CJN{$cnE<&g4B&x?u9GGAU@_WG>LkiA8K7nZ(j%4BpP?YW+oXKdN%T2MJw!T$k}Ugh5jd&E%mMGA zGOW^}PW}-2JFI-Qvam~y^i9A}KJqlmY7x6O7QF`bqr~wShwnZ^A9)%*N`@>ku62A4 zewaUovO@Ep*_|A2VQ@o$3;!@nfI1HA`z{>ApU;6140 zO-1M-P%b%?K|QFeI4pq@lvf=1D{LG%Km2y&`Qeuw&kw(=c%H8sqwudBzr);n6To-! zVbJFWa0_Toz#pI|Xva=%jX@i_X}E)Ve*N&B#p5IJ!}u7o$DhE{$2dfe9)c2|P5|fp m{lqJQ{6h!!Ir+bifc;6_#A^!P%KFk>aE}4@H?Lm%rSpFR*|8b` literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/constructs.lua b/src/test/resources/bytecode-compiler/lua5.2/constructs.lua new file mode 100644 index 00000000..470f8539 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/constructs.lua @@ -0,0 +1,240 @@ +print "testing syntax" + +-- testing priorities + +assert(2^3^2 == 2^(3^2)); +assert(2^3*4 == (2^3)*4); +assert(2^-2 == 1/4 and -2^- -2 == - - -4); +assert(not nil and 2 and not(2>3 or 3<2)); +assert(-3-1-5 == 0+0-9); +assert(-2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0); +assert(2*1+3/3 == 3 and 1+2 .. 3*1 == "33"); +assert(not(2+1 > 3*1) and "a".."b" > "a"); + +assert(not ((true or false) and nil)) +assert( true or false and nil) + +local a,b = 1,nil; +assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75); +x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x); +x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x); + +x,y=1,2; +assert((x>y) and x or y == 2); +x,y=2,1; +assert((x>y) and x or y == 2); + +assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891) + + +-- silly loops +repeat until 1; repeat until true; +while false do end; while nil do end; + +do -- test old bug (first name could not be an `upvalue') + local a; function f(x) x={a=1}; x={x=1}; x={G=1} end +end + +function f (i) + if type(i) ~= 'number' then return i,'jojo'; end; + if i > 0 then return i, f(i-1); end; +end + +x = {f(3), f(5), f(10);}; +assert(x[1] == 3 and x[2] == 5 and x[3] == 10 and x[4] == 9 and x[12] == 1); +assert(x[nil] == nil) +x = {f'alo', f'xixi', nil}; +assert(x[1] == 'alo' and x[2] == 'xixi' and x[3] == nil); +x = {f'alo'..'xixi'}; +assert(x[1] == 'aloxixi') +x = {f{}} +assert(x[2] == 'jojo' and type(x[1]) == 'table') + + +local f = function (i) + if i < 10 then return 'a'; + elseif i < 20 then return 'b'; + elseif i < 30 then return 'c'; + end; +end + +assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil) + +for i=1,1000 do break; end; +n=100; +i=3; +t = {}; +a=nil +while not a do + a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end; +end +assert(a == n*(n+1)/2 and i==3); +assert(t[1] and t[n] and not t[0] and not t[n+1]) + +function f(b) + local x = 1; + repeat + local a; + if b==1 then local b=1; x=10; break + elseif b==2 then x=20; break; + elseif b==3 then x=30; + else local a,b,c,d=math.sin(1); x=x+1; + end + until x>=12; + return x; +end; + +assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12) + + +local f = function (i) + if i < 10 then return 'a' + elseif i < 20 then return 'b' + elseif i < 30 then return 'c' + else return 8 + end +end + +assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8) + +local a, b = nil, 23 +x = {f(100)*2+3 or a, a or b+2} +assert(x[1] == 19 and x[2] == 25) +x = {f=2+3 or a, a = b+2} +assert(x.f == 5 and x.a == 25) + +a={y=1} +x = {a.y} +assert(x[1] == 1) + +function f(i) + while 1 do + if i>0 then i=i-1; + else return; end; + end; +end; + +function g(i) + while 1 do + if i>0 then i=i-1 + else return end + end +end + +f(10); g(10); + +do + function f () return 1,2,3; end + local a, b, c = f(); + assert(a==1 and b==2 and c==3) + a, b, c = (f()); + assert(a==1 and b==nil and c==nil) +end + +local a,b = 3 and f(); +assert(a==1 and b==nil) + +function g() f(); return; end; +assert(g() == nil) +function g() return nil or f() end +a,b = g() +assert(a==1 and b==nil) + +print'+'; + + +f = [[ +return function ( a , b , c , d , e ) + local x = a >= b or c or ( d and e ) or nil + return x +end , { a = 1 , b = 2 >= 1 , } or { 1 }; +]] +f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes +f,a = loadstring(f)(); +assert(a.a == 1 and a.b) + +function g (a,b,c,d,e) + if not (a>=b or c or d and e or nil) then return 0; else return 1; end; +end + +function h (a,b,c,d,e) + while (a>=b or c or (d and e) or nil) do return 1; end; + return 0; +end; + +assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1) +assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) +assert(f(1,2,'a') +~= -- force SETLINE before nil +nil, "") +assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1) +assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and + h(1,2,nil,1,'x') == 1) +assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and + h(1,2,nil,nil,'x') == 0) +assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and + h(1,2,nil,1,nil) == 0) + +assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true) +x = 2<3 and not 3; assert(x==false) +x = 2<1 or (2>1 and 'a'); assert(x=='a') + + +do + local a; if nil then a=1; else a=2; end; -- this nil comes as PUSHNIL 2 + assert(a==2) +end + +function F(a) + assert(debug.getinfo(1, "n").name == 'F') + return a,2,3 +end + +a,b = F(1)~=nil; assert(a == true and b == nil); +a,b = F(nil)==nil; assert(a == true and b == nil) + +---------------------------------------------------------------- +-- creates all combinations of +-- [not] ([not] arg op [not] (arg op [not] arg )) +-- and tests each one + +function ID(x) return x end + +function f(t, i) + local b = t.n + local res = math.mod(math.floor(i/c), b)+1 + c = c*b + return t[res] +end + +local arg = {" ( 1 < 2 ) ", " ( 1 >= 2 ) ", " F ( ) ", " nil "; n=4} + +local op = {" and ", " or ", " == ", " ~= "; n=4} + +local neg = {" ", " not "; n=2} + +local i = 0 +repeat + c = 1 + local s = f(neg, i)..'ID('..f(neg, i)..f(arg, i)..f(op, i).. + f(neg, i)..'ID('..f(arg, i)..f(op, i)..f(neg, i)..f(arg, i)..'))' + local s1 = string.gsub(s, 'ID', '') + K,X,NX,WX1,WX2 = nil + s = string.format([[ + local a = %s + local b = not %s + K = b + local xxx; + if %s then X = a else X = b end + if %s then NX = b else NX = a end + while %s do WX1 = a; break end + while %s do WX2 = a; break end + repeat if (%s) then break end; assert(b) until not(%s) + ]], s1, s, s1, s, s1, s, s1, s, s) + assert(loadstring(s))() + assert(X and not NX and not WX1 == K and not WX2 == K) + if math.mod(i,4000) == 0 then print('+') end + i = i+1 +until i==c + +print'OK' diff --git a/src/test/resources/bytecode-compiler/lua5.2/constructs.luac b/src/test/resources/bytecode-compiler/lua5.2/constructs.luac new file mode 100644 index 0000000000000000000000000000000000000000..d05a9c8cfef59c8c4bcf2f7b9e8a5b6b02dc6ecd GIT binary patch literal 13414 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 then break end; f() + if 3<4 then a=1 else break end; f() + while 1 do local x=10; break end; f() + local b = 1 + if 3>4 then return math.sin(1) end; f() + a = 3<4; f() + a = 3<4 or 1; f() + repeat local x=20; if 4>3 then f() else break end; f() until 1 + g = {} + f(g).x = f(2) and f(10)+f(9) + assert(g.x == f(19)) + function g(x) if not x then return 3 end return (x('a', 'x')) end + assert(g(f) == 'a') +until 1 + +test([[if +math.sin(1) +then + a=1 +else + a=2 +end +]], {2,4,7}) + +test([[-- +if nil then + a=1 +else + a=2 +end +]], {2,5,6}) + +test([[a=1 +repeat + a=a+1 +until a==3 +]], {1,3,4,3,4}) + +test([[ do + return +end +]], {2}) + +test([[local a +a=1 +while a<=3 do + a=a+1 +end +]], {2,3,4,3,4,3,4,3,5}) + +test([[while math.sin(1) do + if math.sin(1) + then + break + end +end +a=1]], {1,2,4,7}) + +test([[for i=1,3 do + a=i +end +]], {1,2,1,2,1,2,1,3}) + +test([[for i,v in pairs{'a','b'} do + a=i..v +end +]], {1,2,1,2,1,3}) + +test([[for i=1,4 do a=1 end]], {1,1,1,1,1}) + + + +print'+' + +a = {}; L = nil +local glob = 1 +local oldglob = glob +debug.sethook(function (e,l) + collectgarbage() -- force GC during a hook + local f, m, c = debug.gethook() + assert(m == 'crl' and c == 0) + if e == "line" then + if glob ~= oldglob then + L = l-1 -- get the first line where "glob" has changed + oldglob = glob + end + elseif e == "call" then + local f = debug.getinfo(2, "f").func + a[f] = 1 + else assert(e == "return") + end +end, "crl") + +function f(a,b) + collectgarbage() + local _, x = debug.getlocal(1, 1) + local _, y = debug.getlocal(1, 2) + assert(x == a and y == b) + assert(debug.setlocal(2, 3, "pera") == "AA".."AA") + assert(debug.setlocal(2, 4, "ma��") == "B") + x = debug.getinfo(2) + assert(x.func == g and x.what == "Lua" and x.name == 'g' and + x.nups == 0 and string.find(x.source, "^@.*db%.lua")) + glob = glob+1 + assert(debug.getinfo(1, "l").currentline == L+1) + assert(debug.getinfo(1, "l").currentline == L+2) +end + +function foo() + glob = glob+1 + assert(debug.getinfo(1, "l").currentline == L+1) +end; foo() -- set L +-- check line counting inside strings and empty lines + +_ = 'alo\ +alo' .. [[ + +]] +--[[ +]] +assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines + + +--[[function g(...) + do local a,b,c; a=math.sin(40); end + local feijao + local AAAA,B = "xuxu", "mam�o" + f(AAAA,B) + assert(AAAA == "pera" and B == "ma��") + do + local B = 13 + local x,y = debug.getlocal(1,5) + assert(x == 'B' and y == 13) + end +end]] + +g() + + +assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print]) + + +-- tests for manipulating non-registered locals (C and Lua temporaries) + +local n, v = debug.getlocal(0, 1) +assert(v == 0 and n == "(*temporary)") +local n, v = debug.getlocal(0, 2) +assert(v == 2 and n == "(*temporary)") +assert(not debug.getlocal(0, 3)) +assert(not debug.getlocal(0, 0)) + +function f() + assert(select(2, debug.getlocal(2,3)) == 1) + assert(not debug.getlocal(2,4)) + debug.setlocal(2, 3, 10) + return 20 +end + +function g(a,b) return (a+1) + f() end + +assert(g(0,0) == 30) + + +debug.sethook(nil); +assert(debug.gethook() == nil) + + +-- testing access to function arguments + +X = nil +a = {} +--function a:f (a, b, ...) local c = 13 end +debug.sethook(function (e) + assert(e == "call") + dostring("XX = 12") -- test dostring inside hooks + -- testing errors inside hooks + assert(not pcall(loadstring("a='joao'+1"))) + debug.sethook(function (e, l) + assert(debug.getinfo(2, "l").currentline == l) + local f,m,c = debug.gethook() + assert(e == "line") + assert(m == 'l' and c == 0) + debug.sethook(nil) -- hook is called only once + assert(not X) -- check that + X = {}; local i = 1 + local x,y + while 1 do + x,y = debug.getlocal(2, i) + if x==nil then break end + X[x] = y + i = i+1 + end + end, "l") +end, "c") + +a:f(1,2,3,4,5) +assert(X.self == a and X.a == 1 and X.b == 2 and X.arg.n == 3 and X.c == nil) +assert(XX == 12) +assert(debug.gethook() == nil) + + +-- testing upvalue access +local function getupvalues (f) + local t = {} + local i = 1 + while true do + local name, value = debug.getupvalue(f, i) + if not name then break end + assert(not t[name]) + t[name] = value + i = i + 1 + end + return t +end + +local a,b,c = 1,2,3 +local function foo1 (a) b = a; return c end +local function foo2 (x) a = x; return c+b end +assert(debug.getupvalue(foo1, 3) == nil) +assert(debug.getupvalue(foo1, 0) == nil) +assert(debug.setupvalue(foo1, 3, "xuxu") == nil) +local t = getupvalues(foo1) +assert(t.a == nil and t.b == 2 and t.c == 3) +t = getupvalues(foo2) +assert(t.a == 1 and t.b == 2 and t.c == 3) +assert(debug.setupvalue(foo1, 1, "xuxu") == "b") +assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu") +-- cannot manipulate C upvalues from Lua +assert(debug.getupvalue(io.read, 1) == nil) +assert(debug.setupvalue(io.read, 1, 10) == nil) + + +-- testing count hooks +local a=0 +debug.sethook(function (e) a=a+1 end, "", 1) +a=0; for i=1,1000 do end; assert(1000 < a and a < 1012) +debug.sethook(function (e) a=a+1 end, "", 4) +a=0; for i=1,1000 do end; assert(250 < a and a < 255) +local f,m,c = debug.gethook() +assert(m == "" and c == 4) +debug.sethook(function (e) a=a+1 end, "", 4000) +a=0; for i=1,1000 do end; assert(a == 0) +debug.sethook(print, "", 2^24 - 1) -- count upperbound +local f,m,c = debug.gethook() +assert(({debug.gethook()})[3] == 2^24 - 1) +debug.sethook() + + +-- tests for tail calls +local function f (x) + if x then + assert(debug.getinfo(1, "S").what == "Lua") + local tail = debug.getinfo(2) + assert(not pcall(getfenv, 3)) + assert(tail.what == "tail" and tail.short_src == "(tail call)" and + tail.linedefined == -1 and tail.func == nil) + assert(debug.getinfo(3, "f").func == g1) + assert(getfenv(3)) + assert(debug.getinfo(4, "S").what == "tail") + assert(not pcall(getfenv, 5)) + assert(debug.getinfo(5, "S").what == "main") + assert(getfenv(5)) + print"+" + end +end + +function g(x) return f(x) end + +function g1(x) g(x) end + +local function h (x) local f=g1; return f(x) end + +h(true) + +local b = {} +debug.sethook(function (e) table.insert(b, e) end, "cr") +h(false) +debug.sethook() +local res = {"return", -- first return (from sethook) + "call", "call", "call", "call", + "return", "tail return", "return", "tail return", + "call", -- last call (to sethook) +} +for _, k in ipairs(res) do assert(k == table.remove(b, 1)) end + + +lim = 30000 +local function foo (x) + if x==0 then + assert(debug.getinfo(lim+2).what == "main") + for i=2,lim do assert(debug.getinfo(i, "S").what == "tail") end + else return foo(x-1) + end +end + +foo(lim) + + +print"+" + + +-- testing traceback + +assert(debug.traceback(print) == print) +assert(debug.traceback(print, 4) == print) +assert(string.find(debug.traceback("hi", 4), "^hi\n")) +assert(string.find(debug.traceback("hi"), "^hi\n")) +assert(not string.find(debug.traceback("hi"), "'traceback'")) +assert(string.find(debug.traceback("hi", 0), "'traceback'")) +assert(string.find(debug.traceback(), "^stack traceback:\n")) + +-- testing debugging of coroutines + +local function checktraceback (co, p) + local tb = debug.traceback(co) + local i = 0 + for l in string.gmatch(tb, "[^\n]+\n?") do + assert(i == 0 or string.find(l, p[i])) + i = i+1 + end + assert(p[i] == nil) +end + + +local function f (n) + if n > 0 then return f(n-1) + else coroutine.yield() end +end + +local co = coroutine.create(f) +coroutine.resume(co, 3) +checktraceback(co, {"yield", "db.lua", "tail", "tail", "tail"}) + + +co = coroutine.create(function (x) + local a = 1 + coroutine.yield(debug.getinfo(1, "l")) + coroutine.yield(debug.getinfo(1, "l").currentline) + return a + end) + +local tr = {} +local foo = function (e, l) table.insert(tr, l) end +debug.sethook(co, foo, "l") + +local _, l = coroutine.resume(co, 10) +local x = debug.getinfo(co, 1, "lfLS") +assert(x.currentline == l.currentline and x.activelines[x.currentline]) +assert(type(x.func) == "function") +for i=x.linedefined + 1, x.lastlinedefined do + assert(x.activelines[i]) + x.activelines[i] = nil +end +assert(next(x.activelines) == nil) -- no 'extra' elements +assert(debug.getinfo(co, 2) == nil) +local a,b = debug.getlocal(co, 1, 1) +assert(a == "x" and b == 10) +a,b = debug.getlocal(co, 1, 2) +assert(a == "a" and b == 1) +debug.setlocal(co, 1, 2, "hi") +assert(debug.gethook(co) == foo) +assert(table.getn(tr) == 2 and + tr[1] == l.currentline-1 and tr[2] == l.currentline) + +a,b,c = pcall(coroutine.resume, co) +assert(a and b and c == l.currentline+1) +checktraceback(co, {"yield", "in function <"}) + +a,b = coroutine.resume(co) +assert(a and b == "hi") +assert(table.getn(tr) == 4 and tr[4] == l.currentline+2) +assert(debug.gethook(co) == foo) +assert(debug.gethook() == nil) +checktraceback(co, {}) + + +-- check traceback of suspended (or dead with error) coroutines + +function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end + +co = coroutine.create(function (x) f(x) end) +a, b = coroutine.resume(co, 3) +t = {"'yield'", "'f'", "in function <"} +while coroutine.status(co) == "suspended" do + checktraceback(co, t) + a, b = coroutine.resume(co) + table.insert(t, 2, "'f'") -- one more recursive call to 'f' +end +t[1] = "'error'" +checktraceback(co, t) + + +-- test acessing line numbers of a coroutine from a resume inside +-- a C function (this is a known bug in Lua 5.0) + +local function g(x) + coroutine.yield(x) +end + +local function f (i) + debug.sethook(function () end, "l") + for j=1,1000 do + g(i+j) + end +end + +local co = coroutine.wrap(f) +co(10) +pcall(co) +pcall(co) + + +assert(type(debug.getregistry()) == "table") + + +print"OK" + diff --git a/src/test/resources/bytecode-compiler/lua5.2/db.luac b/src/test/resources/bytecode-compiler/lua5.2/db.luac new file mode 100644 index 0000000000000000000000000000000000000000..5d166e0f0a7d199847b1e5a19b350bd9ba8ce1bb GIT binary patch literal 25376 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 J0_HB}{{#8$*X95K literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/errors.lua b/src/test/resources/bytecode-compiler/lua5.2/errors.lua new file mode 100644 index 00000000..3cc3211d --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/errors.lua @@ -0,0 +1,250 @@ +print("testing errors") + +function doit (s) + local f, msg = loadstring(s) + if f == nil then return msg end + local cond, msg = pcall(f) + return (not cond) and msg +end + + +function checkmessage (prog, msg) + assert(string.find(doit(prog), msg, 1, true)) +end + +function checksyntax (prog, extra, token, line) + local msg = doit(prog) + token = string.gsub(token, "(%p)", "%%%1") + local pt = string.format([[^%%[string ".*"%%]:%d: .- near '%s'$]], + line, token) + assert(string.find(msg, pt)) + assert(string.find(msg, msg, 1, true)) +end + + +-- test error message with no extra info +assert(doit("error('hi', 0)") == 'hi') + +-- test error message with no info +assert(doit("error()") == nil) + + +-- test common errors/errors that crashed in the past +assert(doit("unpack({}, 1, n=2^30)")) +assert(doit("a=math.sin()")) +assert(not doit("tostring(1)") and doit("tostring()")) +assert(doit"tonumber()") +assert(doit"repeat until 1; a") +checksyntax("break label", "", "label", 1) +assert(doit";") +assert(doit"a=1;;") +assert(doit"return;;") +assert(doit"assert(false)") +assert(doit"assert(nil)") +assert(doit"a=math.sin\n(3)") +assert(doit("function a (... , ...) end")) +assert(doit("function a (, ...) end")) + +checksyntax([[ + local a = {4 + +]], "'}' expected (to close '{' at line 1)", "", 3) + + +-- tests for better error messages + +checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'") +checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)", + "local 'bbbb'") +checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'") +checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'") +assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'")) +checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number") + +aaa = nil +checkmessage("aaa.bbb:ddd(9)", "global 'aaa'") +checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'") +checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'") +checkmessage("local a,b,c; (function () a = b+1 end)()", "upvalue 'b'") +assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)") + +checkmessage("b=1; local aaa='a'; x=aaa+b", "local 'aaa'") +checkmessage("aaa={}; x=3/aaa", "global 'aaa'") +checkmessage("aaa='2'; b=nil;x=aaa*b", "global 'b'") +checkmessage("aaa={}; x=-aaa", "global 'aaa'") +assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'")) +assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'")) + +checkmessage([[aaa=9 +repeat until 3==3 +local x=math.sin(math.cos(3)) +if math.sin(1) == x then return math.sin(1) end -- tail call +local a,b = 1, { + {x='a'..'b'..'c', y='b', z=x}, + {1,2,3,4,5} or 3+3<=3+3, + 3+1>3+1, + {d = x and aaa[x or y]}} +]], "global 'aaa'") + +checkmessage([[ +local x,y = {},1 +if math.sin(1) == 0 then return 3 end -- return +x.a()]], "field 'a'") + +checkmessage([[ +prefix = nil +insert = nil +while 1 do + local a + if nil then break end + insert(prefix, a) +end]], "global 'insert'") + +checkmessage([[ -- tail call + return math.sin("a") +]], "'sin'") + +checkmessage([[collectgarbage("nooption")]], "invalid option") + +checkmessage([[x = print .. "a"]], "concatenate") + +checkmessage("getmetatable(io.stdin).__gc()", "no value") + +print'+' + + +-- testing line error + +function lineerror (s) + local err,msg = pcall(loadstring(s)) + local line = string.match(msg, ":(%d+):") + return line and line+0 +end + +assert(lineerror"local a\n for i=1,'a' do \n print(i) \n end" == 2) +assert(lineerror"\n local a \n for k,v in 3 \n do \n print(k) \n end" == 3) +assert(lineerror"\n\n for k,v in \n 3 \n do \n print(k) \n end" == 4) +assert(lineerror"function a.x.y ()\na=a+1\nend" == 1) + +local p = [[ +function g() f() end +function f(x) error('a', X) end +g() +]] +X=3;assert(lineerror(p) == 3) +X=0;assert(lineerror(p) == nil) +X=1;assert(lineerror(p) == 2) +X=2;assert(lineerror(p) == 1) + +lineerror = nil + +C = 0 +local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end + +local function checkstackmessage (m) + return (string.find(m, "^.-:%d+: stack overflow")) +end +assert(checkstackmessage(doit('y()'))) +assert(checkstackmessage(doit('y()'))) +assert(checkstackmessage(doit('y()'))) +-- teste de linhas em erro +C = 0 +local l1 +local function g() + l1 = debug.getinfo(1, "l").currentline; y() +end +local _, stackmsg = xpcall(g, debug.traceback) +local stack = {} +for line in string.gmatch(stackmsg, "[^\n]*") do + local curr = string.match(line, ":(%d+):") + if curr then table.insert(stack, tonumber(curr)) end +end +local i=1 +while stack[i] ~= l1 do + assert(stack[i] == l) + i = i+1 +end +assert(i > 15) + + +-- error in error handling +local res, msg = xpcall(error, error) +assert(not res and type(msg) == 'string') + +local function f (x) + if x==0 then error('a\n') + else + local aux = function () return f(x-1) end + local a,b = xpcall(aux, aux) + return a,b + end +end +f(3) + +-- non string messages +function f() error{msg='x'} end +res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end) +assert(msg.msg == 'xy') + +print('+') +checksyntax("syntax error", "", "error", 1) +checksyntax("1.000", "", "1.000", 1) +checksyntax("[[a]]", "", "[[a]]", 1) +checksyntax("'aa'", "", "'aa'", 1) + +-- test 255 as first char in a chunk +checksyntax("\255a = 1", "", "\255", 1) + +doit('I = loadstring("a=9+"); a=3') +assert(a==3 and I == nil) +print('+') + +lim = 1000 +if rawget(_G, "_soft") then lim = 100 end +for i=1,lim do + doit('a = ') + doit('a = 4+nil') +end + + +-- testing syntax limits +local function testrep (init, rep) + local s = "local a; "..init .. string.rep(rep, 400) + local a,b = loadstring(s) + assert(not a and string.find(b, "syntax levels")) +end +testrep("a=", "{") +testrep("a=", "(") +testrep("", "a(") +testrep("", "do ") +testrep("", "while a do ") +testrep("", "if a then else ") +testrep("", "function foo () ") +testrep("a=", "a..") +testrep("a=", "a^") + + +-- testing other limits +-- upvalues +local s = "function foo ()\n local " +for j = 1,70 do + s = s.."a"..j..", " +end +s = s.."b\n" +for j = 1,70 do + s = s.."function foo"..j.." ()\n a"..j.."=3\n" +end +local a,b = loadstring(s) +assert(string.find(b, "line 3")) + +-- local variables +s = "\nfunction foo ()\n local " +for j = 1,300 do + s = s.."a"..j..", " +end +s = s.."b\n" +local a,b = loadstring(s) +assert(string.find(b, "line 2")) + + +print('OK') diff --git a/src/test/resources/bytecode-compiler/lua5.2/errors.luac b/src/test/resources/bytecode-compiler/lua5.2/errors.luac new file mode 100644 index 0000000000000000000000000000000000000000..c8da9550e25cbb1860080612e3b98a2a9fca72d6 GIT binary patch literal 11713 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 Ndq9suF552{{|BWE4mtn; literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/events.lua b/src/test/resources/bytecode-compiler/lua5.2/events.lua new file mode 100644 index 00000000..388e6c1f --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/events.lua @@ -0,0 +1,360 @@ +print('testing metatables') + +X = 20; B = 30 + +setfenv(1, setmetatable({}, {__index=_G})) + +collectgarbage() + +X = X+10 +assert(X == 30 and _G.X == 20) +B = false +assert(B == false) +B = nil +assert(B == 30) + +assert(getmetatable{} == nil) +assert(getmetatable(4) == nil) +assert(getmetatable(nil) == nil) +a={}; setmetatable(a, {__metatable = "xuxu", + __tostring=function(x) return x.name end}) +assert(getmetatable(a) == "xuxu") +assert(tostring(a) == nil) +-- cannot change a protected metatable +assert(pcall(setmetatable, a, {}) == false) +a.name = "gororoba" +assert(tostring(a) == "gororoba") + +local a, t = {10,20,30; x="10", y="20"}, {} +assert(setmetatable(a,t) == a) +assert(getmetatable(a) == t) +assert(setmetatable(a,nil) == a) +assert(getmetatable(a) == nil) +assert(setmetatable(a,t) == a) + + +function f (t, i, e) + assert(not e) + local p = rawget(t, "parent") + return (p and p[i]+3), "dummy return" +end + +t.__index = f + +a.parent = {z=25, x=12, [4] = 24} +assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10") + +collectgarbage() + +a = setmetatable({}, t) +function f(t, i, v) rawset(t, i, v-3) end +t.__newindex = f +a[1] = 30; a.x = "101"; a[5] = 200 +assert(a[1] == 27 and a.x == 98 and a[5] == 197) + + +local c = {} +a = setmetatable({}, t) +t.__newindex = c +a[1] = 10; a[2] = 20; a[3] = 90 +assert(c[1] == 10 and c[2] == 20 and c[3] == 90) + + +do + local a; + a = setmetatable({}, {__index = setmetatable({}, + {__index = setmetatable({}, + {__index = function (_,n) return a[n-3]+4, "lixo" end})})}) + a[0] = 20 + for i=0,10 do + assert(a[i*3] == 20 + i*4) + end +end + + +do -- newindex + local foi + local a = {} + for i=1,10 do a[i] = 0; a['a'..i] = 0; end + setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end}) + foi = false; a[1]=0; assert(not foi) + foi = false; a['a1']=0; assert(not foi) + foi = false; a['a11']=0; assert(foi) + foi = false; a[11]=0; assert(foi) + foi = false; a[1]=nil; assert(not foi) + foi = false; a[1]=nil; assert(foi) +end + + +--function f (t, ...) return t, {...} end +t.__call = f + +do + local x,y = a(unpack{'a', 1}) + assert(x==a and y[1]=='a' and y[2]==1 and y[3]==nil) + x,y = a() + assert(x==a and y[1]==nil) +end + + +local b = setmetatable({}, t) +setmetatable(b,t) + +function f(op) + return function () cap = {[0] = op} ; return end +end +t.__add = f("add") +t.__sub = f("sub") +t.__mul = f("mul") +t.__div = f("div") +t.__mod = f("mod") +t.__unm = f("unm") +t.__pow = f("pow") + +assert(b+5 == b) +assert(cap[0] == "add" and cap[1] == b and cap[2] == 5 and cap[3]==nil) +assert(b+'5' == b) +assert(cap[0] == "add" and cap[1] == b and cap[2] == '5' and cap[3]==nil) +assert(5+b == 5) +assert(cap[0] == "add" and cap[1] == 5 and cap[2] == b and cap[3]==nil) +assert('5'+b == '5') +assert(cap[0] == "add" and cap[1] == '5' and cap[2] == b and cap[3]==nil) +b=b-3; assert(getmetatable(b) == t) +assert(5-a == 5) +assert(cap[0] == "sub" and cap[1] == 5 and cap[2] == a and cap[3]==nil) +assert('5'-a == '5') +assert(cap[0] == "sub" and cap[1] == '5' and cap[2] == a and cap[3]==nil) +assert(a*a == a) +assert(cap[0] == "mul" and cap[1] == a and cap[2] == a and cap[3]==nil) +assert(a/0 == a) +assert(cap[0] == "div" and cap[1] == a and cap[2] == 0 and cap[3]==nil) +assert(a%2 == a) +assert(cap[0] == "mod" and cap[1] == a and cap[2] == 2 and cap[3]==nil) +assert(-a == a) +assert(cap[0] == "unm" and cap[1] == a) +assert(a^4 == a) +assert(cap[0] == "pow" and cap[1] == a and cap[2] == 4 and cap[3]==nil) +assert(a^'4' == a) +assert(cap[0] == "pow" and cap[1] == a and cap[2] == '4' and cap[3]==nil) +assert(4^a == 4) +assert(cap[0] == "pow" and cap[1] == 4 and cap[2] == a and cap[3]==nil) +assert('4'^a == '4') +assert(cap[0] == "pow" and cap[1] == '4' and cap[2] == a and cap[3]==nil) + + +t = {} +t.__lt = function (a,b,c) + collectgarbage() + assert(c == nil) + if type(a) == 'table' then a = a.x end + if type(b) == 'table' then b = b.x end + return aOp(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1))) + assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a'))) + assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1))) + assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a'))) +end + +test() + +t.__le = function (a,b,c) + assert(c == nil) + if type(a) == 'table' then a = a.x end + if type(b) == 'table' then b = b.x end + return a<=b, "dummy" +end + +test() -- retest comparisons, now using both `lt' and `le' + + +-- test `partial order' + +local function Set(x) + local y = {} + for _,k in pairs(x) do y[k] = 1 end + return setmetatable(y, t) +end + +t.__lt = function (a,b) + for k in pairs(a) do + if not b[k] then return false end + b[k] = nil + end + return next(b) ~= nil +end + +t.__le = nil + +assert(Set{1,2,3} < Set{1,2,3,4}) +assert(not(Set{1,2,3,4} < Set{1,2,3,4})) +assert((Set{1,2,3,4} <= Set{1,2,3,4})) +assert((Set{1,2,3,4} >= Set{1,2,3,4})) +assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-) + +t.__le = function (a,b) + for k in pairs(a) do + if not b[k] then return false end + end + return true +end + +assert(not (Set{1,3} <= Set{3,5})) -- now its OK! +assert(not(Set{1,3} <= Set{3,5})) +assert(not(Set{1,3} >= Set{3,5})) + +t.__eq = function (a,b) + for k in pairs(a) do + if not b[k] then return false end + b[k] = nil + end + return next(b) == nil +end + +local s = Set{1,3,5} +assert(s == Set{3,5,1}) +assert(not rawequal(s, Set{3,5,1})) +assert(rawequal(s, s)) +assert(Set{1,3,5,1} == Set{3,5,1}) +assert(Set{1,3,5} ~= Set{3,5,1,6}) +t[Set{1,3,5}] = 1 +assert(t[Set{1,3,5}] == nil) -- `__eq' is not valid for table accesses + + +t.__concat = function (a,b,c) + assert(c == nil) + if type(a) == 'table' then a = a.val end + if type(b) == 'table' then b = b.val end + if A then return a..b + else + return setmetatable({val=a..b}, t) + end +end + +c = {val="c"}; setmetatable(c, t) +d = {val="d"}; setmetatable(d, t) + +A = true +assert(c..d == 'cd') +assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g") + +A = false +x = c..d +assert(getmetatable(x) == t and x.val == 'cd') +x = 0 .."a".."b"..c..d.."e".."f".."g" +assert(x.val == "0abcdefg") + + +-- test comparison compatibilities +local t1, t2, c, d +t1 = {}; c = {}; setmetatable(c, t1) +d = {} +t1.__eq = function () return true end +t1.__lt = function () return true end +assert(c ~= d and not pcall(function () return c < d end)) +setmetatable(d, t1) +assert(c == d and c < d and not(d <= c)) +t2 = {} +t2.__eq = t1.__eq +t2.__lt = t1.__lt +setmetatable(d, t2) +assert(c == d and c < d and not(d <= c)) + + + +-- test for several levels of calls +local i +local tt = { + __call = function (t) + i = i+1 + if t.f then return t.f() + else return {} + end + end +} + +local a = setmetatable({}, tt) +local b = setmetatable({f=a}, tt) +local c = setmetatable({f=b}, tt) + +i = 0 +x = c(3,4,5) +assert(i == 3 and x[1] == 3 and x[3] == 5) + + +assert(_G.X == 20) +assert(_G == getfenv(0)) + +print'+' + +local _g = _G +setfenv(1, setmetatable({}, {__index=function (_,k) return _g[k] end})) + +-- testing proxies +assert(getmetatable(newproxy()) == nil) +assert(getmetatable(newproxy(false)) == nil) + +local u = newproxy(true) + +getmetatable(u).__newindex = function (u,k,v) + getmetatable(u)[k] = v +end + +getmetatable(u).__index = function (u,k) + return getmetatable(u)[k] +end + +for i=1,10 do u[i] = i end +for i=1,10 do assert(u[i] == i) end + +local k = newproxy(u) +assert(getmetatable(k) == getmetatable(u)) + + +a = {} +rawset(a, "x", 1, 2, 3) +assert(a.x == 1 and rawget(a, "x", 3) == 1) + +print '+' + +-- testing metatables for basic types +mt = {} +debug.setmetatable(10, mt) +assert(getmetatable(-2) == mt) +mt.__index = function (a,b) return a+b end +assert((10)[3] == 13) +assert((10)["3"] == 13) +debug.setmetatable(23, nil) +assert(getmetatable(-2) == nil) + +debug.setmetatable(true, mt) +assert(getmetatable(false) == mt) +mt.__index = function (a,b) return a or b end +assert((true)[false] == true) +assert((false)[false] == false) +debug.setmetatable(false, nil) +assert(getmetatable(true) == nil) + +debug.setmetatable(nil, mt) +assert(getmetatable(nil) == mt) +mt.__add = function (a,b) return (a or 0) + (b or 0) end +assert(10 + nil == 10) +assert(nil + 23 == 23) +assert(nil + nil == 0) +debug.setmetatable(nil, nil) +assert(getmetatable(nil) == nil) + +debug.setmetatable(nil, {}) + + +print 'OK' + +return 12 diff --git a/src/test/resources/bytecode-compiler/lua5.2/events.luac b/src/test/resources/bytecode-compiler/lua5.2/events.luac new file mode 100644 index 0000000000000000000000000000000000000000..c25210a01818e675e680f978d377f99b1e61ba06 GIT binary patch literal 21321 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 BUFSIZ) +io.output(file) +for i=1,5001 do io.write('0123456789123') end +io.write('\n12346') +io.close() +io.input(file) +local x = io.read('*a') +io.input():seek('set', 0) +local y = io.read(30001)..io.read(1005)..io.read(0)..io.read(1)..io.read(100003) +assert(x == y and string.len(x) == 5001*13 + 6) +io.input():seek('set', 0) +y = io.read() -- huge line +assert(x == y..'\n'..io.read()) +assert(io.read() == nil) +io.close(io.input()) +assert(os.remove(file)) +x = nil; y = nil + +x, y = pcall(io.popen, "ls") +if x then + assert(y:read("*a")) + assert(y:close()) +else + (Message or print)('\a\n >>> popen not available<<<\n\a') +end + +print'+' + +local t = os.time() +T = os.date("*t", t) +loadstring(os.date([[assert(T.year==%Y and T.month==%m and T.day==%d and + T.hour==%H and T.min==%M and T.sec==%S and + T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))() + +assert(os.time(T) == t) + +T = os.date("!*t", t) +loadstring(os.date([[!assert(T.year==%Y and T.month==%m and T.day==%d and + T.hour==%H and T.min==%M and T.sec==%S and + T.wday==%w+1 and T.yday==%j and type(T.isdst) == 'boolean')]], t))() + +do + local T = os.date("*t") + local t = os.time(T) + assert(type(T.isdst) == 'boolean') + T.isdst = nil + local t1 = os.time(T) + assert(t == t1) -- if isdst is absent uses correct default +end + +t = os.time(T) +T.year = T.year-1; +local t1 = os.time(T) +-- allow for leap years +assert(math.abs(os.difftime(t,t1)/(24*3600) - 365) < 2) + +t = os.time() +t1 = os.time(os.date("*t")) +assert(os.difftime(t1,t) <= 2) + +local t1 = os.time{year=2000, month=10, day=1, hour=23, min=12, sec=17} +local t2 = os.time{year=2000, month=10, day=1, hour=23, min=10, sec=19} +assert(os.difftime(t1,t2) == 60*2-2) + +io.output(io.stdout) +local d = os.date('%d') +local m = os.date('%m') +local a = os.date('%Y') +local ds = os.date('%w') + 1 +local h = os.date('%H') +local min = os.date('%M') +local s = os.date('%S') +io.write(string.format('test done on %2.2d/%2.2d/%d', d, m, a)) +io.write(string.format(', at %2.2d:%2.2d:%2.2d\n', h, min, s)) +io.write(string.format('%s\n', _VERSION)) diff --git a/src/test/resources/bytecode-compiler/lua5.2/files.luac b/src/test/resources/bytecode-compiler/lua5.2/files.luac new file mode 100644 index 0000000000000000000000000000000000000000..f37231dadb06f2740c0a3ec04426775400a29aa0 GIT binary patch literal 18040 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 S-?b_9uJ|Rx@ZP~Y>Hh#>^S3Yn literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/gc.lua b/src/test/resources/bytecode-compiler/lua5.2/gc.lua new file mode 100644 index 00000000..86a9f758 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/gc.lua @@ -0,0 +1,312 @@ +print('testing garbage collection') + +collectgarbage() + +_G["while"] = 234 + +limit = 5000 + + + +contCreate = 0 + +print('tables') +while contCreate <= limit do + local a = {}; a = nil + contCreate = contCreate+1 +end + +a = "a" + +contCreate = 0 +print('strings') +while contCreate <= limit do + a = contCreate .. "b"; + a = string.gsub(a, '(%d%d*)', string.upper) + a = "a" + contCreate = contCreate+1 +end + + +contCreate = 0 + +a = {} + +print('functions') +function a:test () + while contCreate <= limit do + loadstring(string.format("function temp(a) return 'a%d' end", contCreate))() + assert(temp() == string.format('a%d', contCreate)) + contCreate = contCreate+1 + end +end + +a:test() + +-- collection of functions without locals, globals, etc. +do local f = function () end end + + +print("functions with errors") +prog = [[ +do + a = 10; + function foo(x,y) + a = sin(a+0.456-0.23e-12); + return function (z) return sin(%x+z) end + end + local x = function (w) a=a+w; end +end +]] +do + local step = 1 + if rawget(_G, "_soft") then step = 13 end + for i=1, string.len(prog), step do + for j=i, string.len(prog), step do + pcall(loadstring(string.sub(prog, i, j))) + end + end +end + +print('long strings') +x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789" +assert(string.len(x)==80) +s = '' +n = 0 +k = 300 +while n < k do s = s..x; n=n+1; j=tostring(n) end +assert(string.len(s) == k*80) +s = string.sub(s, 1, 20000) +s, i = string.gsub(s, '(%d%d%d%d)', math.sin) +assert(i==20000/4) +s = nil +x = nil + +assert(_G["while"] == 234) + + +local bytes = gcinfo() +while 1 do + local nbytes = gcinfo() + if nbytes < bytes then break end -- run until gc + bytes = nbytes + a = {} +end + + +local function dosteps (siz) + collectgarbage() + collectgarbage"stop" + local a = {} + for i=1,100 do a[i] = {{}}; local b = {} end + local x = gcinfo() + local i = 0 + repeat + i = i+1 + until collectgarbage("step", siz) + assert(gcinfo() < x) + return i +end + +assert(dosteps(0) > 10) +assert(dosteps(6) < dosteps(2)) +assert(dosteps(10000) == 1) +assert(collectgarbage("step", 1000000) == true) +assert(collectgarbage("step", 1000000)) + + +do + local x = gcinfo() + collectgarbage() + collectgarbage"stop" + repeat + local a = {} + until gcinfo() > 1000 + collectgarbage"restart" + repeat + local a = {} + until gcinfo() < 1000 +end + +lim = 15 +a = {} +-- fill a with `collectable' indices +for i=1,lim do a[{}] = i end +b = {} +for k,v in pairs(a) do b[k]=v end +-- remove all indices and collect them +for n in pairs(b) do + a[n] = nil + assert(type(n) == 'table' and next(n) == nil) + collectgarbage() +end +b = nil +collectgarbage() +for n in pairs(a) do error'cannot be here' end +for i=1,lim do a[i] = i end +for i=1,lim do assert(a[i] == i) end + + +print('weak tables') +a = {}; setmetatable(a, {__mode = 'k'}); +-- fill a with some `collectable' indices +for i=1,lim do a[{}] = i end +-- and some non-collectable ones +for i=1,lim do local t={}; a[t]=t end +for i=1,lim do a[i] = i end +for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end +collectgarbage() +local i = 0 +for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end +assert(i == 3*lim) + +a = {}; setmetatable(a, {__mode = 'v'}); +a[1] = string.rep('b', 21) +collectgarbage() +assert(a[1]) -- strings are *values* +a[1] = nil +-- fill a with some `collectable' values (in both parts of the table) +for i=1,lim do a[i] = {} end +for i=1,lim do a[i..'x'] = {} end +-- and some non-collectable ones +for i=1,lim do local t={}; a[t]=t end +for i=1,lim do a[i+lim]=i..'x' end +collectgarbage() +local i = 0 +for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end +assert(i == 2*lim) + +a = {}; setmetatable(a, {__mode = 'vk'}); +local x, y, z = {}, {}, {} +-- keep only some items +a[1], a[2], a[3] = x, y, z +a[string.rep('$', 11)] = string.rep('$', 11) +-- fill a with some `collectable' values +for i=4,lim do a[i] = {} end +for i=1,lim do a[{}] = i end +for i=1,lim do local t={}; a[t]=t end +collectgarbage() +assert(next(a) ~= nil) +local i = 0 +for k,v in pairs(a) do + assert((k == 1 and v == x) or + (k == 2 and v == y) or + (k == 3 and v == z) or k==v); + i = i+1 +end +assert(i == 4) +x,y,z=nil +collectgarbage() +assert(next(a) == string.rep('$', 11)) + + +-- testing userdata +collectgarbage("stop") -- stop collection +local u = newproxy(true) +local s = 0 +local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'}) +for i=1,10 do a[newproxy(u)] = i end +for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end +local a1 = {}; for k,v in pairs(a) do a1[k] = v end +for k,v in pairs(a1) do a[v] = k end +for i =1,10 do assert(a[i]) end +getmetatable(u).a = a1 +getmetatable(u).u = u +do + local u = u + getmetatable(u).__gc = function (o) + assert(a[o] == 10-s) + assert(a[10-s] == nil) -- udata already removed from weak table + assert(getmetatable(o) == getmetatable(u)) + assert(getmetatable(o).a[o] == 10-s) + s=s+1 + end +end +a1, u = nil +assert(next(a) ~= nil) +collectgarbage() +assert(s==11) +collectgarbage() +assert(next(a) == nil) -- finalized keys are removed in two cycles + + +-- __gc x weak tables +local u = newproxy(true) +setmetatable(getmetatable(u), {__mode = "v"}) +getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen +collectgarbage() + +local u = newproxy(true) +local m = getmetatable(u) +m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"}); +m.__gc = function (o) + assert(next(getmetatable(o).x) == nil) + m = 10 +end +u, m = nil +collectgarbage() +assert(m==10) + + +-- errors during collection +u = newproxy(true) +getmetatable(u).__gc = function () error "!!!" end +u = nil +assert(not pcall(collectgarbage)) + + +if not rawget(_G, "_soft") then + print("deep structures") + local a = {} + for i = 1,200000 do + a = {next = a} + end + collectgarbage() +end + +-- create many threads with self-references and open upvalues +local thread_id = 0 +local threads = {} + +function fn(thread) + local x = {} + threads[thread_id] = function() + thread = x + end + coroutine.yield() +end + +while thread_id < 1000 do + local thread = coroutine.create(fn) + coroutine.resume(thread, thread) + thread_id = thread_id + 1 +end + + + +-- create a userdata to be collected when state is closed +do + local newproxy,assert,type,print,getmetatable = + newproxy,assert,type,print,getmetatable + local u = newproxy(true) + local tt = getmetatable(u) + ___Glob = {u} -- avoid udata being collected before program end + tt.__gc = function (o) + assert(getmetatable(o) == tt) + -- create new objects during GC + local a = 'xuxu'..(10+3)..'joao', {} + ___Glob = o -- ressurect object! + newproxy(o) -- creates a new one with same metatable + print(">>> closing state " .. "<<<\n") + end +end + +-- create several udata to raise errors when collected while closing state +do + local u = newproxy(true) + getmetatable(u).__gc = function (o) return o + 1 end + table.insert(___Glob, u) -- preserve udata until the end + for i = 1,10 do table.insert(___Glob, newproxy(u)) end +end + +print('OK') diff --git a/src/test/resources/bytecode-compiler/lua5.2/gc.luac b/src/test/resources/bytecode-compiler/lua5.2/gc.luac new file mode 100644 index 0000000000000000000000000000000000000000..4838fa37852e802cff66adf325ff6b6248f0f0b0 GIT binary patch literal 14596 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/literals.lua b/src/test/resources/bytecode-compiler/lua5.2/literals.lua new file mode 100644 index 00000000..01d84d5a --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/literals.lua @@ -0,0 +1,176 @@ +print('testing scanner') + +local function dostring (x) return assert(loadstring(x))() end + +dostring("x = 'a\0a'") +assert(x == 'a\0a' and string.len(x) == 3) + +-- escape sequences +assert('\n\"\'\\' == [[ + +"'\]]) + +assert(string.find("\a\b\f\n\r\t\v", "^%c%c%c%c%c%c%c$")) + +-- assume ASCII just for tests: +assert("\09912" == 'c12') +assert("\99ab" == 'cab') +assert("\099" == '\99') +assert("\099\n" == 'c\10') +assert('\0\0\0alo' == '\0' .. '\0\0' .. 'alo') + +assert(010 .. 020 .. -030 == "1020-30") + +-- long variable names + +var = string.rep('a', 15000) +prog = string.format("%s = 5", var) +dostring(prog) +assert(_G[var] == 5) +var = nil +print('+') + +-- escapes -- +assert("\n\t" == [[ + + ]]) +assert([[ + + $debug]] == "\n $debug") +assert([[ [ ]] ~= [[ ] ]]) +-- long strings -- +b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" +assert(string.len(b) == 960) +prog = [=[ +print('+') + +a1 = [["isto e' um string com várias 'aspas'"]] +a2 = "'aspas'" + +assert(string.find(a1, a2) == 31) +print('+') + +a1 = [==[temp = [[um valor qualquer]]; ]==] +assert(loadstring(a1))() +assert(temp == 'um valor qualquer') +-- long strings -- +b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789" +assert(string.len(b) == 960) +print('+') + +a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +00123456789012345678901234567890123456789123456789012345678901234567890123456789 +]] +assert(string.len(a) == 1863) +assert(string.sub(a, 1, 40) == string.sub(b, 1, 40)) +x = 1 +]=] + +print('+') +x = nil +dostring(prog) +assert(x) + +prog = nil +a = nil +b = nil + + +-- testing line ends +prog = [[ +a = 1 -- a comment +b = 2 + + +x = [=[ +hi +]=] +y = "\ +hello\r\n\ +" +return debug.getinfo(1).currentline +]] + +for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do + local prog, nn = string.gsub(prog, "\n", n) + assert(dostring(prog) == nn) + assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n") +end + + +-- testing comments and strings with long brackets +a = [==[]=]==] +assert(a == "]=") + +a = [==[[===[[=[]]=][====[]]===]===]==] +assert(a == "[===[[=[]]=][====[]]===]===") + +a = [====[[===[[=[]]=][====[]]===]===]====] +assert(a == "[===[[=[]]=][====[]]===]===") + +a = [=[]]]]]]]]]=] +assert(a == "]]]]]]]]") + + +--[===[ +x y z [==[ blu foo +]== +] +]=]==] +error error]=]===] + +-- generate all strings of four of these chars +local x = {"=", "[", "]", "\n"} +local len = 4 +local function gen (c, n) + if n==0 then coroutine.yield(c) + else + for _, a in pairs(x) do + gen(c..a, n-1) + end + end +end + +for s in coroutine.wrap(function () gen("", len) end) do + assert(s == loadstring("return [====[\n"..s.."]====]")()) +end + + +-- testing decimal point locale +if os.setlocale("pt_BR") or os.setlocale("ptb") then + assert(tonumber("3,4") == 3.4 and tonumber"3.4" == nil) + assert(assert(loadstring("return 3.4"))() == 3.4) + assert(assert(loadstring("return .4,3"))() == .4) + assert(assert(loadstring("return 4."))() == 4.) + assert(assert(loadstring("return 4.+.5"))() == 4.5) + local a,b = loadstring("return 4.5.") + assert(string.find(b, "'4%.5%.'")) + assert(os.setlocale("C")) +else + (Message or print)( + '\a\n >>> pt_BR locale not available: skipping decimal point tests <<<\n\a') +end + + +print('OK') diff --git a/src/test/resources/bytecode-compiler/lua5.2/literals.luac b/src/test/resources/bytecode-compiler/lua5.2/literals.luac new file mode 100644 index 0000000000000000000000000000000000000000..3427ab7ee9c30fe2ff0ec585f67b53c03f076199 GIT binary patch literal 9185 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 BuND9R literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/locals.lua b/src/test/resources/bytecode-compiler/lua5.2/locals.lua new file mode 100644 index 00000000..b0fc556f --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/locals.lua @@ -0,0 +1,127 @@ +print('testing local variables plus some extra stuff') + +do + local i = 10 + do local i = 100; assert(i==100) end + do local i = 1000; assert(i==1000) end + assert(i == 10) + if i ~= 10 then + local i = 20 + else + local i = 30 + assert(i == 30) + end +end + + + +f = nil + +local f +x = 1 + +a = nil +loadstring('local a = {}')() +assert(type(a) ~= 'table') + +function f (a) + local _1, _2, _3, _4, _5 + local _6, _7, _8, _9, _10 + local x = 3 + local b = a + local c,d = a,b + if (d == b) then + local x = 'q' + x = b + assert(x == 2) + else + assert(nil) + end + assert(x == 3) + local f = 10 +end + +local b=10 +local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3 + + +assert(x == 1) + +f(2) +assert(type(f) == 'function') + + +-- testing globals ;-) +do + local f = {} + local _G = _G + for i=1,10 do f[i] = function (x) A=A+1; return A, _G.getfenv(x) end end + A=10; assert(f[1]() == 11) + for i=1,10 do assert(setfenv(f[i], {A=i}) == f[i]) end + assert(f[3]() == 4 and A == 11) + local a,b = f[8](1) + assert(b.A == 9) + a,b = f[8](0) + assert(b.A == 11) -- `real' global + local g + local function f () assert(setfenv(2, {a='10'}) == g) end + g = function () f(); _G.assert(_G.getfenv(1).a == '10') end + g(); assert(getfenv(g).a == '10') +end + +-- test for global table of loaded chunks +local function foo (s) + return loadstring(s) +end + +assert(getfenv(foo("")) == _G) +local a = {loadstring = loadstring} +setfenv(foo, a) +assert(getfenv(foo("")) == _G) +setfenv(0, a) -- change global environment +assert(getfenv(foo("")) == a) +setfenv(0, _G) + + +-- testing limits for special instructions + +local a +local p = 4 +for i=2,31 do + for j=-3,3 do + assert(loadstring(string.format([[local a=%s;a=a+ + %s; + assert(a + ==2^%s)]], j, p-j, i))) () + assert(loadstring(string.format([[local a=%s; + a=a-%s; + assert(a==-2^%s)]], -j, p-j, i))) () + assert(loadstring(string.format([[local a,b=0,%s; + a=b-%s; + assert(a==-2^%s)]], -j, p-j, i))) () + end + p =2*p +end + +print'+' + + +if rawget(_G, "querytab") then + -- testing clearing of dead elements from tables + collectgarbage("stop") -- stop GC + local a = {[{}] = 4, [3] = 0, alo = 1, + a1234567890123456789012345678901234567890 = 10} + + local t = querytab(a) + + for k,_ in pairs(a) do a[k] = nil end + collectgarbage() -- restore GC and collect dead fiels in `a' + for i=0,t-1 do + local k = querytab(a, i) + assert(k == nil or type(k) == 'number' or k == 'alo') + end +end + +print('OK') + +return 5,f diff --git a/src/test/resources/bytecode-compiler/lua5.2/locals.luac b/src/test/resources/bytecode-compiler/lua5.2/locals.luac new file mode 100644 index 0000000000000000000000000000000000000000..b53f1f0a922d820eb74b8b75e7a09c71ef0165be GIT binary patch literal 5885 zcmbtX-E$jP760AcwX8a%O)~|j4W^<^O4EFhuQmzA&8}q2G$AdN8Ab)%WbMcvw~FOR za_UU!_{vV)k_Y<0z?#z59qs` zy9y3=iJ7+xxDCT*YFpqvq+s$>KL92?P-O6t+W?JHiGpw(r)&a10Wq_Z*#nq zcXOy~8>1R()YT9lOF<4T|G3l>JeX`(b;1);s2-UFAa2;}CrId$DjjSpJ z+7jKI2jrOefCsg^L7RHW>UY1GZ_!t(p5;1Zjhsz6n{unnDG-~UW5!0Vkpq_o19j#b zP@i^_^7kp9Qp4~VN4*X-`1U>MVis6&E$51QFrU`JojhavsL@o*bt%NQFR*%uAk4206S*R=ZFXoMp`aR=gAM$p; zhFr=|9W(up6<2F04()ODC@H+W> zT&sf%K(Pq~N;|Hl?a?gvSMsq&5RhJ8fcnVF^%UHZb$RqR9?bs@ifig2uSTHhwNdZI zvSOWZeV4e_1zlgazq`1Wc`t`PufCLT3>&>b!5U?8%DODk7UNNBp%<&PwM<(ru@$Iw zdm;MuMvC%V>ld)OuGkjeLtKx#N}<7;Qq5j?O$&eV`gFreLoGV+?vie%b&Exn8p^+wDQmsH3d`2UuMsavJO8oyYLUoyZSuE&tiVf9F4`*FXMD z{b#6UkaGUTyl?kj~|7VBT-os~xZXDf~7 zFIVdBuTs_pNIeX2|MS_4&{7+gGUt57%cjOyH=kP%=JVMLtZANe*4@vWS*O7@^DRf` z?F&47+74`_Mi;eO;+hrQW~!eX+q%m`;^-9DhkfaOuhf1aSV>7e%hX+)eJ;mylKo$p zPji=vwV}Sm&H?sKg&_0}9WS7eR)oP8QO~To2XbH*B3}!y~5Ka#{wVN}=FoT*z8Jj`uC+)IQ z=x26%yf#&yh9hBK9tpjAvG@xvFBPC#tW6ZBZ^Ya^v5`zXcrG3=Y*Cnu6pYmq$KkL% z*i7UHI3EkA1G6M?BC%GizxX%9I!Ro92pnte8vxA2^+msJCEq1^I}|4Pq8%u`w6kGuvdmd&<<}lQ+85un(bmA?B0G@nzuOu z(y3dndyfop2LdaN|-n>4;k-q(m{iA;pA^DTIec%C@01zzA>j2AiH z2#3g*<$98khoJ*kWqjY(q?4?V#o$ZAnU0^d|5Oe??LevU!(>-}oWE#esm|H_{vPaB;~E zxTDZAUeW;q^4XizWxi$OWo^uUw_7F~b+Q9;-ngWW)4kK;B=))0TY{9Z=LkTwgldOv z_Gg42%&cF60m+phU;TO4<<~WE-{mcgMtxLkb(>Kav^HPyIr4L)2V9t2PfR9nsg0UH z!usY#B!1c3<&k)pVQ+Va5=b1^PTpdzBS#@7d8Fl7%VUdW33tHNOf_Jtfk{>VNQ?vl zPiy$+V_PZ4*Og?%XYH=-N>QcPZ=wVe$F-BUIFhUc;nxCh-`7c8Bou5(PO47x{SG;C z#Yr)Ae*=(ZDjDmViCDSNh0sChxhkq=A;qppo0A}fT4zlDOolVz+lf>}Z6l<}-wblu zZv;6z0;zI=eC8VUP|ejo=7z^bsKEU^ATLT#Qp3KMZFn zbB@@mC%nq}hvD0lc~lJ2#&+=xM_(X2^0*x<`DxhB@9Tr06g05N}XE z#+w``@jcQz!S^Yj#9O4diyu%ni2~`ji63?0$E1_^3F#!Hg(Q)jlkt@Fdw!hr zNfb#Zk(`sU#5PGt-0voEjpL0_CO^gu$K8-m@M;3`al9FBa8B@k7vxS&;wI^xLTrAR zz&yuEe3P_AOhG#4^KGh!Bk6D9oOHOoZE8P9f^cqtqgZbO2T4gUmbo#UNl-qdcdX4x z7s(SMIbqiQw;b67FB{noA;s~F{Nt6Gn!Z|`J0!P_+4eTSG<9vNw#Ju`jeNCMoLOUs zkxG5}L|ZvBI!)eBll;hN%G}9OWabhHpgF9?qE^n=DQjD}Jq9IfR!ucZ_IfQmy&5;u`<) zSZBOEU8|H!Yct~MJshU)5kjcTZK`c8O6?RSB%U@uN-gGhr53Zr=oC& literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/main.lua b/src/test/resources/bytecode-compiler/lua5.2/main.lua new file mode 100644 index 00000000..2f818eca --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/main.lua @@ -0,0 +1,160 @@ +# testing special comment on first line + +print ("testing lua.c options") + +assert(os.execute() ~= 0) -- machine has a system command + +prog = os.tmpname() +otherprog = os.tmpname() +out = os.tmpname() +local arg-- = {n = select("#", ...), ...} + +do + local i = 0 + while arg[i] do i=i-1 end + progname = '"'..arg[i+1]..'"' +end +print(progname) + +local prepfile = function (s, p) + p = p or prog + io.output(p) + io.write(s) + assert(io.close()) +end + +function checkout (s) + io.input(out) + local t = io.read("*a") + io.input():close() + assert(os.remove(out)) + if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end + assert(s == t) + return t +end + +--[[function auxrun (...) + local s = string.format(...) + s = string.gsub(s, "lua", progname, 1) + return os.execute(s) +end + +function RUN (...) + assert(auxrun(...) == 0) +end + +function NoRun (...) + print("\n(the next error is expected by the test)") + assert(auxrun(...) ~= 0) +end]] + +-- test 2 files +prepfile("print(1); a=2") +prepfile("print(a)", otherprog) +RUN("lua -l %s -l%s -lstring -l io %s > %s", prog, otherprog, otherprog, out) +checkout("1\n2\n2\n") + +local a = [[ + assert(table.getn(arg) == 3 and arg[1] == 'a' and + arg[2] == 'b' and arg[3] == 'c') + assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == %s) + assert(arg[4] == nil and arg[-4] == nil) + local a, b, c = ... + assert(... == 'a' and a == 'a' and b == 'b' and c == 'c') +]] +a = string.format(a, progname) +prepfile(a) +RUN('lua "-e " -- %s a b c', prog) + +prepfile"assert(arg==nil)" +prepfile("assert(arg)", otherprog) +RUN("lua -l%s - < %s", prog, otherprog) + +prepfile"" +RUN("lua - < %s > %s", prog, out) +checkout("") + +-- test many arguments +prepfile[[print(({...})[30])]] +RUN("lua %s %s > %s", prog, string.rep(" a", 30), out) +checkout("a\n") + +RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out) +checkout("1\n3\n") + +prepfile[[ + print( +1, a +) +]] +RUN("lua - < %s > %s", prog, out) +checkout("1\tnil\n") + +prepfile[[ += (6*2-6) -- === +a += 10 +print(a) += a]] +RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) +checkout("6\n10\n10\n\n") + +prepfile("a = [[b\nc\nd\ne]]\n=a") +print(prog) +RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) +checkout("b\nc\nd\ne\n\n") + +prompt = "alo" +prepfile[[ -- +a = 2 +]] +RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out) +checkout(string.rep(prompt, 3).."\n") + +s = [=[ -- +function f ( x ) + local a = [[ +xuxu +]] + local b = "\ +xuxu\n" + if x == 11 then return 1 , 2 end --[[ test multiple returns ]] + return x + 1 + --\\ +end +=( f( 10 ) ) +assert( a == b ) +=f( 11 ) ]=] +s = string.gsub(s, ' ', '\n\n') +prepfile(s) +RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) +checkout("11\n1\t2\n\n") + +prepfile[[#comment in 1st line without \n at the end]] +RUN("lua %s", prog) + +prepfile("#comment with a binary file\n"..string.dump(loadstring("print(1)"))) +RUN("lua %s > %s", prog, out) +checkout("1\n") + +prepfile("#comment with a binary file\r\n"..string.dump(loadstring("print(1)"))) +RUN("lua %s > %s", prog, out) +checkout("1\n") + +-- close Lua with an open file +prepfile(string.format([[io.output(%q); io.write('alo')]], out)) +RUN("lua %s", prog) +checkout('alo') + +assert(os.remove(prog)) +assert(os.remove(otherprog)) +assert(not os.remove(out)) + +RUN("lua -v") + +NoRun("lua -h") +NoRun("lua -e") +NoRun("lua -e a") +NoRun("lua -f") + +print("OK") diff --git a/src/test/resources/bytecode-compiler/lua5.2/main.luac b/src/test/resources/bytecode-compiler/lua5.2/main.luac new file mode 100644 index 0000000000000000000000000000000000000000..6764d219e123655afbad756bbcc56241149d8208 GIT binary patch literal 4957 zcmb_gZF3t}6+Tzq$nvIcQYcAZFu4(9ET@*IvP<{?D!UH0r!P1jLm9yKWLEOp-l^V@ zR;?L^=`L**U`l78(^7`I!x#RClnxFv6#jw#1HXXbIrr|h)Tj7{bvrmhR3EIH&lL^BSFY~;{d-=TFH&~6mbQ_g^{5=FP5 zzeSO@fqt^@(hT%DqPMf>`FGCq(K{BVa~(l<;p5MsuR0WMMEB@w^q;q4nxKDzzTYPj zVuC)xcP@(FT_cBj{T13pOg_f^6=NaqMHJB>nxJFI72ThptXKgbi5`8A298D9*rhed zdccXxI-2M?w=m99PNRtZi9Y=gb_;7`OCXbt{O;h%M4 zZ~-)FT4dEctW&lwc-Zp1u*oRvha&+bJDKI%Z)jU&_|i^a<+4 zc|XhBV9dB!+veyWAL_9ehxYMBS{uAfJ@`nkFDq7QGG5QHE*@N@8LZzGkI@xZWO%Mz z^!dlCmi^B~)_s*+F-@^Mje1fh*N;Btd077NxL|K;I?%MLmw5bq?OpJQXFdK`rZ?E2 zfw)A+=#|}=&f?L1aOr<@7GFxu0sa5MkIId^nsLQt>Y+X}BI3S9Z=-Khv8!GXNva&_iCQ_vd(RP6LUT>q}>!9r|3aKqhEyh zlCr;s8oh))Ze{V+Y6p#wtScZxzY_+H16k{Oi&fcdg+a5?A!`yUuha3{Fq;6~gbc?J z`A2@W8~Qj@EI$95;i2EwI*ZwEXvY2fyVg`PPMc@Is?EY{>~2jA+e6JnVMEDM?} zei2`KWhHwFe`KKmB&A+wA@nLWfAPQ%8wIa@FfYqxxh%cLo&>jD+GT#un`4PB&4*P> zT2)ahWxT8<)wy|CCf?byn0P4`hqgsMJYV!>{%p3Q_AuKSaqxn2+z4tz%QF$1)|yoh zx&>KTkX2chi;IiH2*9M)AiXqGQB1M~jKoqodYvtuM@{cH+jTE#9ZtB8yIAD>da@#` zWIcge3>RH4BV3f~v2#)gE}xZ*izUCKii3KfMbA`9n$+#f(CEq+eu1U@a(;XH`Ca(A zl=y*{vl??>wEdQDUFn&wd(lz7>|tFul2M3XJ1x!UWzqM_%lMXg(_;B#a&GdHy^Mjp zIw`f@E-gsUHfbtNVWqH`g+*BwZr)fb-kg_6O}SjQJ-aMR&)dc?IFGD_B#5G)zq7e@ z`}>;IBy7fe8N0CF zLftqsUad*iMGy$i=JC80^5Bz#p2afT8I2&>_BqGm4U1v?z^3|p;%&d%sPYLS_hmsI z$y86OFzuu6QP)P4rd82*GQXq5JB>WV!9Iqe{YoW?!=oYFe%Ng{WJxZ_CFx@`BIfOF z$)}dAcWYtLs`-ggM=Hn3h$H!&EWu&1xU*wppj|G={Q_zr=Mh%YpL!EiASko01f3*x zq8)U)6}>tLc-5RpKC((ByEL`L+;iZ*UTxOvej}6tVnj%_pyA6mgYXdNm)vPcFXUx# z;JOT)VLpUr(qq{kkyL_)*Z#TO4{AQVbI0v<>*!&wRITalnQf>KPJJrseWkr7t;)fc z;NWWypJEy&*I*Jf7ZGc#8y04NhC>$Yo9!U<3v=j_IczU(p0;0azJW8HX`ZY2hJKA~ zG`G5ql=v`3!<++E<~}$3+2DPh(A%#NHxKvlQ$R*T;rn^{6x4X_$Pvc0Qj;FyA0d)Fkab0{2BPqlA7FsAI|wSA?GI3T!5f~ zp3>)ydYAKJ+P;T@b(-9@27FN2Kl2v!fqU{L(av`B+wnfQL-fDAAiIguZ5GS zI)WK7vastAa`Ft=_=~_9OoJy&gST)RoJ!6#-^@nAFXD0F<~HEg=f!eIzmUQ=L8oyO z{Dbf<=nNdt?gOv_xxp>aX{>@bSOcBLI`~n%1ew9hphxivWCk0c4Q_*e6l}uIU<-5_ zKLBs=8t9(@{QT({Y(r+S3%UaEvpNQQkQwZQHaGxna0q%70b~ZRgRTLM 10e30) + assert(-math.huge < -10e30) +end + +--[[function f(...) + if select('#', ...) == 1 then + return (...) + else + return "***" + end +end]] + +assert(tonumber{} == nil) +assert(tonumber'+0.01' == 1/100 and tonumber'+.01' == 0.01 and + tonumber'.01' == 0.01 and tonumber'-1.' == -1 and + tonumber'+1.' == 1) +assert(tonumber'+ 0.01' == nil and tonumber'+.e1' == nil and + tonumber'1e' == nil and tonumber'1.0e+' == nil and + tonumber'.' == nil) +assert(tonumber('-12') == -10-2) +assert(tonumber('-1.2e2') == - - -120) +assert(f(tonumber('1 a')) == nil) +assert(f(tonumber('e1')) == nil) +assert(f(tonumber('e 1')) == nil) +assert(f(tonumber(' 3.4.5 ')) == nil) +assert(f(tonumber('')) == nil) +assert(f(tonumber('', 8)) == nil) +assert(f(tonumber(' ')) == nil) +assert(f(tonumber(' ', 9)) == nil) +assert(f(tonumber('99', 8)) == nil) +assert(tonumber(' 1010 ', 2) == 10) +assert(tonumber('10', 36) == 36) +--assert(tonumber('\n -10 \n', 36) == -36) +--assert(tonumber('-fFfa', 16) == -(10+(16*(15+(16*(15+(16*15))))))) +assert(tonumber('fFfa', 15) == nil) +--assert(tonumber(string.rep('1', 42), 2) + 1 == 2^42) +assert(tonumber(string.rep('1', 32), 2) + 1 == 2^32) +--assert(tonumber('-fffffFFFFF', 16)-1 == -2^40) +assert(tonumber('ffffFFFF', 16)+1 == 2^32) + +assert(1.1 == 1.+.1) +assert(100.0 == 1E2 and .01 == 1e-2) +assert(1111111111111111-1111111111111110== 1000.00e-03) +-- 1234567890123456 +assert(1.1 == '1.'+'.1') +assert('1111111111111111'-'1111111111111110' == tonumber" +0.001e+3 \n\t") + +function eq (a,b,limit) + if not limit then limit = 10E-10 end + return math.abs(a-b) <= limit +end + +assert(0.1e-30 > 0.9E-31 and 0.9E30 < 0.1e31) + +assert(0.123456 > 0.123455) + +assert(tonumber('+1.23E30') == 1.23*10^30) + +-- testing order operators +assert(not(1<1) and (1<2) and not(2<1)) +assert(not('a'<'a') and ('a'<'b') and not('b'<'a')) +assert((1<=1) and (1<=2) and not(2<=1)) +assert(('a'<='a') and ('a'<='b') and not('b'<='a')) +assert(not(1>1) and not(1>2) and (2>1)) +assert(not('a'>'a') and not('a'>'b') and ('b'>'a')) +assert((1>=1) and not(1>=2) and (2>=1)) +assert(('a'>='a') and not('a'>='b') and ('b'>='a')) + +-- testing mod operator +assert(-4%3 == 2) +assert(4%-3 == -2) +assert(math.pi - math.pi % 1 == 3) +assert(math.pi - math.pi % 0.001 == 3.141) + +local function testbit(a, n) + return a/2^n % 2 >= 1 +end + +assert(eq(math.sin(-9.8)^2 + math.cos(-9.8)^2, 1)) +assert(eq(math.tan(math.pi/4), 1)) +assert(eq(math.sin(math.pi/2), 1) and eq(math.cos(math.pi/2), 0)) +assert(eq(math.atan(1), math.pi/4) and eq(math.acos(0), math.pi/2) and + eq(math.asin(1), math.pi/2)) +assert(eq(math.deg(math.pi/2), 90) and eq(math.rad(90), math.pi/2)) +assert(math.abs(-10) == 10) +assert(eq(math.atan2(1,0), math.pi/2)) +assert(math.ceil(4.5) == 5.0) +assert(math.floor(4.5) == 4.0) +assert(math.mod(10,3) == 1) +assert(eq(math.sqrt(10)^2, 10)) +assert(eq(math.log10(2), math.log(2)/math.log(10))) +assert(eq(math.exp(0), 1)) +assert(eq(math.sin(10), math.sin(10%(2*math.pi)))) +local v,e = math.frexp(math.pi) +assert(eq(math.ldexp(v,e), math.pi)) + +assert(eq(math.tanh(3.5), math.sinh(3.5)/math.cosh(3.5))) + +assert(tonumber(' 1.3e-2 ') == 1.3e-2) +assert(tonumber(' -1.00000000000001 ') == -1.00000000000001) + +-- testing constant limits +-- 2^23 = 8388608 +assert(8388609 + -8388609 == 0) +assert(8388608 + -8388608 == 0) +assert(8388607 + -8388607 == 0) + +if rawget(_G, "_soft") then return end + +f = io.tmpfile() +assert(f) +f:write("a = {") +i = 1 +repeat + f:write("{", math.sin(i), ", ", math.cos(i), ", ", i/3, "},\n") + i=i+1 +until i > 1000 +f:write("}") +f:seek("set", 0) +assert(loadstring(f:read('*a')))() +assert(f:close()) + +assert(eq(a[300][1], math.sin(300))) +assert(eq(a[600][1], math.sin(600))) +assert(eq(a[500][2], math.cos(500))) +assert(eq(a[800][2], math.cos(800))) +assert(eq(a[200][3], 200/3)) +assert(eq(a[1000][3], 1000/3, 0.001)) +print('+') + +do -- testing NaN + local NaN = 10e500 - 10e400 + assert(NaN ~= NaN) + assert(not (NaN < NaN)) + assert(not (NaN <= NaN)) + assert(not (NaN > NaN)) + assert(not (NaN >= NaN)) + assert(not (0 < NaN)) + assert(not (NaN < 0)) + local a = {} + assert(not pcall(function () a[NaN] = 1 end)) + assert(a[NaN] == nil) + a[1] = 1 + assert(not pcall(function () a[NaN] = 1 end)) + assert(a[NaN] == nil) +end + +require "checktable" +stat(a) + +a = nil + +-- testing implicit convertions + +local a,b = '10', '20' +assert(a*b == 200 and a+b == 30 and a-b == -10 and a/b == 0.5 and -b == -20) +assert(a == '10' and b == '20') + + +math.randomseed(0) + +local i = 0 +local Max = 0 +local Min = 2 +repeat + local t = math.random() + Max = math.max(Max, t) + Min = math.min(Min, t) + i=i+1 + flag = eq(Max, 1, 0.001) and eq(Min, 0, 0.001) +until flag or i>10000 +assert(0 <= Min and Max<1) +assert(flag); + +for i=1,10 do + local t = math.random(5) + assert(1 <= t and t <= 5) +end + +i = 0 +Max = -200 +Min = 200 +repeat + local t = math.random(-10,0) + Max = math.max(Max, t) + Min = math.min(Min, t) + i=i+1 + flag = (Max == 0 and Min == -10) +until flag or i>10000 +assert(-10 <= Min and Max<=0) +assert(flag); + + +print('OK') diff --git a/src/test/resources/bytecode-compiler/lua5.2/math.luac b/src/test/resources/bytecode-compiler/lua5.2/math.luac new file mode 100644 index 0000000000000000000000000000000000000000..6926245a9f4e1fdbc0e633e0efd8498bb5ba66a7 GIT binary patch literal 11819 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/nextvar.lua b/src/test/resources/bytecode-compiler/lua5.2/nextvar.lua new file mode 100644 index 00000000..4e9ffb83 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/nextvar.lua @@ -0,0 +1,396 @@ +print('testing tables, next, and for') + +local a = {} + +-- make sure table has lots of space in hash part +for i=1,100 do a[i.."+"] = true end +for i=1,100 do a[i.."+"] = nil end +-- fill hash part with numeric indices testing size operator +for i=1,100 do + a[i] = true + assert(#a == i) +end + + +if T then +-- testing table sizes + +local l2 = math.log(2) +local function log2 (x) return math.log(x)/l2 end + +local function mp2 (n) -- minimum power of 2 >= n + local mp = 2^math.ceil(log2(n)) + assert(n == 0 or (mp/2 < n and n <= mp)) + return mp +end + +local function fb (n) + local r, nn = T.int2fb(n) + assert(r < 256) + return nn +end + +-- test fb function +local a = 1 +local lim = 2^30 +while a < lim do + local n = fb(a) + assert(a <= n and n <= a*1.125) + a = math.ceil(a*1.3) +end + + +local function check (t, na, nh) + local a, h = T.querytab(t) + if a ~= na or h ~= nh then + print(na, nh, a, h) + assert(nil) + end +end + +-- testing constructor sizes +local lim = 40 +local s = 'return {' +for i=1,lim do + s = s..i..',' + local s = s + for k=0,lim do + local t = loadstring(s..'}')() + assert(#t == i) + check(t, fb(i), mp2(k)) + s = string.format('%sa%d=%d,', s, k, k) + end +end + + +-- tests with unknown number of elements +local a = {} +for i=1,lim do a[i] = i end -- build auxiliary table +for k=0,lim do + local a = {unpack(a,1,k)} + assert(#a == k) + check(a, k, 0) + a = {1,2,3,unpack(a,1,k)} + check(a, k+3, 0) + assert(#a == k + 3) +end + + +print'+' + +-- testing tables dynamically built +local lim = 130 +local a = {}; a[2] = 1; check(a, 0, 1) +a = {}; a[0] = 1; check(a, 0, 1); a[2] = 1; check(a, 0, 2) +a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1) +a = {} +for i = 1,lim do + a[i] = 1 + assert(#a == i) + check(a, mp2(i), 0) +end + +a = {} +for i = 1,lim do + a['a'..i] = 1 + assert(#a == 0) + check(a, 0, mp2(i)) +end + +a = {} +for i=1,16 do a[i] = i end +check(a, 16, 0) +for i=1,11 do a[i] = nil end +for i=30,40 do a[i] = nil end -- force a rehash (?) +check(a, 0, 8) +a[10] = 1 +for i=30,40 do a[i] = nil end -- force a rehash (?) +check(a, 0, 8) +for i=1,14 do a[i] = nil end +for i=30,50 do a[i] = nil end -- force a rehash (?) +check(a, 0, 4) + +-- reverse filling +for i=1,lim do + local a = {} + for i=i,1,-1 do a[i] = i end -- fill in reverse + check(a, mp2(i), 0) +end + +-- size tests for vararg +lim = 35 +--[[function foo (n, ...) + local arg = {...} + check(arg, n, 0) + assert(select('#', ...) == n) + arg[n+1] = true + check(arg, mp2(n+1), 0) + arg.x = true + check(arg, mp2(n+1), 1) +end]] +local a = {} +for i=1,lim do a[i] = true; foo(i, unpack(a)) end + +end + + +-- test size operation on empty tables +assert(#{} == 0) +assert(#{nil} == 0) +assert(#{nil, nil} == 0) +assert(#{nil, nil, nil} == 0) +assert(#{nil, nil, nil, nil} == 0) +print'+' + + +local nofind = {} + +a,b,c = 1,2,3 +a,b,c = nil + +local function find (name) + local n,v + while 1 do + n,v = next(_G, n) + if not n then return nofind end + assert(v ~= nil) + if n == name then return v end + end +end + +local function find1 (name) + for n,v in pairs(_G) do + if n==name then return v end + end + return nil -- not found +end + +do -- create 10000 new global variables + for i=1,10000 do _G[i] = i end +end + + +a = {x=90, y=8, z=23} +assert(table.foreach(a, function(i,v) if i=='x' then return v end end) == 90) +assert(table.foreach(a, function(i,v) if i=='a' then return v end end) == nil) +table.foreach({}, error) + +table.foreachi({x=10, y=20}, error) +local a = {n = 1} +table.foreachi({n=3}, function (i, v) + assert(a.n == i and not v) + a.n=a.n+1 +end) +a = {10,20,30,nil,50} +table.foreachi(a, function (i,v) assert(a[i] == v) end) +assert(table.foreachi({'a', 'b', 'c'}, function (i,v) + if i==2 then return v end + end) == 'b') + + +assert(print==find("print") and print == find1("print")) +assert(_G["print"]==find("print")) +assert(assert==find1("assert")) +assert(nofind==find("return")) +assert(not find1("return")) +_G["ret" .. "urn"] = nil +assert(nofind==find("return")) +_G["xxx"] = 1 +assert(xxx==find("xxx")) +print('+') + +a = {} +for i=0,10000 do + if math.mod(i,10) ~= 0 then + a['x'..i] = i + end +end + +n = {n=0} +for i,v in pairs(a) do + n.n = n.n+1 + assert(i and v and a[i] == v) +end +assert(n.n == 9000) +a = nil + +-- remove those 10000 new global variables +for i=1,10000 do _G[i] = nil end + +do -- clear global table + local a = {} + local preserve = {io = 1, string = 1, debug = 1, os = 1, + coroutine = 1, table = 1, math = 1} + for n,v in pairs(_G) do a[n]=v end + for n,v in pairs(a) do + if not preserve[n] and type(v) ~= "function" and + not string.find(n, "^[%u_]") then + _G[n] = nil + end + collectgarbage() + end +end + +local function foo () + local getfenv, setfenv, assert, next = + getfenv, setfenv, assert, next + local n = {gl1=3} + setfenv(foo, n) + assert(getfenv(foo) == getfenv(1)) + assert(getfenv(foo) == n) + assert(print == nil and gl1 == 3) + gl1 = nil + gl = 1 + assert(n.gl == 1 and next(n, 'gl') == nil) +end +foo() + +print'+' + +local function checknext (a) + local b = {} + table.foreach(a, function (k,v) b[k] = v end) + for k,v in pairs(b) do assert(a[k] == v) end + for k,v in pairs(a) do assert(b[k] == v) end + b = {} + do local k,v = next(a); while k do b[k] = v; k,v = next(a,k) end end + for k,v in pairs(b) do assert(a[k] == v) end + for k,v in pairs(a) do assert(b[k] == v) end +end + +checknext{1,x=1,y=2,z=3} +checknext{1,2,x=1,y=2,z=3} +checknext{1,2,3,x=1,y=2,z=3} +checknext{1,2,3,4,x=1,y=2,z=3} +checknext{1,2,3,4,5,x=1,y=2,z=3} + +assert(table.getn{} == 0) +assert(table.getn{[-1] = 2} == 0) +assert(table.getn{1,2,3,nil,nil} == 3) +for i=0,40 do + local a = {} + for j=1,i do a[j]=j end + assert(table.getn(a) == i) +end + + +assert(table.maxn{} == 0) +assert(table.maxn{["1000"] = true} == 0) +assert(table.maxn{["1000"] = true, [24.5] = 3} == 24.5) +assert(table.maxn{[1000] = true} == 1000) +assert(table.maxn{[10] = true, [100*math.pi] = print} == 100*math.pi) + + +-- int overflow +a = {} +for i=0,50 do a[math.pow(2,i)] = true end +assert(a[table.getn(a)]) + +print("+") + + +-- erasing values +local t = {[{1}] = 1, [{2}] = 2, [string.rep("x ", 4)] = 3, + [100.3] = 4, [4] = 5} + +local n = 0 +for k, v in pairs( t ) do + n = n+1 + assert(t[k] == v) + t[k] = nil + collectgarbage() + assert(t[k] == nil) +end +assert(n == 5) + + +local function test (a) + table.insert(a, 10); table.insert(a, 2, 20); + table.insert(a, 1, -1); table.insert(a, 40); + table.insert(a, table.getn(a)+1, 50) + table.insert(a, 2, -2) + assert(table.remove(a,1) == -1) + assert(table.remove(a,1) == -2) + assert(table.remove(a,1) == 10) + assert(table.remove(a,1) == 20) + assert(table.remove(a,1) == 40) + assert(table.remove(a,1) == 50) + assert(table.remove(a,1) == nil) +end + +a = {n=0, [-7] = "ban"} +test(a) +assert(a.n == 0 and a[-7] == "ban") + +a = {[-7] = "ban"}; +test(a) +assert(a.n == nil and table.getn(a) == 0 and a[-7] == "ban") + + +table.insert(a, 1, 10); table.insert(a, 1, 20); table.insert(a, 1, -1) +assert(table.remove(a) == 10) +assert(table.remove(a) == 20) +assert(table.remove(a) == -1) + +a = {'c', 'd'} +table.insert(a, 3, 'a') +table.insert(a, 'b') +assert(table.remove(a, 1) == 'c') +assert(table.remove(a, 1) == 'd') +assert(table.remove(a, 1) == 'a') +assert(table.remove(a, 1) == 'b') +assert(table.getn(a) == 0 and a.n == nil) +print("+") + +a = {} +for i=1,1000 do + a[i] = i; a[i-1] = nil +end +assert(next(a,nil) == 1000 and next(a,1000) == nil) + +assert(next({}) == nil) +assert(next({}, nil) == nil) + +for a,b in pairs{} do error"not here" end +for i=1,0 do error'not here' end +for i=0,1,-1 do error'not here' end +a = nil; for i=1,1 do assert(not a); a=1 end; assert(a) +a = nil; for i=1,1,-1 do assert(not a); a=1 end; assert(a) + +a = 0; for i=0, 1, 0.1 do a=a+1 end; assert(a==11) +-- precision problems +--a = 0; for i=1, 0, -0.01 do a=a+1 end; assert(a==101) +a = 0; for i=0, 0.999999999, 0.1 do a=a+1 end; assert(a==10) +a = 0; for i=1, 1, 1 do a=a+1 end; assert(a==1) +a = 0; for i=1e10, 1e10, -1 do a=a+1 end; assert(a==1) +a = 0; for i=1, 0.99999, 1 do a=a+1 end; assert(a==0) +a = 0; for i=99999, 1e5, -1 do a=a+1 end; assert(a==0) +a = 0; for i=1, 0.99999, -1 do a=a+1 end; assert(a==1) + +-- conversion +a = 0; for i="10","1","-2" do a=a+1 end; assert(a==5) + + +collectgarbage() + + +-- testing generic 'for' + +local function f (n, p) + local t = {}; for i=1,p do t[i] = i*10 end + return function (_,n) + if n > 0 then + n = n-1 + return n, unpack(t) + end + end, nil, n +end + +local x = 0 +for n,a,b,c,d in f(5,3) do + x = x+1 + assert(a == 10 and b == 20 and c == 30 and d == nil) +end +assert(x == 5) + +print"OK" diff --git a/src/test/resources/bytecode-compiler/lua5.2/nextvar.luac b/src/test/resources/bytecode-compiler/lua5.2/nextvar.luac new file mode 100644 index 0000000000000000000000000000000000000000..dff65c4203dedb0beffa0e15a7dab7ff860d9304 GIT binary patch literal 22086 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 Vt8JQA>z!8H9M!Omn)+Jn{{bX+9%cXl literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/pm.lua b/src/test/resources/bytecode-compiler/lua5.2/pm.lua new file mode 100644 index 00000000..fa125dc9 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/pm.lua @@ -0,0 +1,273 @@ +print('testing pattern matching') + +function f(s, p) + local i,e = string.find(s, p) + if i then return string.sub(s, i, e) end +end + +function f1(s, p) + p = string.gsub(p, "%%([0-9])", function (s) return "%" .. (s+1) end) + p = string.gsub(p, "^(^?)", "%1()", 1) + p = string.gsub(p, "($?)$", "()%1", 1) + local t = {string.match(s, p)} + return string.sub(s, t[1], t[#t] - 1) +end + +a,b = string.find('', '') -- empty patterns are tricky +assert(a == 1 and b == 0); +a,b = string.find('alo', '') +assert(a == 1 and b == 0) +a,b = string.find('a\0o a\0o a\0o', 'a', 1) -- first position +assert(a == 1 and b == 1) +a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the midle +assert(a == 5 and b == 7) +a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the midle +assert(a == 9 and b == 11) +a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end +assert(a == 9 and b == 11); +a,b = string.find('a\0a\0a\0a\0\0ab', 'b') -- last position +assert(a == 11 and b == 11) +assert(string.find('a\0a\0a\0a\0\0ab', 'b\0') == nil) -- check ending +assert(string.find('', '\0') == nil) +assert(string.find('alo123alo', '12') == 4) +assert(string.find('alo123alo', '^12') == nil) + +assert(f('aloALO', '%l*') == 'alo') +assert(f('aLo_ALO', '%a*') == 'aLo') + +assert(f('aaab', 'a*') == 'aaa'); +assert(f('aaa', '^.*$') == 'aaa'); +assert(f('aaa', 'b*') == ''); +assert(f('aaa', 'ab*a') == 'aa') +assert(f('aba', 'ab*a') == 'aba') +assert(f('aaab', 'a+') == 'aaa') +assert(f('aaa', '^.+$') == 'aaa') +assert(f('aaa', 'b+') == nil) +assert(f('aaa', 'ab+a') == nil) +assert(f('aba', 'ab+a') == 'aba') +assert(f('a$a', '.$') == 'a') +assert(f('a$a', '.%$') == 'a$') +assert(f('a$a', '.$.') == 'a$a') +assert(f('a$a', '$$') == nil) +assert(f('a$b', 'a$') == nil) +assert(f('a$a', '$') == '') +assert(f('', 'b*') == '') +assert(f('aaa', 'bb*') == nil) +assert(f('aaab', 'a-') == '') +assert(f('aaa', '^.-$') == 'aaa') +assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab') +assert(f('aabaaabaaabaaaba', 'b.-b') == 'baaab') +assert(f('alo xo', '.o$') == 'xo') +assert(f(' \n isto é assim', '%S%S*') == 'isto') +assert(f(' \n isto é assim', '%S*$') == 'assim') +assert(f(' \n isto é assim', '[a-z]*$') == 'assim') +assert(f('um caracter ? extra', '[^%sa-z]') == '?') +assert(f('', 'a?') == '') +assert(f('á', 'á?') == 'á') +assert(f('ábl', 'á?b?l?') == 'ábl') +assert(f(' ábl', 'á?b?l?') == '') +assert(f('aa', '^aa?a?a') == 'aa') +assert(f(']]]áb', '[^]]') == 'á') +assert(f("0alo alo", "%x*") == "0a") +assert(f("alo alo", "%C+") == "alo alo") +print('+') + +assert(f1('alo alx 123 b\0o b\0o', '(..*) %1') == "b\0o b\0o") +assert(f1('axz123= 4= 4 34', '(.+)=(.*)=%2 %1') == '3= 4= 4 3') +assert(f1('=======', '^(=*)=%1$') == '=======') +assert(string.match('==========', '^([=]*)=%1$') == nil) + +local function range (i, j) + if i <= j then + return i, range(i+1, j) + end +end + +local abc = string.char(range(0, 255)); + +assert(string.len(abc) == 256) + +function strset (p) + local res = {s=''} + string.gsub(abc, p, function (c) res.s = res.s .. c end) + return res.s +end; + +assert(string.len(strset('[\200-\210]')) == 11) + +assert(strset('[a-z]') == "abcdefghijklmnopqrstuvwxyz") +assert(strset('[a-z%d]') == strset('[%da-uu-z]')) +assert(strset('[a-]') == "-a") +assert(strset('[^%W]') == strset('[%w]')) +assert(strset('[]%%]') == '%]') +assert(strset('[a%-z]') == '-az') +assert(strset('[%^%[%-a%]%-b]') == '-[]^ab') +assert(strset('%Z') == strset('[\1-\255]')) +assert(strset('.') == strset('[\1-\255%z]')) +print('+'); + +assert(string.match("alo xyzK", "(%w+)K") == "xyz") +assert(string.match("254 K", "(%d*)K") == "") +assert(string.match("alo ", "(%w*)$") == "") +assert(string.match("alo ", "(%w+)$") == nil) +assert(string.find("(álo)", "%(á") == 1) +local a, b, c, d, e = string.match("âlo alo", "^(((.).).* (%w*))$") +assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil) +a, b, c, d = string.match('0123456789', '(.+(.?)())') +assert(a == '0123456789' and b == '' and c == 11 and d == nil) +print('+') + +assert(string.gsub('ülo ülo', 'ü', 'x') == 'xlo xlo') +assert(string.gsub('alo úlo ', ' +$', '') == 'alo úlo') -- trim +assert(string.gsub(' alo alo ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim +assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ') +t = "abç d" +a, b = string.gsub(t, '(.)', '%1@') +assert('@'..a == string.gsub(t, '', '@') and b == 5) +a, b = string.gsub('abçd', '(.)', '%0@', 2) +assert(a == 'a@b@çd' and b == 2) +assert(string.gsub('alo alo', '()[al]', '%1') == '12o 56o') +assert(string.gsub("abc=xyz", "(%w*)(%p)(%w+)", "%3%2%1-%0") == + "xyz=abc-abc=xyz") +assert(string.gsub("abc", "%w", "%1%0") == "aabbcc") +assert(string.gsub("abc", "%w+", "%0%1") == "abcabc") +assert(string.gsub('áéí', '$', '\0óú') == 'áéí\0óú') +assert(string.gsub('', '^', 'r') == 'r') +assert(string.gsub('', '$', 'r') == 'r') +print('+') + +assert(string.gsub("um (dois) tres (quatro)", "(%(%w+%))", string.upper) == + "um (DOIS) tres (QUATRO)") + +do + local function setglobal (n,v) rawset(_G, n, v) end + string.gsub("a=roberto,roberto=a", "(%w+)=(%w%w*)", setglobal) + assert(_G.a=="roberto" and _G.roberto=="a") +end + +function f(a,b) return string.gsub(a,'.',b) end +assert(string.gsub("trocar tudo em |teste|b| é |beleza|al|", "|([^|]*)|([^|]*)|", f) == + "trocar tudo em bbbbb é alalalalalal") + +local function dostring (s) return loadstring(s)() or "" end +assert(string.gsub("alo $a=1$ novamente $return a$", "$([^$]*)%$", dostring) == + "alo novamente 1") + +x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$", + "$([^$]*)%$", dostring) +assert(x == ' assim vai para ALO') + +t = {} +s = 'a alo jose joao' +r = string.gsub(s, '()(%w+)()', function (a,w,b) + assert(string.len(w) == b-a); + t[a] = b-a; + end) +assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4) + + +function isbalanced (s) + return string.find(string.gsub(s, "%b()", ""), "[()]") == nil +end + +assert(isbalanced("(9 ((8))(\0) 7) \0\0 a b ()(c)() a")) +assert(not isbalanced("(9 ((8) 7) a b (\0 c) a")) +assert(string.gsub("alo 'oi' alo", "%b''", '"') == 'alo " alo') + + +local t = {"apple", "orange", "lime"; n=0} +assert(string.gsub("x and x and x", "x", function () t.n=t.n+1; return t[t.n] end) + == "apple and orange and lime") + +t = {n=0} +string.gsub("first second word", "%w%w*", function (w) t.n=t.n+1; t[t.n] = w end) +assert(t[1] == "first" and t[2] == "second" and t[3] == "word" and t.n == 3) + +t = {n=0} +assert(string.gsub("first second word", "%w+", + function (w) t.n=t.n+1; t[t.n] = w end, 2) == "first second word") +assert(t[1] == "first" and t[2] == "second" and t[3] == nil) + +assert(not pcall(string.gsub, "alo", "(.", print)) +assert(not pcall(string.gsub, "alo", ".)", print)) +assert(not pcall(string.gsub, "alo", "(.", {})) +assert(not pcall(string.gsub, "alo", "(.)", "%2")) +assert(not pcall(string.gsub, "alo", "(%1)", "a")) +assert(not pcall(string.gsub, "alo", "(%0)", "a")) + +-- big strings +local a = string.rep('a', 300000) +assert(string.find(a, '^a*.?$')) +assert(not string.find(a, '^a*.?b$')) +assert(string.find(a, '^a-.?$')) + +-- deep nest of gsubs +function rev (s) + return string.gsub(s, "(.)(.+)", function (c,s1) return rev(s1)..c end) +end + +local x = string.rep('012345', 10) +assert(rev(rev(x)) == x) + + +-- gsub with tables +assert(string.gsub("alo alo", ".", {}) == "alo alo") +assert(string.gsub("alo alo", "(.)", {a="AA", l=""}) == "AAo AAo") +assert(string.gsub("alo alo", "(.).", {a="AA", l="K"}) == "AAo AAo") +assert(string.gsub("alo alo", "((.)(.?))", {al="AA", o=false}) == "AAo AAo") + +assert(string.gsub("alo alo", "().", {2,5,6}) == "256 alo") + +t = {}; setmetatable(t, {__index = function (t,s) return string.upper(s) end}) +assert(string.gsub("a alo b hi", "%w%w+", t) == "a ALO b HI") + + +-- tests for gmatch +assert(string.gfind == string.gmatch) +local a = 0 +for i in string.gmatch('abcde', '()') do assert(i == a+1); a=i end +assert(a==6) + +t = {n=0} +for w in string.gmatch("first second word", "%w+") do + t.n=t.n+1; t[t.n] = w +end +assert(t[1] == "first" and t[2] == "second" and t[3] == "word") + +t = {3, 6, 9} +for i in string.gmatch ("xuxx uu ppar r", "()(.)%2") do + assert(i == table.remove(t, 1)) +end +assert(table.getn(t) == 0) + +t = {} +for i,j in string.gmatch("13 14 10 = 11, 15= 16, 22=23", "(%d+)%s*=%s*(%d+)") do + t[i] = j +end +a = 0 +for k,v in pairs(t) do assert(k+1 == v+0); a=a+1 end +assert(a == 3) + + +-- tests for `%f' (`frontiers') + +assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x") +assert(string.gsub("[[]] [][] [[[[", "%f[[].", "x") == "x[]] x]x] x[[[") +assert(string.gsub("01abc45de3", "%f[%d]", ".") == ".01abc.45de.3") +assert(string.gsub("01abc45 de3x", "%f[%D]%w", ".") == "01.bc45 de3.") +assert(string.gsub("function", "%f[\1-\255]%w", ".") == ".unction") +assert(string.gsub("function", "%f[^\1-\255]", ".") == "function.") + +local i, e = string.find(" alo aalo allo", "%f[%S].-%f[%s].-%f[%S]") +assert(i == 2 and e == 5) +local k = string.match(" alo aalo allo", "%f[%S](.-%f[%s].-%f[%S])") +assert(k == 'alo ') + +local a = {1, 5, 9, 14, 17,} +for k in string.gmatch("alo alo th02 is 1hat", "()%f[%w%d]") do + assert(table.remove(a, 1) == k) +end +assert(table.getn(a) == 0) + + +print('OK') diff --git a/src/test/resources/bytecode-compiler/lua5.2/pm.luac b/src/test/resources/bytecode-compiler/lua5.2/pm.luac new file mode 100644 index 0000000000000000000000000000000000000000..c8cdf232b076f73cba8188dc90851e9f99ff3f64 GIT binary patch literal 21413 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/sort.lua b/src/test/resources/bytecode-compiler/lua5.2/sort.lua new file mode 100644 index 00000000..6fccd49e --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/sort.lua @@ -0,0 +1,74 @@ +print"testing sort" + + +function check (a, f) + f = f or function (x,y) return x__!L&a3q`s0!OyBVxf4(y_cRM?_rA8ORL4Hp)_#MJt@+7Gy3pA?=qET<7Y=D>=upt&g8-qd_b3_K#!WY(RJ=iSJ0u8|? z!dl%fqFt-q0p6QbK@NMWVQ&qLHujds-fGy}Y>>~)VQ>9`Fh;Pqd@7E1^}~nBJuV{d zA*oXPTt1#!?l{enYT4@|wioF~dAo`DIBzZSNebiZ7k$PE{q>KSTH>i|dx^Zu^=rz{ zB6=;1MfF8>AR#s@L)996bWWf@q)^XLzp+`7nnxYqsRsu4%$*$39ieCr%{q$x{H7jU zL7p>#>_Fdkpl>_Sx1H$QPV{XjjY-OI4U%z(y^^edqK_8Q{~XtVB)Wxj(68td=zpB7 zWgs`iwakRMx`~+PQO|{7J|k{#pt}Ky8x8R(*EKIrQVsQ<6(Td=7@(n?5+m}#m6hU| zuMeOTe-u$}5I05GfB=y>Y$n+BY&%p!>%{ zb5piINwKOs=1oPaUNzoz+2dWa<*JYLAY`V;y$bZm+zI*&QpBn18nc|TGv&B`)hfAG z*MWoMR@EsUAafU1op$zf;Hxj>MOOD|=*iVvfeK%(ss0 z-y7A-=7~ojN0hGRPnN2d=b~<$@f~;hf}SPwEzEkfMg)St&&w{G7tcRuDFb zzNiaT!=;6R+PVb|b~lVdw}E4)#4kc+_-goE{c(kl38y7I35i}rtGPNvYBtt)$-Hrp zc?-+i$|SeWf%1$U2BD3lNA4cRrBI#iGk3Yk zF~LC)wc6x_rERhHO5l922G-+R=m>1kMC^s*8D}QsHelR?lZ4YU=3LqvM)eeAO?h~} zm*m`V;rgoYOfQMqs7pNgB%rfA+&lO-?S+7WbaO!51s|Xb+7+p@NHf0#(y3=_;U(8R zhxd;`v~1$orgpK~$Tk^KVOWc~s4&d6#JmpzhHjA+;>3#P(^^hWyEc*@TO-j0YHg8M zpwB=6+`I1s5ARD}sTCvTVX zi0X84)5 z#9EE#K{Ixx&;>pXUbANzEO$e`fOSc{2!1)}HR=28C9F^4W$-KEAm)-tURRnvgf(kL z@)ly6KcoLEUW1K?;dQJ@;s|&RzUee%OsCIX59EyA{|R~6NumHgjpLAM^n*@<&jz2v zaSQ$UOP;wXi$R+-ak;kljqmW20K0GOERq(Q8n=0r0&XZf?SHor>*ymA&*OWZwc!hgEz9J4 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/strings.lua b/src/test/resources/bytecode-compiler/lua5.2/strings.lua new file mode 100644 index 00000000..237dbad3 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/strings.lua @@ -0,0 +1,176 @@ +print('testing strings and string library') + +assert('alo' < 'alo1') +assert('' < 'a') +assert('alo\0alo' < 'alo\0b') +assert('alo\0alo\0\0' > 'alo\0alo\0') +assert('alo' < 'alo\0') +assert('alo\0' > 'alo') +assert('\0' < '\1') +assert('\0\0' < '\0\1') +assert('\1\0a\0a' <= '\1\0a\0a') +assert(not ('\1\0a\0b' <= '\1\0a\0a')) +assert('\0\0\0' < '\0\0\0\0') +assert(not('\0\0\0\0' < '\0\0\0')) +assert('\0\0\0' <= '\0\0\0\0') +assert(not('\0\0\0\0' <= '\0\0\0')) +assert('\0\0\0' <= '\0\0\0') +assert('\0\0\0' >= '\0\0\0') +assert(not ('\0\0b' < '\0\0a\0')) +print('+') + +assert(string.sub("123456789",2,4) == "234") +assert(string.sub("123456789",7) == "789") +assert(string.sub("123456789",7,6) == "") +assert(string.sub("123456789",7,7) == "7") +assert(string.sub("123456789",0,0) == "") +assert(string.sub("123456789",-10,10) == "123456789") +assert(string.sub("123456789",1,9) == "123456789") +assert(string.sub("123456789",-10,-20) == "") +assert(string.sub("123456789",-1) == "9") +assert(string.sub("123456789",-4) == "6789") +assert(string.sub("123456789",-6, -4) == "456") +assert(string.sub("\000123456789",3,5) == "234") +assert(("\000123456789"):sub(8) == "789") +print('+') + +assert(string.find("123456789", "345") == 3) +a,b = string.find("123456789", "345") +assert(string.sub("123456789", a, b) == "345") +assert(string.find("1234567890123456789", "345", 3) == 3) +assert(string.find("1234567890123456789", "345", 4) == 13) +assert(string.find("1234567890123456789", "346", 4) == nil) +assert(string.find("1234567890123456789", ".45", -9) == 13) +assert(string.find("abcdefg", "\0", 5, 1) == nil) +assert(string.find("", "") == 1) +assert(string.find('', 'aaa', 1) == nil) +assert(('alo(.)alo'):find('(.)', 1, 1) == 4) +print('+') + +assert(string.len("") == 0) +assert(string.len("\0\0\0") == 3) +assert(string.len("1234567890") == 10) + +assert(#"" == 0) +assert(#"\0\0\0" == 3) +assert(#"1234567890" == 10) + +assert(string.byte("a") == 97) +assert(string.byte("á") > 127) +assert(string.byte(string.char(255)) == 255) +assert(string.byte(string.char(0)) == 0) +assert(string.byte("\0") == 0) +assert(string.byte("\0\0alo\0x", -1) == string.byte('x')) +assert(string.byte("ba", 2) == 97) +assert(string.byte("\n\n", 2, -1) == 10) +assert(string.byte("\n\n", 2, 2) == 10) +assert(string.byte("") == nil) +assert(string.byte("hi", -3) == nil) +assert(string.byte("hi", 3) == nil) +assert(string.byte("hi", 9, 10) == nil) +assert(string.byte("hi", 2, 1) == nil) +assert(string.char() == "") +assert(string.char(0, 255, 0) == "\0\255\0") +assert(string.char(0, string.byte("á"), 0) == "\0á\0") +assert(string.char(string.byte("ál\0óu", 1, -1)) == "ál\0óu") +assert(string.char(string.byte("ál\0óu", 1, 0)) == "") +assert(string.char(string.byte("ál\0óu", -10, 100)) == "ál\0óu") +print('+') + +assert(string.upper("ab\0c") == "AB\0C") +assert(string.lower("\0ABCc%$") == "\0abcc%$") +assert(string.rep('teste', 0) == '') +assert(string.rep('tés\00tê', 2) == 'tés\0têtés\000tê') +assert(string.rep('', 10) == '') + +assert(string.reverse"" == "") +assert(string.reverse"\0\1\2\3" == "\3\2\1\0") +assert(string.reverse"\0001234" == "4321\0") + +for i=0,30 do assert(string.len(string.rep('a', i)) == i) end + +assert(type(tostring(nil)) == 'string') +assert(type(tostring(12)) == 'string') +assert(''..12 == '12' and type(12 .. '') == 'string') +assert(string.find(tostring{}, 'table:')) +assert(string.find(tostring(print), 'function:')) +assert(tostring(1234567890123) == '1234567890123') +assert(#tostring('\0') == 1) +assert(tostring(true) == "true") +assert(tostring(false) == "false") +print('+') + +x = '"ílo"\n\\' +assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\') +assert(string.format('%q', "\0") == [["\000"]]) +assert(string.format("\0%c\0%c%x\0", string.byte("á"), string.byte("b"), 140) == + "\0á\0b8c\0") +assert(string.format('') == "") +assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) == + string.format("%c%c%c%c", 34, 48, 90, 100)) +assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be') +assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023") +assert(tonumber(string.format("%f", 10.3)) == 10.3) +x = string.format('"%-50s"', 'a') +assert(#x == 52) +assert(string.sub(x, 1, 4) == '"a ') + +assert(string.format("-%.20s.20s", string.rep("%", 2000)) == "-"..string.rep("%", 20)..".20s") +assert(string.format('"-%20s.20s"', string.rep("%", 2000)) == + string.format("%q", "-"..string.rep("%", 2000)..".20s")) + + +-- longest number that can be formated +assert(string.len(string.format('%99.99f', -1e308)) >= 100) + +assert(loadstring("return 1\n--comentário sem EOL no final")() == 1) + + +assert(table.concat{} == "") +assert(table.concat({}, 'x') == "") +assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2") +local a = {}; for i=1,3000 do a[i] = "xuxu" end +assert(table.concat(a, "123").."123" == string.rep("xuxu123", 3000)) +assert(table.concat(a, "b", 20, 20) == "xuxu") +assert(table.concat(a, "", 20, 21) == "xuxuxuxu") +assert(table.concat(a, "", 22, 21) == "") +assert(table.concat(a, "3", 2999) == "xuxu3xuxu") + +a = {"a","b","c"} +assert(table.concat(a, ",", 1, 0) == "") +assert(table.concat(a, ",", 1, 1) == "a") +assert(table.concat(a, ",", 1, 2) == "a,b") +assert(table.concat(a, ",", 2) == "b,c") +assert(table.concat(a, ",", 3) == "c") +assert(table.concat(a, ",", 4) == "") + +local locales = { "ptb", "ISO-8859-1", "pt_BR" } +local function trylocale (w) + for _, l in ipairs(locales) do + if os.setlocale(l, w) then return true end + end + return false +end + +if not trylocale("collate") then + print("locale not supported") +else + assert("alo" < "álo" and "álo" < "amo") +end + +if not trylocale("ctype") then + print("locale not supported") +else + assert(string.gsub("áéíóú", "%a", "x") == "xxxxx") + assert(string.gsub("áÁéÉ", "%l", "x") == "xÁxÉ") + assert(string.gsub("áÁéÉ", "%u", "x") == "áxéx") + assert(string.upper"áÁé{xuxu}ção" == "ÁÁÉ{XUXU}ÇÃO") +end + +os.setlocale("C") +assert(os.setlocale() == 'C') +assert(os.setlocale(nil, "numeric") == 'C') + +print('OK') + + diff --git a/src/test/resources/bytecode-compiler/lua5.2/strings.luac b/src/test/resources/bytecode-compiler/lua5.2/strings.luac new file mode 100644 index 0000000000000000000000000000000000000000..d1cbfbce34789b8866568207b56c36cf833b063e GIT binary patch literal 14068 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/lua5.2/verybig.lua b/src/test/resources/bytecode-compiler/lua5.2/verybig.lua new file mode 100644 index 00000000..59e0142a --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/verybig.lua @@ -0,0 +1,100 @@ +if rawget(_G, "_soft") then return 10 end + +print "testing large programs (>64k)" + +-- template to create a very big test file +prog = [[$ + +local a,b + +b = {$1$ + b30009 = 65534, + b30010 = 65535, + b30011 = 65536, + b30012 = 65537, + b30013 = 16777214, + b30014 = 16777215, + b30015 = 16777216, + b30016 = 16777217, + b30017 = 4294967294, + b30018 = 4294967295, + b30019 = 4294967296, + b30020 = 4294967297, + b30021 = -65534, + b30022 = -65535, + b30023 = -65536, + b30024 = -4294967297, + b30025 = 15012.5, + $2$ +}; + +assert(b.a50008 == 25004 and b["a11"] == 5.5) +assert(b.a33007 == 16503.5 and b.a50009 == 25004.5) +assert(b["b"..30024] == -4294967297) + +function b:xxx (a,b) return a+b end +assert(b:xxx(10, 12) == 22) -- pushself with non-constant index +b.xxx = nil + +s = 0; n=0 +for a,b in pairs(b) do s=s+b; n=n+1 end +assert(s==13977183656.5 and n==70001) + +require "checktable" +stat(b) + +a = nil; b = nil +print'+' + +function f(x) b=x end + +a = f{$3$} or 10 + +assert(a==10) +assert(b[1] == "a10" and b[2] == 5 and b[table.getn(b)-1] == "a50009") + + +function xxxx (x) return b[x] end + +assert(xxxx(3) == "a11") + +a = nil; b=nil +xxxx = nil + +return 10 + +]] + +-- functions to fill in the $n$ +F = { +function () -- $1$ + for i=10,50009 do + io.write('a', i, ' = ', 5+((i-10)/2), ',\n') + end +end, + +function () -- $2$ + for i=30026,50009 do + io.write('b', i, ' = ', 15013+((i-30026)/2), ',\n') + end +end, + +function () -- $3$ + for i=10,50009 do + io.write('"a', i, '", ', 5+((i-10)/2), ',\n') + end +end, +} + +file = os.tmpname() +io.output(file) +for s in string.gmatch(prog, "$([^$]+)") do + local n = tonumber(s) + if not n then io.write(s) else F[n]() end +end +io.close() +result = dofile(file) +assert(os.remove(file)) +print'OK' +return result + diff --git a/src/test/resources/bytecode-compiler/lua5.2/verybig.luac b/src/test/resources/bytecode-compiler/lua5.2/verybig.luac new file mode 100644 index 0000000000000000000000000000000000000000..76939ec9c4e5a95917f54a27a28b1523a323472d GIT binary patch literal 3122 zcmcguU60#T5FMu>ySTLU0jVn0(o2ZcM7s%bKD$%_d)wVbPzqX55iCowvrg7(Vh7u4 zgQ}{IsCeL&pMdxuDS}6S0*^>M!aGQuxpv|didc!NmX3YrcxLX*+i!nqSJw7KS;}X-C&-8WF8Uk#Zi~S)C$fMs$Hl$&o>ZuF#rJ%IeJc@+4l_ z(D_a$`XwSN=#jEY^dJ{)(aLO#eb6dJ4{~&cR`e_I4Gy-s2=WYaWYCndv_@0qES-pQ z##PdlNLibba^Aq)q(nQ)722Ktkcq$_7^Iwy2;3I!D$~q?5m6prDZzx%b6^+7oKaJ&VpX}u5nJ~ zM-s1bQj%KutZ`P7O8s$8<;OUbX*H`_ts>0}wSs!@vZ|UvVEbXws+dhw8=-eZ4WouI z-9usR=S@w^A28djG)qg`bu13@=b|>N^-43=kG&w%#LKeHe%{JgDln@xq{F2kORCx% zyWP<7Tw%R7nM_0xo1r9pI~@D2FxM?%yN8R}S)-^`H-uIz$*J%VqFffEac~sagPyqK zgh#^l+;Z1*gV1zC;kbwPM71jHydzv^psE1|)yu-|RMnp6^Y(+57@3YA6fyasCxT9} zZn1{Dt}RCpbUIpnv)$G%)mzOLl9$=LolYAy)R2vDe?E46TjaY(cK23jS_3<;A`lcB zZ_PM@%YrvMha!*8#r2CzW#|PyRTt}KB9dylF8i7+Emp0X zPgbxNuU;a_P%1djE+Q$VTEE{)?aRtNAl`$ zR?%P2YKUgG)8mbeiVdgH0T{X z?hnnddqml1F)kGMKP?=rqrb2edhU2=*?!!w?)Xj!m)yF#124c(b~Za04-&73UK|SZ z@tK(G487Y)H1EDGTiy;--U5i!Gq@>$p_BYzysT%U+(VsK6eD983O)^y@^Grq>C8+y z^SzNhmB~CXwlcJ4&>3>S3O~VdqehJt^)_3{#`-{;l58-cQG^ z_dCd88P_2n2W-O?csljo!%R1TCtYv;Q#juKX03d(!SfscV}rjBN6YvC@^Qd6T!N=l zaNh08KTqhEq~pU7tNe4!m70U~w*fwxyjwVd4--U^BobG9(SH5{=tUNZ literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/regressions/bigattr.lc b/src/test/resources/bytecode-compiler/regressions/bigattr.lc deleted file mode 100644 index 89cfe19d71bb06fa0fd686ad3827c1aecd103015..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmb34DNPJyU}WK7;b7opfB=W2%=E;Pk|MpF(nP2bh+<;m1d?1p%nrn?K+M^2paD!X z9BhQ}4>rj$G=Swq@+kM dr!umY<6Zs27yv)U6-NL7 literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/regressions/comparators.lc b/src/test/resources/bytecode-compiler/regressions/comparators.lc deleted file mode 100644 index ff3e574d564aa251951bdb345b627e5fe94e1bd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 230 zcmb34DNPJyU}WK7;b0JCfB=W&{M>@XqQsK?qGG+A(nP2*h+<;q0g|jh?CQY45Dvs* z4U7$}2Y`GAMut|PxXghDM)w8=26iA@jNxy+976+GJqr_100c4_;LJ1xvy6cSWGV>c crB;+6c*UtXX<%WH#f)eeWF{`m4Auo90R3SVKmY&$ diff --git a/src/test/resources/bytecode-compiler/regressions/comparators.luac b/src/test/resources/bytecode-compiler/regressions/comparators.luac new file mode 100644 index 0000000000000000000000000000000000000000..56dbdd5d377b06b9810b18da212ab1ca5223dbd8 GIT binary patch literal 226 zcmb34DNPJwU}Rxo;b4%Q%*!Rk#Q+10%sfCQ8-oLbn}Y*GgaZSEcmrbt+W{b-!I7a2 zC@y=Tfsw<3p^<}up+TJCZ@oN20|P5i4+|5J$b?X75UPxU1tgP~T2TUF6{qH;0RGf)l$Kzcy7#k=~2F#rJR-x)vv literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/regressions/construct.lc b/src/test/resources/bytecode-compiler/regressions/construct.lc deleted file mode 100644 index 0fbb681a933119303a44162d16729904cea19527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 zcmb34DNPJyU}WK7;b7orfB=W&{Ji3lqSE9Ny`0iSs33@9VrB=DTtMvVz`)Shz`zj0 tz`&>kWP`-z7#bLufiw#fkN|-S1T&3+g#{wWkeHJXW`VRY;=xem3;?E#42u8& diff --git a/src/test/resources/bytecode-compiler/regressions/construct.luac b/src/test/resources/bytecode-compiler/regressions/construct.luac new file mode 100644 index 0000000000000000000000000000000000000000..79a58f216fa01aa5c9273f41fd060bd0c25bdc68 GIT binary patch literal 166 zcmb34DNPJwU}Rxo;b4%Q%*!Rk#Q+10%13Q<3ot>RRNosM4vqD~ei9%v>NoHB9 zl|pfLW>> testC not active: skipping API tests <<<\n\a') -print('\a\b\f\n\r\v\\\'\"\[\]') +print('\a\b\f\n\r\v\\\'\"') diff --git a/src/test/resources/bytecode-compiler/regressions/controlchars.luac b/src/test/resources/bytecode-compiler/regressions/controlchars.luac new file mode 100644 index 0000000000000000000000000000000000000000..fc8a1550cede2a362053a6240d11ec72dcd6b607 GIT binary patch literal 227 zcmb34DNPJwU}Rxo;b4%Q%*!Rk#Q+10Ozc1=8-oLbqXPqjtOElhnB4$j%QG}EFazaT z*nmVqQD$BV1B(%m$gbV$z6D=Es)NzOO=JK$3L+1o UnSgv`4CI34S%GxCt6vxc0RP4yVE_OC literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/regressions/mathrandomseed.lc b/src/test/resources/bytecode-compiler/regressions/mathrandomseed.lc deleted file mode 100644 index 100cba3391133e0025837daf33c9390349a05696..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 159 zcmb34DNPJyU}WK7;b0J9fB=Ww#FC7n#JrUJ+~U;K6uq3%M5qjiVq#(ik|50H;K1P6 sz`!8mz`!WS(7?b96k`ENfB?u01{Q7zhXG+4SQS(hVhkgYhmK(i00CkVd;kCd diff --git a/src/test/resources/bytecode-compiler/regressions/mathrandomseed.luac b/src/test/resources/bytecode-compiler/regressions/mathrandomseed.luac new file mode 100644 index 0000000000000000000000000000000000000000..b0fefaa7a5b5d0b84d671e849f546e67e5303ad2 GIT binary patch literal 167 zcmb34DNPJwU}Rxo;b4%Q%*!Rk#Q+10OsqgA8-oJ_yMqIRV*>+&tOEn1JVOHmGfqytDXLYZDpX(9v242TL;8l)E_ M4w8>|^$TMF0GJ~a^8f$< literal 0 HcmV?d00001 diff --git a/src/test/resources/bytecode-compiler/regressions/modulo.lc b/src/test/resources/bytecode-compiler/regressions/modulo.lc deleted file mode 100644 index 85a3c1a2f79fd5b66141f33d1c788d77d6eed9e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220 zcmb34DNPJyU}WK7;b7olfB=Ww{FKt1e7&5~M5qXeVq)S1l8g=v4D2BK0FZ6~@fm@9 zMj#(ZH!w2DF*Go60OeVjfCLC6GB86JAMD{`NiZ=62e?==TC-q U5r{#?gD}W&5N1KbKqeRf0LNtycmMzZ diff --git a/src/test/resources/bytecode-compiler/regressions/modulo.luac b/src/test/resources/bytecode-compiler/regressions/modulo.luac new file mode 100644 index 0000000000000000000000000000000000000000..6a7940ab3564466f1b2cba7336852d2a98cecd31 GIT binary patch literal 188 zcmb34DNPJwU}Rxo;b4%Q%*!Rk#Q+10OsqgAhl4`{#{q{%4hP33jsuR(@(c|O96)gv zCLoc>zzk9S!5+*{g7Fy~!2D!5{{Wbu0^@(!57Ng7#ymjMAvZszG$&s#r!8lnVGP5j*|l0qasbMkED#&kC<`MYgoIG&B&_WPy~r6@A#o2E z5`Bssf+O(WFAqgE9Vtlg)z5#bD%YRf+b>SamtwL!%jKGQvY*OXIeU9FeScE6CbA^C zg69}mBe&qdxB>^p4LC3s1HX~SQpqI|$r3zBhlBWW5NCsUG>FH#)$bD=CNiR8($OZ^ zXTVwNB42D}RIfyys^0n=)s6MGMAvWwes9`!{T20aoOd{T_u+WBMJZVZD``5NO4cod zGJ&sC%L%Lgv2g1cBwDkpf$tDOU7yPoYGXh87(e8$K~wiR+qjc+{QS5EZrBsH32)pNyls=e&rv@U7w#~|%qd)j ztMG;3NQbx%J;nxXF_`1!3f+e_t`Kb@-Gp+R1|vD;=Tey(+*z0)yyn0*(3xWV_6sNT z+R4KCj`TiX0S-4iRI|_PkamUv-5tcee^Va5kWp~%VsP45VGF(x%*b*+ndLB_{03-u zgLiZX!XqjsU4H_0ihMe%g-B70d_|6aG|-I^i+s+D!Yt~-jJ_A<3={5siTcjEwMC-c z4Zo Date: Wed, 7 Apr 2021 03:25:35 -0400 Subject: [PATCH 18/24] Removed debug prints --- src/main/java/org/squiddev/cobalt/LuaError.java | 1 - src/main/java/org/squiddev/cobalt/compiler/LuaC.java | 4 ---- src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java | 1 - 3 files changed, 6 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/LuaError.java b/src/main/java/org/squiddev/cobalt/LuaError.java index cb2fda8e..c8794d07 100644 --- a/src/main/java/org/squiddev/cobalt/LuaError.java +++ b/src/main/java/org/squiddev/cobalt/LuaError.java @@ -76,7 +76,6 @@ public LuaError(Throwable cause) { level = 1; calculateLevel = true; value = ValueFactory.valueOf("vm error: " + cause.toString()); - cause.printStackTrace(System.err); } /** diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index a93467bd..b0bf6635 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -76,10 +76,6 @@ public class LuaC implements LuaCompiler { protected static void _assert(boolean b, int line) throws CompileException { if (!b) { // So technically this should fire a runtime exception but... - System.err.println("compiler assert failed on line " + line + "\nstack trace:"); - for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { - System.err.println(ste); - } throw new CompileException("compiler assert failed"); } } diff --git a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java index 2f469615..a737e669 100644 --- a/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/CoroutineLib.java @@ -93,7 +93,6 @@ public Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws LuaErr Varargs result = LuaThread.resume(state, thread, args.subargs(2)); return varargsOf(Constants.TRUE, result); } catch (LuaError le) { - le.printStackTrace(System.out); return varargsOf(Constants.FALSE, le.value); } } From 6dfde634b25864cebf1cf28cf368f0d5b4b6ff2b Mon Sep 17 00:00:00 2001 From: MCJack123 Date: Wed, 7 Apr 2021 04:17:51 -0400 Subject: [PATCH 19/24] Fixed LOADK/EXTRAARG not being emitted --- .../squiddev/cobalt/compiler/FuncState.java | 23 ++++++++++++++++--- .../org/squiddev/cobalt/compiler/LuaC.java | 5 ++++ .../cobalt/function/LuaInterpreter.java | 21 +---------------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index fe532e70..217da871 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -599,11 +599,11 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState.VK: { - this.codeABx(OP_LOADK, reg, e.u.info); + this.codek(reg, e.u.info); break; } case LexState.VKNUM: { - this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval())); + this.codek(reg, this.numberK(e.u.nval())); break; } case LexState.VRELOCABLE: { @@ -1139,6 +1139,23 @@ int codeABx(int o, int a, int bc) throws CompileException { } + private int codeextraarg(int a) throws CompileException { + _assert(a <= MAXARG_Ax); + return code(CREATE_Ax(OP_EXTRAARG, a), this.ls.lastline); + } + + + int codek(int reg, int k) throws CompileException { + if (k <= MAXARG_Bx) { + return codeABx(OP_LOADK, reg, k); + } else { + int p = codeABx(OP_LOADKX, reg, 0); + codeextraarg(k); + return p; + } + } + + private void setlist(int base, int nelems, int tostore) throws CompileException { int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; int b = (tostore == LUA_MULTRET) ? 0 : tostore; @@ -1147,7 +1164,7 @@ private void setlist(int base, int nelems, int tostore) throws CompileException this.codeABC(OP_SETLIST, base, b, c); } else { this.codeABC(OP_SETLIST, base, b, 0); - this.code(c, this.ls.lastline); + this.codeextraarg(c); } this.freereg = base + 1; /* free registers with list values */ } diff --git a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java index b0bf6635..24bd50e2 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LuaC.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LuaC.java @@ -123,6 +123,11 @@ public static int CREATE_ABx(int o, int a, int bc) { ((bc << Lua52.POS_Bx) & Lua52.MASK_Bx); } + public static int CREATE_Ax(int o, int a) { + return ((o << Lua52.POS_OP) & Lua52.MASK_OP) | + ((a << Lua52.POS_Ax) & Lua52.MASK_Ax); + } + // vector reallocation public static LuaValue[] realloc(LuaValue[] v, int n) { diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 48246841..0fd6b182 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -597,25 +597,6 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; case OP_TFORCALL: { // A C: R(A+3), ..., R(A+2+C) := R(A)(R(A+1), R(A+2)); - /*int c = ((i >> POS_C) & MAXARG_C); - - LuaValue val = stack[a]; - if (val instanceof LuaInterpretedFunction) { - function = (LuaInterpretedFunction) val; - di = setupCall(state, function, stack[a + 1], stack[a + 2], 0); - continue newFrame; - } - - Varargs v = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); - i = code[pc++]; - a = ((i >> POS_A) & MAXARG_A); - if (c > 0) { - while (--c > 0) stack[a + 3 + c] = v.arg(c); - v = NONE; - } else { - di.top = a + 3 + v.count(); - di.extras = v; - }*/ di.extras = OperationHelper.invoke(state, stack[a], ValueFactory.varargsOf(stack[a + 1], stack[a + 2]), a); for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { stack[a + 2 + c] = di.extras.arg(c); @@ -659,7 +640,7 @@ A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), case OP_SETLIST: { // A B C: R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B int b = (i >>> POS_B) & MAXARG_B; int c = (i >> POS_C) & MAXARG_C; - if (c == 0) c = code[pc++]; + if (c == 0) c = p.isLua52 ? GETARG_Ax(code[pc++]) : code[pc++]; int offset = (c - 1) * LFIELDS_PER_FLUSH; LuaTable tbl = stack[a].checkTable(); From 19ca42497baf98007cf76aa38008599dbef321d0 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Sun, 21 Nov 2021 06:59:54 -0500 Subject: [PATCH 20/24] Allow arguments to xpcall --- src/main/java/org/squiddev/cobalt/lib/BaseLib.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index ac9503a6..93116287 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -335,7 +335,7 @@ protected Varargs invoke(LuaState state, DebugFrame di, Varargs args) throws Lua case 0: // "pcall", // (f, arg1, ...) -> status, result1, ... return pcall(state, di, args.checkValue(1), args.subargs(2), null); case 1: // "xpcall", // (f, err) -> result1, ... - return pcall(state, di, args.checkValue(1), Constants.NONE, args.checkValue(2)); + return pcall(state, di, args.checkValue(1), args.subargs(3), args.checkValue(2)); case 2: // "load", // ( func|str [,chunkname[, mode[, env]]] ) -> chunk | nil, msg { From 484ae628a03bf322117930011e1a6f76b5407ce9 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Wed, 1 Dec 2021 18:44:49 -0500 Subject: [PATCH 21/24] Added separator argument to string.rep --- .../java/org/squiddev/cobalt/lib/StringLib.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/lib/StringLib.java b/src/main/java/org/squiddev/cobalt/lib/StringLib.java index d7898879..e1887847 100644 --- a/src/main/java/org/squiddev/cobalt/lib/StringLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/StringLib.java @@ -273,23 +273,28 @@ static LuaValue dump(LuaFunction f, boolean strip) throws LuaError { /** - * string.rep (s, n) + * string.rep (s, n [, sep]) * - * Returns a string that is the concatenation of n copies of the string s. + * Returns a string that is the concatenation of n copies of the string s separated + * by the string sep. The default value for sep is the empty string (that is, no separator). */ static Varargs rep(Varargs args) throws LuaError { LuaString s = args.arg(1).checkLuaString(); int n = args.arg(2).checkInteger(); + LuaString sep = args.arg(3).optLuaString(EMPTYSTRING); int len = s.length(); + int seplen = sep.length(); - if (n <= 0 || len == 0) { + if (n <= 0 || (len == 0 && seplen == 0)) { return Constants.EMPTYSTRING; } else if (n == 1) { return s; } else { - final byte[] bytes = new byte[len * n]; - for (int offset = 0; offset < bytes.length; offset += len) { + final byte[] bytes = new byte[len * n + seplen * (n - 1)]; + for (int offset = 0; offset < bytes.length; offset += len + seplen) { s.copyTo(0, bytes, offset, len); + if (offset + len + seplen < bytes.length) + sep.copyTo(0, bytes, offset + len, seplen); } return LuaString.valueOf(bytes); } From 614801e729dfc901a2064812a82492ac0350627a Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Thu, 16 Dec 2021 15:23:28 -0500 Subject: [PATCH 22/24] Bumped version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e42dafb1..ff28b5b4 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.squiddev' -version = '0.5.4' +version = '0.6.0' java { toolchain { From b9a8435a1caf4fafd49382f51cdd389838b01b39 Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Thu, 16 Dec 2021 15:33:19 -0500 Subject: [PATCH 23/24] Changed behavior of blockGoto Now, when blockGoto is enabled, goto is always treated as a name instead of throwing an error. This is much more useful than the previous behavior. --- src/main/java/org/squiddev/cobalt/compiler/LexState.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/compiler/LexState.java b/src/main/java/org/squiddev/cobalt/compiler/LexState.java index 5f275b75..7f9d647f 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState.java @@ -60,6 +60,7 @@ public class LexState { } public static final LuaString ENV = LuaString.valueOf("_ENV"); + private static final LuaString GOTO = LuaString.valueOf("goto"); private static final int EOZ = -1; private static final int MAXSRC = 80; @@ -2122,7 +2123,7 @@ private void retstat() throws CompileException { private void statement() throws CompileException { int line = this.linenumber; /* may be needed for error messages */ enterlevel(); - if (this.t.token == TK_NAME && this.t.seminfo.ts.equals(LuaString.valueOf("goto"))) { + if (this.t.token == TK_NAME && !LuaC.blockGoto && this.t.seminfo.ts.equals(GOTO)) { lookahead(); if (lookahead.token == TK_NAME) { this.t.token = TK_GOTO; @@ -2179,9 +2180,6 @@ private void statement() throws CompileException { } case TK_BREAK: /* stat -> breakstat */ case TK_GOTO: { /* stat -> 'goto' NAME */ - if (this.t.token == TK_GOTO && LuaC.blockGoto) { - throw lexError("illegal jump statement", this.t.token); - } this.gotostat(fs.jump()); break; } From 5a58bdbed6ba6f0dfcadd27bb8ca66e18063082e Mon Sep 17 00:00:00 2001 From: JackMacWindows Date: Sat, 9 Apr 2022 18:02:35 -0400 Subject: [PATCH 24/24] Fixed string.unpack not throwing on unfinished zstr --- src/main/java/org/squiddev/cobalt/lib/StringPacker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/squiddev/cobalt/lib/StringPacker.java b/src/main/java/org/squiddev/cobalt/lib/StringPacker.java index aff6c50b..516efa31 100644 --- a/src/main/java/org/squiddev/cobalt/lib/StringPacker.java +++ b/src/main/java/org/squiddev/cobalt/lib/StringPacker.java @@ -391,7 +391,8 @@ static Varargs unpack(Varargs args) throws LuaError { } case ZSTR: { int len = 0; - for (int i = str.offset + pos, end = str.offset + str.length; i < end; i++, len++) { + for (int i = str.offset + pos, end = str.offset + str.length; i <= end; i++, len++) { + if (i == end) throw ErrorFactory.argError(2, "unfinished string for format 'z'"); if (str.bytes[i] == 0) break; }