1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 /* #define LOG_NDEBUG 0
6 */
7 
8 #include <stdint.h>
9 
10 #include "amp_config.h"
11 #include "amp_defines.h"
12 #include "amp_task.h"
13 #include "repl.h"
14 #include "quickjs.h"
15 #include "quickjs_addon_common.h"
16 #include "aos_system.h"
17 
18 #define MOD_STR "MODULE_REPL"
19 
20 static JSClassID js_repl_class_id;
21 
22 extern JSContext *js_get_context(void);
23 
24 static uint16_t repl_init_flag = 0;
25 static uint16_t repl_js_cb_flag = 0;
26 static JSValue repl_js_cb_ref = JS_UNDEFINED;
27 static aos_sem_t g_repl_close_sem = NULL;
28 
native_repl_open(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)29 static JSValue native_repl_open(JSContext *ctx, JSValueConst this_val,
30                           int argc, JSValueConst *argv)
31 {
32     aos_repl_init(NULL);
33 
34     return JS_NewInt32(ctx, 0);
35 }
36 
native_repl_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)37 static JSValue native_repl_close(JSContext *ctx, JSValueConst this_val,
38                           int argc, JSValueConst *argv)
39 {
40     int ret = 0;
41     repl_init_flag = 0;
42 
43     aos_sem_wait(&g_repl_close_sem, AOS_WAIT_FOREVER);
44 
45     ret = aos_repl_close();
46 
47     return JS_NewInt32(ctx, ret);
48 }
49 
native_repl_puts(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)50 static JSValue native_repl_puts(JSContext *ctx, JSValueConst this_val,
51                           int argc, JSValueConst *argv)
52 {
53     int ret = -1;
54     int i   = 0;
55     char *msg = NULL;
56     size_t msg_len = 0;
57     JSValue val;
58     const char *buf;
59 
60     if(argc < 1)
61     {
62         amp_warn(MOD_STR, "parameter must be array");
63         goto out;
64     }
65 
66     if(JS_IsString(argv[0])) {
67         buf = JS_ToCString(ctx, argv[0]);
68         msg_len = strlen(buf);
69         i = 1;
70     } else {
71         buf = JS_GetArrayBuffer(ctx, &msg_len, argv[0]);
72         if(!buf) {
73             amp_warn(MOD_STR, "parameter buffer is invalid, size: %d", msg_len);
74             goto out;
75         }
76     }
77 
78     msg = (char *)aos_malloc(msg_len + 1);
79     if (!msg) {
80         amp_warn(MOD_STR, "allocate memory failed");
81         goto out;
82     }
83 
84     memcpy(msg, buf, msg_len);
85 
86     msg[msg_len] = 0;
87 
88     ret  = aos_repl_write(msg);
89     if (-1 == ret) {
90         amp_error(MOD_STR, "native_repl_write fail!");
91     }
92     aos_free(msg);
93 out:
94     if(i == 1) {
95         JS_FreeCString(ctx, buf);
96     }
97     return JS_NewInt32(ctx, ret);
98 }
99 
native_repl_read(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)100 static JSValue native_repl_read(JSContext *ctx, JSValueConst this_val,
101                           int argc, JSValueConst *argv)
102 {
103     uint32_t max_len = 16;
104     uint32_t recvsize = 0;
105 
106     uint64_t pos, len;
107     size_t size;
108     size_t ret;
109     uint8_t *buf;
110 
111     if (JS_ToIndex(ctx, &pos, argv[1]))
112         return JS_EXCEPTION;
113     if (JS_ToIndex(ctx, &len, argv[2]))
114         return JS_EXCEPTION;
115 
116     buf = JS_GetArrayBuffer(ctx, &size, argv[0]);
117     if (!buf)
118         return JS_EXCEPTION;
119     if ((uint32_t)pos + (uint32_t)len > size)
120         return JS_ThrowRangeError(ctx, "read/write array buffer overflow");
121 
122     ret = aos_repl_read(buf + (uint32_t)pos, (uint32_t)len, &recvsize);
123 
124     return JS_NewInt32(ctx, ret);
125 }
126 
repl_read_notify(void * arg)127 static void repl_read_notify(void *arg)
128 {
129     JSContext *ctx = js_get_context();
130     if (repl_init_flag) {
131         JSValue val = JS_Call(ctx, repl_js_cb_ref, JS_UNDEFINED, 0, NULL);
132         JS_FreeValue(ctx, val);
133     }
134 }
135 
repl_task_entry()136 static void repl_task_entry()
137 {
138     aos_printf("repl task begin\n");
139     while (repl_init_flag) {
140         if(repl_js_cb_flag == 1) {
141             amp_task_schedule_call(repl_read_notify, NULL);
142         }
143         aos_msleep(100);
144     }
145     aos_printf("repl task exited\n");
146     aos_sem_signal(&g_repl_close_sem);
147     aos_task_exit(0);
148 }
149 
repl_read_task_start(void)150 void repl_read_task_start(void)
151 {
152     aos_task_t repl_task;
153 
154     if (!repl_init_flag) {
155         repl_init_flag = 1;
156         aos_task_new_ext(&repl_task, "amp repl task", repl_task_entry, NULL, 1024 * 4, AOS_DEFAULT_APP_PRI);
157     }
158 }
159 
native_repl_setReadHandler(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)160 static JSValue native_repl_setReadHandler(JSContext *ctx, JSValueConst this_val,
161                           int argc, JSValueConst *argv)
162 {
163     JSValue read_cb = argv[0];
164     if (!JS_IsFunction(ctx, read_cb)) {
165         return JS_ThrowTypeError(ctx, "not a function");
166     }
167     repl_js_cb_ref = JS_DupValue(ctx, read_cb);
168     repl_js_cb_flag = 1;
169     return JS_NewInt32(ctx, 0);
170 }
171 
module_repl_source_clean(void)172 static void module_repl_source_clean(void)
173 {
174     JSContext *ctx = js_get_context();
175     if (repl_js_cb_flag) {
176         JS_FreeValue(ctx, repl_js_cb_ref);
177     }
178 }
179 
180 static JSClassDef js_repl_class = {
181     "REPL",
182 };
183 
184 static const JSCFunctionListEntry js_repl_funcs[] = {
185     JS_CFUNC_DEF("open", 1, native_repl_open),
186     JS_CFUNC_DEF("read", 0, native_repl_read ),
187     JS_CFUNC_DEF("puts", 1, native_repl_puts ),
188     JS_CFUNC_DEF("exit", 0, native_repl_close ),
189     JS_CFUNC_DEF("setReadHandler", 1, native_repl_setReadHandler )
190 };
191 
js_repl_init(JSContext * ctx,JSModuleDef * m)192 static int js_repl_init(JSContext *ctx, JSModuleDef *m)
193 {
194     JSValue proto;
195 
196     JS_NewClassID(&js_repl_class_id);
197 
198     JS_NewClass(JS_GetRuntime(ctx), js_repl_class_id, &js_repl_class);
199     proto = JS_NewObject(ctx);
200     JS_SetPropertyFunctionList(ctx, proto, js_repl_funcs,
201                                countof(js_repl_funcs));
202     JS_SetClassProto(ctx, js_repl_class_id, proto);
203 
204     return JS_SetModuleExportList(ctx, m, js_repl_funcs,
205                                   countof(js_repl_funcs));
206 }
207 
js_init_module_repl(JSContext * ctx,const char * module_name)208 JSModuleDef *js_init_module_repl(JSContext *ctx, const char *module_name)
209 {
210     JSModuleDef *m;
211     m = JS_NewCModule(ctx, module_name, js_repl_init);
212     if (!m)
213         return NULL;
214     JS_AddModuleExportList(ctx, m, js_repl_funcs, countof(js_repl_funcs));
215     return m;
216 }
217 
module_repl_register(void)218 void module_repl_register(void)
219 {
220     amp_debug(MOD_STR, "module_repl_register\n");
221     JSContext *ctx = js_get_context();
222 
223     if (!g_repl_close_sem) {
224         if (aos_sem_new(&g_repl_close_sem, 0) != 0) {
225             amp_error(MOD_STR, "create repl sem fail");
226             return;
227         }
228     }
229 
230     amp_module_free_register(module_repl_source_clean);
231 
232     js_init_module_repl(ctx, "REPL");
233 }
234