code / lua-immutable

Lines1.0K C473 Lua341 Markdown173 make28
(652 lines)
1 /*
2 * immutable.c
3 * An immutable table library by Bruce Hill. This library returns a single function
4 * that can be used to declare immutable classes, like so:
5 *
6 * immutable = require 'immutable'
7 * local Foo = immutable({"baz","qux"}, {class_var=5})
8 * local foo = Foo("hello", 99)
9 * assert(foo.baz == "hello")
10 * assert(not pcall(function() foo.x = 'mutable' end))
11 * local t = {[foo]="it works"}
12 * assert(t[Foo("hello", 99)] == "it works")
13 * assert(foo.class_var == 5)
15 * Instances *are* garbage collected.
17 * Class layout:
18 * __instances: weak-valued map from hash -> hash bucket
19 * hash buckets: weak-keyed map from instance userdata -> table of values
20 * __buckets: weak-keyed map from instance userdata -> hash bucket (used to manage hash bucket lifetimes)
21 * __fields: list of named fields
22 * metamethods, methods, class variables, etc.
23 */
25 #include "lua.h"
26 #include "lauxlib.h"
28 // The C API changed from 5.1 to 5.2, so these shims help the code compile on >=5.2
29 #if LUA_VERSION_NUM >= 502
30 #define lua_objlen(L, i) lua_rawlen(L, i)
31 #define luaL_register(L, _, R) luaL_setfuncs(L, R, 0)
32 #endif
33 // Lua 5.3 introduced lua_isinteger, fall back to lua_isnumber
34 #if LUA_VERSION_NUM < 503
35 #define lua_isinteger(L, i) lua_isnumber(L, i)
36 #endif
38 #if !defined(LUAI_HASHLIMIT)
39 #define LUAI_HASHLIMIT 5
40 #endif
42 // This is used to create a unique light userdata to store in the registry to allow all
43 // hash buckets to share the same {__mode='k'} metatable
44 static int WEAK_KEY_METATABLE;
45 // This is used to create a unique light userdata to store in the registry to allow all
46 // __instances tables to share the same {__mode='v'} metatable
47 static int WEAK_VALUE_METATABLE;
48 // This is used to create a unique light userdata to store in the registry to allow all
49 // constructed classes to have the same metatable:
50 // {__new=Lcreate_instance, __tostring=function(cls) return cls.name or 'immutable(...)' end}
51 static int SHARED_CLASS_METATABLE;
52 static int SUPER_METHODS, NIL_SENTINEL;
54 typedef struct {
55 int hash;
56 size_t array_len;
57 } immutable_info_t;
59 #define IMMUTABLE_GETVALUE(i) if (from_table) {\
60 if (i <= num_fields) {\
61 lua_rawgeti(L, fields_index, i);\
62 } else {\
63 lua_pushinteger(L, i-num_fields);\
64 }\
65 lua_gettable(L, 2);\
66 } else {\
67 if (i <= num_args) {\
68 lua_pushvalue(L, 1+i);\
69 } else {\
70 lua_pushlightuserdata(L, &NIL_SENTINEL);\
71 }\
72 }\
73 if (lua_isnil(L, -1)) {\
74 lua_pop(L, 1);\
75 lua_pushlightuserdata(L, &NIL_SENTINEL);\
78 static inline int _create_instance(lua_State *L, int from_table)
80 size_t num_args = lua_gettop(L)-1;
81 lua_getfield(L, 1, "__fields");
82 int fields_index = lua_gettop(L);
83 size_t num_fields = lua_objlen(L, -1);
84 size_t num_values = from_table ? (num_fields + lua_objlen(L, 2)) : (num_args >= num_fields ? num_args : num_fields);
85 size_t array_len = num_values - num_fields;
87 // Compute the hash and populate the values table:
88 // Stack: [fields]
89 unsigned long long ull_hash = 0x9a937c4d; // Seed
90 for (size_t i=1; i <= num_values; i++) {
91 unsigned long long item_hash;
92 IMMUTABLE_GETVALUE(i);
93 // Stack: [fields, value[i]]
94 int type = lua_type(L, -1);
95 switch (type) {
96 case LUA_TNIL: case LUA_TNONE:
97 // Arbitrarily chosen value
98 item_hash = 0x97167da9;
99 break;
100 case LUA_TNUMBER:
102 // Cast float bits to integer
103 lua_Number num = lua_tonumber(L, -1);
104 item_hash = *((lua_Integer*)&num);
105 if (item_hash == 0) {
106 item_hash = 0x2887c992;
108 break;
110 case LUA_TBOOLEAN:
111 // Arbitrarily chosen values
112 item_hash = lua_toboolean(L, -1)? 0x82684f71 : 0x88d66f2a;
113 break;
114 case LUA_TLIGHTUSERDATA:
115 if (lua_touserdata(L, -1) == &NIL_SENTINEL) {
116 item_hash = (1000003 * type) ^ (lua_Integer)lua_topointer(L, -1);
117 break;
119 // fallthrough
120 case LUA_TTABLE:
121 case LUA_TFUNCTION:
122 case LUA_TUSERDATA:
123 case LUA_TTHREAD:
124 item_hash = (1000003 * type) ^ (lua_Integer)lua_topointer(L, -1);
125 break;
126 case LUA_TSTRING:
128 // Algorithm taken from Lua 5.3's implementation
129 size_t len;
130 const char *str = lua_tolstring(L, -1, &len);
131 item_hash = len ^ 0xd2e9e9ac; // Arbitrary seed
132 size_t step = (len >> LUAI_HASHLIMIT) + 1;
133 for (; len >= step; len -= step)
134 item_hash ^= ((item_hash<<5) + (item_hash>>2) + (unsigned char)(str[len - 1]));
135 break;
137 default:
138 item_hash = 0;
140 ull_hash = (1000003 * ull_hash) ^ item_hash;
141 lua_pop(L, 1);
142 // Stack: [fields]
144 int hash = (int)ull_hash;
146 lua_getfield(L, 1, "__instances");
147 // Stack: [fields, __instances]
148 // Find bucket
149 lua_rawgeti(L, -1, hash);
150 // Stack: [fields, __instances, bucket]
151 if (lua_isnil(L, -1)) {
152 // Make a new bucket
153 // Stack: [fields, __instances, nil]
154 lua_pop(L, 1);
155 // Stack: [fields, __instances]
156 lua_createtable(L, 0, 1);
157 // Stack: [fields, __instances, bucket]
158 lua_pushlightuserdata(L, (void*)&WEAK_KEY_METATABLE);
159 lua_gettable(L, LUA_REGISTRYINDEX);
160 // Stack: [fields, __instances, bucket, {__mode='k'}]
161 lua_setmetatable(L, -2);
162 // Stack: [fields, __instances, bucket]
163 lua_pushvalue(L, -1);
164 // Stack: [fields, __instances, bucket, bucket]
165 lua_rawseti(L, -3, hash);
166 // Stack: [fields, __instances, bucket]
167 } else {
168 // Stack: [fields, __instances, bucket]
169 // scan bucket
170 lua_pushnil(L);
171 while (lua_next(L, -2) != 0) { // for hash_collider_inst, collider_table in pairs(bucket) do
172 // Stack: [fields, __instances, bucket, hash_collider_inst, collider_table]
173 // Shallow equality check:
174 immutable_info_t *collider_info = (immutable_info_t*)lua_touserdata(L, -2);
175 if (collider_info->array_len != array_len) {
176 lua_pop(L, 1);
177 goto next_bucket_item;
179 // Stack: [fields, __instances, bucket, hash_collider_inst, collider_table]
180 for (size_t i = 1; i <= num_fields; i++) {
181 lua_rawgeti(L, fields_index, i);
182 lua_gettable(L, -2);
183 IMMUTABLE_GETVALUE(i);
184 // Stack: [fields, __instances, bucket, hash_collider_inst, collider_table, collider_val, inst_val]
185 if (! lua_rawequal(L, -1, -2)) { // If the i'th entry doesn't match the i'th arg
186 lua_pop(L, 3);
187 goto next_bucket_item;
188 } else {
189 lua_pop(L, 2);
192 for (size_t i = 1; i <= array_len; i++) {
193 lua_rawgeti(L, -1, i);
194 IMMUTABLE_GETVALUE(i+num_fields);
195 // Stack: [fields, __instances, bucket, hash_collider_inst, collider_table, collider_val, inst_val]
196 if (! lua_rawequal(L, -1, -2)) { // If the i'th entry doesn't match the i'th arg
197 lua_pop(L, 3);
198 goto next_bucket_item;
199 } else {
200 lua_pop(L, 2);
203 // bucket item matches
204 // Stack: [fields, __instances, bucket, hash_collider_inst, collider_table]
205 lua_pop(L, 1);
206 return 1;
208 next_bucket_item: ;
211 // Stack: [fields, __instances, bucket]
212 int bucket_index = lua_gettop(L);
214 // Failed to find an existing instance, so create a new one
215 immutable_info_t *userdata = (immutable_info_t*)lua_newuserdata(L, sizeof(immutable_info_t));
216 // Stack: [fields, __instances, bucket, inst_userdata]
217 int userdata_index = lua_gettop(L);
218 userdata->hash = hash;
219 userdata->array_len = array_len;
221 lua_pushvalue(L, 1);
222 // Stack: [fields, __instances, bucket, inst_userdata, cls]
223 lua_setmetatable(L, -2);
224 // Stack: [fields, __instances, bucket, inst_userdata]
226 // Set up a ref to the bucket so its lifetime is tied to inst_userdata
227 lua_getfield(L, 1, "__buckets");
228 // Stack: [fields, __instances, bucket, inst_userdata, __buckets]
229 lua_pushvalue(L, userdata_index);
230 // Stack: [fields, __instances, bucket, inst_userdata, __buckets, inst_userdata]
231 lua_pushvalue(L, bucket_index);
232 // Stack: [fields, __instances, bucket, inst_userdata, __buckets, inst_userdata, bucket]
233 lua_settable(L, -3);
234 lua_pop(L, 1);
235 // Stack: [fields, __instances, bucket, inst_userdata]
237 lua_pushvalue(L, userdata_index);
238 // Stack: [fields, __instances, bucket, inst_userdata, inst_userdata]
239 lua_createtable(L, num_values-num_fields, num_fields);
240 for (size_t i=1; i <= num_fields; i++) {
241 lua_rawgeti(L, fields_index, i);
242 IMMUTABLE_GETVALUE(i);
243 lua_settable(L, -3);
245 for (size_t i=1; i <= array_len; i++) {
246 IMMUTABLE_GETVALUE(i+num_fields);
247 lua_rawseti(L, -2, i);
249 // Stack: [fields, __instances, bucket, inst_userdata, inst_userdata, inst_table]
250 lua_settable(L, -4); // buckets[inst_userdata] = inst_table
251 // Stack: [fields, __instances, bucket, inst_userdata]
252 lua_gc(L, LUA_GCSTEP, 3);
253 return 1;
256 static int Lcreate_instance(lua_State *L)
258 int num_args = lua_gettop(L)-1;
259 lua_getfield(L, 1, "__new");
260 if (! lua_isnil(L, -1)) {
261 lua_insert(L, 2); // move __new
262 lua_pushvalue(L, 1);
263 lua_insert(L, 3); // make cls the first argument to __new
264 lua_call(L, num_args+1, LUA_MULTRET);
265 } else {
266 lua_pop(L, 1);
268 return _create_instance(L, 0);
271 static int Lfrom_table(lua_State *L)
273 return _create_instance(L, 1);
276 static int Lis_instance(lua_State *L)
278 if (lua_type(L, 2) != LUA_TUSERDATA) {
279 lua_pushboolean(L, 0);
280 return 1;
282 lua_getmetatable(L, 2);
283 lua_pushboolean(L, lua_rawequal(L, -1, 1));
284 return 1;
287 static int Llen(lua_State *L)
289 luaL_checktype(L, 1, LUA_TUSERDATA);
290 if (! lua_getmetatable(L, 1)) {
291 luaL_error(L, "invalid type");
293 immutable_info_t *info = (immutable_info_t *)lua_touserdata(L, 1);
294 lua_pushinteger(L, info->array_len);
295 return 1;
298 static int Lindex(lua_State *L)
300 // Return inst[key], <whether inst has key>
301 luaL_checktype(L, 1, LUA_TUSERDATA);
302 if (! lua_getmetatable(L, 1)) {
303 luaL_error(L, "invalid type");
305 int class_index = lua_gettop(L);
306 // Stack: [mt]
307 immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
308 if (! info) {
309 luaL_error(L, "invalid type");
312 // Stack: [mt]
313 lua_getfield(L, -1, "__instances");
314 // Stack: [mt, buckets]
315 lua_rawgeti(L, -1, info->hash);
316 // Stack: [mt, buckets, bucket]
317 if (lua_isnil(L, -1)) {
318 luaL_error(L, "Failed to find hash bucket");
320 lua_pushvalue(L, 1);
321 // Stack: [mt, buckets, bucket, inst_udata]
322 lua_rawget(L, -2);
323 // Stack: [mt, buckets, bucket, inst_table]
324 if (lua_isnil(L, -1)) {
325 luaL_error(L, "Failed to find instance");
327 lua_pushvalue(L, 2);
328 lua_gettable(L, -2);
329 if (lua_isnil(L, -1)) {
330 // Fall back to class:
331 lua_pushvalue(L, 2);
332 lua_gettable(L, class_index);
333 return 1;
335 if (lua_islightuserdata(L, -1) && lua_touserdata(L, -1) == &NIL_SENTINEL) {
336 lua_pop(L, 1);
337 lua_pushnil(L);
339 lua_pushboolean(L, 1);
340 return 2;
343 static int Ltostring(lua_State *L)
345 luaL_Buffer b;
346 luaL_buffinit(L, &b);
347 if (! lua_getmetatable(L, 1)) {
348 luaL_error(L, "invalid type");
350 int cls_index = lua_gettop(L);
351 // Stack: [mt]
353 lua_getfield(L, cls_index, "name");
354 if (!lua_isnil(L, -1)) {
355 luaL_addvalue(&b);
356 } else {
357 lua_pop(L, 1);
359 luaL_addstring(&b, "(");
361 lua_getfield(L, cls_index, "__instances");
362 // Stack: [mt, buckets]
363 immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
364 if (! info) {
365 luaL_error(L, "invalid type");
367 lua_rawgeti(L, -1, info->hash);
368 // Stack: [mt, buckets, bucket]
369 if (lua_isnil(L, -1)) {
370 luaL_error(L, "Failed to find hash bucket");
372 lua_pushvalue(L, 1);
373 // Stack: [mt, buckets, bucket, inst_udata]
374 lua_rawget(L, -2);
375 if (lua_isnil(L, -1)) {
376 luaL_error(L, "Failed to find instance table");
378 int inst_table_index = lua_gettop(L);
379 // Stack: [mt, buckets, bucket, inst_table]
380 lua_getfield(L, cls_index, "__fields");
381 int fields_index = lua_gettop(L);
382 lua_getglobal(L, "tostring");
383 int tostring_index = lua_gettop(L);
384 // Stack: [mt, buckets, bucket, inst_table, __fields, tostring]
385 int num_fields = lua_objlen(L, fields_index);
386 int n = num_fields + info->array_len, i = 1;
387 if (i <= n) {
388 goto first_list_item;
390 while (++i <= n) {
391 // Stack: [mt, buckets, bucket, inst_table, tostring, ???]
392 luaL_addstring(&b, ", ");
393 first_list_item:
394 lua_pushvalue(L, tostring_index);
395 // Stack: [mt, buckets, bucket, inst_table, tostring, ???, tostring]
396 if (i <= num_fields) {
397 lua_rawgeti(L, fields_index, i);
398 lua_gettable(L, inst_table_index);
399 } else {
400 lua_rawgeti(L, inst_table_index, i - num_fields);
402 if (lua_islightuserdata(L, -1) && lua_touserdata(L, -1) == &NIL_SENTINEL) {
403 lua_pop(L, 1);
404 lua_pushnil(L);
406 // Stack: [mt, buckets, bucket, inst_table, tostring, ???, tostring, value]
407 int quotes = lua_type(L, -1) == LUA_TSTRING;
408 lua_call(L, 1, 1);
409 if (quotes) {
410 luaL_addchar(&b, '"');
412 // Stack: [mt, buckets, bucket, inst_table, tostring, ???, value string]
413 // TODO: properly escape strings? str:gsub('["\\]',"\\%1"):gsub("\n","\\n")
414 luaL_addvalue(&b);
415 // Stack: [mt, buckets, bucket, inst_table, tostring, ???]
416 if (quotes) {
417 luaL_addchar(&b, '"');
420 luaL_addstring(&b, ")");
421 luaL_pushresult(&b);
422 return 1;
425 static int Lnexti(lua_State *L)
427 if (! lua_getmetatable(L, 1)) {
428 luaL_error(L, "invalid type");
430 // Stack: [mt]
431 lua_getfield(L, -1, "__instances");
432 // Stack: [mt, buckets]
433 immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
434 if (! info) {
435 luaL_error(L, "invalid type");
437 int i = lua_tointeger(L, 2)+1;
438 if (i > (int)info->array_len) {
439 return 0;
441 lua_rawgeti(L, -1, info->hash);
442 // Stack: [mt, buckets, bucket]
443 if (lua_isnil(L, -1)) {
444 luaL_error(L, "Failed to find hash bucket");
446 lua_pushvalue(L, 1);
447 // Stack: [mt, buckets, bucket, inst_udata]
448 lua_rawget(L, -2);
449 // Stack: [mt, buckets, bucket, inst_table]
450 lua_pushinteger(L, i);
451 // Stack: [mt, buckets, bucket, inst_table, i]
452 lua_rawgeti(L, -2, i);
453 // Stack: [mt, buckets, bucket, inst_table, i, table[i]]
454 if (lua_islightuserdata(L, -1) && lua_touserdata(L, -1) == &NIL_SENTINEL) {
455 lua_pop(L, 1);
456 lua_pushnil(L);
458 return 2;
461 static int Lipairs(lua_State *L)
463 lua_pushcfunction(L, Lnexti);
464 // Stack: [Lnexti]
465 lua_pushvalue(L, 1);
466 // Stack: [Lnexti, inst_udata]
467 lua_pushinteger(L, 0);
468 // Stack: [Lnexti, inst_udata, 0]
469 return 3;
472 static int Lnext(lua_State *L)
474 if (! lua_getmetatable(L, 1)) {
475 luaL_error(L, "invalid type");
477 // Stack: [mt]
478 lua_getfield(L, -1, "__instances");
479 // Stack: [mt, buckets]
480 immutable_info_t *info = (immutable_info_t*)lua_touserdata(L, 1);
481 if (! info) {
482 luaL_error(L, "invalid type");
484 lua_rawgeti(L, -1, info->hash);
485 // Stack: [mt, buckets, bucket]
486 if (lua_isnil(L, -1)) {
487 luaL_error(L, "Failed to find hash bucket");
489 lua_pushvalue(L, 1);
490 // Stack: [mt, buckets, bucket, inst_udata]
491 lua_rawget(L, -2);
492 // Stack: [mt, buckets, bucket, inst_table]
493 lua_pushvalue(L, 2);
494 // TODO: this is in a random order, and it might be good to force it to be in the same order as __fields
495 if (lua_next(L, -2) == 0) {
496 return 0;
498 if (lua_islightuserdata(L, -1) && lua_touserdata(L, -1) == &NIL_SENTINEL) {
499 lua_pop(L, 1);
500 lua_pushnil(L);
502 return 2;
505 static int Lpairs(lua_State *L)
507 lua_pushcfunction(L, Lnext);
508 // Stack: [Lnext]
509 lua_pushvalue(L, 1);
510 // Stack: [Lnext, inst_udata]
511 lua_pushnil(L);
512 // Stack: [Lnext, inst_udata, nil]
513 return 3;
516 static int Lhash(lua_State *L)
518 luaL_checktype(L, 1, LUA_TUSERDATA);
519 immutable_info_t *info = (immutable_info_t *)lua_touserdata(L, 1);
520 lua_pushinteger(L, info->hash);
521 return 1;
524 static const luaL_Reg Rinstance_metamethods[] =
526 {"__len", Llen},
527 {"__index", Lindex},
528 {"__tostring", Ltostring},
529 {"__ipairs", Lipairs},
530 {"__pairs", Lpairs},
531 {"__hash", Lhash},
532 {"from_table", Lfrom_table},
533 {"is_instance", Lis_instance},
534 {NULL, NULL}
537 static int Lmake_class(lua_State *L)
539 size_t num_args = lua_gettop(L);
540 // immutable([fields], [methods/metamethods])
541 lua_createtable(L, 0, 24); // Rough guess of number of fields from Rinstance_metamethods + __fields, etc.
542 // Stack: [CLS]
543 // Populate CLS.__len, CLS.__index, CLS.__pairs, etc.
544 luaL_register(L,NULL,Rinstance_metamethods);
546 // If methods were passed in, copy them over, overwriting defaults if desired
547 if (num_args >= 2 && lua_type(L, 2) == LUA_TTABLE) {
548 // Stack: [CLS]
549 lua_pushnil(L);
550 // Stack: [CLS, nil]
551 while (lua_next(L, 2) != 0) {
552 // Stack: [CLS, method_name, method_value]
553 lua_pushvalue(L, -2);
554 // Stack: [CLS, method_name, method_value, method_name]
555 lua_insert(L, -2);
556 // Stack: [CLS, method_name, method_name, method_value]
557 lua_settable(L, -4);
558 // Stack: [CLS, method_name]
560 // Stack: [CLS]
563 lua_pushlightuserdata(L, (void*)&SUPER_METHODS);
564 lua_gettable(L, LUA_REGISTRYINDEX);
565 lua_setfield(L, -2, "__super");
567 // Stack: [CLS]
568 lua_createtable(L, 0, 32); // Rough guess: at least 32 instances concurrently
569 // Stack: [CLS, __instances]
570 lua_pushlightuserdata(L, (void*)&WEAK_VALUE_METATABLE);
571 lua_gettable(L, LUA_REGISTRYINDEX);
572 // Stack: [CLS, __instances, {__mode='v'}]
573 lua_setmetatable(L, -2);
574 // Stack: [CLS, __instances]
575 lua_setfield(L, -2, "__instances");
577 // Stack: [CLS]
578 lua_createtable(L, 0, 32); // Rough guess: at least 32 instances concurrently
579 // Stack: [CLS, __buckets]
580 lua_pushlightuserdata(L, (void*)&WEAK_KEY_METATABLE);
581 lua_gettable(L, LUA_REGISTRYINDEX);
582 // Stack: [CLS, __buckets, {__mode='k'}]
583 lua_setmetatable(L, -2);
584 // Stack: [CLS, __buckets]
585 lua_setfield(L, -2, "__buckets");
587 // Stack: [CLS]
588 switch (num_args == 0 ? LUA_TNIL : lua_type(L, 1)) {
589 case LUA_TTABLE:
590 lua_pushvalue(L, 1);
591 break;
592 case LUA_TNIL: case LUA_TNONE:
593 // If no fields were passed in, set __fields to empty table
594 lua_createtable(L, 0, 0);
595 break;
596 default: {
597 luaL_error(L, "immutable expected the fields to be either table or nil");
600 lua_setfield(L, -2, "__fields");
601 // Stack: [CLS]
602 lua_pushlightuserdata(L, (void*)&SHARED_CLASS_METATABLE);
603 lua_gettable(L, LUA_REGISTRYINDEX);
604 lua_setmetatable(L, -2);
605 // Stack [CLS]
607 return 1;
610 static int Lclass_tostring(lua_State *L)
612 lua_getfield(L, 1, "name");
613 if (lua_isnil(L, -1)) {
614 lua_pushfstring(L, "immutable type: %p", lua_topointer(L, 1));
616 return 1;
619 static const luaL_Reg Rclass_metamethods[] =
621 { "__call", Lcreate_instance},
622 { "__tostring", Lclass_tostring},
623 { NULL, NULL}
626 LUALIB_API int luaopen_immutable(lua_State *L)
628 lua_pushlightuserdata(L, (void*)&SUPER_METHODS);
629 lua_createtable(L, 0, 8);
630 luaL_register(L,NULL,Rinstance_metamethods);
631 lua_settable(L, LUA_REGISTRYINDEX);
633 lua_pushlightuserdata(L, (void*)&WEAK_VALUE_METATABLE);
634 lua_createtable(L, 0, 1);
635 lua_pushstring(L, "v");
636 lua_setfield(L, -2, "__mode");
637 lua_settable(L, LUA_REGISTRYINDEX);
639 lua_pushlightuserdata(L, (void*)&WEAK_KEY_METATABLE);
640 lua_createtable(L, 0, 1);
641 lua_pushstring(L, "k");
642 lua_setfield(L, -2, "__mode");
643 lua_settable(L, LUA_REGISTRYINDEX);
645 lua_pushlightuserdata(L, (void*)&SHARED_CLASS_METATABLE);
646 lua_createtable(L, 0, 2);
647 luaL_register(L,NULL,Rclass_metamethods);
648 lua_settable(L, LUA_REGISTRYINDEX);
650 lua_pushcfunction(L, Lmake_class);
651 return 1;