Source

v8erl / src / driver.cc

Full commit
#include <string>
#include "js2erl.h"

static Persistent<ObjectTemplate> global_template;
static Persistent<ObjectTemplate> term_template;

static int init();
static void finish();
static ErlDrvData start(ErlDrvPort, char *);
static void stop(ErlDrvData);
static void output(ErlDrvData, char *, int);
static void ready_async(ErlDrvData, ErlDrvThreadData);
// local
static void run_script(void *);
static void async_free(void *);

class V8Data {
public:
  ErlDrvPort port;
  Persistent<Context> context;

  V8Data(ErlDrvPort, Handle<Context>);
  ~V8Data();
  ErlDrvData castOut();
  static V8Data *castIn(ErlDrvData);
};

V8Data::V8Data(ErlDrvPort p, Handle<Context> c) {
  port = p;
  context = Persistent<Context>::New(c);
};

V8Data::~V8Data() {
  context.Dispose();
};

ErlDrvData V8Data::castOut() { 
  return reinterpret_cast<ErlDrvData>(this); 
};

V8Data *V8Data::castIn(ErlDrvData data) {
  return reinterpret_cast<V8Data *>(data);
};

class AsyncData {
public:
  // input
  Handle<Context> context;
  char *buf;
  int len;
  // output
  ErlDrvTermData *terms;
  int tlen;

  AsyncData(Handle<Context>, char *, int);
  void setResult(Handle<Value>);
  void send(ErlDrvPort);
  void setError(char *);
  ~AsyncData();
  void *castOut();
  static AsyncData *castIn(void *);
};

AsyncData::AsyncData(Handle<Context> c, char *b, int l) {
  context = c;
  buf     = b;
  len     = l;
};

AsyncData::~AsyncData() {
  driver_free(terms);
};

void AsyncData::setResult(Handle<Value> result) {
  tlen  = terms_length(result);
  terms = (ErlDrvTermData *)driver_alloc(sizeof(ErlDrvTermData) * tlen);
  value_to_terms(result, terms);
};

void AsyncData::send(ErlDrvPort port) {
  driver_output_term(port, terms, tlen);
};

void AsyncData::setError(char *msg) {
  tlen = 2;
  terms = (ErlDrvTermData *)driver_alloc(sizeof(ErlDrvTermData) * tlen);
  terms[0] = ERL_DRV_ATOM;
  terms[1] = driver_mk_atom(msg);
};

void *AsyncData::castOut() {
  return reinterpret_cast<void *>(this); 
};

AsyncData *AsyncData::castIn(void *data) {
  return reinterpret_cast<AsyncData *>(data);
};

//
//
//

ErlDrvEntry driver_entry = {
  init, // init driver
  start, // open_port
  stop, // close_port
  output, // output		
  NULL,			/* ready_input */
  NULL,			/* ready_output */ 
  "v8erl", // driver name
  finish,  // unload dynamic driver
  NULL, // handle: EMULATOR USE ONLY
  NULL,			/* control */
  NULL,			/* timeout */
  NULL,			/* outputv */
  ready_async, // async event done
  NULL,			/* flush */
  NULL,			/* call */
  NULL			/* event */
};

extern "C" { 
  DRIVER_INIT(v8erl) {
    return &driver_entry;
  }
}

Persistent<ObjectTemplate> erl_global() {
  Handle<ObjectTemplate> global = ObjectTemplate::New(); 
  return Persistent<ObjectTemplate>::New(global);
}

Persistent<ObjectTemplate> erl_term_template() {
  Handle<ObjectTemplate> temp = ObjectTemplate::New();
  temp->SetInternalFieldCount(1);
  return Persistent<ObjectTemplate>::New(temp);
}

static int init() {
  global_template = erl_global();
  term_template = erl_term_template();
  return 0;
}

static void finish() {
  global_template.Dispose();
  term_template.Dispose();
}

static ErlDrvData start(ErlDrvPort port, char *cmd) {
  if (port == NULL) { 
    return ERL_DRV_ERROR_GENERAL; // why?
  } else { 
    Handle<Context> context = Context::New(NULL, global_template);
    V8Data *data = new V8Data(port, context);
    return data->castOut(); 
  }
}

static void stop(ErlDrvData edata) {
  delete V8Data::castIn(edata);
}


static void output(ErlDrvData edata, char *buf, int len) {
  V8Data     *data = V8Data::castIn(edata);
  AsyncData *adata = new AsyncData(data->context, buf, len);
  driver_async(data->port, 
               (unsigned int *)&(data->port), // per-port async
               run_script, 
               adata, 
               async_free);
}


static void ready_async(ErlDrvData edata, ErlDrvThreadData tdata) {
  V8Data     *data = V8Data::castIn(edata);
  AsyncData *adata = AsyncData::castIn(tdata);
  adata->send(data->port);
  delete adata;
}


static void run_script(void *tdata) {
  AsyncData *adata = AsyncData::castIn(tdata);
 
  Context::Scope context_scope(adata->context);
  HandleScope handle_scope;
  TryCatch try_catch;

  Handle<String> script = String::New(adata->buf, adata->len);
  Handle<Script> compiled = Script::Compile(script);
  if (compiled.IsEmpty()) {
    adata->setError("compiler_error"); 
  } else { 
    adata->setResult(compiled->Run()); 
  }
}


static void async_free(void *tdata) {
  delete AsyncData::castIn(tdata);
}