Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/core/nativecall_dyncall.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#ifndef _WIN32
3
#include <dlfcn.h>
4
#endif
5
6
/* Maps a calling convention name to an ID. */
7
3
MVMint16 MVM_nativecall_get_calling_convention(MVMThreadContext *tc, MVMString *name) {
8
3
    MVMint16 result = DC_CALL_C_DEFAULT;
9
3
    if (name && MVM_string_graphs(tc, name) > 0) {
10
0
        char *cname = MVM_string_utf8_encode_C_string(tc, name);
11
0
        if (strcmp(cname, "cdecl") == 0)
12
0
            result = DC_CALL_C_X86_CDECL;
13
0
        else if (strcmp(cname, "stdcall") == 0)
14
0
            result = DC_CALL_C_X86_WIN32_STD;
15
0
        else if (strcmp(cname, "thisgnu") == 0)
16
0
            result = DC_CALL_C_X86_WIN32_THIS_GNU;
17
0
        else if (strcmp(cname, "thisms") == 0)
18
0
            result = DC_CALL_C_X86_WIN32_THIS_MS;
19
0
        else if (strcmp(cname, "stdcall") == 0)
20
0
            result = DC_CALL_C_X64_WIN64;
21
0
        else {
22
0
            char *waste[] = { cname, NULL };
23
0
            MVM_exception_throw_adhoc_free(tc, waste,
24
0
                "Unknown calling convention '%s' used for native call", cname);
25
0
        }
26
0
        MVM_free(cname);
27
0
    }
28
3
    return result;
29
3
}
30
31
/* Map argument type ID to dyncall character ID. */
32
0
static char get_signature_char(MVMint16 type_id) {
33
0
    if ( (type_id & MVM_NATIVECALL_ARG_RW_MASK) == MVM_NATIVECALL_ARG_RW)
34
0
        return 'p';
35
0
36
0
    switch (type_id & MVM_NATIVECALL_ARG_TYPE_MASK) {
37
0
        case MVM_NATIVECALL_ARG_VOID:
38
0
            return 'v';
39
0
        case MVM_NATIVECALL_ARG_CHAR:
40
0
            return 'c';
41
0
        case MVM_NATIVECALL_ARG_SHORT:
42
0
            return 's';
43
0
        case MVM_NATIVECALL_ARG_INT:
44
0
            return 'i';
45
0
        case MVM_NATIVECALL_ARG_LONG:
46
0
            return 'j';
47
0
        case MVM_NATIVECALL_ARG_LONGLONG:
48
0
            return 'l';
49
0
        case MVM_NATIVECALL_ARG_FLOAT:
50
0
            return 'f';
51
0
        case MVM_NATIVECALL_ARG_DOUBLE:
52
0
            return 'd';
53
0
        case MVM_NATIVECALL_ARG_ASCIISTR:
54
0
        case MVM_NATIVECALL_ARG_UTF8STR:
55
0
        case MVM_NATIVECALL_ARG_UTF16STR:
56
0
        case MVM_NATIVECALL_ARG_CSTRUCT:
57
0
        case MVM_NATIVECALL_ARG_CPOINTER:
58
0
        case MVM_NATIVECALL_ARG_CARRAY:
59
0
        case MVM_NATIVECALL_ARG_CUNION:
60
0
        case MVM_NATIVECALL_ARG_VMARRAY:
61
0
        case MVM_NATIVECALL_ARG_CALLBACK:
62
0
            return 'p';
63
0
        case MVM_NATIVECALL_ARG_UCHAR:
64
0
            return 'C';
65
0
        case MVM_NATIVECALL_ARG_USHORT:
66
0
            return 'S';
67
0
        case MVM_NATIVECALL_ARG_UINT:
68
0
            return 'I';
69
0
        case MVM_NATIVECALL_ARG_ULONG:
70
0
            return 'J';
71
0
        case MVM_NATIVECALL_ARG_ULONGLONG:
72
0
            return 'L';
73
0
        default:
74
0
            return '\0';
75
0
    }
76
0
}
77
78
/* Sets up a callback, caching the information to avoid duplicate work. */
79
static char callback_handler(DCCallback *cb, DCArgs *args, DCValue *result, MVMNativeCallback *data);
80
0
static void * unmarshal_callback(MVMThreadContext *tc, MVMObject *callback, MVMObject *sig_info) {
81
0
    MVMNativeCallbackCacheHead *callback_data_head = NULL;
82
0
    MVMNativeCallback **callback_data_handle;
83
0
    MVMString          *cuid;
84
0
85
0
    if (!IS_CONCRETE(callback))
86
0
        return NULL;
87
0
88
0
    /* Try to locate existing cached callback info. */
89
0
    callback = MVM_frame_find_invokee(tc, callback, NULL);
90
0
    cuid     = ((MVMCode *)callback)->body.sf->body.cuuid;
91
0
    MVM_HASH_GET(tc, tc->native_callback_cache, cuid, callback_data_head);
92
0
93
0
    if (!callback_data_head) {
94
0
        callback_data_head = MVM_malloc(sizeof(MVMNativeCallbackCacheHead));
95
0
        callback_data_head->head = NULL;
96
0
97
0
        MVM_HASH_BIND(tc, tc->native_callback_cache, cuid, callback_data_head);
98
0
    }
99
0
100
0
    callback_data_handle = &(callback_data_head->head);
101
0
102
0
    while (*callback_data_handle) {
103
0
        if ((*callback_data_handle)->target == callback) /* found it, break */
104
0
            break;
105
0
106
0
        callback_data_handle = &((*callback_data_handle)->next);
107
0
    }
108
0
109
0
    if (!*callback_data_handle) {
110
0
        /* First, build the MVMNativeCallback */
111
0
        MVMCallsite *cs;
112
0
        char        *signature;
113
0
        MVMObject   *typehash;
114
0
        MVMint64     num_info, i;
115
0
        MVMNativeCallback *callback_data;
116
0
117
0
        num_info                 = MVM_repr_elems(tc, sig_info);
118
0
        callback_data            = MVM_malloc(sizeof(MVMNativeCallback));
119
0
        callback_data->num_types = num_info;
120
0
        callback_data->typeinfos = MVM_malloc(num_info * sizeof(MVMint16));
121
0
        callback_data->types     = MVM_malloc(num_info * sizeof(MVMObject *));
122
0
        callback_data->next      = NULL;
123
0
124
0
        /* A dyncall signature looks like this: xxx)x
125
0
        * Argument types before the ) and return type after it. Thus,
126
0
        * num_info+1 must be NULL (zero-terminated string) and num_info-1
127
0
        * must be the ).
128
0
        */
129
0
        signature = MVM_malloc(num_info + 2);
130
0
        signature[num_info + 1] = '\0';
131
0
        signature[num_info - 1] = ')';
132
0
133
0
        /* We'll also build up a MoarVM callsite as we go. */
134
0
        cs                 = MVM_calloc(1, sizeof(MVMCallsite));
135
0
        cs->flag_count     = num_info - 1;
136
0
        cs->arg_flags      = MVM_malloc(cs->flag_count * sizeof(MVMCallsiteEntry));
137
0
        cs->arg_count      = num_info - 1;
138
0
        cs->num_pos        = num_info - 1;
139
0
        cs->has_flattening = 0;
140
0
        cs->is_interned    = 0;
141
0
        cs->with_invocant  = NULL;
142
0
143
0
        typehash = MVM_repr_at_pos_o(tc, sig_info, 0);
144
0
        callback_data->types[0] = MVM_repr_at_key_o(tc, typehash,
145
0
            tc->instance->str_consts.typeobj);
146
0
        callback_data->typeinfos[0] = MVM_nativecall_get_arg_type(tc, typehash, 1);
147
0
        signature[num_info] = get_signature_char(callback_data->typeinfos[0]);
148
0
        for (i = 1; i < num_info; i++) {
149
0
            typehash = MVM_repr_at_pos_o(tc, sig_info, i);
150
0
            callback_data->types[i] = MVM_repr_at_key_o(tc, typehash,
151
0
                tc->instance->str_consts.typeobj);
152
0
            callback_data->typeinfos[i] = MVM_nativecall_get_arg_type(tc, typehash, 0) & ~MVM_NATIVECALL_ARG_FREE_STR;
153
0
            signature[i - 1] = get_signature_char(callback_data->typeinfos[i]);
154
0
            switch (callback_data->typeinfos[i] & MVM_NATIVECALL_ARG_TYPE_MASK) {
155
0
                case MVM_NATIVECALL_ARG_CHAR:
156
0
                case MVM_NATIVECALL_ARG_SHORT:
157
0
                case MVM_NATIVECALL_ARG_INT:
158
0
                case MVM_NATIVECALL_ARG_LONG:
159
0
                case MVM_NATIVECALL_ARG_LONGLONG:
160
0
                    cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT;
161
0
                    break;
162
0
                case MVM_NATIVECALL_ARG_UCHAR:
163
0
                case MVM_NATIVECALL_ARG_USHORT:
164
0
                case MVM_NATIVECALL_ARG_UINT:
165
0
                case MVM_NATIVECALL_ARG_ULONG:
166
0
                case MVM_NATIVECALL_ARG_ULONGLONG:
167
0
                    /* TODO: should probably be UINT, when we can support that. */
168
0
                    cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT;
169
0
                    break;
170
0
                case MVM_NATIVECALL_ARG_FLOAT:
171
0
                case MVM_NATIVECALL_ARG_DOUBLE:
172
0
                    cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_NUM;
173
0
                    break;
174
0
                default:
175
0
                    cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_OBJ;
176
0
                    break;
177
0
            }
178
0
        }
179
0
180
0
        MVM_callsite_try_intern(tc, &cs);
181
0
182
0
        callback_data->tc        = tc;
183
0
        callback_data->cs        = cs;
184
0
        callback_data->target    = callback;
185
0
        callback_data->cb        = dcbNewCallback(signature, (DCCallbackHandler *)callback_handler, callback_data);
186
0
187
0
        /* Now insert the MVMCallback into the linked list. */
188
0
        *callback_data_handle = callback_data;
189
0
        MVM_free(signature);
190
0
    }
191
0
192
0
    return (*callback_data_handle)->cb;
193
0
}
194
195
/* Called to handle a callback. */
196
typedef struct {
197
    MVMObject   *invokee;
198
    MVMRegister *args;
199
    MVMCallsite *cs;
200
} CallbackInvokeData;
201
0
static void callback_invoke(MVMThreadContext *tc, void *data) {
202
0
    /* Invoke the coderef, to set up the nested interpreter. */
203
0
    CallbackInvokeData *cid = (CallbackInvokeData *)data;
204
0
    STABLE(cid->invokee)->invoke(tc, cid->invokee, cid->cs, cid->args);
205
0
206
0
    /* Ensure we exit interp after callback. */
207
0
    tc->thread_entry_frame = tc->cur_frame;
208
0
}
209
0
static char callback_handler(DCCallback *cb, DCArgs *cb_args, DCValue *cb_result, MVMNativeCallback *data) {
210
0
    CallbackInvokeData cid;
211
0
    MVMint32 num_roots, i;
212
0
    MVMRegister res;
213
0
    MVMRegister *args;
214
0
215
0
    /* Unblock GC if needed, so this thread can do work. */
216
0
    MVMThreadContext *tc = data->tc;
217
0
    MVMint32 was_blocked = MVM_gc_is_thread_blocked(tc);
218
0
    if (was_blocked)
219
0
        MVM_gc_mark_thread_unblocked(tc);
220
0
221
0
    /* Build a callsite and arguments buffer. */
222
0
    args = MVM_malloc(data->num_types * sizeof(MVMRegister));
223
0
    num_roots = 0;
224
0
    for (i = 1; i < data->num_types; i++) {
225
0
        MVMObject *type     = data->types[i];
226
0
        MVMint16   typeinfo = data->typeinfos[i];
227
0
        switch (typeinfo & MVM_NATIVECALL_ARG_TYPE_MASK) {
228
0
            case MVM_NATIVECALL_ARG_CHAR:
229
0
                args[i - 1].i64 = dcbArgChar(cb_args);
230
0
                break;
231
0
            case MVM_NATIVECALL_ARG_SHORT:
232
0
                args[i - 1].i64 = dcbArgShort(cb_args);
233
0
                break;
234
0
            case MVM_NATIVECALL_ARG_INT:
235
0
                args[i - 1].i64 = dcbArgInt(cb_args);
236
0
                break;
237
0
            case MVM_NATIVECALL_ARG_LONG:
238
0
                args[i - 1].i64 = dcbArgLong(cb_args);
239
0
                break;
240
0
            case MVM_NATIVECALL_ARG_LONGLONG:
241
0
                args[i - 1].i64 = dcbArgLongLong(cb_args);
242
0
                break;
243
0
            case MVM_NATIVECALL_ARG_FLOAT:
244
0
                args[i - 1].n64 = dcbArgFloat(cb_args);
245
0
                break;
246
0
            case MVM_NATIVECALL_ARG_DOUBLE:
247
0
                args[i - 1].n64 = dcbArgDouble(cb_args);
248
0
                break;
249
0
            case MVM_NATIVECALL_ARG_ASCIISTR:
250
0
            case MVM_NATIVECALL_ARG_UTF8STR:
251
0
            case MVM_NATIVECALL_ARG_UTF16STR:
252
0
                args[i - 1].o = MVM_nativecall_make_str(tc, type, typeinfo,
253
0
                    (char *)dcbArgPointer(cb_args));
254
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
255
0
                num_roots++;
256
0
                break;
257
0
            case MVM_NATIVECALL_ARG_CSTRUCT:
258
0
                args[i - 1].o = MVM_nativecall_make_cstruct(tc, type,
259
0
                    dcbArgPointer(cb_args));
260
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
261
0
                num_roots++;
262
0
                break;
263
0
            case MVM_NATIVECALL_ARG_CPOINTER:
264
0
                args[i - 1].o = MVM_nativecall_make_cpointer(tc, type,
265
0
                    dcbArgPointer(cb_args));
266
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
267
0
                num_roots++;
268
0
                break;
269
0
            case MVM_NATIVECALL_ARG_CARRAY:
270
0
                args[i - 1].o = MVM_nativecall_make_carray(tc, type,
271
0
                    dcbArgPointer(cb_args));
272
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
273
0
                num_roots++;
274
0
                break;
275
0
            case MVM_NATIVECALL_ARG_CUNION:
276
0
                args[i - 1].o = MVM_nativecall_make_cunion(tc, type,
277
0
                    dcbArgPointer(cb_args));
278
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
279
0
                num_roots++;
280
0
                break;
281
0
            case MVM_NATIVECALL_ARG_CALLBACK:
282
0
                /* TODO: A callback -return- value means that we have a C method
283
0
                * that needs to be wrapped similarly to a is native(...) Perl 6
284
0
                * sub. */
285
0
                dcbArgPointer(cb_args);
286
0
                args[i - 1].o = type;
287
0
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
288
0
                num_roots++;
289
0
                break;
290
0
            case MVM_NATIVECALL_ARG_UCHAR:
291
0
                args[i - 1].i64 = dcbArgUChar(cb_args);
292
0
                break;
293
0
            case MVM_NATIVECALL_ARG_USHORT:
294
0
                args[i - 1].i64 = dcbArgUShort(cb_args);
295
0
                break;
296
0
            case MVM_NATIVECALL_ARG_UINT:
297
0
                args[i - 1].i64 = dcbArgUInt(cb_args);
298
0
                break;
299
0
            case MVM_NATIVECALL_ARG_ULONG:
300
0
                args[i - 1].i64 = dcbArgULong(cb_args);
301
0
                break;
302
0
            case MVM_NATIVECALL_ARG_ULONGLONG:
303
0
                args[i - 1].i64 = dcbArgULongLong(cb_args);
304
0
                break;
305
0
            default:
306
0
                MVM_exception_throw_adhoc(tc,
307
0
                    "Internal error: unhandled dyncall callback argument type");
308
0
        }
309
0
    }
310
0
311
0
    /* Call into a nested interpreter (since we already are in one). Need to
312
0
     * save a bunch of state around each side of this. */
313
0
    cid.invokee = data->target;
314
0
    cid.args    = args;
315
0
    cid.cs      = data->cs;
316
0
    {
317
0
        MVMuint8 **backup_interp_cur_op         = tc->interp_cur_op;
318
0
        MVMuint8 **backup_interp_bytecode_start = tc->interp_bytecode_start;
319
0
        MVMRegister **backup_interp_reg_base    = tc->interp_reg_base;
320
0
        MVMCompUnit **backup_interp_cu          = tc->interp_cu;
321
0
        MVMFrame *backup_cur_frame              = MVM_frame_force_to_heap(tc, tc->cur_frame);
322
0
        MVMFrame *backup_thread_entry_frame     = tc->thread_entry_frame;
323
0
        MVMROOT(tc, backup_cur_frame, {
324
0
        MVMROOT(tc, backup_thread_entry_frame, {
325
0
            MVMuint32 backup_mark                   = MVM_gc_root_temp_mark(tc);
326
0
            jmp_buf backup_interp_jump;
327
0
            memcpy(backup_interp_jump, tc->interp_jump, sizeof(jmp_buf));
328
0
329
0
            tc->cur_frame->return_value = &res;
330
0
            tc->cur_frame->return_type  = MVM_RETURN_OBJ;
331
0
            MVM_interp_run(tc, callback_invoke, &cid);
332
0
333
0
            tc->interp_cur_op         = backup_interp_cur_op;
334
0
            tc->interp_bytecode_start = backup_interp_bytecode_start;
335
0
            tc->interp_reg_base       = backup_interp_reg_base;
336
0
            tc->interp_cu             = backup_interp_cu;
337
0
            tc->cur_frame             = backup_cur_frame;
338
0
            tc->current_frame_nr      = backup_cur_frame->sequence_nr;
339
0
            tc->thread_entry_frame    = backup_thread_entry_frame;
340
0
            memcpy(tc->interp_jump, backup_interp_jump, sizeof(jmp_buf));
341
0
            MVM_gc_root_temp_mark_reset(tc, backup_mark);
342
0
        });
343
0
        });
344
0
    }
345
0
346
0
    /* Handle return value. */
347
0
    if (res.o) {
348
0
        MVMContainerSpec const *contspec = STABLE(res.o)->container_spec;
349
0
        if (contspec && contspec->fetch_never_invokes)
350
0
            contspec->fetch(data->tc, res.o, &res);
351
0
    }
352
0
    switch (data->typeinfos[0] & MVM_NATIVECALL_ARG_TYPE_MASK) {
353
0
        case MVM_NATIVECALL_ARG_VOID:
354
0
            break;
355
0
        case MVM_NATIVECALL_ARG_CHAR:
356
0
            cb_result->c = (signed char)MVM_nativecall_unmarshal_char(data->tc, res.o);
357
0
            break;
358
0
        case MVM_NATIVECALL_ARG_SHORT:
359
0
            cb_result->s = MVM_nativecall_unmarshal_short(data->tc, res.o);
360
0
            break;
361
0
        case MVM_NATIVECALL_ARG_INT:
362
0
            cb_result->i = MVM_nativecall_unmarshal_int(data->tc, res.o);
363
0
            break;
364
0
        case MVM_NATIVECALL_ARG_LONG:
365
0
            cb_result->j = MVM_nativecall_unmarshal_long(data->tc, res.o);
366
0
            break;
367
0
        case MVM_NATIVECALL_ARG_LONGLONG:
368
0
            cb_result->l = MVM_nativecall_unmarshal_longlong(data->tc, res.o);
369
0
            break;
370
0
        case MVM_NATIVECALL_ARG_FLOAT:
371
0
            cb_result->f = MVM_nativecall_unmarshal_float(data->tc, res.o);
372
0
            break;
373
0
        case MVM_NATIVECALL_ARG_DOUBLE:
374
0
            cb_result->d = MVM_nativecall_unmarshal_double(data->tc, res.o);
375
0
            break;
376
0
        case MVM_NATIVECALL_ARG_ASCIISTR:
377
0
        case MVM_NATIVECALL_ARG_UTF8STR:
378
0
        case MVM_NATIVECALL_ARG_UTF16STR:
379
0
            cb_result->Z = MVM_nativecall_unmarshal_string(data->tc, res.o, data->typeinfos[0], NULL);
380
0
            break;
381
0
        case MVM_NATIVECALL_ARG_CSTRUCT:
382
0
            cb_result->p = MVM_nativecall_unmarshal_cstruct(data->tc, res.o);
383
0
            break;
384
0
        case MVM_NATIVECALL_ARG_CPOINTER:
385
0
            cb_result->p = MVM_nativecall_unmarshal_cpointer(data->tc, res.o);
386
0
            break;
387
0
        case MVM_NATIVECALL_ARG_CARRAY:
388
0
            cb_result->p = MVM_nativecall_unmarshal_carray(data->tc, res.o);
389
0
            break;
390
0
        case MVM_NATIVECALL_ARG_CUNION:
391
0
            cb_result->p = MVM_nativecall_unmarshal_cunion(data->tc, res.o);
392
0
            break;
393
0
        case MVM_NATIVECALL_ARG_VMARRAY:
394
0
            cb_result->p = MVM_nativecall_unmarshal_vmarray(data->tc, res.o);
395
0
            break;
396
0
        case MVM_NATIVECALL_ARG_CALLBACK:
397
0
            cb_result->p = unmarshal_callback(data->tc, res.o, data->types[0]);
398
0
            break;
399
0
        case MVM_NATIVECALL_ARG_UCHAR:
400
0
            cb_result->c = MVM_nativecall_unmarshal_uchar(data->tc, res.o);
401
0
            break;
402
0
        case MVM_NATIVECALL_ARG_USHORT:
403
0
            cb_result->s = MVM_nativecall_unmarshal_ushort(data->tc, res.o);
404
0
            break;
405
0
        case MVM_NATIVECALL_ARG_UINT:
406
0
            cb_result->i = MVM_nativecall_unmarshal_uint(data->tc, res.o);
407
0
            break;
408
0
        case MVM_NATIVECALL_ARG_ULONG:
409
0
            cb_result->j = MVM_nativecall_unmarshal_ulong(data->tc, res.o);
410
0
            break;
411
0
        case MVM_NATIVECALL_ARG_ULONGLONG:
412
0
            cb_result->l = MVM_nativecall_unmarshal_ulonglong(data->tc, res.o);
413
0
            break;
414
0
        default:
415
0
            MVM_exception_throw_adhoc(data->tc,
416
0
                "Internal error: unhandled dyncall callback return type");
417
0
    }
418
0
419
0
    /* Clean up. */
420
0
    MVM_gc_root_temp_pop_n(tc, num_roots);
421
0
    MVM_free(args);
422
0
423
0
    /* Re-block GC if needed, so other threads will be able to collect. */
424
0
    if (was_blocked)
425
0
        MVM_gc_mark_thread_blocked(tc);
426
0
427
0
    /* Indicate what we're producing as a result. */
428
0
    return get_signature_char(data->typeinfos[0]);
429
0
}
430
431
0
#define handle_arg(what, cont_X, dc_type, reg_slot, dc_fun, unmarshal_fun) do { \
432
0
    MVMRegister r; \
433
0
    if ((arg_types[i] & MVM_NATIVECALL_ARG_RW_MASK) == MVM_NATIVECALL_ARG_RW) { \
434
0
        if (MVM_6model_container_is ## cont_X(tc, value)) { \
435
0
            dc_type *rw = (dc_type *)MVM_malloc(sizeof(dc_type)); \
436
0
            MVM_6model_container_de ## cont_X(tc, value, &r); \
437
0
            *rw = (dc_type)r. reg_slot ; \
438
0
            if (!free_rws) \
439
0
                free_rws = (void **)MVM_malloc(num_args * sizeof(void *)); \
440
0
            free_rws[num_rws] = rw; \
441
0
            num_rws++; \
442
0
            dcArgPointer(vm, rw); \
443
0
        } \
444
0
        else \
445
0
            MVM_exception_throw_adhoc(tc, \
446
0
                "Native call expected argument that references a native %s, but got %s", \
447
0
                what, REPR(value)->name); \
448
0
    } \
449
0
    else { \
450
0
        if (value && IS_CONCRETE(value) && STABLE(value)->container_spec) { \
451
0
            STABLE(value)->container_spec->fetch(tc, value, &r); \
452
0
            dc_fun(vm, unmarshal_fun(tc, r.o)); \
453
0
        } \
454
0
        else { \
455
0
            dc_fun(vm, unmarshal_fun(tc, value)); \
456
0
        } \
457
0
    } \
458
0
} while (0)
459
460
MVMObject * MVM_nativecall_invoke(MVMThreadContext *tc, MVMObject *res_type,
461
3
        MVMObject *site, MVMObject *args) {
462
3
    MVMObject  *result = NULL;
463
3
    char      **free_strs = NULL;
464
3
    void      **free_rws  = NULL;
465
3
    MVMint16    num_strs  = 0;
466
3
    MVMint16    num_rws   = 0;
467
3
    MVMint16    i;
468
3
469
3
    /* Get native call body, so we can locate the call info. Read out all we
470
3
     * shall need, since later we may allocate a result and and move it. */
471
3
    MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site);
472
3
    MVMint16  num_args    = body->num_args;
473
3
    MVMint16 *arg_types   = body->arg_types;
474
3
    MVMint16  ret_type    = body->ret_type;
475
3
    void     *entry_point = body->entry_point;
476
3
    void     *ptr         = NULL;
477
3
478
3
    /* Create and set up call VM. */
479
3
    DCCallVM *vm = dcNewCallVM(8192);
480
3
    dcMode(vm, body->convention);
481
3
    dcReset(vm);
482
3
483
3
    /* Process arguments. */
484
6
    for (i = 0; i < num_args; i++) {
485
3
        MVMObject *value = MVM_repr_at_pos_o(tc, args, i);
486
3
        switch (arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) {
487
0
            case MVM_NATIVECALL_ARG_CHAR:
488
0
                handle_arg("integer", cont_i, DCchar, i64, dcArgChar, MVM_nativecall_unmarshal_char);
489
0
                break;
490
0
            case MVM_NATIVECALL_ARG_SHORT:
491
0
                handle_arg("integer", cont_i, DCshort, i64, dcArgShort, MVM_nativecall_unmarshal_short);
492
0
                break;
493
0
            case MVM_NATIVECALL_ARG_INT:
494
0
                handle_arg("integer", cont_i, DCint, i64, dcArgInt, MVM_nativecall_unmarshal_int);
495
0
                break;
496
0
            case MVM_NATIVECALL_ARG_LONG:
497
0
                handle_arg("integer", cont_i, DClong, i64, dcArgLong, MVM_nativecall_unmarshal_long);
498
0
                break;
499
0
            case MVM_NATIVECALL_ARG_LONGLONG:
500
0
                handle_arg("integer", cont_i, DClonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_longlong);
501
0
                break;
502
0
            case MVM_NATIVECALL_ARG_FLOAT:
503
0
                handle_arg("number", cont_n, DCfloat, n64, dcArgFloat, MVM_nativecall_unmarshal_float);
504
0
                break;
505
0
            case MVM_NATIVECALL_ARG_DOUBLE:
506
0
                handle_arg("number", cont_n, DCdouble, n64, dcArgDouble, MVM_nativecall_unmarshal_double);
507
0
                break;
508
2
            case MVM_NATIVECALL_ARG_ASCIISTR:
509
2
            case MVM_NATIVECALL_ARG_UTF8STR:
510
2
            case MVM_NATIVECALL_ARG_UTF16STR:
511
2
                {
512
2
                    MVMint16 free = 0;
513
2
                    char *str = MVM_nativecall_unmarshal_string(tc, value, arg_types[i], &free);
514
2
                    if (free) {
515
2
                        if (!free_strs)
516
2
                            free_strs = (char**)MVM_malloc(num_args * sizeof(char *));
517
2
                        free_strs[num_strs] = str;
518
2
                        num_strs++;
519
2
                    }
520
2
                    dcArgPointer(vm, str);
521
2
                }
522
2
                break;
523
0
            case MVM_NATIVECALL_ARG_CSTRUCT:
524
0
                dcArgPointer(vm, MVM_nativecall_unmarshal_cstruct(tc, value));
525
0
                break;
526
0
            case MVM_NATIVECALL_ARG_CPPSTRUCT: {
527
0
                    /* We need to allocate the struct (THIS) for C++ constructor before passing it along. */
528
0
                    if (i == 0 && !IS_CONCRETE(value)) {
529
0
                        MVMCPPStructREPRData *repr_data = (MVMCPPStructREPRData *)STABLE(res_type)->REPR_data;
530
0
                        /* Allocate a full byte aligned area where the C++ structure fits into. */
531
0
                        ptr    = MVM_malloc(repr_data->struct_size > 0 ? repr_data->struct_size : 1);
532
0
                        result = MVM_nativecall_make_cppstruct(tc, res_type, ptr);
533
0
534
0
                        dcArgPointer(vm, ptr);
535
0
                    }
536
0
                    else {
537
0
                        dcArgPointer(vm, MVM_nativecall_unmarshal_cppstruct(tc, value));
538
0
                    }
539
0
                }
540
0
                break;
541
1
            case MVM_NATIVECALL_ARG_CPOINTER:
542
1
                if ((arg_types[i] & MVM_NATIVECALL_ARG_RW_MASK) == MVM_NATIVECALL_ARG_RW) {
543
0
                    DCpointer *rw = (DCpointer *)MVM_malloc(sizeof(DCpointer *));
544
0
                    *rw           = (DCpointer)MVM_nativecall_unmarshal_cpointer(tc, value);
545
0
                    if (!free_rws)
546
0
                        free_rws = (void **)MVM_malloc(num_args * sizeof(void *));
547
0
                    free_rws[num_rws] = rw;
548
0
                    num_rws++;
549
0
                    dcArgPointer(vm, rw);
550
0
                }
551
1
                else {
552
1
                    dcArgPointer(vm, MVM_nativecall_unmarshal_cpointer(tc, value));
553
1
                }
554
1
                break;
555
0
            case MVM_NATIVECALL_ARG_CARRAY:
556
0
                dcArgPointer(vm, MVM_nativecall_unmarshal_carray(tc, value));
557
0
                break;
558
0
            case MVM_NATIVECALL_ARG_CUNION:
559
0
                dcArgPointer(vm, MVM_nativecall_unmarshal_cunion(tc, value));
560
0
                break;
561
0
            case MVM_NATIVECALL_ARG_VMARRAY:
562
0
                dcArgPointer(vm, MVM_nativecall_unmarshal_vmarray(tc, value));
563
0
                break;
564
0
            case MVM_NATIVECALL_ARG_CALLBACK:
565
0
                dcArgPointer(vm, unmarshal_callback(tc, value, body->arg_info[i]));
566
0
                break;
567
0
            case MVM_NATIVECALL_ARG_UCHAR:
568
0
                handle_arg("integer", cont_i, DCuchar, i64, dcArgChar, MVM_nativecall_unmarshal_uchar);
569
0
                break;
570
0
            case MVM_NATIVECALL_ARG_USHORT:
571
0
                handle_arg("integer", cont_i, DCushort, i64, dcArgShort, MVM_nativecall_unmarshal_ushort);
572
0
                break;
573
0
            case MVM_NATIVECALL_ARG_UINT:
574
0
                handle_arg("integer", cont_i, DCuint, i64, dcArgInt, MVM_nativecall_unmarshal_uint);
575
0
                break;
576
0
            case MVM_NATIVECALL_ARG_ULONG:
577
0
                handle_arg("integer", cont_i, DCulong, i64, dcArgLong, MVM_nativecall_unmarshal_ulong);
578
0
                break;
579
0
            case MVM_NATIVECALL_ARG_ULONGLONG:
580
0
                handle_arg("integer", cont_i, DCulonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_ulonglong);
581
0
                break;
582
0
            default:
583
0
                MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall argument type");
584
3
        }
585
3
    }
586
3
587
3
    MVMROOT(tc, args, {
588
3
    MVMROOT(tc, res_type, {
589
3
        MVM_gc_mark_thread_blocked(tc);
590
3
        if (result) {
591
3
            /* We are calling a C++ constructor so we hand back the invocant (THIS) we recorded earlier. */
592
3
            dcCallVoid(vm, body->entry_point);
593
3
            MVM_gc_mark_thread_unblocked(tc);
594
3
        }
595
3
        else {
596
3
            /* Call and process return values. */
597
3
            switch (ret_type & MVM_NATIVECALL_ARG_TYPE_MASK) {
598
3
                case MVM_NATIVECALL_ARG_VOID:
599
3
                    dcCallVoid(vm, entry_point);
600
3
                    MVM_gc_mark_thread_unblocked(tc);
601
3
                    result = res_type;
602
3
                    break;
603
3
                case MVM_NATIVECALL_ARG_CHAR: {
604
3
                    MVMint64 native_result = (signed char)dcCallChar(vm, entry_point);
605
3
                    MVM_gc_mark_thread_unblocked(tc);
606
3
                    result = MVM_nativecall_make_int(tc, res_type, native_result);
607
3
                    break;
608
3
                }
609
3
                case MVM_NATIVECALL_ARG_SHORT: {
610
3
                    MVMint64 native_result = dcCallShort(vm, entry_point);
611
3
                    MVM_gc_mark_thread_unblocked(tc);
612
3
                    result = MVM_nativecall_make_int(tc, res_type, native_result);
613
3
                    break;
614
3
                }
615
3
                case MVM_NATIVECALL_ARG_INT: {
616
3
                    MVMint64 native_result = dcCallInt(vm, entry_point);
617
3
                    MVM_gc_mark_thread_unblocked(tc);
618
3
                    result = MVM_nativecall_make_int(tc, res_type, native_result);
619
3
                    break;
620
3
                }
621
3
                case MVM_NATIVECALL_ARG_LONG: {
622
3
                    MVMint64 native_result = dcCallLong(vm, entry_point);
623
3
                    MVM_gc_mark_thread_unblocked(tc);
624
3
                    result = MVM_nativecall_make_int(tc, res_type, native_result);
625
3
                    break;
626
3
                }
627
3
                case MVM_NATIVECALL_ARG_LONGLONG: {
628
3
                    MVMint64 native_result = dcCallLongLong(vm, entry_point);
629
3
                    MVM_gc_mark_thread_unblocked(tc);
630
3
                    result = MVM_nativecall_make_int(tc, res_type, native_result);
631
3
                    break;
632
3
                }
633
3
                case MVM_NATIVECALL_ARG_FLOAT: {
634
3
                    MVMnum64 native_result = dcCallFloat(vm, entry_point);
635
3
                    MVM_gc_mark_thread_unblocked(tc);
636
3
                    result = MVM_nativecall_make_num(tc, res_type, native_result);
637
3
                    break;
638
3
                }
639
3
                case MVM_NATIVECALL_ARG_DOUBLE: {
640
3
                    MVMnum64 native_result = dcCallDouble(vm, entry_point);
641
3
                    MVM_gc_mark_thread_unblocked(tc);
642
3
                    result = MVM_nativecall_make_num(tc, res_type, native_result);
643
3
                    break;
644
3
                }
645
3
                case MVM_NATIVECALL_ARG_ASCIISTR:
646
3
                case MVM_NATIVECALL_ARG_UTF8STR:
647
3
                case MVM_NATIVECALL_ARG_UTF16STR: {
648
3
                    char *native_result = (char *)dcCallPointer(vm, entry_point);
649
3
                    MVM_gc_mark_thread_unblocked(tc);
650
3
                    result = MVM_nativecall_make_str(tc, res_type, body->ret_type,
651
3
                        native_result);
652
3
                    break;
653
3
                }
654
3
                case MVM_NATIVECALL_ARG_CSTRUCT: {
655
3
                    void *native_result = dcCallPointer(vm, body->entry_point);
656
3
                    MVM_gc_mark_thread_unblocked(tc);
657
3
                    result = MVM_nativecall_make_cstruct(tc, res_type, native_result);
658
3
                    break;
659
3
                }
660
3
                case MVM_NATIVECALL_ARG_CPPSTRUCT: {
661
3
                    void *native_result = dcCallPointer(vm, body->entry_point);
662
3
                    MVM_gc_mark_thread_unblocked(tc);
663
3
                    result = MVM_nativecall_make_cppstruct(tc, res_type, native_result);
664
3
                    break;
665
3
                }
666
3
                case MVM_NATIVECALL_ARG_CPOINTER: {
667
3
                    void *native_result = dcCallPointer(vm, body->entry_point);
668
3
                    MVM_gc_mark_thread_unblocked(tc);
669
3
                    result = MVM_nativecall_make_cpointer(tc, res_type, native_result);
670
3
                    break;
671
3
                }
672
3
                case MVM_NATIVECALL_ARG_CARRAY: {
673
3
                    void *native_result = dcCallPointer(vm, body->entry_point);
674
3
                    MVM_gc_mark_thread_unblocked(tc);
675
3
                    result = MVM_nativecall_make_carray(tc, res_type, native_result);
676
3
                    break;
677
3
                }
678
3
                case MVM_NATIVECALL_ARG_CUNION: {
679
3
                    void *native_result = dcCallPointer(vm, body->entry_point);
680
3
                    MVM_gc_mark_thread_unblocked(tc);
681
3
                    result = MVM_nativecall_make_cunion(tc, res_type, native_result);
682
3
                    break;
683
3
                }
684
3
                case MVM_NATIVECALL_ARG_CALLBACK:
685
3
                    /* TODO: A callback -return- value means that we have a C method
686
3
                    * that needs to be wrapped similarly to a is native(...) Perl 6
687
3
                    * sub. */
688
3
                    dcCallPointer(vm, body->entry_point);
689
3
                    MVM_gc_mark_thread_unblocked(tc);
690
3
                    result = res_type;
691
3
                    break;
692
3
                case MVM_NATIVECALL_ARG_UCHAR: {
693
3
                    MVMuint64 native_result = (DCuchar)dcCallChar(vm, entry_point);
694
3
                    MVM_gc_mark_thread_unblocked(tc);
695
3
                    result = MVM_nativecall_make_uint(tc, res_type, native_result);
696
3
                    break;
697
3
                }
698
3
                case MVM_NATIVECALL_ARG_USHORT: {
699
3
                    MVMuint64 native_result = (DCushort)dcCallShort(vm, entry_point);
700
3
                    MVM_gc_mark_thread_unblocked(tc);
701
3
                    result = MVM_nativecall_make_uint(tc, res_type, native_result);
702
3
                    break;
703
3
                }
704
3
                case MVM_NATIVECALL_ARG_UINT: {
705
3
                    MVMuint64 native_result = (DCuint)dcCallInt(vm, entry_point);
706
3
                    MVM_gc_mark_thread_unblocked(tc);
707
3
                    result = MVM_nativecall_make_uint(tc, res_type, native_result);
708
3
                    break;
709
3
                }
710
3
                case MVM_NATIVECALL_ARG_ULONG: {
711
3
                    MVMuint64 native_result = (DCulong)dcCallLong(vm, entry_point);
712
3
                    MVM_gc_mark_thread_unblocked(tc);
713
3
                    result = MVM_nativecall_make_uint(tc, res_type, native_result);
714
3
                    break;
715
3
                }
716
3
                case MVM_NATIVECALL_ARG_ULONGLONG: {
717
3
                    MVMuint64 native_result = (DCulonglong)dcCallLongLong(vm, entry_point);
718
3
                    MVM_gc_mark_thread_unblocked(tc);
719
3
                    result = MVM_nativecall_make_uint(tc, res_type, native_result);
720
3
                    break;
721
3
                }
722
3
                default:
723
3
                    MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall return type");
724
3
            }
725
3
        }
726
3
    });
727
3
    });
728
3
729
3
    num_rws = 0;
730
6
    for (i = 0; i < num_args; i++) {
731
3
        MVMObject *value = MVM_repr_at_pos_o(tc, args, i);
732
3
        if ((arg_types[i] & MVM_NATIVECALL_ARG_RW_MASK) == MVM_NATIVECALL_ARG_RW) {
733
0
            switch (arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) {
734
0
                case MVM_NATIVECALL_ARG_CHAR:
735
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCchar *)free_rws[num_rws]);
736
0
                    break;
737
0
                case MVM_NATIVECALL_ARG_SHORT:
738
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCshort *)free_rws[num_rws]);
739
0
                    break;
740
0
                case MVM_NATIVECALL_ARG_INT:
741
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCint *)free_rws[num_rws]);
742
0
                    break;
743
0
                case MVM_NATIVECALL_ARG_LONG:
744
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DClong *)free_rws[num_rws]);
745
0
                    break;
746
0
                case MVM_NATIVECALL_ARG_LONGLONG:
747
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DClonglong *)free_rws[num_rws]);
748
0
                    break;
749
0
                case MVM_NATIVECALL_ARG_FLOAT:
750
0
                    MVM_6model_container_assign_n(tc, value, (MVMnum64)*(DCfloat *)free_rws[num_rws]);
751
0
                    break;
752
0
                case MVM_NATIVECALL_ARG_DOUBLE:
753
0
                    MVM_6model_container_assign_n(tc, value, (MVMnum64)*(DCdouble *)free_rws[num_rws]);
754
0
                    break;
755
0
                case MVM_NATIVECALL_ARG_UCHAR:
756
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCuchar *)free_rws[num_rws]);
757
0
                    break;
758
0
                case MVM_NATIVECALL_ARG_USHORT:
759
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCushort *)free_rws[num_rws]);
760
0
                    break;
761
0
                case MVM_NATIVECALL_ARG_UINT:
762
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCuint *)free_rws[num_rws]);
763
0
                    break;
764
0
                case MVM_NATIVECALL_ARG_ULONG:
765
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCulong *)free_rws[num_rws]);
766
0
                    break;
767
0
                case MVM_NATIVECALL_ARG_ULONGLONG:
768
0
                    MVM_6model_container_assign_i(tc, value, (MVMint64)*(DCulonglong *)free_rws[num_rws]);
769
0
                    break;
770
0
                case MVM_NATIVECALL_ARG_CPOINTER:
771
0
                    REPR(value)->box_funcs.set_int(tc, STABLE(value), value, OBJECT_BODY(value),
772
0
                        (MVMint64)*(DCpointer *)free_rws[num_rws]);
773
0
                    break;
774
0
                default:
775
0
                    MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall argument type");
776
0
            }
777
0
            num_rws++;
778
0
        }
779
3
        /* Perform CArray/CStruct write barriers. */
780
3
        MVM_nativecall_refresh(tc, value);
781
3
    }
782
3
783
3
    /* Free any memory that we need to. */
784
3
    if (free_strs) {
785
4
        for (i = 0; i < num_strs; i++)
786
2
            MVM_free(free_strs[i]);
787
2
        MVM_free(free_strs);
788
2
    }
789
3
790
3
    if (free_rws) {
791
0
        for (i = 0; i < num_rws; i++)
792
0
            MVM_free(free_rws[i]);
793
0
        MVM_free(free_rws);
794
0
    }
795
3
796
3
    /* Finally, free call VM. */
797
3
    dcFree(vm);
798
3
799
3
    return result;
800
3
}