PageRenderTime 73ms CodeModel.GetById 13ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/c_src/erlv8_term.cc

http://github.com/beamjs/erlv8
C++ | 427 lines | 375 code | 41 blank | 11 comment | 116 complexity | 873062d2dd38b311fec9c56fa863e6ff MD5 | raw file
  1#include "erlv8.hh"
  2
  3int enif_is_proplist(ErlNifEnv * env, ERL_NIF_TERM term)
  4{
  5  ERL_NIF_TERM head, tail;
  6  ERL_NIF_TERM current = term;
  7  int arity;
  8  ERL_NIF_TERM *array;
  9  ErlNifBinary string_binary;
 10
 11  if (!enif_is_list(env,term)) {
 12        return 0;
 13  }
 14  while (enif_get_list_cell(env, current, &head, &tail)) {
 15        if (!enif_is_tuple(env,head)) return 0; // not a tuple -> not a proplist
 16        enif_get_tuple(env,head,&arity,(const ERL_NIF_TERM **)&array);
 17        if (arity != 2) return 0; // does not consist of two elements -> not a proplist
 18        if (enif_is_list(env, array[0])) {
 19          unsigned len;
 20          enif_get_list_length(env, array[0], &len);
 21          char * str = (char *) malloc(len + 1);
 22          if (!enif_get_string(env, array[0], str, len + 1, ERL_NIF_LATIN1)) {
 23                free(str);
 24                return 0;
 25          }
 26          free(str);
 27        } else if (!enif_is_atom(env, array[0])) {
 28      if (enif_inspect_iolist_as_binary(env, array[0], &string_binary)) { // string
 29        return 1;
 30      }
 31          return 0;
 32        }
 33
 34        current = tail;
 35  }
 36  return 1;
 37}
 38
 39v8::PropertyAttribute term_to_property_attribute(ErlNifEnv * env, ERL_NIF_TERM term) {
 40  unsigned len;
 41  char name[MAX_ATOM_LEN];
 42
 43  if (enif_is_atom(env, term)) {
 44    v8::PropertyAttribute property_attribute;
 45    enif_get_atom_length(env, term, &len, ERL_NIF_LATIN1);
 46    enif_get_atom(env,term, (char *) &name,len + 1, ERL_NIF_LATIN1);
 47    if (!strcmp(name,"none")) {
 48      property_attribute = v8::None;
 49    } else if (!strcmp(name,"readonly")) {
 50      property_attribute = v8::ReadOnly;
 51    } else if (!strcmp(name,"dontenum")) {
 52      property_attribute = v8::DontEnum;
 53    } else if (!strcmp(name,"dontdelete")) {
 54      property_attribute = v8::DontDelete;
 55    } else { // always return something
 56      property_attribute = v8::None;
 57    }
 58    return property_attribute;
 59  } else if (enif_is_list(env, term)) {
 60        ERL_NIF_TERM current = term;
 61        ERL_NIF_TERM head, tail;
 62        v8::PropertyAttribute property_attribute = v8::None;
 63        while (enif_get_list_cell(env, current, &head, &tail)) {
 64          property_attribute = (v8::PropertyAttribute) (property_attribute | term_to_property_attribute(env,head));
 65          current = tail;
 66        }
 67        return property_attribute;
 68  } else {
 69        return v8::None;
 70  }
 71}
 72
 73void weak_external_cleaner(v8::Persistent<v8::Value> object, void * data) {
 74  if (object.IsNearDeath()) {
 75        term_ref_t * term_ref = (term_ref_t *) v8::External::Unwrap(v8::Handle<v8::External>::Cast(object));
 76        enif_free_env(term_ref->env);
 77        enif_free(term_ref);
 78    object.Dispose();
 79    object.Clear();
 80        v8::V8::AdjustAmountOfExternalAllocatedMemory(-(long)sizeof(term_ref_t));
 81  }
 82}
 83
 84inline v8::Handle<v8::Value> term_to_external(ERL_NIF_TERM term) {
 85  v8::HandleScope handle_scope;
 86  term_ref_t * term_ref = (term_ref_t *) enif_alloc(sizeof(term_ref_t));
 87  term_ref->env = enif_alloc_env();
 88  term_ref->term = enif_make_copy(term_ref->env, term);
 89  v8::Persistent<v8::External> obj = v8::Persistent<v8::External>::New(v8::External::New(term_ref));
 90  obj.MakeWeak(NULL,weak_external_cleaner);
 91  v8::V8::AdjustAmountOfExternalAllocatedMemory((long)sizeof(term_ref_t));
 92  return obj;
 93}
 94
 95inline ERL_NIF_TERM external_to_term(v8::Handle<v8::Value> val) {
 96        term_ref_t * term_ref = (term_ref_t *) v8::External::Unwrap(v8::Handle<v8::External>::Cast(val));
 97        return term_ref->term;
 98}
 99
100v8::Handle<v8::Object> externalize_term(map<ERL_NIF_TERM, v8::Handle<v8::Object>, cmp_erl_nif_term> cache, v8::Handle<v8::Object> proto, ERL_NIF_TERM term) {
101  v8::HandleScope handle_scope;
102  map<ERL_NIF_TERM, v8::Handle<v8::Object>, cmp_erl_nif_term>::iterator iter = cache.find(term);
103
104  if (iter != cache.end()) {
105    return handle_scope.Close(iter->second); // it was cached before
106  } else {
107    v8::Handle<v8::Value> external = term_to_external(term);
108    v8::Handle<v8::Object> obj = v8::Object::New();
109    obj->SetPrototype(proto);
110    obj->SetHiddenValue(v8::String::New("__erlv8__"), external);
111    cache.insert(std::pair<ERL_NIF_TERM, v8::Handle<v8::Object> >(term, obj)); // cache it
112    return handle_scope.Close(obj);
113  }
114
115}
116
117v8::Handle<v8::Value> term_to_js(v8::Handle<v8::Context> ctx,  v8::Isolate* isolate, ErlNifEnv *env, ERL_NIF_TERM term) {
118  TRACE("(%p) term_to_js - 1\n", isolate);
119  LHCS(isolate, ctx);
120  TRACE("(%p) term_to_js - 2\n", isolate);
121  int _int; unsigned int _uint; long _long; unsigned long _ulong; ErlNifSInt64 _int64; ErlNifUInt64 _uint64; double _double;
122  ErlNifBinary string_binary;
123  unsigned len;
124  char name[MAX_ATOM_LEN];
125
126  if (enif_is_atom(env, term)) {
127    TRACE("(%p) term_to_js - 3 a\n", isolate);
128    enif_get_atom_length(env, term, &len, ERL_NIF_LATIN1);
129    enif_get_atom(env, term, (char *) &name,len + 1, ERL_NIF_LATIN1);
130    v8::Handle<v8::Value> result;
131    // check for special atoms
132    if (strcmp(name,"false")==0) {
133      result = v8::Local<v8::Boolean>::New(v8::Boolean::New(0));
134    } else if (strcmp(name,"true")==0) {
135      result = v8::Local<v8::Boolean>::New(v8::Boolean::New(1));
136    } else if (strcmp(name,"ok")==0) {
137      result = v8::Local<v8::Boolean>::New(v8::Boolean::New(1));
138    } else if (strcmp(name,"undefined")==0) {
139      result = v8::Undefined();
140    } else if (strcmp(name,"null")==0) {
141      result = v8::Null();
142    } else { // if it is not a special atom, convert it to a string
143      result = v8::String::New(name);
144    }
145    return handle_scope.Close(result);
146  } else if (enif_get_int(env,term,&_int)) {
147    TRACE("(%p) term_to_js - 3 b\n", isolate);
148    return handle_scope.Close(v8::Local<v8::Integer>::New(v8::Integer::New(_int)));
149  } else if (enif_get_uint(env,term,&_uint)) {
150    TRACE("(%p) term_to_js - 3 c\n", isolate);
151    return handle_scope.Close(v8::Local<v8::Integer>::New(v8::Integer::NewFromUnsigned(_uint)));
152  } else if (enif_get_long(env,term,&_long)) {
153    TRACE("(%p) term_to_js - 3 d\n", isolate);
154    return handle_scope.Close(v8::Local<v8::Number>::New(v8::Number::New(_long)));
155  } else if (enif_get_ulong(env,term,&_ulong)) {
156    TRACE("(%p) term_to_js - 3 e\n", isolate);
157    return handle_scope.Close(v8::Local<v8::Number>::New(v8::Number::New(_ulong)));
158  } else if (enif_get_int64(env,term,&_int64)) {
159    TRACE("(%p) term_to_js - 3 f\n", isolate);
160    return handle_scope.Close(v8::Local<v8::Number>::New(v8::Number::New(_int64)));
161  } else if (enif_get_uint64(env,term,&_uint64)) {
162    TRACE("(%p) term_to_js - 3 g\n", isolate);
163    return handle_scope.Close(v8::Local<v8::Number>::New(v8::Number::New(_uint64)));
164  } else if (enif_get_double(env,term,&_double)) {
165    TRACE("(%p) term_to_js - 3 h\n", isolate);
166    return handle_scope.Close(v8::Local<v8::Number>::New(v8::Number::New(_double)));
167  } else if (enif_inspect_iolist_as_binary(env, term, &string_binary)) { // string
168    TRACE("(%p) term_to_js - 3 i\n", isolate);
169    v8::Local<v8::String> s = v8::String::New((const char *)string_binary.data, string_binary.size);
170    if (s->Utf8Length() != string_binary.size)
171      printf("%d != %lu\n", s->Utf8Length()-1, string_binary.size);
172    return handle_scope.Close(s);
173  } else if (enif_is_tuple(env, term)) {
174    TRACE("(%p) term_to_js - 3 j\n", isolate);
175    ERL_NIF_TERM *array;
176    int arity;
177    enif_get_tuple(env,term,&arity,(const ERL_NIF_TERM **)&array);
178    if (arity == 3) {
179      enif_get_atom_length(env, array[0], &len, ERL_NIF_LATIN1);
180      enif_get_atom(env,array[0], (char *) &name,len + 1, ERL_NIF_LATIN1);
181      val_res_t *res;
182      // check if it is a v8_fun
183      int isv8fun = strcmp(name,"erlv8_fun")==0;
184      // check if it is an object
185      int isobj = strcmp(name,"erlv8_object")==0;
186      // check if it is an array
187      int isarray = strcmp(name,"erlv8_array")==0;
188
189      if (isobj||isarray) {
190        if (enif_get_resource(env,array[1],val_resource,(void **)(&res))) {
191          return handle_scope.Close(res->val->ToObject());
192        } else if (isobj && enif_is_proplist(env,array[1])) {
193          v8::Local<v8::Object> obj = v8::Object::New();
194          ERL_NIF_TERM head, tail;
195          ERL_NIF_TERM current = array[1];
196          int arity;
197          ERL_NIF_TERM *arr;
198          while (enif_get_list_cell(env, current, &head, &tail)) {
199            enif_get_tuple(env,head,&arity,(const ERL_NIF_TERM **)&arr);
200            obj->Set(term_to_js(ctx, isolate, env,arr[0]),
201                     term_to_js(ctx, isolate, env,arr[1]));
202
203            current = tail;
204          }
205          return handle_scope.Close(obj);
206        } else if (isarray && enif_is_list(env, array[1])) {
207          unsigned int i,alen;
208          ERL_NIF_TERM head, tail;
209          ERL_NIF_TERM current = array[1];
210
211          enif_get_list_length(env, current, &alen);
212
213          v8::Local<v8::Array> arrobj = v8::Array::New(alen);
214
215          i = 0;
216          while (enif_get_list_cell(env, current, &head, &tail)) {
217            arrobj->Set(v8::Integer::New(i), term_to_js(ctx, isolate, env,head));
218            current = tail;
219            i++;
220          }
221          return handle_scope.Close(arrobj);
222        }
223      }
224
225      if ((isv8fun) &&
226          (enif_get_resource(env,array[1],val_resource,(void **)(&res)))){
227        return handle_scope.Close(res->val);
228      } else if ((isv8fun) && (enif_is_fun(env, array[1]))) {
229        v8::Handle<v8::Function> f = v8::Handle<v8::Function>::Cast(term_to_js(ctx, isolate, env, array[1]));
230        v8::Handle<v8::Object> o = v8::Handle<v8::Object>::Cast(term_to_js(ctx, isolate, env, array[2]));
231
232        v8::Local<v8::Array> keys = o->GetPropertyNames();
233
234        for (unsigned int i=0;i<keys->Length();i++) {
235          v8::Local<v8::Value> key = keys->Get(v8::Integer::New(i));
236          f->Set(key,o->Get(key));
237        }
238
239        return handle_scope.Close(f);
240
241      }
242
243    }
244
245    if (arity == 2) {
246      enif_get_atom_length(env, array[0], &len, ERL_NIF_LATIN1);
247      enif_get_atom(env,array[0], (char *) &name,len + 1, ERL_NIF_LATIN1);
248      // check if it is an error
249      int iserror = strcmp(name,"error")==0;
250      int isthrow = strcmp(name,"throw")==0;
251      if (iserror) {
252        return handle_scope.Close(v8::Exception::Error(v8::Handle<v8::String>::Cast(term_to_js(ctx, isolate, env,array[1]))));
253      }
254      if (isthrow) {
255        return v8::ThrowException(term_to_js(ctx, isolate, env, array[1]));
256      }
257    }
258
259  } else if (enif_is_fun(env, term)) {
260    TRACE("(%p) term_to_js - 3 k\n", isolate);
261    VM * vm = (VM *) v8::External::Unwrap(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__")));
262    map<ERL_NIF_TERM, v8::Handle<v8::FunctionTemplate>, cmp_erl_nif_term>::iterator iter = vm->fun_map.find(term);
263
264    if (iter != vm->fun_map.end()) {
265      return handle_scope.Close(iter->second->GetFunction()); // it was cached before
266    } else {
267      v8::Handle<v8::Value> external = term_to_external(term);
268      v8::Persistent<v8::FunctionTemplate> t = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(WrapFun,external));
269
270      v8::Local<v8::Function> f = t->GetFunction();
271      f->SetHiddenValue(vm->string__erlv8__, external);
272
273      vm->fun_map.insert(std::pair<ERL_NIF_TERM, v8::Handle<v8::FunctionTemplate> >(external_to_term(external), t)); // cache it
274      return handle_scope.Close(f);
275    }
276  } else if (enif_is_pid(env, term)) {
277    VM * vm = (VM *) v8::External::Unwrap(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__")));
278    return handle_scope.Close(externalize_term(vm->extern_map, vm->external_proto_pid, term));
279  } else if (enif_is_ref(env, term)) {
280    VM * vm = (VM *) v8::External::Unwrap(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__")));
281    return handle_scope.Close(externalize_term(vm->extern_map, vm->external_proto_ref, term));
282  }
283
284  return v8::Undefined(); // if nothing else works, return undefined
285};
286
287
288ERL_NIF_TERM js_to_term(v8::Handle<v8::Context> ctx,  v8::Isolate* isolate, ErlNifEnv *env, v8::Handle<v8::Value> val) {
289  TRACE("(%p) js_to_term - 1\n", isolate);
290  LHCS(isolate, ctx);
291  TRACE("(%p) js_to_term - 2\n", isolate);
292  if (val.IsEmpty()) {
293    TRACE("(%p) js_to_term - 3 a\n", isolate);
294    return enif_make_atom(env,"undefined");
295  } else if (val->IsFunction()) {  // the reason why this check is so high up here is because it is also an object, so it should be before any object.
296    TRACE("(%p) js_to_term - 3 b\n", isolate);
297    val_res_t *ptr;
298    TRACE("(%p) js_to_term - 4\n", isolate);
299    v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(val);
300    ERL_NIF_TERM resource_term;
301    TRACE("(%p) js_to_term - 5\n", isolate);
302    ptr = (val_res_t *)enif_alloc_resource(val_resource, sizeof(val_res_t));
303    TRACE("(%p) js_to_term - 6\n", isolate);
304    ptr->ctx = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
305    TRACE("(%p) js_to_term - 7\n", isolate);
306    ptr->val = v8::Persistent<v8::Function>::New(v8::Handle<v8::Function>::Cast(val));
307    TRACE("(%p) js_to_term - 8\n", isolate);
308    resource_term = enif_make_resource(env, ptr);
309    TRACE("(%p) js_to_term - 9\n", isolate);
310    enif_release_resource(ptr);
311    TRACE("(%p) js_to_term - 10\n", isolate);
312    VM * vm = (VM *) v8::External::Unwrap(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__")));
313    TRACE("(%p) js_to_term - 11\n", isolate);
314    ERL_NIF_TERM term = enif_make_tuple3(env,enif_make_atom(env,"erlv8_fun"),
315                                         resource_term,
316                                         enif_make_pid(env, vm->server));
317    TRACE("(%p) js_to_term - 11\n", isolate);
318
319    return term;
320  } else if	(val->IsUndefined()) {
321    TRACE("(%p) js_to_term - 3 c\n", isolate);
322    return enif_make_atom(env,"undefined");
323  } else if (val->IsNull()) {
324    TRACE("(%p) js_to_term - 3 d\n", isolate);
325    return enif_make_atom(env,"null");
326  } else if (val->IsTrue()) {
327    TRACE("(%p) js_to_term - 3 c\n", isolate);
328    return enif_make_atom(env,"true");
329  } else if (val->IsFalse()) {
330    TRACE("(%p) js_to_term - 3 d\n", isolate);
331    return enif_make_atom(env,"false");
332  } else if (val->IsString()) {
333    TRACE("(%p) js_to_term - 3 e\n", isolate);
334    ErlNifBinary result_binary = {0};
335    {
336      /*      v8::Local<v8::String> S =  val->ToString();
337              v8::String::Utf8Value  V = v8::String::Utf8Value(val->ToString());*/
338      /*      enif_alloc_binary(S->Utf8Length(), &result_binary);
339              S->WriteUtf8((char *) result_binary.data, result_binary.size);*/
340    }
341    /*    enif_alloc_binary(v8::String::Utf8Value(val->ToString()).length(), &result_binary);
342          (void)memcpy(result_binary.data, *v8::String::Utf8Value(val->ToString()), result_binary.size);*/
343
344    enif_alloc_binary(v8::String::Utf8Value(val).length(), &result_binary);
345    (void)memcpy(result_binary.data, *v8::String::Utf8Value(val), result_binary.size);
346
347
348    return enif_make_binary(env, &result_binary);
349  } else if (val->IsInt32()) {
350    TRACE("(%p) js_to_term - 3 f\n", isolate);
351        return enif_make_long(env,val->ToInt32()->Value());
352  } else if (val->IsUint32()) {
353    TRACE("(%p) js_to_term - 3 g\n", isolate);
354        return enif_make_int64(env,val->ToUint32()->Value());
355  } else if (val->IsNumber()) {
356    TRACE("(%p) js_to_term - 3 h\n", isolate);
357    double d = val->ToNumber()->Value();
358    if (d == round(d)) {
359      return enif_make_int64(env,d);
360    } else {
361      return enif_make_double(env,d);
362    }
363  } else if (val->IsArray()) {
364    TRACE("(%p) js_to_term - 3 i\n", isolate);
365    val_res_t *ptr;
366    v8::Handle<v8::Array> arr = v8::Handle<v8::Array>::Cast(val);
367    ERL_NIF_TERM resource_term;
368    ptr = (val_res_t *)enif_alloc_resource(val_resource, sizeof(val_res_t));
369    ptr->val = v8::Persistent<v8::Array>::New(v8::Handle<v8::Array>::Cast(val));
370    ptr->ctx = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
371    resource_term = enif_make_resource(env, ptr);
372    enif_release_resource(ptr);
373
374    VM * vm = (VM *) v8::External::Unwrap(v8::Context::GetCurrent()->Global()->GetHiddenValue(v8::String::New("__erlv8__")));
375    ERL_NIF_TERM term = enif_make_tuple3(env,
376                                         enif_make_atom(env, "erlv8_array"),
377                                         resource_term,
378                                         enif_make_pid(env, vm->server)
379                                         );
380
381    return term;
382  } else if (val->IsObject()) {
383    TRACE("(%p) js_to_term - 3 j\n", isolate);
384    v8::Local<v8::Object> obj = val->ToObject();
385    TRACE("(%p) js_to_term - 4\n", isolate);
386    v8::Local<v8::Context> c = v8::Context::GetCurrent();
387    TRACE("(%p) js_to_term - 5\n", isolate);
388    v8::Local<v8::Object> g = c->Global();
389    TRACE("(%p) js_to_term - 6\n", isolate);
390    v8::Local<v8::Value> v = g->GetHiddenValue(v8::String::New("__erlv8__"));
391    TRACE("(%p) js_to_term - 7\n", isolate);
392    VM * vm = (VM *) v8::External::Unwrap(v);
393    TRACE("(%p) js_to_term - 8\n", isolate);
394    if (obj->GetPrototype()->Equals(vm->external_proto_num) ||
395        obj->GetPrototype()->Equals(vm->external_proto_atom) ||
396        obj->GetPrototype()->Equals(vm->external_proto_bin) ||
397        obj->GetPrototype()->Equals(vm->external_proto_ref) ||
398        obj->GetPrototype()->Equals(vm->external_proto_fun) ||
399        obj->GetPrototype()->Equals(vm->external_proto_port) ||
400        obj->GetPrototype()->Equals(vm->external_proto_pid) ||
401        obj->GetPrototype()->Equals(vm->external_proto_tuple) ||
402        obj->GetPrototype()->Equals(vm->external_proto_list)) {
403      TRACE("(%p) js_to_term - 7\n", isolate);
404      return enif_make_copy(env, external_to_term(v8::Handle<v8::External>::Cast(obj->GetHiddenValue(vm->string__erlv8__))));
405    } else {
406      TRACE("(%p) js_to_term - 3 k\n", isolate);
407      ERL_NIF_TERM resource_term;
408
409      val_res_t *ptr;
410      ptr = (val_res_t *)enif_alloc_resource(val_resource, sizeof(val_res_t));
411      ptr->val = v8::Persistent<v8::Object>::New(v8::Handle<v8::Object>::Cast(val));
412      ptr->ctx = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
413      resource_term = enif_make_resource(env, ptr);
414      enif_release_resource(ptr);
415
416      ERL_NIF_TERM term = enif_make_tuple3(env,
417                                           enif_make_atom(env, "erlv8_object"),
418                                           resource_term,
419                                           enif_make_pid(env, vm->server)
420                                           );
421
422      return term;
423    }
424  } else {
425    return enif_make_atom(env,"$unknown");
426  }
427};