/******************************************************************************* * 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; import net.luaos.tb.common.LuaUtil; /** * Central interpreter loop * * Extension of {@link LuaFunction} which executes lua bytecode. *

* A {@link LuaClosure} is a combination of a {@link Prototype} * and a {@link LuaValue} to use as an environment for execution. *

* There are three main ways {@link LuaClosure} instances are created: *

*

* To construct it directly, the {@link Prototype} is typically created via a compiler such as {@link LuaC}: *

 {@code
 * InputStream is = new ByteArrayInputStream("print('hello,world').getBytes());
 * Prototype p = LuaC.instance.compile(is, "script");
 * LuaValue _G = JsePlatform.standardGlobals()
 * LuaClosure f = new LuaClosure(p, _G);
 * }
*

* To construct it indirectly, the {@link LuaC} compiler may be used, * which implements the {@link LuaCompiler} interface: *

 {@code
 * LuaFunction f = LuaC.instance.load(is, "script", _G);
 * }
*

* Typically, a closure that has just been loaded needs to be initialized by executing it, * and its return value can be saved if needed: *

 {@code
 * LuaValue r = f.call();
 * _G.set( "mypkg", r ) 
 * }
*

* In the preceding, the loaded value is typed as {@link LuaFunction} * to allow for the possibility of other compilers such as {@link LuaJC} * producing {@link LuaFunction} directly without * creating a {@link Prototype} or {@link LuaClosure}. *

* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue}, * all the value operations can be used directly such as: *

* @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; 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, env, env instanceof Globals? (Globals) env: null); } public LuaClosure(Prototype p, LuaValue env, Globals globals) { 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); } this.globals = globals; // XXX Stefan if (globals == null) throw new RuntimeException("Fatal: Closure has no globals."); } 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>>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>>23; if ( b == 0 ) { top = a + (b = varargs.narg()); v = varargs; } else { for ( int j=1; j=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) { if (globals == null || globals.errorfunc == null) return msg; LuaValue errfunc = globals.errorfunc; globals.errorfunc = null; try { return errfunc.call( LuaValue.valueOf(msg) ).tojstring(); } catch ( Throwable t ) { return "error in error handling"; } finally { globals.errorfunc = errfunc; } } 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()); if (globals != null && globals.debuglib != null) le.traceback = le.traceback + "\n" + globals.debuglib.traceback(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); this.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+">"; } public void setGlobals(Globals globals) { this.globals = globals; } public Globals getGlobals() { return globals; } }