1 #define DART_SHARED_LIB
2 #include "dart_api.h"
3 #include "dart_native_api.h"
4 #include <ejdb2.h>
5 #include <ejdb2/iowow/iwconv.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <pthread.h>
9 #include <errno.h>
10
11 typedef void (*WrapperFunction)(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
12
13 typedef enum {
14 _EJD_ERROR_START = (IW_ERROR_START + 15000UL + 4000),
15 EJD_ERROR_CREATE_PORT, /**< Failed to create a Dart port (EJD_ERROR_CREATE_PORT) */
16 EJD_ERROR_POST_PORT, /**< Failed to post message to Dart port (EJD_ERROR_POST_PORT) */
17 EJD_ERROR_INVALID_NATIVE_CALL_ARGS, /**< Invalid native function call args (EJD_ERROR_INVALID_NATIVE_CALL_ARGS) */
18 EJD_ERROR_INVALID_STATE, /**< Invalid native extension state (EJD_ERROR_INVALID_STATE) */
19 _EJD_ERROR_END,
20 } jbr_ecode_t;
21
22 struct NativeFunctionLookup {
23 const char *name;
24 Dart_NativeFunction fn;
25 };
26
27 struct WrapperFunctionLookup {
28 const char *name;
29 WrapperFunction fn;
30 };
31
32 typedef struct EJDB2Handle {
33 EJDB db;
34 char *path;
35 int64_t refs;
36 struct EJDB2Handle *next;
37 struct EJDB2Handle *prev;
38 } EJDB2Handle;
39
40 typedef struct EJDB2Context {
41 Dart_Port port;
42 EJDB2Handle *dbh;
43 Dart_WeakPersistentHandle wph;
44 } EJDB2Context;
45
46 typedef struct EJDB2JQLContext {
47 JQL q;
48 Dart_WeakPersistentHandle wph;
49 } EJDB2JQLContext;
50
51 static pthread_mutex_t k_shared_scope_mtx = PTHREAD_MUTEX_INITIALIZER;
52 static EJDB2Handle *k_head_handle;
53
54 static Dart_NativeFunction ejd_resolve_name(Dart_Handle name, int argc, bool *auto_setup_scope);
55
56 IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle);
57 IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc);
58 IW_INLINE void ejd_error_rc_throw(iwrc rc);
59
60 static void ejd_explain_rc(Dart_NativeArguments args);
61 static void ejd_exec(Dart_NativeArguments args);
62 static void ejd_exec_check(Dart_NativeArguments args);
63 static void ejd_jql_set(Dart_NativeArguments args);
64 static void ejd_jql_get_limit(Dart_NativeArguments args);
65
66 static void ejd_port_handler(Dart_Port receive_port, Dart_CObject *msg);
67 static void ejd_ctx_finalizer(void *isolate_callback_data, void *peer);
68
69 static void ejd_port(Dart_NativeArguments arguments);
70 static void ejd_set_handle(Dart_NativeArguments args);
71 static void ejd_get_handle(Dart_NativeArguments args);
72 static void ejd_create_query(Dart_NativeArguments args);
73
74 static void ejd_open_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
75 static void ejd_close_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
76 static void ejd_get_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
77 static void ejd_put_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
78 static void ejd_del_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
79 static void ejd_patch_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
80 static void ejd_idx_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
81 static void ejd_rmi_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
82 static void ejd_rmc_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
83 static void ejd_info_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
84 static void ejd_rename_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
85 static void ejd_bkp_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
86
87 static struct NativeFunctionLookup k_scoped_functions[] = {
88 { "port", ejd_port },
89 { "exec", ejd_exec },
90 { "check_exec", ejd_exec_check },
91 { "jql_set", ejd_jql_set },
92 { "jql_get_limit", ejd_jql_get_limit },
93 { "create_query", ejd_create_query },
94 { "set_handle", ejd_set_handle },
95 { "get_handle", ejd_get_handle },
96 { "explain_rc", ejd_explain_rc },
97 { 0, 0 }
98 };
99
100 static struct WrapperFunctionLookup k_wrapped_functions[] = {
101 { "get", ejd_get_wrapped },
102 { "put", ejd_put_wrapped },
103 { "del", ejd_del_wrapped },
104 { "rename", ejd_rename_wrapped },
105 { "patch", ejd_patch_wrapped },
106 { "idx", ejd_idx_wrapped },
107 { "rmi", ejd_rmi_wrapped },
108 { "rmc", ejd_rmc_wrapped },
109 { "info", ejd_info_wrapped },
110 { "open", ejd_open_wrapped },
111 { "close", ejd_close_wrapped },
112 { "bkp", ejd_bkp_wrapped },
113 { 0, 0 }
114 };
115
116 #define EJTH(h_) ejd_error_check_propagate(h_)
117
118 #define EJGO(h_, rh_, label_) \
119 if (Dart_IsError(h_)) { \
120 rh_ = (h_); \
121 goto label_; \
122 }
123
124 #define EJLIB() EJTH(Dart_LookupLibrary(Dart_NewStringFromCString("package:ejdb2_dart/ejdb2_dart.dart")))
125
126 #define EJPORT_RC(co_, rc_) \
127 if (rc_) { \
128 (co_)->type = Dart_CObject_kInt64; \
129 (co_)->value.as_int64 = (rc_); \
130 }
131
cobject_str(Dart_CObject * co,bool nulls,iwrc * rcp)132 IW_INLINE char *cobject_str(Dart_CObject *co, bool nulls, iwrc *rcp) {
133 *rcp = 0;
134 if (co) {
135 if (co->type == Dart_CObject_kString) {
136 return co->value.as_string;
137 } else if (nulls && (co->type == Dart_CObject_kNull)) {
138 return 0;
139 }
140 }
141 *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
142 return 0;
143 }
144
cobject_int(Dart_CObject * co,bool nulls,iwrc * rcp)145 IW_INLINE int64_t cobject_int(Dart_CObject *co, bool nulls, iwrc *rcp) {
146 *rcp = 0;
147 if (co) {
148 if (co->type == Dart_CObject_kInt32) {
149 return co->value.as_int32;
150 } else if (co->type == Dart_CObject_kInt64) {
151 return co->value.as_int64;
152 } else if (nulls && (co->type == Dart_CObject_kNull)) {
153 return 0;
154 }
155 }
156 *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
157 return 0;
158 }
159
cobject_bool(Dart_CObject * co,bool nulls,iwrc * rcp)160 IW_INLINE bool cobject_bool(Dart_CObject *co, bool nulls, iwrc *rcp) {
161 *rcp = 0;
162 if (co) {
163 if (co->type == Dart_CObject_kBool) {
164 return co->value.as_bool;
165 } else if (nulls && (co->type == Dart_CObject_kNull)) {
166 return false;
167 }
168 }
169 *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
170 return false;
171 }
172
cobject_double(Dart_CObject * co,bool nulls,iwrc * rcp)173 IW_INLINE double cobject_double(Dart_CObject *co, bool nulls, iwrc *rcp) {
174 *rcp = 0;
175 if (co) {
176 if (co->type == Dart_CObject_kDouble) {
177 return co->value.as_double;
178 } else if (nulls && (co->type == Dart_CObject_kNull)) {
179 return 0;
180 }
181 }
182 *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
183 return 0;
184 }
185
ejd_error_check_propagate(Dart_Handle handle)186 IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle) {
187 if (Dart_IsError(handle)) {
188 Dart_PropagateError(handle);
189 }
190 return handle;
191 }
192
ejd_error_object_handle(iwrc rc,const char * msg)193 static Dart_Handle ejd_error_object_handle(iwrc rc, const char *msg) {
194 Dart_Handle hmsg = Dart_Null();
195 if (msg) {
196 hmsg = EJTH(Dart_NewStringFromCString(msg));
197 } else if (rc) {
198 const char *explained = iwlog_ecode_explained(rc);
199 if (explained) {
200 hmsg = EJTH(Dart_NewStringFromCString(explained));
201 }
202 }
203 Dart_Handle hrc = EJTH(Dart_NewIntegerFromUint64(rc));
204 Dart_Handle hclass = EJTH(Dart_GetClass(EJLIB(), Dart_NewStringFromCString("EJDB2Error")));
205 Dart_Handle args[] = { hrc, hmsg };
206 return Dart_New(hclass, Dart_Null(), 2, args);
207 }
208
ejd_error_rc_create(iwrc rc)209 IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc) {
210 const char *msg = iwlog_ecode_explained(rc);
211 return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
212 }
213
ejd_error_rc_create2(iwrc rc,const char * msg)214 IW_INLINE Dart_Handle ejd_error_rc_create2(iwrc rc, const char *msg) {
215 return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
216 }
217
ejd_error_rc_throw(iwrc rc)218 IW_INLINE void ejd_error_rc_throw(iwrc rc) {
219 Dart_PropagateError(ejd_error_rc_create(rc));
220 }
221
ejd_port(Dart_NativeArguments args)222 static void ejd_port(Dart_NativeArguments args) {
223 EJDB2Context *ctx;
224 intptr_t ptr = 0;
225
226 Dart_EnterScope();
227 Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
228 EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
229
230 if (!ptr) {
231 ctx = malloc(sizeof(*ctx));
232 if (!ctx) {
233 Dart_SetReturnValue(args, ejd_error_rc_create(IW_ERROR_ALLOC));
234 goto finish;
235 }
236 ctx->dbh = 0;
237 ctx->port = Dart_NewNativePort("ejd_port_handler", ejd_port_handler, true);
238 if (ctx->port == ILLEGAL_PORT) {
239 Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_CREATE_PORT));
240 goto finish;
241 }
242 ctx->wph = Dart_NewWeakPersistentHandle(self, ctx, sizeof(*ctx), ejd_ctx_finalizer);
243 if (!ctx->wph) {
244 Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_INVALID_STATE));
245 goto finish;
246 }
247 Dart_SetNativeInstanceField(self, 0, (intptr_t) ctx);
248 } else {
249 ctx = (void*) ptr;
250 }
251 Dart_SetReturnValue(args, EJTH(Dart_NewSendPort(ctx->port)));
252
253 finish:
254 Dart_ExitScope();
255 }
256
ejd_get_handle(Dart_NativeArguments args)257 static void ejd_get_handle(Dart_NativeArguments args) {
258 intptr_t ptr = 0;
259 Dart_EnterScope();
260 Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
261 EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
262 EJDB2Context *ctx = (void*) ptr;
263 if (ctx && ctx->dbh) {
264 Dart_SetReturnValue(args, Dart_NewInteger((intptr_t) ctx->dbh));
265 } else {
266 Dart_SetReturnValue(args, Dart_Null());
267 }
268 Dart_ExitScope();
269 }
270
ejd_set_handle(Dart_NativeArguments args)271 static void ejd_set_handle(Dart_NativeArguments args) {
272 intptr_t ptr;
273 Dart_EnterScope();
274 Dart_SetReturnValue(args, Dart_Null());
275 Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
276 EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
277 EJDB2Context *ctx = (void*) ptr;
278 if (!ctx) {
279 Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_INVALID_STATE));
280 goto finish;
281 }
282 Dart_Handle dh = Dart_GetNativeArgument(args, 1);
283 if (Dart_IsInteger(dh)) {
284 int64_t llv;
285 EJTH(Dart_GetNativeIntegerArgument(args, 1, &llv));
286 ctx->dbh = (void*) llv;
287 } else if (Dart_IsNull(dh)) {
288 ctx->dbh = 0;
289 }
290
291 finish:
292 Dart_ExitScope();
293 }
294
ejd_jql_finalizer(void * isolate_callback_data,void * peer)295 static void ejd_jql_finalizer(void *isolate_callback_data, void *peer) {
296 EJDB2JQLContext *ctx = peer;
297 if (ctx) {
298 if (ctx->q) {
299 jql_destroy(&ctx->q);
300 }
301 if (ctx->wph && Dart_CurrentIsolateGroup()) {
302 Dart_DeleteWeakPersistentHandle(ctx->wph);
303 }
304 free(ctx);
305 }
306 }
307
ejd_create_query(Dart_NativeArguments args)308 static void ejd_create_query(Dart_NativeArguments args) {
309 Dart_EnterScope();
310
311 iwrc rc = 0;
312 JQL q = 0;
313 intptr_t ptr = 0;
314 const char *query = 0;
315 const char *collection = 0;
316 EJDB2JQLContext *qctx = 0;
317
318 Dart_Handle ret = Dart_Null();
319 Dart_Handle hlib = EJLIB();
320 Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
321 Dart_Handle hquery = EJTH(Dart_GetNativeArgument(args, 1));
322 Dart_Handle hcoll = EJTH(Dart_GetNativeArgument(args, 2));
323
324 EJTH(Dart_StringToCString(hquery, &query));
325 if (Dart_IsString(hcoll)) {
326 EJTH(Dart_StringToCString(hcoll, &collection));
327 }
328 EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));
329
330 EJDB2Context *ctx = (void*) ptr;
331 if (!ctx || !ctx->dbh || !ctx->dbh->db) {
332 rc = EJD_ERROR_INVALID_STATE;
333 goto finish;
334 }
335 rc = jql_create2(&q, collection, query, JQL_KEEP_QUERY_ON_PARSE_ERROR | JQL_SILENT_ON_PARSE_ERROR);
336 RCGO(rc, finish);
337
338 if (!collection) {
339 collection = jql_collection(q);
340 }
341 hcoll = Dart_NewStringFromCString(collection);
342 EJGO(hcoll, ret, finish);
343
344 Dart_Handle hclass = Dart_GetClass(hlib, Dart_NewStringFromCString("JQL"));
345 EJGO(hclass, ret, finish);
346
347 Dart_Handle jqargs[] = { hself, hquery, hcoll };
348 Dart_Handle jqinst = Dart_New(hclass, Dart_NewStringFromCString("_"), 3, jqargs);
349 EJGO(jqinst, ret, finish);
350
351 ret = Dart_SetNativeInstanceField(jqinst, 0, (intptr_t) q);
352 EJGO(ret, ret, finish);
353
354 qctx = malloc(sizeof(*qctx));
355 if (!qctx) {
356 Dart_SetReturnValue(args, ejd_error_rc_create(IW_ERROR_ALLOC));
357 goto finish;
358 }
359
360 qctx->q = q;
361 qctx->wph = Dart_NewWeakPersistentHandle(jqinst, qctx, jql_estimate_allocated_size(q) + sizeof(*qctx),
362 ejd_jql_finalizer);
363 if (!qctx->wph) {
364 rc = EJD_ERROR_INVALID_STATE;
365 goto finish;
366 }
367
368 ret = jqinst;
369
370 finish:
371 if (rc || Dart_IsError(ret)) {
372 if (rc) {
373 if (q && (rc == JQL_ERROR_QUERY_PARSE)) {
374 ret = ejd_error_rc_create2(rc, jql_error(q));
375 } else {
376 ret = ejd_error_rc_create(rc);
377 }
378 }
379 if (q) {
380 jql_destroy(&q);
381 }
382 free(qctx);
383 }
384 Dart_SetReturnValue(args, ret);
385 Dart_ExitScope();
386 }
387
388 // Query execution context
389 // Contains a state to manage resultset backpressure
390 typedef struct QCTX {
391 bool aggregate_count;
392 bool explain;
393 bool paused;
394 int pending_count;
395 Dart_Port reply_port;
396 JQL q;
397 EJDB2Context *dctx;
398 int64_t limit;
399 pthread_mutex_t mtx;
400 pthread_cond_t cond;
401 } *QCTX;
402
ejd_exec_pause_guard(QCTX qctx)403 static iwrc ejd_exec_pause_guard(QCTX qctx) {
404 iwrc rc = 0;
405 int rci = pthread_mutex_lock(&qctx->mtx);
406 if (rci) {
407 return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
408 }
409 while (qctx->paused) {
410 rci = pthread_cond_wait(&qctx->cond, &qctx->mtx);
411 if (rci) {
412 rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
413 break;
414 }
415 }
416 qctx->pending_count++;
417 pthread_mutex_unlock(&qctx->mtx);
418 return rc;
419 }
420
ejd_exec_visitor(struct _EJDB_EXEC * ux,EJDB_DOC doc,int64_t * step)421 static iwrc ejd_exec_visitor(struct _EJDB_EXEC *ux, EJDB_DOC doc, int64_t *step) {
422 iwrc rc = 0;
423 QCTX qctx = ux->opaque;
424
425 rc = ejd_exec_pause_guard(qctx);
426 RCRET(rc);
427
428 IWXSTR *xstr = iwxstr_new();
429 if (!xstr) {
430 return iwrc_set_errno(IW_ERROR_ALLOC, errno);
431 }
432 if (doc->node) {
433 rc = jbn_as_json(doc->node, jbl_xstr_json_printer, xstr, 0);
434 } else {
435 rc = jbl_as_json(doc->raw, jbl_xstr_json_printer, xstr, 0);
436 }
437 RCGO(rc, finish);
438
439 Dart_CObject result, rv1, rv2, rv3;
440 Dart_CObject *rv[] = { &rv1, &rv2, &rv3 };
441 result.type = Dart_CObject_kArray;
442 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
443 result.value.as_array.values = rv;
444 rv1.type = Dart_CObject_kInt64;
445 rv1.value.as_int64 = doc->id;
446 rv2.type = Dart_CObject_kString;
447 rv2.value.as_string = iwxstr_ptr(xstr);
448 if (ux->log) { // Add explain log to the first record
449 rv3.type = Dart_CObject_kString;
450 rv3.value.as_string = iwxstr_ptr(ux->log);
451 ux->log = 0;
452 } else {
453 rv3.type = Dart_CObject_kNull;
454 }
455 if (!Dart_PostCObject(qctx->reply_port, &result)) {
456 *step = 0; // End of cursor loop
457 }
458
459 finish:
460 iwxstr_destroy(xstr);
461 return rc;
462 }
463
ejd_exec_port_handler(Dart_Port receive_port,Dart_CObject * msg)464 static void ejd_exec_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
465 iwrc rc = 0;
466 Dart_CObject result = { .type = Dart_CObject_kNull };
467 if ((msg->type != Dart_CObject_kInt64) || !msg->value.as_int64) {
468 iwlog_error2("Invalid message recieved");
469 return;
470 }
471 QCTX qctx = (void*) msg->value.as_int64;
472 IWXSTR *exlog = 0;
473 EJDB_EXEC ux = { 0 };
474 EJDB2Context *dctx = qctx->dctx;
475
476 if (!qctx->q || !dctx || !dctx->dbh || !dctx->dbh->db) {
477 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
478 goto finish;
479 }
480 if (qctx->explain) {
481 exlog = iwxstr_new();
482 if (!exlog) {
483 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
484 goto finish;
485 }
486 }
487
488 qctx->aggregate_count = jql_has_aggregate_count(qctx->q);
489
490 ux.q = qctx->q;
491 ux.db = dctx->dbh->db;
492 ux.visitor = qctx->aggregate_count ? 0 : ejd_exec_visitor;
493 ux.opaque = qctx;
494 ux.log = exlog;
495 ux.limit = qctx->limit;
496
497 rc = ejdb_exec(&ux);
498 RCGO(rc, finish);
499
500 if (qctx->aggregate_count) {
501 Dart_CObject result, rv1, rv2, rv3;
502 Dart_CObject *rv[] = { &rv1, &rv2, &rv3 };
503 result.type = Dart_CObject_kArray;
504 result.value.as_array.length = (sizeof(rv) / sizeof(rv[0]));
505 result.value.as_array.values = rv;
506 rv1.type = Dart_CObject_kInt64;
507 rv1.value.as_int64 = ux.cnt;
508 rv2.type = Dart_CObject_kNull;
509 if (exlog) {
510 rv3.type = Dart_CObject_kString;
511 rv3.value.as_string = iwxstr_ptr(exlog);
512 } else {
513 rv3.type = Dart_CObject_kNull;
514 }
515 Dart_PostCObject(qctx->reply_port, &result);
516 } else if (exlog && (ux.cnt == 0)) {
517 result.type = Dart_CObject_kString;
518 result.value.as_string = iwxstr_ptr(exlog);
519 }
520
521 finish:
522 if (rc) {
523 iwlog_ecode_error3(rc);
524 EJPORT_RC(&result, rc);
525 }
526 if (qctx->reply_port != ILLEGAL_PORT) {
527 Dart_PostCObject(qctx->reply_port, &result); // Last NULL or error(int)
528 }
529 if (exlog) {
530 iwxstr_destroy(exlog);
531 }
532 Dart_CloseNativePort(receive_port);
533 }
534
ejd_exec(Dart_NativeArguments args)535 static void ejd_exec(Dart_NativeArguments args) {
536 Dart_EnterScope();
537
538 iwrc rc = 0;
539 intptr_t qptr, ptr = 0;
540 bool explain = false;
541 int64_t limit = 0;
542 QCTX qctx = 0;
543
544 Dart_Port reply_port = ILLEGAL_PORT;
545 Dart_Port exec_port = ILLEGAL_PORT;
546 Dart_Handle ret = Dart_Null();
547
548 Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
549 Dart_Handle hdb = EJTH(Dart_GetField(hself, Dart_NewStringFromCString("db")));
550 Dart_Handle hport = EJTH(Dart_GetNativeArgument(args, 1));
551
552 EJTH(Dart_GetNativeBooleanArgument(args, 2, &explain));
553 EJTH(Dart_GetNativeIntegerArgument(args, 3, &limit));
554 EJTH(Dart_SendPortGetId(hport, &reply_port));
555
556 EJTH(Dart_GetNativeInstanceField(hdb, 0, &ptr));
557 EJDB2Context *dctx = (void*) ptr;
558 if (!dctx || !dctx->dbh || !dctx->dbh->db) {
559 rc = EJD_ERROR_INVALID_STATE;
560 goto finish;
561 }
562
563 // JQL pointer
564 EJTH(Dart_GetNativeInstanceField(hself, 0, &qptr));
565 if (!qptr) {
566 rc = EJD_ERROR_INVALID_STATE;
567 goto finish;
568 }
569
570 // JQL exec port
571 exec_port = Dart_NewNativePort("ejd_exec_port_handler", ejd_exec_port_handler, false);
572 if (exec_port == ILLEGAL_PORT) {
573 rc = EJD_ERROR_CREATE_PORT;
574 goto finish;
575 }
576
577 qctx = calloc(1, sizeof(*qctx));
578 if (!qctx) {
579 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
580 goto finish;
581 }
582
583 pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
584 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
585 memcpy(&qctx->mtx, &mtx, sizeof(mtx));
586 memcpy(&qctx->cond, &cond, sizeof(cond));
587
588 qctx->reply_port = reply_port;
589 qctx->q = (void*) qptr;
590 qctx->dctx = dctx;
591 qctx->explain = explain;
592 qctx->limit = limit;
593
594 // Now post a message to the query executor
595 Dart_CObject msg;
596 msg.type = Dart_CObject_kInt64;
597 msg.value.as_int64 = (int64_t) qctx;
598
599 if (!Dart_PostCObject(exec_port, &msg)) {
600 rc = EJD_ERROR_POST_PORT;
601 goto finish;
602 }
603
604 finish:
605 if (rc || Dart_IsError(ret)) {
606 if (qctx) {
607 free(qctx);
608 qctx = 0;
609 }
610 if (exec_port != ILLEGAL_PORT) {
611 Dart_CloseNativePort(exec_port);
612 }
613 if (rc) {
614 ret = ejd_error_rc_create(rc);
615 }
616 }
617 if (qctx) {
618 ret = Dart_NewInteger((int64_t) (void*) qctx);
619 }
620 Dart_SetReturnValue(args, ret);
621 Dart_ExitScope();
622 }
623
ejd_exec_check(Dart_NativeArguments args)624 static void ejd_exec_check(Dart_NativeArguments args) {
625 iwrc rc = 0;
626 Dart_EnterScope();
627 Dart_Handle ret = Dart_Null();
628
629 if (Dart_GetNativeArgumentCount(args) < 3) {
630 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
631 goto finish;
632 }
633 bool terminate = false;
634 int64_t hptr = 0;
635
636 EJTH(Dart_GetNativeIntegerArgument(args, 1, &hptr));
637 EJTH(Dart_GetNativeBooleanArgument(args, 2, &terminate));
638
639 if (hptr < 1) {
640 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
641 goto finish;
642 }
643 QCTX qctx = (void*) hptr;
644
645 if (terminate) {
646 free(qctx);
647 goto finish;
648 }
649
650 int rci = pthread_mutex_lock(&qctx->mtx);
651 if (rci) {
652 rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
653 goto finish;
654 }
655 qctx->pending_count--;
656 if (qctx->paused) {
657 if (qctx->pending_count < 32) {
658 qctx->paused = false;
659 pthread_cond_broadcast(&qctx->cond);
660 }
661 } else if (qctx->pending_count > 64) {
662 qctx->paused = true;
663 pthread_cond_broadcast(&qctx->cond);
664 }
665 pthread_mutex_unlock(&qctx->mtx);
666
667 finish:
668 if (rc) {
669 ret = ejd_error_rc_create(rc);
670 }
671 Dart_SetReturnValue(args, ret);
672 Dart_ExitScope();
673 }
674
ejd_free_str(void * ptr,void * op)675 static void ejd_free_str(void *ptr, void *op) {
676 free(ptr);
677 }
678
ejd_free_json_node(void * ptr,void * op)679 static void ejd_free_json_node(void *ptr, void *op) {
680 IWPOOL *pool = op;
681 if (pool) {
682 iwpool_destroy(pool);
683 }
684 }
685
ejd_jql_get_limit(Dart_NativeArguments args)686 static void ejd_jql_get_limit(Dart_NativeArguments args) {
687 Dart_EnterScope();
688 Dart_Handle ret = Dart_Null();
689 iwrc rc;
690 int64_t limit;
691 intptr_t ptr = 0;
692 Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
693 EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));
694 JQL q = (void*) ptr;
695 if (!q) {
696 rc = EJD_ERROR_INVALID_STATE;
697 goto finish;
698 }
699 rc = jql_get_limit(q, &limit);
700 RCGO(rc, finish);
701 ret = Dart_NewInteger(limit);
702
703 finish:
704 if (rc || Dart_IsError(ret)) {
705 if (rc) {
706 ret = ejd_error_rc_create(rc);
707 }
708 }
709 Dart_SetReturnValue(args, ret);
710 Dart_ExitScope();
711 }
712
ejd_jql_set(Dart_NativeArguments args)713 static void ejd_jql_set(Dart_NativeArguments args) {
714 // void set(dynamic place, dynamic value) native 'ejd_jql_set';
715 Dart_EnterScope();
716 Dart_Handle ret = Dart_Null();
717
718 iwrc rc = 0;
719 intptr_t ptr = 0;
720 Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
721 EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));
722 JQL q = (void*) ptr;
723 if (!q) {
724 rc = EJD_ERROR_INVALID_STATE;
725 goto finish;
726 }
727 int64_t npl = 0, type = 0;
728 const char *svalue, *spl = 0;
729 Dart_Handle hpl = EJTH(Dart_GetNativeArgument(args, 1));
730 Dart_Handle hvalue = EJTH(Dart_GetNativeArgument(args, 2));
731 Dart_Handle htype = EJTH(Dart_GetNativeArgument(args, 3));
732
733 if (Dart_IsString(hpl)) {
734 EJTH(Dart_StringToCString(hpl, &spl));
735 } else {
736 EJTH(Dart_IntegerToInt64(hpl, &npl));
737 }
738 if (Dart_IsInteger(htype)) {
739 EJTH(Dart_IntegerToInt64(htype, &type));
740 }
741 if (type == 1) { // JSON
742 EJTH(Dart_StringToCString(hvalue, &svalue));
743 JBL_NODE node;
744 IWPOOL *pool = iwpool_create(64);
745 if (!pool) {
746 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
747 goto finish;
748 }
749 rc = jbn_from_json(svalue, &node, pool);
750 if (rc) {
751 iwpool_destroy(pool);
752 goto finish;
753 }
754 rc = jql_set_json2(q, spl, npl, node, ejd_free_json_node, pool);
755 if (rc) {
756 iwpool_destroy(pool);
757 goto finish;
758 }
759 } else if (type == 2) { // Regexp
760 EJTH(Dart_StringToCString(hvalue, &svalue));
761 char *str = strdup(svalue);
762 if (!str) {
763 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
764 goto finish;
765 }
766 rc = jql_set_regexp2(q, spl, npl, str, ejd_free_str, 0);
767 if (rc) {
768 free(str);
769 goto finish;
770 }
771 } else { // All other cases
772 if (Dart_IsString(hvalue)) {
773 EJTH(Dart_StringToCString(hvalue, &svalue));
774 char *str = strdup(svalue);
775 if (!str) {
776 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
777 goto finish;
778 }
779 rc = jql_set_str2(q, spl, npl, str, ejd_free_str, 0);
780 if (rc) {
781 free(str);
782 goto finish;
783 }
784 } else if (Dart_IsInteger(hvalue)) {
785 int64_t val;
786 EJTH(Dart_IntegerToInt64(hvalue, &val));
787 rc = jql_set_i64(q, spl, npl, val);
788 } else if (Dart_IsDouble(hvalue)) {
789 double val;
790 EJTH(Dart_DoubleValue(hvalue, &val));
791 rc = jql_set_f64(q, spl, npl, val);
792 } else if (Dart_IsBoolean(hvalue)) {
793 bool val;
794 EJTH(Dart_BooleanValue(hvalue, &val));
795 rc = jql_set_bool(q, spl, npl, val);
796 } else if (Dart_IsNull(hvalue)) {
797 rc = jql_set_null(q, spl, npl);
798 }
799 }
800
801 finish:
802 if (rc || Dart_IsError(ret)) {
803 if (rc) {
804 ret = ejd_error_rc_create(rc);
805 }
806 }
807 Dart_SetReturnValue(args, ret);
808 Dart_ExitScope();
809 }
810
ejd_explain_rc(Dart_NativeArguments args)811 static void ejd_explain_rc(Dart_NativeArguments args) {
812 Dart_EnterScope();
813 int64_t llv = 0;
814 EJTH(Dart_GetNativeIntegerArgument(args, 0, &llv));
815 const char *msg = iwlog_ecode_explained((iwrc) llv);
816 if (msg) {
817 Dart_SetReturnValue(args, EJTH(Dart_NewStringFromCString(msg)));
818 } else {
819 Dart_SetReturnValue(args, Dart_Null());
820 }
821 Dart_ExitScope();
822 }
823
ejd_resolve_name(Dart_Handle name,int argc,bool * auto_setup_scope)824 static Dart_NativeFunction ejd_resolve_name(
825 Dart_Handle name,
826 int argc,
827 bool *auto_setup_scope) {
828 if (!Dart_IsString(name) || !auto_setup_scope) {
829 return 0;
830 }
831 Dart_EnterScope();
832 const char *cname;
833 EJTH(Dart_StringToCString(name, &cname));
834 for (int i = 0; k_scoped_functions[i].name; ++i) {
835 if (strcmp(cname, k_scoped_functions[i].name) == 0) {
836 *auto_setup_scope = true;
837 Dart_ExitScope();
838 return k_scoped_functions[i].fn;
839 }
840 }
841 Dart_ExitScope();
842 return 0;
843 }
844
ejd_port_handler(Dart_Port receive_port,Dart_CObject * msg)845 static void ejd_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
846 if ( (msg->type != Dart_CObject_kArray)
847 || (msg->value.as_array.length < 2)
848 || (msg->value.as_array.values[0]->type != Dart_CObject_kSendPort)
849 || (msg->value.as_array.values[1]->type != Dart_CObject_kString)) {
850 iwlog_error2("Invalid message recieved");
851 return;
852 }
853 Dart_Port reply_port = msg->value.as_array.values[0]->value.as_send_port.id;
854 const char *fname = msg->value.as_array.values[1]->value.as_string;
855 for (int i = 0; k_wrapped_functions[i].name; ++i) {
856 if (strcmp(k_wrapped_functions[i].name, fname) == 0) {
857 k_wrapped_functions[i].fn(receive_port, msg, reply_port);
858 break;
859 }
860 }
861 }
862
ejdb2_isolate_shared_open(const EJDB_OPTS * opts,EJDB2Handle ** hptr)863 static iwrc ejdb2_isolate_shared_open(const EJDB_OPTS *opts, EJDB2Handle **hptr) {
864 iwrc rc = 0;
865 EJDB db = 0;
866 int rci = pthread_mutex_lock(&k_shared_scope_mtx);
867 if (rci) {
868 return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
869 }
870 const char *path = opts->kv.path;
871 EJDB2Handle *h = k_head_handle;
872 while (h) {
873 if (!strcmp(h->path, path)) {
874 break;
875 }
876 h = h->next;
877 }
878 if (h) {
879 h->refs++;
880 *hptr = h;
881 goto finish;
882 }
883 rc = ejdb_open(opts, &db);
884 RCGO(rc, finish);
885
886 h = calloc(1, sizeof(*h));
887 if (!h) {
888 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
889 goto finish;
890 }
891 h->path = strdup(path);
892 if (!h->path) {
893 free(h);
894 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
895 goto finish;
896 }
897 h->refs = 1;
898 h->db = db;
899 if (k_head_handle) {
900 k_head_handle->prev = h;
901 h->next = k_head_handle;
902 }
903 k_head_handle = h;
904 *hptr = h;
905
906 finish:
907 pthread_mutex_unlock(&k_shared_scope_mtx);
908 if (rc) {
909 iwlog_ecode_error3(rc);
910 if (db) {
911 ejdb_close(&db);
912 }
913 }
914 return rc;
915 }
916
ejdb2_isolate_shared_release(EJDB2Handle ** hp)917 static iwrc ejdb2_isolate_shared_release(EJDB2Handle **hp) {
918 iwrc rc = 0;
919 int rci = pthread_mutex_lock(&k_shared_scope_mtx);
920 if (rci) {
921 return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
922 }
923 EJDB2Handle *h = *hp;
924 *hp = 0;
925 if (--h->refs <= 0) {
926 if (h->db) {
927 rc = ejdb_close(&h->db);
928 }
929 if (h->prev) {
930 h->prev->next = h->next;
931 } else {
932 k_head_handle = h->next;
933 }
934 if (h->next) {
935 h->next->prev = h->prev;
936 }
937 free(h->path);
938 free(h);
939 }
940 pthread_mutex_unlock(&k_shared_scope_mtx);
941 return rc;
942 }
943
ejd_open_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)944 static void ejd_open_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
945 iwrc rc = 0;
946 Dart_CObject result, rv1;
947 Dart_CObject *rv[1] = { &rv1 };
948
949 int c = 2;
950 EJDB_OPTS opts = { 0 };
951 EJDB2Handle *dbh = 0;
952
953 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 16 + c)) {
954 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
955 goto finish;
956 }
957 Dart_CObject **varr = msg->value.as_array.values;
958
959 // opts.kv.path // non null
960 // opts.kv.oflags // non null
961 // opts.kv.wal.enabled // non null
962 // opts.kv.wal.check_crc_on_checkpoint
963 // opts.kv.wal.checkpoint_buffer_sz
964 // opts.kv.wal.checkpoint_timeout_sec
965 // opts.kv.wal.savepoint_timeout_sec
966 // opts.kv.wal.wal_buffer_sz
967 // opts.document_buffer_sz
968 // opts.sort_buffer_sz
969 // opts.http.enabled
970 // opts.http.access_token
971 // opts.http.bind
972 // opts.http.max_body_size
973 // opts.http.port
974 // opts.http.read_anon
975
976 opts.kv.path = cobject_str(varr[c++], false, &rc);
977 RCGO(rc, finish);
978 opts.kv.oflags = cobject_int(varr[c++], false, &rc);
979 RCGO(rc, finish);
980 opts.kv.wal.enabled = cobject_bool(varr[c++], false, &rc);
981 RCGO(rc, finish);
982 opts.kv.wal.check_crc_on_checkpoint = cobject_bool(varr[c++], true, &rc);
983 RCGO(rc, finish);
984 opts.kv.wal.checkpoint_buffer_sz = cobject_int(varr[c++], true, &rc);
985 RCGO(rc, finish);
986 opts.kv.wal.checkpoint_timeout_sec = cobject_int(varr[c++], true, &rc);
987 RCGO(rc, finish);
988 opts.kv.wal.savepoint_timeout_sec = cobject_int(varr[c++], true, &rc);
989 RCGO(rc, finish);
990 opts.kv.wal.wal_buffer_sz = cobject_int(varr[c++], true, &rc);
991 RCGO(rc, finish);
992 opts.document_buffer_sz = cobject_int(varr[c++], true, &rc);
993 RCGO(rc, finish);
994 opts.sort_buffer_sz = cobject_int(varr[c++], true, &rc);
995 RCGO(rc, finish);
996 opts.http.enabled = cobject_bool(varr[c++], true, &rc);
997 RCGO(rc, finish);
998 opts.http.access_token = cobject_str(varr[c++], true, &rc);
999 RCGO(rc, finish);
1000 opts.http.bind = cobject_str(varr[c++], true, &rc);
1001 RCGO(rc, finish);
1002 opts.http.max_body_size = cobject_int(varr[c++], true, &rc);
1003 RCGO(rc, finish);
1004 opts.http.port = cobject_int(varr[c++], true, &rc);
1005 RCGO(rc, finish);
1006 opts.http.read_anon = cobject_bool(varr[c++], true, &rc);
1007 RCGO(rc, finish);
1008
1009 opts.kv.file_lock_fail_fast = true;
1010 opts.no_wal = !opts.kv.wal.enabled;
1011 opts.http.blocking = false;
1012 opts.http.access_token_len = opts.http.access_token ? strlen(opts.http.access_token) : 0;
1013
1014 rc = ejdb2_isolate_shared_open(&opts, &dbh);
1015 RCGO(rc, finish);
1016
1017 rv1.type = Dart_CObject_kInt64;
1018 rv1.value.as_int64 = (intptr_t) dbh;
1019 result.type = Dart_CObject_kArray;
1020 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
1021 result.value.as_array.values = rv;
1022
1023 finish:
1024 EJPORT_RC(&result, rc);
1025 Dart_PostCObject(reply_port, &result);
1026 }
1027
ejd_close_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1028 static void ejd_close_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1029 iwrc rc = 0;
1030 Dart_CObject result = { .type = Dart_CObject_kArray };
1031
1032 if (msg->value.as_array.length != 3) {
1033 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1034 goto finish;
1035 }
1036 intptr_t ptr = cobject_int(msg->value.as_array.values[2], false, &rc);
1037 RCGO(rc, finish);
1038 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1039 if (!dbh) {
1040 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1041 goto finish;
1042 }
1043 rc = ejdb2_isolate_shared_release(&dbh);
1044 RCGO(rc, finish);
1045
1046 if (receive_port != ILLEGAL_PORT) {
1047 Dart_CloseNativePort(receive_port);
1048 }
1049
1050 finish:
1051 EJPORT_RC(&result, rc);
1052 Dart_PostCObject(reply_port, &result);
1053 return;
1054 }
1055
ejd_patch_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1056 static void ejd_patch_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1057 iwrc rc = 0;
1058 Dart_CObject result = { .type = Dart_CObject_kArray };
1059
1060 int c = 2;
1061 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 5 + c)) {
1062 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1063 goto finish;
1064 }
1065
1066 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1067 RCGO(rc, finish);
1068 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1069 if (!dbh || !dbh->db) {
1070 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1071 goto finish;
1072 }
1073 EJDB db = dbh->db;
1074 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1075 RCGO(rc, finish);
1076
1077 const char *patch = cobject_str(msg->value.as_array.values[c++], false, &rc);
1078 RCGO(rc, finish);
1079
1080 int64_t id = cobject_int(msg->value.as_array.values[c++], true, &rc);
1081 RCGO(rc, finish);
1082
1083 bool upsert = cobject_bool(msg->value.as_array.values[c++], true, &rc);
1084 RCGO(rc, finish);
1085
1086 if (upsert) {
1087 rc = ejdb_merge_or_put(db, coll, patch, id);
1088 } else {
1089 rc = ejdb_patch(db, coll, patch, id);
1090 }
1091
1092 finish:
1093 EJPORT_RC(&result, rc);
1094 Dart_PostCObject(reply_port, &result);
1095 }
1096
ejd_put_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1097 static void ejd_put_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1098 iwrc rc = 0;
1099 Dart_CObject result, rv1;
1100 Dart_CObject *rv[1] = { &rv1 };
1101
1102 JBL jbl = 0;
1103 int c = 2;
1104 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 4 + c)) {
1105 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1106 goto finish;
1107 }
1108
1109 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1110 RCGO(rc, finish);
1111 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1112 if (!dbh || !dbh->db) {
1113 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1114 goto finish;
1115 }
1116 EJDB db = dbh->db;
1117 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1118 RCGO(rc, finish);
1119
1120 const char *json = cobject_str(msg->value.as_array.values[c++], false, &rc);
1121 RCGO(rc, finish);
1122
1123 int64_t id = cobject_int(msg->value.as_array.values[c++], true, &rc);
1124 RCGO(rc, finish);
1125
1126 rc = jbl_from_json(&jbl, json);
1127 RCGO(rc, finish);
1128
1129 if (id > 0) {
1130 rc = ejdb_put(db, coll, jbl, id);
1131 } else {
1132 rc = ejdb_put_new(db, coll, jbl, &id);
1133 }
1134 RCGO(rc, finish);
1135
1136 rv1.type = Dart_CObject_kInt64;
1137 rv1.value.as_int64 = id;
1138 result.type = Dart_CObject_kArray;
1139 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
1140 result.value.as_array.values = rv;
1141
1142 finish:
1143 if (jbl) {
1144 jbl_destroy(&jbl);
1145 }
1146 EJPORT_RC(&result, rc);
1147 Dart_PostCObject(reply_port, &result);
1148 }
1149
ejd_del_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1150 static void ejd_del_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1151 iwrc rc = 0;
1152 Dart_CObject result = { .type = Dart_CObject_kArray };
1153
1154 int c = 2;
1155 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 3 + c)) {
1156 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1157 goto finish;
1158 }
1159
1160 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1161 RCGO(rc, finish);
1162 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1163 if (!dbh || !dbh->db) {
1164 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1165 goto finish;
1166 }
1167 EJDB db = dbh->db;
1168 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1169 RCGO(rc, finish);
1170
1171 int64_t id = cobject_int(msg->value.as_array.values[c++], false, &rc);
1172 RCGO(rc, finish);
1173
1174 rc = ejdb_del(db, coll, id);
1175
1176 finish:
1177 EJPORT_RC(&result, rc);
1178 Dart_PostCObject(reply_port, &result);
1179 }
1180
ejd_rename_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1181 static void ejd_rename_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1182 iwrc rc = 0;
1183 Dart_CObject result = { .type = Dart_CObject_kArray };
1184
1185 int c = 2;
1186 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 3 + c)) {
1187 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1188 goto finish;
1189 }
1190 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1191 RCGO(rc, finish);
1192 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1193 if (!dbh || !dbh->db) {
1194 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1195 goto finish;
1196 }
1197 EJDB db = dbh->db;
1198
1199 const char *oname = cobject_str(msg->value.as_array.values[c++], false, &rc);
1200 RCGO(rc, finish);
1201
1202 const char *nname = cobject_str(msg->value.as_array.values[c++], false, &rc);
1203 RCGO(rc, finish);
1204
1205 rc = ejdb_rename_collection(db, oname, nname);
1206
1207 finish:
1208 EJPORT_RC(&result, rc);
1209 Dart_PostCObject(reply_port, &result);
1210 }
1211
ejd_idx_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1212 static void ejd_idx_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1213 iwrc rc = 0;
1214 Dart_CObject result = { .type = Dart_CObject_kArray };
1215
1216 int c = 2;
1217 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 4 + c)) {
1218 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1219 goto finish;
1220 }
1221
1222 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1223 RCGO(rc, finish);
1224 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1225 if (!dbh || !dbh->db) {
1226 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1227 goto finish;
1228 }
1229 EJDB db = dbh->db;
1230 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1231 RCGO(rc, finish);
1232
1233 const char *path = cobject_str(msg->value.as_array.values[c++], false, &rc);
1234 RCGO(rc, finish);
1235
1236 int64_t mode = cobject_int(msg->value.as_array.values[c++], false, &rc);
1237 RCGO(rc, finish);
1238
1239 rc = ejdb_ensure_index(db, coll, path, mode);
1240
1241 finish:
1242 EJPORT_RC(&result, rc);
1243 Dart_PostCObject(reply_port, &result);
1244 }
1245
ejd_rmi_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1246 static void ejd_rmi_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1247 iwrc rc = 0;
1248 Dart_CObject result = { .type = Dart_CObject_kArray };
1249
1250 int c = 2;
1251 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 4 + c)) {
1252 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1253 goto finish;
1254 }
1255
1256 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1257 RCGO(rc, finish);
1258 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1259 if (!dbh || !dbh->db) {
1260 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1261 goto finish;
1262 }
1263 EJDB db = dbh->db;
1264 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1265 RCGO(rc, finish);
1266
1267 const char *path = cobject_str(msg->value.as_array.values[c++], false, &rc);
1268 RCGO(rc, finish);
1269
1270 int64_t mode = cobject_int(msg->value.as_array.values[c++], false, &rc);
1271 RCGO(rc, finish);
1272
1273 rc = ejdb_remove_index(db, coll, path, mode);
1274
1275 finish:
1276 EJPORT_RC(&result, rc);
1277 Dart_PostCObject(reply_port, &result);
1278 }
1279
ejd_rmc_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1280 static void ejd_rmc_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1281 iwrc rc = 0;
1282 Dart_CObject result = { .type = Dart_CObject_kArray };
1283
1284 int c = 2;
1285 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 2 + c)) {
1286 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1287 goto finish;
1288 }
1289
1290 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1291 RCGO(rc, finish);
1292 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1293 if (!dbh || !dbh->db) {
1294 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1295 goto finish;
1296 }
1297 EJDB db = dbh->db;
1298 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1299 RCGO(rc, finish);
1300
1301 rc = ejdb_remove_collection(db, coll);
1302
1303 finish:
1304 EJPORT_RC(&result, rc);
1305 Dart_PostCObject(reply_port, &result);
1306 }
1307
ejd_bkp_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1308 static void ejd_bkp_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1309 iwrc rc = 0;
1310 Dart_CObject result, rv1;
1311 Dart_CObject *rv[1] = { &rv1 };
1312 uint64_t ts = 0;
1313
1314 int c = 2;
1315 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 2 + c)) {
1316 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1317 goto finish;
1318 }
1319
1320 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1321 RCGO(rc, finish);
1322 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1323 if (!dbh || !dbh->db) {
1324 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1325 goto finish;
1326 }
1327
1328 EJDB db = dbh->db;
1329 const char *fileName = cobject_str(msg->value.as_array.values[c++], false, &rc);
1330 RCGO(rc, finish);
1331
1332 rc = ejdb_online_backup(db, &ts, fileName);
1333
1334 rv1.type = Dart_CObject_kInt64;
1335 rv1.value.as_int64 = ts;
1336 result.type = Dart_CObject_kArray;
1337 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
1338 result.value.as_array.values = rv;
1339
1340 finish:
1341 EJPORT_RC(&result, rc);
1342 Dart_PostCObject(reply_port, &result);
1343 }
1344
ejd_info_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1345 static void ejd_info_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1346 iwrc rc = 0;
1347 Dart_CObject result, rv1;
1348 Dart_CObject *rv[1] = { &rv1 };
1349
1350 JBL jbl = 0;
1351 IWXSTR *xstr = 0;
1352 int c = 2;
1353
1354 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 1 + c)) {
1355 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1356 goto finish;
1357 }
1358
1359 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1360 RCGO(rc, finish);
1361 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1362 if (!dbh || !dbh->db) {
1363 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1364 goto finish;
1365 }
1366 EJDB db = dbh->db;
1367 rc = ejdb_get_meta(db, &jbl);
1368 RCGO(rc, finish);
1369
1370 xstr = iwxstr_new2(jbl_size(jbl) * 2);
1371 if (!xstr) {
1372 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1373 goto finish;
1374 }
1375 rc = jbl_as_json(jbl, jbl_xstr_json_printer, xstr, 0);
1376 RCGO(rc, finish);
1377
1378 rv1.type = Dart_CObject_kString;
1379 rv1.value.as_string = iwxstr_ptr(xstr);
1380 result.type = Dart_CObject_kArray;
1381 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
1382 result.value.as_array.values = rv;
1383
1384 finish:
1385 if (jbl) {
1386 jbl_destroy(&jbl);
1387 }
1388 EJPORT_RC(&result, rc);
1389 Dart_PostCObject(reply_port, &result);
1390 if (xstr) {
1391 iwxstr_destroy(xstr);
1392 }
1393 }
1394
ejd_get_wrapped(Dart_Port receive_port,Dart_CObject * msg,Dart_Port reply_port)1395 static void ejd_get_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port) {
1396 iwrc rc = 0;
1397 Dart_CObject result, rv1;
1398 Dart_CObject *rv[1] = { &rv1 };
1399
1400 JBL jbl = 0;
1401 IWXSTR *xstr = 0;
1402 int c = 2;
1403
1404 if ((msg->type != Dart_CObject_kArray) || (msg->value.as_array.length != 3 + c)) {
1405 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1406 goto finish;
1407 }
1408
1409 intptr_t ptr = cobject_int(msg->value.as_array.values[c++], false, &rc);
1410 RCGO(rc, finish);
1411 EJDB2Handle *dbh = (EJDB2Handle*) ptr;
1412 if (!dbh || !dbh->db) {
1413 rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
1414 goto finish;
1415 }
1416 EJDB db = dbh->db;
1417 const char *coll = cobject_str(msg->value.as_array.values[c++], false, &rc);
1418 RCGO(rc, finish);
1419
1420 int64_t id = cobject_int(msg->value.as_array.values[c++], true, &rc);
1421 RCGO(rc, finish);
1422
1423 rc = ejdb_get(db, coll, id, &jbl);
1424 RCGO(rc, finish);
1425
1426 xstr = iwxstr_new2(jbl_size(jbl) * 2);
1427 if (!xstr) {
1428 rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
1429 goto finish;
1430 }
1431 rc = jbl_as_json(jbl, jbl_xstr_json_printer, xstr, 0);
1432 RCGO(rc, finish);
1433
1434 rv1.type = Dart_CObject_kString;
1435 rv1.value.as_string = iwxstr_ptr(xstr);
1436 result.type = Dart_CObject_kArray;
1437 result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
1438 result.value.as_array.values = rv;
1439
1440 finish:
1441 if (jbl) {
1442 jbl_destroy(&jbl);
1443 }
1444 EJPORT_RC(&result, rc);
1445 Dart_PostCObject(reply_port, &result);
1446 if (xstr) {
1447 iwxstr_destroy(xstr);
1448 }
1449 }
1450
1451 ///////////////////////////////////////////////////////////////////////////
1452 //
1453 ///////////////////////////////////////////////////////////////////////////
1454
ejd_ecodefn(locale_t locale,uint32_t ecode)1455 static const char *ejd_ecodefn(locale_t locale, uint32_t ecode) {
1456 if (!((ecode > _EJD_ERROR_START) && (ecode < _EJD_ERROR_END))) {
1457 return 0;
1458 }
1459 switch (ecode) {
1460 case EJD_ERROR_CREATE_PORT:
1461 return "Failed to create a Dart port (EJD_ERROR_CREATE_PORT)";
1462 case EJD_ERROR_INVALID_NATIVE_CALL_ARGS:
1463 return "Invalid native function call args (EJD_ERROR_INVALID_NATIVE_CALL_ARGS)";
1464 case EJD_ERROR_INVALID_STATE:
1465 return "Invalid native extension state (EJD_ERROR_INVALID_STATE)";
1466 case EJD_ERROR_POST_PORT:
1467 return "Failed to post message to Dart port (EJD_ERROR_POST_PORT)";
1468 }
1469 return 0;
1470 }
1471
ejdb2dart_Init(Dart_Handle parent_library)1472 DART_EXPORT Dart_Handle ejdb2dart_Init(Dart_Handle parent_library) {
1473 static volatile int ejd_ecodefn_initialized = 0;
1474 if (__sync_bool_compare_and_swap(&ejd_ecodefn_initialized, 0, 1)) {
1475 iwrc rc = ejdb_init();
1476 if (rc) {
1477 return ejd_error_rc_create(rc);
1478 }
1479 iwlog_register_ecodefn(ejd_ecodefn);
1480 }
1481 if (Dart_IsError(parent_library)) {
1482 return parent_library;
1483 }
1484 Dart_Handle dh = Dart_SetNativeResolver(parent_library, ejd_resolve_name, 0);
1485 if (Dart_IsError(dh)) {
1486 return dh;
1487 }
1488 return Dart_Null();
1489 }
1490
ejd_ctx_finalizer(void * isolate_callback_data,void * peer)1491 static void ejd_ctx_finalizer(void *isolate_callback_data, void *peer) {
1492 EJDB2Context *ctx = peer;
1493 if (ctx) {
1494 if (ctx->dbh) {
1495 iwrc rc = ejdb2_isolate_shared_release(&ctx->dbh);
1496 if (rc) {
1497 iwlog_ecode_error3(rc);
1498 }
1499 }
1500 if (ctx->wph && Dart_CurrentIsolateGroup()) {
1501 Dart_DeleteWeakPersistentHandle(ctx->wph);
1502 }
1503 }
1504
1505 free(ctx);
1506 }
1507