-
Notifications
You must be signed in to change notification settings - Fork 1
/
js-support.h
279 lines (228 loc) · 8.11 KB
/
js-support.h
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#pragma once
#include "mytypes.h"
#include "Vec3.h"
#include "Mat3.h"
#include <string>
#include <utility> // for pair
#include <vector>
#include <array>
#include <map>
#include <functional>
#include <sstream>
#include <assert.h>
#include <mujs.h>
//
// JsSupport contains various support routines for binding various object types to JS
//
// It assumes that the presence of "const char* TAG_{cls}", "Js{cls}::xnew", etc in the module where it is included in
//
class JsSupport {
public:
static void beginDefineClass(js_State *J, const char *clsTag, js_CFunction newFun);
static void endDefineClass(js_State *J);
static void beginNamespace(js_State *J, const char *nsNameStr);
static void endNamespace(js_State *J, const char *nsNameStr);
static void addJsConstructor(js_State *J, const char *clsTag, js_CFunction newFun);
static void addMethodCpp(js_State *J, const char *cls, const char *methodStr, const char *prototypeName, js_CFunction methodFun, unsigned nargs);
static void addMethodJs(js_State *J, const char *cls, const char *methodStr, const char *codeStr);
static void addJsFunction(js_State *J, const char *funcNameStr, js_CFunction funcFun, unsigned nargs);
static void addNsFunctionCpp(js_State *J, const char *nsNameStr, const char *jsfnNameStr, js_CFunction funcFun, unsigned nargs);
static void addNsFunctionJs(js_State *J, const char *nsNameStr, const char *fnNameStr, const char *codeStr);
static void addNsConstInt(js_State *J, const char *nsNameStr, const char *constNameStr, int constValue);
static void registerFuncRequire(js_State *J);
static void registerFuncImportCodeString(js_State *J);
static void registerErrorToString(js_State *J);
static std::vector<int> objToInt32Array(js_State *J, int idx, const char *fname);
static std::vector<int> objToInt32ArrayZ(js_State *J, int idx, const char *fname);
static char toChar(js_State *J, int idx);
[[noreturn]] static void error(js_State *J, const std::string &msg);
private:
static void initObjectRegistry(js_State *J, const char *objTag);
static void popPreviousStackElement(js_State *J);
}; // JsSupport
// convenience macros that come with JsSupport
#define BEGIN_NAMESPACE(ns) \
JsSupport::beginNamespace(J, #ns);
#define END_NAMESPACE(ns) \
JsSupport::endNamespace(J, #ns);
// add method defined by the C++ code
#define ADD_METHOD_CPP(cls, methodName, methodBody, nargs) \
JsSupport::addMethodCpp(J, #cls, #methodName, #cls ".prototype." #methodName, [](js_State *J) methodBody, nargs);
#define ADD_METHOD_CPPc(cls, methodName, methodBody, nargs) \
JsSupport::addMethodCpp(J, cls, #methodName, #cls ".prototype." #methodName, [](js_State *J) methodBody, nargs);
// add method defined in JavaScript
#define ADD_METHOD_JS(cls, method, code...) \
JsSupport::addMethodJs(J, #cls, #method, #code);
#define ADD_JS_FUNCTION(name, nargs) \
JsSupport::addJsFunction(J, #name, JsBinding::name, nargs);
#define ADD_JS_FUNCTIONinline(name, fnBody, nargs) \
JsSupport::addJsFunction(J, #name, [](js_State *J) fnBody, nargs);
#define ADD_NS_FUNCTION_CPP(ns, jsfn, cfn, nargs) \
JsSupport::addNsFunctionCpp(J, #ns, #jsfn, cfn, nargs);
#define ADD_NS_FUNCTION_CPPnew(ns, jsfn, fnBody, nargs) \
JsSupport::addNsFunctionCpp(J, #ns, #jsfn, [](js_State *J) fnBody, nargs);
#define ADD_NS_FUNCTION_JS(ns, func, code...) \
JsSupport::addNsFunctionJs(J, #ns, #func, #code);
#define AssertNargs(n) assert(js_gettop(J) == n+1);
#define AssertNargsRange(n1,n2) assert(n1+1 <= js_gettop(J) && js_gettop(J) <= n2+1);
#define AssertNargs2(nmin,nmax) assert(nmin+1 <= js_gettop(J) && js_gettop(J) <= nmax+1);
#define AssertStack(n) assert(js_gettop(J) == n);
//
// helper classes and functions
//
namespace JsBinding {
class StrPtr { // class that maps pointer<->string for the purpose of passing pointers to JS
public:
static void* s2p(const std::string &s) {
void *ptr = 0;
::sscanf(s.c_str(), "%p", &ptr);
return ptr;
}
static std::string p2s(void *ptr) {
char buf[sizeof(void*)*2+1];
::sprintf(buf, "%p", ptr);
return buf;
}
}; // StrPtr
// complex argument support
Vec3 objToVec3(js_State *J, int idx, const char *fname);
Mat3 objToMat3x3(js_State *J, int idx, const char *fname);
}; // JsBinding
//
// Return helpers
//
inline void Push(js_State *J, const bool &val) {
js_pushboolean(J, val ? 1 : 0);
}
inline void Push(js_State *J, const Float &val) {
js_pushnumber(J, val);
}
inline void Push(js_State *J, const unsigned &val) {
js_pushnumber(J, val);
}
inline void Push(js_State *J, const int &val) {
js_pushnumber(J, val);
}
inline void Push(js_State *J, const long &val) {
js_pushnumber(J, val);
}
inline void Push(js_State *J, const uint64_t &val) {
js_pushnumber(J, val);
}
inline void Push(js_State *J, const char *val) {
js_pushstring(J, val);
}
inline void Push(js_State *J, char val) {
const char c[2] = {val, 0};
js_pushstring(J, c);
}
inline void Push(js_State *J, const std::string &val) {
js_pushstring(J, val.c_str());
}
inline void Push(js_State *J, const Vec3 &val) {
js_newarray(J);
unsigned idx = 0;
for (auto c : val) {
js_pushnumber(J, c);
js_setindex(J, -2, idx++);
}
}
inline void Push(js_State *J, const Mat3 &val) {
js_newarray(J);
unsigned idx = 0;
for (auto &r : val) {
Push(J, r);
js_setindex(J, -2, idx++);
}
}
inline void Push(js_State *J, void* const &val) {
Push(J, JsBinding::StrPtr::p2s(val));
}
template<typename T, std::size_t SZ>
inline void Push(js_State *J, const std::array<T, SZ> &val) {
js_newarray(J);
unsigned idx = 0;
for (auto const &v : val) {
Push(J, v);
js_setindex(J, -2, idx++);
}
}
template<typename T>
inline void Push(js_State *J, const std::vector<T> &val) {
js_newarray(J);
unsigned idx = 0;
for (auto const &v : val) {
Push(J, v);
js_setindex(J, -2, idx++);
}
}
// this probably belongs in util.h or similar
template<typename T>
inline const std::string ToString(const T &t) {
std::ostringstream ss;
ss << t;
return ss.str();
}
template<>
inline const std::string ToString<std::string>(const std::string &t) {return t;}
template<typename K, typename T, class Comp>
inline void Push(js_State *J, const std::map<K,T,Comp> &val) {
js_newobject(J);
for (auto const &v : val) {
Push(J, v.second);
js_setproperty(J, -2, ToString<K>(v.first).c_str());
}
}
template<typename T1, typename T2>
inline void Push(js_State *J, const std::pair<T1,T2> &val) {
js_newarray(J);
Push(J, val.first);
js_setindex(J, -2, 0/*idx*/);
Push(J, val.second);
js_setindex(J, -2, 1/*idx*/);
}
template<typename T>
inline void Return(js_State *J, const T &val) {
Push(J, val);
}
inline void ReturnVoid(js_State *J) {
js_pushundefined(J);
}
inline void ReturnNull(js_State *J) {
js_pushnull(J);
}
//
// convenience macros to return object
//
#define ReturnObj(v) xnewo(J, v)
#define ReturnObjExt(type, v) Js##type::xnewo(J, v)
#define ReturnObjZ(v) xnewoZ(J, v)
#define ReturnObjZExt(type, v) Js##type::xnewoZ(J, v)
//
// convenience macro to access arguments
//
#define GetNArgs() (js_gettop(J)-1)
#define GetArg(type, n) ((type*)js_touserdata(J, n, TAG_##type))
#define GetArgExt(type, tag, n) ((type*)js_touserdata(J, n, tag))
#define GetArgZ(type, n) (!js_isundefined(J, n) ? (type*)js_touserdata(J, n, TAG_##type) : nullptr)
#define GetArgBoolean(n) js_toboolean(J, n)
#define GetArgFloat(n) js_tonumber(J, n)
#define GetArgString(n) std::string(js_tostring(J, n))
#define GetArgStringCptr(n) js_tostring(J, n)
#define GetArgChar(n) JsSupport::toChar(J, n)
#define GetArgInt32(n) js_toint32(J, n)
#define GetArgUInt32(n) js_touint32(J, n)
// complex arguments need yet to be templetized
#define GetArgVec3(n) JsBinding::objToVec3(J, n, __func__)
#define GetArgMat3x3(n) JsBinding::objToMat3x3(J, n, __func__)
#define GetArgUInt32Array(n) JsSupport::objToInt32Array(J, n, __func__)
#define GetArgUInt32ArrayZ(n) JsSupport::objToInt32ArrayZ(J, n, __func__)
//
// the error processing macro
//
#define JS_ERROR(stmt...) \
{ \
std::ostringstream ss; \
ss << stmt; \
JsSupport::error(J, ss.str()); \
}