• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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