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/build.gradle b/build.gradle index ec441ea8..ff28b5b4 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'org.squiddev' -version = '0.5.5' +version = '0.6.0' java { toolchain { diff --git a/src/main/java/org/squiddev/cobalt/ErrorFactory.java b/src/main/java/org/squiddev/cobalt/ErrorFactory.java index 1aa3c20d..ba82e6de 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,7 +107,7 @@ 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); } } } 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..4260d103 --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/Lua52.java @@ -0,0 +1,430 @@ +/* + * 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 */ + (0 << 7) | (1 << 6) | (OpArgR << 4) | (OpArgN << 2) | (iAsBx), /* OP_TFORLOOP */ + (0 << 7) | (0 << 6) | (OpArgU << 4) | (OpArgU << 2) | (iABC), /* OP_SETLIST */ + (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/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 1bd2d1a9..f9b0474b 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; @@ -105,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 */ @@ -129,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; } @@ -145,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; } @@ -172,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]; @@ -459,6 +450,9 @@ public void run() { // Clear the function after the first run LuaFunction function = func; func = null; + if (function instanceof LuaInterpretedFunction) { + ((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/LuaUserdata.java b/src/main/java/org/squiddev/cobalt/LuaUserdata.java index c0a24191..41f1b62d 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/LuaValue.java b/src/main/java/org/squiddev/cobalt/LuaValue.java index e57905c0..41c72b7e 100644 --- a/src/main/java/org/squiddev/cobalt/LuaValue.java +++ b/src/main/java/org/squiddev/cobalt/LuaValue.java @@ -1125,7 +1125,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"); } @@ -1142,7 +1145,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/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index 738f16c9..b5df08be 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -167,6 +167,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..473eb215 --- /dev/null +++ b/src/main/java/org/squiddev/cobalt/Print52.java @@ -0,0 +1,297 @@ +/* + * 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", + "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 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(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: + ps.print(a + " " + sbx); + break; + } + switch (o) { + case OP_LOADK: + 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(" ; "); + if (f.upvalues.length > b) { + printValue(ps, f.upvalues[b]); + } else { + ps.print("-"); + } + break; + case OP_GETTABUP: + ps.print(" ; "); + if (f.upvalues.length > b) { + printValue(ps, f.upvalues[b]); + } else { + ps.print("-"); + } + ps.print("["); + if (c > 0xff) printConstant(ps, f, c & 0x0ff); + else ps.print("-"); + ps.print("]"); + break; + case OP_SETTABUP: + ps.print(" ; "); + if (f.upvalues.length > a) { + printValue(ps, f.upvalues[a]); + } else { + ps.print("-"); + } + ps.print("["); + if (b > 0xff) printConstant(ps, f, b & 0x0ff); + else ps.print("-"); + ps.print("] = "); + if (c > 0xff) printConstant(ps, f, c & 0x0ff); + else ps.print("-"); + 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: + case OP_TFORLOOP: + 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; + } +} 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..f2728076 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.*; @@ -38,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; @@ -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 @@ -83,7 +90,6 @@ public final class BytecodeLoader { * @param stream The stream to read from */ public BytecodeLoader(InputStream stream) { - this.is = new DataInputStream(stream); } @@ -155,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(); @@ -250,12 +256,16 @@ 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 */ 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 +292,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 @@ -304,14 +325,16 @@ 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. */ 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 +343,13 @@ 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/compiler/DumpState.java b/src/main/java/org/squiddev/cobalt/compiler/DumpState.java index 6cc666f4..7020fa4e 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 || 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/FuncState.java b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java index a5866048..217da871 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/FuncState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/FuncState.java @@ -33,21 +33,25 @@ 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.LexState.VUPVAL; 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 +73,7 @@ static class BlockCnt { short actvar[] = new short[LUAI_MAXVARS]; /* declared-variable stack */ FuncState() { + } @@ -77,11 +82,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 +120,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 +175,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 +218,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 +251,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 +264,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,34 +281,31 @@ 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) { - 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; - } + 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, 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 +322,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 +343,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 +368,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 +388,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 +396,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 +415,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 +427,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 +485,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 +541,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 +555,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,11 +599,11 @@ private void discharge2reg(expdesc e, int reg) throws CompileException { break; } case LexState.VK: { - this.codeABx(OP_LOADK, reg, e.u.s.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: { @@ -578,8 +612,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 +622,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 +636,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 +653,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 +669,21 @@ 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 exp2anyregup(expdesc e) throws CompileException { + if (e.k != VUPVAL || e.hasjumps()) { + this.exp2anyreg(e); + } } void exp2val(expdesc e) throws CompileException { @@ -657,23 +697,26 @@ 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.s.info = (e.k == LexState.VNIL) ? this.nilK() - : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval()) + e.u.info = (e.k == LexState.VNIL) ? this.nilK() : 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.VKNUM: { + e.u.info = this.numberK(e.u.nval()); + e.k = LexState.VK; + /* go through */ + } 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 +732,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 +760,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,26 +788,22 @@ 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 { 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 = LexState.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.s.info; + pc = NO_JUMP; /* always true; do nothing */ break; } default: { @@ -778,24 +813,20 @@ 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 */ - break; - } - case LexState.VTRUE: { - pc = this.jump(); /* always jump */ + case LexState.VJMP: { + pc = e.u.info; break; } - case LexState.VJMP: { - pc = e.u.s.info; + case LexState.VNIL: + case LexState.VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ break; } default: { @@ -805,7 +836,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 +861,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 +881,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; } @@ -897,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) @@ -917,8 +947,9 @@ 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; + fixline(line); } } @@ -934,11 +965,11 @@ 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; } - 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) { @@ -946,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: @@ -954,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: @@ -995,10 +1026,10 @@ 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 == 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 +1037,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,34 +1048,34 @@ 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); + 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); @@ -1108,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; @@ -1116,8 +1164,36 @@ 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 */ } + + 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/LexState.java b/src/main/java/org/squiddev/cobalt/compiler/LexState.java index 4572361d..5d6f8a76 100644 --- a/src/main/java/org/squiddev/cobalt/compiler/LexState.java +++ b/src/main/java/org/squiddev/cobalt/compiler/LexState.java @@ -59,6 +59,9 @@ 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; private static final int MAX_INT = Integer.MAX_VALUE - 2; @@ -110,7 +113,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 +150,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 +166,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 +315,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 +394,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 +419,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 +715,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 +749,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 +761,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 +788,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 +806,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 +820,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 +832,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 +846,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; } @@ -946,6 +985,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; } @@ -954,8 +994,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(LexState.ENV, var, 1); + LuaC._assert(var.k == LexState.VLOCAL || var.k == LexState.VUPVAL, linenumber); + codestring(key, varname); + fs.indexed(var, key); } } @@ -996,32 +1040,115 @@ 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; + } + + 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) { - Prototype f = new Prototype(); + 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; - fs.lasttarget = -1; + fs.lasttarget = 0; fs.jpc = new IntPtr(NO_JUMP); fs.freereg = 0; fs.nk = 0; @@ -1032,13 +1159,14 @@ 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 { 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); f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); // f.sizelineinfo = fs.pc; @@ -1048,7 +1176,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 @@ -1064,7 +1192,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); @@ -1113,7 +1241,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 +1257,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 +1266,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 +1337,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 +1348,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 +1356,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 +1367,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 { @@ -1257,16 +1387,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; @@ -1290,17 +1416,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 */ @@ -1313,8 +1439,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; @@ -1335,13 +1461,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 */ @@ -1350,7 +1477,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; @@ -1360,14 +1487,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: @@ -1380,7 +1507,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: { @@ -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 */ @@ -1422,7 +1549,7 @@ private void simpleexp(expdesc v) throws CompileException { return; } default: { - this.primaryexp(v); + this.suffixedexp(v); return; } } @@ -1513,9 +1640,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); } @@ -1524,11 +1652,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(); @@ -1550,14 +1679,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 +1699,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 +1728,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); } } @@ -1619,10 +1750,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); } @@ -1660,25 +1791,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,29 +1849,23 @@ 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 */ } 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; } @@ -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,36 +2004,24 @@ 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 { - 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; } @@ -1925,7 +2078,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 */ @@ -1940,60 +2093,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 && !LuaC.blockGoto && this.t.seminfo.ts.equals(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 +2167,41 @@ 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 */ + 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/LoadState.java b/src/main/java/org/squiddev/cobalt/compiler/LoadState.java index 94e13577..1a5ede7f 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; @@ -73,7 +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 * @@ -129,6 +129,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 ce934920..24bd50e2 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; @@ -42,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 @@ -70,7 +71,9 @@ 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... throw new CompileException("compiler assert failed"); @@ -80,43 +83,49 @@ 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 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); + } + + 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 @@ -153,6 +162,14 @@ public static LocalVariable[] realloc(LocalVariable[] v, int n) { return a; } + 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)); + } + return a; + } + public static int[] realloc(int[] v, int n) { int[] a = new int[n]; if (v != null) { @@ -180,6 +197,11 @@ 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)); + } else if (!p.isLua52 && p.nups == 0) { + closure.setfenv(env); + } return closure; } @@ -207,28 +229,33 @@ 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); + 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(); + FuncState.BlockCnt bl = new FuncState.BlockCnt(); // lexstate.buff = buff; + lexstate.dyd = dyd; + dyd.nactvar = dyd.ngt = dyd.nlabel = 0; lexstate.setinput(firstByte, z, name); - lexstate.open_func(funcstate); + 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; + LexState.expdesc v = new LexState.expdesc(); + v.init(LexState.VLOCAL, 0); + funcstate.newupvalue(LexState.ENV, v); lexstate.nextToken(); /* read first token */ - lexstate.chunk(); + lexstate.statlist(); 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/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/debug/DebugHook.java b/src/main/java/org/squiddev/cobalt/debug/DebugHook.java index 1c790699..3d457d66 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 45849627..39ba9b59 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/LibFunction.java b/src/main/java/org/squiddev/cobalt/function/LibFunction.java index a6831a1d..e0ef34ab 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 56a05fff..928292a5 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 @@ -69,14 +65,14 @@ public LuaTable getMetatable(LuaState state) { return state.functionMetatable; } - @Override + @Override @Deprecated public LuaTable getfenv() { - return env; + return null; } - @Override + @Override @Deprecated public void setfenv(LuaTable env) { - this.env = env != null ? env : null; + } public String debugName() { @@ -199,6 +195,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/LuaInterpretedFunction.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpretedFunction.java index 7d3ccb44..519889f6 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; @@ -99,7 +100,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; + if (p.isLua52) this.upvalues = p.nups > 0 ? new Upvalue[p.nups] : NO_UPVALUES; + else this.upvalues = new Upvalue[p.nups + 1]; } /** @@ -111,14 +113,26 @@ 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; + 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 < p.upvalues.length; 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); + } } public void nilUpvalues() { int nups = p.nups; if (nups > 0) { Upvalue[] upvalues = this.upvalues; - for (int i = 0; i < nups; i++) { + for (int i = p.isLua52 ? 0 : 1; i < nups; i++) { upvalues[i] = new Upvalue(Constants.NIL); } } @@ -149,14 +163,40 @@ 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() { + if (!p.isLua52) return (LuaTable)upvalues[0].getValue(); + else { + 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; // TODO: maybe this should return _G (state.globalTable) instead? + } + } + + @Override + 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 < p.upvalues.length; i++) { + if (p.upvalues[i].equals(LexState.ENV)) { + upvalues[i] = new Upvalue(env); + } + } + } + } + @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/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index ebb0d5e8..0a51ea34 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.*; @@ -141,13 +141,27 @@ 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; 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; @@ -175,8 +189,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,25 +203,43 @@ 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) 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); - do { - stack[b--] = NIL; - } while (b >= a); + if (p.isLua52) { + while (b >= 0) { + stack[a+(b--)] = NIL; + } + } else { + do { + stack[b--] = NIL; + } while (b >= a); + } break; } 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)] + 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, 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,11 +250,18 @@ 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_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]); + 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) @@ -309,8 +352,8 @@ static Varargs execute(final LuaState state, DebugFrame di, LuaInterpretedFuncti break; } - case OP_JMP: // sBx: pc+=sBx - pc += ((i >>> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + case OP_JMP: // A sBx: pc+=sBx; if (A) close all upvalues >= R(A - 1) + pc = jumpStatement(p, openups, pc, a, GETARG_sBx(i)); break; case OP_EQ: { // A B C: if ((RK(B) == RK(C)) ~= A) then pc++ @@ -318,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; @@ -328,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; @@ -338,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; @@ -346,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; @@ -358,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; @@ -553,22 +596,43 @@ 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)); + 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: { /* 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(); - 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; } @@ -576,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(); @@ -612,13 +676,24 @@ 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); - 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 + 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) { + 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]; + } + } + } else { + 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+1] // OP_GETUPVAL + : openups[b] != null ? openups[b] : (openups[b] = new Upvalue(stack, b)); // OP_MOVE + } } stack[a] = newcl; break; @@ -634,7 +709,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? } } } @@ -698,10 +777,12 @@ 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_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 +804,15 @@ public static void resume(LuaState state, DebugFrame di, LuaInterpretedFunction break; } + case OP_TFORCALL: { + int a = (i >>> POS_A) & MAXARG_A; + di.extras = varargs; + for (int c = (i >> POS_C) & MAXARG_C; c > 1; --c) { + di.stack[a + 2 + c] = varargs.arg(c); + } + break; + } + case OP_CALL: case OP_TAILCALL: { int a = (i >>> POS_A) & MAXARG_A; int c = (i >>> POS_C) & MAXARG_C; @@ -737,7 +827,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; diff --git a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java index 09616d2f..93116287 100644 --- a/src/main/java/org/squiddev/cobalt/lib/BaseLib.java +++ b/src/main/java/org/squiddev/cobalt/lib/BaseLib.java @@ -24,8 +24,10 @@ */ 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.compiler.LuaC; import org.squiddev.cobalt.debug.DebugFrame; import org.squiddev.cobalt.debug.DebugHandler; import org.squiddev.cobalt.debug.DebugState; @@ -110,7 +112,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("Lua 5.2")); return env; } @@ -217,7 +219,7 @@ 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")); + 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(); @@ -333,10 +335,16 @@ 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 { + // 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); @@ -491,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 366830b3..a737e669 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; @@ -107,10 +107,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/DebugLib.java b/src/main/java/org/squiddev/cobalt/lib/DebugLib.java index 919e14d1..2c823a61 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; @@ -394,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; } @@ -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..c97829ea 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; @@ -114,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; } } @@ -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; @@ -209,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/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); } 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; } 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/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/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/compiler/CompileTestHelper.java b/src/test/java/org/squiddev/cobalt/compiler/CompileTestHelper.java index e58fd52e..b0fe192f 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,10 +45,11 @@ 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")); + // 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"); + 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/java/org/squiddev/cobalt/vm/LuaOperationsTest.java b/src/test/java/org/squiddev/cobalt/vm/LuaOperationsTest.java index 709ab4c6..5ed43562 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 { @@ -259,27 +259,17 @@ public LuaValue call(LuaState state) throws LuaError { // 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 92a5b4eb..00016e54 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/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/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..5463b959 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 @@ -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) @@ -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/errors.lua b/src/test/resources/assert/lua5.1/errors.lua index aaee6a05..35a2a21a 100644 --- a/src/test/resources/assert/lua5.1/errors.lua +++ b/src/test/resources/assert/lua5.1/errors.lua @@ -37,12 +37,12 @@ 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)") -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/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.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' 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) 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 00000000..03de65ed Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/all.luac differ 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 00000000..05214f8d Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/api.luac differ diff --git a/src/test/resources/bytecode-compiler/lua5.2/attrib.lua b/src/test/resources/bytecode-compiler/lua5.2/attrib.lua new file mode 100644 index 00000000..745447e9 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/attrib.lua @@ -0,0 +1,339 @@ +do --[ + +print "testing require" + +assert(require"string" == string) +assert(require"math" == math) +assert(require"table" == table) +assert(require"io" == io) +assert(require"os" == os) +assert(require"debug" == debug) +assert(require"coroutine" == coroutine) + +assert(type(package.path) == "string") +assert(type(package.cpath) == "string") +assert(type(package.loaded) == "table") +assert(type(package.preload) == "table") + + +local DIR = "libs/" + +local function createfiles (files, preextras, posextras) + for n,c in pairs(files) do + io.output(DIR..n) + io.write(string.format(preextras, n)) + io.write(c) + io.write(string.format(posextras, n)) + io.close(io.output()) + end +end + +function removefiles (files) + for n in pairs(files) do + os.remove(DIR..n) + end +end + +local files = { + ["A.lua"] = "", + ["B.lua"] = "assert(...=='B');require 'A'", + ["A.lc"] = "", + ["A"] = "", + ["L"] = "", + ["XXxX"] = "", + ["C.lua"] = "package.loaded[...] = 25; require'C'" +} + +AA = nil +local extras = [[ +NAME = '%s' +REQUIRED = ... +return AA]] + +createfiles(files, "", extras) + + +local oldpath = package.path + +package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR) + +local try = function (p, n, r) + NAME = nil + local rr = require(p) + assert(NAME == n) + assert(REQUIRED == p) + assert(rr == r) +end + +assert(require"C" == 25) +assert(require"C" == 25) +AA = nil +try('B', 'B.lua', true) +assert(package.loaded.B) +assert(require"B" == true) +assert(package.loaded.A) +package.loaded.A = nil +try('B', nil, true) -- should not reload package +try('A', 'A.lua', true) +package.loaded.A = nil +os.remove(DIR..'A.lua') +AA = {} +try('A', 'A.lc', AA) -- now must find second option +assert(require("A") == AA) +AA = false +try('K', 'L', false) -- default option +try('K', 'L', false) -- default option (should reload it) +assert(rawget(_G, "_REQUIREDNAME") == nil) + +AA = "x" +try("X", "XXxX", AA) + + +removefiles(files) + + +-- testing require of sub-packages + +package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR) + +files = { + ["P1/init.lua"] = "AA = 10", + ["P1/xuxu.lua"] = "AA = 20", +} + +createfiles(files, "module(..., package.seeall)\n", "") +AA = 0 + +local m = assert(require"P1") +assert(m == P1 and m._NAME == "P1" and AA == 0 and m.AA == 10) +assert(require"P1" == P1 and P1 == m) +assert(require"P1" == P1) +assert(P1._PACKAGE == "") + +local m = assert(require"P1.xuxu") +assert(m == P1.xuxu and m._NAME == "P1.xuxu" and AA == 0 and m.AA == 20) +assert(require"P1.xuxu" == P1.xuxu and P1.xuxu == m) +assert(require"P1.xuxu" == P1.xuxu) +assert(require"P1" == P1) +assert(P1.xuxu._PACKAGE == "P1.") +assert(P1.AA == 10 and P1._PACKAGE == "") +assert(P1._G == _G and P1.xuxu._G == _G) + + + +removefiles(files) + + +package.path = "" +assert(not pcall(require, "file_does_not_exist")) +package.path = "??\0?" +assert(not pcall(require, "file_does_not_exist1")) + +package.path = oldpath + +-- check 'require' error message +local fname = "file_does_not_exist2" +local m, err = pcall(require, fname) +for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do + t = string.gsub(t, "?", fname) + assert(string.find(err, t, 1, true)) +end + + +--[=[local function import(...) + local f = {...} + return function (m) + for i=1, #f do m[f[i]] = _G[f[i]] end + end +end]=] + +local assert, module, package = assert, module, package +X = nil; x = 0; assert(_G.x == 0) -- `x' must be a global variable +module"X"; x = 1; assert(_M.x == 1) +module"X.a.b.c"; x = 2; assert(_M.x == 2) +module("X.a.b", package.seeall); x = 3 +assert(X._NAME == "X" and X.a.b.c._NAME == "X.a.b.c" and X.a.b._NAME == "X.a.b") +assert(X._M == X and X.a.b.c._M == X.a.b.c and X.a.b._M == X.a.b) +assert(X.x == 1 and X.a.b.c.x == 2 and X.a.b.x == 3) +assert(X._PACKAGE == "" and X.a.b.c._PACKAGE == "X.a.b." and + X.a.b._PACKAGE == "X.a.") +assert(_PACKAGE.."c" == "X.a.c") +assert(X.a._NAME == nil and X.a._M == nil) +module("X.a", import("X")) ; x = 4 +assert(X.a._NAME == "X.a" and X.a.x == 4 and X.a._M == X.a) +module("X.a.b", package.seeall); assert(x == 3); x = 5 +assert(_NAME == "X.a.b" and X.a.b.x == 5) + +assert(X._G == nil and X.a._G == nil and X.a.b._G == _G and X.a.b.c._G == nil) + +setfenv(1, _G) +assert(x == 0) + +assert(not pcall(module, "x")) +assert(not pcall(module, "math.sin")) + + +-- testing C libraries + + +local p = "" -- On Mac OS X, redefine this to "_" + +-- assert(loadlib == package.loadlib) -- only for compatibility +local f, err, when = package.loadlib("libs/lib1.so", p.."luaopen_lib1") +if not f then + (Message or print)('\a\n >>> 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 00000000..93ada7ca Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/attrib.luac differ 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 00000000..e0db0a00 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/big.luac differ 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 a" 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 00000000..4c348629 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/checktable.luac differ 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 00000000..7ad5a57e Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/closure.luac differ 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 00000000..28a9b407 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/code.luac differ 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 00000000..d05a9c8c Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/constructs.luac differ diff --git a/src/test/resources/bytecode-compiler/lua5.2/db.lua b/src/test/resources/bytecode-compiler/lua5.2/db.lua new file mode 100644 index 00000000..e7c527c6 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/db.lua @@ -0,0 +1,499 @@ +-- testing debug library + +local function dostring(s) return assert(loadstring(s))() end + +print"testing debug library and debug information" + +do +local a=1 +end + +function test (s, l, p) + collectgarbage() -- avoid gc during trace + local function f (event, line) + assert(event == 'line') + local l = table.remove(l, 1) + if p then print(l, line) end + assert(l == line, "wrong trace!!") + end + debug.sethook(f,"l"); loadstring(s)(); debug.sethook() + assert(table.getn(l) == 0) +end + + +do + local a = debug.getinfo(print) + assert(a.what == "C" and a.short_src == "[C]") + local b = debug.getinfo(test, "SfL") + assert(b.name == nil and b.what == "Lua" and b.linedefined == 11 and + b.lastlinedefined == b.linedefined + 10 and + b.func == test and not string.find(b.short_src, "%[")) + assert(b.activelines[b.linedefined + 1] and + b.activelines[b.lastlinedefined]) + assert(not b.activelines[b.linedefined] and + not b.activelines[b.lastlinedefined + 1]) +end + + +-- test file and string names truncation +a = "function f () end" +local function dostring (s, x) return loadstring(s, x)() end +dostring(a) +assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a)) +dostring(a..string.format("; %s\n=1", string.rep('p', 400))) +assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$')) +dostring("\n"..a) +assert(debug.getinfo(f).short_src == '[string "..."]') +dostring(a, "") +assert(debug.getinfo(f).short_src == '[string ""]') +dostring(a, "@xuxu") +assert(debug.getinfo(f).short_src == "xuxu") +dostring(a, "@"..string.rep('p', 1000)..'t') +assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$")) +dostring(a, "=xuxu") +assert(debug.getinfo(f).short_src == "xuxu") +dostring(a, string.format("=%s", string.rep('x', 500))) +assert(string.find(debug.getinfo(f).short_src, "^x*")) +dostring(a, "=") +assert(debug.getinfo(f).short_src == "") +a = nil; f = nil; + + +repeat + local g = {x = function () + local a = debug.getinfo(2) + assert(a.name == 'f' and a.namewhat == 'local') + a = debug.getinfo(1) + assert(a.name == 'x' and a.namewhat == 'field') + return 'xixi' + end} + local f = function () return 1+1 and (not 1 or g.x()) end + assert(f() == 'xixi') + g = debug.getinfo(f) + assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name) + + function f (x, name) -- local! + name = name or 'f' + local a = debug.getinfo(1) + assert(a.name == name and a.namewhat == 'local') + return x + end + + -- breaks in different conditions + if 3>4 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 00000000..5d166e0f Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/db.luac differ 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 00000000..c8da9550 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/errors.luac differ 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 00000000..c25210a0 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/events.luac differ diff --git a/src/test/resources/bytecode-compiler/lua5.2/files.lua b/src/test/resources/bytecode-compiler/lua5.2/files.lua new file mode 100644 index 00000000..4175dc34 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/files.lua @@ -0,0 +1,324 @@ + +print('testing i/o') + +assert(io.input(io.stdin) == io.stdin) +assert(io.output(io.stdout) == io.stdout) + + +assert(type(io.input()) == "userdata" and io.type(io.output()) == "file") +assert(io.type(8) == nil) +local a = {}; setmetatable(a, {}) +assert(io.type(a) == nil) + +local a,b,c = io.open('xuxu_nao_existe') +assert(not a and type(b) == "string" and type(c) == "number") + +a,b,c = io.open('/a/b/c/d', 'w') +assert(not a and type(b) == "string" and type(c) == "number") + +local file = os.tmpname() +local otherfile = os.tmpname() + +assert(os.setlocale('C', 'all')) + +io.input(io.stdin); io.output(io.stdout); + +os.remove(file) +assert(loadfile(file) == nil) +assert(io.open(file) == nil) +io.output(file) +assert(io.output() ~= io.stdout) + +assert(io.output():seek() == 0) +assert(io.write("alo alo")) +assert(io.output():seek() == string.len("alo alo")) +assert(io.output():seek("cur", -3) == string.len("alo alo")-3) +assert(io.write("joao")) +assert(io.output():seek("end") == string.len("alo joao")) + +assert(io.output():seek("set") == 0) + +assert(io.write('"álo"', "{a}\n", "second line\n", "third line \n")) +assert(io.write('çfourth_line')) +io.output(io.stdout) +collectgarbage() -- file should be closed by GC +assert(io.input() == io.stdin and rawequal(io.output(), io.stdout)) +print('+') + +-- test GC for files +collectgarbage() +for i=1,120 do + for i=1,5 do + io.input(file) + assert(io.open(file, 'r')) + io.lines(file) + end + collectgarbage() +end + +assert(os.rename(file, otherfile)) +assert(os.rename(file, otherfile) == nil) + +io.output(io.open(otherfile, "a")) +assert(io.write("\n\n\t\t 3450\n")); +io.close() + +-- test line generators +assert(os.rename(otherfile, file)) +io.output(otherfile) +local f = io.lines(file) +while f() do end; +assert(not pcall(f)) -- read lines after EOF +assert(not pcall(f)) -- read lines after EOF +-- copy from file to otherfile +for l in io.lines(file) do io.write(l, "\n") end +io.close() +-- copy from otherfile back to file +local f = assert(io.open(otherfile)) +assert(io.type(f) == "file") +io.output(file) +assert(io.output():read() == nil) +for l in f:lines() do io.write(l, "\n") end +assert(f:close()); io.close() +assert(not pcall(io.close, f)) -- error trying to close again +assert(tostring(f) == "file (closed)") +assert(io.type(f) == "closed file") +io.input(file) +f = io.open(otherfile):lines() +for l in io.lines() do assert(l == f()) end +assert(os.remove(otherfile)) + +io.input(file) +do -- test error returns + local a,b,c = io.input():write("xuxu") + assert(not a and type(b) == "string" and type(c) == "number") +end +assert(io.read(0) == "") -- not eof +assert(io.read(5, '*l') == '"álo"') +assert(io.read(0) == "") +assert(io.read() == "second line") +local x = io.input():seek() +assert(io.read() == "third line ") +assert(io.input():seek("set", x)) +assert(io.read('*l') == "third line ") +assert(io.read(1) == "ç") +assert(io.read(string.len"fourth_line") == "fourth_line") +assert(io.input():seek("cur", -string.len"fourth_line")) +assert(io.read() == "fourth_line") +assert(io.read() == "") -- empty line +assert(io.read('*n') == 3450) +assert(io.read(1) == '\n') +assert(io.read(0) == nil) -- end of file +assert(io.read(1) == nil) -- end of file +assert(({io.read(1)})[2] == nil) +assert(io.read() == nil) -- end of file +assert(({io.read()})[2] == nil) +assert(io.read('*n') == nil) -- end of file +assert(({io.read('*n')})[2] == nil) +assert(io.read('*a') == '') -- end of file (OK for `*a') +assert(io.read('*a') == '') -- end of file (OK for `*a') +collectgarbage() +print('+') +io.close(io.input()) +assert(not pcall(io.read)) + +assert(os.remove(file)) + +local t = '0123456789' +for i=1,12 do t = t..t; end +assert(string.len(t) == 10*2^12) + +io.output(file) +io.write("alo\n") +io.close() +assert(not pcall(io.write)) +local f = io.open(file, "a") +io.output(f) +collectgarbage() + +assert(io.write(' ' .. t .. ' ')) +assert(io.write(';', 'end of file\n')) +f:flush(); io.flush() +f:close() +print('+') + +io.input(file) +assert(io.read() == "alo") +assert(io.read(1) == ' ') +assert(io.read(string.len(t)) == t) +assert(io.read(1) == ' ') +assert(io.read(0)) +assert(io.read('*a') == ';end of file\n') +assert(io.read(0) == nil) +assert(io.close(io.input())) + +assert(os.remove(file)) +print('+') + +local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'" +io.output(file) +assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1))) +io.close() +assert(loadfile(file))() +assert(x1 == x2) +print('+') +assert(os.remove(file)) +assert(os.remove(file) == nil) +assert(os.remove(otherfile) == nil) + +io.output(file) +assert(io.write("qualquer coisa\n")) +assert(io.write("mais qualquer coisa")) +io.close() +io.output(assert(io.open(otherfile, 'wb'))) +assert(io.write("outra coisa\0\1\3\0\0\0\0\255\0")) +io.close() + +local filehandle = assert(io.open(file, 'r')) +local otherfilehandle = assert(io.open(otherfile, 'rb')) +assert(filehandle ~= otherfilehandle) +assert(type(filehandle) == "userdata") +assert(filehandle:read('*l') == "qualquer coisa") +io.input(otherfilehandle) +assert(io.read(string.len"outra coisa") == "outra coisa") +assert(filehandle:read('*l') == "mais qualquer coisa") +filehandle:close(); +assert(type(filehandle) == "userdata") +io.input(otherfilehandle) +assert(io.read(4) == "\0\1\3\0") +assert(io.read(3) == "\0\0\0") +assert(io.read(0) == "") -- 255 is not eof +assert(io.read(1) == "\255") +assert(io.read('*a') == "\0") +assert(not io.read(0)) +assert(otherfilehandle == io.input()) +otherfilehandle:close() +assert(os.remove(file)) +assert(os.remove(otherfile)) +collectgarbage() + +io.output(file) +io.write[[ + 123.4 -56e-2 not a number +second line +third line + +and the rest of the file +]] +io.close() +io.input(file) +local _,a,b,c,d,e,h,__ = io.read(1, '*n', '*n', '*l', '*l', '*l', '*a', 10) +assert(io.close(io.input())) +assert(_ == ' ' and __ == nil) +assert(type(a) == 'number' and a==123.4 and b==-56e-2) +assert(d=='second line' and e=='third line') +assert(h==[[ + +and the rest of the file +]]) +assert(os.remove(file)) +collectgarbage() + +-- testing buffers +do + local f = assert(io.open(file, "w")) + local fr = assert(io.open(file, "r")) + assert(f:setvbuf("full", 2000)) + f:write("x") + assert(fr:read("*all") == "") -- full buffer; output not written yet + f:close() + fr:seek("set") + assert(fr:read("*all") == "x") -- `close' flushes it + f = assert(io.open(file), "w") + assert(f:setvbuf("no")) + f:write("x") + fr:seek("set") + assert(fr:read("*all") == "x") -- no buffer; output is ready + f:close() + f = assert(io.open(file, "a")) + assert(f:setvbuf("line")) + f:write("x") + fr:seek("set", 1) + assert(fr:read("*all") == "") -- line buffer; no output without `\n' + f:write("a\n") + fr:seek("set", 1) + assert(fr:read("*all") == "xa\n") -- now we have a whole line + f:close(); fr:close() +end + + +-- testing large files (> 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 00000000..f37231da Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/files.luac differ 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 00000000..4838fa37 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/gc.luac differ 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 00000000..3427ab7e Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/literals.luac differ 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 00000000..b53f1f0a Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/locals.luac differ 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 00000000..6764d219 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/main.luac differ diff --git a/src/test/resources/bytecode-compiler/lua5.2/math.lua b/src/test/resources/bytecode-compiler/lua5.2/math.lua new file mode 100644 index 00000000..a0e69713 --- /dev/null +++ b/src/test/resources/bytecode-compiler/lua5.2/math.lua @@ -0,0 +1,208 @@ +print("testing numbers and math lib") + +do + local a,b,c = "2", " 3e0 ", " 10 " + assert(a+b == 5 and -b == -3 and b+"2" == 5 and "10"-c == 0) + assert(type(a) == 'string' and type(b) == 'string' and type(c) == 'string') + assert(a == "2" and b == " 3e0 " and c == " 10 " and -c == -" 10 ") + assert(c%a == 0 and a^b == 8) +end + + +do + local a,b = math.modf(3.5) + assert(a == 3 and b == 0.5) + assert(math.huge > 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 00000000..6926245a Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/math.luac differ 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 00000000..dff65c42 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/nextvar.luac differ 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 00000000..c8cdf232 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/pm.luac differ 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 '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 00000000..d1cbfbce Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/strings.luac differ 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 00000000..76939ec9 Binary files /dev/null and b/src/test/resources/bytecode-compiler/lua5.2/verybig.luac differ 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 89cfe19d..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/bigattr.lc and /dev/null differ diff --git a/src/test/resources/bytecode-compiler/regressions/bigattr.luac b/src/test/resources/bytecode-compiler/regressions/bigattr.luac new file mode 100644 index 00000000..b4c2f321 Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/bigattr.luac differ 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 ff3e574d..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/comparators.lc and /dev/null differ 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 00000000..56dbdd5d Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/comparators.luac differ 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 0fbb681a..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/construct.lc and /dev/null differ 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 00000000..79a58f21 Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/construct.luac differ diff --git a/src/test/resources/bytecode-compiler/regressions/controlchars.lc b/src/test/resources/bytecode-compiler/regressions/controlchars.lc deleted file mode 100644 index ae295695..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/controlchars.lc and /dev/null differ diff --git a/src/test/resources/bytecode-compiler/regressions/controlchars.lua b/src/test/resources/bytecode-compiler/regressions/controlchars.lua index dff4ab89..3b46ddb2 100644 --- a/src/test/resources/bytecode-compiler/regressions/controlchars.lua +++ b/src/test/resources/bytecode-compiler/regressions/controlchars.lua @@ -1,2 +1,2 @@ print('\a\n >>> 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 00000000..fc8a1550 Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/controlchars.luac differ 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 100cba33..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/mathrandomseed.lc and /dev/null differ 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 00000000..b0fefaa7 Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/mathrandomseed.luac differ 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 85a3c1a2..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/modulo.lc and /dev/null differ 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 00000000..6a7940ab Binary files /dev/null and b/src/test/resources/bytecode-compiler/regressions/modulo.luac differ diff --git a/src/test/resources/bytecode-compiler/regressions/varargs.lc b/src/test/resources/bytecode-compiler/regressions/varargs.lc deleted file mode 100644 index 756389f2..00000000 Binary files a/src/test/resources/bytecode-compiler/regressions/varargs.lc and /dev/null differ diff --git a/src/test/resources/bytecode-compiler/regressions/varargs.lua b/src/test/resources/bytecode-compiler/regressions/varargs.lua deleted file mode 100644 index 2d0d04bb..00000000 --- a/src/test/resources/bytecode-compiler/regressions/varargs.lua +++ /dev/null @@ -1,29 +0,0 @@ -function p(a,...) - print("a",a) - print("...",...) - print("...,a",...,a) - print("a,...",a,...) -end -function q(a,...) - print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) -end -function r(a,...) - print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) - print("a",a) - print("...",...) - print("...,a",...,a) - print("a,...",a,...) -end -function s(a) - local arg = { '1', '2', '3' } - print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) - print("a",a) -end -function t(a,...) - local arg = { '1', '2', '3' } - print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) - print("a",a) - print("...",...) - print("...,a",...,a) - print("a,...",a,...) -end 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 diff --git a/src/test/resources/compare/errors/baselibargs.lua b/src/test/resources/compare/errors/baselibargs.lua index dfb0531f..a7bdcd1b 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() 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/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... 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