webmcp
changeset 138:8a533f370038
First part of refactoring JSON library (use LUA_REGISTRYINDEX with lightuserdata keys instead of C closures)
author | jbe |
---|---|
date | Mon Jul 28 23:39:23 2014 +0200 (2014-07-28) |
parents | f490b78827d6 |
children | a4ce17051eff |
files | libraries/json/json.c |
line diff
1.1 --- a/libraries/json/json.c Mon Jul 28 19:04:32 2014 +0200 1.2 +++ b/libraries/json/json.c Mon Jul 28 23:39:23 2014 +0200 1.3 @@ -11,9 +11,22 @@ 1.4 #define JSON_UPVAL_PAIRS_ITERFUNC lua_upvalueindex(6) 1.5 #define JSON_UPVAL_IPAIRS_ITERFUNC lua_upvalueindex(7) 1.6 1.7 +#define JSON_REGENT static char 1.8 +#define JSON_REGREF void * 1.9 + 1.10 +JSON_REGENT json_nullmark; 1.11 +JSON_REGENT json_shadowtbl; 1.12 +JSON_REGENT json_unknownmt; 1.13 +JSON_REGENT json_objectmt; 1.14 +JSON_REGENT json_arraymt; 1.15 + 1.16 +#define json_regfetch(L, x) (lua_pushlightuserdata((L), &(x)), lua_rawget((L), LUA_REGISTRYINDEX)) 1.17 + 1.18 +#define json_regstore(L, x) (lua_pushlightuserdata(L, &(x)), lua_pushvalue(L, -2), lua_rawset(L, LUA_REGISTRYINDEX)); 1.19 + 1.20 // marks a table as JSON object or JSON array: 1.21 // (returns its modified argument or a new table if argument is nil) 1.22 -static int json_mark(lua_State *L, const char *marker) { 1.23 +static int json_mark(lua_State *L, JSON_REGREF mt) { 1.24 // if argument is nil, then create new table: 1.25 if (lua_isnoneornil(L, 1)) { 1.26 lua_settop(L, 0); 1.27 @@ -21,35 +34,36 @@ 1.28 // skip testing of existing shadow table: 1.29 goto json_object_create_shadow_table; 1.30 } 1.31 + // check if shadow table already exists: 1.32 + json_regfetch(L, json_shadowtbl); 1.33 lua_pushvalue(L, 1); 1.34 - lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.35 + lua_rawget(L, -2); 1.36 if (lua_isnil(L, -1)) { 1.37 json_object_create_shadow_table: 1.38 // set shadow table: 1.39 lua_pushvalue(L, 1); 1.40 lua_newtable(L); 1.41 - lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.42 + lua_rawset(L, -4); 1.43 } 1.44 + // discard everything but table to return: 1.45 + lua_settop(L, 1); 1.46 // set metatable: 1.47 - lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.48 + json_regfetch(L, mt); 1.49 lua_setmetatable(L, 1); 1.50 - // mark table as JSON object or as JSON array 1.51 - lua_pushvalue(L, 1); 1.52 - lua_pushstring(L, marker); 1.53 - lua_rawset(L, JSON_UPVAL_TYPES); 1.54 + // return table: 1.55 return 1; 1.56 } 1.57 1.58 // marks a table as JSON object: 1.59 // (returns its modified argument or a new table if argument is nil) 1.60 static int json_object(lua_State *L) { 1.61 - return json_mark(L, "object"); 1.62 + return json_mark(L, &json_objectmt); 1.63 } 1.64 1.65 // marks a table as JSON array: 1.66 // (returns its modified argument or a new table if argument is nil) 1.67 static int json_array(lua_State *L) { 1.68 - return json_mark(L, "array"); 1.69 + return json_mark(L, &json_arraymt); 1.70 } 1.71 1.72 #define JSON_STATE_VALUE 0 1.73 @@ -61,6 +75,11 @@ 1.74 #define JSON_STATE_ARRAY_SEPARATOR 6 1.75 #define JSON_STATE_END 7 1.76 1.77 +#define json_import_objectmt_idx 2 1.78 +#define json_import_arraymt_idx 3 1.79 +#define json_import_shadowtbl_idx 4 1.80 +#define json_import_nullmark_idx 5 1.81 + 1.82 // decodes a JSON document: 1.83 static int json_import(lua_State *L) { 1.84 const char *str; // string to parse 1.85 @@ -72,6 +91,16 @@ 1.86 luaL_Buffer luabuf; // Lua buffer to decode (possibly escaped) strings 1.87 char *cbuf; // C buffer to decode (possibly escaped) strings 1.88 size_t writepos; // write position of decoded strings in C buffer 1.89 + // limit stack to 1 element: 1.90 + lua_settop(L, 1); 1.91 + // push json_objectmt on stack position 2: 1.92 + json_regfetch(L, json_objectmt); 1.93 + // push json_arraymt on stack position 3: 1.94 + json_regfetch(L, json_arraymt); 1.95 + // push json_shadowtbl on stack position 4: 1.96 + json_regfetch(L, json_shadowtbl); 1.97 + // push json_nullmark on stack position 5: 1.98 + json_regfetch(L, json_nullmark); 1.99 // require string as first argument: 1.100 str = luaL_checklstring(L, 1, &total); 1.101 // if string contains a NULL byte, this is a syntax error 1.102 @@ -102,18 +131,14 @@ 1.103 // create JSON object on stack: 1.104 lua_newtable(L); 1.105 // set metatable of JSON object: 1.106 - lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.107 + lua_pushvalue(L, json_import_objectmt_idx); 1.108 lua_setmetatable(L, -2); 1.109 - // mark JSON object as JSON object (as opposed to JSON array): 1.110 - lua_pushvalue(L, -1); 1.111 - lua_pushliteral(L, "object"); 1.112 - lua_rawset(L, JSON_UPVAL_TYPES); 1.113 // create internal shadow table on stack: 1.114 lua_newtable(L); 1.115 - // register internal shadow table: 1.116 + // register internal shadow table (and cleanup stack afterwards): 1.117 lua_pushvalue(L, -2); 1.118 lua_pushvalue(L, -2); 1.119 - lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.120 + lua_rawset(L, json_import_shadowtbl_idx); 1.121 // increment level: 1.122 level++; 1.123 // expect object key (or end of object) and continue with loop: 1.124 @@ -129,18 +154,14 @@ 1.125 // create JSON array on stack: 1.126 lua_newtable(L); 1.127 // set metatable of JSON array: 1.128 - lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.129 + lua_pushvalue(L, json_import_arraymt_idx); 1.130 lua_setmetatable(L, -2); 1.131 - // mark JSON array as JSON array (as opposed to JSON object): 1.132 - lua_pushvalue(L, -1); 1.133 - lua_pushliteral(L, "array"); 1.134 - lua_rawset(L, JSON_UPVAL_TYPES); 1.135 // create internal shadow table on stack: 1.136 lua_newtable(L); 1.137 - // register internal shadow table: 1.138 + // register internal shadow table (and cleanup stack afterwards): 1.139 lua_pushvalue(L, -2); 1.140 lua_pushvalue(L, -2); 1.141 - lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.142 + lua_rawset(L, json_import_shadowtbl_idx); 1.143 // increment level: 1.144 level++; 1.145 // expect array value (or end of array) and continue with loop: 1.146 @@ -169,15 +190,15 @@ 1.147 if (--level) { 1.148 // if nested, then check if outer(!) structure is an array or object: 1.149 lua_pushvalue(L, c == '}' ? -4 : -3); 1.150 - lua_rawget(L, JSON_UPVAL_TYPES); 1.151 - if (lua_tostring(L, -1)[0] == 'a') { 1.152 + lua_getmetatable(L, -1); 1.153 + if (lua_touserdata(L, -1) == &json_arraymt) { 1.154 // select array value processing: 1.155 mode = JSON_STATE_ARRAY_VALUE; 1.156 } else { 1.157 // select object value processing: 1.158 mode = JSON_STATE_OBJECT_VALUE; 1.159 } 1.160 - // pop JSON type from stack (from rawget JSON_UPVAL_TYPES): 1.161 + // pop metatable from stack (that was needed for type distinction): 1.162 lua_pop(L, 1); 1.163 // store value in outer structure: 1.164 goto json_import_process_value; 1.165 @@ -306,7 +327,7 @@ 1.166 // consume 4 input characters for "null": 1.167 pos += 4; 1.168 // put special null-marker on stack: 1.169 - lua_pushvalue(L, JSON_UPVAL_NULLMARK); 1.170 + lua_pushvalue(L, json_import_nullmark_idx); 1.171 } else { 1.172 // all other cases are a syntax error: 1.173 goto json_import_syntax_error; 1.174 @@ -348,22 +369,32 @@ 1.175 return 2; 1.176 } 1.177 1.178 +#define json_path_shadowtbl_idx 1 1.179 +#define json_path_nullmark_idx 2 1.180 +#define json_path_idxshift 2 1.181 + 1.182 // gets a value or its type from a JSON document (first argument) 1.183 // optionally using a path (variable number of keys after first argument): 1.184 static int json_path(lua_State *L, int type_mode) { 1.185 - int argc; 1.186 - int idx = 2; 1.187 + int stacktop; 1.188 + int idx = 2 + json_path_idxshift; 1.189 + // insert json_shadowtbl on stack at position 1: 1.190 + json_regfetch(L, json_shadowtbl); 1.191 + lua_insert(L, 1); 1.192 + // insert json_nullmark on stack at position 2: 1.193 + json_regfetch(L, json_nullmark); 1.194 + lua_insert(L, 2); 1.195 // store number of arguments: 1.196 - argc = lua_gettop(L); 1.197 + stacktop = lua_gettop(L); 1.198 // follow path, starting with first argument as "current value": 1.199 - lua_pushvalue(L, 1); 1.200 + lua_pushvalue(L, 1 + json_path_idxshift); 1.201 // process each "path key": 1.202 - while (idx <= argc) { 1.203 + while (idx <= stacktop) { 1.204 // if "current value" is nil, then the path cannot be walked and nil is returned: 1.205 if (lua_isnil(L, -1)) return 1; 1.206 // try to get shadow table of "current value": 1.207 lua_pushvalue(L, -1); 1.208 - lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.209 + lua_rawget(L, json_path_shadowtbl_idx); 1.210 if (lua_isnil(L, -1)) { 1.211 // if no shadow table is found, 1.212 // drop nil from stack: 1.213 @@ -393,20 +424,33 @@ 1.214 if (!type_mode) { 1.215 // if a value (and not its type) was requested, 1.216 // check if value is the null-marker, and store nil on top of Lua stack in that case: 1.217 - if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) lua_pushnil(L); 1.218 + if (lua_rawequal(L, -1, json_path_nullmark_idx)) lua_pushnil(L); 1.219 } else { 1.220 // if the type was requested, 1.221 // check if value is the null-marker: 1.222 - if (lua_rawequal(L, -1, JSON_UPVAL_NULLMARK)) { 1.223 + if (lua_rawequal(L, -1, json_path_nullmark_idx)) { 1.224 // if yes, store string "null" on top of Lua stack: 1.225 lua_pushliteral(L, "null"); 1.226 } else { 1.227 // otherwise, 1.228 - // try to read type ("object" or "array") from internal type table: 1.229 - lua_pushvalue(L, -1); 1.230 - lua_rawget(L, JSON_UPVAL_TYPES); 1.231 - // if no entry is found in the internal type table, get the Lua type: 1.232 - if (lua_isnil(L, -1)) lua_pushstring(L, lua_typename(L, lua_type(L, -2))); 1.233 + // check if metatable indicates "object" or "array": 1.234 + if (lua_getmetatable(L, -1)) { 1.235 + json_regfetch(L, json_objectmt); 1.236 + if (lua_rawequal(L, -2, -1)) { 1.237 + // return string "object": 1.238 + lua_pushliteral(L, "object"); 1.239 + return 1; 1.240 + } 1.241 + json_regfetch(L, json_arraymt); 1.242 + if (lua_rawequal(L, -3, -1)) { 1.243 + // return string "array": 1.244 + lua_pushliteral(L, "array"); 1.245 + return 1; 1.246 + } 1.247 + lua_pop(L, 3); 1.248 + } 1.249 + // otherwise, get the Lua type: 1.250 + lua_pushstring(L, lua_typename(L, lua_type(L, -1))); 1.251 } 1.252 } 1.253 // return the top most value on the Lua stack: 1.254 @@ -428,7 +472,7 @@ 1.255 // checks if a value in a JSON document (first argument) is null: 1.256 static int json_isnull(lua_State *L) { 1.257 const char *jsontype; 1.258 - lua_getfield(L, JSON_UPVAL_LIBRARY, "type"); 1.259 + lua_pushcfunction(L, json_type); 1.260 lua_insert(L, 1); 1.261 lua_call(L, lua_gettop(L) - 1, 1); 1.262 jsontype = lua_tostring(L, -1); 1.263 @@ -437,33 +481,57 @@ 1.264 return 1; 1.265 } 1.266 1.267 +#define json_setnull_unknownmt_idx 3 1.268 +#define json_setnull_objectmt_idx 4 1.269 +#define json_setnull_arraymt_idx 5 1.270 +#define json_setnull_shadowtbl_idx 6 1.271 + 1.272 static int json_setnull(lua_State *L) { 1.273 + // truncate stack to two elements: 1.274 lua_settop(L, 2); 1.275 - lua_pushvalue(L, JSON_UPVAL_METATABLE); 1.276 - lua_setmetatable(L, 1); 1.277 + // push json_unknownmt to stack position 3: 1.278 + json_regfetch(L, json_unknownmt); 1.279 + // push json_objectmt to stack position 4: 1.280 + json_regfetch(L, json_objectmt); 1.281 + // push json_arraymt to stack position 5: 1.282 + json_regfetch(L, json_arraymt); 1.283 + // push json_shadowtbl to stack position 6: 1.284 + json_regfetch(L, json_shadowtbl); 1.285 + // 1.286 + lua_getmetatable(L, 1); 1.287 + if ( 1.288 + !lua_rawequal(L, -1, json_setnull_unknownmt_idx) && 1.289 + !lua_rawequal(L, -1, json_setnull_objectmt_idx) && 1.290 + !lua_rawequal(L, -1, json_setnull_arraymt_idx) 1.291 + ) { 1.292 + lua_pushvalue(L, json_setnull_unknownmt_idx); 1.293 + lua_setmetatable(L, 1); 1.294 + } 1.295 lua_pushvalue(L, 1); 1.296 - lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.297 + lua_rawget(L, json_setnull_shadowtbl_idx); 1.298 if (lua_isnil(L, -1)) { 1.299 lua_newtable(L); 1.300 lua_pushvalue(L, 1); 1.301 lua_pushvalue(L, -2); 1.302 - lua_rawset(L, JSON_UPVAL_SHADOWTBL); 1.303 + lua_rawset(L, json_setnull_shadowtbl_idx); 1.304 } 1.305 lua_pushvalue(L, 2); 1.306 - lua_pushvalue(L, JSON_UPVAL_NULLMARK); 1.307 + json_regfetch(L, json_nullmark); 1.308 lua_rawset(L, -3); 1.309 return 0; 1.310 } 1.311 1.312 static int json_len(lua_State *L) { 1.313 lua_settop(L, 1); 1.314 + json_regfetch(L, json_shadowtbl); 1.315 lua_pushvalue(L, 1); 1.316 - lua_rawget(L, JSON_UPVAL_SHADOWTBL); 1.317 - if (lua_isnil(L, -1)) lua_pop(L, 1); 1.318 - lua_pushinteger(L, lua_rawlen(L, -1)); 1.319 + lua_rawget(L, -2); 1.320 + lua_pushinteger(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1)); 1.321 return 1; 1.322 } 1.323 1.324 +/* 1.325 + 1.326 static int json_index(lua_State *L) { 1.327 lua_settop(L, 2); 1.328 lua_pushvalue(L, 1); 1.329 @@ -528,6 +596,8 @@ 1.330 return 3; 1.331 } 1.332 1.333 +*/ 1.334 + 1.335 static const struct luaL_Reg json_module_functions[] = { 1.336 {"object", json_object}, 1.337 {"array", json_array}, 1.338 @@ -541,50 +611,40 @@ 1.339 1.340 static const struct luaL_Reg json_metatable_functions[] = { 1.341 {"__len", json_len}, 1.342 + /* 1.343 {"__index", json_index}, 1.344 {"__newindex", json_newindex}, 1.345 {"__pairs", json_pairs}, 1.346 {"__ipairs", json_ipairs}, 1.347 + */ 1.348 {NULL, NULL} 1.349 }; 1.350 1.351 int luaopen_json(lua_State *L) { 1.352 lua_settop(L, 0); 1.353 - lua_newtable(L); // 1: library table on stack position 1.354 - lua_pushvalue(L, 1); // 2: copy of library table 1.355 - lua_newtable(L); // 3: table used as JSON NULL value in internal shadow tables 1.356 - lua_newtable(L); // 4: ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil 1.357 - lua_newtable(L); // 5: ephemeron table to store the type of the JSON object/array 1.358 - lua_newtable(L); // 6: metatable for ephemeron tables 1.359 + lua_newtable(L); // library 1.360 + lua_newtable(L); 1.361 + luaL_setfuncs(L, json_metatable_functions, 0); 1.362 + json_regstore(L, json_unknownmt); 1.363 + lua_setfield(L, 1, "ambiguous_mt"); 1.364 + lua_newtable(L); 1.365 + luaL_setfuncs(L, json_metatable_functions, 0); 1.366 + json_regstore(L, json_objectmt); 1.367 + lua_setfield(L, 1, "object_mt"); 1.368 + lua_newtable(L); 1.369 + luaL_setfuncs(L, json_metatable_functions, 0); 1.370 + json_regstore(L, json_arraymt); 1.371 + lua_setfield(L, 1, "array_mt"); 1.372 + lua_newtable(L); // ephemeron table to store shadow tables for each JSON object/array to allow NULL values returned as nil 1.373 + lua_newtable(L); // metatable for ephemeron table 1.374 lua_pushliteral(L, "__mode"); 1.375 lua_pushliteral(L, "k"); 1.376 - lua_rawset(L, 6); 1.377 - lua_pushvalue(L, 6); // 7: cloned metatable reference 1.378 - lua_setmetatable(L, 4); 1.379 - lua_setmetatable(L, 5); 1.380 - lua_newtable(L); // 6: metatable for JSON objects and JSON arrays 1.381 - lua_pushvalue(L, 2); 1.382 - lua_pushvalue(L, 3); 1.383 - lua_pushvalue(L, 4); 1.384 - lua_pushvalue(L, 5); 1.385 - lua_pushvalue(L, 6); 1.386 - lua_pushcclosure(L, json_pairs_iterfunc, 5); // 7: iteration function for pairs 1.387 - lua_pushvalue(L, 2); 1.388 - lua_pushvalue(L, 3); 1.389 - lua_pushvalue(L, 4); 1.390 - lua_pushvalue(L, 5); 1.391 - lua_pushvalue(L, 6); 1.392 - lua_pushcclosure(L, json_ipairs_iterfunc, 5); // 8: iteration function for ipairs 1.393 - lua_pushvalue(L, 6); 1.394 - lua_pushvalue(L, 2); 1.395 - lua_pushvalue(L, 3); 1.396 - lua_pushvalue(L, 4); 1.397 - lua_pushvalue(L, 5); 1.398 - lua_pushvalue(L, 6); 1.399 - lua_pushvalue(L, 7); 1.400 - lua_pushvalue(L, 8); 1.401 - luaL_setfuncs(L, json_metatable_functions, 7); 1.402 - lua_setfield(L, 1, "metatable"); 1.403 - luaL_setfuncs(L, json_module_functions, 7); 1.404 + lua_rawset(L, -3); 1.405 + lua_setmetatable(L, -2); 1.406 + json_regstore(L, json_shadowtbl); 1.407 + lua_newtable(L); 1.408 + json_regstore(L, json_nullmark); 1.409 + lua_settop(L, 1); 1.410 + luaL_setfuncs(L, json_module_functions, 0); 1.411 return 1; 1.412 }