forked from livecode/livecode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfoundation-ffi-js.cpp
More file actions
177 lines (147 loc) · 5.15 KB
/
foundation-ffi-js.cpp
File metadata and controls
177 lines (147 loc) · 5.15 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* -*-c++-*-
Copyright (C) 2015 LiveCode Ltd.
This file is part of LiveCode.
LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.
LiveCode 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 General Public License
for more details.
You should have received a copy of the GNU General Public License
along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
#include <emscripten.h>
#include <ffi.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
/* Alignment of Emscripten function pointers in its function pointer
* array. */
#define FUNCTION_POINTER_ALIGNMENT 2
/* Alignment of Emscripten data pointers */
#define DATA_POINTER_ALIGNMENT 4
/* ================================================================
* Helper functions
* ================================================================ */
/* Map libffi type descriptors to LLVM IR type descriptors. */
static const char *
ffi_map_to_llvm_ir(ffi_type *p_type) {
if (p_type == &ffi_type_void)
return NULL;
if (p_type == &ffi_type_sint8 || p_type == &ffi_type_uint8)
return "i8";
if (p_type == &ffi_type_uint16 || p_type == &ffi_type_sint16)
return "i16";
if (p_type == &ffi_type_uint32 || p_type == &ffi_type_sint32)
return "i32";
if (p_type == &ffi_type_uint64 || p_type == &ffi_type_sint64)
return "i64";
if (p_type == &ffi_type_float)
return "float";
if (p_type == &ffi_type_double)
return "double";
if (p_type == &ffi_type_pointer)
return "*";
abort(); /* Should never be reached */
return NULL;
};
/* ================================================================
* Emscripten-specific implementations of libffi functions
* ================================================================ */
extern "C" ffi_status
ffi_prep_cif_machdep(ffi_cif *cif)
{
return FFI_OK;
}
extern "C" void
ffi_call(ffi_cif *p_cif,
void (*p_fn)(void),
void *x_rvalue,
void **x_avalues)
{
/* Only pay any attention to the "default ABI", which we'll treat as the
* Emscripten ABI (even if it isn't). */
assert(p_cif->abi == FFI_DEFAULT_ABI);
/* Convert the data type information stored in the cif data to
* values that are more easily understood by getValue() and
* setValue(). */
const char *t_rvalue_type_ir = ffi_map_to_llvm_ir(p_cif->rtype);
const char *t_avalue_types_ir[p_cif->nargs];
for (unsigned i = 0; i < p_cif->nargs; ++i)
{
t_avalue_types_ir[i] = ffi_map_to_llvm_ir(p_cif->arg_types[i]);
}
/* Marshal the function and dispatch it, in JavaScript */
EM_ASM_({
// ---------- Sensible names for arguments
var fn = $0;
var nargs = $1;
var rvalue_ptr = $2;
var avalue_ptr = $3;
var rvalue_type_ptr = $4;
var avalue_types_ptr = $5;
var DATA_POINTER_ALIGNMENT = $6;
var FUNCTION_POINTER_ALIGNMENT = $7;
var pointer_stringify = Module.Pointer_stringify;
var getValue = Module.getValue;
var setValue = Module.setValue;
var stack = 0;
// ---------- Get JavaScript function
// Compute the correct index into the Emscripten runtime
// function pointer array.
var index = fn / FUNCTION_POINTER_ALIGNMENT - 1;
// Get the actual JavaScript function
var func = Module.Runtime.functionPointers[index];
/*DEBUG // (no way to hide this behind an #ifdef, unfortunately)
// Get the function name
var DBG_func_name;
for (var key in Module) {
if (Module.hasOwnProperty(key) && Module[key] == func) {
DBG_func_name = key;
}
}
var DBG_func_args = [];
*/
// ---------- Marshal arguments
var func_args = [];
for (var i = 0; i < nargs; ++i) {
var arg_type_ptr = avalue_types_ptr + i * DATA_POINTER_ALIGNMENT;
var arg_val_ptr = avalue_ptr + i * DATA_POINTER_ALIGNMENT;
var arg_type = pointer_stringify(getValue(arg_type_ptr, "*"));
// Each value in the argument values array is a
// pointer to the location where the actual argument
// value is stored.
var arg_val = getValue(getValue(arg_val_ptr, "*"), arg_type);
func_args.push(arg_val);
/*DEBUG
DBG_func_args.push('(' + arg_type + ')' + arg_val.toString(16));
*/
}
var ret_type = null;
if (rvalue_type_ptr != 0) {
ret_type = pointer_stringify(rvalue_type_ptr);
}
/*DEBUG
console.error('ffi_call: ' + fn.toString(16) + '=' + DBG_func_name +
' ' + DBG_func_args);
*/
// ---------- Invoke
stack = Module.Runtime.stackSave();
var func_retval = func.apply(null, func_args);
Module.Runtime.stackRestore(stack);
// ---------- Store return value
if (ret_type) {
// the rvalue_ptr is a pointer to a location in which
// the return value should be saved
setValue(getValue(rvalue_ptr, "*"), func_retval, ret_type);
}
},
/* 0 */ int(p_fn),
/* 1 */ int(p_cif->nargs),
/* 2 */ int(x_rvalue),
/* 3 */ int(x_avalues),
/* 4 */ t_rvalue_type_ir,
/* 5 */ t_avalue_types_ir,
/* 6 */ DATA_POINTER_ALIGNMENT,
/* 7 */ FUNCTION_POINTER_ALIGNMENT);
}