1 | /* Copyright JS Foundation and other contributors, http://js.foundation |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. |
5 | * You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | * See the License for the specific language governing permissions and |
13 | * limitations under the License. |
14 | */ |
15 | |
16 | #include <stdbool.h> |
17 | #include <stdio.h> |
18 | #include <stdlib.h> |
19 | |
20 | #include "jerryscript-port.h" |
21 | #include "jerryscript-port-default.h" |
22 | |
23 | #include "cli.h" |
24 | #include "main-utils.h" |
25 | #include "main-options.h" |
26 | |
27 | /** |
28 | * Command line option IDs |
29 | */ |
30 | typedef enum |
31 | { |
32 | OPT_HELP, |
33 | OPT_VERSION, |
34 | OPT_MEM_STATS, |
35 | OPT_TEST262_OBJECT, |
36 | OPT_PARSE_ONLY, |
37 | OPT_SHOW_OP, |
38 | OPT_SHOW_RE_OP, |
39 | OPT_DEBUG_SERVER, |
40 | OPT_DEBUG_PORT, |
41 | OPT_DEBUG_CHANNEL, |
42 | OPT_DEBUG_PROTOCOL, |
43 | OPT_DEBUG_SERIAL_CONFIG, |
44 | OPT_DEBUGGER_WAIT_SOURCE, |
45 | OPT_EXEC_SNAP, |
46 | OPT_EXEC_SNAP_FUNC, |
47 | OPT_MODULE, |
48 | OPT_LOG_LEVEL, |
49 | OPT_NO_PROMPT, |
50 | OPT_CALL_ON_EXIT, |
51 | OPT_USE_STDIN, |
52 | } main_opt_id_t; |
53 | |
54 | /** |
55 | * Command line options |
56 | */ |
57 | static const cli_opt_t main_opts[] = |
58 | { |
59 | CLI_OPT_DEF (.id = OPT_HELP, .opt = "h" , .longopt = "help" , |
60 | .help = "print this help and exit" ), |
61 | CLI_OPT_DEF (.id = OPT_VERSION, .opt = "v" , .longopt = "version" , |
62 | .help = "print tool and library version and exit" ), |
63 | CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "mem-stats" , |
64 | .help = "dump memory statistics" ), |
65 | CLI_OPT_DEF (.id = OPT_TEST262_OBJECT, .longopt = "test262-object" , |
66 | .help = "create test262 object" ), |
67 | CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "parse-only" , |
68 | .help = "don't execute JS input" ), |
69 | CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "show-opcodes" , |
70 | .help = "dump parser byte-code" ), |
71 | CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "show-regexp-opcodes" , |
72 | .help = "dump regexp byte-code" ), |
73 | CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server" , |
74 | .help = "start debug server and wait for a connecting client" ), |
75 | CLI_OPT_DEF (.id = OPT_DEBUG_PORT, .longopt = "debug-port" , .meta = "NUM" , |
76 | .help = "debug server port (default: 5001)" ), |
77 | CLI_OPT_DEF (.id = OPT_DEBUG_CHANNEL, .longopt = "debug-channel" , .meta = "[websocket|rawpacket]" , |
78 | .help = "Specify the debugger transmission channel (default: websocket)" ), |
79 | CLI_OPT_DEF (.id = OPT_DEBUG_PROTOCOL, .longopt = "debug-protocol" , .meta = "PROTOCOL" , |
80 | .help = "Specify the transmission protocol over the communication channel (tcp|serial, default: tcp)" ), |
81 | CLI_OPT_DEF (.id = OPT_DEBUG_SERIAL_CONFIG, .longopt = "serial-config" , .meta = "OPTIONS_STRING" , |
82 | .help = "Configure parameters for serial port (default: /dev/ttyS0,115200,8,N,1)" ), |
83 | CLI_OPT_DEF (.id = OPT_DEBUGGER_WAIT_SOURCE, .longopt = "debugger-wait-source" , |
84 | .help = "wait for an executable source from the client" ), |
85 | CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot" , .meta = "FILE" , |
86 | .help = "execute input snapshot file(s)" ), |
87 | CLI_OPT_DEF (.id = OPT_EXEC_SNAP_FUNC, .longopt = "exec-snapshot-func" , .meta = "FILE NUM" , |
88 | .help = "execute specific function from input snapshot file(s)" ), |
89 | CLI_OPT_DEF (.id = OPT_MODULE, .opt = "m" , .longopt = "module" , .meta = "FILE" , |
90 | .help = "execute module file" ), |
91 | CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level" , .meta = "NUM" , |
92 | .help = "set log level (0-3)" ), |
93 | CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "no-prompt" , |
94 | .help = "don't print prompt in REPL mode" ), |
95 | CLI_OPT_DEF (.id = OPT_CALL_ON_EXIT, .longopt = "call-on-exit" , .meta = "STRING" , |
96 | .help = "invoke the specified function when the process is just about to exit" ), |
97 | CLI_OPT_DEF (.id = OPT_USE_STDIN, .opt = "" , .help = "read from standard input" ), |
98 | CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE" , |
99 | .help = "input JS file(s)" ) |
100 | }; |
101 | |
102 | /** |
103 | * Check whether a usage-related condition holds. If not, print an error |
104 | * message, print the usage, and terminate the application. |
105 | */ |
106 | static void |
107 | check_usage (bool condition, /**< the condition that must hold */ |
108 | const char *name, /**< name of the application (argv[0]) */ |
109 | const char *msg, /**< error message to print if condition does not hold */ |
110 | const char *opt) /**< optional part of the error message */ |
111 | { |
112 | if (!condition) |
113 | { |
114 | jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s: %s%s\n" , name, msg, opt != NULL ? opt : "" ); |
115 | exit (JERRY_STANDALONE_EXIT_CODE_FAIL); |
116 | } |
117 | } /* check_usage */ |
118 | |
119 | /** |
120 | * Check whether JerryScript has a requested feature enabled or not. If not, |
121 | * print a warning message. |
122 | * |
123 | * @return the status of the feature. |
124 | */ |
125 | static bool |
126 | check_feature (jerry_feature_t feature, /**< feature to check */ |
127 | const char *option) /**< command line option that triggered this check */ |
128 | { |
129 | if (!jerry_is_feature_enabled (feature)) |
130 | { |
131 | jerry_port_default_set_log_level (JERRY_LOG_LEVEL_WARNING); |
132 | jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Ignoring '%s' option because this feature is disabled!\n" , option); |
133 | return false; |
134 | } |
135 | return true; |
136 | } /* check_feature */ |
137 | |
138 | /** |
139 | * parse input arguments |
140 | */ |
141 | void |
142 | main_parse_args (int argc, /**< argc */ |
143 | char **argv, /**< argv */ |
144 | main_args_t *arguments_p) /**< [in/out] arguments reference */ |
145 | { |
146 | arguments_p->source_count = 0; |
147 | |
148 | arguments_p->debug_channel = "websocket" ; |
149 | arguments_p->debug_protocol = "tcp" ; |
150 | arguments_p->debug_serial_config = "/dev/ttyS0,115200,8,N,1" ; |
151 | arguments_p->debug_port = 5001; |
152 | |
153 | arguments_p->exit_cb_name_p = NULL; |
154 | arguments_p->init_flags = JERRY_INIT_EMPTY; |
155 | arguments_p->option_flags = OPT_FLAG_EMPTY; |
156 | |
157 | cli_state_t cli_state = cli_init (main_opts, argc, argv); |
158 | for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state)) |
159 | { |
160 | switch (id) |
161 | { |
162 | case OPT_HELP: |
163 | { |
164 | cli_help (argv[0], NULL, main_opts); |
165 | exit (JERRY_STANDALONE_EXIT_CODE_OK); |
166 | |
167 | break; |
168 | } |
169 | case OPT_VERSION: |
170 | { |
171 | printf ("Version: %d.%d.%d%s\n" , |
172 | JERRY_API_MAJOR_VERSION, |
173 | JERRY_API_MINOR_VERSION, |
174 | JERRY_API_PATCH_VERSION, |
175 | JERRY_COMMIT_HASH); |
176 | exit (JERRY_STANDALONE_EXIT_CODE_OK); |
177 | |
178 | break; |
179 | } |
180 | case OPT_MEM_STATS: |
181 | { |
182 | if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg)) |
183 | { |
184 | jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); |
185 | arguments_p->init_flags |= JERRY_INIT_MEM_STATS; |
186 | } |
187 | break; |
188 | } |
189 | case OPT_TEST262_OBJECT: |
190 | { |
191 | arguments_p->option_flags |= OPT_FLAG_TEST262_OBJECT; |
192 | break; |
193 | } |
194 | case OPT_PARSE_ONLY: |
195 | { |
196 | arguments_p->option_flags |= OPT_FLAG_PARSE_ONLY; |
197 | break; |
198 | } |
199 | case OPT_SHOW_OP: |
200 | { |
201 | if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg)) |
202 | { |
203 | jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); |
204 | arguments_p->init_flags |= JERRY_INIT_SHOW_OPCODES; |
205 | } |
206 | break; |
207 | } |
208 | case OPT_CALL_ON_EXIT: |
209 | { |
210 | arguments_p->exit_cb_name_p = cli_consume_string (&cli_state); |
211 | break; |
212 | } |
213 | case OPT_SHOW_RE_OP: |
214 | { |
215 | if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg)) |
216 | { |
217 | jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG); |
218 | arguments_p->init_flags |= JERRY_INIT_SHOW_REGEXP_OPCODES; |
219 | } |
220 | break; |
221 | } |
222 | case OPT_DEBUG_SERVER: |
223 | { |
224 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
225 | { |
226 | arguments_p->option_flags |= OPT_FLAG_DEBUG_SERVER; |
227 | } |
228 | break; |
229 | } |
230 | case OPT_DEBUG_PORT: |
231 | { |
232 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
233 | { |
234 | arguments_p->debug_port = (uint16_t) cli_consume_int (&cli_state); |
235 | } |
236 | break; |
237 | } |
238 | case OPT_DEBUG_CHANNEL: |
239 | { |
240 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
241 | { |
242 | const char *debug_channel = cli_consume_string (&cli_state); |
243 | check_usage (!strcmp (debug_channel, "websocket" ) || !strcmp (debug_channel, "rawpacket" ), |
244 | argv[0], "Error: invalid value for --debug-channel: " , cli_state.arg); |
245 | |
246 | arguments_p->debug_channel = debug_channel; |
247 | } |
248 | break; |
249 | } |
250 | case OPT_DEBUG_PROTOCOL: |
251 | { |
252 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
253 | { |
254 | const char *debug_protocol = cli_consume_string (&cli_state); |
255 | check_usage (!strcmp (debug_protocol, "tcp" ) || !strcmp (debug_protocol, "serial" ), |
256 | argv[0], "Error: invalid value for --debug-protocol: " , cli_state.arg); |
257 | |
258 | arguments_p->debug_protocol = debug_protocol; |
259 | } |
260 | break; |
261 | } |
262 | case OPT_DEBUG_SERIAL_CONFIG: |
263 | { |
264 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
265 | { |
266 | arguments_p->debug_serial_config = cli_consume_string (&cli_state); |
267 | } |
268 | break; |
269 | } |
270 | case OPT_DEBUGGER_WAIT_SOURCE: |
271 | { |
272 | if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg)) |
273 | { |
274 | arguments_p->option_flags |= OPT_FLAG_WAIT_SOURCE; |
275 | } |
276 | break; |
277 | } |
278 | case OPT_EXEC_SNAP: |
279 | { |
280 | const bool is_enabled = check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg); |
281 | const uint32_t path_index = cli_consume_path (&cli_state); |
282 | |
283 | if (is_enabled) |
284 | { |
285 | main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; |
286 | arguments_p->source_count++; |
287 | |
288 | source_p->type = SOURCE_SNAPSHOT; |
289 | source_p->path_index = path_index; |
290 | source_p->snapshot_index = 0; |
291 | } |
292 | |
293 | break; |
294 | } |
295 | case OPT_EXEC_SNAP_FUNC: |
296 | { |
297 | const bool is_enabled = check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg); |
298 | const uint32_t path_index = cli_consume_path (&cli_state); |
299 | const uint16_t snapshot_index = (uint16_t) cli_consume_int (&cli_state); |
300 | |
301 | if (is_enabled) |
302 | { |
303 | main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; |
304 | arguments_p->source_count++; |
305 | |
306 | source_p->type = SOURCE_SNAPSHOT; |
307 | source_p->path_index = path_index; |
308 | source_p->snapshot_index = snapshot_index; |
309 | } |
310 | |
311 | break; |
312 | } |
313 | case OPT_MODULE: |
314 | { |
315 | const uint32_t path_index = cli_consume_path (&cli_state); |
316 | |
317 | main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; |
318 | arguments_p->source_count++; |
319 | |
320 | source_p->type = SOURCE_MODULE; |
321 | source_p->path_index = path_index; |
322 | source_p->snapshot_index = 0; |
323 | |
324 | break; |
325 | } |
326 | case OPT_LOG_LEVEL: |
327 | { |
328 | long int log_level = cli_consume_int (&cli_state); |
329 | check_usage (log_level >= 0 && log_level <= 3, |
330 | argv[0], "Error: invalid value for --log-level: " , cli_state.arg); |
331 | |
332 | jerry_port_default_set_log_level ((jerry_log_level_t) log_level); |
333 | break; |
334 | } |
335 | case OPT_NO_PROMPT: |
336 | { |
337 | arguments_p->option_flags |= OPT_FLAG_NO_PROMPT; |
338 | break; |
339 | } |
340 | case OPT_USE_STDIN: |
341 | { |
342 | arguments_p->option_flags |= OPT_FLAG_USE_STDIN; |
343 | break; |
344 | } |
345 | case CLI_OPT_DEFAULT: |
346 | { |
347 | main_source_t *source_p = arguments_p->sources_p + arguments_p->source_count; |
348 | arguments_p->source_count++; |
349 | |
350 | source_p->type = SOURCE_SCRIPT; |
351 | source_p->path_index = cli_consume_path (&cli_state); |
352 | break; |
353 | } |
354 | default: |
355 | { |
356 | cli_state.error = "Internal error" ; |
357 | break; |
358 | } |
359 | } |
360 | } |
361 | |
362 | if (cli_state.error != NULL) |
363 | { |
364 | if (cli_state.arg != NULL) |
365 | { |
366 | jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n" , cli_state.error, cli_state.arg); |
367 | } |
368 | else |
369 | { |
370 | jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n" , cli_state.error); |
371 | } |
372 | |
373 | exit (JERRY_STANDALONE_EXIT_CODE_FAIL); |
374 | } |
375 | } /* main_parse_args */ |
376 | |