<commit_before>#define DART_SHARED_LIB
#include "dart_api.h"
#include "dart_native_api.h"
#include <ejdb2.h>
#include <ejdb2/iowow/iwconv.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

typedef void(*WrapperFunction)(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);

typedef enum {
  _EJD_ERROR_START = (IW_ERROR_START + 15000UL + 4000),
  EJD_ERROR_CREATE_PORT,                 /**< Failed to create a Dart port (EJD_ERROR_CREATE_PORT) */
  EJD_ERROR_POST_PORT,                   /**< Failed to post message to Dart port (EJD_ERROR_POST_PORT) */
  EJD_ERROR_INVALID_NATIVE_CALL_ARGS,    /**< Invalid native function call args (EJD_ERROR_INVALID_NATIVE_CALL_ARGS) */
  EJD_ERROR_INVALID_STATE,               /**< Invalid native extension state (EJD_ERROR_INVALID_STATE) */
  _EJD_ERROR_END,
} jbr_ecode_t;

struct NativeFunctionLookup {
  const char *name;
  Dart_NativeFunction fn;
};

struct WrapperFunctionLookup {
  const char *name;
  WrapperFunction fn;
};

typedef struct EJDB2Handle {
  EJDB db;
  char *path;
  int64_t refs;
  struct EJDB2Handle *next;
  struct EJDB2Handle *prev;
} EJDB2Handle;

typedef struct EJDB2Context {
  Dart_Port port;
  EJDB2Handle *dbh;
} EJDB2Context;

static pthread_mutex_t k_shared_scope_mtx = PTHREAD_MUTEX_INITIALIZER;
static EJDB2Handle *k_head_handle;

static Dart_NativeFunction ejd_resolve_name(Dart_Handle name, int argc, bool *auto_setup_scope);

IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle);
IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc);
IW_INLINE Dart_Handle ejd_error_rc_throw(iwrc rc);

static void explain_rc(Dart_NativeArguments args);
static void jql_exec(Dart_NativeArguments args);
static void jql_set(Dart_NativeArguments args);

static void ejdb2_port_handler(Dart_Port receive_port, Dart_CObject *msg);
static void ejdb2_ctx_finalizer(void *isolate_callback_data, Dart_WeakPersistentHandle handle, void *peer);

static void ejdb2_port(Dart_NativeArguments arguments);
static void ejdb2_set_handle(Dart_NativeArguments args);
static void ejdb2_get_handle(Dart_NativeArguments args);
static void ejdb2_create_query(Dart_NativeArguments args);

static void ejdb2_open_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_close_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_get_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_put_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_del_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_patch_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_idx_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_rmi_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_rmc_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_info_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);

static struct NativeFunctionLookup k_scoped_functions[] = {
  {"port", ejdb2_port},
  {"exec", jql_exec},
  {"jql_set", jql_set},
  {"create_query", ejdb2_create_query},
  {"set_handle", ejdb2_set_handle},
  {"get_handle", ejdb2_get_handle},
  {"explain_rc", explain_rc},
  {0, 0}
};

static struct WrapperFunctionLookup k_wrapped_functions[] = {
  {"get", ejdb2_get_wrapped},
  {"put", ejdb2_put_wrapped},
  {"del", ejdb2_del_wrapped},
  {"patch", ejdb2_patch_wrapped},
  {"idx", ejdb2_idx_wrapped},
  {"rmi", ejdb2_rmi_wrapped},
  {"rmc", ejdb2_rmc_wrapped},
  {"info", ejdb2_info_wrapped},
  {"open", ejdb2_open_wrapped},
  {"close", ejdb2_close_wrapped},
  {0, 0}
};

#define EJTH(h_) ejd_error_check_propagate(h_)

#define EJGO(h_, rh_, label_) \
  if (Dart_IsError(h_)) {   \
    rh_ = (h_);             \
    goto label_;            \
  }

#define EJLIB() EJTH(Dart_LookupLibrary(Dart_NewStringFromCString("package:ejdb2_dart/ejdb2_dart.dart")))

#define EJPORT_RC(co_, rc_)               \
  if (rc_) {                              \
    (co_)->type = Dart_CObject_kInt64;    \
    (co_)->value.as_int64 = (rc_);        \
  }

IW_INLINE char *cobject_str(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kString) {
      return co->value.as_string;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE int64_t cobject_int(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kInt32) {
      return co->value.as_int32;
    } else if (co->type == Dart_CObject_kInt64) {
      return co->value.as_int64;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE bool cobject_bool(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kBool) {
      return co->value.as_bool;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return false;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return false;
}

IW_INLINE double cobject_double(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kDouble) {
      return co->value.as_double;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle) {
  if (Dart_IsError(handle)) {
    Dart_PropagateError(handle);
  }
  return handle;
}

static Dart_Handle ejd_error_object_handle(iwrc rc, const char *msg) {
  Dart_Handle hmsg = Dart_Null();
  if (msg) {
    hmsg = EJTH(Dart_NewStringFromCString(msg));
  } else if (rc) {
    const char *explained = iwlog_ecode_explained(rc);
    if (explained) {
      hmsg = EJTH(Dart_NewStringFromCString(explained));
    }
  }
  Dart_Handle hrc = EJTH(Dart_NewIntegerFromUint64(rc));
  Dart_Handle hclass = EJTH(Dart_GetClass(EJLIB(), Dart_NewStringFromCString("EJDB2Error")));
  Dart_Handle args[] = {hrc, hmsg};
  return Dart_New(hclass, Dart_Null(), 2, args);
}

IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc) {
  const char *msg = iwlog_ecode_explained(rc);
  return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
}

IW_INLINE Dart_Handle ejd_error_rc_create2(iwrc rc, const char *msg) {
  return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
}

IW_INLINE Dart_Handle ejd_error_rc_throw(iwrc rc) {
  return Dart_PropagateError(ejd_error_rc_create(rc));
}

static void ejdb2_port(Dart_NativeArguments args) {
  EJDB2Context *ctx;
  intptr_t ptr = 0;

  Dart_EnterScope();
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));

  if (!ptr) {
    ctx = malloc(sizeof(*ctx));
    if (!ctx) {
      Dart_SetReturnValue(args, ejd_error_rc_create(IW_ERROR_ALLOC));
      goto finish;
    }
    ctx->dbh = 0;
    ctx->port = Dart_NewNativePort("ejdb2_port_handler", ejdb2_port_handler, true);
    if (ctx->port == ILLEGAL_PORT) {
      Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_CREATE_PORT));
      goto finish;
    }
    Dart_SetNativeInstanceField(self, 0, (intptr_t) ctx);
    Dart_NewWeakPersistentHandle(self, ctx, sizeof(*ctx), ejdb2_ctx_finalizer);
  } else {
    ctx = (void *) ptr;
  }
  Dart_SetReturnValue(args, EJTH(Dart_NewSendPort(ctx->port)));

finish:
  Dart_ExitScope();
}

static void ejdb2_get_handle(Dart_NativeArguments args) {
  intptr_t ptr = 0;
  Dart_EnterScope();
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
  EJDB2Context *ctx = (void *) ptr;
  if (ctx && ctx->dbh) {
    Dart_SetReturnValue(args, Dart_NewInteger((intptr_t) ctx->dbh));
  } else {
    Dart_SetReturnValue(args, Dart_Null());
  }
  Dart_ExitScope();
}

static void ejdb2_set_handle(Dart_NativeArguments args) {
  intptr_t ptr;
  Dart_EnterScope();
  Dart_SetReturnValue(args, Dart_Null());
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
  EJDB2Context *ctx = (void *) ptr;
  if (!ctx) {
    Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_INVALID_STATE));
    goto finish;
  }
  Dart_Handle dh = Dart_GetNativeArgument(args, 1);
  if (Dart_IsInteger(dh)) {
    EJTH(Dart_GetNativeIntegerArgument(args, 1, &ptr));
    ctx->dbh = (void *) ptr;
  } else if (Dart_IsNull(dh)) {
    ctx->dbh = 0;
  }

finish:
  Dart_ExitScope();
}

static void ejdb2_jql_finalizer(void *isolate_callback_data, Dart_WeakPersistentHandle handle, void *peer) {
  JQL q = (void *) peer;
  if (q) {
    jql_destroy(&q);
  }
}

static void ejdb2_create_query(Dart_NativeArguments args) {
  Dart_EnterScope();

  iwrc rc = 0;
  JQL q = 0;
  intptr_t ptr = 0;
  const char *query = 0;
  const char *collection = 0;

  Dart_Handle ret = Dart_Null();
  Dart_Handle hlib = EJLIB();
  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  Dart_Handle hquery = EJTH(Dart_GetNativeArgument(args, 1));
  Dart_Handle hcoll = EJTH(Dart_GetNativeArgument(args, 2));

  EJTH(Dart_StringToCString(hquery, &query));
  if (Dart_IsString(hcoll)) {
    EJTH(Dart_StringToCString(hcoll, &collection));
  }
  EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));

  EJDB2Context *ctx = (void *) ptr;
  if (!ctx || !ctx->dbh || !ctx->dbh->db) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  rc = jql_create2(&q, collection, query, JQL_KEEP_QUERY_ON_PARSE_ERROR | JQL_SILENT_ON_PARSE_ERROR);
  RCGO(rc, finish);

  if (!collection) {
    collection = jql_collection(q);
  }
  hcoll = Dart_NewStringFromCString(collection);
  EJGO(hcoll, ret, finish);

  Dart_Handle hclass = Dart_GetClass(hlib, Dart_NewStringFromCString("JQL"));
  EJGO(hclass, ret, finish);

  Dart_Handle jqargs[] = {hself, hquery, hcoll};
  Dart_Handle jqinst = Dart_New(hclass, Dart_NewStringFromCString("_"), 3, jqargs);
  EJGO(jqinst, ret, finish);

  ret = Dart_SetNativeInstanceField(jqinst, 0, (intptr_t) q);
  EJGO(ret, ret, finish);
  Dart_NewWeakPersistentHandle(jqinst, q, jql_estimate_allocated_size(q), ejdb2_jql_finalizer);

  ret = jqinst;

finish:
  if (rc || Dart_IsError(ret)) {
    if (rc) {
      if (q && rc == JQL_ERROR_QUERY_PARSE) {
        ret = ejd_error_rc_create2(rc, jql_error(q));
      } else {
        ret = ejd_error_rc_create(rc);
      }
    }
    if (q) {
      jql_destroy(&q);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

struct UXCTX {
  bool aggregate_count;
  Dart_Port reply_port;
};

static iwrc jql_exec_visitor(struct _EJDB_EXEC *ux, EJDB_DOC doc, int64_t *step) {
  iwrc rc = 0;
  struct UXCTX *uctx = ux->opaque;
  IWXSTR *xstr = iwxstr_new();
  if (!xstr) {
    return IW_ERROR_ALLOC;
  }
  if (doc->node) {
    rc = jbl_node_as_json(doc->node, jbl_xstr_json_printer, xstr, 0);
  } else {
    rc = jbl_as_json(doc->raw, jbl_xstr_json_printer, xstr, 0);
  }
  RCGO(rc, finish);

  Dart_CObject result, rv1, rv2;
  Dart_CObject *rv[2] = {&rv1, &rv2};
  result.type = Dart_CObject_kArray;
  result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
  result.value.as_array.values = rv;
  rv1.type = Dart_CObject_kInt64;
  rv1.value.as_int64 = doc->id;
  rv2.type = Dart_CObject_kString;
  rv2.value.as_string = iwxstr_ptr(xstr);

  if (!Dart_PostCObject(uctx->reply_port, &result)) {
    *step = 0; // End of cursor loop
  }

finish:
  iwxstr_destroy(xstr);
  return rc;
}

static void jql_exec_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
  iwrc rc = 0;
  Dart_Port reply_port = ILLEGAL_PORT;
  Dart_CObject result = {.type = Dart_CObject_kNull};
  if (msg->type != Dart_CObject_kArray || msg->value.as_array.length != 3)  {
    iwlog_error2("Invalid message recieved");
    return;
  }
  //  [reply_port, qptr, dbptr]
  EJDB_EXEC ux = {0};
  Dart_CObject **varr = msg->value.as_array.values;
  reply_port = cobject_int(varr[0], false, &rc);
  RCGO(rc, finish);
  ux.q = (void *) cobject_int(varr[1], false, &rc);
  if (!ux.q) {
    rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
    goto finish;
  }
  EJDB2Context *dctx = (void *) cobject_int(varr[2], false, &rc);
  if (!dctx || !dctx->dbh || !dctx->dbh->db) {
    rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
    goto finish;
  }
  struct UXCTX uctx = {
    .aggregate_count = jql_has_aggregate_count(ux.q),
    .reply_port = reply_port
  };
  ux.db = dctx->dbh->db;
  ux.visitor = uctx.aggregate_count ? 0 : jql_exec_visitor;
  ux.opaque = &uctx;

  rc = ejdb_exec(&ux);
  RCGO(rc, finish);

  if (uctx.aggregate_count) {
    Dart_CObject result, rv1, rv2;
    Dart_CObject *rv[2] = {&rv1, &rv2};
    result.type = Dart_CObject_kArray;
    result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
    result.value.as_array.values = rv;
    rv1.type = Dart_CObject_kInt64;
    rv1.value.as_int64 = ux.cnt;
    rv2.type = Dart_CObject_kNull;
    Dart_PostCObject(reply_port, &result);
  }

finish:
  if (rc) {
    iwlog_ecode_error3(rc);
    EJPORT_RC(&result, rc);
  }
  if (reply_port != ILLEGAL_PORT) {
    Dart_PostCObject(reply_port, &result); // Last NULL or error(int)
  }
  Dart_CloseNativePort(receive_port);
}

static void jql_exec(Dart_NativeArguments args) {
  // void _exec(SendPort sendPort) native 'exec';
  Dart_EnterScope();

  iwrc rc = 0;
  intptr_t qptr = 0, ptr = 0;
  Dart_Port reply_port = ILLEGAL_PORT;
  Dart_Port exec_port = ILLEGAL_PORT;
  Dart_Handle ret = Dart_Null();

  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  Dart_Handle hdb = EJTH(Dart_GetField(hself, Dart_NewStringFromCString("db")));
  Dart_Handle hport = EJTH(Dart_GetNativeArgument(args, 1));
  EJTH(Dart_SendPortGetId(hport, &reply_port));

  EJTH(Dart_GetNativeInstanceField(hdb, 0, &ptr));
  EJDB2Context *dctx = (void *) ptr;
  if (!dctx || !dctx->dbh || !dctx->dbh->db) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  // JQL pointer
  EJTH(Dart_GetNativeInstanceField(hself, 0, &qptr));
  if (!qptr) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  // JQL exec port
  exec_port = Dart_NewNativePort("jql_exec_port_handler", jql_exec_port_handler, false);
  if (exec_port == ILLEGAL_PORT) {
    rc = EJD_ERROR_CREATE_PORT;
    goto finish;
  }
  // Now post a message to the query executor port: [reply_port, qptr, dctx]
  Dart_CObject msg, marg1, marg2, marg3;
  Dart_CObject *margs[] = {&marg1, &marg2, &marg3};

  msg.type = Dart_CObject_kArray;
  msg.value.as_array.length = 3;
  msg.value.as_array.values = margs;

  marg1.type = Dart_CObject_kInt64;
  marg1.value.as_int64 = reply_port;

  marg2.type = Dart_CObject_kInt64;
  marg2.value.as_int64 = qptr;

  marg3.type = Dart_CObject_kInt64;
  marg3.value.as_int64 = (int64_t) dctx;

  if (!Dart_PostCObject(exec_port, &msg)) {
    rc = EJD_ERROR_POST_PORT;
    goto finish;
  }

finish:
  if (rc || Dart_IsError(ret)) {
    if (exec_port != ILLEGAL_PORT) {
      Dart_CloseNativePort(exec_port);
    }
    if (rc) {
      ret = ejd_error_rc_create(rc);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

static void jql_free_str(void *ptr, void *op) {
  if (ptr) free(ptr);
}

static void jql_free_json_node(void *ptr, void *op) {
  IWPOOL *pool = op;
  if (pool) iwpool_destroy(pool);
}

static void jql_set(Dart_NativeArguments args) {
  // void set(dynamic place, dynamic value) native 'jql_set';
  Dart_EnterScope();
  Dart_Handle ret = Dart_Null();

  iwrc rc = 0;
  intptr_t ptr = 0;
  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));
  JQL q = (void *) ptr;
  if (!q) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  int64_t npl = 0, type = 0;
  const char *svalue, *spl = 0;
  Dart_Handle hpl = EJTH(Dart_GetNativeArgument(args, 1));
  Dart_Handle hvalue = EJTH(Dart_GetNativeArgument(args, 2));
  Dart_Handle htype = EJTH(Dart_GetNativeArgument(args, 3));

  if (Dart_IsString(hpl)) {
    EJTH(Dart_StringToCString(hpl, &spl));
  } else {
    EJTH(Dart_IntegerToInt64(hpl, &npl));
  }
  if (Dart_IsInteger(htype)) {
    EJTH(Dart_IntegerToInt64(htype, &type));
  }
  if (type == 1) { // JSON
    EJTH(Dart_StringToCString(hvalue, &svalue));
    JBL_NODE node;
    IWPOOL *pool = iwpool_create(1024);
    if (!pool) {
      rc = IW_ERROR_ALLOC;
      goto finish;
    }
    rc = jbl_node_from_json(svalue, &node, pool);
    if (rc) {
      iwpool_destroy(pool);
      goto finish;
    }
    rc = jql_set_json2(q, spl, npl, node, jql_free_json_node, pool);
    if (rc) {
      iwpool_destroy(pool);
      goto finish;
    }
  } else if (type == 2) { // Regexp
    EJTH(Dart_StringToCString(hvalue, &svalue));
    char *str = strdup(svalue);
    if (!str) {
      rc = IW_ERROR_ALLOC;
      goto finish;
    }
    rc = jql_set_regexp2(q, spl, npl, str, jql_free_str, 0);
    if (rc) {
      free(str);
      goto finish;
    }
  } else { // All other cases
    if (Dart_IsString(hvalue)) {
      EJTH(Dart_StringToCString(hvalue, &svalue));
      char *str = strdup(svalue);
      if (!str) {
        rc = IW_ERROR_ALLOC;
        goto finish;
      }
      rc = jql_set_str2(q, spl, npl, str, jql_free_str, 0);
      if (rc) {
        free(str);
        goto finish;
      }
    } else if (Dart_IsInteger(hvalue)) {
      int64_t val;
      EJTH(Dart_IntegerToInt64(hvalue, &val));
      rc = jql_set_i64(q, spl, npl, val);
    } else if (Dart_IsDouble(hvalue)) {
      double val;
      EJTH(Dart_DoubleValue(hvalue, &val));
      rc = jql_set_f64(q, spl, npl, val);
    } else if (Dart_IsBoolean(hvalue)) {
      bool val;
      EJTH(Dart_BooleanValue(hvalue, &val));
      rc = jql_set_bool(q, spl, npl, val);
    } else if (Dart_IsNull(hvalue)) {
      rc = jql_set_null(q, spl, npl);
    }
  }

finish:
  if (rc || Dart_IsError(ret)) {
    if (rc) {
      ret = ejd_error_rc_create(rc);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

static void explain_rc(Dart_NativeArguments args) {
  Dart_EnterScope();
  int64_t llv = 0;
  EJTH(Dart_GetNativeIntegerArgument(args, 0, &llv));
  const char *msg = iwlog_ecode_explained((iwrc) llv);
  if (msg) {
    Dart_SetReturnValue(args, EJTH(Dart_NewStringFromCString(msg)));
  } else {
    Dart_SetReturnValue(args, Dart_Null());
  }
  Dart_ExitScope();
}

static Dart_NativeFunction ejd_resolve_name(Dart_Handle name,
                                            int argc,
                                            bool *auto_setup_scope) {
  if (!Dart_IsString(name) || !auto_setup_scope) {
    return 0;
  }
  Dart_EnterScope();
  const char *cname;
  EJTH(Dart_StringToCString(name, &cname));
  for (int i = 0; k_scoped_functions[i].name; ++i) {
    if (strcmp(cname, k_scoped_functions[i].name) == 0) {
      *auto_setup_scope = true;
      Dart_ExitScope();
      return k_scoped_functions[i].fn;
    }
  }
  Dart_ExitScope();
  return 0;
}

static void ejdb2_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
  if (msg->type != Dart_CObject_kArray
      || msg->value.as_array.length < 2
      || msg->value.as_array.values[0]->type != Dart_CObject_kSendPort
      || msg->value.as_array.values[1]->type != Dart_CObject_kString) {
    iwlog_error2("Invalid message recieved");
    return;
  }
  Dart_Port reply_port = msg->value.as_array.values[0]->value.as_send_port.id;
  const char *fname = msg->value.as_array.values[1]->value.as_string;
  for (int i = 0; k_wrapped_functions[i].name; ++i) {
    if (strcmp(k_wrapped_functions[i].name, fname) == 0) {
      k_wrapped_functions[i].fn(receive_port, msg, reply_port);
      break;
    }
  }
}

static iwrc ejdb2_isolate_shared_open(const EJDB_OPTS *opts, EJDB2Handle **hptr) {
  iwrc rc = 0;
  EJDB db = 0;
  int rci = pthread_mutex_lock(&k_shared_scope_mtx);
  if (rci) return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
  const char *path = opts->kv.path;
  EJDB2Handle *h = k_head_handle;
  while (h) {
    if (!strcmp(h->path, path)) {
      break;
    }
    h = h->next;
  }
  if (h) {
    h->refs++;
    *hptr = h;
    goto finish;
  }
  rc = ejdb_open(opts, &db);
  RCGO(rc, finish);

  h = calloc(1, sizeof(*h));
  if (!h) {
    rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
    goto finish;
  }
  h->path = strdup(path);
  if (!h->path) {
    free(h);
    rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
    goto finish;
  }
  h->refs = 1;
  h->db = db;
  if (k_head_handle) {
    k_head_handle->prev = h;
    h->next = k_head_handle;
  }
  k_head_handle = h;
  *hptr = h;

finish:
  pthread_mutex_unlock(&k_shared_scope_mtx);
  if (rc) {
    iwlog_ecode_error3(rc);
    if (db) {
      ejdb_close(&db);
    }
  }
  return rc;
}

static iwrc ejdb2_isolate_shared_release(EJDB2Handle **hp) {
  iwrc rc = 0;
  int rci = pthread_mutex_lock(&k_shared_scope_mtx);
  if (rci) return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
  EJDB2Handle *h = *hp;
  *hp = 0;
  if (--h->refs <= 0) {
    rc = ejdb_close(&h->db);
    if (h->prev) {<commit_msg>* Be more safe in ejdb2_isolate_shared_release<commit_after>#define DART_SHARED_LIB
#include "dart_api.h"
#include "dart_native_api.h"
#include <ejdb2.h>
#include <ejdb2/iowow/iwconv.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

typedef void(*WrapperFunction)(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);

typedef enum {
  _EJD_ERROR_START = (IW_ERROR_START + 15000UL + 4000),
  EJD_ERROR_CREATE_PORT,                 /**< Failed to create a Dart port (EJD_ERROR_CREATE_PORT) */
  EJD_ERROR_POST_PORT,                   /**< Failed to post message to Dart port (EJD_ERROR_POST_PORT) */
  EJD_ERROR_INVALID_NATIVE_CALL_ARGS,    /**< Invalid native function call args (EJD_ERROR_INVALID_NATIVE_CALL_ARGS) */
  EJD_ERROR_INVALID_STATE,               /**< Invalid native extension state (EJD_ERROR_INVALID_STATE) */
  _EJD_ERROR_END,
} jbr_ecode_t;

struct NativeFunctionLookup {
  const char *name;
  Dart_NativeFunction fn;
};

struct WrapperFunctionLookup {
  const char *name;
  WrapperFunction fn;
};

typedef struct EJDB2Handle {
  EJDB db;
  char *path;
  int64_t refs;
  struct EJDB2Handle *next;
  struct EJDB2Handle *prev;
} EJDB2Handle;

typedef struct EJDB2Context {
  Dart_Port port;
  EJDB2Handle *dbh;
} EJDB2Context;

static pthread_mutex_t k_shared_scope_mtx = PTHREAD_MUTEX_INITIALIZER;
static EJDB2Handle *k_head_handle;

static Dart_NativeFunction ejd_resolve_name(Dart_Handle name, int argc, bool *auto_setup_scope);

IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle);
IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc);
IW_INLINE Dart_Handle ejd_error_rc_throw(iwrc rc);

static void explain_rc(Dart_NativeArguments args);
static void jql_exec(Dart_NativeArguments args);
static void jql_set(Dart_NativeArguments args);

static void ejdb2_port_handler(Dart_Port receive_port, Dart_CObject *msg);
static void ejdb2_ctx_finalizer(void *isolate_callback_data, Dart_WeakPersistentHandle handle, void *peer);

static void ejdb2_port(Dart_NativeArguments arguments);
static void ejdb2_set_handle(Dart_NativeArguments args);
static void ejdb2_get_handle(Dart_NativeArguments args);
static void ejdb2_create_query(Dart_NativeArguments args);

static void ejdb2_open_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_close_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_get_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_put_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_del_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_patch_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_idx_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_rmi_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_rmc_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);
static void ejdb2_info_wrapped(Dart_Port receive_port, Dart_CObject *msg, Dart_Port reply_port);

static struct NativeFunctionLookup k_scoped_functions[] = {
  {"port", ejdb2_port},
  {"exec", jql_exec},
  {"jql_set", jql_set},
  {"create_query", ejdb2_create_query},
  {"set_handle", ejdb2_set_handle},
  {"get_handle", ejdb2_get_handle},
  {"explain_rc", explain_rc},
  {0, 0}
};

static struct WrapperFunctionLookup k_wrapped_functions[] = {
  {"get", ejdb2_get_wrapped},
  {"put", ejdb2_put_wrapped},
  {"del", ejdb2_del_wrapped},
  {"patch", ejdb2_patch_wrapped},
  {"idx", ejdb2_idx_wrapped},
  {"rmi", ejdb2_rmi_wrapped},
  {"rmc", ejdb2_rmc_wrapped},
  {"info", ejdb2_info_wrapped},
  {"open", ejdb2_open_wrapped},
  {"close", ejdb2_close_wrapped},
  {0, 0}
};

#define EJTH(h_) ejd_error_check_propagate(h_)

#define EJGO(h_, rh_, label_) \
  if (Dart_IsError(h_)) {   \
    rh_ = (h_);             \
    goto label_;            \
  }

#define EJLIB() EJTH(Dart_LookupLibrary(Dart_NewStringFromCString("package:ejdb2_dart/ejdb2_dart.dart")))

#define EJPORT_RC(co_, rc_)               \
  if (rc_) {                              \
    (co_)->type = Dart_CObject_kInt64;    \
    (co_)->value.as_int64 = (rc_);        \
  }

IW_INLINE char *cobject_str(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kString) {
      return co->value.as_string;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE int64_t cobject_int(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kInt32) {
      return co->value.as_int32;
    } else if (co->type == Dart_CObject_kInt64) {
      return co->value.as_int64;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE bool cobject_bool(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kBool) {
      return co->value.as_bool;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return false;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return false;
}

IW_INLINE double cobject_double(Dart_CObject *co, bool nulls, iwrc *rcp) {
  *rcp = 0;
  if (co) {
    if (co->type == Dart_CObject_kDouble) {
      return co->value.as_double;
    } else if (nulls && co->type == Dart_CObject_kNull) {
      return 0;
    }
  }
  *rcp = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
  return 0;
}

IW_INLINE Dart_Handle ejd_error_check_propagate(Dart_Handle handle) {
  if (Dart_IsError(handle)) {
    Dart_PropagateError(handle);
  }
  return handle;
}

static Dart_Handle ejd_error_object_handle(iwrc rc, const char *msg) {
  Dart_Handle hmsg = Dart_Null();
  if (msg) {
    hmsg = EJTH(Dart_NewStringFromCString(msg));
  } else if (rc) {
    const char *explained = iwlog_ecode_explained(rc);
    if (explained) {
      hmsg = EJTH(Dart_NewStringFromCString(explained));
    }
  }
  Dart_Handle hrc = EJTH(Dart_NewIntegerFromUint64(rc));
  Dart_Handle hclass = EJTH(Dart_GetClass(EJLIB(), Dart_NewStringFromCString("EJDB2Error")));
  Dart_Handle args[] = {hrc, hmsg};
  return Dart_New(hclass, Dart_Null(), 2, args);
}

IW_INLINE Dart_Handle ejd_error_rc_create(iwrc rc) {
  const char *msg = iwlog_ecode_explained(rc);
  return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
}

IW_INLINE Dart_Handle ejd_error_rc_create2(iwrc rc, const char *msg) {
  return Dart_NewUnhandledExceptionError(ejd_error_object_handle(rc, msg));
}

IW_INLINE Dart_Handle ejd_error_rc_throw(iwrc rc) {
  return Dart_PropagateError(ejd_error_rc_create(rc));
}

static void ejdb2_port(Dart_NativeArguments args) {
  EJDB2Context *ctx;
  intptr_t ptr = 0;

  Dart_EnterScope();
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));

  if (!ptr) {
    ctx = malloc(sizeof(*ctx));
    if (!ctx) {
      Dart_SetReturnValue(args, ejd_error_rc_create(IW_ERROR_ALLOC));
      goto finish;
    }
    ctx->dbh = 0;
    ctx->port = Dart_NewNativePort("ejdb2_port_handler", ejdb2_port_handler, true);
    if (ctx->port == ILLEGAL_PORT) {
      Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_CREATE_PORT));
      goto finish;
    }
    Dart_SetNativeInstanceField(self, 0, (intptr_t) ctx);
    Dart_NewWeakPersistentHandle(self, ctx, sizeof(*ctx), ejdb2_ctx_finalizer);
  } else {
    ctx = (void *) ptr;
  }
  Dart_SetReturnValue(args, EJTH(Dart_NewSendPort(ctx->port)));

finish:
  Dart_ExitScope();
}

static void ejdb2_get_handle(Dart_NativeArguments args) {
  intptr_t ptr = 0;
  Dart_EnterScope();
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
  EJDB2Context *ctx = (void *) ptr;
  if (ctx && ctx->dbh) {
    Dart_SetReturnValue(args, Dart_NewInteger((intptr_t) ctx->dbh));
  } else {
    Dart_SetReturnValue(args, Dart_Null());
  }
  Dart_ExitScope();
}

static void ejdb2_set_handle(Dart_NativeArguments args) {
  intptr_t ptr;
  Dart_EnterScope();
  Dart_SetReturnValue(args, Dart_Null());
  Dart_Handle self = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(self, 0, &ptr));
  EJDB2Context *ctx = (void *) ptr;
  if (!ctx) {
    Dart_SetReturnValue(args, ejd_error_rc_create(EJD_ERROR_INVALID_STATE));
    goto finish;
  }
  Dart_Handle dh = Dart_GetNativeArgument(args, 1);
  if (Dart_IsInteger(dh)) {
    EJTH(Dart_GetNativeIntegerArgument(args, 1, &ptr));
    ctx->dbh = (void *) ptr;
  } else if (Dart_IsNull(dh)) {
    ctx->dbh = 0;
  }

finish:
  Dart_ExitScope();
}

static void ejdb2_jql_finalizer(void *isolate_callback_data, Dart_WeakPersistentHandle handle, void *peer) {
  JQL q = (void *) peer;
  if (q) {
    jql_destroy(&q);
  }
}

static void ejdb2_create_query(Dart_NativeArguments args) {
  Dart_EnterScope();

  iwrc rc = 0;
  JQL q = 0;
  intptr_t ptr = 0;
  const char *query = 0;
  const char *collection = 0;

  Dart_Handle ret = Dart_Null();
  Dart_Handle hlib = EJLIB();
  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  Dart_Handle hquery = EJTH(Dart_GetNativeArgument(args, 1));
  Dart_Handle hcoll = EJTH(Dart_GetNativeArgument(args, 2));

  EJTH(Dart_StringToCString(hquery, &query));
  if (Dart_IsString(hcoll)) {
    EJTH(Dart_StringToCString(hcoll, &collection));
  }
  EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));

  EJDB2Context *ctx = (void *) ptr;
  if (!ctx || !ctx->dbh || !ctx->dbh->db) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  rc = jql_create2(&q, collection, query, JQL_KEEP_QUERY_ON_PARSE_ERROR | JQL_SILENT_ON_PARSE_ERROR);
  RCGO(rc, finish);

  if (!collection) {
    collection = jql_collection(q);
  }
  hcoll = Dart_NewStringFromCString(collection);
  EJGO(hcoll, ret, finish);

  Dart_Handle hclass = Dart_GetClass(hlib, Dart_NewStringFromCString("JQL"));
  EJGO(hclass, ret, finish);

  Dart_Handle jqargs[] = {hself, hquery, hcoll};
  Dart_Handle jqinst = Dart_New(hclass, Dart_NewStringFromCString("_"), 3, jqargs);
  EJGO(jqinst, ret, finish);

  ret = Dart_SetNativeInstanceField(jqinst, 0, (intptr_t) q);
  EJGO(ret, ret, finish);
  Dart_NewWeakPersistentHandle(jqinst, q, jql_estimate_allocated_size(q), ejdb2_jql_finalizer);

  ret = jqinst;

finish:
  if (rc || Dart_IsError(ret)) {
    if (rc) {
      if (q && rc == JQL_ERROR_QUERY_PARSE) {
        ret = ejd_error_rc_create2(rc, jql_error(q));
      } else {
        ret = ejd_error_rc_create(rc);
      }
    }
    if (q) {
      jql_destroy(&q);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

struct UXCTX {
  bool aggregate_count;
  Dart_Port reply_port;
};

static iwrc jql_exec_visitor(struct _EJDB_EXEC *ux, EJDB_DOC doc, int64_t *step) {
  iwrc rc = 0;
  struct UXCTX *uctx = ux->opaque;
  IWXSTR *xstr = iwxstr_new();
  if (!xstr) {
    return IW_ERROR_ALLOC;
  }
  if (doc->node) {
    rc = jbl_node_as_json(doc->node, jbl_xstr_json_printer, xstr, 0);
  } else {
    rc = jbl_as_json(doc->raw, jbl_xstr_json_printer, xstr, 0);
  }
  RCGO(rc, finish);

  Dart_CObject result, rv1, rv2;
  Dart_CObject *rv[2] = {&rv1, &rv2};
  result.type = Dart_CObject_kArray;
  result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
  result.value.as_array.values = rv;
  rv1.type = Dart_CObject_kInt64;
  rv1.value.as_int64 = doc->id;
  rv2.type = Dart_CObject_kString;
  rv2.value.as_string = iwxstr_ptr(xstr);

  if (!Dart_PostCObject(uctx->reply_port, &result)) {
    *step = 0; // End of cursor loop
  }

finish:
  iwxstr_destroy(xstr);
  return rc;
}

static void jql_exec_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
  iwrc rc = 0;
  Dart_Port reply_port = ILLEGAL_PORT;
  Dart_CObject result = {.type = Dart_CObject_kNull};
  if (msg->type != Dart_CObject_kArray || msg->value.as_array.length != 3)  {
    iwlog_error2("Invalid message recieved");
    return;
  }
  //  [reply_port, qptr, dbptr]
  EJDB_EXEC ux = {0};
  Dart_CObject **varr = msg->value.as_array.values;
  reply_port = cobject_int(varr[0], false, &rc);
  RCGO(rc, finish);
  ux.q = (void *) cobject_int(varr[1], false, &rc);
  if (!ux.q) {
    rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
    goto finish;
  }
  EJDB2Context *dctx = (void *) cobject_int(varr[2], false, &rc);
  if (!dctx || !dctx->dbh || !dctx->dbh->db) {
    rc = EJD_ERROR_INVALID_NATIVE_CALL_ARGS;
    goto finish;
  }
  struct UXCTX uctx = {
    .aggregate_count = jql_has_aggregate_count(ux.q),
    .reply_port = reply_port
  };
  ux.db = dctx->dbh->db;
  ux.visitor = uctx.aggregate_count ? 0 : jql_exec_visitor;
  ux.opaque = &uctx;

  rc = ejdb_exec(&ux);
  RCGO(rc, finish);

  if (uctx.aggregate_count) {
    Dart_CObject result, rv1, rv2;
    Dart_CObject *rv[2] = {&rv1, &rv2};
    result.type = Dart_CObject_kArray;
    result.value.as_array.length = sizeof(rv) / sizeof(rv[0]);
    result.value.as_array.values = rv;
    rv1.type = Dart_CObject_kInt64;
    rv1.value.as_int64 = ux.cnt;
    rv2.type = Dart_CObject_kNull;
    Dart_PostCObject(reply_port, &result);
  }

finish:
  if (rc) {
    iwlog_ecode_error3(rc);
    EJPORT_RC(&result, rc);
  }
  if (reply_port != ILLEGAL_PORT) {
    Dart_PostCObject(reply_port, &result); // Last NULL or error(int)
  }
  Dart_CloseNativePort(receive_port);
}

static void jql_exec(Dart_NativeArguments args) {
  // void _exec(SendPort sendPort) native 'exec';
  Dart_EnterScope();

  iwrc rc = 0;
  intptr_t qptr = 0, ptr = 0;
  Dart_Port reply_port = ILLEGAL_PORT;
  Dart_Port exec_port = ILLEGAL_PORT;
  Dart_Handle ret = Dart_Null();

  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  Dart_Handle hdb = EJTH(Dart_GetField(hself, Dart_NewStringFromCString("db")));
  Dart_Handle hport = EJTH(Dart_GetNativeArgument(args, 1));
  EJTH(Dart_SendPortGetId(hport, &reply_port));

  EJTH(Dart_GetNativeInstanceField(hdb, 0, &ptr));
  EJDB2Context *dctx = (void *) ptr;
  if (!dctx || !dctx->dbh || !dctx->dbh->db) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  // JQL pointer
  EJTH(Dart_GetNativeInstanceField(hself, 0, &qptr));
  if (!qptr) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  // JQL exec port
  exec_port = Dart_NewNativePort("jql_exec_port_handler", jql_exec_port_handler, false);
  if (exec_port == ILLEGAL_PORT) {
    rc = EJD_ERROR_CREATE_PORT;
    goto finish;
  }
  // Now post a message to the query executor port: [reply_port, qptr, dctx]
  Dart_CObject msg, marg1, marg2, marg3;
  Dart_CObject *margs[] = {&marg1, &marg2, &marg3};

  msg.type = Dart_CObject_kArray;
  msg.value.as_array.length = 3;
  msg.value.as_array.values = margs;

  marg1.type = Dart_CObject_kInt64;
  marg1.value.as_int64 = reply_port;

  marg2.type = Dart_CObject_kInt64;
  marg2.value.as_int64 = qptr;

  marg3.type = Dart_CObject_kInt64;
  marg3.value.as_int64 = (int64_t) dctx;

  if (!Dart_PostCObject(exec_port, &msg)) {
    rc = EJD_ERROR_POST_PORT;
    goto finish;
  }

finish:
  if (rc || Dart_IsError(ret)) {
    if (exec_port != ILLEGAL_PORT) {
      Dart_CloseNativePort(exec_port);
    }
    if (rc) {
      ret = ejd_error_rc_create(rc);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

static void jql_free_str(void *ptr, void *op) {
  if (ptr) free(ptr);
}

static void jql_free_json_node(void *ptr, void *op) {
  IWPOOL *pool = op;
  if (pool) iwpool_destroy(pool);
}

static void jql_set(Dart_NativeArguments args) {
  // void set(dynamic place, dynamic value) native 'jql_set';
  Dart_EnterScope();
  Dart_Handle ret = Dart_Null();

  iwrc rc = 0;
  intptr_t ptr = 0;
  Dart_Handle hself = EJTH(Dart_GetNativeArgument(args, 0));
  EJTH(Dart_GetNativeInstanceField(hself, 0, &ptr));
  JQL q = (void *) ptr;
  if (!q) {
    rc = EJD_ERROR_INVALID_STATE;
    goto finish;
  }
  int64_t npl = 0, type = 0;
  const char *svalue, *spl = 0;
  Dart_Handle hpl = EJTH(Dart_GetNativeArgument(args, 1));
  Dart_Handle hvalue = EJTH(Dart_GetNativeArgument(args, 2));
  Dart_Handle htype = EJTH(Dart_GetNativeArgument(args, 3));

  if (Dart_IsString(hpl)) {
    EJTH(Dart_StringToCString(hpl, &spl));
  } else {
    EJTH(Dart_IntegerToInt64(hpl, &npl));
  }
  if (Dart_IsInteger(htype)) {
    EJTH(Dart_IntegerToInt64(htype, &type));
  }
  if (type == 1) { // JSON
    EJTH(Dart_StringToCString(hvalue, &svalue));
    JBL_NODE node;
    IWPOOL *pool = iwpool_create(1024);
    if (!pool) {
      rc = IW_ERROR_ALLOC;
      goto finish;
    }
    rc = jbl_node_from_json(svalue, &node, pool);
    if (rc) {
      iwpool_destroy(pool);
      goto finish;
    }
    rc = jql_set_json2(q, spl, npl, node, jql_free_json_node, pool);
    if (rc) {
      iwpool_destroy(pool);
      goto finish;
    }
  } else if (type == 2) { // Regexp
    EJTH(Dart_StringToCString(hvalue, &svalue));
    char *str = strdup(svalue);
    if (!str) {
      rc = IW_ERROR_ALLOC;
      goto finish;
    }
    rc = jql_set_regexp2(q, spl, npl, str, jql_free_str, 0);
    if (rc) {
      free(str);
      goto finish;
    }
  } else { // All other cases
    if (Dart_IsString(hvalue)) {
      EJTH(Dart_StringToCString(hvalue, &svalue));
      char *str = strdup(svalue);
      if (!str) {
        rc = IW_ERROR_ALLOC;
        goto finish;
      }
      rc = jql_set_str2(q, spl, npl, str, jql_free_str, 0);
      if (rc) {
        free(str);
        goto finish;
      }
    } else if (Dart_IsInteger(hvalue)) {
      int64_t val;
      EJTH(Dart_IntegerToInt64(hvalue, &val));
      rc = jql_set_i64(q, spl, npl, val);
    } else if (Dart_IsDouble(hvalue)) {
      double val;
      EJTH(Dart_DoubleValue(hvalue, &val));
      rc = jql_set_f64(q, spl, npl, val);
    } else if (Dart_IsBoolean(hvalue)) {
      bool val;
      EJTH(Dart_BooleanValue(hvalue, &val));
      rc = jql_set_bool(q, spl, npl, val);
    } else if (Dart_IsNull(hvalue)) {
      rc = jql_set_null(q, spl, npl);
    }
  }

finish:
  if (rc || Dart_IsError(ret)) {
    if (rc) {
      ret = ejd_error_rc_create(rc);
    }
  }
  Dart_SetReturnValue(args, ret);
  Dart_ExitScope();
}

static void explain_rc(Dart_NativeArguments args) {
  Dart_EnterScope();
  int64_t llv = 0;
  EJTH(Dart_GetNativeIntegerArgument(args, 0, &llv));
  const char *msg = iwlog_ecode_explained((iwrc) llv);
  if (msg) {
    Dart_SetReturnValue(args, EJTH(Dart_NewStringFromCString(msg)));
  } else {
    Dart_SetReturnValue(args, Dart_Null());
  }
  Dart_ExitScope();
}

static Dart_NativeFunction ejd_resolve_name(Dart_Handle name,
                                            int argc,
                                            bool *auto_setup_scope) {
  if (!Dart_IsString(name) || !auto_setup_scope) {
    return 0;
  }
  Dart_EnterScope();
  const char *cname;
  EJTH(Dart_StringToCString(name, &cname));
  for (int i = 0; k_scoped_functions[i].name; ++i) {
    if (strcmp(cname, k_scoped_functions[i].name) == 0) {
      *auto_setup_scope = true;
      Dart_ExitScope();
      return k_scoped_functions[i].fn;
    }
  }
  Dart_ExitScope();
  return 0;
}

static void ejdb2_port_handler(Dart_Port receive_port, Dart_CObject *msg) {
  if (msg->type != Dart_CObject_kArray
      || msg->value.as_array.length < 2
      || msg->value.as_array.values[0]->type != Dart_CObject_kSendPort
      || msg->value.as_array.values[1]->type != Dart_CObject_kString) {
    iwlog_error2("Invalid message recieved");
    return;
  }
  Dart_Port reply_port = msg->value.as_array.values[0]->value.as_send_port.id;
  const char *fname = msg->value.as_array.values[1]->value.as_string;
  for (int i = 0; k_wrapped_functions[i].name; ++i) {
    if (strcmp(k_wrapped_functions[i].name, fname) == 0) {
      k_wrapped_functions[i].fn(receive_port, msg, reply_port);
      break;
    }
  }
}

static iwrc ejdb2_isolate_shared_open(const EJDB_OPTS *opts, EJDB2Handle **hptr) {
  iwrc rc = 0;
  EJDB db = 0;
  int rci = pthread_mutex_lock(&k_shared_scope_mtx);
  if (rci) return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
  const char *path = opts->kv.path;
  EJDB2Handle *h = k_head_handle;
  while (h) {
    if (!strcmp(h->path, path)) {
      break;
    }
    h = h->next;
  }
  if (h) {
    h->refs++;
    *hptr = h;
    goto finish;
  }
  rc = ejdb_open(opts, &db);
  RCGO(rc, finish);

  h = calloc(1, sizeof(*h));
  if (!h) {
    rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
    goto finish;
  }
  h->path = strdup(path);
  if (!h->path) {
    free(h);
    rc = iwrc_set_errno(IW_ERROR_ALLOC, errno);
    goto finish;
  }
  h->refs = 1;
  h->db = db;
  if (k_head_handle) {
    k_head_handle->prev = h;
    h->next = k_head_handle;
  }
  k_head_handle = h;
  *hptr = h;

finish:
  pthread_mutex_unlock(&k_shared_scope_mtx);
  if (rc) {
    iwlog_ecode_error3(rc);
    if (db) {
      ejdb_close(&db);
    }
  }
  return rc;
}

static iwrc ejdb2_isolate_shared_release(EJDB2Handle **hp) {
  iwrc rc = 0;
  int rci = pthread_mutex_lock(&k_shared_scope_mtx);
  if (rci) return iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci);
  EJDB2Handle *h = *hp;
  *hp = 0;
  if (--h->refs <= 0) {
    if (h->db) rc = ejdb_close(&h->db);
    if (h->prev) {
{'fraction_non_alphanumeric': 0.1269807583474816, 'fraction_numerical': 0.01270986606300698, 'mean_word_length': 3.980504991192014, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 150, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4319821', 'n_tokens_mistral': 17793, 'n_tokens_neox': 17144, 'n_words': 3219}