static char callback_handler(DCCallback *cb, DCArgs *cb_args, DCValue *cb_result, MVMNativeCallback *data) { CallbackInvokeData cid; MVMint32 num_roots, i; MVMRegister res; /* Build a callsite and arguments buffer. */ MVMThreadContext *tc = data->tc; MVMRegister *args = MVM_malloc(data->num_types * sizeof(MVMRegister)); num_roots = 0; for (i = 1; i < data->num_types; i++) { MVMObject *type = data->types[i]; MVMint16 typeinfo = data->typeinfos[i]; switch (typeinfo & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_CHAR: args[i - 1].i64 = dcbArgChar(cb_args); break; case MVM_NATIVECALL_ARG_SHORT: args[i - 1].i64 = dcbArgShort(cb_args); break; case MVM_NATIVECALL_ARG_INT: args[i - 1].i64 = dcbArgInt(cb_args); break; case MVM_NATIVECALL_ARG_LONG: args[i - 1].i64 = dcbArgLong(cb_args); break; case MVM_NATIVECALL_ARG_LONGLONG: args[i - 1].i64 = dcbArgLongLong(cb_args); break; case MVM_NATIVECALL_ARG_FLOAT: args[i - 1].n64 = dcbArgFloat(cb_args); break; case MVM_NATIVECALL_ARG_DOUBLE: args[i - 1].n64 = dcbArgDouble(cb_args); break; case MVM_NATIVECALL_ARG_ASCIISTR: case MVM_NATIVECALL_ARG_UTF8STR: case MVM_NATIVECALL_ARG_UTF16STR: args[i - 1].o = MVM_nativecall_make_str(tc, type, typeinfo, (char *)dcbArgPointer(cb_args)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CSTRUCT: args[i - 1].o = MVM_nativecall_make_cstruct(tc, type, dcbArgPointer(cb_args)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CPOINTER: args[i - 1].o = MVM_nativecall_make_cpointer(tc, type, dcbArgPointer(cb_args)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CARRAY: args[i - 1].o = MVM_nativecall_make_carray(tc, type, dcbArgPointer(cb_args)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CUNION: args[i - 1].o = MVM_nativecall_make_cunion(tc, type, dcbArgPointer(cb_args)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CALLBACK: /* TODO: A callback -return- value means that we have a C method * that needs to be wrapped similarly to a is native(...) Perl 6 * sub. */ dcbArgPointer(cb_args); args[i - 1].o = type; MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; case MVM_NATIVECALL_ARG_UCHAR: args[i - 1].i64 = dcbArgUChar(cb_args); break; case MVM_NATIVECALL_ARG_USHORT: args[i - 1].i64 = dcbArgUShort(cb_args); break; case MVM_NATIVECALL_ARG_UINT: args[i - 1].i64 = dcbArgUInt(cb_args); break; case MVM_NATIVECALL_ARG_ULONG: args[i - 1].i64 = dcbArgULong(cb_args); break; case MVM_NATIVECALL_ARG_ULONGLONG: args[i - 1].i64 = dcbArgULongLong(cb_args); break; default: MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall callback argument type"); } } /* Call into a nested interpreter (since we already are in one). Need to * save a bunch of state around each side of this. */ cid.invokee = data->target; cid.args = args; cid.cs = data->cs; { MVMuint8 **backup_interp_cur_op = tc->interp_cur_op; MVMuint8 **backup_interp_bytecode_start = tc->interp_bytecode_start; MVMRegister **backup_interp_reg_base = tc->interp_reg_base; MVMCompUnit **backup_interp_cu = tc->interp_cu; MVMFrame *backup_cur_frame = tc->cur_frame; MVMFrame *backup_thread_entry_frame = tc->thread_entry_frame; MVMuint32 backup_mark = MVM_gc_root_temp_mark(tc); jmp_buf backup_interp_jump; memcpy(backup_interp_jump, tc->interp_jump, sizeof(jmp_buf)); tc->cur_frame->return_value = &res; tc->cur_frame->return_type = MVM_RETURN_OBJ; MVM_interp_run(tc, &callback_invoke, &cid); tc->interp_cur_op = backup_interp_cur_op; tc->interp_bytecode_start = backup_interp_bytecode_start; tc->interp_reg_base = backup_interp_reg_base; tc->interp_cu = backup_interp_cu; tc->cur_frame = backup_cur_frame; tc->thread_entry_frame = backup_thread_entry_frame; memcpy(tc->interp_jump, backup_interp_jump, sizeof(jmp_buf)); MVM_gc_root_temp_mark_reset(tc, backup_mark); } /* Handle return value. */ if (res.o) { MVMContainerSpec const *contspec = STABLE(res.o)->container_spec; if (contspec && contspec->fetch_never_invokes) contspec->fetch(data->tc, res.o, &res); } switch (data->typeinfos[0] & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_VOID: break; case MVM_NATIVECALL_ARG_CHAR: cb_result->c = MVM_nativecall_unmarshal_char(data->tc, res.o); break; case MVM_NATIVECALL_ARG_SHORT: cb_result->s = MVM_nativecall_unmarshal_short(data->tc, res.o); break; case MVM_NATIVECALL_ARG_INT: cb_result->i = MVM_nativecall_unmarshal_int(data->tc, res.o); break; case MVM_NATIVECALL_ARG_LONG: cb_result->j = MVM_nativecall_unmarshal_long(data->tc, res.o); break; case MVM_NATIVECALL_ARG_LONGLONG: cb_result->l = MVM_nativecall_unmarshal_longlong(data->tc, res.o); break; case MVM_NATIVECALL_ARG_FLOAT: cb_result->f = MVM_nativecall_unmarshal_float(data->tc, res.o); break; case MVM_NATIVECALL_ARG_DOUBLE: cb_result->d = MVM_nativecall_unmarshal_double(data->tc, res.o); break; case MVM_NATIVECALL_ARG_ASCIISTR: case MVM_NATIVECALL_ARG_UTF8STR: case MVM_NATIVECALL_ARG_UTF16STR: cb_result->Z = MVM_nativecall_unmarshal_string(data->tc, res.o, data->typeinfos[0], NULL); break; case MVM_NATIVECALL_ARG_CSTRUCT: cb_result->p = MVM_nativecall_unmarshal_cstruct(data->tc, res.o); break; case MVM_NATIVECALL_ARG_CPOINTER: cb_result->p = MVM_nativecall_unmarshal_cpointer(data->tc, res.o); break; case MVM_NATIVECALL_ARG_CARRAY: cb_result->p = MVM_nativecall_unmarshal_carray(data->tc, res.o); break; case MVM_NATIVECALL_ARG_CUNION: cb_result->p = MVM_nativecall_unmarshal_cunion(data->tc, res.o); break; case MVM_NATIVECALL_ARG_VMARRAY: cb_result->p = MVM_nativecall_unmarshal_vmarray(data->tc, res.o); break; case MVM_NATIVECALL_ARG_CALLBACK: cb_result->p = unmarshal_callback(data->tc, res.o, data->types[0]); break; case MVM_NATIVECALL_ARG_UCHAR: cb_result->c = MVM_nativecall_unmarshal_uchar(data->tc, res.o); break; case MVM_NATIVECALL_ARG_USHORT: cb_result->s = MVM_nativecall_unmarshal_ushort(data->tc, res.o); break; case MVM_NATIVECALL_ARG_UINT: cb_result->i = MVM_nativecall_unmarshal_uint(data->tc, res.o); break; case MVM_NATIVECALL_ARG_ULONG: cb_result->j = MVM_nativecall_unmarshal_ulong(data->tc, res.o); break; case MVM_NATIVECALL_ARG_ULONGLONG: cb_result->l = MVM_nativecall_unmarshal_ulonglong(data->tc, res.o); break; default: MVM_exception_throw_adhoc(data->tc, "Internal error: unhandled dyncall callback return type"); } /* Clean up. */ MVM_gc_root_temp_pop_n(tc, num_roots); MVM_free(args); /* Indicate what we're producing as a result. */ return get_signature_char(data->typeinfos[0]); }
MVMObject * MVM_nativecall_invoke(MVMThreadContext *tc, MVMObject *res_type, MVMObject *site, MVMObject *args) { MVMObject *result = NULL; char **free_strs = NULL; void **free_rws = NULL; MVMint16 num_strs = 0; MVMint16 num_rws = 0; MVMint16 i; /* Get native call body, so we can locate the call info. Read out all we * shall need, since later we may allocate a result and and move it. */ MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site); MVMint16 num_args = body->num_args; MVMint16 *arg_types = body->arg_types; MVMint16 ret_type = body->ret_type; void *entry_point = body->entry_point; /* Create and set up call VM. */ DCCallVM *vm = dcNewCallVM(8192); dcMode(vm, body->convention); /* Process arguments. */ for (i = 0; i < num_args; i++) { MVMObject *value = MVM_repr_at_pos_o(tc, args, i); switch (arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_CHAR: handle_arg("integer", cont_i, DCchar, i64, dcArgChar, MVM_nativecall_unmarshal_char); break; case MVM_NATIVECALL_ARG_SHORT: handle_arg("integer", cont_i, DCshort, i64, dcArgShort, MVM_nativecall_unmarshal_short); break; case MVM_NATIVECALL_ARG_INT: handle_arg("integer", cont_i, DCint, i64, dcArgInt, MVM_nativecall_unmarshal_int); break; case MVM_NATIVECALL_ARG_LONG: handle_arg("integer", cont_i, DClong, i64, dcArgLong, MVM_nativecall_unmarshal_long); break; case MVM_NATIVECALL_ARG_LONGLONG: handle_arg("integer", cont_i, DClonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_longlong); break; case MVM_NATIVECALL_ARG_FLOAT: handle_arg("number", cont_n, DCfloat, n64, dcArgFloat, MVM_nativecall_unmarshal_float); break; case MVM_NATIVECALL_ARG_DOUBLE: handle_arg("number", cont_n, DCdouble, n64, dcArgDouble, MVM_nativecall_unmarshal_double); break; case MVM_NATIVECALL_ARG_ASCIISTR: case MVM_NATIVECALL_ARG_UTF8STR: case MVM_NATIVECALL_ARG_UTF16STR: { MVMint16 free = 0; char *str = MVM_nativecall_unmarshal_string(tc, value, arg_types[i], &free); if (free) { if (!free_strs) free_strs = (char**)MVM_malloc(num_args * sizeof(char *)); free_strs[num_strs] = str; num_strs++; } dcArgPointer(vm, str); } break; case MVM_NATIVECALL_ARG_CSTRUCT: dcArgPointer(vm, MVM_nativecall_unmarshal_cstruct(tc, value)); break; case MVM_NATIVECALL_ARG_CPOINTER: dcArgPointer(vm, MVM_nativecall_unmarshal_cpointer(tc, value)); break; case MVM_NATIVECALL_ARG_CARRAY: dcArgPointer(vm, MVM_nativecall_unmarshal_carray(tc, value)); break; case MVM_NATIVECALL_ARG_CUNION: dcArgPointer(vm, MVM_nativecall_unmarshal_cunion(tc, value)); break; case MVM_NATIVECALL_ARG_VMARRAY: dcArgPointer(vm, MVM_nativecall_unmarshal_vmarray(tc, value)); break; case MVM_NATIVECALL_ARG_CALLBACK: dcArgPointer(vm, unmarshal_callback(tc, value, body->arg_info[i])); break; case MVM_NATIVECALL_ARG_UCHAR: handle_arg("integer", cont_i, DCuchar, i64, dcArgChar, MVM_nativecall_unmarshal_uchar); break; case MVM_NATIVECALL_ARG_USHORT: handle_arg("integer", cont_i, DCushort, i64, dcArgShort, MVM_nativecall_unmarshal_ushort); break; case MVM_NATIVECALL_ARG_UINT: handle_arg("integer", cont_i, DCuint, i64, dcArgInt, MVM_nativecall_unmarshal_uint); break; case MVM_NATIVECALL_ARG_ULONG: handle_arg("integer", cont_i, DCulong, i64, dcArgLong, MVM_nativecall_unmarshal_ulong); break; case MVM_NATIVECALL_ARG_ULONGLONG: handle_arg("integer", cont_i, DCulonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_ulonglong); break; default: MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall argument type"); } } /* Call and process return values. */ MVMROOT(tc, args, { MVMROOT(tc, res_type, { switch (ret_type & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_VOID: dcCallVoid(vm, entry_point); result = res_type; break; case MVM_NATIVECALL_ARG_CHAR: result = MVM_nativecall_make_int(tc, res_type, dcCallChar(vm, entry_point)); break; case MVM_NATIVECALL_ARG_SHORT: result = MVM_nativecall_make_int(tc, res_type, dcCallShort(vm, entry_point)); break; case MVM_NATIVECALL_ARG_INT: result = MVM_nativecall_make_int(tc, res_type, dcCallInt(vm, entry_point)); break; case MVM_NATIVECALL_ARG_LONG: result = MVM_nativecall_make_int(tc, res_type, dcCallLong(vm, entry_point)); break; case MVM_NATIVECALL_ARG_LONGLONG: result = MVM_nativecall_make_int(tc, res_type, dcCallLongLong(vm, entry_point)); break; case MVM_NATIVECALL_ARG_FLOAT: result = MVM_nativecall_make_num(tc, res_type, dcCallFloat(vm, entry_point)); break; case MVM_NATIVECALL_ARG_DOUBLE: result = MVM_nativecall_make_num(tc, res_type, dcCallDouble(vm, entry_point)); break; case MVM_NATIVECALL_ARG_ASCIISTR: case MVM_NATIVECALL_ARG_UTF8STR: case MVM_NATIVECALL_ARG_UTF16STR: result = MVM_nativecall_make_str(tc, res_type, body->ret_type, (char *)dcCallPointer(vm, entry_point)); break; case MVM_NATIVECALL_ARG_CSTRUCT: result = MVM_nativecall_make_cstruct(tc, res_type, dcCallPointer(vm, body->entry_point)); break; case MVM_NATIVECALL_ARG_CPOINTER: result = MVM_nativecall_make_cpointer(tc, res_type, dcCallPointer(vm, body->entry_point)); break; case MVM_NATIVECALL_ARG_CARRAY: result = MVM_nativecall_make_carray(tc, res_type, dcCallPointer(vm, body->entry_point)); break; case MVM_NATIVECALL_ARG_CUNION: result = MVM_nativecall_make_cunion(tc, res_type, dcCallPointer(vm, body->entry_point)); break; case MVM_NATIVECALL_ARG_CALLBACK: /* TODO: A callback -return- value means that we have a C method * that needs to be wrapped similarly to a is native(...) Perl 6 * sub. */ dcCallPointer(vm, body->entry_point); result = res_type; break; case MVM_NATIVECALL_ARG_UCHAR: result = MVM_nativecall_make_uint(tc, res_type, (DCuchar)dcCallChar(vm, entry_point)); break; case MVM_NATIVECALL_ARG_USHORT: result = MVM_nativecall_make_uint(tc, res_type, (DCushort)dcCallShort(vm, entry_point)); break; case MVM_NATIVECALL_ARG_UINT: result = MVM_nativecall_make_uint(tc, res_type, (DCuint)dcCallInt(vm, entry_point)); break; case MVM_NATIVECALL_ARG_ULONG: result = MVM_nativecall_make_uint(tc, res_type, (DCulong)dcCallLong(vm, entry_point)); break; case MVM_NATIVECALL_ARG_ULONGLONG: result = MVM_nativecall_make_uint(tc, res_type, (DCulonglong)dcCallLongLong(vm, entry_point)); break; default: MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall return type"); } }); });
static void callback_handler(ffi_cif *cif, void *cb_result, void **cb_args, void *cb_data) { CallbackInvokeData cid; MVMint32 num_roots, i; MVMRegister res; MVMRegister *args; MVMNativeCallback *data = (MVMNativeCallback *)cb_data; void **values = MVM_malloc(sizeof(void *) * (data->cs->arg_count ? data->cs->arg_count : 1)); unsigned int interval_id; /* Locate the MoarVM thread this callback is being run on. */ MVMThreadContext *tc = MVM_nativecall_find_thread_context(data->instance); /* Unblock GC if needed, so this thread can do work. */ MVMint32 was_blocked = MVM_gc_is_thread_blocked(tc); if (was_blocked) MVM_gc_mark_thread_unblocked(tc); interval_id = MVM_telemetry_interval_start(tc, "nativecall callback handler"); /* Build a callsite and arguments buffer. */ args = MVM_malloc(data->num_types * sizeof(MVMRegister)); num_roots = 0; for (i = 1; i < data->num_types; i++) { MVMObject *type = data->types[i]; MVMint16 typeinfo = data->typeinfos[i]; switch (typeinfo & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_CHAR: args[i - 1].i64 = *(signed char *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_SHORT: args[i - 1].i64 = *(signed short *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_INT: args[i - 1].i64 = *(signed int *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_LONG: args[i - 1].i64 = *(signed long *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_LONGLONG: args[i - 1].i64 = *(signed long long *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_FLOAT: args[i - 1].n64 = *(float *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_DOUBLE: args[i - 1].n64 = *(double *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_ASCIISTR: case MVM_NATIVECALL_ARG_UTF8STR: case MVM_NATIVECALL_ARG_UTF16STR: args[i - 1].o = MVM_nativecall_make_str(tc, type, typeinfo, *(char **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CSTRUCT: args[i - 1].o = MVM_nativecall_make_cstruct(tc, type, *(void **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CPPSTRUCT: args[i - 1].o = MVM_nativecall_make_cppstruct(tc, type, *(void **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CPOINTER: args[i - 1].o = MVM_nativecall_make_cpointer(tc, type, *(void **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CARRAY: args[i - 1].o = MVM_nativecall_make_carray(tc, type, *(void **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CUNION: args[i - 1].o = MVM_nativecall_make_cunion(tc, type, *(void **)cb_args[i - 1]); MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; break; case MVM_NATIVECALL_ARG_CALLBACK: /* TODO: A callback -return- value means that we have a C method * that needs to be wrapped similarly to a is native(...) Perl 6 * sub. */ /* XXX do something with the function pointer: *(void **)cb_args[i - 1] */ args[i - 1].o = type; MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o)); num_roots++; case MVM_NATIVECALL_ARG_UCHAR: args[i - 1].i64 = *(unsigned char *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_USHORT: args[i - 1].i64 = *(unsigned short *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_UINT: args[i - 1].i64 = *(unsigned int *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_ULONG: args[i - 1].i64 = *(unsigned long *)cb_args[i - 1]; break; case MVM_NATIVECALL_ARG_ULONGLONG: args[i - 1].i64 = *(unsigned long long *)cb_args[i - 1]; break; default: MVM_telemetry_interval_stop(tc, interval_id, "nativecall callback handler failed"); MVM_exception_throw_adhoc(tc, "Internal error: unhandled libffi callback argument type"); } } /* Call into a nested interpreter (since we already are in one). Need to * save a bunch of state around each side of this. */ cid.invokee = data->target; cid.args = args; cid.cs = data->cs; { MVMuint8 **backup_interp_cur_op = tc->interp_cur_op; MVMuint8 **backup_interp_bytecode_start = tc->interp_bytecode_start; MVMRegister **backup_interp_reg_base = tc->interp_reg_base; MVMCompUnit **backup_interp_cu = tc->interp_cu; MVMFrame *backup_cur_frame = MVM_frame_force_to_heap(tc, tc->cur_frame); MVMFrame *backup_thread_entry_frame = tc->thread_entry_frame; void **backup_jit_return_address = tc->jit_return_address; tc->jit_return_address = NULL; MVMROOT2(tc, backup_cur_frame, backup_thread_entry_frame, { MVMuint32 backup_mark = MVM_gc_root_temp_mark(tc); jmp_buf backup_interp_jump; memcpy(backup_interp_jump, tc->interp_jump, sizeof(jmp_buf)); tc->cur_frame->return_value = &res; tc->cur_frame->return_type = MVM_RETURN_OBJ; MVM_interp_run(tc, callback_invoke, &cid); tc->interp_cur_op = backup_interp_cur_op; tc->interp_bytecode_start = backup_interp_bytecode_start; tc->interp_reg_base = backup_interp_reg_base; tc->interp_cu = backup_interp_cu; tc->cur_frame = backup_cur_frame; tc->current_frame_nr = backup_cur_frame->sequence_nr; tc->thread_entry_frame = backup_thread_entry_frame; tc->jit_return_address = backup_jit_return_address; memcpy(tc->interp_jump, backup_interp_jump, sizeof(jmp_buf)); MVM_gc_root_temp_mark_reset(tc, backup_mark); }); }