diff options
author | Mike Pall <mike> | 2011-11-14 14:15:57 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2011-11-14 14:18:25 +0100 |
commit | 71d00a56dbab6c29c0346093dbe530d7b7608be4 (patch) | |
tree | 5e28e19b4d2f20168d5ee0e4fe500b1e2b233c1c /src/lj_ccallback.c | |
parent | e9eb4fdb4a08baaa2d9190187a6c38d5b3f8b091 (diff) | |
download | luajit-71d00a56dbab6c29c0346093dbe530d7b7608be4.tar.gz luajit-71d00a56dbab6c29c0346093dbe530d7b7608be4.tar.bz2 luajit-71d00a56dbab6c29c0346093dbe530d7b7608be4.zip |
FFI: Add callback support (for x86/x64).
Diffstat (limited to 'src/lj_ccallback.c')
-rw-r--r-- | src/lj_ccallback.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c new file mode 100644 index 00000000..8aab7f36 --- /dev/null +++ b/src/lj_ccallback.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | ** FFI C callback handling. | ||
3 | ** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #include "lj_obj.h" | ||
7 | |||
8 | #if LJ_HASFFI | ||
9 | |||
10 | #include "lj_gc.h" | ||
11 | #include "lj_err.h" | ||
12 | #include "lj_tab.h" | ||
13 | #include "lj_state.h" | ||
14 | #include "lj_frame.h" | ||
15 | #include "lj_ctype.h" | ||
16 | #include "lj_cconv.h" | ||
17 | #include "lj_ccall.h" | ||
18 | #include "lj_ccallback.h" | ||
19 | #include "lj_target.h" | ||
20 | #include "lj_vm.h" | ||
21 | |||
22 | /* -- Target-specific handling of callback slots -------------------------- */ | ||
23 | |||
24 | #define CALLBACK_MCODE_SIZE (LJ_PAGESIZE * LJ_NUM_CBPAGE) | ||
25 | |||
26 | #if LJ_TARGET_X86ORX64 | ||
27 | |||
28 | #define CALLBACK_MCODE_HEAD (LJ_64 ? 8 : 0) | ||
29 | #define CALLBACK_MCODE_GROUP (-2+1+2+5+(LJ_64 ? 6 : 5)) | ||
30 | |||
31 | #define CALLBACK_SLOT2OFS(slot) \ | ||
32 | (CALLBACK_MCODE_HEAD + CALLBACK_MCODE_GROUP*((slot)/32) + 4*(slot)) | ||
33 | |||
34 | static MSize CALLBACK_OFS2SLOT(MSize ofs) | ||
35 | { | ||
36 | MSize group; | ||
37 | ofs -= CALLBACK_MCODE_HEAD; | ||
38 | group = ofs / (32*4 + CALLBACK_MCODE_GROUP); | ||
39 | return (ofs % (32*4 + CALLBACK_MCODE_GROUP))/4 + group*32; | ||
40 | } | ||
41 | |||
42 | #define CALLBACK_MAX_SLOT \ | ||
43 | (((CALLBACK_MCODE_SIZE-CALLBACK_MCODE_HEAD)/(CALLBACK_MCODE_GROUP+4*32))*32) | ||
44 | |||
45 | #else | ||
46 | |||
47 | /* Missing support for this architecture. */ | ||
48 | #define CALLBACK_SLOT2OFS(slot) (0*(slot)) | ||
49 | #define CALLBACK_OFS2SLOT(ofs) (0*(ofs)) | ||
50 | #define CALLBACK_MAX_SLOT 0 | ||
51 | |||
52 | #endif | ||
53 | |||
54 | /* Convert callback slot number to callback function pointer. */ | ||
55 | static void *callback_slot2ptr(CTState *cts, MSize slot) | ||
56 | { | ||
57 | return (uint8_t *)cts->cb.mcode + CALLBACK_SLOT2OFS(slot); | ||
58 | } | ||
59 | |||
60 | /* Convert callback function pointer to slot number. */ | ||
61 | MSize lj_ccallback_ptr2slot(CTState *cts, void *p) | ||
62 | { | ||
63 | uintptr_t ofs = (uintptr_t)((uint8_t *)p -(uint8_t *)cts->cb.mcode); | ||
64 | if (ofs < CALLBACK_MCODE_SIZE) { | ||
65 | MSize slot = CALLBACK_OFS2SLOT((MSize)ofs); | ||
66 | if (CALLBACK_SLOT2OFS(slot) == (MSize)ofs) | ||
67 | return slot; | ||
68 | } | ||
69 | return ~0u; /* Not a known callback function pointer. */ | ||
70 | } | ||
71 | |||
72 | #if LJ_TARGET_X86ORX64 | ||
73 | /* Initialize machine code for callback function pointers. */ | ||
74 | static void callback_mcode_init(global_State *g, uint8_t *page) | ||
75 | { | ||
76 | uint8_t *p = page; | ||
77 | uint8_t *target = (uint8_t *)(void *)lj_vm_ffi_callback; | ||
78 | MSize slot; | ||
79 | #if LJ_64 | ||
80 | *(void **)p = target; p += 8; | ||
81 | #endif | ||
82 | for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) { | ||
83 | /* mov al, slot; jmp group */ | ||
84 | *p++ = XI_MOVrib | RID_EAX; *p++ = (uint8_t)slot; | ||
85 | if ((slot & 31) == 31 || slot == CALLBACK_MAX_SLOT-1) { | ||
86 | /* push ebp/rbp; mov ah, slot>>8; mov ebp, &g. */ | ||
87 | *p++ = XI_PUSH + RID_EBP; | ||
88 | *p++ = XI_MOVrib | (RID_EAX+4); *p++ = (uint8_t)(slot >> 8); | ||
89 | *p++ = XI_MOVri | RID_EBP; | ||
90 | *(int32_t *)p = i32ptr(g); p += 4; | ||
91 | #if LJ_64 | ||
92 | /* jmp [rip-pageofs] where lj_vm_ffi_callback is stored. */ | ||
93 | *p++ = XI_GROUP5; *p++ = XM_OFS0 + (XOg_JMP<<3) + RID_EBP; | ||
94 | *(int32_t *)p = (int32_t)(page-(p+4)); p += 4; | ||
95 | #else | ||
96 | /* jmp lj_vm_ffi_callback. */ | ||
97 | *p++ = XI_JMP; *(int32_t *)p = target-(p+4); p += 4; | ||
98 | #endif | ||
99 | } else { | ||
100 | *p++ = XI_JMPs; *p++ = (uint8_t)((2+2)*(31-(slot&31)) - 2); | ||
101 | } | ||
102 | } | ||
103 | lua_assert(p - page <= CALLBACK_MCODE_SIZE); | ||
104 | } | ||
105 | #else | ||
106 | /* Missing support for this architecture. */ | ||
107 | #define callback_mcode_init(g, p) UNUSED(p) | ||
108 | #endif | ||
109 | |||
110 | /* -- Machine code management --------------------------------------------- */ | ||
111 | |||
112 | #if LJ_TARGET_WINDOWS | ||
113 | |||
114 | #define WIN32_LEAN_AND_MEAN | ||
115 | #include <windows.h> | ||
116 | |||
117 | #elif LJ_TARGET_POSIX | ||
118 | |||
119 | #include <sys/mman.h> | ||
120 | #ifndef MAP_ANONYMOUS | ||
121 | #define MAP_ANONYMOUS MAP_ANON | ||
122 | #endif | ||
123 | |||
124 | #endif | ||
125 | |||
126 | /* Allocate and initialize area for callback function pointers. */ | ||
127 | static void callback_mcode_new(CTState *cts) | ||
128 | { | ||
129 | size_t sz = (size_t)CALLBACK_MCODE_SIZE; | ||
130 | void *p; | ||
131 | if (CALLBACK_MAX_SLOT == 0) | ||
132 | lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); | ||
133 | #if LJ_TARGET_WINDOWS | ||
134 | p = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); | ||
135 | if (!p) | ||
136 | lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); | ||
137 | #elif LJ_TARGET_POSIX | ||
138 | p = mmap(NULL, sz, (PROT_READ|PROT_WRITE), MAP_PRIVATE|MAP_ANONYMOUS, | ||
139 | -1, 0); | ||
140 | if (p == MAP_FAILED) | ||
141 | lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); | ||
142 | #else | ||
143 | /* Fallback allocator. Fails if memory is not executable by default. */ | ||
144 | p = lj_mem_new(cts->L, sz); | ||
145 | #endif | ||
146 | cts->cb.mcode = p; | ||
147 | callback_mcode_init(cts->g, p); | ||
148 | #if LJ_TARGET_WINDOWS | ||
149 | { | ||
150 | DWORD oprot; | ||
151 | VirtualProtect(p, sz, PAGE_EXECUTE_READ, &oprot); | ||
152 | } | ||
153 | #elif LJ_TARGET_POSIX | ||
154 | mprotect(p, sz, (PROT_READ|PROT_EXEC)); | ||
155 | #endif | ||
156 | } | ||
157 | |||
158 | /* Free area for callback function pointers. */ | ||
159 | void lj_ccallback_mcode_free(CTState *cts) | ||
160 | { | ||
161 | size_t sz = (size_t)CALLBACK_MCODE_SIZE; | ||
162 | void *p = cts->cb.mcode; | ||
163 | if (p == NULL) return; | ||
164 | #if LJ_TARGET_WINDOWS | ||
165 | VirtualFree(p, 0, MEM_RELEASE); | ||
166 | UNUSED(sz); | ||
167 | #elif LJ_TARGET_POSIX | ||
168 | munmap(p, sz); | ||
169 | #else | ||
170 | lj_mem_free(cts->g, p, sz); | ||
171 | #endif | ||
172 | } | ||
173 | |||
174 | /* -- C callback entry ---------------------------------------------------- */ | ||
175 | |||
176 | /* Target-specific handling of register arguments. Similar to lj_ccall.c. */ | ||
177 | #if LJ_TARGET_X86 | ||
178 | |||
179 | #define CALLBACK_HANDLE_REGARG \ | ||
180 | if (!isfp) { /* Only non-FP values may be passed in registers. */ \ | ||
181 | if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \ | ||
182 | if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \ | ||
183 | } else if (ngpr + 1 <= maxgpr) { \ | ||
184 | sp = &cts->cb.gpr[ngpr]; \ | ||
185 | ngpr += n; \ | ||
186 | goto done; \ | ||
187 | } \ | ||
188 | } | ||
189 | |||
190 | #elif LJ_TARGET_X64 && LJ_ABI_WIN | ||
191 | |||
192 | /* Windows/x64 argument registers are strictly positional (use ngpr). */ | ||
193 | #define CALLBACK_HANDLE_REGARG \ | ||
194 | if (isfp) { \ | ||
195 | if (ngpr < 4) { sp = &cts->cb.fpr[ngpr++]; nfpr = ngpr; goto done; } \ | ||
196 | } else { \ | ||
197 | if (ngpr < 4) { sp = &cts->cb.gpr[ngpr++]; goto done; } \ | ||
198 | } | ||
199 | |||
200 | #elif LJ_TARGET_X64 | ||
201 | |||
202 | #define CALLBACK_HANDLE_REGARG \ | ||
203 | if (isfp) { \ | ||
204 | if (nfpr + n <= CCALL_NARG_FPR) { \ | ||
205 | sp = &cts->cb.fpr[nfpr]; \ | ||
206 | nfpr += n; \ | ||
207 | goto done; \ | ||
208 | } \ | ||
209 | } else { \ | ||
210 | if (ngpr + n <= maxgpr) { \ | ||
211 | sp = &cts->cb.gpr[ngpr]; \ | ||
212 | ngpr += n; \ | ||
213 | goto done; \ | ||
214 | } \ | ||
215 | } | ||
216 | |||
217 | #elif LJ_TARGET_ARM | ||
218 | |||
219 | #define CALLBACK_HANDLE_REGARG \ | ||
220 | UNUSED(ngpr); UNUSED(maxgpr); goto done; /* NYI */ | ||
221 | |||
222 | #elif LJ_TARGET_PPC | ||
223 | |||
224 | #define CALLBACK_HANDLE_REGARG \ | ||
225 | UNUSED(ngpr); UNUSED(nfpr); UNUSED(maxgpr); goto done; /* NYI */ | ||
226 | #define CALLBACK_HANDLE_RET /* NYI */ | ||
227 | |||
228 | #else | ||
229 | #error "Missing calling convention definitions for this architecture" | ||
230 | #endif | ||
231 | |||
232 | /* Convert and push callback arguments to Lua stack. */ | ||
233 | static void callback_conv_args(CTState *cts, lua_State *L) | ||
234 | { | ||
235 | TValue *o = L->top; | ||
236 | intptr_t *stack = cts->cb.stack; | ||
237 | MSize slot = cts->cb.slot; | ||
238 | CTypeID id = 0, rid, fid; | ||
239 | CType *ct; | ||
240 | GCfunc *fn; | ||
241 | MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR; | ||
242 | #if CCALL_NARG_FPR | ||
243 | MSize nfpr = 0; | ||
244 | #endif | ||
245 | |||
246 | if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) { | ||
247 | ct = ctype_get(cts, id); | ||
248 | rid = ctype_cid(ct->info); | ||
249 | fn = funcV(lj_tab_getint(cts->miscmap, (int32_t)slot)); | ||
250 | } else { /* Must set up frame first, before throwing the error. */ | ||
251 | ct = NULL; | ||
252 | rid = 0; | ||
253 | fn = (GCfunc *)L; | ||
254 | } | ||
255 | o->u32.lo = LJ_CONT_FFI_CALLBACK; /* Continuation returns from callback. */ | ||
256 | o->u32.hi = rid; /* Return type. x86: +(spadj<<16). */ | ||
257 | o++; | ||
258 | setframe_gc(o, obj2gco(fn)); | ||
259 | setframe_ftsz(o, (int)((char *)(o+1) - (char *)L->base) + FRAME_CONT); | ||
260 | L->top = L->base = ++o; | ||
261 | if (!ct) | ||
262 | lj_err_caller(cts->L, LJ_ERR_FFI_BADCBACK); | ||
263 | if (isluafunc(fn)) | ||
264 | setcframe_pc(L->cframe, proto_bc(funcproto(fn))+1); | ||
265 | lj_state_checkstack(L, LUA_MINSTACK); /* May throw. */ | ||
266 | o = L->base; /* Might have been reallocated. */ | ||
267 | |||
268 | #if LJ_TARGET_X86 | ||
269 | /* x86 has several different calling conventions. */ | ||
270 | switch (ctype_cconv(ct->info)) { | ||
271 | case CTCC_FASTCALL: maxgpr = 2; break; | ||
272 | case CTCC_THISCALL: maxgpr = 1; break; | ||
273 | default: maxgpr = 0; break; | ||
274 | } | ||
275 | #endif | ||
276 | |||
277 | fid = ct->sib; | ||
278 | while (fid) { | ||
279 | CType *ctf = ctype_get(cts, fid); | ||
280 | if (!ctype_isattrib(ctf->info)) { | ||
281 | CType *cta; | ||
282 | void *sp; | ||
283 | CTSize sz; | ||
284 | int isfp; | ||
285 | MSize n; | ||
286 | lua_assert(ctype_isfield(ctf->info)); | ||
287 | cta = ctype_rawchild(cts, ctf); | ||
288 | if (ctype_isenum(cta->info)) cta = ctype_child(cts, cta); | ||
289 | isfp = ctype_isfp(cta->info); | ||
290 | sz = (cta->size + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1); | ||
291 | n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */ | ||
292 | |||
293 | CALLBACK_HANDLE_REGARG /* Handle register arguments. */ | ||
294 | |||
295 | /* Otherwise pass argument on stack. */ | ||
296 | if (CCALL_ALIGN_STACKARG && LJ_32 && sz == 8) | ||
297 | nsp = (nsp + 1) & ~1u; /* Align 64 bit argument on stack. */ | ||
298 | sp = &stack[nsp]; | ||
299 | nsp += n; | ||
300 | |||
301 | done: | ||
302 | if (LJ_BE && cta->size < CTSIZE_PTR) | ||
303 | sp = (void *)((uint8_t *)sp + CTSIZE_PTR-cta->size); | ||
304 | lj_cconv_tv_ct(cts, cta, 0, o++, sp); | ||
305 | } | ||
306 | fid = ctf->sib; | ||
307 | } | ||
308 | L->top = o; | ||
309 | #if LJ_TARGET_X86 | ||
310 | /* Store stack adjustment for returns from fastcall/stdcall callbacks. */ | ||
311 | switch (ctype_cconv(ct->info)) { | ||
312 | case CTCC_FASTCALL: case CTCC_STDCALL: | ||
313 | (L->base-2)->u32.hi |= (nsp << (16+2)); | ||
314 | break; | ||
315 | } | ||
316 | #endif | ||
317 | } | ||
318 | |||
319 | /* Convert Lua object to callback result. */ | ||
320 | static void callback_conv_result(CTState *cts, lua_State *L, TValue *o) | ||
321 | { | ||
322 | CType *ctr = ctype_raw(cts, (uint16_t)(L->base-2)->u32.hi); | ||
323 | #if LJ_TARGET_X86 | ||
324 | cts->cb.gpr[2] = 0; | ||
325 | #endif | ||
326 | if (!ctype_isvoid(ctr->info)) { | ||
327 | uint8_t *dp = (uint8_t *)&cts->cb.gpr[0]; | ||
328 | #ifdef CALLBACK_HANDLE_RET | ||
329 | CALLBACK_HANDLE_RET | ||
330 | #endif | ||
331 | #if CCALL_NUM_FPR | ||
332 | if (ctype_isfp(ctr->info)) | ||
333 | dp = (uint8_t *)&cts->cb.fpr[0]; | ||
334 | #endif | ||
335 | lj_cconv_ct_tv(cts, ctr, dp, o, 0); | ||
336 | /* Extend returned integers to (at least) 32 bits. */ | ||
337 | if (ctype_isinteger_or_bool(ctr->info) && ctr->size < 4) { | ||
338 | if (ctr->info & CTF_UNSIGNED) | ||
339 | *(uint32_t *)dp = ctr->size == 1 ? (uint32_t)*(uint8_t *)dp : | ||
340 | (uint32_t)*(uint16_t *)dp; | ||
341 | else | ||
342 | *(int32_t *)dp = ctr->size == 1 ? (int32_t)*(int8_t *)dp : | ||
343 | (int32_t)*(int16_t *)dp; | ||
344 | } | ||
345 | #if LJ_TARGET_X86 | ||
346 | if (ctype_isfp(ctr->info)) | ||
347 | cts->cb.gpr[2] = ctr->size == sizeof(float) ? 1 : 2; | ||
348 | #endif | ||
349 | } | ||
350 | } | ||
351 | |||
352 | /* Enter callback. */ | ||
353 | lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf) | ||
354 | { | ||
355 | lua_State *L = cts->L; | ||
356 | lua_assert(L != NULL); | ||
357 | if (gcref(cts->g->jit_L)) | ||
358 | lj_err_caller(gco2th(gcref(cts->g->jit_L)), LJ_ERR_FFI_BADCBACK); | ||
359 | /* Setup C frame. */ | ||
360 | cframe_prev(cf) = L->cframe; | ||
361 | setcframe_L(cf, L); | ||
362 | cframe_errfunc(cf) = -1; | ||
363 | cframe_nres(cf) = 0; | ||
364 | L->cframe = cf; | ||
365 | callback_conv_args(cts, L); | ||
366 | return L; /* Now call the function on this stack. */ | ||
367 | } | ||
368 | |||
369 | /* Leave callback. */ | ||
370 | void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o) | ||
371 | { | ||
372 | lua_State *L = cts->L; | ||
373 | GCfunc *fn; | ||
374 | TValue *obase = L->base; | ||
375 | L->base = L->top; /* Keep continuation frame for throwing errors. */ | ||
376 | /* PC of RET* is lost. Point to last line for result conv. errors. */ | ||
377 | fn = curr_func(L); | ||
378 | if (isluafunc(fn)) { | ||
379 | GCproto *pt = funcproto(fn); | ||
380 | setcframe_pc(L->cframe, proto_bc(pt)+pt->sizebc); | ||
381 | } | ||
382 | callback_conv_result(cts, L, o); | ||
383 | /* Finally drop C frame and continuation frame. */ | ||
384 | L->cframe = cframe_prev(L->cframe); | ||
385 | L->top -= 2; | ||
386 | L->base = obase; | ||
387 | } | ||
388 | |||
389 | /* -- C callback management ----------------------------------------------- */ | ||
390 | |||
391 | /* Get an unused slot in the callback slot table. */ | ||
392 | static MSize callback_slot_new(CTState *cts, CType *ct) | ||
393 | { | ||
394 | CTypeID id = ctype_typeid(cts, ct); | ||
395 | CTypeID1 *cbid = cts->cb.cbid; | ||
396 | MSize top; | ||
397 | for (top = cts->cb.topid; top < cts->cb.sizeid; top++) | ||
398 | if (LJ_LIKELY(cbid[top] == 0)) | ||
399 | goto found; | ||
400 | #if CALLBACK_MAX_SLOT | ||
401 | if (top >= CALLBACK_MAX_SLOT) | ||
402 | #endif | ||
403 | lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); | ||
404 | if (!cts->cb.mcode) | ||
405 | callback_mcode_new(cts); | ||
406 | lj_mem_growvec(cts->L, cbid, cts->cb.sizeid, CALLBACK_MAX_SLOT, CTypeID1); | ||
407 | cts->cb.cbid = cbid; | ||
408 | memset(cbid+top, 0, (cts->cb.sizeid-top)*sizeof(CTypeID1)); | ||
409 | found: | ||
410 | cbid[top] = id; | ||
411 | cts->cb.topid = top+1; | ||
412 | return top; | ||
413 | } | ||
414 | |||
415 | /* Check for function pointer and supported argument/result types. */ | ||
416 | static CType *callback_checkfunc(CTState *cts, CType *ct) | ||
417 | { | ||
418 | int narg = 0; | ||
419 | if (!ctype_isptr(ct->info) || (LJ_64 && ct->size != CTSIZE_PTR)) | ||
420 | return NULL; | ||
421 | ct = ctype_rawchild(cts, ct); | ||
422 | if (ctype_isfunc(ct->info)) { | ||
423 | CType *ctr = ctype_rawchild(cts, ct); | ||
424 | CTypeID fid = ct->sib; | ||
425 | if (!(ctype_isvoid(ctr->info) || ctype_isenum(ctr->info) || | ||
426 | ctype_isptr(ctr->info) || (ctype_isnum(ctr->info) && ctr->size <= 8))) | ||
427 | return NULL; | ||
428 | if ((ct->info & CTF_VARARG)) | ||
429 | return NULL; | ||
430 | while (fid) { | ||
431 | CType *ctf = ctype_get(cts, fid); | ||
432 | if (!ctype_isattrib(ctf->info)) { | ||
433 | CType *cta; | ||
434 | lua_assert(ctype_isfield(ctf->info)); | ||
435 | cta = ctype_rawchild(cts, ctf); | ||
436 | if (!(ctype_isenum(cta->info) || ctype_isptr(cta->info) || | ||
437 | (ctype_isnum(cta->info) && cta->size <= 8)) || | ||
438 | ++narg >= LUA_MINSTACK-3) | ||
439 | return NULL; | ||
440 | } | ||
441 | fid = ctf->sib; | ||
442 | } | ||
443 | return ct; | ||
444 | } | ||
445 | return NULL; | ||
446 | } | ||
447 | |||
448 | /* Create a new callback and return the callback function pointer. */ | ||
449 | void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn) | ||
450 | { | ||
451 | ct = callback_checkfunc(cts, ct); | ||
452 | if (ct) { | ||
453 | MSize slot = callback_slot_new(cts, ct); | ||
454 | GCtab *t = cts->miscmap; | ||
455 | setfuncV(cts->L, lj_tab_setint(cts->L, t, (int32_t)slot), fn); | ||
456 | return callback_slot2ptr(cts, slot); | ||
457 | } | ||
458 | return NULL; /* Bad conversion. */ | ||
459 | } | ||
460 | |||
461 | #endif | ||