/c_src/erlv8.cc

http://github.com/beamjs/erlv8 · C++ · 554 lines · 456 code · 88 blank · 10 comment · 34 complexity · 7651d6aa0a00533353532f3ad4999e8a MD5 · raw file

  1. #include "erlv8.hh"
  2. typedef TickHandlerResolution (*TickHandler)(VM *, char *, ERL_NIF_TERM, ERL_NIF_TERM, ERL_NIF_TERM, int, const ERL_NIF_TERM*);
  3. struct ErlV8TickHandler {
  4. const char * name;
  5. TickHandler handler;
  6. };
  7. static ErlV8TickHandler tick_handlers[] =
  8. {
  9. {"stop", StopTickHandler},
  10. {"result", ResultTickHandler},
  11. {"call", CallTickHandler},
  12. {"inst", InstantiateTickHandler},
  13. {"delete", DeleteTickHandler},
  14. {"taint", TaintTickHandler},
  15. {"equals", EqualsTickHandler},
  16. {"strict_equals", StrictEqualsTickHandler},
  17. {"get", GetTickHandler},
  18. {"get_proto", GetProtoTickHandler},
  19. {"get_hidden", GetHiddenTickHandler},
  20. {"set", SetTickHandler},
  21. {"set_proto", SetProtoTickHandler},
  22. {"set_hidden", SetHiddenTickHandler},
  23. {"set_accessor", SetAccessorTickHandler},
  24. {"proplist", ProplistTickHandler},
  25. {"list", ListTickHandler},
  26. {"script", ScriptTickHandler},
  27. {"gc", GCTickHandler},
  28. {"to_string", ToStringTickHandler},
  29. {"to_detail_string", ToDetailStringTickHandler},
  30. {"extern_proto", ExternProtoTickHandler},
  31. {"externalize", ExternalizeTickHandler},
  32. {"internal_count", InternalCountTickHandler},
  33. {"set_internal", SetInternalTickHandler},
  34. {"set_internal_extern", SetInternalTickHandler},
  35. {"get_internal", GetInternalTickHandler},
  36. {NULL, UnknownTickHandler}
  37. };
  38. VM::VM() {
  39. env = enif_alloc_env();
  40. mutex = enif_mutex_create((char*)"erlv8_vm_mutex");
  41. isolate = v8::Isolate::New();
  42. v8::Isolate::Scope iscope(isolate);
  43. v8::Locker locker(isolate);
  44. v8::HandleScope handle_scope;
  45. // Moved into the VM object since we have a own isolate for each VM
  46. global_template = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
  47. external_template = v8::Persistent<v8::ObjectTemplate>::New(v8::ObjectTemplate::New());
  48. empty_constructor = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(EmptyFun));
  49. string__erlv8__ = v8::Persistent<v8::String>::New(v8::String::New("__erlv8__"));
  50. context = v8::Context::New(NULL, global_template);
  51. v8::Context::Scope context_scope(context);
  52. tid = enif_thread_self();
  53. context->Global()->SetHiddenValue(string__erlv8__,v8::External::New(this));
  54. ctx_res_t *ptr = (ctx_res_t *)enif_alloc_resource(ctx_resource, sizeof(ctx_res_t));
  55. ptr->ctx = v8::Persistent<v8::Context>::New(context);
  56. ERL_NIF_TERM resource_term = enif_make_resource(env, ptr);
  57. enif_release_resource(ptr);
  58. context->Global()->SetHiddenValue(v8::String::New("__erlv8__ctx__"),term_to_external(resource_term));
  59. v8::Local<v8::Object> tmp = external_template->NewInstance();
  60. external_proto_num = v8::Persistent<v8::Object>::New(tmp);
  61. external_proto_atom = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  62. external_proto_bin = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  63. external_proto_ref = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  64. external_proto_fun = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  65. external_proto_port = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  66. external_proto_pid = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  67. external_proto_tuple = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  68. external_proto_list = v8::Persistent<v8::Object>::New(external_template->NewInstance());
  69. push_socket = zmq_socket(zmq_context, ZMQ_PUSH);
  70. ticker_push_socket = zmq_socket(zmq_context, ZMQ_PUSH);
  71. pull_socket = zmq_socket(zmq_context, ZMQ_PULL);
  72. char socket_id[64];
  73. sprintf(socket_id, "inproc://tick-publisher-%ld", (long int) this);
  74. char ticker_socket_id[64];
  75. sprintf(ticker_socket_id, "inproc://tick-publisher-ticker-%ld", (long int) this);
  76. zmq_bind(push_socket, socket_id);
  77. zmq_bind(ticker_push_socket, ticker_socket_id);
  78. zmq_connect(pull_socket, socket_id);
  79. zmq_connect(pull_socket, ticker_socket_id);
  80. };
  81. VM::~VM() {
  82. // v8::Isolate::Scope iscope(isolate);
  83. // v8::Locker locker(isolate);
  84. // v8::HandleScope handle_scope;
  85. isolate->Enter();
  86. TRACE("(%p) VM::~VM - 1\n", isolate);
  87. enif_mutex_destroy(mutex);
  88. TRACE("(%p) VM::~VM - 2\n", isolate);
  89. TRACE("(%p) VM::~VM - 3\n", isolate);
  90. external_proto_bin.Dispose();
  91. external_proto_bin.Clear();
  92. TRACE("(%p) VM::~VM - 4\n", isolate);
  93. external_proto_ref.Dispose();
  94. external_proto_ref.Clear();
  95. external_proto_fun.Dispose();
  96. external_proto_fun.Clear();
  97. external_proto_port.Dispose();
  98. external_proto_port.Clear();
  99. external_proto_pid.Dispose();
  100. external_proto_pid.Dispose();
  101. external_proto_tuple.Clear();
  102. external_proto_list.Dispose();
  103. external_proto_list.Clear();
  104. TRACE("(%p) VM::~VM - 4\n", isolate);
  105. global_template.Dispose();
  106. global_template.Clear();
  107. TRACE("(%p) VM::~VM - 5\n", isolate);
  108. external_template.Dispose();
  109. external_template.Clear();
  110. TRACE("(%p) VM::~VM - 6\n", isolate);
  111. empty_constructor.Dispose();
  112. empty_constructor.Clear();
  113. TRACE("(%p) VM::~VM - 7\n", isolate);
  114. string__erlv8__.Dispose();
  115. string__erlv8__.Clear();
  116. TRACE("(%p) VM::~VM - 8\n", isolate);
  117. external_proto_num.Dispose();
  118. external_proto_num.Clear();
  119. TRACE("(%p) VM::~VM - 9\n", isolate);
  120. external_proto_atom.Dispose();
  121. external_proto_atom.Clear();
  122. TRACE("(%p) VM::~VM - 10\n", isolate);
  123. enif_free_env(env);
  124. TRACE("(%p) VM::~VM - 11\n", isolate);
  125. context.Dispose();
  126. context.Clear();
  127. while (v8::Isolate::GetCurrent() == isolate) {
  128. isolate->Exit();
  129. }
  130. // this should dispoe everything created in the isolate:
  131. // http://markmail.org/message/mcn27ibuijhgkehl
  132. isolate->Dispose();
  133. zmq_close(push_socket);
  134. zmq_close(ticker_push_socket);
  135. zmq_close(pull_socket);
  136. };
  137. void VM::run() {
  138. v8::Isolate::Scope iscope(isolate);
  139. v8::Locker locker(isolate);
  140. v8::HandleScope handle_scope; // the very top level handle scope
  141. ticker(0);
  142. };
  143. void VM::terminate() {
  144. TRACE("(%p) VM::terminate - 1\n", isolate);
  145. v8::V8::TerminateExecution(isolate);
  146. }
  147. v8::Handle<v8::Value> VM::ticker(ERL_NIF_TERM ref0) {
  148. TRACE("(%p) VM::ticker - 0\n", isolate);
  149. LHCS(isolate, context);
  150. isolate->Enter();
  151. TRACE("(%p) VM::ticker - 1\n", isolate);
  152. char name[MAX_ATOM_LEN];
  153. unsigned len;
  154. ErlNifEnv * ref_env = enif_alloc_env();
  155. ERL_NIF_TERM ref;
  156. TRACE("(%p) VM::ticker - 2\n", isolate);
  157. if ((unsigned long) ref0 == 0) {
  158. ref = ref0;
  159. DEBUG(server, enif_make_atom(env, "current_ticker"), enif_make_atom(env, "top"));
  160. } else {
  161. ref = enif_make_copy(ref_env, ref0);
  162. DEBUG(server, enif_make_atom(env, "current_ticker"), enif_make_copy(env, ref));
  163. }
  164. TRACE("(%p) VM::ticker - 3\n", isolate);
  165. zmq_msg_t msg;
  166. Tick tick_s;
  167. ERL_NIF_TERM tick, tick_ref;
  168. while (1) {
  169. {
  170. isolate->Exit();
  171. TRACE("(%p) VM::ticker - 3.1\n", isolate);
  172. v8::Unlocker unlocker(isolate);
  173. TRACE("(%p) VM::ticker - 3.2\n", isolate);
  174. zmq_msg_init (&msg);
  175. TRACE("(%p) VM::ticker - 3.3\n", isolate);
  176. zmq_recv (pull_socket, &msg, 0);
  177. TRACE("(%p) VM::ticker - 3.4\n", isolate);
  178. memcpy(&tick_s, zmq_msg_data(&msg), sizeof(Tick));
  179. TRACE("(%p) VM::ticker - 3.5\n", isolate);
  180. tick = enif_make_copy(env, tick_s.tick);
  181. TRACE("(%p) VM::ticker - 3.6\n", isolate);
  182. tick_ref = enif_make_copy(env, tick_s.ref);
  183. TRACE("(%p) VM::ticker - 3.7\n", isolate);
  184. enif_free_env(tick_s.env);
  185. TRACE("(%p) VM::ticker - 3.8\n", isolate);
  186. zmq_msg_close(&msg);
  187. TRACE("(%p) VM::ticker - 3.9\n", isolate);
  188. }
  189. isolate->Enter();
  190. TRACE("(%p) VM::ticker - 4\n", isolate);
  191. DEBUG(server,
  192. enif_make_tuple2(env,
  193. enif_make_atom(env, "last_tick"),
  194. (unsigned long) ref == 0 ? enif_make_atom(env,"top") : enif_make_copy(env, ref)),
  195. enif_make_copy(env, tick));
  196. if (enif_is_tuple(env, tick)) { // should be always true, just a sanity check
  197. TRACE("(%p) VM::ticker - 5\n", isolate);
  198. ERL_NIF_TERM *array;
  199. int arity;
  200. enif_get_tuple(env,tick,&arity,(const ERL_NIF_TERM **)&array);
  201. enif_get_atom_length(env, array[0], &len, ERL_NIF_LATIN1);
  202. enif_get_atom(env,array[0],(char *)&name,len + 1, ERL_NIF_LATIN1);
  203. // lookup the matrix
  204. unsigned int i = 0;
  205. bool stop_flag = false;
  206. TRACE("(%p) VM::ticker - 6 (%s)\n", isolate, name);
  207. while (!stop_flag) {
  208. if ((!tick_handlers[i].name) ||
  209. (!strcmp(name,tick_handlers[i].name))) { // handler has been located
  210. TRACE("(%p) VM::ticker - 7\n", isolate);
  211. TickHandlerResolution resolution = (tick_handlers[i].handler(this, name, tick, tick_ref, ref, arity, array));
  212. TRACE("(%p) VM::ticker - 8\n", isolate);
  213. switch (resolution.type) {
  214. case DONE:
  215. stop_flag = true;
  216. break;
  217. case NEXT:
  218. break;
  219. case RETURN:
  220. TRACE("(%p) VM::ticker - 9\n", isolate);
  221. enif_free_env(ref_env);
  222. TRACE("(%p) VM::ticker - 10\n", isolate);
  223. enif_clear_env(env);
  224. TRACE("(%p) VM::ticker - 11\n", isolate);
  225. zmq_msg_t tick_msg;
  226. int e;
  227. TRACE("(%p) VM::ticker - 12\n", isolate);
  228. while (!pop_ticks.empty()) {
  229. TRACE("(%p) VM::ticker - 12.1\n", isolate);
  230. Tick newtick = pop_ticks.front();
  231. TRACE("(%p) VM::ticker - 12.2\n", isolate);
  232. pop_ticks.pop();
  233. TRACE("(%p) VM::ticker - 12.3\n", isolate);
  234. zmq_msg_init_size(&tick_msg, sizeof(Tick));
  235. TRACE("(%p) VM::ticker - 12.4\n", isolate);
  236. memcpy(zmq_msg_data(&tick_msg), &newtick, sizeof(Tick));
  237. TRACE("(%p) VM::ticker - 12.5\n", isolate);
  238. do {
  239. e = zmq_send(ticker_push_socket, &tick_msg, ZMQ_NOBLOCK);
  240. TRACE("(%p) VM::ticker - 12.6\n", isolate);
  241. } while (e == EAGAIN);
  242. zmq_msg_close(&tick_msg);
  243. }
  244. TRACE("(%p) VM::ticker - 13\n", isolate);
  245. return handle_scope.Close(resolution.value);
  246. break;
  247. }
  248. }
  249. i++;
  250. }
  251. }
  252. enif_clear_env(env);
  253. }
  254. };
  255. void * start_vm(void *data) {
  256. VM *vm = reinterpret_cast<VM *>(data);
  257. vm->run();
  258. enif_mutex_lock(vm->mutex);
  259. enif_mutex_unlock(vm->mutex);
  260. delete vm;
  261. return NULL;
  262. };
  263. static ERL_NIF_TERM new_vm(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  264. {
  265. ERL_NIF_TERM term;
  266. VM *vm = new VM();
  267. vm_res_t *ptr = (vm_res_t *)enif_alloc_resource(vm_resource, sizeof(vm_res_t));
  268. ptr->vm = vm;
  269. vm->resource = ptr;
  270. term = enif_make_resource(env, ptr);
  271. enif_release_resource(ptr);
  272. return term;
  273. };
  274. static ERL_NIF_TERM set_server(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  275. {
  276. vm_res_t *res;
  277. if (enif_get_resource(env,argv[0],vm_resource,(void **)(&res))) {
  278. res->vm->server = (ErlNifPid *) malloc(sizeof(ErlNifPid));
  279. enif_get_local_pid(env, argv[1], res->vm->server);
  280. enif_thread_create((char *)"erlv8", &res->vm->tid, start_vm, res->vm, NULL);
  281. return enif_make_atom(env,"ok");
  282. };
  283. return enif_make_badarg(env);
  284. };
  285. static ERL_NIF_TERM context(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  286. {
  287. vm_res_t *res;
  288. if (enif_get_resource(env,argv[0],vm_resource,(void **)(&res))) {
  289. LHCS(res->vm->isolate, res->vm->context);
  290. ctx_res_t *ptr = (ctx_res_t *)enif_alloc_resource(ctx_resource, sizeof(ctx_res_t));
  291. ptr->ctx = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
  292. ERL_NIF_TERM term = enif_make_resource(env, ptr);
  293. enif_release_resource(ptr);
  294. return term;
  295. };
  296. return enif_make_badarg(env);
  297. };
  298. static ERL_NIF_TERM kill(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  299. vm_res_t *res;
  300. TRACE("kill - 1\n");
  301. if (enif_get_resource(env,argv[0],vm_resource,(void **)(&res))) {
  302. TRACE("kill - 2\n");
  303. res->vm->terminate();
  304. TRACE("kill - 3\n");
  305. return enif_make_atom(env,"ok");
  306. } else {
  307. TRACE("kill - 3\n");
  308. return enif_make_badarg(env);
  309. }
  310. }
  311. static ERL_NIF_TERM stop(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  312. vm_res_t *res;
  313. int e;
  314. if (enif_get_resource(env,argv[0],vm_resource,(void **)(&res))) {
  315. if ((!enif_is_ref(env, argv[1])))
  316. return enif_make_badarg(env);
  317. TRACE("(%p) stop\n", res->vm->isolate);
  318. zmq_msg_t tick_msg;
  319. Tick tick;
  320. tick.env = enif_alloc_env();
  321. tick.tick = enif_make_tuple1(tick.env, enif_make_atom(tick.env, "stop"));
  322. tick.ref = enif_make_copy(tick.env, argv[1]);
  323. zmq_msg_init_size(&tick_msg, sizeof(Tick));
  324. memcpy(zmq_msg_data(&tick_msg), &tick, sizeof(Tick));
  325. enif_mutex_lock(res->vm->mutex);
  326. do {
  327. e = zmq_send(res->vm->push_socket, &tick_msg, ZMQ_NOBLOCK);
  328. } while (e == EAGAIN);
  329. zmq_msg_close(&tick_msg);
  330. enif_mutex_unlock(res->vm->mutex);
  331. return enif_make_atom(env,"ok");
  332. } else {
  333. return enif_make_badarg(env);
  334. };
  335. };
  336. static ERL_NIF_TERM tick(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  337. vm_res_t *res;
  338. int e;
  339. if (enif_get_resource(env,argv[0],vm_resource,(void **)(&res))) {
  340. if ((!enif_is_ref(env, argv[1])))
  341. return enif_make_badarg(env);
  342. zmq_msg_t tick_msg;
  343. Tick tick;
  344. tick.env = enif_alloc_env();
  345. tick.tick = enif_make_copy(tick.env, argv[2]);
  346. tick.ref = enif_make_copy(tick.env, argv[1]);
  347. zmq_msg_init_size(&tick_msg, sizeof(Tick));
  348. memcpy(zmq_msg_data(&tick_msg), &tick, sizeof(Tick));
  349. do {
  350. e = zmq_send(res->vm->push_socket, &tick_msg, ZMQ_NOBLOCK);
  351. } while (e == EAGAIN);
  352. zmq_msg_close(&tick_msg);
  353. return enif_make_atom(env,"tack");
  354. } else {
  355. return enif_make_badarg(env);
  356. };
  357. };
  358. static ERL_NIF_TERM global(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  359. ctx_res_t *res;
  360. vm_res_t *vm_res;
  361. if (
  362. enif_get_resource(env, argv[0], vm_resource, (void **)(&vm_res))
  363. && enif_get_resource(env,argv[1],ctx_resource,(void **)(&res))) {
  364. LHCS(vm_res->vm->isolate, res->ctx);
  365. v8::Handle<v8::Object> global = res->ctx->Global();
  366. return js_to_term(res->ctx, vm_res->vm->isolate, env,global);
  367. } else {
  368. return enif_make_badarg(env);
  369. };
  370. };
  371. static ERL_NIF_TERM new_context(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  372. vm_res_t *res;
  373. if (enif_get_resource(env, argv[0], vm_resource, (void **)(&res))) {
  374. LHCS(res->vm->isolate, res->vm->context);
  375. v8::Persistent<v8::Context> context = v8::Context::New(NULL, res->vm->global_template);
  376. context->Global()->SetHiddenValue(res->vm->string__erlv8__, v8::External::New(res->vm));
  377. ctx_res_t *ptr = (ctx_res_t *)enif_alloc_resource(ctx_resource, sizeof(ctx_res_t));
  378. ptr->ctx = v8::Persistent<v8::Context>::New(context);
  379. ERL_NIF_TERM resource_term = enif_make_resource(env, ptr);
  380. enif_release_resource(ptr);
  381. context->Global()->SetHiddenValue(v8::String::New("__erlv8__ctx__"), term_to_external(resource_term));
  382. return resource_term;
  383. } else {
  384. return enif_make_badarg(env);
  385. };
  386. };
  387. static ErlNifFunc nif_funcs[] =
  388. {
  389. {"kill", 1, kill},
  390. {"new_vm", 0, new_vm},
  391. {"set_server", 2, set_server},
  392. {"context", 1, context},
  393. {"new_context", 1, new_context},
  394. {"global", 2, global},
  395. {"tick", 3, tick},
  396. {"stop", 2, stop},
  397. };
  398. v8::Handle<v8::Value> EmptyFun(const v8::Arguments &arguments) {
  399. v8::HandleScope handle_scope;
  400. return v8::Undefined();
  401. }
  402. v8::Handle<v8::Value> WrapFun(const v8::Arguments &arguments) {
  403. v8::HandleScope handle_scope;
  404. VM * vm = (VM *)__ERLV8__(v8::Context::GetCurrent()->Global());
  405. // each call gets a unique ref
  406. ERL_NIF_TERM ref = enif_make_ref(vm->env);
  407. // prepare arguments
  408. ERL_NIF_TERM *arr = (ERL_NIF_TERM *) malloc(sizeof(ERL_NIF_TERM) * arguments.Length());
  409. for (int i=0;i<arguments.Length();i++) {
  410. arr[i] = js_to_term(vm->context, vm->isolate, vm->env,arguments[i]);
  411. }
  412. ERL_NIF_TERM arglist = enif_make_list_from_array(vm->env,arr,arguments.Length());
  413. free(arr);
  414. // send invocation request
  415. SEND(vm->server,
  416. enif_make_tuple3(env,
  417. enif_make_copy(env,external_to_term(arguments.Data())),
  418. enif_make_tuple7(env,
  419. enif_make_atom(env,"erlv8_fun_invocation"),
  420. enif_make_atom(env,arguments.IsConstructCall() ? "true" : "false"),
  421. js_to_term(vm->context, vm->isolate, env, arguments.Holder()),
  422. js_to_term(vm->context, vm->isolate, env, arguments.This()),
  423. enif_make_copy(env, ref),
  424. enif_make_pid(env, vm->server),
  425. enif_make_copy(env, external_to_term(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__ctx__"))))),
  426. enif_make_copy(env,arglist)));
  427. return handle_scope.Close(vm->ticker(ref));
  428. };
  429. static void vm_resource_destroy(ErlNifEnv* env, void* obj) {
  430. };
  431. static void val_resource_destroy(ErlNifEnv* env, void* obj) {
  432. };
  433. static void ctx_resource_destroy(ErlNifEnv* env, void* obj) {
  434. };
  435. int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM load_info)
  436. {
  437. TRACE("load\n");
  438. zmq_context = zmq_init(0); // we are using inproc only, so no I/O threads
  439. vm_resource = enif_open_resource_type(env, NULL, "erlv8_vm_resource", vm_resource_destroy, (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER), NULL);
  440. val_resource = enif_open_resource_type(env, NULL, "erlv8_val_resource", val_resource_destroy, (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER), NULL);
  441. ctx_resource = enif_open_resource_type(env, NULL, "erlv8_ctx_resource", ctx_resource_destroy, (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER), NULL);
  442. v8::V8::Initialize();
  443. int preemption = 100; // default value
  444. enif_get_int(env, load_info, &preemption);
  445. v8::Locker locker;
  446. v8::Locker::StartPreemption(preemption);
  447. v8::HandleScope handle_scope;
  448. return 0;
  449. };
  450. void unload(ErlNifEnv *env, void* priv_data)
  451. {
  452. TRACE("unload\n");
  453. v8::Locker::StopPreemption();
  454. zmq_term(zmq_context);
  455. };
  456. int reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
  457. return 0;
  458. }
  459. int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) {
  460. return 0;
  461. }
  462. ErlNifResourceType * ctx_resource;
  463. ErlNifResourceType * vm_resource;
  464. ErlNifResourceType * val_resource;
  465. void *zmq_context;
  466. ERL_NIF_INIT(erlv8_nif,nif_funcs,load,reload,upgrade,unload)