/
BaseSignature.h
218 lines (156 loc) · 6 KB
/
BaseSignature.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
// This file is part of nbind, copyright (C) 2014-2015 BusFaster Ltd.
// Released under the MIT license, see LICENSE.
#pragma once
namespace nbind {
// Constants for packing several method signatures into a single overloaded name,
// and several methods with identical argument and return types into a single signature.
static constexpr unsigned int overloadShift = 16;
static constexpr unsigned int signatureMemberMask = 0xffff;
// Base class for signatures of all constructors, functions and methods.
// A signature represents a unique set of argument and return types,
// and the invoker functions needed to deal with such types.
class BaseSignature {
public:
// Type of the signature.
// Determines the actual signature class of each instance.
enum class Type: unsigned int { function, method, getter, setter, constructor };
BaseSignature(Type type, funcPtr caller, const TYPEID *typeList, unsigned int arity) :
type(type), caller(caller), typeList(typeList), arity(arity) {}
Type getType() const { return(type); }
funcPtr getCaller() const { return(caller); }
// Type list is one item longer than arity,
// because it starts with the return type (not counted in arity).
const TYPEID *getTypeList() const { return(typeList); }
unsigned int getArity() const { return(arity); }
// A value constructor pointer is included in each signature,
// but only used for constructors.
funcPtr getValueConstructor() const { return(valueConstructor); }
void setValueConstructor(funcPtr valueConstructor) {
this->valueConstructor = valueConstructor;
}
template<typename MethodType>
static inline funcPtr getDirect(MethodType func) { return(nullptr); }
private:
const Type type;
const funcPtr caller;
const TYPEID *typeList;
const unsigned int arity;
funcPtr valueConstructor;
};
// Templated static class for each different function call signature exposed by the
// Node.js plugin. Used to pass arguments and return values between C++ and Node.js.
// Everything must be static because the V8 JavaScript engine wants a single
// function pointer to call, so each template variant is a singleton class.
template <class Signature, typename ReturnType, typename... Args>
class TemplatedBaseSignature : public BaseSignature {
public:
TemplatedBaseSignature() : BaseSignature(
Signature::typeExpr,
reinterpret_cast<funcPtr>(Signature::call),
listTypes<ReturnType, Args...>(),
sizeof...(Args)
) {}
// Making the instance a direct class member fails on some platforms.
// Maybe it registers the signature too early.
static Signature &getInstance() {
// Linkage for a singleton instance of each templated class.
static Signature instance;
return(instance);
}
// Information about a single named function.
// This wrapper around Signature::MethodType is needed because TemplatedBaseSignature itself cannot see the type directly.
// It's passed as a CRTP argument and is not fully defined here, but inside an inner class that doesn't matter.
struct MethodInfo {
typedef typename Signature::MethodType MethodType;
MethodInfo(MethodType func) : func(func) {}
const MethodType func;
};
static const MethodInfo &getMethod(unsigned int num) {
return(getInstance().funcVect[num]);
}
template <typename MethodType>
static unsigned int addMethod(MethodType func) {
auto &funcVect = getInstance().funcVect;
# if defined(BUILDING_NODE_EXTENSION)
if(funcVect.size() >= signatureMemberMask) {
// TODO:
// ABORT ABORT ABORT too many functions with the same signature!
}
# endif // BUILDING_NODE_EXTENSION
funcVect.emplace_back(func);
return(funcVect.size() - 1);
}
#if defined(BUILDING_NODE_EXTENSION)
// Specialize static caller functions defined in Caller.h.
typedef Caller<
ReturnType,
typename emscripten::internal::MapWithIndex<
TypeList,
ArgFromWire,
Args...
>::type
> CallWrapper;
template <typename NanArgs>
static bool typesAreValid(NanArgs &args) {
typedef Checker<
typename emscripten::internal::MapWithIndex<
TypeList,
CheckWire,
Args...
>::type
> checker;
return(checker::typesAreValid(args));
}
static bool arityIsValid(const Nan::FunctionCallbackInfo<v8::Value> &args) {
static constexpr decltype(args.Length()) arity = sizeof...(Args);
return(args.Length() == arity);
}
template <typename Value>
static bool arityIsValid(const Nan::PropertyCallbackInfo<Value> &args) {
return(true);
}
template <typename NanArgs, typename Bound>
static bool getTargetSafely(NanArgs &nanArgs, Bound **targetOut) {
v8::Local<v8::Object> targetWrapped = nanArgs.This();
Bound *target = node::ObjectWrap::Unwrap<BindWrapper<Bound>>(targetWrapped)->getBound();
if(target == nullptr) return(false);
*targetOut = target;
return(true);
}
// Overload second argument, effectively a partial specialization of the function template above.
template <typename NanArgs>
static bool getTargetSafely(NanArgs &nanArgs, void **targetOut) {
return(true);
}
template <typename Bound, typename V8Args, typename NanArgs>
static void callInnerSafely(V8Args &args, NanArgs &nanArgs) {
Bound *target = nullptr;
if(!arityIsValid(nanArgs)) {
// TODO: When function is overloaded, this test could be skipped...
Nan::ThrowError("Wrong number of arguments");
return;
}
if(!Signature::typesAreValid(args)) {
Nan::ThrowTypeError("Type mismatch");
return;
}
if(!getTargetSafely(nanArgs, &target)) {
Nan::ThrowError("Attempt to access deleted object");
return;
}
Status::clearError();
try {
Signature::callInner(args, nanArgs, target);
if(Status::getError() != nullptr) Nan::ThrowError(Status::getError());
} catch(const std::exception &ex) {
const char *message = Status::getError();
if(message == nullptr) message = ex.what();
Nan::ThrowError(message);
}
}
#endif // BUILDING_NODE_EXTENSION
// The funcVect vector cannot be moved to BaseSignature because it can contain pointers to
// functions or class methods, and there isn't a single pointer type able to hold both.
std::vector<struct MethodInfo> funcVect;
};
} // namespace