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