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
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
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) >
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 >
It is easy to see:lua
table.insert
on a empty table must start with position 1
123
only be treat with the hash key in my
, not array index.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.
若是想了解更清楚, 能夠在源代碼裏搜索一下函數(或者宏) 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