/******************************************************************************* * 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.vm2; /** * 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.source.tojstring(): "?") + ":" + (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+">"; } }