Source

v8erl / src / driver.cc

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

#define DRV_RUN_SCRIPT 'R'

// driver symbols
static ErlDrvData start(ErlDrvPort, char *);
static void stop(ErlDrvData);
static void outputv(ErlDrvData, ErlIOVec *);
static void ready_async(ErlDrvData, ErlDrvThreadData);

// local
static void run_script(void *);
static void async_free(void *);

static const char OK[] = "ok";
static const char ERROR[] = "error";
static char DRIVER_NAME[] = "v8erl";

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

  PortContext(ErlDrvPort);
  ~PortContext();
  ErlDrvData castOut();
  static PortContext *castIn(void *);
};

PortContext::PortContext(ErlDrvPort p) {
  port = p;

  HandleScope handle_scope;
  Handle<ObjectTemplate> global = ObjectTemplate::New();
  context = Context::New(NULL, global);
};

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

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

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

class AsyncData {
public:
  // input
  ErlDrvPort port;
  Persistent<Context> context;
  char *buf;
  int len;
  // output
  const char *type;
  Persistent<Value> result;

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

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

AsyncData::~AsyncData() {
  result.Dispose();
};

void AsyncData::setResult(const char *t, Handle<Value> r) {
  type = t;
  result = Persistent<Value>::New(r);
};

void AsyncData::send() {
  Context::Scope context_scope(context);
  HandleScope handle_scope;

  unsigned int tlen = terms_length(result) + 4;
  ErlDrvTermData *terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
  terms[0] = ERL_DRV_ATOM;
  terms[1] = driver_mk_atom(const_cast<char *>(type));

  list<void *> pl;
  value_to_terms(result, terms + 2, &pl);

  terms[tlen - 2] = ERL_DRV_TUPLE;
  terms[tlen - 1] = 2;

  driver_output_term(port, terms, tlen);

  for (list<void *>::iterator iter = pl.begin() ; iter != pl.end(); ++iter) {
    driver_free(*iter);
  }
  driver_free(terms);
};

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

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

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

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

static ErlDrvData start(ErlDrvPort port, char *cmd) {
  PortContext *data = new PortContext(port);
  return data->castOut(); 
}

static void stop(ErlDrvData edata) {
  PortContext *data = PortContext::castIn(edata);
  delete data;
}

static void outputv(ErlDrvData edata, ErlIOVec *ev) {
  //printf("[drv] outputv\n");
  ErlDrvBinary* input = ev->binv[1];

  PortContext *data = PortContext::castIn(edata);
  AsyncData *adata = new AsyncData(data->port, data->context, input->orig_bytes, input->orig_size);
  driver_async(data->port, NULL, run_script, adata, async_free);
}

static void ready_async(ErlDrvData edata, ErlDrvThreadData tdata) {
  //printf("[drv] ready_async\n");
  AsyncData *adata = AsyncData::castIn(tdata);
  adata->send();
  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);
  //String::AsciiValue script_str(script);
  //printf("[drv] run_script \"%s\"\n", *script_str);

  Handle<Script> compiled = Script::Compile(script);
  if (compiled.IsEmpty()) {
    adata->setResult(ERROR, try_catch.Message()->Get());
  } else {
    Handle<Value> value = compiled->Run();
    if (value.IsEmpty()) {
      adata->setResult(ERROR, try_catch.Message()->Get());
    } else {
      adata->setResult(OK, value);
    }
  }
}

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