Add line info support. (#2286)

Add line info data to byte, which allows getting a backtrace info directly
from the engine. Snapshots are not supported.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2018-04-19 02:12:54 +02:00 committed by yichoi
parent 095b730f9d
commit 5e097dc354
24 changed files with 773 additions and 52 deletions

View File

@ -58,6 +58,7 @@ Possible compile time enabled feature types:
- JERRY_FEATURE_TYPEDARRAY - Typedarray support
- JERRY_FEATURE_DATE - Date support
- JERRY_FEATURE_REGEXP - RegExp support
- JERRY_FEATURE_LINE_INFO - line info available
## jerry_parse_opts_t
@ -5434,6 +5435,31 @@ main (void)
- [jerry_run](#jerry_run)
- [jerry_vm_exec_stop_callback_t](#jerry_vm_exec_stop_callback_t)
## jerry_get_backtrace
**Summary**
Get backtrace. The backtrace is an array of strings where
each string contains the position of the corresponding frame.
The array length is zero if the backtrace is not available.
This function is typically called from native callbacks.
**Prototype**
```c
jerry_value_t
jerry_get_backtrace (uint32_t max_depth);
```
- `max_depth` - backtrace collection stops after reaching this value, 0 = unlimited
- return value
- a new array
**See also**
- [jerry_create_external_function](#jerry_create_external_function)
# ArrayBuffer and TypedArray functions

View File

@ -22,6 +22,7 @@ set(FEATURE_DEBUGGER OFF CACHE BOOL "Enable JerryScript debugger
set(FEATURE_ERROR_MESSAGES OFF CACHE BOOL "Enable error messages?")
set(FEATURE_EXTERNAL_CONTEXT OFF CACHE BOOL "Enable external context?")
set(FEATURE_JS_PARSER ON CACHE BOOL "Enable js-parser?")
set(FEATURE_LINE_INFO OFF CACHE BOOL "Enable line info?")
set(FEATURE_MEM_STATS OFF CACHE BOOL "Enable memory statistics?")
set(FEATURE_MEM_STRESS_TEST OFF CACHE BOOL "Enable mem-stress test?")
set(FEATURE_PARSER_DUMP OFF CACHE BOOL "Enable parser byte-code dumps?")
@ -63,6 +64,7 @@ message(STATUS "FEATURE_DEBUGGER " ${FEATURE_DEBUGGER})
message(STATUS "FEATURE_ERROR_MESSAGES " ${FEATURE_ERROR_MESSAGES})
message(STATUS "FEATURE_EXTERNAL_CONTEXT " ${FEATURE_EXTERNAL_CONTEXT})
message(STATUS "FEATURE_JS_PARSER " ${FEATURE_JS_PARSER})
message(STATUS "FEATURE_LINE_INFO " ${FEATURE_LINE_INFO})
message(STATUS "FEATURE_MEM_STATS " ${FEATURE_MEM_STATS})
message(STATUS "FEATURE_MEM_STRESS_TEST " ${FEATURE_MEM_STRESS_TEST})
message(STATUS "FEATURE_PARSER_DUMP " ${FEATURE_PARSER_DUMP} ${FEATURE_PARSER_DUMP_MESSAGE})
@ -174,6 +176,11 @@ if(NOT FEATURE_JS_PARSER)
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_DISABLE_JS_PARSER)
endif()
# JS line info
if(FEATURE_LINE_INFO)
set(DEFINES_JERRY ${DEFINES_JERRY} JERRY_ENABLE_LINE_INFO)
endif()
# Memory statistics
if(FEATURE_MEM_STATS)
set(DEFINES_JERRY ${DEFINES_JERRY} JMEM_STATS)

View File

@ -726,6 +726,10 @@ jerry_generate_snapshot_with_args (const jerry_char_t *resource_name_p, /**< scr
JERRY_UNUSED (resource_name_p);
JERRY_UNUSED (resource_name_length);
#ifdef JERRY_ENABLE_LINE_INFO
JERRY_CONTEXT (resource_name) = ECMA_VALUE_UNDEFINED;
#endif /* JERRY_ENABLE_LINE_INFO */
snapshot_globals_t globals;
ecma_value_t parse_status;
ecma_compiled_code_t *bytecode_data_p;
@ -1579,6 +1583,11 @@ jerry_parse_and_save_literals (const jerry_char_t *source_p, /**< script source
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
ecma_value_t parse_status;
ecma_compiled_code_t *bytecode_data_p;
#ifdef JERRY_ENABLE_LINE_INFO
JERRY_CONTEXT (resource_name) = ECMA_VALUE_UNDEFINED;
#endif /* JERRY_ENABLE_LINE_INFO */
parse_status = parser_parse_script (NULL,
0,
source_p,

View File

@ -41,7 +41,7 @@ typedef struct
/**
* Jerry snapshot format version.
*/
#define JERRY_SNAPSHOT_VERSION (11u)
#define JERRY_SNAPSHOT_VERSION (12u)
/**
* Snapshot configuration flags.

View File

@ -415,6 +415,11 @@ jerry_parse (const jerry_char_t *resource_name_p, /**< resource name (usually a
#ifndef JERRY_DISABLE_JS_PARSER
jerry_assert_api_available ();
#if defined JERRY_ENABLE_LINE_INFO && !defined JERRY_DISABLE_JS_PARSER
JERRY_CONTEXT (resource_name) = ecma_find_or_create_literal_string (resource_name_p,
(lit_utf8_size_t) resource_name_length);
#endif /* JERRY_ENABLE_LINE_INFO && !JERRY_DISABLE_JS_PARSER */
ecma_compiled_code_t *bytecode_data_p;
ecma_value_t parse_status;
@ -482,6 +487,11 @@ jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (u
ecma_compiled_code_t *bytecode_data_p;
ecma_value_t parse_status;
#ifdef JERRY_ENABLE_LINE_INFO
JERRY_CONTEXT (resource_name) = ecma_find_or_create_literal_string (resource_name_p,
(lit_utf8_size_t) resource_name_length);
#endif /* JERRY_ENABLE_LINE_INFO */
if (arg_list_p == NULL)
{
/* Must not be a NULL value. */
@ -874,6 +884,9 @@ bool jerry_is_feature_enabled (const jerry_feature_t feature)
#ifndef CONFIG_DISABLE_REGEXP_BUILTIN
|| feature == JERRY_FEATURE_REGEXP
#endif /* !CONFIG_DISABLE_REGEXP_BUILTIN */
#ifdef JERRY_ENABLE_LINE_INFO
|| feature == JERRY_FEATURE_LINE_INFO
#endif /* JERRY_ENABLE_LINE_INFO */
);
} /* jerry_is_feature_enabled */
@ -2845,6 +2858,18 @@ jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, /**< per
#endif /* JERRY_VM_EXEC_STOP */
} /* jerry_set_vm_exec_stop_callback */
/**
* Get backtrace. The backtrace is an array of strings where
* each string contains the position of the corresponding frame.
* The array length is zero if the backtrace is not available.
*
* @return array value
*/
jerry_value_t
jerry_get_backtrace (uint32_t max_depth)
{
return vm_get_backtrace (max_depth);
} /* jerry_get_backtrace */
/**
* Check if the given value is an ArrayBuffer object.

View File

@ -21,8 +21,12 @@
#include "ecma-function-object.h"
#include "ecma-lex-env.h"
#include "ecma-try-catch-macro.h"
#include "lit-magic-strings.h"
#include "js-parser.h"
#include "lit-magic-strings.h"
#ifdef JERRY_ENABLE_LINE_INFO
#include "jcontext.h"
#endif /* JERRY_ENABLE_LINE_INFO */
#define ECMA_BUILTINS_INTERNAL
#include "ecma-builtins-internal.h"
@ -155,6 +159,10 @@ ecma_builtin_function_dispatch_construct (const ecma_value_t *arguments_list_p,
ECMA_STRING_TO_UTF8_STRING (arguments_str_p, arguments_buffer_p, arguments_buffer_size);
ECMA_STRING_TO_UTF8_STRING (function_body_str_p, function_body_buffer_p, function_body_buffer_size);
#ifdef JERRY_ENABLE_LINE_INFO
JERRY_CONTEXT (resource_name) = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);
#endif /* JERRY_ENABLE_LINE_INFO */
ecma_compiled_code_t *bytecode_data_p = NULL;
ecma_value_t ret_value = parser_parse_script (arguments_buffer_p,

View File

@ -23,6 +23,10 @@
#include "js-parser.h"
#include "vm.h"
#ifdef JERRY_ENABLE_LINE_INFO
#include "jcontext.h"
#endif /* JERRY_ENABLE_LINE_INFO */
/** \addtogroup ecma ECMA
* @{
*
@ -88,6 +92,10 @@ ecma_op_eval_chars_buffer (const lit_utf8_byte_t *code_p, /**< code characters b
bool is_strict_call = (is_direct && is_called_from_strict_mode_code);
#ifdef JERRY_ENABLE_LINE_INFO
JERRY_CONTEXT (resource_name) = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);
#endif /* JERRY_ENABLE_LINE_INFO */
ecma_value_t parse_status = parser_parse_script (NULL,
0,
code_p,

View File

@ -24,6 +24,10 @@
#include "jcontext.h"
#include "jrt.h"
#ifdef JERRY_ENABLE_LINE_INFO
#include "vm.h"
#endif /* JERRY_ENABLE_LINE_INFO */
/** \addtogroup ecma ECMA
* @{
*
@ -134,6 +138,24 @@ ecma_new_standard_error (ecma_standard_error_t error_type) /**< native error typ
((ecma_extended_object_t *) new_error_obj_p)->u.class_prop.class_id = LIT_MAGIC_STRING_ERROR_UL;
#ifdef JERRY_ENABLE_LINE_INFO
/* The "stack" identifier is not a magic string. */
const char *stack_id_p = "stack";
ecma_string_t *stack_str_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) stack_id_p, 5);
ecma_property_value_t *prop_value_p = ecma_create_named_data_property (new_error_obj_p,
stack_str_p,
ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
NULL);
ecma_deref_ecma_string (stack_str_p);
ecma_value_t backtrace_value = vm_get_backtrace (0);
prop_value_p->value = backtrace_value;
ecma_deref_object (ecma_get_object_from_value (backtrace_value));
#endif /* JERRY_ENABLE_LINE_INFO */
return new_error_obj_p;
} /* ecma_new_standard_error */

View File

@ -95,6 +95,7 @@ typedef enum
JERRY_FEATURE_TYPEDARRAY, /**< Typedarray support */
JERRY_FEATURE_DATE, /**< Date support */
JERRY_FEATURE_REGEXP, /**< Regexp support */
JERRY_FEATURE_LINE_INFO, /**< line info available */
JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */
} jerry_feature_t;
@ -465,7 +466,7 @@ bool jerry_delete_property (const jerry_value_t obj_val, const jerry_value_t pro
bool jerry_delete_property_by_index (const jerry_value_t obj_val, uint32_t index);
jerry_value_t jerry_get_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val);
jerry_value_t jerry_get_property_by_index (const jerry_value_t obj_val, uint32_t index);
jerry_value_t jerry_get_property_by_index (const jerry_value_t obj_val, uint32_t index);
jerry_value_t jerry_set_property (const jerry_value_t obj_val, const jerry_value_t prop_name_val,
const jerry_value_t value_to_set);
jerry_value_t jerry_set_property_by_index (const jerry_value_t obj_val, uint32_t index,
@ -531,6 +532,7 @@ jerry_instance_t *jerry_create_instance (uint32_t heap_size, jerry_instance_allo
* Miscellaneous functions.
*/
void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency);
jerry_value_t jerry_get_backtrace (uint32_t max_depth);
/**
* Array buffer components.

View File

@ -124,6 +124,10 @@ typedef struct
int debugger_connection; /**< holds the file descriptor of the socket communication */
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
ecma_value_t resource_name; /**< resource name (usually a file name) */
#endif /* JERRY_ENABLE_LINE_INFO */
#ifdef JMEM_STATS
jmem_heap_stats_t jmem_heap_stats; /**< heap's memory usage statistics */
#endif /* JMEM_STATS */

View File

@ -539,6 +539,10 @@
/* Basic opcodes. */ \
CBC_OPCODE (CBC_EXT_DEBUGGER, CBC_NO_FLAG, 0, \
VM_OC_NONE) \
CBC_OPCODE (CBC_EXT_RESOURCE_NAME, CBC_NO_FLAG, 0, \
VM_OC_RESOURCE_NAME) \
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
VM_OC_LINE) \
\
/* Binary compound assignment opcodes with pushing the result. */ \
CBC_EXT_BINARY_LVALUE_OPERATION (CBC_EXT_ASSIGN_ADD, \

View File

@ -308,8 +308,12 @@ typedef struct
/** extra data for each breakpoint */
parser_breakpoint_info_t breakpoint_info[JERRY_DEBUGGER_MAX_BUFFER_SIZE / sizeof (parser_breakpoint_info_t)];
uint16_t breakpoint_info_count; /**< current breakpoint index */
parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint was inserted */
parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint has been inserted */
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
parser_line_counter_t last_line_info_line; /**< last line where line info has been inserted */
#endif /* JERRY_ENABLE_LINE_INFO */
} parser_context_t;
/**
@ -485,6 +489,12 @@ void parser_send_breakpoints (parser_context_t *context_p, jerry_debugger_header
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
void parser_emit_line_info (parser_context_t *context_p, uint32_t line, bool flush_cbc);
#endif /* JERRY_ENABLE_LINE_INFO */
/**
* @}
* @}

View File

@ -17,9 +17,9 @@
#ifndef JERRY_DISABLE_JS_PARSER
#ifdef JERRY_DEBUGGER
#if defined (JERRY_DEBUGGER) || defined (JERRY_ENABLE_LINE_INFO)
#include "jcontext.h"
#endif /*JERRY_DEBUGGER */
#endif /* JERRY_DEBUGGER || JERRY_ENABLE_LINE_INFO */
/** \addtogroup parser Parser
* @{
@ -314,9 +314,9 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
#ifdef JERRY_DEBUGGER
#if defined (JERRY_DEBUGGER) || defined (JERRY_ENABLE_LINE_INFO)
parser_line_counter_t ident_line_counter = context_p->token.line;
#endif /* JERRY_DEBUGGER */
#endif /* JERRY_DEBUGGER || JERRY_ENABLE_LINE_INFO */
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR;
@ -347,6 +347,13 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
if (ident_line_counter != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, ident_line_counter, false);
}
#endif /* JERRY_ENABLE_LINE_INFO */
parser_parse_expression (context_p,
PARSE_EXPR_STATEMENT | PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL);
}
@ -1080,6 +1087,10 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context *
switch_case_was_found = false;
default_case_was_found = false;
#ifdef JERRY_ENABLE_LINE_INFO
uint32_t last_line_info_line = context_p->last_line_info_line;
#endif /* JERRY_ENABLE_LINE_INFO */
while (true)
{
parser_scan_until (context_p, &unused_range, LEXER_KEYW_CASE);
@ -1134,6 +1145,13 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context *
lexer_next_token (context_p);
#ifdef JERRY_ENABLE_LINE_INFO
if (context_p->token.line != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, context_p->token.line, true);
}
#endif /* JERRY_ENABLE_LINE_INFO */
parser_parse_expression (context_p, PARSE_EXPR);
if (context_p->token.type != LEXER_COLON)
@ -1148,6 +1166,10 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context *
JERRY_ASSERT (switch_case_was_found || default_case_was_found);
#ifdef JERRY_ENABLE_LINE_INFO
context_p->last_line_info_line = last_line_info_line;
#endif /* JERRY_ENABLE_LINE_INFO */
if (!switch_case_was_found)
{
/* There was no case statement, so the expression result
@ -1634,6 +1656,15 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
parser_emit_cbc_ext (context_p, CBC_EXT_RESOURCE_NAME);
parser_flush_cbc (context_p);
}
context_p->last_line_info_line = 0;
#endif /* JERRY_ENABLE_LINE_INFO */
while (context_p->token.type == LEXER_LITERAL
&& context_p->token.lit_location.type == LEXER_STRING_LITERAL)
{
@ -1685,6 +1716,9 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
context_p->last_breakpoint_line = context_p->token.line;
}
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
parser_emit_line_info (context_p, context_p->token.line, false);
#endif /* JERRY_ENABLE_LINE_INFO */
lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL);
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
@ -1755,6 +1789,20 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
if (context_p->token.line != context_p->last_line_info_line
&& context_p->token.type != LEXER_SEMICOLON
&& context_p->token.type != LEXER_LEFT_BRACE
&& context_p->token.type != LEXER_RIGHT_BRACE
&& context_p->token.type != LEXER_KEYW_VAR
&& context_p->token.type != LEXER_KEYW_FUNCTION
&& context_p->token.type != LEXER_KEYW_CASE
&& context_p->token.type != LEXER_KEYW_DEFAULT)
{
parser_emit_line_info (context_p, context_p->token.line, true);
}
#endif /* JERRY_ENABLE_LINE_INFO */
switch (context_p->token.type)
{
case LEXER_SEMICOLON:

View File

@ -17,6 +17,10 @@
#ifndef JERRY_DISABLE_JS_PARSER
#ifdef JERRY_ENABLE_LINE_INFO
#include "jcontext.h"
#endif /* JERRY_ENABLE_LINE_INFO */
/** \addtogroup parser Parser
* @{
*
@ -352,6 +356,57 @@ parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */
}
} /* parser_emit_cbc_push_number */
#ifdef JERRY_ENABLE_LINE_INFO
/**
* Append a line info data
*/
void
parser_emit_line_info (parser_context_t *context_p, /**< context */
uint32_t line, /**< current line */
bool flush_cbc) /**< flush last byte code */
{
if (JERRY_CONTEXT (resource_name) == ECMA_VALUE_UNDEFINED)
{
return;
}
if (flush_cbc && context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE)
{
parser_flush_cbc (context_p);
}
#ifdef PARSER_DUMP_BYTE_CODE
if (context_p->is_show_opcodes)
{
JERRY_DEBUG_MSG (" [%3d] CBC_EXT_LINE %d\n", (int) context_p->stack_depth, line);
}
#endif /* PARSER_DUMP_BYTE_CODE */
parser_emit_two_bytes (context_p, CBC_EXT_OPCODE, CBC_EXT_LINE);
context_p->byte_code_size += 2;
context_p->last_line_info_line = line;
do
{
uint8_t byte = (uint8_t) (line & CBC_LOWER_SEVEN_BIT_MASK);
line >>= 7;
if (line > 0)
{
byte = (uint8_t) (byte | CBC_HIGHEST_BIT_MASK);
}
PARSER_APPEND_TO_BYTE_CODE (context_p, byte);
context_p->byte_code_size++;
}
while (line > 0);
} /* parser_emit_line_info */
#endif /* JERRY_ENABLE_LINE_INFO */
/**
* Append a byte code with a branch argument
*/

View File

@ -1292,6 +1292,24 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code
flags = cbc_ext_flags[ext_opcode];
JERRY_DEBUG_MSG (" %3d : %s", (int) cbc_offset, cbc_ext_names[ext_opcode]);
byte_code_p += 2;
#ifdef JERRY_ENABLE_LINE_INFO
if (ext_opcode == CBC_EXT_LINE)
{
uint32_t value = 0;
uint8_t byte;
do
{
byte = *byte_code_p++;
value = (value << 7) | (byte & CBC_LOWER_SEVEN_BIT_MASK);
}
while (byte & CBC_HIGHEST_BIT_MASK);
JERRY_DEBUG_MSG (" %d\n", (int) value);
continue;
}
#endif /* JERRY_ENABLE_LINE_INFO */
}
if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))
@ -1488,6 +1506,23 @@ parser_post_processing (parser_context_t *context_p) /**< context */
flags = cbc_ext_flags[ext_opcode];
PARSER_NEXT_BYTE (page_p, offset);
length++;
#ifdef JERRY_ENABLE_LINE_INFO
if (ext_opcode == CBC_EXT_LINE)
{
uint8_t last_byte = 0;
do
{
last_byte = page_p->bytes[offset];
PARSER_NEXT_BYTE (page_p, offset);
length++;
}
while (last_byte & CBC_HIGHEST_BIT_MASK);
continue;
}
#endif /* JERRY_ENABLE_LINE_INFO */
}
while (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))
@ -1639,6 +1674,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
total_size += context_p->argument_count * sizeof (ecma_value_t);
}
#ifdef JERRY_ENABLE_LINE_INFO
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
total_size += sizeof (ecma_value_t);
}
#endif /* JERRY_ENABLE_LINE_INFO */
#ifdef JERRY_ENABLE_SNAPSHOT_SAVE
total_size_used = total_size;
#endif
@ -1802,6 +1844,25 @@ parser_post_processing (parser_context_t *context_p) /**< context */
opcode_p++;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
#ifdef JERRY_ENABLE_LINE_INFO
if (ext_opcode == CBC_EXT_LINE)
{
uint8_t last_byte = 0;
do
{
last_byte = page_p->bytes[offset];
*dst_p++ = last_byte;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
}
while (last_byte & CBC_HIGHEST_BIT_MASK);
continue;
}
#endif /* JERRY_ENABLE_LINE_INFO */
}
if (flags & CBC_HAS_BRANCH_ARG)
@ -1994,6 +2055,21 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
}
#ifdef JERRY_ENABLE_LINE_INFO
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
ecma_value_t *resource_name_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
if ((context_p->status_flags & PARSER_ARGUMENTS_NEEDED)
&& !(context_p->status_flags & PARSER_IS_STRICT))
{
resource_name_p -= context_p->argument_count;
}
resource_name_p[-1] = JERRY_CONTEXT (resource_name);
}
#endif /* JERRY_ENABLE_LINE_INFO */
if (context_p->status_flags & PARSER_NAMED_FUNCTION_EXP)
{
ECMA_SET_INTERNAL_VALUE_POINTER (literal_pool_p[const_literal_end],

View File

@ -51,6 +51,10 @@ typedef struct vm_frame_ctx_t
struct vm_frame_ctx_t *prev_context_p; /**< previous context */
ecma_value_t this_binding; /**< this binding */
ecma_value_t call_block_result; /**< preserve block result during a call */
#ifdef JERRY_ENABLE_LINE_INFO
ecma_value_t resource_name; /**< current resource name (usually a file name) */
uint32_t current_line; /**< currently executed line */
#endif /* JERRY_ENABLE_LINE_INFO */
uint16_t context_depth; /**< current context depth */
uint8_t is_eval_code; /**< eval mode flag */
uint8_t call_operation; /**< perform a call or construct operation */

133
jerry-core/vm/vm-utils.c Normal file
View File

@ -0,0 +1,133 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecma-array-object.h"
#include "ecma-helpers.h"
#include "jcontext.h"
#include "vm.h"
/**
* Check whether currently executed code is strict mode code
*
* @return true - current code is executed in strict mode,
* false - otherwise
*/
bool
vm_is_strict_mode (void)
{
JERRY_ASSERT (JERRY_CONTEXT (vm_top_context_p) != NULL);
return JERRY_CONTEXT (vm_top_context_p)->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE;
} /* vm_is_strict_mode */
/**
* Check whether currently performed call (on top of call-stack) is performed in form,
* meeting conditions of 'Direct Call to Eval' (see also: ECMA-262 v5, 15.1.2.1.1)
*
* Warning:
* the function should only be called from implementation
* of built-in 'eval' routine of Global object
*
* @return true - currently performed call is performed through 'eval' identifier,
* without 'this' argument,
* false - otherwise
*/
inline bool __attr_always_inline___
vm_is_direct_eval_form_call (void)
{
return (JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL) != 0;
} /* vm_is_direct_eval_form_call */
/**
* Get backtrace. The backtrace is an array of strings where
* each string contains the position of the corresponding frame.
* The array length is zero if the backtrace is not available.
*
* @return array ecma value
*/
ecma_value_t
vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimited */
{
#ifdef JERRY_ENABLE_LINE_INFO
ecma_value_t result_array = ecma_op_create_array_object (NULL, 0, false);
if (max_depth == 0)
{
max_depth = UINT32_MAX;
}
vm_frame_ctx_t *context_p = JERRY_CONTEXT (vm_top_context_p);
ecma_object_t *array_p = ecma_get_object_from_value (result_array);
uint32_t index = 0;
while (context_p != NULL)
{
if (context_p->resource_name == ECMA_VALUE_UNDEFINED)
{
context_p = context_p->prev_context_p;
continue;
}
ecma_string_t *str_p = ecma_get_string_from_value (context_p->resource_name);
if (ecma_string_is_empty (str_p))
{
const char *unknown_str_p = "<unknown>:";
str_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) unknown_str_p,
(lit_utf8_size_t) strlen (unknown_str_p));
}
else
{
ecma_ref_ecma_string (str_p);
str_p = ecma_append_magic_string_to_string (str_p, LIT_MAGIC_STRING_COLON_CHAR);
}
ecma_string_t *line_str_p = ecma_new_ecma_string_from_uint32 (context_p->current_line);
str_p = ecma_concat_ecma_strings (str_p, line_str_p);
ecma_deref_ecma_string (line_str_p);
ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index);
ecma_property_value_t *prop_value_p;
prop_value_p = ecma_create_named_data_property (array_p,
index_str_p,
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
NULL);
ecma_deref_ecma_string (index_str_p);
prop_value_p->value = ecma_make_string_value (str_p);
context_p = context_p->prev_context_p;
index++;
if (index >= max_depth)
{
break;
}
}
if (index > 0)
{
JERRY_ASSERT (ecma_get_object_type (array_p) == ECMA_OBJECT_TYPE_ARRAY);
((ecma_extended_object_t *) array_p)->u.array.length = index;
}
return result_array;
#else /* !JERRY_ENABLE_LINE_INFO */
JERRY_UNUSED (max_depth);
return ecma_op_create_array_object (NULL, 0, false);
#endif /* JERRY_ENABLE_LINE_INFO */
} /* vm_get_backtrace */

View File

@ -2596,6 +2596,52 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
#endif /* JERRY_DEBUGGER */
continue;
}
#ifdef JERRY_ENABLE_LINE_INFO
case VM_OC_RESOURCE_NAME:
{
ecma_length_t formal_params_number = 0;
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_NON_STRICT_ARGUMENTS_NEEDED)
{
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
formal_params_number = args_p->argument_end;
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
formal_params_number = args_p->argument_end;
}
}
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
byte_p += ((size_t) bytecode_header_p->size) << JMEM_ALIGNMENT_LOG;
ecma_value_t *resource_name_p = (ecma_value_t *) byte_p;
resource_name_p -= formal_params_number;
frame_ctx_p->resource_name = resource_name_p[-1];
continue;
}
case VM_OC_LINE:
{
uint32_t value = 0;
uint8_t byte;
do
{
byte = *byte_code_p++;
value = (value << 7) | (byte & CBC_LOWER_SEVEN_BIT_MASK);
}
while (byte & CBC_HIGHEST_BIT_MASK);
frame_ctx_p->current_line = value;
continue;
}
#endif /* JERRY_ENABLE_LINE_INFO */
default:
{
JERRY_UNREACHABLE ();
@ -2990,6 +3036,10 @@ vm_run (const ecma_compiled_code_t *bytecode_header_p, /**< byte-code data heade
frame_ctx.lex_env_p = lex_env_p;
frame_ctx.prev_context_p = JERRY_CONTEXT (vm_top_context_p);
frame_ctx.this_binding = this_binding_value;
#ifdef JERRY_ENABLE_LINE_INFO
frame_ctx.resource_name = ECMA_VALUE_UNDEFINED;
frame_ctx.current_line = 0;
#endif /* JERRY_ENABLE_LINE_INFO */
frame_ctx.context_depth = 0;
frame_ctx.is_eval_code = is_eval_code;
frame_ctx.call_operation = VM_NO_EXEC_OP;
@ -3001,38 +3051,6 @@ vm_run (const ecma_compiled_code_t *bytecode_header_p, /**< byte-code data heade
return vm_execute (&frame_ctx, arg_list_p, arg_list_len);
} /* vm_run */
/**
* Check whether currently executed code is strict mode code
*
* @return true - current code is executed in strict mode,
* false - otherwise
*/
bool
vm_is_strict_mode (void)
{
JERRY_ASSERT (JERRY_CONTEXT (vm_top_context_p) != NULL);
return JERRY_CONTEXT (vm_top_context_p)->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE;
} /* vm_is_strict_mode */
/**
* Check whether currently performed call (on top of call-stack) is performed in form,
* meeting conditions of 'Direct Call to Eval' (see also: ECMA-262 v5, 15.1.2.1.1)
*
* Warning:
* the function should only be called from implementation
* of built-in 'eval' routine of Global object
*
* @return true - currently performed call is performed through 'eval' identifier,
* without 'this' argument,
* false - otherwise
*/
inline bool __attr_always_inline___
vm_is_direct_eval_form_call (void)
{
return (JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL) != 0;
} /* vm_is_direct_eval_form_call */
/**
* @}
* @}

View File

@ -203,8 +203,10 @@ typedef enum
VM_OC_FINALLY, /**< finally */
VM_OC_CONTEXT_END, /**< context end */
VM_OC_JUMP_AND_EXIT_CONTEXT, /**< jump and exit context */
VM_OC_BREAKPOINT_ENABLED, /**< enabled breakpoint for debugger */
VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */
VM_OC_BREAKPOINT_ENABLED, /**< enabled breakpoint for debugger */
VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */
VM_OC_RESOURCE_NAME, /**< resource name of the current function */
VM_OC_LINE, /**< line number of the next statement */
} vm_oc_types;
/**
@ -286,6 +288,8 @@ ecma_value_t vm_run (const ecma_compiled_code_t *bytecode_header_p, ecma_value_t
bool vm_is_strict_mode (void);
bool vm_is_direct_eval_form_call (void);
ecma_value_t vm_get_backtrace (uint32_t max_depth);
/**
* @}
* @}

View File

@ -89,9 +89,58 @@ print_unhandled_exception (jerry_value_t error_value) /**< error value */
{
assert (!jerry_value_has_error_flag (error_value));
jerry_char_t err_str_buf[256];
if (jerry_value_is_object (error_value))
{
jerry_value_t stack_str = jerry_create_string ((const jerry_char_t *) "stack");
jerry_value_t backtrace_val = jerry_get_property (error_value, stack_str);
jerry_release_value (stack_str);
if (!jerry_value_has_error_flag (backtrace_val)
&& jerry_value_is_array (backtrace_val))
{
printf ("Exception backtrace:\n");
uint32_t length = jerry_get_array_length (backtrace_val);
/* This length should be enough. */
if (length > 32)
{
length = 32;
}
for (uint32_t i = 0; i < length; i++)
{
jerry_value_t item_val = jerry_get_property_by_index (backtrace_val, i);
if (!jerry_value_has_error_flag (item_val)
&& jerry_value_is_string (item_val))
{
jerry_size_t str_size = jerry_get_string_size (item_val);
if (str_size >= 256)
{
printf ("%3d: [Backtrace string too long]\n", i);
}
else
{
jerry_size_t string_end = jerry_string_to_char_buffer (item_val, err_str_buf, str_size);
assert (string_end == str_size);
err_str_buf[string_end] = 0;
printf ("%3d: %s\n", i, err_str_buf);
}
}
jerry_release_value (item_val);
}
}
jerry_release_value (backtrace_val);
}
jerry_value_t err_str_val = jerry_value_to_string (error_value);
jerry_size_t err_str_size = jerry_get_string_size (err_str_val);
jerry_char_t err_str_buf[256];
if (err_str_size >= 256)
{

View File

@ -0,0 +1,202 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "jerryscript.h"
#include "test-common.h"
static jerry_value_t
backtrace_handler (const jerry_value_t function_obj, /**< function object */
const jerry_value_t this_val, /**< this value */
const jerry_value_t args_p[], /**< argument list */
const jerry_length_t args_count) /**< argument count */
{
JERRY_UNUSED (function_obj);
JERRY_UNUSED (this_val);
uint32_t max_depth = 0;
if (args_count > 0 && jerry_value_is_number (args_p[0]))
{
max_depth = (uint32_t) jerry_get_number_value (args_p[0]);
}
return jerry_get_backtrace (max_depth);
} /* backtrace_handler */
static jerry_value_t
run (const char *resource_name_p, /**< resource name */
const char *source_p) /**< source code */
{
jerry_value_t code = jerry_parse ((const jerry_char_t *) resource_name_p,
strlen (resource_name_p),
(const jerry_char_t *) source_p,
strlen (source_p),
JERRY_PARSE_NO_OPTS);
TEST_ASSERT (!jerry_value_has_error_flag (code));
jerry_value_t result = jerry_run (code);
jerry_release_value (code);
return result;
} /* run */
static void
compare (jerry_value_t array, /**< array */
uint32_t index, /**< item index */
const char *str) /**< string to compare */
{
jerry_char_t buf[64];
size_t len = strlen (str);
TEST_ASSERT (len < sizeof (buf));
jerry_value_t value = jerry_get_property_by_index (array, index);
TEST_ASSERT (!jerry_value_has_error_flag (value)
&& jerry_value_is_string (value));
TEST_ASSERT (jerry_get_string_size (value) == len);
jerry_size_t str_len = jerry_string_to_char_buffer (value, buf, (jerry_size_t) len);
TEST_ASSERT (str_len == len);
jerry_release_value (value);
TEST_ASSERT (memcmp (buf, str, len) == 0);
} /* compare */
int
main (void)
{
TEST_INIT ();
TEST_ASSERT (jerry_is_feature_enabled (JERRY_FEATURE_LINE_INFO));
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t global = jerry_get_global_object ();
jerry_value_t func = jerry_create_external_function (backtrace_handler);
jerry_value_t name = jerry_create_string ((const jerry_char_t *) "backtrace");
jerry_value_t result = jerry_set_property (global, name, func);
TEST_ASSERT (!jerry_value_has_error_flag (result));
jerry_release_value (result);
jerry_release_value (name);
jerry_release_value (func);
jerry_release_value (global);
const char *source = ("function f() {\n"
" return backtrace(0);\n"
"}\n"
"\n"
"function g() {\n"
" return f();\n"
"}\n"
"\n"
"function h() {\n"
" return g();\n"
"}\n"
"\n"
"h();\n");
jerry_value_t backtrace = run ("something.js", source);
TEST_ASSERT (!jerry_value_has_error_flag (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 4);
compare (backtrace, 0, "something.js:2");
compare (backtrace, 1, "something.js:6");
compare (backtrace, 2, "something.js:10");
compare (backtrace, 3, "something.js:13");
jerry_release_value (backtrace);
/* Depth set to 2 this time. */
source = ("function f() {\n"
" return backtrace(2);\n"
"}\n"
"\n"
"function g() {\n"
" return f();\n"
"}\n"
"\n"
"function h() {\n"
" return g();\n"
"}\n"
"\n"
"h();\n");
backtrace = run ("something_else.js", source);
TEST_ASSERT (!jerry_value_has_error_flag (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 2);
compare (backtrace, 0, "something_else.js:2");
compare (backtrace, 1, "something_else.js:6");
jerry_release_value (backtrace);
jerry_cleanup ();
jerry_init (JERRY_INIT_EMPTY);
source = ("function f() {\n"
" undef_reference;\n"
"}\n"
"\n"
"function g() {\n"
" return f();\n"
"}\n"
"\n"
"g();\n");
jerry_value_t error = run ("bad.js", source);
TEST_ASSERT (jerry_value_has_error_flag (error));
jerry_value_clear_error_flag (&error);
TEST_ASSERT (jerry_value_is_object (error));
name = jerry_create_string ((const jerry_char_t *) "stack");
backtrace = jerry_get_property (error, name);
jerry_release_value (name);
jerry_release_value (error);
TEST_ASSERT (!jerry_value_has_error_flag (backtrace)
&& jerry_value_is_array (backtrace));
TEST_ASSERT (jerry_get_array_length (backtrace) == 3);
compare (backtrace, 0, "bad.js:2");
compare (backtrace, 1, "bad.js:6");
compare (backtrace, 2, "bad.js:9");
jerry_release_value (backtrace);
jerry_cleanup ();
return 0;
} /* main */

View File

@ -216,7 +216,7 @@ main (void)
/* Check the snapshot data. Unused bytes should be filled with zeroes */
const uint8_t expected_data[] =
{
0x4A, 0x52, 0x52, 0x59, 0x0B, 0x00, 0x00, 0x00,
0x4A, 0x52, 0x52, 0x59, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,

View File

@ -86,6 +86,8 @@ def get_arguments():
help='build default jerry port implementation (%(choices)s; default: %(default)s)')
parser.add_argument('--js-parser', metavar='X', choices=['ON', 'OFF'], default='ON', type=str.upper,
help='enable js-parser (%(choices)s; default: %(default)s)')
parser.add_argument('--line-info', metavar='X', choices=['ON', 'OFF'], default='OFF', type=str.upper,
help='provide line info (%(choices)s; default: %(default)s)')
parser.add_argument('--link-lib', metavar='OPT', action='append', default=[],
help='add custom library to be linked')
parser.add_argument('--linker-flag', metavar='OPT', action='append', default=[],
@ -152,6 +154,7 @@ def generate_build_options(arguments):
build_options.append('-DEXTERNAL_COMPILE_FLAGS=' + ' '.join(arguments.compile_flag))
build_options.append('-DFEATURE_CPOINTER_32_BIT=%s' % arguments.cpointer_32bit)
build_options.append('-DFEATURE_ERROR_MESSAGES=%s' % arguments.error_messages)
build_options.append('-DFEATURE_LINE_INFO=%s' % arguments.line_info)
build_options.append('-DJERRY_CMDLINE=%s' % arguments.jerry_cmdline)
build_options.append('-DJERRY_CMDLINE_TEST=%s' % arguments.jerry_cmdline_test)
build_options.append('-DJERRY_CMDLINE_SNAPSHOT=%s' % arguments.jerry_cmdline_snapshot)

View File

@ -34,11 +34,13 @@ def get_binary_path(bin_dir_path):
# Test options for unittests
JERRY_UNITTESTS_OPTIONS = [
Options('unittests',
['--unittests', '--jerry-cmdline=off', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es2015-subset', '--mem-stats=on']),
['--unittests', '--profile=es2015-subset', '--jerry-cmdline=off', '--error-messages=on',
'--snapshot-save=on', '--snapshot-exec=on', '--line-info=on', '--vm-exec-stop=on',
'--mem-stats=on']),
Options('unittests-debug',
['--unittests', '--jerry-cmdline=off', '--debug', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es2015-subset', '--mem-stats=on']),
['--unittests', '--debug', '--profile=es2015-subset', '--jerry-cmdline=off',
'--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--line-info=on',
'--vm-exec-stop=on', '--mem-stats=on']),
Options('doctests',
['--doctests', '--jerry-cmdline=off', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es2015-subset']),
@ -46,11 +48,13 @@ JERRY_UNITTESTS_OPTIONS = [
['--doctests', '--jerry-cmdline=off', '--debug', '--error-messages=on',
'--snapshot-save=on', '--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es2015-subset']),
Options('unittests-es5.1',
['--unittests', '--jerry-cmdline=off', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es5.1', '--mem-stats=on']),
['--unittests', '--profile=es5.1', '--jerry-cmdline=off', '--error-messages=on',
'--snapshot-save=on', '--snapshot-exec=on', '--line-info=on', '--vm-exec-stop=on',
'--mem-stats=on']),
Options('unittests-es5.1-debug',
['--unittests', '--jerry-cmdline=off', '--debug', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es5.1', '--mem-stats=on']),
['--unittests', '--debug', '--profile=es5.1', '--jerry-cmdline=off',
'--error-messages=on', '--snapshot-save=on', '--snapshot-exec=on', '--line-info=on',
'--vm-exec-stop=on', '--mem-stats=on']),
Options('doctests-es5.1',
['--doctests', '--jerry-cmdline=off', '--error-messages=on', '--snapshot-save=on',
'--snapshot-exec=on', '--vm-exec-stop=on', '--profile=es5.1']),