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