diff --git a/src/core/org/luaj/vm2/LuaValue.java b/src/core/org/luaj/vm2/LuaValue.java index 8cec3de4..2cec897b 100644 --- a/src/core/org/luaj/vm2/LuaValue.java +++ b/src/core/org/luaj/vm2/LuaValue.java @@ -21,6 +21,8 @@ ******************************************************************************/ package org.luaj.vm2; +import org.luaj.vm2.Varargs.SubVarargs; + /** * Base class for all concrete lua type values. @@ -3387,8 +3389,8 @@ public class LuaValue extends Varargs { switch ( v.length ) { case 0: return NONE; case 1: return v[0]; - case 2: return new PairVarargs(v[0],v[1]); - default: return new ArrayVarargs(v,NONE); + case 2: return new Varargs.PairVarargs(v[0],v[1]); + default: return new Varargs.ArrayVarargs(v,NONE); } } @@ -3403,8 +3405,8 @@ public class LuaValue extends Varargs { public static Varargs varargsOf(final LuaValue[] v,Varargs r) { switch ( v.length ) { case 0: return r; - case 1: return new PairVarargs(v[0],r); - default: return new ArrayVarargs(v,r); + case 1: return new Varargs.PairVarargs(v[0],r); + default: return new Varargs.ArrayVarargs(v,r); } } @@ -3421,8 +3423,8 @@ public class LuaValue extends Varargs { switch ( length ) { case 0: return NONE; case 1: return v[offset]; - case 2: return new PairVarargs(v[offset+0],v[offset+1]); - default: return new ArrayPartVarargs(v,offset,length); + case 2: return new Varargs.PairVarargs(v[offset+0],v[offset+1]); + default: return new Varargs.ArrayPartVarargs(v,offset,length); } } @@ -3439,8 +3441,8 @@ public class LuaValue extends Varargs { public static Varargs varargsOf(final LuaValue[] v, final int offset, final int length,Varargs more) { switch ( length ) { case 0: return more; - case 1: return new PairVarargs(v[offset],more); - default: return new ArrayPartVarargs(v,offset,length,more); + case 1: return new Varargs.PairVarargs(v[offset],more); + default: return new Varargs.ArrayPartVarargs(v,offset,length,more); } } @@ -3457,7 +3459,7 @@ public class LuaValue extends Varargs { public static Varargs varargsOf(LuaValue v, Varargs r) { switch ( r.narg() ) { case 0: return v; - default: return new PairVarargs(v,r); + default: return new Varargs.PairVarargs(v,r); } } @@ -3474,8 +3476,8 @@ public class LuaValue extends Varargs { */ public static Varargs varargsOf(LuaValue v1,LuaValue v2,Varargs v3) { switch ( v3.narg() ) { - case 0: return new PairVarargs(v1,v2); - default: return new ArrayVarargs(new LuaValue[] {v1,v2},v3); + case 0: return new Varargs.PairVarargs(v1,v2); + default: return new Varargs.ArrayVarargs(new LuaValue[] {v1,v2},v3); } } @@ -3530,120 +3532,21 @@ public class LuaValue extends Varargs { public int narg() { return 0; } public LuaValue arg1() { return NIL; } public String tojstring() { return "none"; } + public Varargs subargs(final int start) { return this; } + } - /** Varargs implemenation backed by an array of LuaValues - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue[]) - * @see LuaValue#varargsOf(LuaValue[], Varargs) + /** + * Create a {@code Varargs} instance containing arguments starting at index {@code start} + * @param start the index from which to include arguments, where 1 is the first argument. + * @return Varargs containing argument { start, start+1, ... , narg-start-1 } */ - static final class ArrayVarargs extends Varargs { - private final LuaValue[] v; - private final Varargs r; - /** Construct a Varargs from an array of LuaValue. - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue[]) - * @see LuaValue#varargsOf(LuaValue[], Varargs) - */ - ArrayVarargs(LuaValue[] v, Varargs r) { - this.v = v; - this.r = r ; - } - public LuaValue arg(int i) { - return i >=1 && i<=v.length? v[i - 1]: r.arg(i-v.length); - } - public int narg() { - return v.length+r.narg(); - } - public LuaValue arg1() { return v.length>0? v[0]: r.arg1(); } - } - - /** Varargs implemenation backed by an array of LuaValues - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue[], int, int) - * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs) - */ - static final class ArrayPartVarargs extends Varargs { - private final int offset; - private final LuaValue[] v; - private final int length; - private final Varargs more; - /** Construct a Varargs from an array of LuaValue. - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static methods on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue[], int, int) - */ - ArrayPartVarargs(LuaValue[] v, int offset, int length) { - this.v = v; - this.offset = offset; - this.length = length; - this.more = NONE; - } - /** Construct a Varargs from an array of LuaValue and additional arguments. - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static method on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs) - */ - public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) { - this.v = v; - this.offset = offset; - this.length = length; - this.more = more; - } - public LuaValue arg(int i) { - return i>=1&&i<=length? v[i+offset-1]: more.arg(i-length); - } - public int narg() { - return length + more.narg(); - } - public LuaValue arg1() { - return length>0? v[offset]: more.arg1(); - } - } - - /** Varargs implemenation backed by two values. - *
- * This is an internal class not intended to be used directly. - * Instead use the corresponding static method on LuaValue. - * - * @see LuaValue#varargsOf(LuaValue, Varargs) - */ - static final class PairVarargs extends Varargs { - private final LuaValue v1; - private final Varargs v2; - /** Construct a Varargs from an two LuaValue. - *
- * This is an internal class not intended to be used directly.
- * Instead use the corresponding static method on LuaValue.
- *
- * @see LuaValue#varargsOf(LuaValue, Varargs)
- */
- PairVarargs(LuaValue v1, Varargs v2) {
- this.v1 = v1;
- this.v2 = v2;
- }
- public LuaValue arg(int i) {
- return i==1? v1: v2.arg(i-1);
- }
- public int narg() {
- return 1+v2.narg();
- }
- public LuaValue arg1() {
- return v1;
- }
+ public Varargs subargs(final int start) {
+ if (start == 1)
+ return this;
+ if (start > 1)
+ return NONE;
+ return new Varargs.SubVarargs(this, start, 1);
}
}
diff --git a/src/core/org/luaj/vm2/Varargs.java b/src/core/org/luaj/vm2/Varargs.java
index 340bdd20..15ce8dce 100644
--- a/src/core/org/luaj/vm2/Varargs.java
+++ b/src/core/org/luaj/vm2/Varargs.java
@@ -505,7 +505,7 @@ public abstract class Varargs {
int end = narg();
switch ( end-start ) {
case 0: return arg(start);
- case 1: return new LuaValue.PairVarargs(arg(start),arg(end));
+ case 1: return new Varargs.PairVarargs(arg(start),arg(end));
}
return end
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static method on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue, Varargs)
+ */
+ static final class PairVarargs extends Varargs {
+ private final LuaValue v1;
+ private final Varargs v2;
+ /** Construct a Varargs from an two LuaValue.
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static method on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue, Varargs)
+ */
+ PairVarargs(LuaValue v1, Varargs v2) {
+ this.v1 = v1;
+ this.v2 = v2;
+ }
+ public LuaValue arg(int i) {
+ return i==1? v1: v2.arg(i-1);
+ }
+ public int narg() {
+ return 1+v2.narg();
+ }
+ public LuaValue arg1() {
+ return v1;
+ }
+ public Varargs subargs(final int start) {
+ if (start == 1)
+ return this;
+ if (start == 2)
+ return v2;
+ if (start > 2)
+ return v2.subargs(start - 1);
+ return new SubVarargs(this, start, 2);
+ }
+ }
+
+ /** Varargs implemenation backed by an array of LuaValues
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static methods on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue[])
+ * @see LuaValue#varargsOf(LuaValue[], Varargs)
+ */
+ static final class ArrayVarargs extends Varargs {
+ private final LuaValue[] v;
+ private final Varargs r;
+ /** Construct a Varargs from an array of LuaValue.
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static methods on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue[])
+ * @see LuaValue#varargsOf(LuaValue[], Varargs)
+ */
+ ArrayVarargs(LuaValue[] v, Varargs r) {
+ this.v = v;
+ this.r = r ;
+ for (int i = 0; i < v.length; ++i)
+ if (v[i] == null)
+ throw new IllegalArgumentException("nulls in array");
+ }
+ public LuaValue arg(int i) {
+ return i >=1 && i<=v.length? v[i - 1]: r.arg(i-v.length);
+ }
+ public int narg() {
+ return v.length+r.narg();
+ }
+ public LuaValue arg1() { return v.length>0? v[0]: r.arg1(); }
+ }
+
+ /** Varargs implemenation backed by an array of LuaValues
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static methods on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue[], int, int)
+ * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
+ */
+ static final class ArrayPartVarargs extends Varargs {
+ private final int offset;
+ private final LuaValue[] v;
+ private final int length;
+ private final Varargs more;
+ /** Construct a Varargs from an array of LuaValue.
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static methods on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue[], int, int)
+ */
+ ArrayPartVarargs(LuaValue[] v, int offset, int length) {
+ this.v = v;
+ this.offset = offset;
+ this.length = length;
+ this.more = LuaValue.NONE;
+ }
+ /** Construct a Varargs from an array of LuaValue and additional arguments.
+ *
+ * This is an internal class not intended to be used directly.
+ * Instead use the corresponding static method on LuaValue.
+ *
+ * @see LuaValue#varargsOf(LuaValue[], int, int, Varargs)
+ */
+ public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) {
+ this.v = v;
+ this.offset = offset;
+ this.length = length;
+ this.more = more;
+ }
+ public LuaValue arg(int i) {
+ return i>=1&&i<=length? v[i+offset-1]: more.arg(i-length);
+ }
+ public int narg() {
+ return length + more.narg();
+ }
+ public LuaValue arg1() {
+ return length>0? v[offset]: more.arg1();
+ }
}
}
diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java
index a0ebc7f5..80508d74 100644
--- a/src/core/org/luaj/vm2/lib/DebugLib.java
+++ b/src/core/org/luaj/vm2/lib/DebugLib.java
@@ -55,7 +55,7 @@ import org.luaj.vm2.Varargs;
* To instantiate and use it directly,
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
* {@code
- * LuaTable _G = new LuaTable();
+ * Globals _G = new Globals();
* _G.load(new DebugLib());
* }
* Doing so will ensure the library is properly initialized
@@ -112,7 +112,7 @@ public class DebugLib extends OneArgFunction {
int lastline;
int bytecodes;
- public LuaTable call(LuaValue env) {
+ public LuaValue call(LuaValue env) {
globals = env.checkglobals();
globals.debuglib = this;
LuaTable debug = new LuaTable();
@@ -148,7 +148,7 @@ public class DebugLib extends OneArgFunction {
final class gethook extends VarArgFunction {
public Varargs invoke(Varargs args) {
return varargsOf(
- hookfunc,
+ hookfunc != null? hookfunc: NIL,
valueOf((hookcall?"c":"")+(hookline?"l":"")+(hookrtrn?"r":"")),
valueOf(hookcount));
}
@@ -237,7 +237,8 @@ public class DebugLib extends OneArgFunction {
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread;
int level = args.checkint(a++);
int local = args.checkint(a++);
- return callstack(thread).getCallFrame(level).getLocal(local);
+ CallFrame f = callstack(thread).getCallFrame(level);
+ return f != null? f.getLocal(local): NONE;
}
}
@@ -282,7 +283,7 @@ public class DebugLib extends OneArgFunction {
// debug.sethook ([thread,] hook, mask [, count])
final class sethook extends VarArgFunction {
- public Varargs call(Varargs args) {
+ public Varargs invoke(Varargs args) {
int a=1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running_thread;
LuaValue func = args.optfunction(a++, null);
@@ -312,7 +313,8 @@ public class DebugLib extends OneArgFunction {
int level = args.checkint(a++);
int local = args.checkint(a++);
LuaValue value = args.arg(a++);
- return callstack(thread).getCallFrame(level).setLocal(local, value);
+ CallFrame f = callstack(thread).getCallFrame(level);
+ return f != null? f.setLocal(local, value): NONE;
}
}
@@ -383,7 +385,7 @@ public class DebugLib extends OneArgFunction {
if ( func instanceof LuaClosure ) {
LuaClosure c = (LuaClosure) func;
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) {
- return userdataOf(c.upValues[up-1].hashCode());
+ return valueOf(c.upValues[up-1].hashCode());
}
}
return NIL;
@@ -397,6 +399,7 @@ public class DebugLib extends OneArgFunction {
int n1 = args.checkint(2);
LuaValue f2 = args.checkfunction(3);
int n2 = args.checkint(4);
+ f1.checkclosure().upValues[n1] = f2.checkclosure().upValues[n2];
return NONE;
}
}
diff --git a/test/junit/org/luaj/vm2/ErrorsTest.java b/test/junit/org/luaj/vm2/ErrorsTest.java
index b8bda0a7..264d3d4d 100644
--- a/test/junit/org/luaj/vm2/ErrorsTest.java
+++ b/test/junit/org/luaj/vm2/ErrorsTest.java
@@ -52,6 +52,7 @@ public class ErrorsTest extends ScriptDrivenTest {
runTest("baselibargs");
}
public void testCoroutineLibArgs() { runTest("coroutinelibargs"); }
+ public void testDebugLibArgs() { runTest("debuglibargs"); }
public void testIoLibArgs() { runTest("iolibargs"); }
public void testMathLibArgs() { runTest("mathlibargs"); }
public void testModuleLibArgs() { runTest("modulelibargs"); }
diff --git a/test/lua/debuglib.lua b/test/lua/debuglib.lua
index a89ff2ba..57ac8cda 100644
--- a/test/lua/debuglib.lua
+++ b/test/lua/debuglib.lua
@@ -211,11 +211,27 @@ local tryhooks = function(mask)
tostring(d1)..' ' )
end
---[[
tryhooks("c")
tryhooks("r")
tryhooks("l")
tryhooks("crl")
---]]
-
\ No newline at end of file
+print( '----- debug.getupvalueid' )
+local x=1, y=2
+function a()
+ return function()
+ return x,y
+ end
+end
+a1 = a()
+a2 = a()
+print('debug.getupvalue(a1,1)', debug.getupvalue(a1,1))
+print('debug.getupvalue(a1,2)', debug.getupvalue(a1,2))
+print('debug.getupvalue(a2,1)', debug.getupvalue(a2,1))
+print('debug.getupvalue(a2,2)', debug.getupvalue(a2,2))
+print('debug.getupvalueid(a1,1) == debug.getupvalueid(a1,1)', debug.getupvalueid(a1,1) == debug.getupvalueid(a1,1))
+print('debug.getupvalueid(a1,1) == debug.getupvalueid(a2,1)', debug.getupvalueid(a1,1) == debug.getupvalueid(a2,1))
+print('debug.getupvalueid(a1,2) == debug.getupvalueid(a1,2)', debug.getupvalueid(a1,2) == debug.getupvalueid(a1,2))
+print('debug.getupvalueid(a1,2) == debug.getupvalueid(a2,2)', debug.getupvalueid(a1,2) == debug.getupvalueid(a2,2))
+
+v
diff --git a/test/lua/errors/debuglibargs.lua b/test/lua/errors/debuglibargs.lua
new file mode 100644
index 00000000..110a98bf
--- /dev/null
+++ b/test/lua/errors/debuglibargs.lua
@@ -0,0 +1,150 @@
+package.path = "?.lua;test/lua/errors/?.lua"
+require 'args'
+
+local alevel = {25,'25'}
+local afuncorlevel = {afunction,25,'25'}
+local notafuncorlevel = {nil,astring,aboolean,athread}
+local notafuncorthread = {nil,astring,aboolean}
+
+-- debug.debug()
+banner('debug.debug - no tests')
+
+-- debug.gethook ([thread])
+banner('debug.gethook')
+checkallpass('debug.gethook',{})
+
+-- debug.getinfo ([thread,] f [, what])
+banner('debug.getinfo')
+local awhat = {"","n","flnStu"}
+local notawhat = {"qzQZ"}
+checkallpass('debug.getinfo',{afuncorlevel})
+checkallpass('debug.getinfo',{somethread,afuncorlevel})
+checkallpass('debug.getinfo',{afuncorlevel,awhat})
+checkallpass('debug.getinfo',{somethread,afuncorlevel,awhat})
+checkallerrors('debug.getinfo',{},'function or level')
+checkallerrors('debug.getinfo',{notafuncorlevel},'function or level')
+checkallerrors('debug.getinfo',{somefunction,nonstring}, 'string expected')
+checkallerrors('debug.getinfo',{notafuncorthread,somefunction}, 'string expected')
+checkallerrors('debug.getinfo',{nonthread,somefunction,{astring}}, 'string expected')
+checkallerrors('debug.getinfo',{somethread,somefunction,notawhat}, 'invalid option')
+
+-- debug.getlocal ([thread,] f, local)
+banner('debug.getlocal')
+local p,q = 'p','q';
+f = function(x,y)
+ print('f: x,y,a,b,p,q', x, y, a, b, p, q)
+ local a,b = x,y
+ local t = coroutine.running()
+ checkallpass('debug.getlocal',{{f,1},{1,'2'}})
+ checkallpass('debug.getlocal',{{t},{f},{1}})
+ checkallerrors('debug.getlocal',{},'number expected')
+ checkallerrors('debug.getlocal',{afuncorlevel,notanumber},'number expected')
+ checkallerrors('debug.getlocal',{notafuncorlevel,somenumber}, 'number expected')
+ checkallerrors('debug.getlocal',{{t},afuncorlevel}, 'got no value')
+ checkallerrors('debug.getlocal',{nonthread,{f},{1,'2'}}, 'number expected')
+ checkallerrors('debug.getlocal',{{t},{100},{1}}, 'level out of range')
+end
+f(1,2)
+
+-- debug.getmetatable (value)
+banner('debug.getmetatable')
+checkallpass('debug.getmetatable',{anylua})
+checkallerrors('debug.getmetatable',{},'value expected')
+
+-- debug.getregistry ()
+banner('debug.getregistry')
+checkallpass('debug.getregistry',{})
+checkallpass('debug.getregistry',{anylua})
+
+-- debug.getupvalue (f, up)
+banner('debug.getupvalue')
+checkallpass('debug.getupvalue',{{f},{1,'2'}})
+checkallerrors('debug.getupvalue',{},'number expected')
+checkallerrors('debug.getupvalue',{notafunction,{1,'2'}}, 'function expected')
+checkallerrors('debug.getupvalue',{{f},notanumber}, 'number expected')
+
+-- debug.getuservalue (u)
+checkallpass('debug.getuservalue',{})
+checkallpass('debug.getuservalue',{anylua})
+
+-- debug.sethook ([thread,] hook, mask [, count])
+local ahookstr = {"cr","l"}
+checkallpass('debug.sethook',{})
+checkallpass('debug.sethook',{somenil,ahookstr})
+checkallpass('debug.sethook',{somefunction,ahookstr})
+checkallpass('debug.sethook',{{nil,athread,n=2},somefunction,ahookstr})
+checkallerrors('debug.sethook',{{astring,afunction,aboolean}},'string expected')
+checkallerrors('debug.sethook',{{astring,afunction,aboolean},{nil,afunction,n=2},ahookstr},'string expected')
+
+-- debug.setlocal ([thread,] level, local, value)
+banner('debug.setlocal')
+local p,q = 'p','q';
+f = function(x,y)
+ print('f: x,y,a,b,p,q', x, y, a, b, p, q)
+ local a,b = x,y
+ local t = coroutine.running()
+ checkallpass('debug.setlocal',{{1},{1},{nil,'foo',n=2}})
+ print('f: x,y,a,b,p,q', x, y, a, b, p, q)
+ checkallpass('debug.setlocal',{{t},{1},{2},{nil,'bar',n=2}})
+ print('f: x,y,a,b,p,q', x, y, a, b, p, q)
+ checkallerrors('debug.setlocal',{},'number expected')
+ checkallerrors('debug.setlocal',{{1}},'value expected')
+ checkallerrors('debug.setlocal',{{1},{1}}, 'value expected')
+ checkallerrors('debug.setlocal',{{t},{1},{2}}, 'value expected')
+ checkallerrors('debug.setlocal',{{atable,astring},{1}}, 'number expected')
+ checkallerrors('debug.setlocal',{{1},nonnumber}, 'value expected')
+ checkallerrors('debug.setlocal',{{atable,astring},{1},{1},{nil,'foo',n=2}}, 'number expected')
+ checkallerrors('debug.setlocal',{{10},{1},{'foo'}}, 'level out of range')
+ return p,q
+end
+f(1,2)
+
+-- debug.setmetatable (value, table)
+banner('debug.setmetatable')
+checkallpass('debug.setmetatable',{anylua,{atable,nil,n=2}})
+checkallerrors('debug.setmetatable',{},'nil or table')
+checkallerrors('debug.setmetatable',{anylua},'nil or table')
+
+-- debug.setupvalue (f, up, value)
+banner('debug.setupvalue')
+checkallpass('debug.setupvalue',{{f},{2,'3'},{nil,aboolean,astring,n=3}})
+print('p,q', p, q)
+checkallerrors('debug.setupvalue',{},'value expected')
+checkallerrors('debug.setupvalue',{{f}},'value expected')
+checkallerrors('debug.setupvalue',{{f},{1}},'value expected')
+checkallerrors('debug.setupvalue',{notafunction,{2}}, 'value expected')
+checkallerrors('debug.setupvalue',{{f},notanumber}, 'value expected')
+
+-- debug.setuservalue (udata, value)
+banner('debug.setuservalue')
+checkallerrors('debug.setuservalue',{},'userdata expected')
+checkallerrors('debug.setuservalue',{anylua},'userdata expected')
+checkallerrors('debug.setuservalue',{anylua,somestring},'userdata expected')
+
+-- debug.traceback ([thread,] [message [, level]])
+banner('debug.traceback')
+local t = coroutine.running()
+checkallpass('debug.traceback',{})
+checkallpass('debug.traceback',{{astring}})
+checkallpass('debug.traceback',{{astring},{anumber}})
+checkallpass('debug.traceback',{{t}})
+checkallpass('debug.traceback',{{t},{astring}})
+checkallpass('debug.traceback',{{t},{astring},{anumber}})
+checkallpass('debug.traceback',{{afunction,aboolean,atable}})
+checkallpass('debug.traceback',{{afunction,aboolean,atable},notanumber})
+
+-- debug.upvalueid (f, n)
+banner('debug.upvalueid')
+checkallpass('debug.upvalueid',{{f},{1,'2'}})
+checkallerrors('debug.upvalueid',{},'number expected')
+checkallerrors('debug.upvalueid',{notafunction,{1,'2'}}, 'function expected')
+checkallerrors('debug.upvalueid',{{f},notanumber}, 'number expected')
+
+-- debug.upvaluejoin (f1, n1, f2, n2)
+banner('debug.upvaluejoin')
+checkallpass('debug.upvaluejoin',{{f},{1,'2'},{f},{1,'2'}})
+checkallerrors('debug.upvaluejoin',{},'number expected')
+checkallerrors('debug.upvaluejoin',{notafunction,{1,'2'}}, 'function expected')
+checkallerrors('debug.upvaluejoin',{{f},notanumber}, 'number expected')
+checkallerrors('debug.upvaluejoin',{{f},{1},notafunction,{1,'2'}}, 'function expected')
+checkallerrors('debug.upvaluejoin',{{f},{1},{f},notanumber}, 'number expected')