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 <assert.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "jerryscript.h"
22#include "jerryscript-ext/debugger.h"
23#include "jerryscript-ext/handler.h"
24#include "jerryscript-port.h"
25#include "jerryscript-port-default.h"
26
27#include "main-utils.h"
28#include "main-options.h"
29
30/**
31 * Max line size that will be printed on a Syntax Error
32 */
33#define SYNTAX_ERROR_MAX_LINE_LENGTH 256
34
35/**
36 * Register a JavaScript function in the global object.
37 */
38static void
39main_register_global_function (const char *name_p, /**< name of the function */
40 jerry_external_handler_t handler_p) /**< function callback */
41{
42 jerry_value_t result_val = jerryx_handler_register_global ((const jerry_char_t *) name_p, handler_p);
43 assert (!jerry_value_is_error (result_val));
44 jerry_release_value (result_val);
45} /* main_register_global_function */
46
47static jerry_value_t
48main_create_realm (const jerry_value_t func_obj_val, /**< function object */
49 const jerry_value_t this_p, /**< this arg */
50 const jerry_value_t args_p[], /**< function arguments */
51 const jerry_length_t args_cnt) /**< number of function arguments */
52{
53 (void) func_obj_val; /* unused */
54 (void) this_p; /* unused */
55 (void) args_p; /* unused */
56 (void) args_cnt; /* unused */
57 return jerry_create_realm ();
58} /* main_create_realm */
59
60/**
61 * Register a method for the $262 object.
62 */
63static void
64test262_register_function (jerry_value_t test262_obj, /** $262 object */
65 const char *name_p, /**< name of the function */
66 jerry_external_handler_t handler_p) /**< function callback */
67{
68 jerry_value_t function_name_val = jerry_create_string ((const jerry_char_t *) name_p);
69 jerry_value_t function_val = jerry_create_external_function (handler_p);
70
71 jerry_value_t result_val = jerry_set_property (test262_obj, function_name_val, function_val);
72
73 jerry_release_value (function_val);
74 jerry_release_value (function_name_val);
75
76 assert (!jerry_value_is_error (result_val));
77 jerry_release_value (result_val);
78} /* test262_register_function */
79
80/**
81 * $262.detachArrayBuffer
82 *
83 * A function which implements the DetachArrayBuffer abstract operation
84 *
85 * @return null value - if success
86 * value marked with error flag - otherwise
87 */
88static jerry_value_t
89test262_detach_array_buffer (const jerry_value_t func_obj_val, /**< function object */
90 const jerry_value_t this_p, /**< this arg */
91 const jerry_value_t args_p[], /**< function arguments */
92 const jerry_length_t args_cnt) /**< number of function arguments */
93{
94 (void) func_obj_val; /* unused */
95 (void) this_p; /* unused */
96
97 if (args_cnt < 1 || !jerry_value_is_arraybuffer (args_p[0]))
98 {
99 return jerry_create_error (JERRY_ERROR_TYPE, (jerry_char_t *) "Expected an ArrayBuffer object");
100 }
101
102 /* TODO: support the optional 'key' argument */
103
104 return jerry_detach_arraybuffer (args_p[0]);
105} /* test262_detach_array_buffer */
106
107/**
108 * $262.evalScript
109 *
110 * A function which accepts a string value as its first argument and executes it
111 *
112 * @return completion of the script parsing and execution.
113 */
114static jerry_value_t
115test262_eval_script (const jerry_value_t func_obj_val, /**< function object */
116 const jerry_value_t this_p, /**< this arg */
117 const jerry_value_t args_p[], /**< function arguments */
118 const jerry_length_t args_cnt) /**< number of function arguments */
119{
120 (void) func_obj_val; /* unused */
121 (void) this_p; /* unused */
122
123 if (args_cnt < 1 || !jerry_value_is_string (args_p[0]))
124 {
125 return jerry_create_error (JERRY_ERROR_TYPE, (jerry_char_t *) "Expected a string");
126 }
127
128 jerry_size_t str_size = jerry_get_utf8_string_size (args_p[0]);
129 jerry_char_t *str_buf_p = malloc (str_size * sizeof (jerry_char_t));
130
131 if (str_buf_p == NULL || jerry_string_to_utf8_char_buffer (args_p[0], str_buf_p, str_size) != str_size)
132 {
133 free (str_buf_p);
134 return jerry_create_error (JERRY_ERROR_RANGE, (jerry_char_t *) "Internal error");
135 }
136
137 jerry_value_t ret_value = jerry_parse (NULL, 0, str_buf_p, str_size, JERRY_PARSE_NO_OPTS);
138
139 if (!jerry_value_is_error (ret_value))
140 {
141 jerry_value_t func_val = ret_value;
142 ret_value = jerry_run (func_val);
143 jerry_release_value (func_val);
144 }
145
146 free (str_buf_p);
147
148 return ret_value;
149} /* test262_eval_script */
150
151static jerry_value_t
152create_test262 (jerry_value_t global_obj);
153
154/**
155 * $262.createRealm
156 *
157 * A function which creates a new realm object, and returns a newly created $262 object
158 *
159 * @return a new $262 object
160 */
161static jerry_value_t
162test262_create_realm (const jerry_value_t func_obj_val, /**< function object */
163 const jerry_value_t this_p, /**< this arg */
164 const jerry_value_t args_p[], /**< function arguments */
165 const jerry_length_t args_cnt) /**< number of function arguments */
166{
167 (void) func_obj_val; /* unused */
168 (void) this_p; /* unused */
169 (void) args_p; /* unused */
170 (void) args_cnt; /* unused */
171
172 jerry_value_t realm_object = jerry_create_realm ();
173 jerry_value_t previous_realm = jerry_set_realm (realm_object);
174 assert (!jerry_value_is_error (previous_realm));
175 jerry_value_t test262_object = create_test262 (realm_object);
176 jerry_set_realm (previous_realm);
177 jerry_release_value (realm_object);
178
179 return test262_object;
180} /* test262_create_realm */
181
182/**
183 * Create a new $262 object
184 *
185 * @return a new $262 object
186 */
187static jerry_value_t
188create_test262 (jerry_value_t global_obj) /**< global object */
189{
190 jerry_value_t test262_object = jerry_create_object ();
191
192 test262_register_function (test262_object, "detachArrayBuffer", test262_detach_array_buffer);
193 test262_register_function (test262_object, "evalScript", test262_eval_script);
194 test262_register_function (test262_object, "createRealm", test262_create_realm);
195 test262_register_function (test262_object, "gc", jerryx_handler_gc);
196
197 jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "global");
198 jerry_value_t result = jerry_set_property (test262_object, prop_name, global_obj);
199 assert (!jerry_value_is_error (result));
200 jerry_release_value (prop_name);
201 jerry_release_value (result);
202 prop_name = jerry_create_string ((const jerry_char_t *) "$262");
203 result = jerry_set_property (global_obj, prop_name, test262_object);
204
205 jerry_release_value (prop_name);
206 assert (!jerry_value_is_error (result));
207 jerry_release_value (result);
208
209 return test262_object;
210} /* create_test262 */
211
212/**
213 * Inits the engine and the debugger
214 */
215void
216main_init_engine (main_args_t *arguments_p) /**< main arguments */
217{
218 jerry_init (arguments_p->init_flags);
219
220 if (arguments_p->option_flags & OPT_FLAG_DEBUG_SERVER)
221 {
222 bool protocol = false;
223
224 if (!strcmp (arguments_p->debug_protocol, "tcp"))
225 {
226 protocol = jerryx_debugger_tcp_create (arguments_p->debug_port);
227 }
228 else
229 {
230 assert (!strcmp (arguments_p->debug_protocol, "serial"));
231 protocol = jerryx_debugger_serial_create (arguments_p->debug_serial_config);
232 }
233
234 if (!strcmp (arguments_p->debug_channel, "rawpacket"))
235 {
236 jerryx_debugger_after_connect (protocol && jerryx_debugger_rp_create ());
237 }
238 else
239 {
240 assert (!strcmp (arguments_p->debug_channel, "websocket"));
241 jerryx_debugger_after_connect (protocol && jerryx_debugger_ws_create ());
242 }
243 }
244 if (arguments_p->option_flags & OPT_FLAG_TEST262_OBJECT)
245 {
246 jerry_value_t global_obj = jerry_get_global_object ();
247 jerry_value_t test262_object = create_test262 (global_obj);
248 jerry_release_value (test262_object);
249 jerry_release_value (global_obj);
250 }
251 main_register_global_function ("assert", jerryx_handler_assert);
252 main_register_global_function ("gc", jerryx_handler_gc);
253 main_register_global_function ("print", jerryx_handler_print);
254 main_register_global_function ("resourceName", jerryx_handler_resource_name);
255 main_register_global_function ("createRealm", main_create_realm);
256} /* main_init_engine */
257
258/**
259 * Print an error value.
260 *
261 * Note: the error value will be released.
262 */
263void
264main_print_unhandled_exception (jerry_value_t error_value) /**< error value */
265{
266 assert (jerry_value_is_error (error_value));
267 error_value = jerry_get_value_from_error (error_value, true);
268
269 jerry_char_t err_str_buf[256];
270
271 jerry_value_t err_str_val = jerry_value_to_string (error_value);
272 jerry_size_t err_str_size = jerry_get_utf8_string_size (err_str_val);
273
274 if (err_str_size >= 256)
275 {
276 const char msg[] = "[Error message too long]";
277 err_str_size = sizeof (msg) / sizeof (char) - 1;
278 memcpy (err_str_buf, msg, err_str_size + 1);
279 }
280 else
281 {
282 jerry_size_t string_end = jerry_string_to_utf8_char_buffer (err_str_val, err_str_buf, err_str_size);
283 assert (string_end == err_str_size);
284 err_str_buf[string_end] = 0;
285
286 if (jerry_is_feature_enabled (JERRY_FEATURE_ERROR_MESSAGES)
287 && jerry_get_error_type (error_value) == JERRY_ERROR_SYNTAX)
288 {
289 jerry_char_t *string_end_p = err_str_buf + string_end;
290 unsigned int err_line = 0;
291 unsigned int err_col = 0;
292 char *path_str_p = NULL;
293 char *path_str_end_p = NULL;
294
295 /* 1. parse column and line information */
296 for (jerry_char_t *current_p = err_str_buf; current_p < string_end_p; current_p++)
297 {
298 if (*current_p == '[')
299 {
300 current_p++;
301
302 if (*current_p == '<')
303 {
304 break;
305 }
306
307 path_str_p = (char *) current_p;
308 while (current_p < string_end_p && *current_p != ':')
309 {
310 current_p++;
311 }
312
313 path_str_end_p = (char *) current_p++;
314
315 err_line = (unsigned int) strtol ((char *) current_p, (char **) &current_p, 10);
316
317 current_p++;
318
319 err_col = (unsigned int) strtol ((char *) current_p, NULL, 10);
320 break;
321 }
322 } /* for */
323
324 if (err_line != 0 && err_col > 0 && err_col < SYNTAX_ERROR_MAX_LINE_LENGTH)
325 {
326 /* Temporarily modify the error message, so we can use the path. */
327 *path_str_end_p = '\0';
328
329 size_t source_size;
330 uint8_t *source_p = jerry_port_read_source (path_str_p, &source_size);
331
332 /* Revert the error message. */
333 *path_str_end_p = ':';
334
335 if (source_p != NULL)
336 {
337 uint32_t curr_line = 1;
338 uint32_t pos = 0;
339
340 /* 2. seek and print */
341 while (pos < source_size && curr_line < err_line)
342 {
343 if (source_p[pos] == '\n')
344 {
345 curr_line++;
346 }
347
348 pos++;
349 }
350
351 /* Print character if:
352 * - The max line length is not reached.
353 * - The current position is valid (it is not the end of the source).
354 * - The current character is not a newline.
355 **/
356 for (uint32_t char_count = 0;
357 (char_count < SYNTAX_ERROR_MAX_LINE_LENGTH) && (pos < source_size) && (source_p[pos] != '\n');
358 char_count++, pos++)
359 {
360 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%c", source_p[pos]);
361 }
362 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "\n");
363
364 jerry_port_release_source (source_p);
365
366 while (--err_col)
367 {
368 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "~");
369 }
370
371 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "^\n\n");
372 }
373 }
374 }
375 }
376
377 jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s\n", err_str_buf);
378 jerry_release_value (err_str_val);
379
380 if (jerry_value_is_object (error_value))
381 {
382 jerry_value_t stack_str = jerry_create_string ((const jerry_char_t *) "stack");
383 jerry_value_t backtrace_val = jerry_get_property (error_value, stack_str);
384 jerry_release_value (stack_str);
385
386 if (jerry_value_is_array (backtrace_val))
387 {
388 uint32_t length = jerry_get_array_length (backtrace_val);
389
390 /* This length should be enough. */
391 if (length > 32)
392 {
393 length = 32;
394 }
395
396 for (uint32_t i = 0; i < length; i++)
397 {
398 jerry_value_t item_val = jerry_get_property_by_index (backtrace_val, i);
399
400 if (jerry_value_is_string (item_val))
401 {
402 jerry_size_t str_size = jerry_get_utf8_string_size (item_val);
403
404 if (str_size >= 256)
405 {
406 printf ("%6u: [Backtrace string too long]\n", i);
407 }
408 else
409 {
410 jerry_size_t string_end = jerry_string_to_utf8_char_buffer (item_val, err_str_buf, str_size);
411 assert (string_end == str_size);
412 err_str_buf[string_end] = 0;
413
414 printf ("%6u: %s\n", i, err_str_buf);
415 }
416 }
417
418 jerry_release_value (item_val);
419 }
420 }
421
422 jerry_release_value (backtrace_val);
423 }
424
425 jerry_release_value (error_value);
426} /* main_print_unhandled_exception */
427
428/**
429 * Runs the source code received by jerry_debugger_wait_for_client_source.
430 *
431 * @return result fo the source code execution
432 */
433jerry_value_t
434main_wait_for_source_callback (const jerry_char_t *resource_name_p, /**< resource name */
435 size_t resource_name_size, /**< size of resource name */
436 const jerry_char_t *source_p, /**< source code */
437 size_t source_size, /**< source code size */
438 void *user_p) /**< user pointer */
439{
440 (void) user_p; /* unused */
441 jerry_value_t ret_val = jerry_parse (resource_name_p,
442 resource_name_size,
443 source_p,
444 source_size,
445 JERRY_PARSE_NO_OPTS);
446
447 if (!jerry_value_is_error (ret_val))
448 {
449 jerry_value_t func_val = ret_val;
450 ret_val = jerry_run (func_val);
451 jerry_release_value (func_val);
452 }
453
454 return ret_val;
455} /* main_wait_for_source_callback */
456
457/**
458 * Check that value contains the reset abort value.
459 *
460 * Note: if the value is the reset abort value, the value is release.
461 *
462 * return true, if reset abort
463 * false, otherwise
464 */
465bool
466main_is_value_reset (jerry_value_t value) /**< jerry value */
467{
468 if (!jerry_value_is_abort (value))
469 {
470 return false;
471 }
472
473 jerry_value_t abort_value = jerry_get_value_from_error (value, false);
474
475 if (!jerry_value_is_string (abort_value))
476 {
477 jerry_release_value (abort_value);
478 return false;
479 }
480
481 static const char restart_str[] = "r353t";
482
483 jerry_size_t str_size = jerry_get_string_size (abort_value);
484 bool is_reset = false;
485
486 if (str_size == sizeof (restart_str) - 1)
487 {
488 JERRY_VLA (jerry_char_t, str_buf, str_size);
489 jerry_string_to_char_buffer (abort_value, str_buf, str_size);
490
491 is_reset = memcmp (restart_str, (char *) (str_buf), str_size) == 0;
492
493 if (is_reset)
494 {
495 jerry_release_value (value);
496 }
497 }
498
499 jerry_release_value (abort_value);
500 return is_reset;
501} /* main_is_value_reset */
502