PageRenderTime 54ms CodeModel.GetById 2ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

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