summaryrefslogtreecommitdiff
path: root/invoke.c
blob: 82803a67bed7a85d9da6dd0d2910af8d886b6144 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */

/*  Libnul - Another Utility Library
 *  Copyright (C) 2008  Søren Sandmann
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the 
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA  02111-1307, USA.
 */

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "ffi.h"
#include "libnul.h"

struct nul_fun_def_t
{
    int		n_args;
    ffi_type   *ffi_ret_type;
    ffi_type  **ffi_arg_types;
    ffi_cif	ffi_cif;
};

static ffi_type *
ffi_type_from_type (nul_type_t type)
{
    switch (type)
    {
    case NUL_TYPE_UINT32: return &ffi_type_uint32;
    case NUL_TYPE_INT32: return &ffi_type_sint32;
    case NUL_TYPE_UINT16: return &ffi_type_uint16;
    case NUL_TYPE_INT16: return &ffi_type_sint16;
    case NUL_TYPE_UINT8: return &ffi_type_uint8;
    case NUL_TYPE_INT8: return &ffi_type_sint8;
	
    case NUL_TYPE_UINT: return &ffi_type_uint;
    case NUL_TYPE_INT: return &ffi_type_sint;
    case NUL_TYPE_USHORT: return &ffi_type_ushort;
    case NUL_TYPE_SHORT: return &ffi_type_sshort;
	
    case NUL_TYPE_UCHAR: return &ffi_type_uchar;
    case NUL_TYPE_SCHAR: return &ffi_type_schar;
#ifdef __CHAR_UNSIGNED__
    case NUL_TYPE_CHAR: return &ffi_type_uchar;
#else
    case NUL_TYPE_CHAR: return &ffi_type_schar;
#endif
	
    case NUL_TYPE_POINTER: return &ffi_type_pointer;
    case NUL_TYPE_STRING: return &ffi_type_pointer;
	
    case NUL_TYPE_DOUBLE: return &ffi_type_double;
    case NUL_TYPE_FLOAT: return &ffi_type_float;

    case NUL_TYPE_VOID: return &ffi_type_void;
    default:
	return NULL;
    }
}

nul_fun_def_t *
nul_fun_def_new (nul_type_t      ret_type,
		 int             n_args,
		 nul_type_t     *arg_types)
{
    nul_fun_def_t *def = malloc (sizeof (nul_fun_def_t));
    int i;
    
    if (!def)
	return NULL;

    def->ffi_arg_types = malloc (sizeof (ffi_type *) * n_args);
    if (!def->ffi_arg_types)
    {
	free (def);
	return NULL;
    }

    for (i = 0; i < n_args; ++i)
    {
	if (arg_types[i] == NUL_TYPE_VOID)
	    g_error ("Arguments tuments cannot have type void");
	else
	    def->ffi_arg_types[i] = ffi_type_from_type (arg_types[i]);
    }

    def->ffi_ret_type = ffi_type_from_type (ret_type);
    
    def->n_args = n_args;

    ffi_prep_cif (&def->ffi_cif, FFI_DEFAULT_ABI, n_args, def->ffi_ret_type, def->ffi_arg_types);
    
    return def;
}

nul_arg_t
nul_fun_def_invoke (nul_fun_def_t   *fun,
		    nul_function_t  f,
		    nul_arg_t      *args)
{
    void *stack_args[8];
    void **ffi_args;
    nul_arg_t ret;
    int i;

    if (fun->n_args > 8)
	ffi_args = malloc (fun->n_args * sizeof (void *));
    else
	ffi_args = stack_args;
    
    for (i = 0; i < fun->n_args; ++i)
	ffi_args[i] = &(args[i]);

    ffi_call (&(fun->ffi_cif), f, &ret, ffi_args);

    if (ffi_args != stack_args)
	free (ffi_args);
    
    return ret;
}

void
nul_fun_def_free (nul_fun_def_t *fun)
{
    free (fun->ffi_arg_types);
    free (fun);
}