blob: 04be7414b3471dad81f09265da50495e7a5a310b [file] [log] [blame] [raw]
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* 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.luaj.vm3;
/**
* Extension of {@link LuaFunction} which executes lua bytecode.
* <p>
* A {@link LuaClosure} is a combination of a {@link Prototype}
* and a {@link LuaValue} to use as an environment for execution.
* Normally the {@link LuaValue} is a {@link Globals} in which case the environment
* will contain standard lua libraries.
*
* <p>
* There are three main ways {@link LuaClosure} instances are created:
* <ul>
* <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li>
* <li>Construct it indirectly by loading a chunk via {@link Globals#load(java.io.Reader, String, LuaValue)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode processing
* </ul>
* <p>
* To construct it directly, the {@link Prototype} is typically created via a compiler such as {@link LuaC}:
* <pre> {@code
* String script = "print( 'hello, world' )";
* InputStream is = new ByteArrayInputStream(script.getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }</pre>
* <p>
* To construct it indirectly, the {@link Globals#load} method may be used:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call();
* }</pre>
* <p>
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* compiling using LuaJC is installed, because no LuaClosure is created in that case
* and the value returned is a {@link LuaFunction} but not a {@link LuaClosure}.
* <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue},
* all the value operations can be used directly such as:
* <ul>
* <li>{@link LuaValue#call()}</li>
* <li>{@link LuaValue#call(LuaValue)}</li>
* <li>{@link LuaValue#invoke()}</li>
* <li>{@link LuaValue#invoke(Varargs)}</li>
* <li>{@link LuaValue#method(String)}</li>
* <li>{@link LuaValue#method(String,LuaValue)}</li>
* <li>{@link LuaValue#invokemethod(String)}</li>
* <li>{@link LuaValue#invokemethod(String,Varargs)}</li>
* <li> ...</li>
* </ul>
* @see LuaValue
* @see LuaFunction
* @see LuaValue#isclosure()
* @see LuaValue#checkclosure()
* @see LuaValue#optclosure(LuaClosure)
* @see LoadState
* @see LoadState#compiler
*/
public class LuaClosure extends LuaFunction {
private static final UpValue[] NOUPVALUES = new UpValue[0];
public final Prototype p;
public UpValue[] upValues;
final Globals globals;
/** Create a closure around a Prototype with a specific environment.
* If the prototype has upvalues, the environment will be written into the first upvalue.
* @param p the Prototype to construct this Closure for.
* @param env the environment to associate with the closure.
*/
public LuaClosure(Prototype p, LuaValue env) {
this.p = p;
if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES;
else {
this.upValues = new UpValue[p.upvalues.length];
this.upValues[0] = new UpValue(new LuaValue[] { env }, 0);
}
globals = env instanceof Globals ? (Globals) env : null;
}
public boolean isclosure() {
return true;
}
public LuaClosure optclosure(LuaClosure defval) {
return this;
}
public LuaClosure checkclosure() {
return this;
}
public LuaValue getmetatable() {
return s_metatable;
}
public String tojstring() {
return "function: " + p.toString();
}
public final LuaValue call() {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 0; i < p.numparams; ++i)
stack[i] = NIL;
return execute(stack, NONE).arg1();
}
public final LuaValue call(LuaValue arg) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
System.arraycopy(NILS, 0, stack, 0, p.maxstacksize);
for (int i = 1; i < p.numparams; ++i)
stack[i] = NIL;
switch (p.numparams) {
default:
stack[0] = arg;
return execute(stack, NONE).arg1();
case 0:
return execute(stack, arg).arg1();
}
}
public final LuaValue call(LuaValue arg1, LuaValue arg2) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 2; i < p.numparams; ++i)
stack[i] = NIL;
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, NONE).arg1();
case 1:
stack[0] = arg1;
return execute(stack, arg2).arg1();
case 0:
return execute(stack, p.is_vararg != 0 ? varargsOf(arg1, arg2) : NONE).arg1();
}
}
public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 3; i < p.numparams; ++i)
stack[i] = NIL;
switch (p.numparams) {
default:
stack[0] = arg1;
stack[1] = arg2;
stack[2] = arg3;
return execute(stack, NONE).arg1();
case 2:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, arg3).arg1();
case 1:
stack[0] = arg1;
return execute(stack, p.is_vararg != 0 ? varargsOf(arg2, arg3) : NONE).arg1();
case 0:
return execute(stack, p.is_vararg != 0 ? varargsOf(arg1, arg2, arg3) : NONE).arg1();
}
}
public final Varargs invoke(Varargs varargs) {
return onInvoke(varargs).eval();
}
public final Varargs onInvoke(Varargs varargs) {
LuaValue[] stack = new LuaValue[p.maxstacksize];
for (int i = 0; i < p.numparams; i++)
stack[i] = varargs.arg(i + 1);
return execute(stack, p.is_vararg != 0 ? varargs.subargs(p.numparams + 1) : NONE);
}
protected Varargs execute(LuaValue[] stack, Varargs varargs) {
// loop through instructions
int i, a, b, c, pc = 0, top = 0;
LuaValue o;
Varargs v = NONE;
int[] code = p.code;
LuaValue[] k = p.k;
// upvalues are only possible when closures create closures
// TODO: use linked list.
UpValue[] openups = p.p.length > 0 ? new UpValue[stack.length] : null;
// allow for debug hooks
if (globals != null && globals.debuglib != null)
globals.debuglib.onCall(this, varargs, stack);
// process instructions
try {
for (; true; ++pc) {
if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction(pc, v, top);
// pull out instruction
i = code[pc];
a = ((i >> 6) & 0xff);
// process the op code
switch (i & 0x3f) {
case Lua.OP_MOVE:/* A B R(A):= R(B) */
stack[a] = stack[i >>> 23];
continue;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
stack[a] = k[i >>> 14];
continue;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
stack[a] = (i >>> 23 != 0) ? LuaValue.TRUE : LuaValue.FALSE;
if ((i & (0x1ff << 14)) != 0)
++pc; /* skip next instruction (if C) */
continue;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
for (b = i >>> 23; b-- >= 0;)
stack[a++] = LuaValue.NIL;
continue;
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
stack[a] = upValues[i >>> 23].getValue();
continue;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
stack[a] = upValues[i >>> 23].getValue().get((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
stack[a] = stack[i >>> 23].get((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
upValues[a].getValue().set(((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]), (c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
upValues[i >>> 23].setValue(stack[a]);
continue;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
stack[a].set(((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]), (c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
stack[a] = new LuaTable(i >>> 23, (i >> 14) & 0x1ff);
continue;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
stack[a + 1] = (o = stack[i >>> 23]);
stack[a] = o.get((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).add((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).sub((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).mul((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).div((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).mod((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
stack[a] = ((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).pow((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]);
continue;
case Lua.OP_UNM: /* A B R(A):= -R(B) */
stack[a] = stack[i >>> 23].neg();
continue;
case Lua.OP_NOT: /* A B R(A):= not R(B) */
stack[a] = stack[i >>> 23].not();
continue;
case Lua.OP_LEN: /* A B R(A):= length of R(B) */
stack[a] = stack[i >>> 23].len();
continue;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
b = i >>> 23;
c = (i >> 14) & 0x1ff;
{
if (c > b + 1) {
Buffer sb = stack[c].buffer();
while (--c >= b)
sb = stack[c].concat(sb);
stack[a] = sb.value();
} else {
stack[a] = stack[c - 1].concat(stack[c]);
}
}
continue;
case Lua.OP_JMP: /* sBx pc+=sBx */
pc += (i >>> 14) - 0x1ffff;
if (a > 0) {
for (--a, b = openups.length; --b >= 0;)
if (openups[b] != null && openups[b].index >= a) {
openups[b].close();
openups[b] = null;
}
}
continue;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
if (((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).eq_b((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
if (((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).lt_b((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
if (((b = i >>> 23) > 0xff ? k[b & 0x0ff] : stack[b]).lteq_b((c = (i >> 14) & 0x1ff) > 0xff ? k[c & 0x0ff] : stack[c]) != (a != 0))
++pc;
continue;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if (stack[a].toboolean() != ((i & (0x1ff << 14)) != 0))
++pc;
continue;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
/* note: doc appears to be reversed */
if ((o = stack[i >>> 23]).toboolean() != ((i & (0x1ff << 14)) != 0))
++pc;
else
stack[a] = o; // TODO: should be sBx?
continue;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
switch (i & (Lua.MASK_B | Lua.MASK_C)) {
case (1 << Lua.POS_B) | (0 << Lua.POS_C):
v = stack[a].invoke(NONE);
top = a + v.narg();
continue;
case (2 << Lua.POS_B) | (0 << Lua.POS_C):
v = stack[a].invoke(stack[a + 1]);
top = a + v.narg();
continue;
case (1 << Lua.POS_B) | (1 << Lua.POS_C):
stack[a].call();
continue;
case (2 << Lua.POS_B) | (1 << Lua.POS_C):
stack[a].call(stack[a + 1]);
continue;
case (3 << Lua.POS_B) | (1 << Lua.POS_C):
stack[a].call(stack[a + 1], stack[a + 2]);
continue;
case (4 << Lua.POS_B) | (1 << Lua.POS_C):
stack[a].call(stack[a + 1], stack[a + 2], stack[a + 3]);
continue;
case (1 << Lua.POS_B) | (2 << Lua.POS_C):
stack[a] = stack[a].call();
continue;
case (2 << Lua.POS_B) | (2 << Lua.POS_C):
stack[a] = stack[a].call(stack[a + 1]);
continue;
case (3 << Lua.POS_B) | (2 << Lua.POS_C):
stack[a] = stack[a].call(stack[a + 1], stack[a + 2]);
continue;
case (4 << Lua.POS_B) | (2 << Lua.POS_C):
stack[a] = stack[a].call(stack[a + 1], stack[a + 2], stack[a + 3]);
continue;
default:
b = i >>> 23;
c = (i >> 14) & 0x1ff;
v = b > 0 ? varargsOf(stack, a + 1, b - 1) : // exact arg count
varargsOf(stack, a + 1, top - v.narg() - (a + 1), v); // from prev top
v = stack[a].invoke(v);
if (c > 0) {
while (--c > 0)
stack[a + c - 1] = v.arg(c);
v = NONE; // TODO: necessary?
} else {
top = a + v.narg();
}
continue;
}
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
switch (i & Lua.MASK_B) {
case (1 << Lua.POS_B):
return new TailcallVarargs(stack[a], NONE);
case (2 << Lua.POS_B):
return new TailcallVarargs(stack[a], stack[a + 1]);
case (3 << Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a + 1], stack[a + 2]));
case (4 << Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a + 1], stack[a + 2], stack[a + 3]));
default:
b = i >>> 23;
v = b > 0 ? varargsOf(stack, a + 1, b - 1) : // exact arg count
varargsOf(stack, a + 1, top - v.narg() - (a + 1), v); // from prev top
return new TailcallVarargs(stack[a], v);
}
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
b = i >>> 23;
switch (b) {
case 0:
return varargsOf(stack, a, top - v.narg() - a, v);
case 1:
return NONE;
case 2:
return stack[a];
default:
return varargsOf(stack, a, b - 1);
}
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
{
LuaValue limit = stack[a + 1];
LuaValue step = stack[a + 2];
LuaValue idx = step.add(stack[a]);
if (step.gt_b(0) ? idx.lteq_b(limit) : idx.gteq_b(limit)) {
stack[a] = idx;
stack[a + 3] = idx;
pc += (i >>> 14) - 0x1ffff;
}
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a + 1].checknumber("'for' limit must be a number");
LuaValue step = stack[a + 2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a + 1] = limit;
stack[a + 2] = step;
pc += (i >>> 14) - 0x1ffff;
}
continue;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
v = stack[a].invoke(varargsOf(stack[a + 1], stack[a + 2]));
c = (i >> 14) & 0x1ff;
while (--c >= 0)
stack[a + 3 + c] = v.arg(c + 1);
v = NONE;
continue;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
if (!stack[a + 1].isnil()) { /* continue loop? */
stack[a] = stack[a + 1]; /* save control varible. */
pc += (i >>> 14) - 0x1ffff;
}
continue;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
{
if ((c = (i >> 14) & 0x1ff) == 0)
c = code[++pc];
int offset = (c - 1) * Lua.LFIELDS_PER_FLUSH;
o = stack[a];
if ((b = i >>> 23) == 0) {
b = top - a - 1;
int m = b - v.narg();
int j = 1;
for (; j <= m; j++)
o.set(offset + j, stack[a + j]);
for (; j <= b; j++)
o.set(offset + j, v.arg(j - m));
} else {
o.presize(offset + b);
for (int j = 1; j <= b; j++)
o.set(offset + j, stack[a + j]);
}
}
continue;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */
{
Prototype newp = p.p[i >>> 14];
LuaClosure ncl = new LuaClosure(newp, globals);
Upvaldesc[] uv = newp.upvalues;
for (int j = 0, nup = uv.length; j < nup; ++j) {
if (uv[j].instack) /* upvalue refes to local variable? */
ncl.upValues[j] = findupval(stack, uv[j].idx, openups);
else
/* get upvalue from enclosing function */
ncl.upValues[j] = upValues[uv[j].idx];
}
stack[a] = ncl;
}
continue;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
b = i >>> 23;
if (b == 0) {
top = a + (b = varargs.narg());
v = varargs;
} else {
for (int j = 1; j < b; ++j)
stack[a + j - 1] = varargs.arg(j);
}
continue;
case Lua.OP_EXTRAARG:
throw new java.lang.IllegalArgumentException("Uexecutable opcode: OP_EXTRAARG");
default:
throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f));
}
}
} catch (LuaError le) {
if (le.traceback == null)
processErrorHooks(le, p, pc);
throw le;
} catch (Exception e) {
LuaError le = new LuaError(e);
processErrorHooks(le, p, pc);
throw le;
} finally {
if (openups != null)
for (int u = openups.length; --u >= 0;)
if (openups[u] != null)
openups[u].close();
if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn();
}
}
/**
* Run the error hook if there is one
* @param msg the message to use in error hook processing.
* */
String errorHook(String msg, int level) {
if (globals == null)
return msg;
final LuaThread r = globals.running;
if (r.errorfunc == null)
return globals.debuglib != null ? msg + "\n" + globals.debuglib.traceback(level) : msg;
final LuaValue e = r.errorfunc;
r.errorfunc = null;
try {
return e.call(LuaValue.valueOf(msg)).tojstring();
} catch (Throwable t) {
return "error in error handling";
} finally {
r.errorfunc = e;
}
}
private void processErrorHooks(LuaError le, Prototype p, int pc) {
le.fileline = (p.source != null ? p.shortsource() : "?") + ":" + (p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length ? String.valueOf(p.lineinfo[pc]) : "?");
le.traceback = errorHook(le.getMessage(), le.level);
}
private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) {
final int n = openups.length;
for (int i = 0; i < n; ++i)
if (openups[i] != null && openups[i].index == idx)
return openups[i];
for (int i = 0; i < n; ++i)
if (openups[i] == null)
return openups[i] = new UpValue(stack, idx);
error("No space for upvalue");
return null;
}
protected LuaValue getUpvalue(int i) {
return upValues[i].getValue();
}
protected void setUpvalue(int i, LuaValue v) {
upValues[i].setValue(v);
}
public String name() {
return "<" + p.shortsource() + ":" + p.linedefined + ">";
}
}