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
|
#ifndef NODE_SQLITE3_SRC_MACROS_H
#define NODE_SQLITE3_SRC_MACROS_H
const char* sqlite_code_string(int code);
const char* sqlite_authorizer_string(int type);
#include <vector>
// TODO: better way to work around StringConcat?
#include <napi.h>
inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) {
return Napi::String::New(str1.Env(), str1.As<Napi::String>().Utf8Value() +
str2.As<Napi::String>().Utf8Value() );
}
// A Napi substitute IsInt32()
inline bool OtherIsInt(Napi::Number source) {
double orig_val = source.DoubleValue();
double int_val = static_cast<double>(source.Int32Value());
if (orig_val == int_val) {
return true;
} else {
return false;
}
}
#define IS_FUNCTION(cb) \
!cb.IsUndefined() && cb.IsFunction()
#define REQUIRE_ARGUMENTS(n) \
if (info.Length() < (n)) { \
Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \
return env.Null(); \
}
#define REQUIRE_ARGUMENT_EXTERNAL(i, var) \
if (info.Length() <= (i) || !info[i].IsExternal()) { \
Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \
return env.Null(); \
} \
Napi::External var = info[i].As<Napi::External>();
#define REQUIRE_ARGUMENT_FUNCTION(i, var) \
if (info.Length() <= (i) || !info[i].IsFunction()) { \
Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
return env.Null(); \
} \
Napi::Function var = info[i].As<Napi::Function>();
#define REQUIRE_ARGUMENT_STRING(i, var) \
if (info.Length() <= (i) || !info[i].IsString()) { \
Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \
return env.Null(); \
} \
std::string var = info[i].As<Napi::String>();
#define REQUIRE_ARGUMENT_INTEGER(i, var) \
if (info.Length() <= (i) || !info[i].IsNumber()) { \
Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
return env.Null(); \
} \
int var(info[i].As<Napi::Number>().Int32Value());
#define OPTIONAL_ARGUMENT_FUNCTION(i, var) \
Napi::Function var; \
if (info.Length() > i && !info[i].IsUndefined()) { \
if (!info[i].IsFunction()) { \
Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \
return env.Null(); \
} \
var = info[i].As<Napi::Function>(); \
}
#define OPTIONAL_ARGUMENT_INTEGER(i, var, default) \
int var; \
if (info.Length() <= (i)) { \
var = (default); \
} \
else if (info[i].IsNumber()) { \
if (OtherIsInt(info[i].As<Number>())) { \
var = info[i].As<Napi::Number>().Int32Value(); \
} \
} \
else { \
Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \
return env.Null(); \
}
#define DEFINE_CONSTANT_INTEGER(target, constant, name) \
Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \
static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
#define DEFINE_CONSTANT_STRING(target, constant, name) \
Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \
static_cast<napi_property_attributes>(napi_enumerable | napi_configurable)),
#define EXCEPTION(msg, errno, name) \
Napi::Value name = Napi::Error::New(env, \
StringConcat( \
StringConcat( \
Napi::String::New(env, sqlite_code_string(errno)), \
Napi::String::New(env, ": ") \
), \
(msg) \
).Utf8Value() \
).Value(); \
Napi::Object name ##_obj = name.As<Napi::Object>(); \
(name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \
(name ##_obj).Set( Napi::String::New(env, "code"), \
Napi::String::New(env, sqlite_code_string(errno)));
#define EMIT_EVENT(obj, argc, argv) \
TRY_CATCH_CALL((obj), \
(obj).Get("emit").As<Napi::Function>(),\
argc, argv \
);
// The Mac OS compiler complains when argv is NULL unless we
// first assign it to a locally defined variable.
#define TRY_CATCH_CALL(context, callback, argc, argv, ...) \
Napi::Value* passed_argv = argv;\
std::vector<napi_value> args;\
if ((argc != 0) && (passed_argv != NULL)) {\
args.assign(passed_argv, passed_argv + argc);\
}\
Napi::Value res = (callback).Call(Napi::Value(context), args); \
if (res.IsEmpty()) return __VA_ARGS__;
#define WORK_DEFINITION(name) \
Napi::Value name(const Napi::CallbackInfo& info); \
static void Work_Begin##name(Baton* baton); \
static void Work_##name(napi_env env, void* data); \
static void Work_After##name(napi_env env, napi_status status, void* data);
#ifdef DEBUG
#define ASSERT_STATUS() assert(status == 0);
#else
#define ASSERT_STATUS() (void)status;
#endif
#define CREATE_WORK(name, workerFn, afterFn) \
int status = napi_create_async_work(env, NULL, Napi::String::New(env, name),\
workerFn, afterFn, baton, &baton->request); \
\
ASSERT_STATUS(); \
napi_queue_async_work(env, baton->request);
#define STATEMENT_BEGIN(type) \
assert(baton); \
assert(baton->stmt); \
assert(!baton->stmt->locked); \
assert(!baton->stmt->finalized); \
assert(baton->stmt->prepared); \
baton->stmt->locked = true; \
baton->stmt->db->pending++; \
auto env = baton->stmt->Env(); \
CREATE_WORK("sqlite3.Statement."#type, Work_##type, Work_After##type);
#define STATEMENT_INIT(type) \
type* baton = static_cast<type*>(data); \
Statement* stmt = baton->stmt;
#define STATEMENT_MUTEX(name) \
if (!stmt->db->_handle) { \
stmt->status = SQLITE_MISUSE; \
stmt->message = "Database handle is closed"; \
return; \
} \
sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle);
#define STATEMENT_END() \
assert(stmt->locked); \
assert(stmt->db->pending); \
stmt->locked = false; \
stmt->db->pending--; \
stmt->Process(); \
stmt->db->Process();
#define BACKUP_BEGIN(type) \
assert(baton); \
assert(baton->backup); \
assert(!baton->backup->locked); \
assert(!baton->backup->finished); \
assert(baton->backup->inited); \
baton->backup->locked = true; \
baton->backup->db->pending++; \
auto env = baton->backup->Env(); \
CREATE_WORK("sqlite3.Backup."#type, Work_##type, Work_After##type);
#define BACKUP_INIT(type) \
type* baton = static_cast<type*>(data); \
Backup* backup = baton->backup;
#define BACKUP_END() \
assert(backup->locked); \
assert(backup->db->pending); \
backup->locked = false; \
backup->db->pending--; \
backup->Process(); \
backup->db->Process();
#endif
|