1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
3 * Description: Definition of source file for generating bytecode.
4 * Create: 2020/09/07
5 */
6
7#ifdef JERRY_FOR_IAR_CONFIG
8
9#include "generate-bytecode.h"
10
11#include <string.h>
12
13#include "config-gt.h"
14#include "config-jupiter.h"
15
16#define VERSION_LEN 30
17#define ONETIME_MAX_OPTBYTES 4096 // max size for reading and writing at onetime
18
19// jerry version code
20char version_str[VERSION_LEN];
21
22// secure functions
23extern int memset_s(void *dest, size_t destMax, int c, size_t count);
24extern int strcpy_s(char *strDest, size_t destMax, const char *strSrc);
25extern int strcat_s(char *strDest, size_t destMax, const char *strSrc);
26extern int strncat_s(char *strDest, size_t destMax, const char *strSrc, size_t count);
27extern int sprintf_s(char *strDest, size_t destMax, const char *format, ...);
28extern int strncpy_s(char *strDest, size_t destMax, const char *strSrc, size_t count);
29
30#ifdef JERRY_IAR_JUPITER
31extern uint8_t* input_buffer;
32extern uint8_t* snapshot_buffer;
33#endif // JERRY_IAR_JUPITER
34
35/**
36 * jerry snapshot format version
37 */
38char* get_jerry_version_no() {
39  if (sprintf_s(version_str, sizeof(version_str),
40      "JERRY_SNAPSHOT_VERSION_%u", JERRY_SNAPSHOT_VERSION) < 0) {
41    return NULL;
42  }
43  return version_str;
44} /* get_jerry_version_no */
45
46/**
47 * splice path and filename
48 */
49char* splice_path(char* str1, char* str2) {
50  int len1 = strlen(str1);
51  int len2 = strlen(str2);
52  int res_len = len1 + len2 + 1; // str1 + "/" + str2
53
54  char* res = (char*)OhosMalloc(MEM_TYPE_JERRY, (res_len + 1) * sizeof(char));
55  if (res == NULL) {
56    return NULL;
57  }
58  if (memset_s(res, res_len + 1, 0, res_len + 1) != 0) {
59    OhosFree(res);
60    res = NULL;
61    return NULL;
62  }
63  if (strcpy_s(res, len1 + 1, str1) != 0) {
64    OhosFree(res);
65    res = NULL;
66    return NULL;
67  }
68  if ((strcat_s(res, len1 + strlen("/") + 1, "/") != 0)
69       || (strcat_s(res, res_len + 1, str2) != 0)) {
70    OhosFree(res);
71    res = NULL;
72    return NULL;
73  }
74  return res;
75} /* splice_path */
76
77/**
78 * judge if is template(js/bc) file
79 */
80bool is_template_file(char* filename, char* template) {
81  const char* pFile;
82  pFile = strrchr(filename, '.');
83  if ((pFile != NULL) && (strcmp(pFile, template) == 0)) {
84    return true;
85  }
86  return false;
87} /* is_template_file */
88
89/**
90 * get output snapshot file absolutely path
91 */
92char* get_output_file_path(char* input_file_path) {
93  int len = strlen(input_file_path);
94  char* output_file_path = (char*)OhosMalloc(MEM_TYPE_JERRY, (len + 1) * sizeof(char));
95  if (output_file_path == NULL) {
96    return NULL;
97  }
98  if (memset_s(output_file_path, len + 1, 0, len + 1) != 0) {
99    OhosFree(output_file_path);
100    output_file_path = NULL;
101    return NULL;
102  }
103  if (strncpy_s(output_file_path, len, input_file_path, len - strlen(".js")) != 0) {
104    OhosFree(output_file_path);
105    output_file_path = NULL;
106    return NULL;
107  }
108  output_file_path[len-3] = '.';
109  output_file_path[len-2] = 'b';
110  output_file_path[len-1] = 'c';
111  output_file_path[len] = '\0';
112  return output_file_path;
113} /* get_output_file_path */
114
115/**
116 * read js bundle file or snapshot file by Fragement
117 */
118EXECRES read_js_or_snapshot_file(char* filename, uint8_t* target_file, int* file_bytesize, int buffer_capacity) {
119  int fd = 0;
120  struct stat file_stat = { 0 };
121  int remain_to_read = 0;
122  int here_to_read = 0;
123  int tmp_read = 0;
124  int read_offset = 0;
125  fd = open(filename, O_RDONLY, S_IREAD);
126  if (fd < 0) {
127    // Error: failed to open file
128    return EXCE_ACE_JERRY_OPEN_FILE_FAILED;
129  }
130  if (fstat(fd, &file_stat) < 0) {
131    close(fd);
132    return EXCE_ACE_JERRY_GET_FILE_STAT_ERROR;
133  }
134  *file_bytesize = file_stat.st_size;
135  if (*file_bytesize > buffer_capacity) {
136    close(fd);
137    return EXCE_ACE_JERRY_FILE_TOO_LARGE;
138  }
139  remain_to_read = *file_bytesize;
140  while (remain_to_read > 0) {
141    here_to_read = (remain_to_read > ONETIME_MAX_OPTBYTES) ?
142                    ONETIME_MAX_OPTBYTES : remain_to_read;
143    tmp_read = read(fd, target_file + read_offset, here_to_read);
144    if (tmp_read < 0 || tmp_read != here_to_read) {
145      close(fd);
146      // Error: failed to read file
147      return EXCE_ACE_JERRY_READ_FILE_FAILED;
148    }
149    read_offset = read_offset + here_to_read;
150    remain_to_read = remain_to_read - here_to_read;
151  }
152  if (read_offset != *file_bytesize) {
153    close(fd);
154    // Error: failed to successfully read file
155    return EXCE_ACE_JERRY_READ_FILE_FAILED;
156  }
157  close(fd);
158  return EXCE_ACE_JERRY_EXEC_OK;
159} /* read_js_or_snapshot_file */
160
161/**
162 * write snapshot file by Fragment
163 */
164EXECRES write_snapshot(char* output_file_name_path, size_t snapshot_size) {
165  int fd = 0;
166  int res = 0;
167  int remain_to_write = 0;
168  int here_to_write = 0;
169  int write_offset = 0;
170
171  fd = open(output_file_name_path, O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
172  if (fd < 0) {
173    // Error: Unable to open snapshot file
174    return EXCE_ACE_JERRY_OPEN_FILE_FAILED;
175  }
176  remain_to_write = snapshot_size;
177  while (remain_to_write > 0) {
178    here_to_write = (remain_to_write > ONETIME_MAX_OPTBYTES) ?
179                    ONETIME_MAX_OPTBYTES : remain_to_write;
180    res = write(fd, snapshot_buffer + write_offset, here_to_write);
181    if (res <= 0 || res != here_to_write) {
182      close(fd);
183      // Error: Unable to write snapshot file
184      return EXCE_ACE_JERRY_WRITE_SNAPSHOT_FILE_FAILED;
185    }
186    write_offset = write_offset + here_to_write;
187    remain_to_write = remain_to_write - here_to_write;
188  }
189  if (write_offset != snapshot_size) {
190    close(fd);
191    // Error: Unable to successfully write snapshot file
192    return EXCE_ACE_JERRY_WRITE_SNAPSHOT_FILE_FAILED;
193  }
194  close(fd);
195  return EXCE_ACE_JERRY_EXEC_OK;
196} /* write_snapshot */
197
198/**
199 * struct for Directory Node
200 */
201typedef struct Node {
202  char* dir_name;
203  struct Node* next;
204} dir_node;
205
206/**
207 * free the memory for linkedlist
208 */
209void free_link(dir_node* head) {
210  dir_node* tmp = NULL;
211  while (head != NULL) {
212    tmp = head;
213    head = head->next;
214    if (tmp->dir_name != NULL) {
215      OhosFree(tmp->dir_name);
216      tmp->dir_name = NULL;
217    }
218    OhosFree(tmp);
219    tmp = NULL;
220  }
221} /* free_link */
222
223/**
224 * generate snapshot file
225 */
226EXECRES generate_snapshot_file(char* input_file, char* output_file) {
227  uint8_t* target_Js = input_buffer;
228  jerry_value_t generate_result;
229  size_t snapshot_size = 0;
230  EXECRES write_res = EXCE_ACE_JERRY_EXEC_OK;
231  bool convert_state = false;
232  int file_bytesize = 0;
233  EXECRES read_res = EXCE_ACE_JERRY_EXEC_OK;
234
235  if (input_file == NULL || output_file == NULL) {
236    return EXCE_ACE_JERRY_NULL_PATH;
237  }
238  read_res = read_js_or_snapshot_file(input_file, target_Js, &file_bytesize, INPUTJS_BUFFER_SIZE);
239  if (read_res != EXCE_ACE_JERRY_EXEC_OK) {
240    return read_res;
241  }
242
243  generate_result = jerry_generate_snapshot (
244    NULL,
245    0,
246    target_Js,
247    file_bytesize,
248    0,
249    (uint32_t* )snapshot_buffer,
250    SNAPSHOT_BUFFER_SIZE);
251
252  convert_state = jerry_value_is_error(generate_result)
253                  || !jerry_value_is_number(generate_result);
254  if (convert_state) {
255    // Error: Generating snapshot failed
256    jerry_release_value(generate_result);
257    return EXCE_ACE_JERRY_GENERATE_SNAPSHOT_FAILED;
258  }
259  snapshot_size = (size_t)jerry_get_number_value(generate_result);
260  jerry_release_value(generate_result);
261
262  write_res = write_snapshot(output_file, snapshot_size);
263  if (write_res != EXCE_ACE_JERRY_EXEC_OK) {
264    // Error: Writing snapshot file failed
265    return write_res;
266  }
267  return EXCE_ACE_JERRY_EXEC_OK;
268}/* generate_snapshot_file */
269
270/**
271 * init the linked list for files in filefolder.
272 */
273EXECRES init_directory_list(char* filefolder, char* start_folder, dir_node **head, dir_node **end) {
274  struct stat file_stat = { 0 };
275  int filefolder_len = strlen(filefolder) + 1;
276  if ((filefolder == NULL) || (stat(filefolder, &file_stat) < 0)) {
277    return EXCE_ACE_JERRY_INPUT_PATH_ERROR;
278  }
279  if ((start_folder = (char*)OhosMalloc(MEM_TYPE_JERRY, filefolder_len)) == NULL) {
280    return EXCE_ACE_JERRY_MALLOC_ERROR;
281  }
282  if (strcpy_s(start_folder, filefolder_len, filefolder) != 0) {
283    OhosFree(start_folder);
284    start_folder = NULL;
285    return EXCE_ACE_JERRY_INPUT_PATH_ERROR;
286  }
287  if ((*head = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
288    OhosFree(start_folder);
289    start_folder = NULL;
290    return EXCE_ACE_JERRY_LINKLIST_ERROR;
291  }
292  if ((*end = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
293    OhosFree(start_folder);
294    start_folder = NULL;
295    OhosFree(*head);
296    *head = NULL;
297    return EXCE_ACE_JERRY_LINKLIST_ERROR;
298  }
299  (*head)->dir_name = NULL;
300  (*head)->next = *end;
301  (*end)->dir_name = start_folder;
302  (*end)->next = NULL;
303  return EXCE_ACE_JERRY_EXEC_OK;
304} /* init_directory_list */
305
306/**
307 * when visited node is a directory, add it to the node list's end.
308 */
309EXECRES add_directory_to_pending_list(dir_node **end, char* input_file_path) {
310  dir_node *new_node = NULL;
311  if ((new_node = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
312    OhosFree(input_file_path);
313    input_file_path = NULL;
314    return EXCE_ACE_JERRY_LINKLIST_ERROR;
315  }
316  // input_file_path for dir will be freed when that node is freed
317  new_node->dir_name = input_file_path;
318  new_node->next = NULL;
319  (*end)->next = new_node;
320  *end = new_node;
321  new_node = NULL;
322  return EXCE_ACE_JERRY_EXEC_OK;
323} /* add_directory_to_pending_list */
324
325/**
326 * transform this js file into snapshot.
327 */
328EXECRES gen_snapshot(char* input_file_path, char* output_file_path) {
329  RefreshAllServiceTimeStamp();
330  jerry_init_flag_t flags = JERRY_INIT_EMPTY;
331  jerry_init (flags);
332  EXECRES generate_val = EXCE_ACE_JERRY_EXEC_OK;
333  generate_val = generate_snapshot_file(input_file_path, output_file_path);
334  jerry_cleanup();
335  OhosFree(output_file_path);
336  output_file_path = NULL;
337  OhosFree(input_file_path);
338  input_file_path = NULL;
339  if (generate_val != EXCE_ACE_JERRY_EXEC_OK) {
340    return generate_val; // return error_code
341  }
342  return EXCE_ACE_JERRY_EXEC_OK;
343} /* gen_snapshot */
344
345/**
346 * validate snapshot's version.
347 */
348EXECRES validate_snapshot(char* input_file_path, char* output_file_path) {
349  uint8_t* snapshot_data_p = snapshot_buffer;
350  int file_bytesize = 0;
351  OhosFree(input_file_path);
352  input_file_path = NULL;
353
354  EXECRES read_res = read_js_or_snapshot_file(output_file_path, snapshot_data_p, &file_bytesize, SNAPSHOT_BUFFER_SIZE);
355  if ((read_res != EXCE_ACE_JERRY_EXEC_OK) || (snapshot_data_p == NULL) || (file_bytesize == 0)) {
356    OhosFree(output_file_path);
357    output_file_path = NULL;
358    return EXCE_ACE_JERRY_READ_FILE_FAILED;
359  }
360  const jerry_snapshot_header_t* header_p = (const jerry_snapshot_header_t*) snapshot_data_p;
361  if (header_p->version != JERRY_SNAPSHOT_VERSION) {
362    OhosFree(output_file_path);
363    output_file_path = NULL;
364    return EXCE_ACE_JERRY_SNAPSHOT_VERSION_ERROR;
365  }
366  OhosFree(output_file_path);
367  output_file_path = NULL;
368  return EXCE_ACE_JERRY_EXEC_OK;
369} /* validate_snapshot */
370
371/**
372 * when visited file is a js file, transform it into snapshot or check its snapshot's version.
373 */
374EXECRES visit_js_file(char* input_file_path, EXECRES (*call_back)(char*, char*)) {
375  char* output_file_path = NULL;
376  if ((output_file_path = get_output_file_path(input_file_path)) == NULL) {
377    OhosFree(input_file_path);
378    input_file_path = NULL;
379    return EXCE_ACE_JERRY_SPLICE_OUTPUT_PATH_ERROR;
380  }
381  EXECRES res = call_back(input_file_path, output_file_path);
382  return res;
383} /* visit_js_file */
384
385/**
386 * visit one single list node.
387 */
388EXECRES visit_directory(dir_node **end, char* current_path, EXECRES (*call_back)(char*, char*)) {
389  struct dirent* direntp = NULL;
390  struct stat file_stat = { 0 };
391  char* filename = NULL;
392  char* input_file_path = NULL;
393  DIR* dir = NULL;
394  if ((dir = (DIR*)opendir(current_path)) == NULL) {
395    return EXCE_ACE_JERRY_OPEN_DIR_FAILED;
396  }
397  while ((direntp = (struct dirent*)readdir(dir)) != NULL) {
398    filename = direntp->d_name;
399    if (strncmp(filename, ".", 1) == 0) {
400      continue;
401    }
402    if ((input_file_path = splice_path(current_path, filename)) == NULL) {
403      closedir(dir);
404      return EXCE_ACE_JERRY_SPLICE_PATH_ERROR;
405    }
406    if (stat(input_file_path, &file_stat) < 0) {
407      OhosFree(input_file_path);
408      input_file_path = NULL;
409      closedir(dir);
410      return EXCE_ACE_JERRY_GET_FILE_STAT_ERROR;
411    }
412    if (file_stat.st_mode & S_IFDIR) {
413      EXECRES add_directory_res = add_directory_to_pending_list(end, input_file_path);
414      if (add_directory_res != EXCE_ACE_JERRY_EXEC_OK) {
415        closedir(dir);
416        return add_directory_res;
417      }
418    } else if (is_template_file(filename, ".js")) {
419      EXECRES visit_js_res = visit_js_file(input_file_path, call_back);
420      if (visit_js_res != EXCE_ACE_JERRY_EXEC_OK) {
421        closedir(dir);
422        return visit_js_res;
423      }
424    } else {
425      OhosFree(input_file_path);
426      input_file_path = NULL;
427    }
428  }
429  closedir(dir);
430  return EXCE_ACE_JERRY_EXEC_OK;
431} /* visit_directory */
432
433/**
434 * visit directory from head list node.
435 */
436EXECRES visit_pending_directories(dir_node **head, dir_node **end, EXECRES (*call_back)(char*, char*)) {
437  dir_node* curr = NULL;
438  char* current_path = NULL;
439  while ((*head)->next != NULL) {
440    curr = (*head)->next;
441    current_path = curr->dir_name;
442    EXECRES visit_res = visit_directory(end, current_path, call_back);
443    if (visit_res != EXCE_ACE_JERRY_EXEC_OK) {
444      free_link(*head);
445      curr = NULL;
446      *end = NULL;
447      return visit_res;
448    }
449    (*head)->next = curr->next;
450    OhosFree(curr->dir_name);
451    curr->dir_name = NULL;
452    OhosFree(curr);
453    curr = NULL;
454  }
455  OhosFree(*head);
456  head = NULL;
457  end = NULL;
458  return EXCE_ACE_JERRY_EXEC_OK;
459} /* visit_pending_directories */
460
461/**
462 * visit directory and do js to snapshot conversion
463 */
464EXECRES generate_snapshots(char* filefolder) {
465  char* start_folder = NULL;
466  dir_node* head = NULL;
467  dir_node* end = NULL;
468
469  EXECRES init_res = init_directory_list(filefolder, start_folder, &head, &end);
470  if (init_res != EXCE_ACE_JERRY_EXEC_OK) {
471    return init_res;
472  }
473  EXECRES visit_res = visit_pending_directories(&head, &end, gen_snapshot);
474  if (visit_res != EXCE_ACE_JERRY_EXEC_OK) {
475    return visit_res;
476  }
477  return EXCE_ACE_JERRY_EXEC_OK;
478} /* generate_snapshots */
479
480/**
481 * visit directory and check snapshot file's version
482 */
483EXECRES validate_snapshots(char* filefolder) {
484  char* start_folder = NULL;
485  dir_node* head = NULL;
486  dir_node* end = NULL;
487
488  EXECRES init_res = init_directory_list(filefolder, start_folder, &head, &end);
489  if (init_res != EXCE_ACE_JERRY_EXEC_OK) {
490    return init_res;
491  }
492  EXECRES visit_res = visit_pending_directories(&head, &end, validate_snapshot);
493  if (visit_res != EXCE_ACE_JERRY_EXEC_OK) {
494    return visit_res;
495  }
496  return EXCE_ACE_JERRY_EXEC_OK;
497} /* validate_snapshots */
498
499/**
500 * visit directory and do js to bytecode conversion ON IAR
501 */
502EXECRES walk_directory(char* filefolder) {
503  EXECRES generate_res = generate_snapshots(filefolder);
504  if (generate_res != EXCE_ACE_JERRY_EXEC_OK) {
505    return generate_res;
506  }
507#if defined (JERRY_ENABLE_SNAPSHOT_VERSION_CHECK) && (JERRY_ENABLE_SNAPSHOT_VERSION_CHECK == 1)
508  return validate_snapshots(filefolder);
509#else
510  return EXCE_ACE_JERRY_EXEC_OK;
511#endif
512} /* walk_directory */
513
514/**
515 * when convertion failed, traverse directory and delete all created bytecode files
516 */
517EXECRES walk_del_bytecode(char* filefolder) {
518  DIR* dir;
519  struct dirent* direntp;
520  struct stat file_stat = { 0 };
521  char* filename = NULL;
522  char* current_path = NULL;
523  char* input_file_path = NULL;
524  char* start_folder = NULL;
525  dir_node *head, *curr, *end, *new_node;
526  int filefolder_len = strlen(filefolder) + 1;
527
528  if ((filefolder == NULL) || (stat(filefolder, &file_stat) < 0)) {
529    return EXCE_ACE_JERRY_INPUT_PATH_ERROR;
530  }
531  if ((start_folder = (char*)OhosMalloc(MEM_TYPE_JERRY, filefolder_len)) == NULL) {
532    return EXCE_ACE_JERRY_MALLOC_ERROR;
533  }
534  if (strcpy_s(start_folder, filefolder_len, filefolder) != 0) {
535    OhosFree(start_folder);
536    start_folder = NULL;
537    return EXCE_ACE_JERRY_INPUT_PATH_ERROR;
538  }
539  if ((head = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
540    OhosFree(start_folder);
541    start_folder = NULL;
542    return EXCE_ACE_JERRY_LINKLIST_ERROR;
543  }
544  if ((end = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
545    OhosFree(start_folder);
546    start_folder = NULL;
547    OhosFree(head);
548    head = NULL;
549    return EXCE_ACE_JERRY_LINKLIST_ERROR;
550  }
551  head->dir_name = NULL;
552  head->next = end;
553  end->dir_name = start_folder;
554  end->next = NULL;
555
556  while (head->next != NULL) {
557    curr = head->next;
558    current_path = curr->dir_name;
559    if ((dir = (DIR*)opendir(current_path)) == NULL) {
560      free_link(head);
561      curr = NULL;
562      end = NULL;
563      return EXCE_ACE_JERRY_OPEN_DIR_FAILED;
564    }
565    while ((direntp = (struct dirent*)readdir(dir)) != NULL) {
566      filename = direntp->d_name;
567      if (strncmp(filename, ".", 1) == 0) {
568        continue;
569      }
570      if ((input_file_path = splice_path(current_path, filename)) == NULL) {
571        closedir(dir);
572        free_link(head);
573        curr = NULL;
574        end = NULL;
575        return EXCE_ACE_JERRY_SPLICE_PATH_ERROR;
576      }
577      if (stat(input_file_path, &file_stat) < 0) {
578        closedir(dir);
579        OhosFree(input_file_path);
580        input_file_path = NULL;
581        free_link(head);
582        curr = NULL;
583        end = NULL;
584        return EXCE_ACE_JERRY_GET_FILE_STAT_ERROR;
585      }
586      if (file_stat.st_mode & S_IFDIR) {
587        // file now is file folder
588        if ((new_node = (dir_node*)OhosMalloc(MEM_TYPE_JERRY, sizeof(dir_node))) == NULL) {
589          closedir(dir);
590          OhosFree(input_file_path);
591          input_file_path = NULL;
592          free_link(head);
593          curr = NULL;
594          end = NULL;
595          return EXCE_ACE_JERRY_LINKLIST_ERROR;
596        }
597        // input_file_path for dir will be freed when that node is freed
598        new_node->dir_name = input_file_path;
599        new_node->next = NULL;
600        end->next = new_node;
601        end = new_node;
602        new_node = NULL;
603      } else if ((is_template_file(filename, ".bc")) && (unlink(input_file_path) != F_OK)) {
604        //file now is .bc file and unlink file failed
605        closedir(dir);
606        OhosFree(input_file_path);
607        input_file_path = NULL;
608        free_link(head);
609        curr = NULL;
610        end = NULL;
611        return EXCE_ACE_JERRY_UNLINKFILE_ERROR;
612      } else {
613        OhosFree(input_file_path);
614        input_file_path = NULL;
615      }
616    }
617    closedir(dir);
618    head->next = curr->next;
619    OhosFree(curr->dir_name);
620    curr->dir_name = NULL;
621    OhosFree(curr);
622    curr = NULL;
623  }
624  OhosFree(head);
625  head = NULL;
626  end = NULL;
627  return EXCE_ACE_JERRY_EXEC_OK;
628} /* walk_del_bytecode */
629
630#endif // end JERRY_FOR_IAR_CONFIG
631