Empirical
js_utils.h
Go to the documentation of this file.
1 
11 #ifndef EMP_JS_UTILS_H
12 #define EMP_JS_UTILS_H
13 
14 #include <map>
15 #include <string>
16 #include <typeinfo>
17 
18 #include "../base/assert.h"
19 #include "../base/vector.h"
20 #include "../base/array.h"
21 
22 #include "init.h"
23 
24 
25 namespace emp {
26 
40 
41  std::map<std::string, std::string> get_type_to_string_map() {
42  // Using typeid().name() could create problems because it varies by
43  // implementation. All that matters is consistency, but obscure could
44  // technically be given the same name. So far it seems to not be an issue
45  // with Emscripten, which is most critical for this code.
46  std::map<std::string, std::string> map_type_names;
47  map_type_names[typeid(int8_t).name()] = "i8";
48  map_type_names[typeid(int16_t).name()] = "i16";
49  map_type_names[typeid(int32_t).name()] = "i32";
50  map_type_names[typeid(int64_t).name()] = "i64";
51  map_type_names[typeid(float).name()] = "float";
52  map_type_names[typeid(double).name()] = "double";
53  map_type_names[typeid(int8_t*).name()] = "i8*";
54  map_type_names[typeid(int16_t*).name()] = "i16*";
55  map_type_names[typeid(int32_t*).name()] = "i32*";
56  map_type_names[typeid(int64_t*).name()] = "i64*";
57  map_type_names[typeid(float*).name()] = "float*";
58  map_type_names[typeid(double*).name()] = "double*";
59  map_type_names[typeid(void*).name()] = "*";
60  map_type_names[typeid(std::string).name()] = "string";
61 
62  return map_type_names;
63  }
64 
72 
74 
75  // This code now works for all containers, as long as they store data contiguously
76 
77  template<typename C, class = typename C::value_type>
78  typename std::enable_if<std::is_pod<typename C::value_type>::value, void>::type
79  pass_array_to_javascript(C values, emp::vector<int> recursive_el)
80  {
81  using T = typename C::value_type;
82  //Figure out what string to use for the type we've been given
83  std::map<std::string, std::string> map_type_names = get_type_to_string_map();
84  emp_assert((map_type_names.find(typeid(T).name()) != map_type_names.end()));
85  int type_size = sizeof(T);
86  (void) type_size;
87  std::string type_string = map_type_names[typeid(T).name()];
88 
89  // Clear array, if this isn't a recursive call
90  if (recursive_el.size() == 0) {
91  EM_ASM({emp_i.__incoming_array = [];});
92  }
93 
94  EM_ASM_ARGS({
95  var curr_array = emp_i.__incoming_array;
96  var depth = 0;
97 
98  // Make sure that we're at the right depth, in case of recursive call.
99  while (curr_array.length > 0) {
100  var next_index = getValue($4+(depth*4), "i32");
101  depth += 1;
102  curr_array = curr_array[next_index];
103  }
104 
105  // Iterate over array, get values, and add them to incoming array.
106  for (i=0; i<$1; i++) {
107  curr_array.push(getValue($0+(i*$2), Pointer_stringify($3)));
108  }
109  }, &values[0], values.size(), type_size, type_string.c_str(), recursive_el.data());
110  }
111 
112  // Specialization for strings
113  template<typename C, class = typename C::value_type>
114  typename std::enable_if<std::is_same<typename C::value_type, std::string>::value, void>::type
115  pass_array_to_javascript(C values, emp::vector<int> recursive_el)
116  {
117  // Clear array, if this isn't a recursive call
118  if (recursive_el.size() == 0) {
119  EM_ASM({emp_i.__incoming_array = [];});
120  };
121 
122  EM_ASM_ARGS({
123  emp_i.__curr_array = emp_i.__incoming_array;
124  var depth = 0;
125 
126  // Make sure that we are at the right depth, in case of recursive call.
127  while (emp_i.__curr_array.length > 0) {
128  var next_index = getValue($0+(depth*4), "i32");
129  depth += 1;
130  emp_i.__curr_array = emp_i.__curr_array[next_index];
131  };
132  }, recursive_el.data());
133 
134  // Iterate over array, get values, and add them to incoming array.
135  for (auto val : values) {
136  (void) val;
137  EM_ASM_ARGS({
138  emp_i.__curr_array.push(Pointer_stringify($0));
139  }, val.c_str());
140  };
141 
142  EM_ASM({delete emp_i.__curr_array;});
143  }
144 
145  // Handle user-defined JSON_TYPE
146  template<typename C, class = typename C::value_type>
147  typename std::enable_if<C::value_type::n_fields != -1, void>::type
148  pass_array_to_javascript(C values, emp::vector<int> recursive_el) {
149 
150  std::map<std::string, std::string> map_type_names = get_type_to_string_map();
151  // Initialize array in Javascript
152  if (recursive_el.size() == 0) {
153  EM_ASM({emp_i.__incoming_array = [];});
154  }
155 
156  // Initialize objects in Javascript
157  EM_ASM_ARGS({
158  var curr_array = emp_i.__incoming_array;
159  var depth = 0;
160 
161  // Make sure that we're at the right depth, in case of recursive call.
162  while (curr_array.length > 0) {
163  var next_index = getValue($1+(depth*4), "i32");
164  depth += 1;
165  curr_array = curr_array[next_index];
166  }
167 
168  // Append empty objects
169  for (i=0; i<$0; i++) {
170  var new_obj = {};
171  curr_array.push(new_obj);
172  }
173  }, values.size(), recursive_el.data());
174 
175  for (std::size_t j = 0; j<values.size(); j++) { // Iterate over array
176  for (std::size_t i = 0; i<values[j].var_names.size(); i++) { // Iterate over object members
177 
178  // Get variable name and type for this member variable
179  std::string var_name = values[j].var_names[i];
180  std::string type_string = map_type_names[values[j].var_types[i].name()];
181  // Make sure member variable is an allowed type
182  emp_assert((map_type_names.find(values[j].var_types[i].name())
183  != map_type_names.end()), values[j].var_types[i].name());
184 
185  // Load data into array of objects
186  EM_ASM_ARGS({
187  var curr_array = emp_i.__incoming_array;
188  var depth = 0;
189 
190  // Make sure we are at the right depth, in case of recursive call.
191  while (curr_array[0].length > 0) {
192  var next_index = getValue($4+(depth*4), "i32");
193  depth += 1;
194  curr_array = curr_array[next_index];
195  }
196 
197  if (Pointer_stringify($1) == "string") {
198  curr_array[$3][Pointer_stringify($2)] = Pointer_stringify($0);
199  } else {
200  curr_array[$3][Pointer_stringify($2)] = getValue($0, Pointer_stringify($1));
201  }
202  }, values[j].pointers[i], type_string.c_str(), var_name.c_str(),
203  j, recursive_el.data());
204  }
205  }
206  }
207 
209 
210  // This version of the function handles non-nested containers
211  template<typename C, class = typename C::value_type>
212  void pass_array_to_javascript(C values) {
214  }
215 
217 
218  // This version of the function handles nested arrays with recursive calls
219  // until a non-array type is found.
220  template<std::size_t SIZE1, std::size_t SIZE2, typename T>
222  emp::vector<int> recursive_el = emp::vector<int>()) {
223 
224  // Initialize if this is the first call to this function
225  if (recursive_el.size() == 0) {
226  EM_ASM({emp_i.__incoming_array = [];});
227  }
228 
229  // Append empty arrays to array that we are currently handling in recursion
230  EM_ASM_ARGS({
231  var curr_array = emp_i.__incoming_array;
232  var depth = 0;
233  while (curr_array.length > 0) {
234  var next_index = getValue($0+(depth*4), "i32");
235  depth += 1;
236  curr_array = curr_array[next_index];
237  }
238  for (i=0; i<$1; i++) {
239  curr_array.push([]);
240  }
241  }, recursive_el.data(), values.size());
242 
243  // Make recursive calls - recursive_els specifies coordinates of array we're
244  // currently operating on
245  for (std::size_t i = 0; i<values.size(); i++) {
246  emp::vector<int> new_recursive_el (recursive_el);
247  new_recursive_el.push_back((int) i);
248  pass_array_to_javascript(values[i], new_recursive_el);
249  }
250  }
251 
252  // This version of the function handles nested vectors with recursive calls
253  // until a non-array type is found.
254  template<typename T>
256  emp::vector<int> recursive_el = emp::vector<int>()) {
257 
258  // Initialize if this is the first call to this function
259  if (recursive_el.size() == 0) {
260  EM_ASM({emp_i.__incoming_array = [];});
261  }
262 
263  // Append empty arrays to array that we are currently handling in recursion
264  EM_ASM_ARGS({
265  var curr_array = emp_i.__incoming_array;
266  var depth = 0;
267  while (curr_array.length > 0) {
268  var next_index = getValue($0+(depth*4), "i32");
269  depth += 1;
270  curr_array = curr_array[next_index];
271  }
272  for (i=0; i<$1; i++) {
273  curr_array.push([]);
274  }
275  }, recursive_el.data(), values.size());
276 
277  // Make recursive calls - recursive_els specifies coordinates of array we are
278  // currently operating on
279  for (std::size_t i = 0; i<values.size(); i++) {
280  emp::vector<int> new_recursive_el (recursive_el);
281  new_recursive_el.push_back((int) i);
282  pass_array_to_javascript(values[i], new_recursive_el);
283  }
284  }
285 
287 
295  //
296  // Don't worry about the recurse argument - it's for handling nested arrays
297  // internally
298  template <std::size_t SIZE, typename T>
299  void pass_array_to_cpp(emp::array<T, SIZE> & arr, bool recurse = false) {
300 
301  //Figure out type stuff
302  std::map<std::string, std::string> map_type_names = get_type_to_string_map();
303  emp_assert((map_type_names.find(typeid(T).name()) != map_type_names.end()), typeid(T).name());
304  int type_size = sizeof(T);
305  (void) type_size;
306  std::string type_string = map_type_names[typeid(T).name()];
307 
308  //Make sure arrays have the same length
309  emp_assert(arr.size() == EM_ASM_INT_V({return emp_i.__outgoing_array.length}),
310  arr.size(), EM_ASM_INT_V({return emp_i.__outgoing_array.length}));
311 
312  //Write emp.__outgoing_array contents to a buffer
313  T * buffer = (T*) EM_ASM_INT({
314  var buffer = Module._malloc(emp_i.__outgoing_array.length*$0);
315 
316  for (i=0; i<emp_i.__outgoing_array.length; i++) {
317  setValue(buffer+(i*$0), emp_i.__outgoing_array[i], Pointer_stringify($1));
318  }
319 
320  return buffer;
321  }, type_size, type_string.c_str());
322 
323  // Populate array from buffer
324  for (std::size_t i=0; i<arr.size(); i++) {
325  arr[i] = *(buffer + i);
326  }
327 
328  // Free the memory we allocated in Javascript
329  free(buffer);
330  }
331 
333  template <typename T>
334  void pass_vector_to_cpp(emp::vector<T> & arr, bool recurse = false) {
335 
336  // Figure out type stuff
337  std::map<std::string, std::string> map_type_names = get_type_to_string_map();
338  emp_assert((map_type_names.find(typeid(T).name()) != map_type_names.end()), typeid(T).name());
339  int type_size = sizeof(T);
340  (void) type_size;
341  std::string type_string = map_type_names[typeid(T).name()];
342 
343  // Write emp.__outgoing_array contents to a buffer
344  T * buffer = (T*) EM_ASM_INT({
345  var buffer = Module._malloc(emp_i.__outgoing_array.length*$0);
346 
347  for (i=0; i<emp_i.__outgoing_array.length; i++) {
348  setValue(buffer+(i*$0), emp_i.__outgoing_array[i], Pointer_stringify($1));
349  }
350 
351  return buffer;
352  }, type_size, type_string.c_str());
353 
354  // Populate array from buffer
355  for (int i=0; i < EM_ASM_INT_V({return emp_i.__outgoing_array.length}); i++) {
356  arr.push_back(*(buffer + i));
357  }
358 
359  //Free the memory we allocated in Javascript
360  free(buffer);
361  }
362 
364 
365 
366  // template <typename T>
367  // typename std::enable_if<C::value_type::n_fields != -1, void>::type
368  // pass_vector_to_cpp(emp::vector<T> & arr, bool recurse = false) {
369  //
370  // // Figure out type stuff
371  // std::map<std::string, std::string> map_type_names = get_type_to_string_map();
372  // emp_assert((map_type_names.find(typeid(T).name()) != map_type_names.end()), typeid(T).name());
373  // int type_size = sizeof(T);
374  // (void) type_size;
375  // std::string type_string = map_type_names[typeid(T).name()];
376  //
377  // // Write emp.__outgoing_array contents to a buffer
378  // T * buffer = (T*) EM_ASM_INT({
379  // var buffer = Module._malloc(emp_i.__outgoing_array.length*$0);
380  //
381  // for (i=0; i<emp_i.__outgoing_array.length; i++) {
382  // setValue(buffer+(i*$0), emp_i.__outgoing_array[i], Pointer_stringify($1));
383  // }
384  //
385  // return buffer;
386  // }, type_size, type_string.c_str());
387  //
388  // // Populate array from buffer
389  // for (int i=0; i < EM_ASM_INT_V({return emp_i.__outgoing_array.length}); i++) {
390  // arr.push_back(*(buffer + i));
391  // }
392  //
393  // //Free the memory we allocated in Javascript
394  // free(buffer);
395  // }
396 
397  // Chars aren't one of the types supported by setValue, but by treating them
398  // as strings in Javascript we can pass them out to a C++ array
399  template <std::size_t SIZE>
400  void pass_array_to_cpp(emp::array<char, SIZE> & arr, bool recurse = false) {
401 
402  emp_assert(arr.size() == EM_ASM_INT_V({return emp_i.__outgoing_array.length}));
403 
404  char * buffer = (char *) EM_ASM_INT_V({
405  // Since we're treating each char as it's own string, each one
406  // will be null-termianted. So we malloc length*2 addresses.
407  var buffer = Module._malloc(emp_i.__outgoing_array.length*2);
408 
409  for (i=0; i<emp_i.__outgoing_array.length; i++) {
410  writeStringToMemory(emp_i.__outgoing_array[i], buffer+(i*2));
411  }
412 
413  return buffer;
414  });
415 
416  for (size_t i=0; i<arr.size(); i++) {
417  arr[i] = *(buffer + i*2);
418  }
419 
420  free(buffer);
421  }
422 
423 
424  // Chars aren't one of the types supported by setValue, but by treating them
425  // as strings in Javascript we can pass them out to a C++ array
426  void pass_vector_to_cpp(emp::vector<char> & arr, bool recurse = false) {
427 
428  char * buffer = (char *) EM_ASM_INT_V({
429  // Since we're treating each char as it's own string, each one
430  // will be null-termianted. So we malloc length*2 addresses.
431  var buffer = Module._malloc(emp_i.__outgoing_array.length*2);
432 
433  for (i=0; i<emp_i.__outgoing_array.length; i++) {
434  writeStringToMemory(emp_i.__outgoing_array[i], buffer+(i*2));
435  }
436 
437  return buffer;
438  });
439 
440  for (int i=0; i<EM_ASM_INT_V({return emp_i.__outgoing_array.length}); i++) {
441  arr.push_back(*(buffer + i*2));
442  }
443 
444  free(buffer);
445  }
446 
447  // We can handle strings in a similar way
448  template <std::size_t SIZE>
449  void pass_array_to_cpp(emp::array<std::string, SIZE> & arr, bool recurse = false) {
450 
451  emp_assert(arr.size() == EM_ASM_INT_V({return emp_i.__outgoing_array.length}));
452 
453  char * buffer = (char *) EM_ASM_INT_V({
454  // Figure how much memory to allocate
455  var arr_size = 0;
456  for (i=0; i<emp_i.__outgoing_array.length; i++) {
457  arr_size += emp_i.__outgoing_array[i].length + 1;
458  }
459 
460  var buffer = Module._malloc(arr_size);
461 
462  // Track place in memory to write too
463  var cumulative_size = 0;
464  for (i=0; i<emp_i.__outgoing_array.length; i++) {
465  writeStringToMemory(emp_i.__outgoing_array[i], buffer + (cumulative_size));
466  cumulative_size += emp_i.__outgoing_array[i].length + 1;
467  }
468 
469  return buffer;
470  });
471 
472  // Track place in memory to read from
473  int cumulative_size = 0;
474  for (size_t i=0; i<arr.size(); i++) {
475  // Since std::string constructor reads to null terminator, this just works.
476  arr[i] = std::string(buffer + cumulative_size);
477  cumulative_size += arr[i].size() + 1;
478  }
479 
480  free(buffer);
481  }
482 
483  // We can handle strings in a similar way
484  void pass_vector_to_cpp(emp::vector<std::string> & arr, bool recurse = false) {
485 
486  char * buffer = (char *) EM_ASM_INT_V({
487  // Figure how much memory to allocate
488  var arr_size = 0;
489  for (i=0; i<emp_i.__outgoing_array.length; i++) {
490  arr_size += emp_i.__outgoing_array[i].length + 1;
491  }
492 
493  var buffer = Module._malloc(arr_size);
494 
495  // Track place in memory to write too
496  var cumulative_size = 0;
497  for (i=0; i<emp_i.__outgoing_array.length; i++) {
498  writeStringToMemory(emp_i.__outgoing_array[i], buffer + (cumulative_size));
499  cumulative_size += emp_i.__outgoing_array[i].length + 1;
500  }
501 
502  return buffer;
503  });
504 
505  // Track place in memory to read from
506  int cumulative_size = 0;
507  for (int i=0; i<EM_ASM_INT_V({return emp_i.__outgoing_array.length}); i++) {
508  // Since std::string constructor reads to null terminator, this just works.
509  arr.push_back(std::string(buffer + cumulative_size));
510  cumulative_size += arr[(size_t)i].size() + 1;
511  }
512 
513  free(buffer);
514  }
515 
516  // We can handle nested arrays through recursive calls on chunks of them
517  template <std::size_t SIZE, std::size_t SIZE2, typename T>
518  void pass_array_to_cpp(emp::array<emp::array<T, SIZE2>, SIZE> & arr, bool recurse = false) {
519 
520  emp_assert(arr.size() == EM_ASM_INT_V({return emp_i.__outgoing_array.length}));
521 
522  // Create temp array to hold whole array while pieces are passed in
523  if (recurse == 0) {
524  EM_ASM({emp_i.__temp_array = [emp_i.__outgoing_array];});
525  } else {
526  // This is a little wasteful of space, but the alternatives are
527  // surprisingly ugly
528  EM_ASM({emp_i.__temp_array.push(emp_i.__outgoing_array);});
529  }
530 
531  for (size_t i = 0; i < arr.size(); i++) {
532  EM_ASM_ARGS({
533  emp_i.__outgoing_array = emp_i.__temp_array[emp_i.__temp_array.length - 1][$0];
534  }, i);
535  pass_array_to_cpp(arr[i], true);
536  }
537 
538  // Clear temp array
539  if (recurse == 0) { EM_ASM({emp_i.__temp_array = [];}); }
540  else { EM_ASM({emp_i.__temp_array.pop();}); }
541  }
542 
544  template <typename T>
545  void pass_vector_to_cpp(emp::vector<emp::vector<T> > & arr, bool recurse = false) {
546 
547  // Create temp array to hold whole array while pieces are passed in
548  int size = EM_ASM_INT_V({return emp_i.__outgoing_array.length});
549 
550  if (recurse == 0) {
551  EM_ASM({
552  emp_i.__temp_array = [emp_i.__outgoing_array];
553  console.log(emp_i.__outgoing_array);
554  });
555  } else {
556  // This is a little wasteful of space, but the alternatives are
557  // surprisingly ugly
558  EM_ASM({emp_i.__temp_array.push(emp_i.__outgoing_array);});
559  }
560 
561  for (int i = 0; i < size; i++) {
562  EM_ASM_ARGS({
563  emp_i.__outgoing_array = emp_i.__temp_array[emp_i.__temp_array.length - 1][$0];
564  }, i);
565  while ((int)arr.size() <= i) {
566  arr.push_back(emp::vector<T>());
567  }
568  pass_vector_to_cpp(arr[i], true);
569  }
570 
571  // Clear temp array
572  if (recurse == 0) {
573  EM_ASM({emp_i.__temp_array = [];});
574  } else {
575  EM_ASM({emp_i.__temp_array.pop();});
576  }
577  }
578 
580 
581 }
582 
583 
584 #endif
Definition: array.h:42
Define Initialize() and other functions to set up Empirical to build Emscripten projects.
void push_back(PB_Ts &&...args)
Definition: vector.h:189
size_t size() const
Definition: vector.h:151
void pass_array_to_cpp(emp::array< T, SIZE > &arr, bool recurse=false)
Definition: js_utils.h:299
void pass_vector_to_cpp(emp::vector< T > &arr, bool recurse=false)
Same as pass_array_to_cpp, but lets you store values in a vector instead.
Definition: js_utils.h:334
std::map< std::string, std::string > get_type_to_string_map()
Definition: js_utils.h:41
constexpr size_t size() const
Definition: array.h:137
If we are in emscripten, make sure to include the header.
Definition: array.h:37
#define emp_assert(...)
Definition: assert.h:199
void pass_array_to_javascript(C values)
Definition: js_utils.h:212