Coverage Report

Created: 2017-04-15 07:07

/home/travis/build/MoarVM/MoarVM/src/core/nativecall.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#ifndef _WIN32
3
#include <dlfcn.h>
4
#endif
5
6
/* Grabs a NativeCall body. */
7
6
MVMNativeCallBody * MVM_nativecall_get_nc_body(MVMThreadContext *tc, MVMObject *obj) {
8
6
    if (REPR(obj)->ID == MVM_REPR_ID_MVMNativeCall)
9
6
        return (MVMNativeCallBody *)OBJECT_BODY(obj);
10
6
    else
11
0
        return (MVMNativeCallBody *)REPR(obj)->box_funcs.get_boxed_ref(tc,
12
0
            STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_MVMNativeCall);
13
6
}
14
15
/* Gets the flag for whether to free a string after a call or not. */
16
2
static MVMint16 get_str_free_flag(MVMThreadContext *tc, MVMObject *info) {
17
2
    MVMString *flag = tc->instance->str_consts.free_str;
18
2
    if (MVM_repr_exists_key(tc, info, flag))
19
2
        if (!MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag)))
20
0
            return MVM_NATIVECALL_ARG_NO_FREE_STR;
21
2
    return MVM_NATIVECALL_ARG_FREE_STR;
22
2
}
23
24
/* Gets the flag for whether to free a string after a call or not. */
25
2
static MVMint16 get_rw_flag(MVMThreadContext *tc, MVMObject *info) {
26
2
    MVMString *flag = tc->instance->str_consts.rw;
27
2
    if (MVM_repr_exists_key(tc, info, flag)) {
28
0
        if (MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag)))
29
0
            return MVM_NATIVECALL_ARG_RW;
30
0
    }
31
2
    return MVM_NATIVECALL_ARG_NO_RW;
32
2
}
33
34
/* Takes a hash describing a type hands back an argument type code. */
35
6
MVMint16 MVM_nativecall_get_arg_type(MVMThreadContext *tc, MVMObject *info, MVMint16 is_return) {
36
6
    MVMString *typename = MVM_repr_get_str(tc, MVM_repr_at_key_o(tc, info,
37
6
        tc->instance->str_consts.type));
38
6
    char *ctypename = MVM_string_utf8_encode_C_string(tc, typename);
39
6
    MVMint16 result;
40
6
    if (strcmp(ctypename, "void") == 0) {
41
2
        if (!is_return) {
42
0
            MVM_free(ctypename);
43
0
            MVM_exception_throw_adhoc(tc,
44
0
                "Cannot use 'void' type except for on native call return values");
45
0
        }
46
2
        result = MVM_NATIVECALL_ARG_VOID;
47
2
    }
48
4
    else if (strcmp(ctypename, "char") == 0)
49
0
        result = MVM_NATIVECALL_ARG_CHAR | get_rw_flag(tc, info);
50
4
    else if (strcmp(ctypename, "short") == 0)
51
0
        result = MVM_NATIVECALL_ARG_SHORT | get_rw_flag(tc, info);
52
4
    else if (strcmp(ctypename, "int") == 0)
53
0
        result = MVM_NATIVECALL_ARG_INT | get_rw_flag(tc, info);
54
4
    else if (strcmp(ctypename, "long") == 0)
55
0
        result = MVM_NATIVECALL_ARG_LONG | get_rw_flag(tc, info);
56
4
    else if (strcmp(ctypename, "longlong") == 0)
57
0
        result = MVM_NATIVECALL_ARG_LONGLONG | get_rw_flag(tc, info);
58
4
    else if (strcmp(ctypename, "uchar") == 0)
59
0
        result = MVM_NATIVECALL_ARG_UCHAR | get_rw_flag(tc, info);
60
4
    else if (strcmp(ctypename, "ushort") == 0)
61
0
        result = MVM_NATIVECALL_ARG_USHORT | get_rw_flag(tc, info);
62
4
    else if (strcmp(ctypename, "uint") == 0)
63
0
        result = MVM_NATIVECALL_ARG_UINT | get_rw_flag(tc, info);
64
4
    else if (strcmp(ctypename, "ulong") == 0)
65
0
        result = MVM_NATIVECALL_ARG_ULONG | get_rw_flag(tc, info);
66
4
    else if (strcmp(ctypename, "ulonglong") == 0)
67
0
        result = MVM_NATIVECALL_ARG_ULONGLONG | get_rw_flag(tc, info);
68
4
    else if (strcmp(ctypename, "float") == 0)
69
0
        result = MVM_NATIVECALL_ARG_FLOAT | get_rw_flag(tc, info);
70
4
    else if (strcmp(ctypename, "double") == 0)
71
0
        result = MVM_NATIVECALL_ARG_DOUBLE | get_rw_flag(tc, info);
72
4
    else if (strcmp(ctypename, "asciistr") == 0)
73
0
        result = MVM_NATIVECALL_ARG_ASCIISTR | get_str_free_flag(tc, info);
74
4
    else if (strcmp(ctypename, "utf8str") == 0)
75
2
        result = MVM_NATIVECALL_ARG_UTF8STR | get_str_free_flag(tc, info);
76
2
    else if (strcmp(ctypename, "utf16str") == 0)
77
0
        result = MVM_NATIVECALL_ARG_UTF16STR | get_str_free_flag(tc, info);
78
2
    else if (strcmp(ctypename, "cstruct") == 0)
79
0
        result = MVM_NATIVECALL_ARG_CSTRUCT;
80
2
    else if (strcmp(ctypename, "cppstruct") == 0)
81
0
        result = MVM_NATIVECALL_ARG_CPPSTRUCT;
82
2
    else if (strcmp(ctypename, "cpointer") == 0)
83
2
        result = MVM_NATIVECALL_ARG_CPOINTER | get_rw_flag(tc, info);
84
0
    else if (strcmp(ctypename, "carray") == 0)
85
0
        result = MVM_NATIVECALL_ARG_CARRAY;
86
0
    else if (strcmp(ctypename, "cunion") == 0)
87
0
        result = MVM_NATIVECALL_ARG_CUNION;
88
0
    else if (strcmp(ctypename, "vmarray") == 0)
89
0
        result = MVM_NATIVECALL_ARG_VMARRAY;
90
0
    else if (strcmp(ctypename, "callback") == 0)
91
0
        result = MVM_NATIVECALL_ARG_CALLBACK;
92
0
    else {
93
0
        char *waste[] = { ctypename, NULL };
94
0
        MVM_exception_throw_adhoc_free(tc, waste, "Unknown type '%s' used for native call", ctypename);
95
0
    }
96
6
    MVM_free(ctypename);
97
6
    return result;
98
6
}
99
100
0
MVMObject * MVM_nativecall_make_int(MVMThreadContext *tc, MVMObject *type, MVMint64 value) {
101
0
    return type ? MVM_repr_box_int(tc, type, value) : NULL;
102
0
}
103
104
0
MVMObject * MVM_nativecall_make_uint(MVMThreadContext *tc, MVMObject *type, MVMuint64 value) {
105
0
    return type ? MVM_repr_box_int(tc, type, (MVMint64)value) : NULL;
106
0
}
107
108
0
MVMObject * MVM_nativecall_make_num(MVMThreadContext *tc, MVMObject *type, MVMnum64 value) {
109
0
    return type ? MVM_repr_box_num(tc, type, value) : NULL;
110
0
}
111
112
0
MVMObject * MVM_nativecall_make_str(MVMThreadContext *tc, MVMObject *type, MVMint16 ret_type, char *cstring) {
113
0
    MVMObject *result = type;
114
0
    if (cstring && type) {
115
0
        MVMString *value;
116
0
117
0
        MVM_gc_root_temp_push(tc, (MVMCollectable **)&type);
118
0
119
0
        switch (ret_type & MVM_NATIVECALL_ARG_TYPE_MASK) {
120
0
            case MVM_NATIVECALL_ARG_ASCIISTR:
121
0
                value = MVM_string_ascii_decode(tc, tc->instance->VMString, cstring, strlen(cstring));
122
0
                break;
123
0
            case MVM_NATIVECALL_ARG_UTF8STR:
124
0
                value = MVM_string_utf8_decode(tc, tc->instance->VMString, cstring, strlen(cstring));
125
0
                break;
126
0
            case MVM_NATIVECALL_ARG_UTF16STR:
127
0
                value = MVM_string_utf16_decode(tc, tc->instance->VMString, cstring, strlen(cstring));
128
0
                break;
129
0
            default:
130
0
                MVM_exception_throw_adhoc(tc, "Internal error: unhandled encoding");
131
0
        }
132
0
133
0
        MVM_gc_root_temp_pop(tc);
134
0
        result = MVM_repr_box_str(tc, type, value);
135
0
        if (ret_type & MVM_NATIVECALL_ARG_FREE_STR)
136
0
            MVM_free(cstring);
137
0
    }
138
0
139
0
    return result;
140
0
}
141
142
/* Constructs a boxed result using a CStruct REPR type. */
143
0
MVMObject * MVM_nativecall_make_cstruct(MVMThreadContext *tc, MVMObject *type, void *cstruct) {
144
0
    MVMObject *result = type;
145
0
    if (cstruct && type) {
146
0
        MVMCStructREPRData *repr_data = (MVMCStructREPRData *)STABLE(type)->REPR_data;
147
0
        if (REPR(type)->ID != MVM_REPR_ID_MVMCStruct)
148
0
            MVM_exception_throw_adhoc(tc,
149
0
                "Native call expected return type with CStruct representation, but got a %s (%s)", REPR(type)->name, STABLE(type)->debug_name);
150
0
        result = REPR(type)->allocate(tc, STABLE(type));
151
0
        ((MVMCStruct *)result)->body.cstruct = cstruct;
152
0
        if (repr_data->num_child_objs)
153
0
            ((MVMCStruct *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *));
154
0
    }
155
0
    return result;
156
0
}
157
158
/* Constructs a boxed result using a CUnion REPR type. */
159
0
MVMObject * MVM_nativecall_make_cunion(MVMThreadContext *tc, MVMObject *type, void *cunion) {
160
0
    MVMObject *result = type;
161
0
    if (cunion && type) {
162
0
        MVMCUnionREPRData *repr_data = (MVMCUnionREPRData *)STABLE(type)->REPR_data;
163
0
        if (REPR(type)->ID != MVM_REPR_ID_MVMCUnion)
164
0
            MVM_exception_throw_adhoc(tc,
165
0
                "Native call expected return type with CUnion representation, but got a %s (%s)", REPR(type)->name, STABLE(type)->debug_name);
166
0
        result = REPR(type)->allocate(tc, STABLE(type));
167
0
        ((MVMCUnion *)result)->body.cunion = cunion;
168
0
        if (repr_data->num_child_objs)
169
0
            ((MVMCUnion *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *));
170
0
    }
171
0
    return result;
172
0
}
173
174
0
MVMObject * MVM_nativecall_make_cppstruct(MVMThreadContext *tc, MVMObject *type, void *cppstruct) {
175
0
    MVMObject *result = type;
176
0
    if (cppstruct && type) {
177
0
        MVMCPPStructREPRData *repr_data = (MVMCPPStructREPRData *)STABLE(type)->REPR_data;
178
0
        if (REPR(type)->ID != MVM_REPR_ID_MVMCPPStruct)
179
0
            MVM_exception_throw_adhoc(tc,
180
0
                "Native call expected return type with CPPStruct representation, but got a %s (%s)", REPR(type)->name, STABLE(type)->debug_name);
181
0
        result = REPR(type)->allocate(tc, STABLE(type));
182
0
        ((MVMCPPStruct *)result)->body.cppstruct = cppstruct;
183
0
        if (repr_data->num_child_objs)
184
0
            ((MVMCPPStruct *)result)->body.child_objs = MVM_calloc(repr_data->num_child_objs, sizeof(MVMObject *));
185
0
    }
186
0
    return result;
187
0
}
188
189
/* Constructs a boxed result using a CPointer REPR type. */
190
1
MVMObject * MVM_nativecall_make_cpointer(MVMThreadContext *tc, MVMObject *type, void *ptr) {
191
1
    MVMObject *result = type;
192
1
    if (ptr && type) {
193
1
        if (REPR(type)->ID != MVM_REPR_ID_MVMCPointer)
194
0
            MVM_exception_throw_adhoc(tc,
195
0
                "Native call expected return type with CPointer representation, but got a %s (%s)", REPR(type)->name, STABLE(type)->debug_name);
196
1
        result = REPR(type)->allocate(tc, STABLE(type));
197
1
        ((MVMCPointer *)result)->body.ptr = ptr;
198
1
    }
199
1
    return result;
200
1
}
201
202
/* Constructs a boxed result using a CArray REPR type. */
203
0
MVMObject * MVM_nativecall_make_carray(MVMThreadContext *tc, MVMObject *type, void *carray) {
204
0
    MVMObject *result = type;
205
0
    if (carray && type) {
206
0
        if (REPR(type)->ID != MVM_REPR_ID_MVMCArray)
207
0
            MVM_exception_throw_adhoc(tc,
208
0
                "Native call expected return type with CArray representation, but got a %s (%s)", REPR(type)->name, STABLE(type)->debug_name);
209
0
        result = REPR(type)->allocate(tc, STABLE(type));
210
0
        ((MVMCArray *)result)->body.storage = carray;
211
0
    }
212
0
    return result;
213
0
}
214
215
0
signed char MVM_nativecall_unmarshal_char(MVMThreadContext *tc, MVMObject *value) {
216
0
    return (signed char)MVM_repr_get_int(tc, value);
217
0
}
218
219
0
signed short MVM_nativecall_unmarshal_short(MVMThreadContext *tc, MVMObject *value) {
220
0
    return (signed short)MVM_repr_get_int(tc, value);
221
0
}
222
223
0
signed int MVM_nativecall_unmarshal_int(MVMThreadContext *tc, MVMObject *value) {
224
0
    return (signed int)MVM_repr_get_int(tc, value);
225
0
}
226
227
0
signed long MVM_nativecall_unmarshal_long(MVMThreadContext *tc, MVMObject *value) {
228
0
    return (signed long)MVM_repr_get_int(tc, value);
229
0
}
230
231
0
signed long long MVM_nativecall_unmarshal_longlong(MVMThreadContext *tc, MVMObject *value) {
232
0
    return (signed long long)MVM_repr_get_int(tc, value);
233
0
}
234
235
0
unsigned char MVM_nativecall_unmarshal_uchar(MVMThreadContext *tc, MVMObject *value) {
236
0
    return (unsigned char)MVM_repr_get_int(tc, value);
237
0
}
238
239
0
unsigned short MVM_nativecall_unmarshal_ushort(MVMThreadContext *tc, MVMObject *value) {
240
0
    return (unsigned short)MVM_repr_get_int(tc, value);
241
0
}
242
243
0
unsigned int MVM_nativecall_unmarshal_uint(MVMThreadContext *tc, MVMObject *value) {
244
0
    return (unsigned int)MVM_repr_get_int(tc, value);
245
0
}
246
247
0
unsigned long MVM_nativecall_unmarshal_ulong(MVMThreadContext *tc, MVMObject *value) {
248
0
    return (unsigned long)MVM_repr_get_int(tc, value);
249
0
}
250
251
0
unsigned long long MVM_nativecall_unmarshal_ulonglong(MVMThreadContext *tc, MVMObject *value) {
252
0
    return (unsigned long long)MVM_repr_get_int(tc, value);
253
0
}
254
255
0
float MVM_nativecall_unmarshal_float(MVMThreadContext *tc, MVMObject *value) {
256
0
    return (float)MVM_repr_get_num(tc, value);
257
0
}
258
259
0
double MVM_nativecall_unmarshal_double(MVMThreadContext *tc, MVMObject *value) {
260
0
    return (double)MVM_repr_get_num(tc, value);
261
0
}
262
263
2
char * MVM_nativecall_unmarshal_string(MVMThreadContext *tc, MVMObject *value, MVMint16 type, MVMint16 *free) {
264
2
    if (IS_CONCRETE(value)) {
265
2
        MVMString *value_str = MVM_repr_get_str(tc, value);
266
2
267
2
        /* Encode string. */
268
2
        char *str;
269
2
        switch (type & MVM_NATIVECALL_ARG_TYPE_MASK) {
270
0
            case MVM_NATIVECALL_ARG_ASCIISTR:
271
0
                str = MVM_string_ascii_encode_any(tc, value_str);
272
0
                break;
273
0
            case MVM_NATIVECALL_ARG_UTF16STR:
274
0
                str = MVM_string_utf16_encode(tc, value_str, 0);
275
0
                break;
276
2
            default:
277
2
                str = MVM_string_utf8_encode_C_string(tc, value_str);
278
2
        }
279
2
280
2
        /* Set whether to free it or not. */
281
2
        if (free) {
282
2
            if (REPR(value)->ID == MVM_REPR_ID_MVMCStr)
283
0
                *free = 0; /* Manually managed. */
284
2
            else if (free && type & MVM_NATIVECALL_ARG_FREE_STR_MASK)
285
2
                *free = 1;
286
2
            else
287
0
                *free = 0;
288
2
        }
289
2
290
2
        return str;
291
2
    }
292
0
    else {
293
0
        return NULL;
294
0
    }
295
2
}
296
297
0
void * MVM_nativecall_unmarshal_cstruct(MVMThreadContext *tc, MVMObject *value) {
298
0
    if (!IS_CONCRETE(value))
299
0
        return NULL;
300
0
    else if (REPR(value)->ID == MVM_REPR_ID_MVMCStruct)
301
0
        return ((MVMCStruct *)value)->body.cstruct;
302
0
    else
303
0
        MVM_exception_throw_adhoc(tc,
304
0
            "Native call expected return type with CStruct representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
305
0
}
306
307
0
void * MVM_nativecall_unmarshal_cppstruct(MVMThreadContext *tc, MVMObject *value) {
308
0
    if (!IS_CONCRETE(value))
309
0
        return NULL;
310
0
    else if (REPR(value)->ID == MVM_REPR_ID_MVMCPPStruct)
311
0
        return ((MVMCPPStruct *)value)->body.cppstruct;
312
0
    else
313
0
        MVM_exception_throw_adhoc(tc,
314
0
            "Native call expected return type with CPPStruct representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
315
0
}
316
317
4
void * MVM_nativecall_unmarshal_cpointer(MVMThreadContext *tc, MVMObject *value) {
318
4
    if (!IS_CONCRETE(value))
319
3
        return NULL;
320
1
    else if (REPR(value)->ID == MVM_REPR_ID_MVMCPointer)
321
1
        return ((MVMCPointer *)value)->body.ptr;
322
1
    else
323
0
        MVM_exception_throw_adhoc(tc,
324
0
            "Native call expected return type with CPointer representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
325
4
}
326
327
0
void * MVM_nativecall_unmarshal_carray(MVMThreadContext *tc, MVMObject *value) {
328
0
    if (!IS_CONCRETE(value))
329
0
        return NULL;
330
0
    else if (REPR(value)->ID == MVM_REPR_ID_MVMCArray)
331
0
        return ((MVMCArray *)value)->body.storage;
332
0
    else
333
0
        MVM_exception_throw_adhoc(tc,
334
0
            "Native call expected return type with CArray representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
335
0
}
336
337
0
void * MVM_nativecall_unmarshal_vmarray(MVMThreadContext *tc, MVMObject *value) {
338
0
    if (!IS_CONCRETE(value))
339
0
        return NULL;
340
0
    else if (REPR(value)->ID == MVM_REPR_ID_VMArray) {
341
0
        MVMArrayBody *body          = &((MVMArray *)value)->body;
342
0
        MVMArrayREPRData *repr_data = (MVMArrayREPRData *)STABLE(value)->REPR_data;
343
0
        size_t start_pos            = body->start * repr_data->elem_size;
344
0
        return ((char *)body->slots.any) + start_pos;
345
0
    }
346
0
    else
347
0
        MVM_exception_throw_adhoc(tc,
348
0
            "Native call expected object with Array representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
349
0
}
350
351
0
void * MVM_nativecall_unmarshal_cunion(MVMThreadContext *tc, MVMObject *value) {
352
0
    if (!IS_CONCRETE(value))
353
0
        return NULL;
354
0
    else if (REPR(value)->ID == MVM_REPR_ID_MVMCUnion)
355
0
        return ((MVMCUnion *)value)->body.cunion;
356
0
    else
357
0
        MVM_exception_throw_adhoc(tc,
358
0
            "Native call expected return type with CUnion representation, but got a %s (%s)", REPR(value)->name, STABLE(value)->debug_name);
359
0
}
360
361
#ifdef _WIN32
362
static const char *dlerror(void)
363
{
364
    static char buf[32];
365
    DWORD dw = GetLastError();
366
    if (dw == 0)
367
        return NULL;
368
    sprintf(buf, "error 0x%x", (unsigned int)dw);
369
    return buf;
370
}
371
#endif
372
373
/* Builds up a native call site out of the supplied arguments. */
374
void MVM_nativecall_build(MVMThreadContext *tc, MVMObject *site, MVMString *lib,
375
3
        MVMString *sym, MVMString *conv, MVMObject *arg_info, MVMObject *ret_info) {
376
3
    char *lib_name = MVM_string_utf8_c8_encode_C_string(tc, lib);
377
3
    char *sym_name = MVM_string_utf8_c8_encode_C_string(tc, sym);
378
3
    MVMint8  keep_sym_name = 0;
379
3
    MVMint16 i;
380
3
381
3
    MVMObject *entry_point_o = (MVMObject *)MVM_repr_at_key_o(tc, ret_info,
382
3
        tc->instance->str_consts.entry_point);
383
3
384
3
    /* Initialize the object; grab native call part of its body. */
385
3
    MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site);
386
3
387
3
    /* Try to load the library. */
388
3
    body->lib_name = lib_name;
389
3
    body->lib_handle = MVM_nativecall_load_lib(strlen(lib_name) ? lib_name : NULL);
390
3
391
3
    if (!body->lib_handle) {
392
0
        char *waste[] = { lib_name, NULL };
393
0
        MVM_free(sym_name);
394
0
        MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate native library '%s': %s", lib_name, dlerror());
395
0
    }
396
3
397
3
    /* Try to locate the symbol. */
398
3
    if (entry_point_o) {
399
3
        body->entry_point = MVM_nativecall_unmarshal_cpointer(tc, entry_point_o);
400
3
        body->sym_name    = sym_name;
401
3
        keep_sym_name     = 1;
402
3
    }
403
3
404
3
    if (!body->entry_point) {
405
3
        body->entry_point = MVM_nativecall_find_sym(body->lib_handle, sym_name);
406
3
        if (!body->entry_point) {
407
0
            char *waste[] = { sym_name, lib_name, NULL };
408
0
            MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate symbol '%s' in native library '%s'",
409
0
                sym_name, lib_name);
410
0
        }
411
3
        body->sym_name = sym_name;
412
3
        keep_sym_name     = 1;
413
3
    }
414
3
415
3
    if (keep_sym_name == 0) {
416
0
        MVM_free(sym_name);
417
0
    }
418
3
419
3
    /* Set calling convention, if any. */
420
3
    body->convention = MVM_nativecall_get_calling_convention(tc, conv);
421
3
422
3
    /* Transform each of the args info structures into a flag. */
423
3
    body->num_args  = MVM_repr_elems(tc, arg_info);
424
3
    body->arg_types = MVM_malloc(sizeof(MVMint16) * (body->num_args ? body->num_args : 1));
425
3
    body->arg_info  = MVM_malloc(sizeof(MVMObject *) * (body->num_args ? body->num_args : 1));
426
3
#ifdef HAVE_LIBFFI
427
    body->ffi_arg_types = MVM_malloc(sizeof(ffi_type *) * (body->num_args ? body->num_args : 1));
428
#endif
429
6
    for (i = 0; i < body->num_args; i++) {
430
3
        MVMObject *info = MVM_repr_at_pos_o(tc, arg_info, i);
431
3
        body->arg_types[i] = MVM_nativecall_get_arg_type(tc, info, 0);
432
3
#ifdef HAVE_LIBFFI
433
        body->ffi_arg_types[i] = MVM_nativecall_get_ffi_type(tc, body->arg_types[i]);
434
#endif
435
3
        if(body->arg_types[i] == MVM_NATIVECALL_ARG_CALLBACK) {
436
0
            MVM_ASSIGN_REF(tc, &(site->header), body->arg_info[i],
437
0
                MVM_repr_at_key_o(tc, info, tc->instance->str_consts.callback_args));
438
0
        }
439
3
        else {
440
3
            body->arg_info[i]  = NULL;
441
3
        }
442
3
    }
443
3
444
3
    /* Transform return argument type info a flag. */
445
3
    body->ret_type     = MVM_nativecall_get_arg_type(tc, ret_info, 1);
446
3
#ifdef HAVE_LIBFFI
447
    body->ffi_ret_type = MVM_nativecall_get_ffi_type(tc, body->ret_type);
448
#endif
449
3
}
450
451
0
static MVMObject * nativecall_cast(MVMThreadContext *tc, MVMObject *target_spec, MVMObject *target_type, void *cpointer_body) {
452
0
    MVMObject *result = NULL;
453
0
454
0
    MVMROOT(tc, target_spec, {
455
0
        MVMROOT(tc, target_type, {
456
0
            switch (REPR(target_type)->ID) {
457
0
                case MVM_REPR_ID_P6opaque: {
458
0
                    const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec));
459
0
                    if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) {
460
0
                        MVMint64 value = 0;
461
0
                        if (ss->is_unsigned)
462
0
                            switch(ss->bits) {
463
0
                                case 8:
464
0
                                    value = *(MVMuint8 *)cpointer_body;
465
0
                                    break;
466
0
                                case 16:
467
0
                                    value = *(MVMuint16 *)cpointer_body;
468
0
                                    break;
469
0
                                case 32:
470
0
                                    value = *(MVMuint32 *)cpointer_body;
471
0
                                    break;
472
0
                                case 64:
473
0
                                default:
474
0
                                    value = *(MVMuint64 *)cpointer_body;
475
0
                            }
476
0
                        else
477
0
                            switch(ss->bits) {
478
0
                                case 8:
479
0
                                    value = *(MVMint8 *)cpointer_body;
480
0
                                    break;
481
0
                                case 16:
482
0
                                    value = *(MVMint16 *)cpointer_body;
483
0
                                    break;
484
0
                                case 32:
485
0
                                    value = *(MVMint32 *)cpointer_body;
486
0
                                    break;
487
0
                                case 64:
488
0
                                default:
489
0
                                    value = *(MVMint64 *)cpointer_body;
490
0
                            }
491
0
492
0
                        result = MVM_nativecall_make_int(tc, target_type, value);
493
0
                    }
494
0
                    else if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) {
495
0
                        MVMnum64 value;
496
0
497
0
                        switch(ss->bits) {
498
0
                            case 32:
499
0
                                value = *(MVMnum32 *)cpointer_body;
500
0
                                break;
501
0
                            case 64:
502
0
                            default:
503
0
                                value = *(MVMnum64 *)cpointer_body;
504
0
                        }
505
0
506
0
                        result = MVM_nativecall_make_num(tc, target_type, value);
507
0
                    }
508
0
                    else if(ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) {
509
0
                        result = MVM_nativecall_make_str(tc, target_type, MVM_NATIVECALL_ARG_UTF8STR,
510
0
                        (char *)cpointer_body);
511
0
                    }
512
0
                    else
513
0
                        MVM_exception_throw_adhoc(tc, "Internal error: unhandled target type");
514
0
515
0
                    break;
516
0
                }
517
0
                case MVM_REPR_ID_P6int: {
518
0
                    const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec));
519
0
                    MVMint64 value;
520
0
                    if (ss->is_unsigned)
521
0
                        switch(ss->bits) {
522
0
                            case 8:
523
0
                                value = *(MVMuint8 *)cpointer_body;
524
0
                                break;
525
0
                            case 16:
526
0
                                value = *(MVMuint16 *)cpointer_body;
527
0
                                break;
528
0
                            case 32:
529
0
                                value = *(MVMuint32 *)cpointer_body;
530
0
                                break;
531
0
                            case 64:
532
0
                            default:
533
0
                                value = *(MVMuint64 *)cpointer_body;
534
0
                        }
535
0
                    else
536
0
                        switch(ss->bits) {
537
0
                            case 8:
538
0
                                value = *(MVMint8 *)cpointer_body;
539
0
                                break;
540
0
                            case 16:
541
0
                                value = *(MVMint16 *)cpointer_body;
542
0
                                break;
543
0
                            case 32:
544
0
                                value = *(MVMint32 *)cpointer_body;
545
0
                                break;
546
0
                            case 64:
547
0
                            default:
548
0
                                value = *(MVMint64 *)cpointer_body;
549
0
                        }
550
0
                    result = MVM_nativecall_make_int(tc, target_type, value);
551
0
                    break;
552
0
                }
553
0
                case MVM_REPR_ID_P6num: {
554
0
                    const MVMStorageSpec *ss = REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec));
555
0
                    MVMnum64 value;
556
0
557
0
                    switch(ss->bits) {
558
0
                        case 32:
559
0
                            value = *(MVMnum32 *)cpointer_body;
560
0
                            break;
561
0
                        case 64:
562
0
                        default:
563
0
                            value = *(MVMnum64 *)cpointer_body;
564
0
                    }
565
0
566
0
                    result = MVM_nativecall_make_num(tc, target_type, value);
567
0
                    break;
568
0
                }
569
0
                case MVM_REPR_ID_MVMCStr:
570
0
                case MVM_REPR_ID_P6str:
571
0
                    result = MVM_nativecall_make_str(tc, target_type, MVM_NATIVECALL_ARG_UTF8STR,
572
0
                        (char *)cpointer_body);
573
0
                    break;
574
0
                case MVM_REPR_ID_MVMCStruct:
575
0
                    result = MVM_nativecall_make_cstruct(tc, target_type, (void *)cpointer_body);
576
0
                    break;
577
0
                case MVM_REPR_ID_MVMCUnion:
578
0
                    result = MVM_nativecall_make_cunion(tc, target_type, (void *)cpointer_body);
579
0
                    break;
580
0
                case MVM_REPR_ID_MVMCPointer:
581
0
                    result = MVM_nativecall_make_cpointer(tc, target_type, (void *)cpointer_body);
582
0
                    break;
583
0
                case MVM_REPR_ID_MVMCArray: {
584
0
                    result = MVM_nativecall_make_carray(tc, target_type, (void *)cpointer_body);
585
0
                    break;
586
0
                }
587
0
                default:
588
0
                    MVM_exception_throw_adhoc(tc, "Internal error: unhandled target type");
589
0
            }
590
0
        });
591
0
    });
592
0
593
0
    return result;
594
0
}
595
596
0
MVMObject * MVM_nativecall_global(MVMThreadContext *tc, MVMString *lib, MVMString *sym, MVMObject *target_spec, MVMObject *target_type) {
597
0
    char *lib_name = MVM_string_utf8_c8_encode_C_string(tc, lib);
598
0
    char *sym_name = MVM_string_utf8_c8_encode_C_string(tc, sym);
599
0
    DLLib *lib_handle;
600
0
    void *entry_point;
601
0
    MVMObject *ret = NULL;
602
0
603
0
    /* Try to load the library. */
604
0
    lib_handle = MVM_nativecall_load_lib(strlen(lib_name) ? lib_name : NULL);
605
0
    if (!lib_handle) {
606
0
        char *waste[] = { lib_name, NULL };
607
0
        MVM_free(sym_name);
608
0
        MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate native library '%s': %s", lib_name, dlerror());
609
0
    }
610
0
611
0
    /* Try to locate the symbol. */
612
0
    entry_point = MVM_nativecall_find_sym(lib_handle, sym_name);
613
0
    if (!entry_point) {
614
0
        char *waste[] = { sym_name, lib_name, NULL };
615
0
        MVM_exception_throw_adhoc_free(tc, waste, "Cannot locate symbol '%s' in native library '%s'",
616
0
            sym_name, lib_name);
617
0
    }
618
0
    MVM_free(sym_name);
619
0
    MVM_free(lib_name);
620
0
621
0
    if (REPR(target_type)->ID == MVM_REPR_ID_MVMCStr
622
0
    ||  REPR(target_type)->ID == MVM_REPR_ID_P6str
623
0
    || (REPR(target_type)->ID == MVM_REPR_ID_P6opaque
624
0
        && REPR(target_spec)->get_storage_spec(tc, STABLE(target_spec))->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR)) {
625
0
        entry_point = *(void **)entry_point;
626
0
    }
627
0
628
0
    ret = nativecall_cast(tc, target_spec, target_type, entry_point);
629
0
    MVM_nativecall_free_lib(lib_handle);
630
0
    return ret;
631
0
}
632
633
0
MVMObject * MVM_nativecall_cast(MVMThreadContext *tc, MVMObject *target_spec, MVMObject *target_type, MVMObject *source) {
634
0
    void *data_body;
635
0
636
0
    if (!source)
637
0
        return target_type;
638
0
639
0
    if (REPR(source)->ID == MVM_REPR_ID_MVMCStruct)
640
0
        data_body = MVM_nativecall_unmarshal_cstruct(tc, source);
641
0
    else if (REPR(source)->ID == MVM_REPR_ID_MVMCPPStruct)
642
0
        data_body = MVM_nativecall_unmarshal_cppstruct(tc, source);
643
0
    else if (REPR(source)->ID == MVM_REPR_ID_MVMCUnion)
644
0
        data_body = MVM_nativecall_unmarshal_cunion(tc, source);
645
0
    else if (REPR(source)->ID == MVM_REPR_ID_MVMCPointer)
646
0
        data_body = MVM_nativecall_unmarshal_cpointer(tc, source);
647
0
    else if (REPR(source)->ID == MVM_REPR_ID_MVMCArray)
648
0
        data_body = MVM_nativecall_unmarshal_carray(tc, source);
649
0
    else if (REPR(source)->ID == MVM_REPR_ID_VMArray)
650
0
        data_body = MVM_nativecall_unmarshal_vmarray(tc, source);
651
0
    else
652
0
        MVM_exception_throw_adhoc(tc,
653
0
            "Native call expected return type with CPointer, CStruct, CArray, or VMArray representation, but got a %s (%s)", REPR(source)->name, STABLE(source)->debug_name);
654
0
    return nativecall_cast(tc, target_spec, target_type, data_body);
655
0
}
656
657
0
MVMint64 MVM_nativecall_sizeof(MVMThreadContext *tc, MVMObject *obj) {
658
0
    if (REPR(obj)->ID == MVM_REPR_ID_MVMCStruct)
659
0
        return ((MVMCStructREPRData *)STABLE(obj)->REPR_data)->struct_size;
660
0
    else if (REPR(obj)->ID == MVM_REPR_ID_MVMCPPStruct)
661
0
        return ((MVMCPPStructREPRData *)STABLE(obj)->REPR_data)->struct_size;
662
0
    else if (REPR(obj)->ID == MVM_REPR_ID_MVMCUnion)
663
0
        return ((MVMCUnionREPRData *)STABLE(obj)->REPR_data)->struct_size;
664
0
    else if (REPR(obj)->ID == MVM_REPR_ID_P6int)
665
0
        return ((MVMP6intREPRData *)STABLE(obj)->REPR_data)->bits / 8;
666
0
    else if (REPR(obj)->ID == MVM_REPR_ID_P6num)
667
0
        return ((MVMP6numREPRData *)STABLE(obj)->REPR_data)->bits / 8;
668
0
    else if (REPR(obj)->ID == MVM_REPR_ID_MVMCPointer
669
0
          || REPR(obj)->ID == MVM_REPR_ID_MVMCArray
670
0
          || REPR(obj)->ID == MVM_REPR_ID_MVMCStr
671
0
          || REPR(obj)->ID == MVM_REPR_ID_P6str)
672
0
        return sizeof(void *);
673
0
    else
674
0
        MVM_exception_throw_adhoc(tc,
675
0
            "NativeCall op sizeof expected type with CPointer, CStruct, CArray, P6int or P6num representation, but got a %s (%s)",
676
0
            REPR(obj)->name, STABLE(obj)->debug_name);
677
0
}
678
679
/* Write-barriers a dyncall object so that delayed changes to the C-side of
680
 * objects are propagated to the HLL side. All CArray and CStruct arguments to
681
 * C functions are write-barriered automatically, so this should be necessary
682
 * only in the rarest of cases. */
683
3
void MVM_nativecall_refresh(MVMThreadContext *tc, MVMObject *cthingy) {
684
3
    if (!IS_CONCRETE(cthingy))
685
0
        return;
686
3
    if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCArray) {
687
0
        MVMCArrayBody      *body      = (MVMCArrayBody *)OBJECT_BODY(cthingy);
688
0
        MVMCArrayREPRData  *repr_data = (MVMCArrayREPRData *)STABLE(cthingy)->REPR_data;
689
0
        void              **storage   = (void **) body->storage;
690
0
        MVMint64            i;
691
0
692
0
        /* No need to check for numbers. They're stored directly in the array. */
693
0
        if (repr_data->elem_kind == MVM_CARRAY_ELEM_KIND_NUMERIC)
694
0
            return;
695
0
696
0
        for (i = 0; i < body->elems; i++) {
697
0
            void *cptr;   /* The pointer in the C storage. */
698
0
            void *objptr; /* The pointer in the object representing the C object. */
699
0
700
0
            /* Ignore elements where we haven't generated an object. */
701
0
            if (!body->child_objs[i])
702
0
                continue;
703
0
704
0
            cptr = storage[i];
705
0
            if (IS_CONCRETE(body->child_objs[i])) {
706
0
                switch (repr_data->elem_kind) {
707
0
                    case MVM_CARRAY_ELEM_KIND_CARRAY:
708
0
                        objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[i]))->storage;
709
0
                        break;
710
0
                    case MVM_CARRAY_ELEM_KIND_CPOINTER:
711
0
                        objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[i]))->ptr;
712
0
                        break;
713
0
                    case MVM_CARRAY_ELEM_KIND_CSTRUCT:
714
0
                        objptr = ((MVMCStructBody *)OBJECT_BODY(body->child_objs[i]))->cstruct;
715
0
                        break;
716
0
                    case MVM_CARRAY_ELEM_KIND_CUNION:
717
0
                        objptr = ((MVMCUnionBody *)OBJECT_BODY(body->child_objs[i]))->cunion;
718
0
                        break;
719
0
                    case MVM_CARRAY_ELEM_KIND_STRING:
720
0
                        objptr = NULL; /* TODO */
721
0
                        break;
722
0
                    default:
723
0
                        MVM_exception_throw_adhoc(tc,
724
0
                            "Fatal error: bad elem_kind (%d) in CArray write barrier",
725
0
                            repr_data->elem_kind);
726
0
                }
727
0
            }
728
0
            else {
729
0
                objptr = NULL;
730
0
            }
731
0
732
0
            if (objptr != cptr)
733
0
                body->child_objs[i] = NULL;
734
0
            else
735
0
                MVM_nativecall_refresh(tc, body->child_objs[i]);
736
0
        }
737
0
    }
738
3
    else if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCStruct) {
739
0
        MVMCStructBody     *body      = (MVMCStructBody *)OBJECT_BODY(cthingy);
740
0
        MVMCStructREPRData *repr_data = (MVMCStructREPRData *)STABLE(cthingy)->REPR_data;
741
0
        char               *storage   = (char *) body->cstruct;
742
0
        MVMint64            i;
743
0
744
0
        for (i = 0; i < repr_data->num_attributes; i++) {
745
0
            MVMint32 kind = repr_data->attribute_locations[i] & MVM_CSTRUCT_ATTR_MASK;
746
0
            MVMint32 slot = repr_data->attribute_locations[i] >> MVM_CSTRUCT_ATTR_SHIFT;
747
0
            void *cptr;   /* The pointer in the C storage. */
748
0
            void *objptr; /* The pointer in the object representing the C object. */
749
0
750
0
            if (kind == MVM_CSTRUCT_ATTR_IN_STRUCT || !body->child_objs[slot])
751
0
                continue;
752
0
753
0
            cptr = *((void **)(storage + repr_data->struct_offsets[i]));
754
0
            if (IS_CONCRETE(body->child_objs[slot])) {
755
0
                switch (kind) {
756
0
                    case MVM_CSTRUCT_ATTR_CARRAY:
757
0
                        objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[slot]))->storage;
758
0
                        break;
759
0
                    case MVM_CSTRUCT_ATTR_CPTR:
760
0
                        objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[slot]))->ptr;
761
0
                        break;
762
0
                    case MVM_CSTRUCT_ATTR_CSTRUCT:
763
0
                        objptr = (MVMCStructBody *)OBJECT_BODY(body->child_objs[slot]);
764
0
                        break;
765
0
                    case MVM_CSTRUCT_ATTR_CUNION:
766
0
                        objptr = (MVMCUnionBody *)OBJECT_BODY(body->child_objs[slot]);
767
0
                        break;
768
0
                    case MVM_CSTRUCT_ATTR_STRING:
769
0
                        objptr = NULL;
770
0
                        break;
771
0
                    default:
772
0
                        MVM_exception_throw_adhoc(tc,
773
0
                            "Fatal error: bad kind (%d) in CStruct write barrier",
774
0
                            kind);
775
0
                }
776
0
            }
777
0
            else {
778
0
                objptr = NULL;
779
0
            }
780
0
781
0
            if (objptr != cptr)
782
0
                body->child_objs[slot] = NULL;
783
0
            else
784
0
                MVM_nativecall_refresh(tc, body->child_objs[slot]);
785
0
        }
786
0
    }
787
3
    else if (REPR(cthingy)->ID == MVM_REPR_ID_MVMCPPStruct) {
788
0
        MVMCPPStructBody     *body      = (MVMCPPStructBody *)OBJECT_BODY(cthingy);
789
0
        MVMCPPStructREPRData *repr_data = (MVMCPPStructREPRData *)STABLE(cthingy)->REPR_data;
790
0
        char                 *storage   = (char *) body->cppstruct;
791
0
        MVMint64              i;
792
0
793
0
        for (i = 0; i < repr_data->num_attributes; i++) {
794
0
            MVMint32 kind = repr_data->attribute_locations[i] & MVM_CPPSTRUCT_ATTR_MASK;
795
0
            MVMint32 slot = repr_data->attribute_locations[i] >> MVM_CPPSTRUCT_ATTR_SHIFT;
796
0
            void *cptr;   /* The pointer in the C storage. */
797
0
            void *objptr; /* The pointer in the object representing the C object. */
798
0
799
0
            if (kind == MVM_CPPSTRUCT_ATTR_IN_STRUCT || !body->child_objs[slot])
800
0
                continue;
801
0
802
0
            cptr = *((void **)(storage + repr_data->struct_offsets[i]));
803
0
            if (IS_CONCRETE(body->child_objs[slot])) {
804
0
                switch (kind) {
805
0
                    case MVM_CPPSTRUCT_ATTR_CARRAY:
806
0
                        objptr = ((MVMCArrayBody *)OBJECT_BODY(body->child_objs[slot]))->storage;
807
0
                        break;
808
0
                    case MVM_CPPSTRUCT_ATTR_CPTR:
809
0
                        objptr = ((MVMCPointerBody *)OBJECT_BODY(body->child_objs[slot]))->ptr;
810
0
                        break;
811
0
                    case MVM_CPPSTRUCT_ATTR_CSTRUCT:
812
0
                        objptr = (MVMCStructBody *)OBJECT_BODY(body->child_objs[slot]);
813
0
                        break;
814
0
                    case MVM_CPPSTRUCT_ATTR_STRING:
815
0
                        objptr = NULL;
816
0
                        break;
817
0
                    default:
818
0
                        MVM_exception_throw_adhoc(tc,
819
0
                            "Fatal error: bad kind (%d) in CPPStruct write barrier",
820
0
                            kind);
821
0
                }
822
0
            }
823
0
            else {
824
0
                objptr = NULL;
825
0
            }
826
0
827
0
            if (objptr != cptr)
828
0
                body->child_objs[slot] = NULL;
829
0
            else
830
0
                MVM_nativecall_refresh(tc, body->child_objs[slot]);
831
0
        }
832
0
    }
833
3
}