Commits

Anonymous committed 19bfdce

v8erl can run scripts now

Comments (0)

Files changed (3)

 clean:    
 	rm -rf src/*.o js/*.jsc $(LIB_DIR) $(EBIN_DIR)
 
-test:
-	erl -pa ebin -run v8 load
+js/%.jsc: js/%.js
+	js -s -C $<
+	touch $@
 
 .PHONY: all clean erl localdirs test
 
-js/%.jsc: js/%.js
-	js -s -C $<
-	touch $@
+test: all
+	erl -pa ebin -noshell -run v8 test -run init stop
 -export([load/0,
          context/0,
          script/2]).
+-export([test/0]).
 -define(LIB, "v8erl").
+-define(DRV_RUN_SCRIPT, $R).
 
 load() ->
     case whereis(v8erl) of
                 {error, already_loaded} ->
                     ok;
                 {error, Err} ->
-		    {error, erl_ddll:format_error(Err)}
+                    {error, erl_ddll:format_error(Err)}
             end;
         _ ->
             ok
     end.
 
+context() ->
+    open_port({spawn, ?LIB}, [binary]).
 
-context() ->
-    load(),
-    open_port({spawn, ?LIB}, []).
+destroy(Context) when is_port(Context) ->
+    port_close(Context).
 
+script(Context, Script) when is_port(Context) ->
+    true = erlang:port_connect(Context, self()),
+    true = erlang:port_command(Context, Script),
+    receive
+	Result ->
+	    Result
+    after
+	3000 ->
+	    {error, timeout}
+    end.
 
-script(Context, Script) when is_port(Context) andalso 
-                             is_binary(Script) ->
-    Context ! Script,
-    receive
-	{Context, {data, Data}} ->
-	    Data;
-	{Context, closed} ->
-	    closed
-    end.
+test() ->
+    io:format("load v8: ~w~n", [load()]),
+    Context = context(),
+    case script(Context, <<"var f = function() { return true; }; g();">>) of
+	{ok, Result} ->
+	    io:format("script value: ~w~n", [Result]);
+	{error, Error} ->
+	    io:format("script error: ~s~n", [Error])
+    end,
+    destroy(Context),
+    ok.
+    
 #include <string>
 #include "js2erl.h"
 
-static Persistent<ObjectTemplate> global_template;
-static Persistent<ObjectTemplate> term_template;
+#define DRV_RUN_SCRIPT 'R'
 
-static int init();
-static void finish();
+// driver symbols
 static ErlDrvData start(ErlDrvPort, char *);
 static void stop(ErlDrvData);
-static void output(ErlDrvData, char *, int);
+static void outputv(ErlDrvData, ErlIOVec *);
 static void ready_async(ErlDrvData, ErlDrvThreadData);
+
 // local
 static void run_script(void *);
 static void async_free(void *);
 
-static char COMPILER_ERROR[] = "compiler_error";
+static char ERROR[] = "error";
+static char COMPILER_ERROR[] = "compilation failed";
 static char DRIVER_NAME[] = "v8erl";
 
-class V8Data {
-public:
+struct PortContext {
   ErlDrvPort port;
   Persistent<Context> context;
 
-  V8Data(ErlDrvPort, Handle<Context>);
-  ~V8Data();
+  void init(ErlDrvPort);
+  void dispose();
   ErlDrvData castOut();
-  static V8Data *castIn(ErlDrvData);
+  unsigned int *key();
+  static PortContext *castIn(void *);
 };
 
-V8Data::V8Data(ErlDrvPort p, Handle<Context> c) {
+void PortContext::init(ErlDrvPort p) {
+  port = p;
+
   HandleScope handle_scope;
-  port = p;
-  context = Persistent<Context>::New(c);
+  Handle<ObjectTemplate> global = ObjectTemplate::New();
+  context = Context::New(NULL, global);
 };
 
-V8Data::~V8Data() {
+void PortContext::dispose() {
   context.Dispose();
 };
 
-ErlDrvData V8Data::castOut() { 
+ErlDrvData PortContext::castOut() { 
   return reinterpret_cast<ErlDrvData>(this); 
 };
 
-V8Data *V8Data::castIn(ErlDrvData data) {
-  return reinterpret_cast<V8Data *>(data);
+unsigned int *PortContext::key() {
+  return reinterpret_cast<unsigned int *>(&port);
 };
 
-class AsyncData {
-public:
+PortContext *PortContext::castIn(void *data) {
+  return reinterpret_cast<PortContext *>(data);
+};
+
+struct AsyncData {
   // input
-  Handle<Context> context;
+  ErlDrvPort port;
+  Persistent<Context> context;
   char *buf;
   int len;
   // output
   ErlDrvTermData *terms;
   int tlen;
 
-  AsyncData(Handle<Context>, char *, int);
+  void init(ErlDrvPort, Persistent<Context>, char *, int);
   void setResult(Handle<Value>);
-  void send(ErlDrvPort);
+  void send();
   void setError(char *);
-  ~AsyncData();
+  void dispose();
   void *castOut();
   static AsyncData *castIn(void *);
 };
 
-AsyncData::AsyncData(Handle<Context> c, char *b, int l) {
+void AsyncData::init(ErlDrvPort p, Persistent<Context> c, char *b, int l) {
+  port = p;
   context = c;
   buf = b;
   len = l;
 };
 
-AsyncData::~AsyncData() {
+void AsyncData::dispose() {
   driver_free(terms);
 };
 
 void AsyncData::setResult(Handle<Value> result) {
   tlen = terms_length(result);
-  terms = (ErlDrvTermData *)driver_alloc(sizeof(ErlDrvTermData) * tlen);
+  terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
   value_to_terms(result, terms);
 };
 
-void AsyncData::send(ErlDrvPort port) {
+void AsyncData::send() {
   driver_output_term(port, terms, tlen);
 };
 
 void AsyncData::setError(char *msg) {
-  tlen = 2;
-  terms = (ErlDrvTermData *)driver_alloc(sizeof(ErlDrvTermData) * tlen);
+  tlen = 7;
+  terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
   terms[0] = ERL_DRV_ATOM;
-  terms[1] = driver_mk_atom(msg);
+  terms[1] = driver_mk_atom(ERROR);
+  terms[2] = ERL_DRV_STRING;
+  terms[3] = reinterpret_cast<ErlDrvTermData>(msg);
+  terms[4] = strlen(msg);
+  terms[5] = ERL_DRV_TUPLE;
+  terms[6] = 2;
 };
 
 void *AsyncData::castOut() {
 };
 
 static ErlDrvEntry driver_entry = {
-  init, // init driver
+  NULL, // init driver
   start, // open_port
   stop, // close_port
-  output, // output		
+  NULL, // output		
   NULL, // ready_input
   NULL, // ready_output
   DRIVER_NAME, // driver name
-  finish, // unload dynamic driver
+  NULL, // unload dynamic driver
   NULL, // handle: EMULATOR USE ONLY
   NULL, // control
   NULL, // timeout
-  NULL, // outputv
+  outputv, // outputv
   ready_async, // async event done
   NULL, // flush
   NULL, // call
   return &driver_entry;
 }
 
-Persistent<ObjectTemplate> erl_global() {
-  HandleScope handle_scope;
-  Handle<ObjectTemplate> global = ObjectTemplate::New(); 
-  return Persistent<ObjectTemplate>::New(global);
-}
-
-Persistent<ObjectTemplate> erl_term_template() {
-  HandleScope handle_scope;
-  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 {
-    HandleScope handle_scope;
-    Handle<Context> context = Context::New(NULL, global_template);
-    V8Data *data = new V8Data(port, context);
-    return data->castOut(); 
-  }
+  PortContext *data = PortContext::castIn(driver_alloc(sizeof(PortContext)));
+  data->init(port);
+  return data->castOut(); 
 }
 
 static void stop(ErlDrvData edata) {
-  delete V8Data::castIn(edata);
+  PortContext *data = PortContext::castIn(edata);
+  data->dispose();
+  driver_free(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 outputv(ErlDrvData edata, ErlIOVec *ev) {
+  ErlDrvBinary* input = ev->binv[1];
+
+  PortContext *data = PortContext::castIn(edata);
+  AsyncData *adata = AsyncData::castIn(driver_alloc(sizeof(AsyncData)));
+  adata->init(data->port, data->context, input->orig_bytes, input->orig_size);
+  driver_async(data->port, data->key(), 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;
+  adata->send();
+  adata->dispose();
+  driver_free(adata);
 }
 
 static void run_script(void *tdata) {
   Handle<Script> compiled = Script::Compile(script);
   if (compiled.IsEmpty()) {
     adata->setError(COMPILER_ERROR); 
-  } else { 
-    adata->setResult(compiled->Run()); 
+  } else {
+    Handle<Value> value = compiled->Run();
+    if (value.IsEmpty()) {
+      Handle<Value> exception = try_catch.Exception();
+      String::AsciiValue exception_str(exception);
+      adata->setError(*exception_str);
+    } else {
+      adata->setResult(value); 
+    }
   }
 }
 
 static void async_free(void *tdata) {
-  delete AsyncData::castIn(tdata);
+  AsyncData *adata = AsyncData::castIn(tdata);
+  adata->dispose();
+  driver_free(adata);
 }