-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlua_bridge_support.m
More file actions
269 lines (254 loc) · 7.57 KB
/
Copy pathlua_bridge_support.m
File metadata and controls
269 lines (254 loc) · 7.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* lua_bridge_support.m — state-agnostic Lua/Foundation bridge helpers.
*
* This borrows LuaSkin's useful boundary patterns without adopting its global
* state owner or general Objective-C object model. Platform roots define the
* native view/window classes and metatable names before including this file.
*/
typedef struct {
void *ptr;
} ObjCRef;
static void push_objc(lua_State *L, id obj, const char *meta) {
ObjCRef *ref = lua_newuserdata(L, sizeof(ObjCRef));
ref->ptr = (void *)CFBridgingRetain(obj);
luaL_setmetatable(L, meta);
}
#ifndef LUA_OBJC_VIEWCONTROLLER_METATABLE
#define LUA_OBJC_VIEWCONTROLLER_METATABLE "uiviewcontroller"
#endif
static ObjCRef *lua_objc_test_ref(lua_State *L, int idx) {
ObjCRef *ref = luaL_testudata(L, idx, LUA_OBJC_VIEW_METATABLE);
if (!ref) ref = luaL_testudata(L, idx, LUA_OBJC_WINDOW_METATABLE);
if (!ref) ref = luaL_testudata(L, idx, LUA_OBJC_VIEWCONTROLLER_METATABLE);
if (!ref) ref = luaL_testudata(L, idx, "nsobject");
return ref;
}
id lua_objc_check_object(
lua_State *L,
int idx,
Class expectedClass,
const char *expectedName
) {
ObjCRef *ref = lua_objc_test_ref(L, idx);
if (!ref) {
luaL_typeerror(L, idx, expectedName);
return nil;
}
id object = (__bridge id)ref->ptr;
if (![object isKindOfClass:expectedClass]) {
NSString *message = [NSString stringWithFormat:@"%s expected, got %@",
expectedName, NSStringFromClass([object class])];
luaL_argerror(L, idx, message.UTF8String);
return nil;
}
return object;
}
#define LUA_OBJC_CHECK_OBJECT(L, idx, type) \
((type *)lua_objc_check_object((L), (idx), [type class], #type))
static int gc_objc(lua_State *L) {
ObjCRef *ref = lua_touserdata(L, 1);
if (ref && ref->ptr) {
CFRelease(ref->ptr);
ref->ptr = NULL;
}
return 0;
}
static BOOL lua_objc_number_is_boolean(NSNumber *number) {
return CFGetTypeID((__bridge CFTypeRef)number) == CFBooleanGetTypeID();
}
static BOOL lua_objc_number_is_integer(NSNumber *number) {
const char *type = number.objCType;
return strchr("cCsSiIlLqQ", type[0]) != NULL && type[1] == '\0';
}
static void lua_objc_push_foundation_value(
lua_State *L,
id value,
NSHashTable *ancestors,
NSUInteger depth
) {
if (!value || value == [NSNull null]) {
lua_pushnil(L);
return;
}
if ([value isKindOfClass:[NSString class]]) {
NSData *utf8 = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
lua_pushlstring(L, utf8.bytes, utf8.length);
return;
}
if ([value isKindOfClass:[NSData class]]) {
NSData *data = (NSData *)value;
lua_pushlstring(L, data.bytes, data.length);
return;
}
if ([value isKindOfClass:[NSNumber class]]) {
NSNumber *number = (NSNumber *)value;
if (lua_objc_number_is_boolean(number)) {
lua_pushboolean(L, number.boolValue);
} else if (lua_objc_number_is_integer(number)) {
lua_pushinteger(L, (lua_Integer)number.longLongValue);
} else {
lua_pushnumber(L, number.doubleValue);
}
return;
}
if (depth >= 64 || [ancestors containsObject:value]) {
lua_pushnil(L);
return;
}
if ([value isKindOfClass:[NSArray class]]) {
[ancestors addObject:value];
lua_createtable(L, (int)[(NSArray *)value count], 0);
[(NSArray *)value enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
lua_objc_push_foundation_value(L, item, ancestors, depth + 1);
lua_rawseti(L, -2, (lua_Integer)idx + 1);
}];
[ancestors removeObject:value];
return;
}
if ([value isKindOfClass:[NSDictionary class]]) {
[ancestors addObject:value];
lua_createtable(L, 0, (int)[(NSDictionary *)value count]);
for (id key in (NSDictionary *)value) {
lua_objc_push_foundation_value(L, key, ancestors, depth + 1);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
continue;
}
lua_objc_push_foundation_value(
L, [(NSDictionary *)value objectForKey:key], ancestors, depth + 1);
lua_settable(L, -3);
}
[ancestors removeObject:value];
return;
}
if ([value isKindOfClass:[LUA_OBJC_VIEW_CLASS class]]) {
push_objc(L, value, LUA_OBJC_VIEW_METATABLE);
return;
}
if ([value isKindOfClass:[LUA_OBJC_WINDOW_CLASS class]]) {
push_objc(L, value, LUA_OBJC_WINDOW_METATABLE);
return;
}
push_objc(L, value, "nsobject");
}
static void push_objc_value(lua_State *L, id value) {
NSHashTable *ancestors =
[NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPointerPersonality];
lua_objc_push_foundation_value(L, value, ancestors, 0);
}
static id lua_objc_foundation_value_at_index(
lua_State *L,
int idx,
NSMutableSet<NSValue *> *ancestors,
NSUInteger depth,
BOOL *converted
) {
idx = lua_absindex(L, idx);
switch (lua_type(L, idx)) {
case LUA_TNIL:
*converted = YES;
return nil;
case LUA_TBOOLEAN:
*converted = YES;
return @((BOOL)lua_toboolean(L, idx));
case LUA_TNUMBER:
*converted = YES;
return lua_isinteger(L, idx)
? @((long long)lua_tointeger(L, idx))
: @(lua_tonumber(L, idx));
case LUA_TSTRING: {
size_t length = 0;
const char *bytes = lua_tolstring(L, idx, &length);
NSString *string = [[NSString alloc] initWithBytes:bytes
length:length encoding:NSUTF8StringEncoding];
*converted = string != nil;
return string;
}
case LUA_TUSERDATA: {
ObjCRef *ref = lua_objc_test_ref(L, idx);
*converted = ref != NULL;
return ref ? (__bridge id)ref->ptr : nil;
}
case LUA_TTABLE: {
if (depth >= 64) {
*converted = NO;
return nil;
}
NSValue *identity =
[NSValue valueWithPointer:lua_topointer(L, idx)];
if ([ancestors containsObject:identity]) {
*converted = NO;
return nil;
}
[ancestors addObject:identity];
lua_Integer count = (lua_Integer)lua_rawlen(L, idx);
BOOL array = YES;
lua_Integer entries = 0;
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
entries++;
if (!lua_isinteger(L, -2)) {
array = NO;
} else {
lua_Integer key = lua_tointeger(L, -2);
if (key < 1 || key > count) array = NO;
}
lua_pop(L, 1);
}
array = array && entries == count;
id result = nil;
BOOL childrenConverted = YES;
if (array) {
NSMutableArray *values =
[NSMutableArray arrayWithCapacity:(NSUInteger)count];
for (lua_Integer i = 1; i <= count; i++) {
lua_rawgeti(L, idx, i);
BOOL childConverted = NO;
id child = lua_objc_foundation_value_at_index(
L, -1, ancestors, depth + 1, &childConverted);
lua_pop(L, 1);
if (!childConverted) {
childrenConverted = NO;
break;
}
[values addObject:child ?: [NSNull null]];
}
if (childrenConverted) result = values;
} else {
NSMutableDictionary *values = [NSMutableDictionary dictionary];
lua_pushnil(L);
while (lua_next(L, idx) != 0) {
BOOL keyConverted = NO;
BOOL valueConverted = NO;
id key = lua_objc_foundation_value_at_index(
L, -2, ancestors, depth + 1, &keyConverted);
id value = lua_objc_foundation_value_at_index(
L, -1, ancestors, depth + 1, &valueConverted);
lua_pop(L, 1);
if (!keyConverted || !valueConverted
|| ![key conformsToProtocol:@protocol(NSCopying)]) {
childrenConverted = NO;
break;
}
values[key] = value ?: [NSNull null];
}
if (!childrenConverted) lua_pop(L, 1);
if (childrenConverted) result = values;
}
[ancestors removeObject:identity];
*converted = childrenConverted;
return result;
}
default:
*converted = NO;
return nil;
}
}
static id lua_to_objc_value(lua_State *L, int idx) {
BOOL converted = NO;
NSMutableSet<NSValue *> *ancestors = [NSMutableSet set];
id value = lua_objc_foundation_value_at_index(L, idx, ancestors, 0, &converted);
if (!converted) {
luaL_error(L, "cannot convert %s to a Foundation value", luaL_typename(L, idx));
}
return value;
}