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 */
30typedef 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 */
57static 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 */
106static void
107check_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 */
125static bool
126check_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 */
141void
142main_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