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 0) { + if (newstart >= this.end) + return LuaValue.NONE; + if (newstart == this.end) + return v.arg(this.end); + if (newstart == this.end-1) + return new Varargs.PairVarargs(v.arg(this.end-1), v.arg(this.end)); + return new SubVarargs(v, newstart, this.end); + } + return new SubVarargs(v, newstart, this.end); + } + } + + /** 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 == 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')