Why statement return error in current Lua(Codea)?

Why statement "table.insert(touches, touch.id, touch)" return error in current Lua(Codea)?

Introduction

When I tried to run some Codea code on the new version, I found that some code about touch always return error. First the error is float without integer representation, after modified it with math.floor, the error changed to position out of bounds. It made me confuse(I had a post in the forum [SOLVED] Should touch.id be a int or a float?). I tried to look for the reason, below is the whole record.git

Test code

The code can run on the old version Codea which using the lua-5.1, and the current Codea is using lua-5.3, so I guess the bug is because of the different version of lua. For the easy testing, I do the debug on my Raspberry Pi which have two versions of lua. the test code is below:ide

Test Code 1

touches = {}
touch={id=100}
table.insert(touches, math.floor(touch.id), touch)

return error in Lua-5.3.2:函數

pi@rpi /opt/software/lua-5.3.2 $ lua
Lua 5.3.2  Copyright (C) 1994-2015 Lua.org, PUC-Rio
>
> touches = {}
> touch={id=100}
> table.insert(touches, math.floor(touch.id), touch)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
        [C]: in function 'table.insert'
        stdin:1: in main chunk
        [C]: in ?
>

return success in Lua-5.1.5:post

pi@rpi /opt/software $ lua5.1
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
>
> touches = {}
> touch={id=100}
> table.insert(touches, math.floor(touch.id), touch)
>

Test Code 2

return error in Lua-5.3.2:ui

kano@kano ~ $ lua
Lua 5.3.2  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> my={}
> table.insert(my,123,12)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
	[C]: in function 'table.insert'
	stdin:1: in main chunk
	[C]: in ?
> table.insert(my,1,12)
> table.insert(my,2,12)
> table.insert(my,4,12)
stdin:1: bad argument #2 to 'insert' (position out of bounds)
stack traceback:
	[C]: in function 'table.insert'
	stdin:1: in main chunk
	[C]: in ?
> my[123]=123
> #my
2
> unpack(my)
stdin:1: attempt to call a nil value (global 'unpack')
stack traceback:
	stdin:1: in main chunk
	[C]: in ?
> table.unpack(my)
12	12
> for k,v in pairs(my) do print(k,v) end
1	12
2	12
123	123
>

return success in Lua-5.1.5:this

pi@rpi /opt/software/lua-5.3.2/src $ lua
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> my={}
> table.insert(my,123,12)
> #my
stdin:1: unexpected symbol near '#'
> table.length(my)
stdin:1: attempt to call field 'length' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> table.len(my)
stdin:1: attempt to call field 'len' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> table.insert(my,1,12)
> table.insert(my,2,12)
> table.insert(my,4,12)
> my[123]=123
> print(#my)
4
> table.unpack(my)
stdin:1: attempt to call field 'unpack' (a nil value)
stack traceback:
        stdin:1: in main chunk
        [C]: ?
> for k,v in pairs(my) do print(k,v) end
1       12
2       12
4       12
123     123
>

Conclusion

It is easy to see:lua

  • Use table.insert on a empty table must start with position 1
  • 123 only be treat with the hash key in my, not array index.
  • The table length will not include the elements which stored with hash.

Analyze the lua source code

Use the command git grep -n "error info", we can find the relational function source code:debug

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "position out of bounds"
src/ltablib.c:90:      luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
src/ltablib.c:110:    luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
pi@rpi /opt/software/lua-5.3.2 $

The result is clear, we know that the code is in file src/ltablib.c, line 90 and 110, the code is below:code

79 static int tinsert (lua_State *L) {
 80   lua_Integer e = aux_getn(L, 1, TAB_RW) + 1;  /* first empty element */
 81   lua_Integer pos;  /* where to insert new element */
 82   switch (lua_gettop(L)) {
 83     case 2: {  /* called with only 2 arguments */
 84       pos = e;  /* insert new element at the end */
 85       break;
 86     }
 87     case 3: {
 88       lua_Integer i;
 89       pos = luaL_checkinteger(L, 2);  /* 2nd argument is the position */
 90       luaL_argcheck(L, 1 <= pos && pos <= e, 2, "position out of bounds");
 91       for (i = e; i > pos; i--) {  /* move up elements */
 92         lua_geti(L, 1, i - 1);
 93         lua_seti(L, 1, i);  /* t[i] = t[i - 1] */
 94       }
 95       break;
 96     }
 97     default: {
 98       return luaL_error(L, "wrong number of arguments to 'insert'");
 99     }
100   }
101   lua_seti(L, 1, pos);  /* t[pos] = v */
102   return 0;
103 }
104 
105 
106 static int tremove (lua_State *L) {
107   lua_Integer size = aux_getn(L, 1, TAB_RW);
108   lua_Integer pos = luaL_optinteger(L, 2, size);
109   if (pos != size)  /* validate 'pos' if given */
110     luaL_argcheck(L, 1 <= pos && pos <= size + 1, 1, "position out of bounds");
111   lua_geti(L, 1, pos);  /* result = t[pos] */
112   for ( ; pos < size; pos++) {
113     lua_geti(L, 1, pos + 1);
114     lua_seti(L, 1, pos);  /* t[pos] = t[pos + 1] */
115   }
116   lua_pushnil(L);
117   lua_seti(L, 1, pos);  /* t[pos] = nil */
118   return 1;
119 }

We can see the check 1 <= pos && pos <= e in function tinsert and the check 1 <= pos && pos <= size + 1 in function tremove. And we can find the define of e and size:element

lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */

The relational code:

27 #define TAB_R   1           /* read */
 28 #define TAB_W   2           /* write */
 29 #define TAB_L   4           /* length */
 30 #define TAB_RW  (TAB_R | TAB_W)     /* read/write */
 31 
 32 
 33 #define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
 34 
 35 
 36 static int checkfield (lua_State *L, const char *key, int n) {
 37   lua_pushstring(L, key);
 38   return (lua_rawget(L, -n) != LUA_TNIL);
 39 }
 40 
 41 
 42 /*
 43 ** Check that 'arg' either is a table or can behave like one (that is,
 44 ** has a metatable with the required metamethods)
 45 */
 46 static void checktab (lua_State *L, int arg, int what) {
 47   if (lua_type(L, arg) != LUA_TTABLE) {  /* is it not a table? */
 48     int n = 1;  /* number of elements to pop */
 49     if (lua_getmetatable(L, arg) &&  /* must have metatable */
 50         (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
 51         (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
 52         (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
 53       lua_pop(L, n);  /* pop metatable and tested metamethods */
 54     }
 55     else
 56       luaL_argerror(L, arg, "table expected");  /* force an error */
 57   }
 58 }

Now we know the meaning: check the insert position, if it is bigger than the current size of the table, then return error with info "position out of bounds".

In version 5.1, we can insert in any position:

pi@rpi /opt/software $ lua5.1
Lua 5.1.5  Copyright (C) 1994-2012 Lua.org, PUC-Rio
> touches = {}
> touch={id=100}
> table.insert(touches, 1000000, touch)
>

Let us see the source code in version 5.1:

90 static int tinsert (lua_State *L) {
 91   int e = aux_getn(L, 1) + 1;  /* first empty element */
 92   int pos;  /* where to insert new element */
 93   switch (lua_gettop(L)) {
 94     case 2: {  /* called with only 2 arguments */
 95       pos = e;  /* insert new element at the end */
 96       break;
 97     }
 98     case 3: {
 99       int i;
100       pos = luaL_checkint(L, 2);  /* 2nd argument is the position */
101       if (pos > e) e = pos;  /* `grow' array if necessary */
102       for (i = e; i > pos; i--) {  /* move up elements */
103         lua_rawgeti(L, 1, i-1);
104         lua_rawseti(L, 1, i);  /* t[i] = t[i-1] */
105       }
106       break;
107     }
108     default: {
109       return luaL_error(L, "wrong number of arguments to " LUA_QL("insert"));
110     }
111   }
112   luaL_setn(L, 1, e);  /* new size */
113   lua_rawseti(L, 1, pos);  /* t[pos] = v */
114   return 0;
115 }
116 
117 
118 static int tremove (lua_State *L) {
119   int e = aux_getn(L, 1);
120   int pos = luaL_optint(L, 2, e);
121   if (!(1 <= pos && pos <= e))  /* position is outside bounds? */
122    return 0;  /* nothing to remove */
123   luaL_setn(L, 1, e - 1);  /* t.n = n-1 */
124   lua_rawgeti(L, 1, pos);  /* result = t[pos] */
125   for ( ;pos<e; pos++) {
126     lua_rawgeti(L, 1, pos+1);
127     lua_rawseti(L, 1, pos);  /* t[pos] = t[pos+1] */
128   }
129   lua_pushnil(L);
130   lua_rawseti(L, 1, e);  /* t[e] = nil */
131   return 1;
132 }

We can see that, it is a different dealing with the insert position argument.

if (pos > e) e = pos;  /* `grow' array if necessary */

If the pos is bigger than the current max position(the e), then expand the table with the pos, It is a "grow" array, but it need alloc more memery. Maybe for the optimizing reason, in 5.3 this is not allowed, then the old code get error.

More details of source code

若是想了解更清楚, 能夠在源代碼裏搜索一下函數(或者宏) luaL_argcheck:

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "luaL_argcheck"
...
src/lauxlib.h:114:#define luaL_argcheck(L, cond,arg,extramsg)   \
...

看樣子是個宏, 打開 src/lauxlib.h, 查到以下宏定義:

114 #define luaL_argcheck(L, cond,arg,extramsg) \
115         ((void)((cond) || luaL_argerror(L, (arg), (extramsg))))

發現又調用了一個 luaL_argerror, 先在本文件裏查一下, 發現有函數聲明:

38 LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);

那麼函數定義應該在 src/lauxlib.c 中, 再用 git grep -n 搜一把, 以下:

pi@rpi /opt/software/lua-5.3.2 $ git grep -n "luaL_argerror"
...
src/lauxlib.c:164:LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
...

很好, 打開看看具體代碼:

164 LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
 165   lua_Debug ar;
 166   if (!lua_getstack(L, 0, &ar))  /* no stack frame? */
 167     return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
 168   lua_getinfo(L, "n", &ar);
 169   if (strcmp(ar.namewhat, "method") == 0) {
 170     arg--;  /* do not count 'self' */
 171     if (arg == 0)  /* error is in the self argument itself? */
 172       return luaL_error(L, "calling '%s' on bad self (%s)",
 173                            ar.name, extramsg);
 174   }
 175   if (ar.name == NULL)
 176     ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
 177   return luaL_error(L, "bad argument #%d to '%s' (%s)",
 178                         arg, ar.name, extramsg);
 179 }

看得出來, 咱們的試驗代碼觸發了最後一條判斷語句:

...
 175   if (ar.name == NULL)
 176     ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
 177   return luaL_error(L, "bad argument #%d to '%s' (%s)",
 178                         arg, ar.name, extramsg);
 ...

--The End

相關文章
相關標籤/搜索