/*******************************************************************************
* 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:
*
* - Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}
* - Construct it indirectly by loading a chunk via {@link LuaCompiler#load(java.io.InputStream, String, LuaValue)}
*
- Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode processing
*
*
* 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:
*
* - {@link LuaValue#call()}
* - {@link LuaValue#call(LuaValue)}
* - {@link LuaValue#invoke()}
* - {@link LuaValue#invoke(Varargs)}
* - {@link LuaValue#method(String)}
* - {@link LuaValue#method(String,LuaValue)}
* - {@link LuaValue#invokemethod(String)}
* - {@link LuaValue#invokemethod(String,Varargs)}
* - ...
*
* @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;
}
}