Commits

Anonymous committed 018a039

handle memory leaks in serialization routine. needs serious cleanup and rethinking.

Comments (0)

Files changed (4)

     end.
 
 print_script_result(Context, Script) ->
+    io:format("[erl] script: ~s~n", [Script]),
     case script(Context, Script) of
 	{ok, Result} ->
-	    io:format("[erl] script value: ~w~n", [Result]);
+	    io:format("[erl] result: ~w~n", [Result]);
 	{error, Error} ->
-	    io:format("[erl] script error: ~s~n", [Error])
+	    io:format("[erl] error: ~s~n", [Error])
     end.
 
 test() ->
     print_script_result(Context, <<"g();">>),
     print_script_result(Context, <<"var g = f; null;">>),
     print_script_result(Context, <<"g();">>),
+    print_script_result(Context, <<"[1, 2, 3];">>),
+    print_script_result(Context, <<"var o = { 'foo': true, 'bar': false }; o;">>),
     destroy(Context),
     ok.
     
 using namespace std;
 using namespace v8;
 
+class PointerList {
+public:
+  PointerList();
+  void **pointers;
+  int allocated;
+  int length;
+  void append(void *);
+  ~PointerList();
+};
+
 extern int terms_length(Handle<Value> val);
-extern int value_to_terms(Handle<Value> val, ErlDrvTermData *terms);
+extern int value_to_terms(Handle<Value> val, ErlDrvTermData *terms, PointerList *pl);
 static void run_script(void *);
 static void async_free(void *);
 
-static char OK[] = "ok";
-static char ERROR[] = "error";
-static char COMPILER_ERROR[] = "compilation failed";
+static const char OK[] = "ok";
+static const char ERROR[] = "error";
 static char DRIVER_NAME[] = "v8erl";
 
-struct PortContext {
+class PortContext {
+public:
   ErlDrvPort port;
   Persistent<Context> context;
 
-  void init(ErlDrvPort);
-  void dispose();
+  PortContext(ErlDrvPort);
+  ~PortContext();
   ErlDrvData castOut();
   static PortContext *castIn(void *);
 };
 
-void PortContext::init(ErlDrvPort p) {
+PortContext::PortContext(ErlDrvPort p) {
   port = p;
 
   HandleScope handle_scope;
   context = Context::New(NULL, global);
 };
 
-void PortContext::dispose() {
+PortContext::~PortContext() {
   context.Dispose();
 };
 
   return reinterpret_cast<PortContext *>(data);
 };
 
-struct AsyncData {
+class AsyncData {
+public:
   // input
   ErlDrvPort port;
   Persistent<Context> context;
   char *buf;
   int len;
   // output
-  ErlDrvTermData *terms;
-  int tlen;
+  const char *type;
+  Persistent<Value> result;
 
-  void init(ErlDrvPort, Persistent<Context>, char *, int);
-  void setResult(Handle<Value>);
+  AsyncData(ErlDrvPort, Persistent<Context>, char *, int);
+  void setResult(const char *, Handle<Value>);
   void send();
-  void setError(char *, int);
-  void dispose();
+  ~AsyncData();
   void *castOut();
   static AsyncData *castIn(void *);
 };
 
-void AsyncData::init(ErlDrvPort p, Persistent<Context> c, char *b, int l) {
+AsyncData::AsyncData(ErlDrvPort p, Persistent<Context> c, char *b, int l) {
   port = p;
   context = c;
   buf = b;
   len = l;
 };
 
-void AsyncData::dispose() {
-  driver_free(terms);
+AsyncData::~AsyncData() {
+  result.Dispose();
 };
 
-void AsyncData::setResult(Handle<Value> result) {
-  tlen = terms_length(result) + 4;
-  terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
-  terms[0] = ERL_DRV_ATOM;
-  terms[1] = driver_mk_atom(OK);
-  value_to_terms(result, terms + 2);
-  terms[tlen - 2] = ERL_DRV_TUPLE;
-  terms[tlen - 1] = 2;
+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));
+
+  PointerList pl;
+  value_to_terms(result, terms + 2, &pl);
+
+  terms[tlen - 2] = ERL_DRV_TUPLE;
+  terms[tlen - 1] = 2;
+
   driver_output_term(port, terms, tlen);
-};
-
-void AsyncData::setError(char *msg, int len) {
-  tlen = 7;
-  terms = reinterpret_cast<ErlDrvTermData *>(driver_alloc(sizeof(ErlDrvTermData) * tlen));
-  terms[0] = ERL_DRV_ATOM;
-  terms[1] = driver_mk_atom(ERROR);
-  terms[2] = ERL_DRV_STRING;
-  terms[3] = reinterpret_cast<ErlDrvTermData>(msg);
-  terms[4] = len;
-  terms[5] = ERL_DRV_TUPLE;
-  terms[6] = 2;
+  driver_free(terms);
 };
 
 void *AsyncData::castOut() {
 }
 
 static ErlDrvData start(ErlDrvPort port, char *cmd) {
-  PortContext *data = PortContext::castIn(driver_alloc(sizeof(PortContext)));
-  data->init(port);
+  PortContext *data = new PortContext(port);
   return data->castOut(); 
 }
 
 static void stop(ErlDrvData edata) {
   PortContext *data = PortContext::castIn(edata);
-  data->dispose();
-  driver_free(edata);
+  delete data;
 }
 
 static void outputv(ErlDrvData edata, ErlIOVec *ev) {
-  printf("[drv] outputv\n");
+  //printf("[drv] outputv\n");
   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);
+  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");
+  //printf("[drv] ready_async\n");
   AsyncData *adata = AsyncData::castIn(tdata);
   adata->send();
-  adata->dispose();
-  driver_free(adata);
+  delete adata;
 }
 
 static void run_script(void *tdata) {
   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);
+  //String::AsciiValue script_str(script);
+  //printf("[drv] run_script \"%s\"\n", *script_str);
 
   Handle<Script> compiled = Script::Compile(script);
   if (compiled.IsEmpty()) {
-    adata->setError(COMPILER_ERROR, sizeof(COMPILER_ERROR)); 
+    adata->setResult(ERROR, try_catch.Message()->Get());
   } else {
     Handle<Value> value = compiled->Run();
     if (value.IsEmpty()) {
-      Handle<Value> exception = try_catch.Exception();
-      String::AsciiValue exception_str(exception);
-      adata->setError(*exception_str, exception_str.length());
+      adata->setResult(ERROR, try_catch.Message()->Get());
     } else {
-      adata->setResult(value); 
+      adata->setResult(OK, value);
     }
   }
 }
 
 static void async_free(void *tdata) {
   AsyncData *adata = AsyncData::castIn(tdata);
-  adata->dispose();
-  driver_free(adata);
+  delete adata;
 }
 #include "js2erl.h"
+#include <string.h>
+
+PointerList::PointerList() {
+  pointers = NULL;
+  allocated = 0;
+  length = 0;
+};
+
+void PointerList::append(void *pointer) {
+  if ((length + 1) > allocated) {
+    if (allocated == 0) {
+      allocated = 2;
+      pointers = reinterpret_cast<void **>(driver_alloc(sizeof(void *) * allocated));
+    } else {
+      allocated *= 2;
+      driver_realloc(pointers, sizeof(void *) * allocated);
+    }
+  }
+  pointers[length] = pointer;
+  length++;
+};
+
+PointerList::~PointerList() {
+  for (int i = 0; i < length; ++i) {
+    printf("[v2t] freeing pointer list length = %d\n", length);
+    driver_free(pointers[i]);
+  }
+};
 
 // internal
-static int date_to_terms(Handle<Date> val, ErlDrvTermData *terms);
-static int object_to_terms(Handle<Object> obj, ErlDrvTermData *terms);
-static int array_to_terms(Handle<Array> arr, ErlDrvTermData *terms);
-static int number_to_terms(Handle<Number> num, ErlDrvTermData *terms);
-static int string_to_terms(Handle<String> str, ErlDrvTermData *terms);
-
+static int date_to_terms(Handle<Date> val, ErlDrvTermData *terms, PointerList *pl);
+static int object_to_terms(Handle<Object> obj, ErlDrvTermData *terms, PointerList *pl);
+static int array_to_terms(Handle<Array> arr, ErlDrvTermData *terms, PointerList *pl);
+static int number_to_terms(Handle<Number> num, ErlDrvTermData *terms, PointerList *pl);
+static int string_to_terms(Handle<String> str, ErlDrvTermData *terms, PointerList *pl);
 
 static int atom(const char *a, ErlDrvTermData *terms) {
   if (terms != NULL) {
 // public
 
 int terms_length(Handle<Value> val) {
-  return value_to_terms(val, NULL);
+  return value_to_terms(val, NULL, NULL);
 }
 
-int value_to_terms(Handle<Value> val, ErlDrvTermData *terms) {
-  HandleScope scope;
+int value_to_terms(Handle<Value> val, ErlDrvTermData *terms, PointerList *pl) {
   if (val->IsNull()) {
-    printf("[v2t] is null\n");
+    //printf("[v2t] is null\n");
     return atom("null", terms);
+  } else if (val->IsString()) {
+    //printf("[v2t] is String\n");
+    return string_to_terms(Handle<String>::Cast(val), terms, pl);
+  } else if (val->IsNumber()) {
+    //printf("[v2t] is Number\n");
+    return number_to_terms(Handle<Number>::Cast(val), terms, pl);
+  } else if (val->IsBoolean()) {
+    //printf("[v2t] is Boolean\n");
+    return atom(val->IsTrue() ? "true" : "false", terms);
   } else if (val->IsObject()) {
-    printf("[v2t] is Object\n");
-    Handle<Object> obj = Handle<Object>::Cast(val);
+    //printf("[v2t] is Object\n");
     if (val->IsArray()) {
-      printf("[v2t] is Array\n");
-      return array_to_terms(Handle<Array>::Cast(obj), terms);
+      //printf("[v2t] is Array\n");
+      return array_to_terms(Handle<Array>::Cast(val), terms, pl);
     } else if (val->IsDate()) {
-      printf("[v2t] is Date\n");
-      return date_to_terms(Handle<Date>::Cast(obj), terms);
+      //printf("[v2t] is Date\n");
+      return date_to_terms(Handle<Date>::Cast(val), terms, pl);
     } else {
-      return object_to_terms(obj, terms);
+      return object_to_terms(Handle<Object>::Cast(val), terms, pl);
     }
-  } else if (val->IsString()) {
-    printf("[v2t] is String\n");
-    return string_to_terms(Handle<String>::Cast(val), terms);
-  } else if (val->IsNumber()) {
-    printf("[v2t] is Number\n");
-    return number_to_terms(Handle<Number>::Cast(val), terms);
-  } else if (val->IsBoolean()) {
-    printf("[v2t] is Boolean\n");
-    return atom(val->IsTrue() ? "true" : "false", terms);
   } else { // functions, etc.
-    printf("[v2t] is unknown\n");
+    //printf("[v2t] is unknown\n");
     return atom("undefined", terms);
   }
 }
 
 // private
 
-int array_to_terms(Handle<Array> arr, ErlDrvTermData *terms) {
+int array_to_terms(Handle<Array> arr, ErlDrvTermData *terms, PointerList *pl) {
   int tlen = 0;
 
   int alen = arr->Length();
   for (int i=0; i<alen; ++i) {
-    tlen += value_to_terms(arr->Get(Integer::New(i)), terms+tlen);
+    tlen += value_to_terms(arr->Get(Integer::New(i)), terms != NULL ? (terms + tlen) : NULL, pl);
   }
 
   if (terms != NULL) {
     terms[tlen] = ERL_DRV_LIST;
-    terms[tlen+1] = alen;
+    terms[tlen + 1] = alen;
   }
   tlen += 2;
 
 }
 
 
-int object_to_terms(Handle<Object> obj, ErlDrvTermData *terms) {
+int object_to_terms(Handle<Object> obj, ErlDrvTermData *terms, PointerList *pl) {
+  printf("[v2t] object_to_terms\n");
   int tlen = 0;
 
-  tlen += atom("struct", terms+tlen);
-
+  tlen += atom("struct", terms != NULL ? (terms + tlen) : NULL);
+  
   Handle<Array> keys = obj->GetPropertyNames();
   int olen = keys->Length();
   for (int i=0; i<olen; ++i) {
     Handle<Value> key = keys->Get(Integer::New(i));
-    tlen += string_to_terms(Handle<String>::Cast(key), terms+tlen);
-    tlen += value_to_terms(obj->Get(key), terms+tlen);
+    tlen += string_to_terms(Handle<String>::Cast(key), terms != NULL ? (terms + tlen) : NULL, pl);
+    tlen += value_to_terms(obj->Get(key), terms != NULL ? (terms + tlen) : NULL, pl);
     
     if (terms != NULL) {
-      terms[tlen]   = ERL_DRV_TUPLE;
-      terms[tlen+1] = 2;
+      terms[tlen] = ERL_DRV_TUPLE;
+      terms[tlen + 1] = 2;
     }
     tlen += 2;
   }
 
   if (terms != NULL) {
     terms[tlen] = ERL_DRV_LIST;
-    terms[tlen+1] = olen;
-    terms[tlen+2] = ERL_DRV_TUPLE;
-    terms[tlen+3] = 2;
+    terms[tlen + 1] = olen;
+    terms[tlen + 2] = ERL_DRV_TUPLE;
+    terms[tlen + 3] = 2;
   }
   tlen += 4;
 
   return tlen;
 }
 
-
-int number_to_terms(Handle<Number> num, ErlDrvTermData *terms) {
+int number_to_terms(Handle<Number> num, ErlDrvTermData *terms, PointerList *pl) {
   if (terms != NULL) {
     if (num->IsInt32()) {
       terms[0] = ERL_DRV_INT;
       terms[1] = num->Int32Value();
     } else {
-      double f = num->Value();
+      double *f = reinterpret_cast<double *>(driver_alloc(sizeof(double))); // XXX memory leak
+      *f = num->Value();
+      pl->append(f);
       terms[0] = ERL_DRV_FLOAT;
-      terms[1] = (ErlDrvTermData)&f;
+      terms[1] = reinterpret_cast<ErlDrvTermData>(f);
     }
   }
   return 2;
 }
 
-
-int string_to_terms(Handle<String> str, ErlDrvTermData *terms) {
+int string_to_terms(Handle<String> str, ErlDrvTermData *terms, PointerList *pl) {
   if (terms != NULL) {
     String::Utf8Value utf8(str);
+    unsigned int len = utf8.length();
+    char *drv_str = reinterpret_cast<char *>(driver_alloc(len)); // XXX memory leak
+    memcpy(drv_str, *utf8, len);
+    pl->append(drv_str);
     terms[0] = ERL_DRV_BUF2BINARY;
-    terms[1] = (ErlDrvTermData)*utf8;
-    terms[2] = utf8.length();
+    terms[1] = reinterpret_cast<ErlDrvTermData>(drv_str);
+    terms[2] = len;
   }
   return 3;
 }
 
-
-int date_to_terms(Handle<Date> date, ErlDrvTermData *terms) {
+int date_to_terms(Handle<Date> date, ErlDrvTermData *terms, PointerList *pl) {
   if (terms != NULL) {
-    double f = date->NumberValue();
+    double *f = reinterpret_cast<double *>(driver_alloc(sizeof(double))); // XXX memory leak
+    *f = date->NumberValue();
+    pl->append(f);
     terms[0] = ERL_DRV_FLOAT;
-    terms[1] = (ErlDrvTermData)&f;
+    terms[1] = reinterpret_cast<ErlDrvTermData>(f);
   }
   return 2;
 }