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  }

Impressum / About Us