1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 
5 #include "amp_platform.h"
6 #include "aos_fs.h"
7 #include "aos_system.h"
8 #include "amp_config.h"
9 #include "amp_defines.h"
10 #include "amp_task.h"
11 #include "board_mgr.h"
12 #include "be_inl.h"
13 #include "cJSON.h"
14 #include "addons/libjs.h"
15 #include "startup/app_entry.h"
16 
17 #define MOD_STR "BE"
18 #define MAX_PATH_SIZE 1024
19 
20 static duk_context *duk_ctx;
21 static char node_modules_path[MAX_PATH_SIZE];
22 static char resolved_path[MAX_PATH_SIZE];
23 static char *file_data;
24 int g_repl_config = 0;
25 
26 #define assert(x) do{ \
27     if(!(x)){ \
28         amp_error(MOD_STR, "fatal expression: " #x "\r\n"); \
29         while(1){ \
30             aos_msleep(10); \
31         }; \
32     } \
33 }while(0)
34 
resolve_path(const char * parent,const char * module_id)35 static char *resolve_path(const char *parent, const char *module_id)
36 {
37     char base[MAX_PATH_SIZE];
38     int i;
39 
40 #ifdef JSE_HIGHLEVEL_JSAPI
41     /* check libjs */
42     for(i = 0; i < libjs_num; i++){
43         if(strcmp(LIBJS_ENTRIES[i].name, module_id) != 0){
44             continue;
45         }
46         return (char *)module_id;
47     }
48 #endif
49 
50     /* path process */
51     if (module_id[0] != '.') {
52         snprintf(base, sizeof(base), "%s/%s", node_modules_path, module_id);
53     } else {
54         strcpy(base, parent);
55         char *last_slash = strrchr(base, '/');
56         if (last_slash) last_slash[0] = '\0';
57 
58         int len       = strlen(module_id);
59         const char *p = module_id;
60 
61         if (len == 1 && p[0] == '.') {
62             p += 1;
63         } else if (p[0] == '.' && p[1] == '/') {
64             p += 2;
65         } else {
66             const char *end = module_id + len;
67             while (p <= end - 3 && p[0] == '.' && p[1] == '.' && p[2] == '/') {
68                 p += 3;
69                 char *last_slash = strrchr(base, '/');
70                 if (last_slash)
71                     last_slash[0] = '\0';
72                 else {
73                     amp_warn(MOD_STR, "resolve path fail\n");
74                     break;
75                 }
76             }
77         }
78         strcat(base, "/");
79         strcat(base, p);
80     }
81 
82     struct aos_stat sb;
83     int ret = aos_stat(base, &sb);
84     amp_debug(MOD_STR, "base: %s, ret: %d\n", base, ret);
85     if (!ret && S_ISREG(sb.st_mode)) {
86         strcpy(resolved_path, base);
87         return resolved_path;
88     }
89 
90     snprintf(resolved_path, sizeof(resolved_path), "%s.js", base);
91     ret = aos_stat(resolved_path, &sb);
92     amp_debug(MOD_STR, "resolved_path: %s, ret: %d\n", resolved_path, ret);
93     if (!ret && S_ISREG(sb.st_mode)) {
94         return resolved_path;
95     }
96 
97     snprintf(resolved_path, sizeof(resolved_path), "%s/index.js", base);
98     ret = aos_stat(resolved_path, &sb);
99     amp_debug(MOD_STR, "resolved_path: %s, ret: %d\n", resolved_path, ret);
100     if (!ret && S_ISREG(sb.st_mode)) {
101         return resolved_path;
102     }
103 
104     snprintf(resolved_path, sizeof(resolved_path), "%s/package.json", base);
105     ret = aos_stat(resolved_path, &sb);
106     amp_debug(MOD_STR, "resolved_path: %s, ret: %d\n", resolved_path, ret);
107     if (ret || !S_ISREG(sb.st_mode)) {
108         amp_warn(MOD_STR, "file %s not exist\n", resolved_path);
109         return NULL;
110     }
111     /* parse package.json */
112     char *pkg_data = aos_malloc(sb.st_size + 1);
113     if (!pkg_data) {
114         amp_warn(MOD_STR, "cannot alloc memory to read package.json for module: %s",
115              module_id);
116         return NULL;
117     }
118 
119     int fp = aos_open(resolved_path, O_RDONLY);
120     aos_read(fp, pkg_data, sb.st_size);
121     aos_close(fp);
122     pkg_data[sb.st_size] = '\0';
123 
124     cJSON *json      = cJSON_Parse(pkg_data);
125     //TODO: cJSON *item_main = cJSON_GetObjectItemCaseSensitive(json, "main");
126     cJSON *item_main = cJSON_GetObjectItem(json, "main");
127     amp_debug(MOD_STR, "item_main: %p\n", item_main);
128     //TODO: if (cJSON_IsString(item_main) && item_main->valuestring) {
129     if (item_main->valuestring) {
130         snprintf(resolved_path, sizeof(resolved_path), "%s/%s", base,
131                  item_main->valuestring);
132         if (!aos_stat(resolved_path, &sb) && S_ISREG(sb.st_mode)) {
133             cJSON_Delete(json);
134             aos_free(pkg_data);
135             return resolved_path;
136         }
137         snprintf(resolved_path, sizeof(resolved_path), "%s/%s.js", base,
138                  item_main->valuestring);
139         if (!aos_stat(resolved_path, &sb) && S_ISREG(sb.st_mode)) {
140             cJSON_Delete(json);
141             aos_free(pkg_data);
142             return resolved_path;
143         }
144     }
145     cJSON_Delete(json);
146     aos_free(pkg_data);
147     return NULL;
148 }
149 
cb_resolve_module(duk_context * ctx)150 static duk_ret_t cb_resolve_module(duk_context *ctx)
151 {
152     const char *module_id;
153     const char *parent_id;
154 
155     module_id = duk_require_string(ctx, 0);
156     parent_id = duk_require_string(ctx, 1);
157 
158     amp_debug(MOD_STR, "module_id: %s, parent_id: %s\n", module_id, parent_id);
159 
160     char *path = resolve_path(parent_id, module_id);
161     if (!path) return duk_type_error(ctx, "cannot find module: %s", module_id);
162 
163     duk_push_string(ctx, path);
164     amp_debug(MOD_STR, "resolve_cb: id:'%s', parent-id:'%s', resolve-to:'%s'\n", module_id,
165           parent_id, duk_get_string(ctx, -1));
166 
167     return 1;
168 }
169 
cb_load_module(duk_context * ctx)170 static duk_ret_t cb_load_module(duk_context *ctx)
171 {
172     const char *filename;
173     const char *module_id;
174     int len;
175     int i;
176 
177     module_id = duk_require_string(ctx, 0);
178     duk_get_prop_string(ctx, 2, "filename");
179     filename = duk_require_string(ctx, -1);
180 
181     amp_debug(MOD_STR, "id:'%s', filename:'%s'\n", module_id, filename);
182 
183 #ifdef JSE_HIGHLEVEL_JSAPI
184     /* find libjs entries */
185     for(i = 0; i < libjs_num; i++){
186         if(strcmp(LIBJS_ENTRIES[i].name, module_id) != 0){
187             continue;
188         }
189         amp_debug(MOD_STR, "find libjs entry: %s", module_id);
190         duk_push_lstring(ctx, LIBJS_ENTRIES[i].content, strlen(LIBJS_ENTRIES[i].content));
191         return 1;
192     }
193 #endif
194 
195     int fp = aos_open(filename, O_RDONLY);
196     assert(fp >= 0);
197     len = aos_lseek(fp, 0, HAL_SEEK_END);
198     amp_debug(MOD_STR, "file: %s, size: %d\n", filename, len);
199     aos_lseek(fp, 0, HAL_SEEK_SET);
200     char *data = (char *)aos_malloc(len);
201     if (!data) {
202         aos_close(fp);
203         amp_warn(MOD_STR, "cannot alloc memory to read module: %s", module_id);
204         return duk_type_error(ctx, "cannot alloc memory to read module: %s",
205                               module_id);
206     }
207 
208     aos_read(fp, data, len);
209     duk_push_lstring(ctx, data, len);
210     aos_free(data);
211     aos_close(fp);
212 
213     return 1;
214 }
215 
handle_assert(duk_context * ctx)216 static duk_ret_t handle_assert(duk_context *ctx)
217 {
218     if (duk_to_boolean(ctx, 0)) return 0;
219 
220     const char *msg = duk_safe_to_string(ctx, 1);
221     // fprintf(stderr, "assertion failed: %s\n", msg);
222     // fflush(stderr);
223     amp_error(MOD_STR,"assertion failed: %s", msg);
224 
225     duk_push_string(ctx, "");
226     return duk_throw(ctx);
227 }
228 
229 #if defined(JSE_HIGHLEVEL_JSAPI) && defined(JSE_CORE_ADDON_INITJS)
module_initjs_register(void)230 static void module_initjs_register(void)
231 {
232     amp_debug(MOD_STR, "module_initjs_register");
233     // amp_debug("%s\r\n", LIBJS_ENTRIES[libjs_num - 1].content);
234     duk_context *ctx = be_get_context();
235     duk_eval_string(ctx, LIBJS_ENTRIES[libjs_num - 1].content);
236     duk_pop(ctx);
237 }
238 #endif
239 
240 /*
241  * register addons
242  */
jsengine_register_addons()243 static void jsengine_register_addons()
244 {
245     duk_context *ctx = be_get_context();
246 
247 /** global Object */
248 #ifdef JSE_CORE_ADDON_BUILDIN
249     module_builtin_register();
250 #endif
251 #ifdef JSE_CORE_ADDON_SYSTEM
252     module_system_register();
253 #endif
254 #ifdef JSE_CORE_ADDON_SYSTIMER
255     module_systimer_register();
256 #endif
257 #ifdef JSE_ADVANCED_ADDON_UND
258     module_und_register();
259 #endif
260 
261     duk_push_object(ctx);
262 
263 /** core component */
264 #ifdef JSE_CORE_ADDON_FS
265     module_fs_register();
266 #endif
267 #ifdef JSE_CORE_ADDON_KV
268     module_kv_register();
269 #endif
270 #ifdef JSE_CORE_ADDON_PM
271     module_pm_register();
272 #endif
273 #ifdef JSE_CORE_ADDON_BATTERY
274     module_battery_register();
275 #endif
276 #ifdef JSE_CORE_ADDON_CHARGER
277     module_charger_register();
278 #endif
279 #ifdef JSE_CORE_ADDON_CHECKSUM
280     module_checksum_register();
281 #endif
282 #ifdef JSE_CORE_ADDON_AT
283     module_at_register();
284 #endif
285 #ifdef JSE_CORE_ADDON_LOG
286     module_log_register();
287 #endif
288 
289 #ifdef JSE_ADVANCED_ADDON_BLECFGNET
290     module_blecfgnet_register();
291 #endif
292 
293 #ifdef JSE_ADVANCED_ADDON_UI
294     //module_vm_register();
295     module_ui_register();
296 #endif
297 
298 #ifdef JSE_ADVANCED_ADDON_KEYPAD
299     module_keypad_register();
300 #endif
301 
302 #ifdef JSE_CORE_ADDON_CRYPTO
303     module_crypto_register();
304 #endif
305 
306 /** network component */
307 #ifdef JSE_NET_ADDON_MQTT
308     module_mqtt_register();
309 #endif
310 #ifdef JSE_NET_ADDON_NETMGR
311     module_netmgr_register();
312 #endif
313 #ifdef JSE_NET_ADDON_WIFI
314     module_wifi_register();
315 #endif
316 #ifdef JSE_NET_ADDON_CELLULAR
317     module_cellular_register();
318 #endif
319 #ifdef JSE_NET_ADDON_UDP
320     module_udp_register();
321 #endif
322 #ifdef JSE_NET_ADDON_TCP
323     module_tcp_register();
324 #endif
325 #ifdef JSE_NET_ADDON_HTTP
326     module_http_register();
327 #endif
328 #ifdef JSE_NET_ADDON_MIIO
329     module_miio_register();
330 #endif
331 
332 /** hardware component */
333 #ifdef JSE_HW_ADDON_ADC
334     module_adc_register();
335 #endif
336 #ifdef JSE_HW_ADDON_DAC
337     module_dac_register();
338 #endif
339 #ifdef JSE_HW_ADDON_CAN
340     module_can_register();
341 #endif
342 #ifdef JSE_HW_ADDON_GPIO
343     module_gpio_register();
344 #endif
345 #ifdef JSE_HW_ADDON_I2C
346     module_i2c_register();
347 #endif
348 #ifdef JSE_HW_ADDON_SPI
349     module_spi_register();
350 #endif
351 #ifdef JSE_HW_ADDON_TIMER
352     module_timer_register();
353 #endif
354 #ifdef JSE_HW_ADDON_IR
355     module_ir_register();
356 #endif
357 #ifdef JSE_HW_ADDON_LCD
358     module_lcd_register();
359 #endif
360 #ifdef JSE_HW_ADDON_PWM
361     module_pwm_register();
362 #endif
363 #ifdef JSE_HW_ADDON_ONEWIRE
364     module_onewire_register();
365 #endif
366 #ifdef JSE_HW_ADDON_RTC
367     module_rtc_register();
368 #endif
369 #ifdef JSE_HW_ADDON_UART
370     module_uart_register();
371 #endif
372 #ifdef JSE_HW_ADDON_WDG
373     module_wdg_register();
374 #endif
375 
376 /** advanced component */
377 #ifdef JSE_ADVANCED_ADDON_AIOT_DEVICE
378     module_aiot_device_register();
379 #endif
380 #ifdef JSE_ADVANCED_ADDON_AIOT_GATEWAY
381     module_aiot_gateway_register();
382 #endif
383 #ifdef JSE_ADVANCED_ADDON_OTA
384     module_app_ota_register();
385 #endif
386 #ifdef JSE_ADVANCED_ADDON_AUDIOPLAYER
387     module_audioplayer_register();
388 #endif
389 #ifdef JSE_ADVANCED_ADDON_TTS
390     module_tts_register();
391 #endif
392 #ifdef JSE_ADVANCED_ADDON_LOCATION
393     module_location_register();
394 #endif
395 #ifdef JSE_ADVANCED_ADDON_PAYBOX
396     module_paybox_register();
397 #endif
398 #ifdef JSE_ADVANCED_ADDON_SMARTCARD
399     module_smartcard_register();
400 #endif
401 
402 /** wireless component */
403 #ifdef JSE_WIRELESS_ADDON_BT_HOST
404     module_bt_host_register();
405 #endif
406 
407 /* init.js */
408 #if defined(JSE_HIGHLEVEL_JSAPI) && defined(JSE_CORE_ADDON_INITJS)
409     module_initjs_register();
410 #endif
411 
412     duk_put_global_string(ctx, "__native");
413 }
414 
duk_debug_write_cb(long arg_level,const char * arg_file,long arg_line,const char * arg_func,const char * arg_msg)415 void duk_debug_write_cb(long arg_level, const char *arg_file, long arg_line, const char *arg_func, const char *arg_msg){
416     amp_debug(MOD_STR, "%s\r\n", arg_msg);
417 }
jsengine_init()418 void jsengine_init()
419 {
420     amp_debug(MOD_STR, "duktape jsengine_init\r\n");
421     if (duk_ctx) {
422         amp_warn(MOD_STR, "bone engine has been initialized\n");
423         return;
424     }
425     duk_ctx = duk_create_heap_default();
426     be_ref_setup(duk_ctx);
427     duk_push_c_function(duk_ctx, handle_assert, 2);
428     duk_put_global_string(duk_ctx, "assert");
429     duk_push_object(duk_ctx);
430     duk_push_c_function(duk_ctx, cb_resolve_module, DUK_VARARGS);
431     duk_put_prop_string(duk_ctx, -2, "resolve");
432     duk_push_c_function(duk_ctx, cb_load_module, DUK_VARARGS);
433     duk_put_prop_string(duk_ctx, -2, "load");
434     be_module_node_init(duk_ctx);
435     amp_debug(MOD_STR, "duktape be_module_node_init\r\n");
436 
437     snprintf(node_modules_path, sizeof(node_modules_path), "/node_modules");
438     // node_modules_path = JSE_FS_ROOT_DIR"/node_modules";
439     /* register all addons */
440     jsengine_register_addons();
441 
442 #ifdef JSE_ADVANCED_ADDON_UI
443     page_entry_register();
444 #endif
445 
446     /* register App() */
447     app_entry_register();
448     amp_debug(MOD_STR, "duktape jsengine_register_addons\r\n");
449 
450     if (g_repl_config) {
451         repl_init();
452     }
453 }
454 
get_stack_raw(duk_context * ctx,void * udata)455 static duk_ret_t get_stack_raw(duk_context *ctx, void *udata)
456 {
457     (void)udata;
458 
459     if (!duk_is_object(ctx, -1)) {
460         return 1;
461     }
462     if (!duk_has_prop_string(ctx, -1, "stack")) {
463         return 1;
464     }
465     if (!duk_is_error(ctx, -1)) {
466         /* Not an Error instance, don't read "stack". */
467         return 1;
468     }
469 
470     duk_get_prop_string(ctx, -1, "stack"); /* caller coerces */
471     duk_remove(ctx, -2);
472     return 1;
473 }
474 
print_pop_error(duk_context * ctx,FILE * f)475 static void print_pop_error(duk_context *ctx, FILE *f)
476 {
477     /* Print error objects with a stack trace specially.
478      * Note that getting the stack trace may throw an error
479      * so this also needs to be safe call wrapped.
480      */
481     (void)duk_safe_call(ctx, get_stack_raw, NULL /*udata*/, 1 /*nargs*/,
482                         1 /*nrets*/);
483     amp_console("%s\n", duk_safe_to_stacktrace(ctx, -1));
484 
485     duk_pop(ctx);
486 }
487 
wrapped_compile_execute(duk_context * ctx,void * udata)488 static duk_ret_t wrapped_compile_execute(duk_context *ctx, void *udata)
489 {
490     const char *src_data;
491     duk_size_t src_len;
492     duk_uint_t comp_flags;
493 
494     (void)udata;
495 
496     /* Use duk_compile_lstring_filename() variant which avoids interning
497      * the source code.  This only really matters for low memory environments.
498      */
499 
500     /* [ ... src_data src_len filename ] */
501     src_data = (const char *)duk_require_pointer(ctx, -3);
502     src_len  = (duk_size_t)duk_require_uint(ctx, -2);
503 
504     comp_flags = DUK_COMPILE_SHEBANG;
505     duk_compile_lstring_filename(ctx, comp_flags, src_data, src_len);
506 
507     /* [ ... src_data src_len function ] */
508     duk_push_global_object(ctx); /* 'this' binding */
509     duk_call_method(ctx, 0);
510 
511     return 0; /* duk_safe_call() cleans up */
512 }
513 
jsengine_start(const char * js)514 void jsengine_start(const char *js)
515 {
516     assert(duk_ctx);
517     if (!js) {
518         amp_warn(MOD_STR, "js is null\n");
519         return;
520     }
521     duk_push_pointer(duk_ctx, (void *)js);
522     duk_push_uint(duk_ctx, (duk_uint_t)strlen(js));
523     duk_push_string(duk_ctx, "eval");
524 
525     int ret = duk_safe_call(duk_ctx, wrapped_compile_execute, NULL /*udata*/,
526                             3 /*nargs*/, 1 /*nret*/);
527     if (ret != DUK_EXEC_SUCCESS) {
528         print_pop_error(duk_ctx, stderr);
529     } else {
530         duk_pop(duk_ctx);
531     }
532 }
533 
jsengine_eval_file(const char * filename)534 void jsengine_eval_file(const char *filename)
535 {
536     amp_debug(MOD_STR, "jsengine_eval_file entry");
537     assert(filename);
538 
539     amp_debug(MOD_STR, "%s %s", "eval file: ", filename);
540 
541     /* set entry */
542     be_module_node_set_entry(duk_ctx, filename);
543 
544     /* read & run js file */
545     struct aos_stat sb;
546     if (aos_stat(filename, &sb) || !S_ISREG(sb.st_mode)) {
547         amp_warn(MOD_STR, "%s %s", "file not exist", filename);
548         return;
549     }
550     amp_debug(MOD_STR, "%s %d", "file size: ", sb.st_size);
551     file_data = (char *)aos_malloc(sb.st_size + 1);
552     if (!file_data) {
553         amp_warn(MOD_STR, "cannot alloc memory");
554         return;
555     }
556     file_data[sb.st_size] = 0;
557 
558     int fp = aos_open(filename, O_RDONLY);
559     aos_read(fp, file_data, sb.st_size);
560     aos_close(fp);
561     duk_push_pointer(duk_ctx, (void *)file_data);
562     duk_push_uint(duk_ctx, (duk_uint_t)sb.st_size);
563     duk_push_string(duk_ctx, filename);
564 
565     int ret = duk_safe_call(duk_ctx, wrapped_compile_execute, NULL /*udata*/,
566                             3 /*nargs*/, 1 /*nret*/);
567     if (ret != DUK_EXEC_SUCCESS) {
568         print_pop_error(duk_ctx, stderr);
569     } else {
570         duk_pop(duk_ctx);
571     }
572 }
573 
jsengine_exit()574 void jsengine_exit()
575 {
576     if (!duk_ctx) {
577         amp_warn(MOD_STR, "jsengine has not been initialized");
578         return;
579     }
580     if (file_data) {
581         aos_free(file_data);
582         file_data = NULL;
583     }
584     duk_destroy_heap(duk_ctx);
585     // aos_free(node_modules_path);
586     duk_ctx = NULL;
587 }
588 
be_get_context()589 duk_context *be_get_context()
590 {
591     return duk_ctx;
592 }
593 
jsengine_loop_once()594 void jsengine_loop_once()
595 {
596 
597 }
598 
599 extern int64_t g_ntp_time;
600 extern int64_t g_up_time;
amp_date_get_now()601 duk_double_t amp_date_get_now()
602 {
603     int64_t uptime = aos_now_ms();
604     g_ntp_time = g_ntp_time + (uptime - g_up_time);
605 
606     duk_double_t time = floor(g_ntp_time);
607 
608     g_ntp_time = g_ntp_time - (uptime - g_up_time);
609     return time;
610 }
611