github.com/aergoio/aergo@v1.3.1/contract/state_module.c (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 #include <string.h> 7 #include <stdlib.h> 8 #include <stdint.h> 9 #include "vm.h" 10 #include "system_module.h" 11 12 #define STATE_MAP_ID "__state_map__" 13 #define STATE_ARRAY_ID "__state_array__" 14 #define STATE_VALUE_ID "__state_value__" 15 16 #define STATE_VAR_KEY_PREFIX "_sv_" 17 #define STATE_VAR_META_LEN "_sv_meta-len_" 18 #define STATE_VAR_META_TYPE "_sv_meta-type_" 19 20 #define STATE_MAX_DIMENSION 5 21 22 static int state_map_delete(lua_State *L); 23 static int state_array_append(lua_State *L); 24 static int state_array_pairs(lua_State *L); 25 26 /* map */ 27 28 typedef struct { 29 char *id; 30 int key_type; 31 int dimension; 32 char *key; 33 } state_map_t; 34 35 static int state_map(lua_State *L) 36 { 37 int argn = lua_gettop(L); 38 39 state_map_t *m = lua_newuserdata(L, sizeof(state_map_t)); /* m */ 40 m->id = NULL; 41 m->key_type = LUA_TNONE; 42 m->key = NULL; 43 if (luaL_isinteger(L, 1)) 44 m->dimension = luaL_checkint(L, 1); 45 else if (argn == 0) 46 m->dimension = 1; 47 else 48 luaL_typerror(L, 1, "integer"); 49 50 if (m->dimension > STATE_MAX_DIMENSION) { 51 luaL_error(L, "dimension over max limit(%d): %d, state.map", 52 STATE_MAX_DIMENSION, m->dimension); 53 } 54 luaL_getmetatable(L, STATE_MAP_ID); /* m mt */ 55 lua_setmetatable(L, -2); /* m */ 56 return 1; 57 } 58 59 static void state_map_check_index(lua_State *L, state_map_t *m) 60 { 61 /* m key */ 62 int key_type = lua_type(L, 2); 63 int stored_type = m->key_type; 64 65 if (key_type != LUA_TNUMBER && key_type != LUA_TSTRING) { 66 luaL_error(L, "invalid key type: " LUA_QS ", state.map: " LUA_QS, 67 lua_typename(L, key_type), m->id); 68 } 69 if (stored_type == LUA_TNONE) { 70 lua_pushcfunction(L, getItemWithPrefix); /* m key f */ 71 lua_pushstring(L, m->id); /* m key f id */ 72 lua_pushstring(L, STATE_VAR_META_TYPE); /* m key f id prefix */ 73 lua_call(L, 2, 1); /* m key t */ 74 if (!lua_isnil(L, -1)) { 75 stored_type = luaL_checkint(L, -1); 76 if (stored_type != LUA_TNUMBER && stored_type != LUA_TSTRING) { 77 luaL_error(L, "invalid stored key type: " LUA_QS ", state.map:", 78 lua_typename(L, stored_type), m->id); 79 } 80 } 81 // need hardfork 82 // m->key_type = stored_type; 83 lua_pop(L, 1); 84 } 85 if (stored_type != LUA_TNONE && key_type != stored_type) { 86 luaL_typerror(L, 2, lua_typename(L, stored_type)); 87 } 88 } 89 90 static void state_map_push_key(lua_State *L, state_map_t *m) 91 { 92 lua_pushstring(L, m->id); /* m key value f id */ 93 lua_pushstring(L, "-"); 94 lua_pushvalue(L, 2); /* m key value f id '-' key */ 95 lua_concat(L, 3); /* m key value f id-key */ 96 } 97 98 static int state_map_get(lua_State *L) 99 { 100 int key_type = LUA_TNONE; 101 int arg = lua_gettop(L); 102 state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID); /* m key */ 103 104 key_type = lua_type(L, 2); 105 if (key_type == LUA_TSTRING) { 106 const char *method = lua_tostring(L, 2); 107 if (method != NULL && strcmp(method, "delete") == 0) { 108 lua_pushcfunction(L, state_map_delete); 109 return 1; 110 } 111 } 112 113 state_map_check_index(L, m); 114 115 if (m->dimension > 1) { 116 state_map_t *subm = lua_newuserdata(L, sizeof(state_map_t)); /* m */ 117 subm->id = strdup(m->id); 118 subm->key_type = m->key_type; 119 subm->dimension = m->dimension - 1; 120 121 luaL_getmetatable(L, STATE_MAP_ID); /* m mt */ 122 lua_setmetatable(L, -2); /* m */ 123 if (m->key == NULL) { 124 subm->key = strdup(lua_tostring(L, 2)); 125 } 126 else { 127 lua_pushstring(L, m->key); /* a key value f id '-' */ 128 lua_pushstring(L, "-"); /* a key value f id '-' */ 129 lua_pushvalue(L, 2); /* m key f id-key prefix */ 130 lua_concat(L, 3); /* m key value f id-key */ 131 subm->key = strdup(lua_tostring(L, -1)); 132 lua_pop(L, 1); 133 } 134 return 1; 135 } 136 137 lua_pushcfunction(L, getItemWithPrefix); /* m key f */ 138 if (m->key != NULL) { 139 lua_pushstring(L, m->key); 140 lua_pushstring(L, "-"); 141 lua_pushvalue(L, 2); 142 lua_concat(L, 3); 143 lua_replace(L, 2); 144 } 145 state_map_push_key(L, m); /* m key f id-key */ 146 if (arg == 3) { 147 lua_pushvalue(L, 3); 148 } 149 150 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* m key value f id-key value prefix */ 151 lua_call(L, arg, 1); /* m key rv */ 152 return 1; 153 } 154 155 static int state_map_set(lua_State *L) 156 { 157 /* m key value */ 158 int key_type = LUA_TNONE; 159 state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID); 160 161 key_type = lua_type(L, 2); 162 163 if (m->dimension > 1) { 164 luaL_error(L, "not permitted to set intermediate dimension of map"); 165 } 166 if (key_type == LUA_TSTRING) { 167 const char *method = lua_tostring(L, 2); 168 if (method != NULL && strcmp(method, "delete") == 0) { 169 luaL_error(L, "can't use " LUA_QL("delete") " as a key"); 170 } 171 } 172 state_map_check_index(L, m); 173 174 if (m->key_type == LUA_TNONE) { 175 lua_pushcfunction(L, setItemWithPrefix); /* m key f */ 176 lua_pushstring(L, m->id); /* m key f id */ 177 lua_pushinteger(L, key_type); /* m key f id type */ 178 lua_pushstring(L, STATE_VAR_META_TYPE); /* m key f id type prefix */ 179 lua_call(L, 3, 0); /* m key */ 180 m->key_type = key_type; 181 } 182 luaL_checkany(L, 3); 183 lua_pushcfunction(L, setItemWithPrefix); /* m key value f */ 184 185 if (m->key != NULL) { 186 lua_pushstring(L, m->key); 187 lua_pushstring(L, "-"); 188 lua_pushvalue(L, 2); 189 lua_concat(L, 3); 190 lua_replace(L, 2); 191 } 192 193 state_map_push_key(L, m); /* m key value f id-key */ 194 lua_pushvalue(L, 3); /* m key value f id-key value */ 195 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* m key value f id-key value prefix */ 196 lua_call(L, 3, 0); /* t key value */ 197 return 0; 198 } 199 200 static int state_map_delete(lua_State *L) 201 { 202 /* m key */ 203 state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID); 204 205 if (m->dimension > 1) { 206 luaL_error(L, "not permitted to set intermediate dimension of map"); 207 } 208 state_map_check_index(L, m); 209 lua_pushcfunction(L, delItemWithPrefix); /* m key f */ 210 211 if (m->key != NULL) { 212 lua_pushstring(L, m->key); 213 lua_pushstring(L, "-"); 214 lua_pushvalue(L, 2); 215 lua_concat(L, 3); 216 lua_replace(L, 2); 217 } 218 state_map_push_key(L, m); /* m key f id-key */ 219 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* m key f id-key prefix */ 220 lua_call(L, 2, 1); /* m key rv */ 221 return 0; 222 } 223 224 static int state_map_gc(lua_State *L) 225 { 226 state_map_t *m = luaL_checkudata(L, 1, STATE_MAP_ID); 227 if (m->id) { 228 free(m->id); 229 m->id = NULL; 230 } 231 if (m->key) { 232 free(m->key); 233 m->key = NULL; 234 } 235 return 0; 236 } 237 238 /* array */ 239 240 typedef struct { 241 char *id; 242 int is_fixed; 243 int dimension; 244 int32_t *lens; 245 char *key; 246 } state_array_t; 247 248 static int state_array(lua_State *L) 249 { 250 int is_fixed; 251 state_array_t *arr; 252 int dimension = lua_gettop(L); 253 int32_t *lens = NULL; 254 255 is_fixed = dimension != 0; 256 257 if (dimension > STATE_MAX_DIMENSION) { 258 luaL_error(L, "dimension over max limit(%d): %d, state.array", 259 STATE_MAX_DIMENSION, dimension); 260 } 261 if (is_fixed) { 262 int i; 263 lens = malloc(sizeof(int32_t) * dimension); 264 for (i = 1; i <= dimension; i++) { 265 if (!luaL_isinteger(L, i)) { 266 luaL_typerror(L, i, "integer"); 267 } 268 lens[i -1] = luaL_checkint(L, i); /* size */ 269 luaL_argcheck(L, (lens[i - 1] > 0), i, "the array length must be greater than zero"); 270 } 271 } 272 arr = lua_newuserdata(L, sizeof(state_array_t)); /* size a */ 273 luaL_getmetatable(L, STATE_ARRAY_ID); /* size a mt */ 274 lua_setmetatable(L, -2); /* size a */ 275 arr->lens = lens; 276 arr->id = NULL; 277 arr->dimension = dimension; 278 arr->is_fixed = is_fixed; 279 arr->key = NULL; 280 return 1; 281 } 282 283 static int state_array_len(lua_State *L) 284 { 285 state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 286 if (arr->lens == NULL) 287 lua_pushinteger(L, 0); 288 else 289 lua_pushinteger(L, arr->lens[0]); 290 return 1; 291 } 292 293 static void state_array_load_len(lua_State *L, state_array_t *arr) 294 { 295 if (!arr->is_fixed && arr->lens == NULL) { 296 lua_pushcfunction(L, getItemWithPrefix); /* a i f */ 297 lua_pushstring(L, arr->id); /* a i f id */ 298 lua_pushstring(L, STATE_VAR_META_LEN); /* a i f id prefix */ 299 lua_call(L, 2, 1); /* a i n */ 300 arr->lens = malloc(sizeof(int32_t)); 301 arr->lens[0] = luaL_optinteger(L, -1, 0); 302 lua_pop(L, 1); 303 } 304 } 305 306 static void state_array_checkarg(lua_State *L, state_array_t *arr) 307 { 308 int idx; 309 if (!luaL_isinteger(L, 2)) { 310 luaL_typerror(L, 2, "integer"); 311 } 312 idx = luaL_checkint(L, 2); 313 luaL_argcheck(L, idx >= 1 && idx <= arr->lens[0], 2, "index out of range"); 314 } 315 316 static void state_array_push_key(lua_State *L, const char *id) 317 { 318 lua_pushstring(L, id); /* a key value f id */ 319 lua_pushstring(L, "-"); /* a key value f id '-' */ 320 lua_pushvalue(L, 2); /* m key value f id '-' key */ 321 lua_concat(L, 3); /* m key value f id-key */ 322 } 323 324 static int state_array_get(lua_State *L) 325 { 326 state_array_t *arr; 327 int arg = lua_gettop(L); 328 int key_type = LUA_TNONE; 329 330 arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 331 state_array_load_len(L, arr); 332 333 if (lua_type(L, 2) == LUA_TSTRING) { /* methods */ 334 const char *method = lua_tostring(L, 2); 335 if (strcmp(method, "append") == 0) { 336 lua_pushcfunction(L, state_array_append); 337 return 1; 338 } else if (strcmp(method, "ipairs") == 0) { 339 lua_pushcfunction(L, state_array_pairs); 340 return 1; 341 } else if (strcmp(method, "length") == 0) { 342 lua_pushcfunction(L, state_array_len); 343 return 1; 344 } 345 luaL_typerror(L, 2, "integer"); 346 } 347 if (arr->dimension > 1) { 348 state_array_t *suba = lua_newuserdata(L, sizeof(state_array_t)); /* m */ 349 suba->id = strdup(arr->id); 350 suba->dimension = arr->dimension - 1; 351 suba->lens = malloc(sizeof(int32_t) * suba->dimension); 352 suba->is_fixed = arr->is_fixed; 353 memcpy(suba->lens, arr->lens + 1, sizeof(int32_t) * suba->dimension); 354 355 luaL_getmetatable(L, STATE_ARRAY_ID); /* m mt */ 356 lua_setmetatable(L, -2); /* m */ 357 if (arr->key == NULL) { 358 suba->key = strdup(lua_tostring(L, 2)); 359 } 360 else { 361 lua_pushstring(L, arr->key); /* a key value f id '-' */ 362 lua_pushstring(L, "-"); /* a key value f id '-' */ 363 lua_pushvalue(L, 2); /* m key f id-key prefix */ 364 lua_concat(L, 3); /* m key value f id-key */ 365 suba->key = strdup(lua_tostring(L, -1)); 366 lua_pop(L, 1); 367 } 368 return 1; 369 } 370 if (arg == 3) { 371 lua_pushvalue(L, 2); 372 } 373 state_array_checkarg(L, arr); /* a i */ 374 lua_pushcfunction(L, getItemWithPrefix); /* a i f */ 375 376 if (arr->key != NULL) { 377 lua_pushstring(L, arr->key); 378 lua_pushstring(L, "-"); 379 lua_pushvalue(L, 2); 380 lua_concat(L, 3); 381 lua_replace(L, 2); 382 } 383 state_array_push_key(L, arr->id); /* a i f id-i */ 384 if (arg == 3) { 385 lua_pushvalue(L, 3); /* a i s i f id-i s */ 386 } 387 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* a i f id-i prefix */ 388 lua_call(L, arg, 1); /* a i rv */ 389 return 1; 390 } 391 392 static int state_array_set(lua_State *L) 393 { 394 state_array_t *arr; 395 396 arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 397 398 if (arr->dimension > 1) { 399 luaL_error(L, "not permitted to set intermediate dimension of array"); 400 } 401 state_array_load_len(L, arr); 402 403 state_array_checkarg(L, arr); /* a i v */ 404 if (arr->key != NULL) { 405 lua_pushstring(L, arr->key); 406 lua_pushstring(L, "-"); 407 lua_pushvalue(L, 2); 408 lua_concat(L, 3); 409 lua_replace(L, 2); 410 } 411 lua_pushcfunction(L, setItemWithPrefix); /* a i v f */ 412 state_array_push_key(L, arr->id); /* a i v f id-i */ 413 lua_pushvalue(L, 3); /* a i v f id-i v */ 414 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* a i v f id-i v prefix */ 415 lua_call(L, 3, 0); /* a i v */ 416 return 0; 417 } 418 419 static int state_array_append(lua_State *L) 420 { 421 state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 422 luaL_checkany(L, 2); 423 if (arr->is_fixed) { 424 luaL_error(L, "the fixed array cannot use " LUA_QL("append") " method"); 425 } 426 if (arr->lens[0] + 1 <= 0) { 427 luaL_error(L, "state.array " LUA_QS " overflow", arr->id); 428 } 429 arr->lens[0]++; 430 lua_pushcfunction(L, state_array_set); /* a v f */ 431 lua_pushvalue(L, 1); /* a v f a */ 432 lua_pushinteger(L, arr->lens[0]); /* a v f a i */ 433 lua_pushvalue(L, 2); /* a v f a i v */ 434 lua_call(L, 3, 0); 435 lua_pushcfunction(L, setItemWithPrefix); 436 lua_pushstring(L, arr->id); 437 lua_pushinteger(L, arr->lens[0]); 438 lua_pushstring(L, STATE_VAR_META_LEN); 439 lua_call(L, 3, 0); 440 return 0; 441 } 442 443 static int state_array_gc(lua_State *L) 444 { 445 state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 446 if (arr->id) { 447 free(arr->id); 448 arr->id = NULL; 449 } 450 if (arr->lens) { 451 free(arr->lens); 452 arr->lens = NULL; 453 } 454 if (arr->key) { 455 free(arr->key); 456 arr->key = NULL; 457 } 458 return 0; 459 } 460 461 static int state_array_iter(lua_State *L) 462 { 463 state_array_t *arr = luaL_checkudata(L, 1, STATE_ARRAY_ID); 464 int i = luaL_checkint(L, 2); 465 i = i + 1; 466 if (i <= arr->lens[0]) { 467 lua_pushinteger(L, i); 468 lua_pushcfunction(L, state_array_get); 469 lua_pushvalue(L, 1); 470 lua_pushinteger(L, i); 471 lua_call(L, 2, 1); 472 return 2; 473 } 474 return 0; 475 } 476 477 static int state_array_pairs(lua_State *L) 478 { 479 luaL_checkudata(L, 1, STATE_ARRAY_ID); 480 lua_pushcfunction(L, state_array_iter); 481 lua_pushvalue(L, 1); 482 lua_pushinteger(L, 0); 483 return 3; 484 } 485 486 /* scalar value */ 487 488 typedef struct { 489 char *id; 490 } state_value_t; 491 492 static int state_value(lua_State *L) 493 { 494 state_value_t *v = lua_newuserdata(L, sizeof(state_value_t)); /* v */ 495 v->id = NULL; 496 luaL_getmetatable(L, STATE_VALUE_ID); /* v mt */ 497 lua_setmetatable(L, -2); /* v */ 498 return 1; 499 } 500 501 static int state_value_get(lua_State *L) 502 { 503 state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID); 504 lua_pushcfunction(L, getItemWithPrefix); /* v f */ 505 lua_pushstring(L, v->id); /* v f id */ 506 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* v f id prefix */ 507 lua_call(L, 2, 1); /* v rv */ 508 return 1; 509 } 510 511 static int state_value_snapget(lua_State *L) 512 { 513 int arg = lua_gettop(L); 514 state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID); /* v */ 515 lua_pushcfunction(L, getItemWithPrefix); /* v f */ 516 lua_pushstring(L, v->id); /* v f id */ 517 if (arg == 2) { 518 lua_pushvalue(L, 2); 519 } 520 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* v f id prefix */ 521 lua_call(L, arg + 1, 1); /* v rv */ 522 return 1; 523 } 524 525 static int state_value_set(lua_State *L) 526 { 527 state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID); /* v */ 528 luaL_checkany(L, 2); 529 lua_pushcfunction(L, setItemWithPrefix); /* t f */ 530 if (v->id == NULL) { 531 luaL_error(L, "invalid state.value: (nil)"); 532 } 533 lua_pushstring(L, v->id); /* v f id */ 534 lua_pushvalue(L, 2); /* t f id value */ 535 lua_pushstring(L, STATE_VAR_KEY_PREFIX); /* v f id value prefix */ 536 lua_call(L, 3, 0); /* v */ 537 return 0; 538 } 539 540 static int state_value_gc(lua_State *L) 541 { 542 state_value_t *v = luaL_checkudata(L, 1, STATE_VALUE_ID); 543 if (v->id) { 544 free(v->id); 545 v->id = NULL; 546 } 547 return 0; 548 } 549 550 /* global variable */ 551 552 static void insert_var(lua_State *L, const char *var_name) 553 { 554 lua_getglobal(L, "abi"); /* "type_name" m */ 555 lua_getfield(L, -1, "register_var"); /* "type_name" m f */ 556 lua_pushstring(L, var_name); /* "type_name" m f var_name */ 557 lua_pushvalue(L, -4); /* "type_name" m f var_name "type_name" */ 558 lua_call(L, 2, 0); /* "type_name" m */ 559 lua_pop(L, 2); 560 } 561 562 static int state_var(lua_State *L) 563 { 564 const char *var_name; 565 state_map_t *m = NULL; 566 state_array_t *arr = NULL; 567 state_value_t *v = NULL; 568 569 luaL_checktype(L, 1, LUA_TTABLE); /* T */ 570 lua_pushnil(L); /* T nil ; push the first key */ 571 while (lua_next(L, -2) != 0) { /* T key value */ 572 var_name = luaL_checkstring(L, -2); 573 if (!lua_isuserdata(L, -1)) { 574 lua_pushfstring(L, "bad argument " LUA_QS ": state.value, state.map or state.array expected, got %s", 575 var_name, lua_typename(L, lua_type(L, -1))); 576 lua_error(L); 577 } 578 579 m = luaL_testudata(L, -1, STATE_MAP_ID); 580 if (m != NULL) { 581 m->id = strdup(var_name); 582 goto found; 583 } 584 585 arr = luaL_testudata(L, -1, STATE_ARRAY_ID); 586 if (arr != NULL) { 587 arr->id = strdup(var_name); /* T key value */ 588 goto found; 589 } 590 591 v = luaL_testudata(L, -1, STATE_VALUE_ID); 592 if (v != NULL) { 593 v->id = strdup(var_name); 594 } else { 595 lua_pushfstring(L, "bad argument " LUA_QS ": state.value, state.map or state.array expected", var_name); 596 lua_error(L); 597 } 598 599 found: 600 lua_newtable(L); 601 insert_var(L, var_name); 602 lua_setglobal(L, var_name); /* T key */ 603 } 604 return 0; 605 } 606 607 static int state_get_snap(lua_State *L) 608 { 609 state_map_t *m = NULL; 610 state_array_t *arr = NULL; 611 state_value_t *v = NULL; 612 613 if (!lua_isuserdata(L, 1)) { 614 luaL_typerror(L, 1, "state.value, state.map or state.array"); 615 } 616 617 m = luaL_testudata(L, 1, STATE_MAP_ID); 618 if (m != NULL) { 619 if (lua_gettop(L) != 3) 620 luaL_error(L, "invalid argument at getsnap, need (state.map, key, blockheight)"); 621 return state_map_get(L); 622 } 623 624 arr = luaL_testudata(L, 1, STATE_ARRAY_ID); 625 if (arr != NULL) { 626 if (lua_gettop(L) != 3) 627 luaL_error(L, "invalid argument at getsnap, need (state.array, index, blockheight)"); 628 return state_array_get(L); 629 } 630 631 v = luaL_testudata(L, 1, STATE_VALUE_ID); 632 if (v != NULL) { 633 if (lua_gettop(L) != 2) 634 luaL_error(L, "invalid argument at getsnap, need (state.value, blockheight)"); 635 return state_value_snapget(L); 636 } 637 638 luaL_typerror(L, 1, "state.value, state.map or state.array"); 639 return 0; 640 } 641 642 int luaopen_state(lua_State *L) 643 { 644 static const luaL_Reg state_map_metas[] = { 645 {"__index", state_map_get}, 646 {"__newindex", state_map_set}, 647 {"__gc", state_map_gc}, 648 {NULL, NULL} 649 }; 650 651 static const luaL_Reg state_array_metas[] = { 652 {"__index", state_array_get}, 653 {"__newindex", state_array_set}, 654 {"__len", state_array_len}, 655 {"__gc", state_array_gc}, 656 {NULL, NULL} 657 }; 658 659 static const luaL_Reg state_value_methods[] = { 660 {"get", state_value_get}, 661 {"set", state_value_set}, 662 {NULL, NULL} 663 }; 664 665 static const luaL_Reg state_lib[] = { 666 {"map", state_map}, 667 {"array", state_array}, 668 {"value", state_value}, 669 {"var", state_var}, 670 {"getsnap", state_get_snap}, 671 {NULL, NULL} 672 }; 673 674 luaL_newmetatable(L, STATE_MAP_ID); 675 luaL_register(L, NULL, state_map_metas); 676 677 luaL_newmetatable(L, STATE_ARRAY_ID); 678 luaL_register(L, NULL, state_array_metas); 679 680 luaL_newmetatable(L, STATE_VALUE_ID); 681 lua_pushvalue(L, -1); 682 lua_setfield(L, -2, "__index"); 683 luaL_register(L, NULL, state_value_methods); 684 lua_pushcfunction(L, state_value_gc); 685 lua_setfield(L, -2, "__gc"); 686 687 luaL_register(L, "state", state_lib); 688 689 return 1; 690 }