Coverage Report

Created: 2018-07-03 15:31

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