Rework storing the line/column/bytecode info (#4707)

This information is stored in a separate memory block instead
of being part of the byte code. Snapshot does not supported.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2021-07-15 13:45:10 +02:00 committed by GitHub
parent 4be05a74eb
commit 998e49a969
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1218 additions and 201 deletions

View File

@ -143,6 +143,7 @@ set(SOURCE_CORE_FILES
ecma/base/ecma-helpers.c
ecma/base/ecma-init-finalize.c
ecma/base/ecma-lcache.c
ecma/base/ecma-line-info.c
ecma/base/ecma-literal-storage.c
ecma/base/ecma-module.c
ecma/base/ecma-property-hashmap.c
@ -295,6 +296,7 @@ set(SOURCE_CORE_FILES
parser/js/common.c
parser/js/js-lexer.c
parser/js/js-parser-expr.c
parser/js/js-parser-line-info-create.c
parser/js/js-parser-mem.c
parser/js/js-parser-module.c
parser/js/js-parser-statm.c
@ -334,6 +336,7 @@ if(ENABLE_AMALGAM)
ecma/base/ecma-helpers.h
ecma/base/ecma-init-finalize.h
ecma/base/ecma-lcache.h
ecma/base/ecma-line-info.h
ecma/base/ecma-literal-storage.h
ecma/base/ecma-module.h
ecma/base/ecma-property-hashmap.h

View File

@ -256,6 +256,11 @@ snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled
uint32_t const_literal_end;
uint32_t literal_end;
#if JERRY_LINE_INFO
/* TODO: support snapshots. */
((ecma_compiled_code_t *) buffer_p)->status_flags &= (uint16_t) ~CBC_CODE_FLAGS_HAS_LINE_INFO;
#endif /* JERRY_LINE_INFO */
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
literal_start_p = (ecma_value_t *) (buffer_p + sizeof (cbc_uint16_arguments_t));

View File

@ -34,7 +34,7 @@
#include "ecma-init-finalize.h"
#include "ecma-iterator-object.h"
#include "ecma-lex-env.h"
#include "lit-char-helpers.h"
#include "ecma-line-info.h"
#include "ecma-literal-storage.h"
#include "ecma-objects.h"
#include "ecma-objects-general.h"
@ -43,12 +43,13 @@
#include "ecma-proxy-object.h"
#include "ecma-symbol-object.h"
#include "ecma-typedarray-object.h"
#include "opcodes.h"
#include "jcontext.h"
#include "jerryscript.h"
#include "jerryscript-debugger-transport.h"
#include "jmem.h"
#include "js-parser.h"
#include "lit-char-helpers.h"
#include "opcodes.h"
#include "re-compiler.h"
JERRY_STATIC_ASSERT (sizeof (jerry_value_t) == sizeof (ecma_value_t),
@ -5262,10 +5263,19 @@ jerry_backtrace_get_location (jerry_backtrace_frame_t *frame_p) /**< frame point
if (frame_p->frame_type == JERRY_BACKTRACE_FRAME_JS)
{
vm_frame_ctx_t *context_p = frame_p->context_p;
const ecma_compiled_code_t *bytecode_header_p = context_p->shared_p->bytecode_header_p;
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO))
{
return NULL;
}
frame_p->location.resource_name = ecma_get_resource_name (bytecode_header_p);
ecma_line_info_get (ecma_compiled_code_get_line_info (bytecode_header_p),
(uint32_t) (context_p->byte_code_p - context_p->byte_code_start_p),
&frame_p->location);
frame_p->location.resource_name = ecma_get_resource_name (context_p->shared_p->bytecode_header_p);
frame_p->location.line = context_p->current_line;
frame_p->location.column = 1;
return &frame_p->location;
}
#endif /* JERRY_LINE_INFO */

View File

@ -19,6 +19,7 @@
#include "ecma-globals.h"
#include "ecma-helpers.h"
#include "ecma-lcache.h"
#include "ecma-line-info.h"
#include "ecma-property-hashmap.h"
#include "jcontext.h"
#include "jrt-bit-fields.h"
@ -1525,6 +1526,13 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO)
{
ecma_line_info_free (ecma_compiled_code_get_line_info (bytecode_p));
}
#endif /* JERRY_LINE_INFO */
#if JERRY_DEBUGGER
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& !(bytecode_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)
@ -1657,6 +1665,43 @@ ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *b
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
/**
* Get the line info data from the byte code
*
* @return pointer to the line info data
*/
uint8_t *
ecma_compiled_code_get_line_info (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
JERRY_ASSERT (bytecode_header_p != NULL);
JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO);
ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p);
#if JERRY_ESNEXT
if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR)
{
base_p--;
}
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO)
{
base_p--;
}
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS)
{
base_p--;
}
#endif /* JERRY_ESNEXT */
return ECMA_GET_INTERNAL_VALUE_POINTER (uint8_t, base_p[-1]);
} /* ecma_compiled_code_get_line_info */
#endif /* JERRY_LINE_INFO */
/**
* Get the resource name of a compiled code.
*

View File

@ -538,6 +538,9 @@ ecma_value_t *ecma_compiled_code_resolve_function_name (const ecma_compiled_code
uint32_t ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p);
ecma_collection_t *ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p);
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
uint8_t *ecma_compiled_code_get_line_info (const ecma_compiled_code_t *bytecode_header_p);
#endif /* JERRY_LINE_INFO */
ecma_value_t ecma_get_resource_name (const ecma_compiled_code_t *bytecode_p);
#if (JERRY_STACK_LIMIT != 0)
uintptr_t ecma_get_current_stack_usage (void);

View File

@ -0,0 +1,283 @@
/* 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-helpers.h"
#include "ecma-line-info.h"
#if JERRY_LINE_INFO
/** \addtogroup ecma ECMA
* @{
*
* \addtogroup ecmalineinfo Line info
* @{
*/
/* The layout of the structure is defined in js-parser-line-info-create.c */
JERRY_STATIC_ASSERT ((ECMA_LINE_INFO_COLUMN_DEFAULT - 1) == ((ECMA_LINE_INFO_ENCODE_TWO_BYTE >> 1) - 1),
ecma_line_info_column_1_must_be_accessible_with_the_highest_one_byte_negative_value);
/**
* Decodes an uint32_t number, and updates the buffer position.
* Numbers expected to be larger values.
*
* @return the decoded value
*/
uint32_t
ecma_line_info_decode_vlq (uint8_t **buffer_p) /**< [in/out] target buffer */
{
uint8_t *source_p = *buffer_p;
uint32_t value = 0;
do
{
value = (value << ECMA_LINE_INFO_VLQ_SHIFT) | (*source_p & ECMA_LINE_INFO_VLQ_MASK);
}
while (*source_p++ & ECMA_LINE_INFO_VLQ_CONTINUE);
*buffer_p = source_p;
return value;
} /* ecma_line_info_decode_vlq */
/**
* Decodes an uint32_t number, and updates the buffer position.
* Numbers expected to be smaller values.
*
* @return the decoded value
*/
static uint32_t
ecma_line_info_decode_small (uint8_t **buffer_p) /**< [in/out] target buffer */
{
uint8_t *source_p = *buffer_p;
uint32_t type = source_p[0];
if (type < ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN)
{
*buffer_p = source_p + 1;
return type;
}
uint32_t value = source_p[1];
if (type == ECMA_LINE_INFO_ENCODE_TWO_BYTE)
{
*buffer_p = source_p + 2;
return (uint32_t) (value + ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN);
}
value |= ((uint32_t) source_p[2]) << 8;
if (type == ECMA_LINE_INFO_ENCODE_THREE_BYTE)
{
*buffer_p = source_p + 3;
return (uint32_t) (value + ECMA_LINE_INFO_ENCODE_THREE_BYTE_MIN);
}
JERRY_ASSERT (type == ECMA_LINE_INFO_ENCODE_FIVE_BYTE);
*buffer_p = source_p + 5;
return value | (((uint32_t) source_p[3]) << 8) | (((uint32_t) source_p[4]) << 8);
} /* ecma_line_info_decode_small */
/**
* Updates a value using an encoded difference.
*
* @return updated value
*/
extern inline uint32_t JERRY_ATTR_ALWAYS_INLINE
ecma_line_info_difference_update (uint32_t current_value, /**< current value */
uint32_t difference_value) /**< encoded difference */
{
if ((difference_value & 0x1) == ECMA_LINE_INFO_INCREASE)
{
return current_value + (difference_value >> 1) + 1;
}
return current_value - (difference_value >> 1);
} /* ecma_line_info_difference_update */
/**
* Release line info data.
*/
void
ecma_line_info_free (uint8_t *line_info_p) /**< line info buffer */
{
uint8_t *source_p = line_info_p;
uint32_t total_length = ecma_line_info_decode_vlq (&source_p);
jmem_heap_free_block (line_info_p, total_length + (uint32_t) (source_p - line_info_p));
} /* ecma_line_info_free */
/**
* Returns the line/column information for a given byte code offset.
*/
void
ecma_line_info_get (uint8_t *line_info_p, /**< line info buffer */
uint32_t offset, /**< byte code offset */
jerry_backtrace_location_t *location_p) /**< [out] location */
{
uint32_t line = 1;
uint32_t column = ECMA_LINE_INFO_COLUMN_DEFAULT;
uint32_t end_offset = 0;
uint32_t end_offset_increase;
uint32_t value;
/* Skip total_length. */
ecma_line_info_decode_vlq (&line_info_p);
while (true)
{
value = ecma_line_info_decode_vlq (&line_info_p);
line = ecma_line_info_difference_update (line, value);
if (*line_info_p == 0)
{
break;
}
uint8_t *size_p = line_info_p + *line_info_p + (ECMA_LINE_INFO_STREAM_SIZE_MIN + 1);
end_offset += ecma_line_info_decode_vlq (&size_p);
if (offset < end_offset)
{
break;
}
line_info_p = size_p;
}
line_info_p++;
do
{
end_offset_increase = ecma_line_info_decode_small (&line_info_p);
if (end_offset_increase & ECMA_LINE_INFO_HAS_LINE)
{
value = ecma_line_info_decode_small (&line_info_p);
line = ecma_line_info_difference_update (line, value);
column = ECMA_LINE_INFO_COLUMN_DEFAULT;
}
end_offset_increase >>= 1;
value = ecma_line_info_decode_small (&line_info_p);
column = ecma_line_info_difference_update (column, value);
end_offset += end_offset_increase;
}
while (end_offset_increase != 0 && end_offset <= offset);
location_p->line = line;
location_p->column = column;
} /* ecma_line_info_get */
#if JERRY_PARSER_DUMP_BYTE_CODE
/**
* Dumps line info data.
*/
void
ecma_line_info_dump (uint8_t *line_info_p) /**< dumps line info data */
{
bool block_last = false;
uint32_t block_line = 1;
uint32_t block_byte_code_offset = 0;
uint32_t value;
value = ecma_line_info_decode_vlq (&line_info_p);
JERRY_DEBUG_MSG ("\nLine info size: %d bytes\n", (int) value);
while (true)
{
value = ecma_line_info_decode_vlq (&line_info_p);
block_line = ecma_line_info_difference_update (block_line, value);
JERRY_DEBUG_MSG ("\nNew block: line: %d", (int) block_line);
if (*line_info_p == 0)
{
JERRY_DEBUG_MSG (" StreamLength: [last]\n");
block_last = true;
}
else
{
uint8_t *size_p = line_info_p + *line_info_p + (ECMA_LINE_INFO_STREAM_SIZE_MIN + 1);
value = ecma_line_info_decode_vlq (&size_p);
JERRY_DEBUG_MSG (" StreamLength: %d ByteCodeSize: %d\n",
(int) (*line_info_p + ECMA_LINE_INFO_STREAM_SIZE_MIN),
(int) value);
}
line_info_p++;
uint32_t stream_line = block_line;
uint32_t stream_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
uint32_t stream_end_offset = block_byte_code_offset;
while (true)
{
uint32_t stream_end_offset_increase = ecma_line_info_decode_small (&line_info_p);
if (stream_end_offset_increase & ECMA_LINE_INFO_HAS_LINE)
{
value = ecma_line_info_decode_small (&line_info_p);
stream_line = ecma_line_info_difference_update (stream_line, value);
stream_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
}
stream_end_offset_increase >>= 1;
value = ecma_line_info_decode_small (&line_info_p);
stream_column = ecma_line_info_difference_update (stream_column, value);
if (stream_end_offset_increase == 0)
{
JERRY_DEBUG_MSG (" ByteCodeEndOffset: [unterminated] Line: %d Column: %d\n",
(int) stream_line,
(int) stream_column);
break;
}
stream_end_offset += stream_end_offset_increase;
JERRY_DEBUG_MSG (" ByteCodeEndOffset: %d Line: %d Column: %d\n",
(int) stream_end_offset,
(int) stream_line,
(int) stream_column);
}
if (block_last)
{
break;
}
block_byte_code_offset += ecma_line_info_decode_vlq (&line_info_p);
}
} /* ecma_line_info_dump */
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
#endif /* JERRY_LINE_INFO */
/**
* @}
* @}
*/

View File

@ -0,0 +1,121 @@
/* 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.
*/
#ifndef ECMA_LINE_INFO_H
#define ECMA_LINE_INFO_H
/** \addtogroup ecma ECMA
* @{
*
* \addtogroup ecmalineinfo Line info
* @{
*/
#if JERRY_LINE_INFO
#include "ecma-globals.h"
/**
* Increase the current value of line or column.
*/
#define ECMA_LINE_INFO_INCREASE 0x0
/**
* Decrease the current value of line or column.
*/
#define ECMA_LINE_INFO_DECREASE 0x1
/**
* Line update is present.
*/
#define ECMA_LINE_INFO_HAS_LINE 0x1
/**
* A default value for columns after a line update.
*/
#define ECMA_LINE_INFO_COLUMN_DEFAULT 126
/**
* Vlq encoding: flag which is set for all bytes except the last one.
*/
#define ECMA_LINE_INFO_VLQ_CONTINUE 0x80
/**
* Vlq encoding: mask to decode the number fragment.
*/
#define ECMA_LINE_INFO_VLQ_MASK 0x7f
/**
* Vlq encoding: number of bits stored in a byte.
*/
#define ECMA_LINE_INFO_VLQ_SHIFT 7
/**
* Small encoding: a value which represents a two byte long number.
*/
#define ECMA_LINE_INFO_ENCODE_TWO_BYTE (UINT8_MAX - 2)
/**
* Small encoding: minimum value of an encoded two byte long number.
*/
#define ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN (UINT8_MAX - 2)
/**
* Small encoding: a value which represents a three byte long number.
*/
#define ECMA_LINE_INFO_ENCODE_THREE_BYTE (UINT8_MAX - 1)
/**
* Small encoding: minimum value of an encoded three byte long number.
*/
#define ECMA_LINE_INFO_ENCODE_THREE_BYTE_MIN (ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN + UINT8_MAX + 1)
/**
* Small encoding: a value which represents a five byte long number.
*/
#define ECMA_LINE_INFO_ENCODE_FIVE_BYTE UINT8_MAX
/**
* Maximum number of line/column entries stored in a stream.
*/
#define ECMA_LINE_INFO_STREAM_VALUE_COUNT_MAX 48
/**
* Minimum size of a stream (except the last one).
*/
#define ECMA_LINE_INFO_STREAM_SIZE_MIN \
((2 * ECMA_LINE_INFO_STREAM_VALUE_COUNT_MAX) - 1)
/* Helper functions for parser/js/js-parser-line-info-create.c. */
uint32_t ecma_line_info_decode_vlq (uint8_t **buffer_p);
uint32_t ecma_line_info_difference_update (uint32_t current_value, uint32_t difference_value);
/* General functions. */
void ecma_line_info_free (uint8_t *line_info_p);
void ecma_line_info_get (uint8_t *line_info_p, uint32_t offset,
jerry_backtrace_location_t *location_p);
#if JERRY_PARSER_DUMP_BYTE_CODE
void ecma_line_info_dump (uint8_t *line_info_p);
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
#endif /* ECMA_LINE_INFO_H */
/**
* @}
* @}
*/
#endif /* !ECMA_LINE_INFO_H */

View File

@ -30,7 +30,7 @@ extern "C"
/**
* Jerry snapshot format version.
*/
#define JERRY_SNAPSHOT_VERSION (65u)
#define JERRY_SNAPSHOT_VERSION (66u)
/**
* Flags for jerry_generate_snapshot and jerry_generate_function_snapshot.

View File

@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t)
*/
JERRY_STATIC_ASSERT (CBC_END == 238,
number_of_cbc_opcodes_changed);
JERRY_STATIC_ASSERT (CBC_EXT_END == 147,
JERRY_STATIC_ASSERT (CBC_EXT_END == 146,
number_of_cbc_ext_opcodes_changed);
#if JERRY_PARSER || JERRY_PARSER_DUMP_BYTE_CODE

View File

@ -606,8 +606,6 @@
VM_OC_STRING_CONCAT | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, CBC_HAS_BYTE_ARG, 1, \
VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
VM_OC_LINE) \
CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \
VM_OC_THROW_REFERENCE_ERROR) \
CBC_OPCODE (CBC_EXT_THROW_ASSIGN_CONST_ERROR, CBC_NO_FLAG, 0, \
@ -889,9 +887,10 @@ typedef enum
CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED = (1u << 4), /**< no need to create a lexical environment */
CBC_CODE_FLAGS_HAS_EXTENDED_INFO = (1u << 5), /**< this function has extended info block */
CBC_CODE_FLAGS_HAS_TAGGED_LITERALS = (1u << 6), /**< this function has tagged template literal list */
CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 7), /**< this function is a static snapshot function */
CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 8), /**< this function should be ignored by debugger */
CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED = (1u << 9), /**< compiled code needs a lexical block */
CBC_CODE_FLAGS_HAS_LINE_INFO = (1u << 7), /**< this function has line info block */
CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 8), /**< this function is a static snapshot function */
CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 9), /**< this function should be ignored by debugger */
CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED = (1u << 10), /**< compiled code needs a lexical block */
/* Bits from bit 12 is reserved for function types (see CBC_FUNCTION_TYPE_SHIFT).
* Note: the last bits are used for type flags because < and >= operators can be used to

View File

@ -430,6 +430,11 @@ util_print_cbc (ecma_compiled_code_t *compiled_code_p) /**< compiled code */
size -= sizeof (ecma_value_t);
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO)
{
size -= sizeof (ecma_value_t);
}
byte_code_end_p = ((uint8_t *) compiled_code_p) + size;
byte_code_p = byte_code_start_p;
@ -462,24 +467,6 @@ util_print_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;
#if JERRY_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_LINE_INFO */
}
if (flags & (CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2))

View File

@ -3388,13 +3388,6 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */
context_p->last_breakpoint_line = ident_line_counter;
}
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
if (ident_line_counter != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, ident_line_counter, false);
}
#endif /* JERRY_LINE_INFO */
} /* parser_pattern_form_assignment */
/**
@ -3483,6 +3476,9 @@ parser_pattern_process_assignment (parser_context_t *context_p, /**< context */
}
parser_line_counter_t ident_line_counter = context_p->token.line;
#if JERRY_LINE_INFO
parser_line_counter_t ident_column_counter = context_p->token.column;
#endif /* JERRY_LINE_INFO */
if (flags & PARSER_PATTERN_BINDING)
{
@ -3538,6 +3534,9 @@ parser_pattern_process_assignment (parser_context_t *context_p, /**< context */
}
parser_pattern_form_assignment (context_p, flags, rhs_opcode, literal_index, ident_line_counter);
#if JERRY_LINE_INFO
parser_line_info_append (context_p, ident_line_counter, ident_column_counter);
#endif /* JERRY_LINE_INFO */
return false;
} /* parser_pattern_process_assignment */
@ -3720,6 +3719,9 @@ parser_parse_object_initializer (parser_context_t *context_p, /**< context */
|| context_p->token.type == LEXER_COMMA);
parser_pattern_form_assignment (context_p, flags, push_prop_opcode, prop_index, start_line);
#if JERRY_LINE_INFO
parser_line_info_append (context_p, start_line, start_column);
#endif /* JERRY_LINE_INFO */
}
if (context_p->token.type == LEXER_RIGHT_BRACE)

View File

@ -486,6 +486,19 @@ typedef struct
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
typedef struct
{
parser_mem_page_t *first_page_p; /**< first page of line info data */
parser_mem_page_t *last_page_p; /**< last page of line info data */
uint32_t byte_code_position; /**< last byte code position */
parser_line_counter_t line; /**< last line */
parser_line_counter_t column; /**< last column */
} parser_line_info_data_t;
#endif /* JERRY_LINE_INFO */
/**
* Those members of a context which needs
* to be saved when a sub-function is parsed.
@ -523,6 +536,10 @@ typedef struct parser_saved_context_t
#ifndef JERRY_NDEBUG
uint16_t context_stack_depth; /**< current context stack depth */
#endif /* !JERRY_NDEBUG */
#if JERRY_LINE_INFO
parser_line_info_data_t line_info; /**< line info data */
#endif /* JERRY_LINE_INFO */
} parser_saved_context_t;
/**
@ -618,6 +635,7 @@ typedef struct
#endif /* JERRY_RESOURCE_NAME */
#if JERRY_LINE_INFO
parser_line_info_data_t line_info; /**< line info data */
parser_line_counter_t last_line_info_line; /**< last line where line info has been inserted */
#endif /* JERRY_LINE_INFO */
} parser_context_t;
@ -875,6 +893,20 @@ void parser_module_add_names_to_node (parser_context_t *context_p,
#endif /* JERRY_MODULE_SYSTEM */
/*
* @}
*
* \addtogroup jsparser_line_info_create Create line info data
* @{
*/
#if JERRY_LINE_INFO
void parser_line_info_free (parser_line_info_data_t *line_info_p);
void parser_line_info_append (parser_context_t *context_p,
parser_line_counter_t line, parser_line_counter_t column);
uint8_t *parser_line_info_generate (parser_context_t *context_p);
#endif /* JERRY_LINE_INFO */
/**
* @}
*
@ -905,12 +937,6 @@ void parser_append_breakpoint_info (parser_context_t *context_p, jerry_debugger_
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
void parser_emit_line_info (parser_context_t *context_p, uint32_t line, bool flush_cbc);
#endif /* JERRY_LINE_INFO */
#if JERRY_PARSER_DUMP_BYTE_CODE
void util_print_cbc (ecma_compiled_code_t *compiled_code_p);
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */

View File

@ -0,0 +1,577 @@
/* 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-line-info.h"
#include "js-parser-internal.h"
#if JERRY_PARSER
#if JERRY_LINE_INFO
/* \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_line_info_create Create line info data
* @{
*/
/*
* The line-info data structure uses two number encodings:
*
* Vlq (variable length quantity):
* Each byte has 7 bit data and the highest bit is set for continuation.
* The format is big endian.
*
* Small:
* First byte can encode signed values between 127 and -125 in 1 byte.
* Large values requires more bytes than vlq.
*
* The line-info data structure is a sequence of chunks:
*
* +------+--------------+------------+----------------+
* | Line | StreamLength | StreamData | [ByteCodeSize] |
* +------+--------------+------------+----------------+
*
* Line [Vlq encoding]:
* Specifies the start line of this chunk, relative to its previous value.
* The starting column is always ECMA_LINE_INFO_COLUMN_DEFAULT
*
* StreamLength [uint8_t]:
* Length of the StreamData in bytes minus ECMA_LINE_INFO_STREAM_SIZE_MIN.
* The 0 value represents the last chunk, which size is not specified
* (Can be less than ECMA_LINE_INFO_STREAM_SIZE_MIN).
*
* StreamData [sequence of bytes]:
* Sequence of the following items:
*
* +-----------+--------+--------+
* | EndOffset | [Line] | Column |
* +-----------+--------+--------+
*
* EndOffset [Small encoding]:
* Specifies the EndOffset in the byte code, relative to the previous EndOffset.
* The range of byte codes corresponding to the line/column position of this item
* is between the EndOffset of the previous item (inclusive) and the EndOffset
* of this item (exclusive). The last end offset of a stream is always 0, which
* represents an unterminated range.
*
* Line [Small encoding] [Optional]:
* If bit 1 of end offset is set, this specifies the line position of this item,
* relative to the previous line position, and the column position is set to
* ECMA_LINE_INFO_COLUMN_DEFAULT.
*
* Column [Small encoding]:
* Specifies the current column position relative to the previous column position.
*
* ByteCodeSize [Vlq encoding] [Optional]:
* If StreamLength is not 0, this specifies the byte code size of the whole range.
* This value can be used to skip the byte codes which line info is stored
* in this chunk. This information is not available for the last chunk.
*/
/**
* Maximum number of bytes requires to encode a number.
*/
#define PARSER_LINE_INFO_BUFFER_MAX_SIZE 5
/**
* Stream generation ends after this size is reached,
* since there might be not enough place for the next item.
*/
#define PARSER_LINE_INFO_STREAM_SIZE_LIMIT \
(ECMA_LINE_INFO_STREAM_SIZE_MIN + UINT8_MAX - ((2 * PARSER_LINE_INFO_BUFFER_MAX_SIZE) + 1))
/**
* Free line info temporary data collected during parsing.
*/
void
parser_line_info_free (parser_line_info_data_t *line_info_p)
{
parser_mem_page_t *current_page_p = line_info_p->first_page_p;
while (current_page_p != NULL)
{
parser_mem_page_t *next_p = current_page_p->next_p;
parser_free (current_page_p, sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE);
current_page_p = next_p;
}
} /* parser_line_info_free */
/**
* Encodes an uint32_t number into a buffer. Numbers expected to be larger values.
*
* @return the number of bytes written to the buffer
*/
static uint32_t
parser_line_info_encode_vlq (uint8_t *buffer_p, /**< target buffer */
uint32_t value) /**< encoded value */
{
if (value <= ECMA_LINE_INFO_VLQ_MASK)
{
*buffer_p = (uint8_t) value;
return 1;
}
uint32_t length = 0;
uint32_t current_value = value;
do
{
current_value >>= ECMA_LINE_INFO_VLQ_SHIFT;
length++;
}
while (current_value > 0);
buffer_p += length;
do
{
*(--buffer_p) = (uint8_t) (value | ECMA_LINE_INFO_VLQ_CONTINUE);
value >>= ECMA_LINE_INFO_VLQ_SHIFT;
}
while (value > 0);
buffer_p[length - 1] &= ECMA_LINE_INFO_VLQ_MASK;
return length;
} /* parser_line_info_encode_vlq */
/**
* Encodes an uint32_t number into a buffer. Numbers expected to be smaller values.
*
* @return the number of bytes written to the buffer
*/
static uint32_t
parser_line_info_encode_small (uint8_t *buffer_p, /**< target buffer */
uint32_t value) /**< encoded value */
{
if (value < ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN)
{
buffer_p[0] = (uint8_t) value;
return 1;
}
uint32_t length;
if (JERRY_LIKELY (value < ECMA_LINE_INFO_ENCODE_THREE_BYTE_MIN))
{
value -= ECMA_LINE_INFO_ENCODE_TWO_BYTE_MIN;
buffer_p[0] = ECMA_LINE_INFO_ENCODE_TWO_BYTE;
length = 2;
}
else
{
if (value <= (ECMA_LINE_INFO_ENCODE_THREE_BYTE_MIN + UINT16_MAX))
{
value -= ECMA_LINE_INFO_ENCODE_THREE_BYTE_MIN;
buffer_p[0] = ECMA_LINE_INFO_ENCODE_THREE_BYTE;
length = 3;
}
else
{
buffer_p[0] = ECMA_LINE_INFO_ENCODE_FIVE_BYTE;
buffer_p[3] = (uint8_t) (value >> 16);
buffer_p[4] = (uint8_t) (value >> 24);
length = 5;
}
buffer_p[2] = (uint8_t) (value >> 8);
}
buffer_p[1] = (uint8_t) value;
return length;
} /* parser_line_info_encode_small */
/**
* Encodes the difference between two values.
*
* @return encoded difference
*/
static inline uint32_t JERRY_ATTR_ALWAYS_INLINE
parser_line_info_difference_get (uint32_t current_value, /**< current value */
uint32_t prev_value) /**< previous value */
{
uint32_t result = current_value - prev_value - 1;
if (result <= (UINT32_MAX >> 1))
{
return (result << 1) | ECMA_LINE_INFO_INCREASE;
}
return ((UINT32_MAX - result) << 1) | ECMA_LINE_INFO_DECREASE;
} /* parser_line_info_difference_get */
/**
* Appends a value at the end of the line info stream.
*/
static void
parser_line_info_append_number (parser_context_t *context_p, /**< context */
uint32_t value) /**< value to be encoded */
{
uint8_t buffer[PARSER_LINE_INFO_BUFFER_MAX_SIZE];
uint32_t length = parser_line_info_encode_vlq (buffer, value);
if (context_p->line_info.last_page_p != NULL
&& context_p->line_info.last_page_p->bytes[0] + length <= PARSER_STACK_PAGE_SIZE)
{
memcpy (context_p->line_info.last_page_p->bytes + context_p->line_info.last_page_p->bytes[0],
buffer,
length);
length += context_p->line_info.last_page_p->bytes[0];
context_p->line_info.last_page_p->bytes[0] = (uint8_t) length;
return;
}
size_t size = sizeof (parser_mem_page_t *) + PARSER_STACK_PAGE_SIZE;
parser_mem_page_t *new_page_p = (parser_mem_page_t *) parser_malloc (context_p, size);
new_page_p->next_p = NULL;
if (context_p->line_info.first_page_p == NULL)
{
context_p->line_info.first_page_p = new_page_p;
context_p->line_info.last_page_p = new_page_p;
}
else
{
context_p->line_info.last_page_p->next_p = new_page_p;
context_p->line_info.last_page_p = new_page_p;
}
context_p->line_info.last_page_p->bytes[0] = (uint8_t) (length + 1);
memcpy (new_page_p->bytes + 1, buffer, length);
} /* parser_line_info_append_number */
/**
* Updates the current line information data.
*/
void
parser_line_info_append (parser_context_t *context_p, /**< context */
parser_line_counter_t line, /**< line */
parser_line_counter_t column) /**< column */
{
if (context_p->line_info.first_page_p != NULL
&& (context_p->byte_code_size == context_p->line_info.byte_code_position
|| (context_p->line_info.line == line
&& context_p->line_info.column == column)))
{
return;
}
/* Sets ECMA_LINE_INFO_HAS_LINE bit. */
uint32_t value = (((context_p->byte_code_size - context_p->line_info.byte_code_position) << 1)
| (uint32_t) (context_p->line_info.line != line));
parser_line_info_append_number (context_p, value);
context_p->line_info.byte_code_position = context_p->byte_code_size;
if (value & ECMA_LINE_INFO_HAS_LINE)
{
value = parser_line_info_difference_get (line, context_p->line_info.line);
parser_line_info_append_number (context_p, value);
context_p->line_info.line = line;
}
value = parser_line_info_difference_get (column, context_p->line_info.column);
parser_line_info_append_number (context_p, value);
context_p->line_info.column = column;
} /* parser_line_info_append */
/**
* Line info iterator structure
*/
typedef struct
{
parser_mem_page_t *current_page_p; /**< current page */
uint32_t offset; /**< current offset */
} parser_line_info_iterator_t;
/**
* Decodes the next value from the iterator stream
*/
static uint32_t
parser_line_info_iterator_get (parser_line_info_iterator_t *iterator_p) /**< iterator */
{
uint8_t *source_p = iterator_p->current_page_p->bytes + iterator_p->offset;
uint32_t result = ecma_line_info_decode_vlq (&source_p);
iterator_p->offset = (uint32_t) (source_p - iterator_p->current_page_p->bytes);
JERRY_ASSERT (iterator_p->offset <= iterator_p->current_page_p->bytes[0]);
if (iterator_p->offset < iterator_p->current_page_p->bytes[0])
{
return result;
}
iterator_p->current_page_p = iterator_p->current_page_p->next_p;
iterator_p->offset = 1;
return result;
} /* parser_line_info_iterator_get */
/**
* Generate line info data
*
* @return generated line info data
*/
uint8_t *
parser_line_info_generate (parser_context_t *context_p) /**< context */
{
parser_line_info_iterator_t iterator;
uint8_t *line_info_p = NULL;
uint8_t *dst_p = NULL;
uint32_t total_length = 0;
uint32_t total_length_size = 0;
while (true)
{
/* The following code runs twice: first the size of the data,
* is computed and the data is generated during the second run.
* Note: line_info_p is NULL during the first run. */
parser_mem_page_t *iterator_byte_code_page_p = context_p->byte_code.first_p;
uint32_t iterator_byte_code_page_offset = 0;
uint32_t iterator_byte_code_base = 0;
uint32_t iterator_last_byte_code_offset = UINT32_MAX;
uint32_t iterator_prev_line = 0;
uint32_t iterator_prev_column = 0;
uint32_t iterator_line = 1;
uint32_t iterator_column = 1;
uint8_t block_buffer[PARSER_LINE_INFO_BUFFER_MAX_SIZE];
uint8_t line_column_buffer[PARSER_LINE_INFO_BUFFER_MAX_SIZE * 2];
uint8_t *block_size_p = NULL;
uint32_t block_byte_code_offset = 0;
uint32_t block_prev_line = 1;
uint32_t stream_byte_code_offset = 0;
uint32_t stream_current_line = 1;
uint32_t stream_current_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
uint32_t stream_prev_line = 1;
uint32_t stream_prev_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
uint32_t stream_size = 0;
uint32_t stream_value_count = 0;
uint32_t value;
iterator.current_page_p = context_p->line_info.first_page_p;
iterator.offset = 1;
do
{
/* Decode line information generated during parsing. */
value = parser_line_info_iterator_get (&iterator);
iterator_byte_code_page_offset += (value >> 1);
if (value & 0x1)
{
value = parser_line_info_iterator_get (&iterator);
JERRY_ASSERT (value != ((0 << 1) | ECMA_LINE_INFO_DECREASE));
iterator_line = ecma_line_info_difference_update (iterator_line, value);
}
value = parser_line_info_iterator_get (&iterator);
iterator_column = ecma_line_info_difference_update (iterator_column, value);
while (iterator_byte_code_page_offset >= PARSER_CBC_STREAM_PAGE_SIZE)
{
iterator_byte_code_base += iterator_byte_code_page_p->bytes[PARSER_CBC_STREAM_PAGE_SIZE - 1];
iterator_byte_code_page_offset -= PARSER_CBC_STREAM_PAGE_SIZE;
iterator_byte_code_page_p = iterator_byte_code_page_p->next_p;
}
uint32_t iterator_byte_code_offset = iterator_byte_code_base;
if (iterator_byte_code_page_offset > 0)
{
uint8_t relative_offset = iterator_byte_code_page_p->bytes[iterator_byte_code_page_offset - 1];
iterator_byte_code_offset += relative_offset & CBC_LOWER_SEVEN_BIT_MASK;
}
/* Skip those line/column pairs which byte code was discarded during post processing
* or does not change line/column (this is possible when multiple skips occures). */
if (iterator_byte_code_offset == iterator_last_byte_code_offset
|| (iterator_line == iterator_prev_line && iterator_column == iterator_prev_column))
{
continue;
}
iterator_prev_line = iterator_line;
iterator_prev_column = iterator_column;
iterator_last_byte_code_offset = iterator_byte_code_offset;
if (block_size_p != NULL)
{
/* Sets ECMA_LINE_INFO_HAS_LINE bit. */
value = (((iterator_byte_code_offset - stream_byte_code_offset) << 1)
| (uint32_t) (stream_prev_line != stream_current_line));
uint32_t line_column_size = 0;
uint32_t offset_size = parser_line_info_encode_small (block_buffer, value);
stream_byte_code_offset = iterator_byte_code_offset;
if (value & ECMA_LINE_INFO_HAS_LINE)
{
value = parser_line_info_difference_get (stream_current_line, stream_prev_line);
line_column_size = parser_line_info_encode_small (line_column_buffer, value);
stream_prev_line = stream_current_line;
stream_prev_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
}
value = parser_line_info_difference_get (stream_current_column, stream_prev_column);
line_column_size += parser_line_info_encode_small (line_column_buffer + line_column_size, value);
stream_prev_column = stream_current_column;
stream_current_line = iterator_line;
stream_current_column = iterator_column;
stream_value_count++;
if (stream_value_count < ECMA_LINE_INFO_STREAM_VALUE_COUNT_MAX
&& (stream_size + offset_size + line_column_size <= PARSER_LINE_INFO_STREAM_SIZE_LIMIT))
{
stream_size += offset_size + line_column_size;
if (line_info_p != NULL)
{
memcpy (dst_p, block_buffer, offset_size);
dst_p += offset_size;
memcpy (dst_p, line_column_buffer, line_column_size);
dst_p += line_column_size;
}
continue;
}
/* Finalize the current chunk. The size of EndOffset is always 1. */
stream_size += 1 + line_column_size;
JERRY_ASSERT (stream_size > ECMA_LINE_INFO_STREAM_SIZE_MIN
&& (stream_size - ECMA_LINE_INFO_STREAM_SIZE_MIN) <= UINT8_MAX);
if (line_info_p != NULL)
{
*block_size_p = (uint8_t) (stream_size - ECMA_LINE_INFO_STREAM_SIZE_MIN);
/* Set EndOffset to 0 and copy the has_line bit. */
*dst_p++ = (uint8_t) (block_buffer[0] & ECMA_LINE_INFO_HAS_LINE);
memcpy (dst_p, line_column_buffer, line_column_size);
dst_p += line_column_size;
}
else
{
total_length += stream_size;
dst_p = block_buffer;
}
uint32_t byte_code_diff = iterator_last_byte_code_offset - block_byte_code_offset;
dst_p += parser_line_info_encode_vlq (dst_p, byte_code_diff);
block_byte_code_offset = iterator_last_byte_code_offset;
if (line_info_p == NULL)
{
total_length += (uint32_t) (dst_p - block_buffer);
}
}
/* Start a new chunk. */
if (line_info_p == NULL)
{
dst_p = block_buffer;
}
value = parser_line_info_difference_get (iterator_line, block_prev_line);
dst_p += parser_line_info_encode_vlq (dst_p, value);
block_size_p = dst_p;
dst_p++;
if (line_info_p == NULL)
{
total_length += (uint32_t) (dst_p - block_buffer);
}
block_prev_line = iterator_line;
stream_current_line = iterator_line;
stream_current_column = iterator_column;
stream_prev_line = iterator_line;
stream_prev_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
stream_size = 0;
stream_value_count = 0;
}
while (iterator.current_page_p != NULL);
value = (stream_prev_line != stream_current_line);
/* Finalize the last stream */
if (line_info_p == NULL)
{
dst_p = line_column_buffer;
total_length += stream_size + 1;
}
else
{
*block_size_p = 0;
/* Small encoded value of has_line bit. */
*dst_p++ = (uint8_t) value;
}
if (value)
{
value = parser_line_info_difference_get (stream_current_line, stream_prev_line);
dst_p += parser_line_info_encode_small (dst_p, value);
stream_prev_column = ECMA_LINE_INFO_COLUMN_DEFAULT;
}
value = parser_line_info_difference_get (stream_current_column, stream_prev_column);
dst_p += parser_line_info_encode_small (dst_p, value);
if (line_info_p == NULL)
{
total_length += (uint32_t) (dst_p - line_column_buffer);
}
if (line_info_p != NULL)
{
break;
}
total_length_size = parser_line_info_encode_vlq (block_buffer, total_length);
/* TODO: Support allocation fail. */
line_info_p = (uint8_t *) jmem_heap_alloc_block (total_length + total_length_size);
dst_p = line_info_p + parser_line_info_encode_vlq (line_info_p, total_length);
}
JERRY_ASSERT (line_info_p + total_length_size + total_length == dst_p);
#if JERRY_PARSER_DUMP_BYTE_CODE
if (context_p->is_show_opcodes)
{
ecma_line_info_dump (line_info_p);
}
#endif /* JERRY_PARSER_DUMP_BYTE_CODE */
return line_info_p;
} /* parser_line_info_generate */
#endif /* JERRY_LINE_INFO */
#endif /* JERRY_PARSER */
/**
* @}
* @}
* @}
*/

View File

@ -485,6 +485,9 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
#if JERRY_DEBUGGER || JERRY_LINE_INFO
parser_line_counter_t ident_line_counter = context_p->token.line;
#endif /* JERRY_DEBUGGER || JERRY_LINE_INFO */
#if JERRY_LINE_INFO
parser_line_counter_t ident_column_counter = context_p->token.column;
#endif /* JERRY_LINE_INFO */
#if JERRY_MODULE_SYSTEM
parser_module_append_export_name (context_p);
@ -522,10 +525,7 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
if (ident_line_counter != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, ident_line_counter, false);
}
parser_line_info_append (context_p, ident_line_counter, ident_column_counter);
#endif /* JERRY_LINE_INFO */
uint16_t index = context_p->lit_object.index;
@ -1866,10 +1866,7 @@ parser_parse_switch_statement_start (parser_context_t *context_p) /**< context *
switch_case_was_found = true;
#if JERRY_LINE_INFO
if (context_p->token.line != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, context_p->token.line, true);
}
parser_line_info_append (context_p, context_p->token.line, context_p->token.column);
#endif /* JERRY_LINE_INFO */
parser_parse_expression (context_p, PARSE_EXPR);
@ -2834,7 +2831,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
parser_emit_line_info (context_p, context_p->token.line, false);
parser_line_info_append (context_p, context_p->token.line, context_p->token.column);
#endif /* JERRY_LINE_INFO */
lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL);
@ -2919,8 +2916,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
if (context_p->token.line != context_p->last_line_info_line
&& context_p->token.type != LEXER_SEMICOLON
if (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
@ -2930,7 +2926,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
&& 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);
parser_line_info_append (context_p, context_p->token.line, context_p->token.column);
}
#endif /* JERRY_LINE_INFO */

View File

@ -453,60 +453,6 @@ parser_emit_cbc_push_number (parser_context_t *context_p, /**< context */
context_p->last_cbc.value = (uint16_t) (value - 1);
} /* parser_emit_cbc_push_number */
#if JERRY_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 (flush_cbc && context_p->last_cbc_opcode != PARSER_CBC_UNAVAILABLE)
{
parser_flush_cbc (context_p);
}
#if JERRY_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 /* JERRY_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;
const uint32_t max_shift_plus_7 = 7 * 5;
uint32_t shift = 7;
while (shift < max_shift_plus_7 && (line >> shift) > 0)
{
shift += 7;
}
do
{
shift -= 7;
uint8_t byte = (uint8_t) ((line >> shift) & CBC_LOWER_SEVEN_BIT_MASK);
if (shift > 0)
{
byte = (uint8_t) (byte | CBC_HIGHEST_BIT_MASK);
}
PARSER_APPEND_TO_BYTE_CODE (context_p, byte);
context_p->byte_code_size++;
}
while (shift > 0);
} /* parser_emit_line_info */
#endif /* JERRY_LINE_INFO */
/**
* Append a byte code with a branch argument
*/

View File

@ -649,6 +649,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
if (context_p->line_info.first_page_p == NULL)
{
parser_line_info_append (context_p, context_p->token.line, context_p->token.column);
}
#endif /* JERRY_LINE_INFO */
JERRY_ASSERT (context_p->stack_depth == 0);
#ifndef JERRY_NDEBUG
JERRY_ASSERT (context_p->context_stack_depth == 0);
@ -730,23 +737,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */
flags = cbc_ext_flags[ext_opcode];
PARSER_NEXT_BYTE (page_p, offset);
length++;
#if JERRY_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_LINE_INFO */
break;
}
case CBC_POST_DECR:
@ -941,6 +931,10 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
total_size += sizeof (ecma_value_t);
#endif /* JERRY_LINE_INFO */
total_size = JERRY_ALIGNUP (total_size, JMEM_ALIGNMENT);
compiled_code_p = (ecma_compiled_code_t *) parser_malloc (context_p, total_size);
@ -1087,6 +1081,10 @@ parser_post_processing (parser_context_t *context_p) /**< context */
compiled_code_p->status_flags |= function_type;
#if JERRY_LINE_INFO
compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_LINE_INFO;
#endif /* JERRY_LINE_INFO */
literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count;
byte_code_p += literal_length;
dst_p = byte_code_p;
@ -1157,25 +1155,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */
opcode_p++;
real_offset++;
PARSER_NEXT_BYTE_UPDATE (page_p, offset, real_offset);
#if JERRY_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_LINE_INFO */
}
/* Only literal and call arguments can be combined. */
@ -1291,6 +1270,10 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
JERRY_ASSERT (dst_p == byte_code_p + length);
#if JERRY_LINE_INFO
uint8_t *line_info_p = parser_line_info_generate (context_p);
#endif /* JERRY_LINE_INFO */
parse_update_branches (context_p, byte_code_p);
parser_cbc_stream_free (&context_p->byte_code);
@ -1371,9 +1354,16 @@ parser_post_processing (parser_context_t *context_p) /**< context */
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_TAGGED_LITERALS;
base_p[-1] = (ecma_value_t) context_p->tagged_template_literal_cp;
#if JERRY_LINE_INFO
--base_p;
#endif /* JERRY_LINE_INFO */
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
ECMA_SET_INTERNAL_VALUE_POINTER (base_p[-1], line_info_p);
#endif /* JERRY_LINE_INFO */
#if JERRY_PARSER_DUMP_BYTE_CODE
if (context_p->is_show_opcodes)
{
@ -1896,6 +1886,14 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
context.context_stack_depth = 0;
#endif /* !JERRY_NDEBUG */
#if JERRY_LINE_INFO
context.line_info.first_page_p = NULL;
context.line_info.last_page_p = NULL;
context.line_info.byte_code_position = 0;
context.line_info.line = 1;
context.line_info.column = 1;
#endif /* JERRY_LINE_INFO */
#if JERRY_PARSER_DUMP_BYTE_CODE
context.is_show_opcodes = (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_SHOW_OPCODES);
context.total_byte_code_size = 0;
@ -2074,6 +2072,10 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
parser_free (context.scope_stack_p, context.scope_stack_size * sizeof (parser_scope_stack_t));
}
#if JERRY_LINE_INFO
parser_line_info_free (&context.line_info);
#endif /* JERRY_LINE_INFO */
#if JERRY_PARSER_DUMP_BYTE_CODE
if (context.is_show_opcodes)
{
@ -2206,6 +2208,10 @@ parser_save_context (parser_context_t *context_p, /**< context */
saved_context_p->context_stack_depth = context_p->context_stack_depth;
#endif /* !JERRY_NDEBUG */
#if JERRY_LINE_INFO
saved_context_p->line_info = context_p->line_info;
#endif /* JERRY_LINE_INFO */
/* Reset private part of the context. */
context_p->status_flags &= PARSER_IS_STRICT;
@ -2236,6 +2242,14 @@ parser_save_context (parser_context_t *context_p, /**< context */
#ifndef JERRY_NDEBUG
context_p->context_stack_depth = 0;
#endif /* !JERRY_NDEBUG */
#if JERRY_LINE_INFO
context_p->line_info.first_page_p = NULL;
context_p->line_info.last_page_p = NULL;
context_p->line_info.byte_code_position = 0;
context_p->line_info.line = 1;
context_p->line_info.column = 1;
#endif /* JERRY_LINE_INFO */
} /* parser_save_context */
/**
@ -2252,6 +2266,10 @@ parser_restore_context (parser_context_t *context_p, /**< context */
parser_free (context_p->scope_stack_p, context_p->scope_stack_size * sizeof (parser_scope_stack_t));
}
#if JERRY_LINE_INFO
parser_line_info_free (&context_p->line_info);
#endif /* JERRY_LINE_INFO */
/* Restore private part of the context. */
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
@ -2284,6 +2302,10 @@ parser_restore_context (parser_context_t *context_p, /**< context */
#ifndef JERRY_NDEBUG
context_p->context_stack_depth = saved_context_p->context_stack_depth;
#endif /* !JERRY_NDEBUG */
#if JERRY_LINE_INFO
context_p->line_info = saved_context_p->line_info;
#endif /* JERRY_LINE_INFO */
} /* parser_restore_context */
/**
@ -2579,10 +2601,7 @@ parser_parse_class_fields (parser_context_t *context_p) /**< context */
}
#if JERRY_LINE_INFO
if (context_p->token.line != context_p->last_line_info_line)
{
parser_emit_line_info (context_p, context_p->token.line, true);
}
parser_line_info_append (context_p, context_p->token.line, context_p->token.column);
#endif /* JERRY_LINE_INFO */
context_p->source_end_p = range.source_end_p;
@ -2822,6 +2841,10 @@ parser_raise_error (parser_context_t *context_p, /**< context */
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
parser_line_info_free (&saved_context_p->line_info);
#endif /* JERRY_LINE_INFO */
saved_context_p = saved_context_p->prev_context_p;
}

View File

@ -113,9 +113,6 @@ 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 block_result; /**< block result */
#if JERRY_LINE_INFO
uint32_t current_line; /**< currently executed line */
#endif /* JERRY_LINE_INFO */
uint16_t context_depth; /**< current context depth */
uint8_t status_flags; /**< combination of vm_frame_ctx_flags_t bits */
uint8_t call_operation; /**< perform a call or construct operation */

View File

@ -15,6 +15,7 @@
#include "ecma-array-object.h"
#include "ecma-helpers.h"
#include "ecma-line-info.h"
#include "jcontext.h"
#include "lit-char-helpers.h"
#include "vm.h"
@ -75,7 +76,8 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite
while (context_p != NULL)
{
ecma_value_t resource_name = ecma_get_resource_name (context_p->shared_p->bytecode_header_p);
const ecma_compiled_code_t *bytecode_header_p = context_p->shared_p->bytecode_header_p;
ecma_value_t resource_name = ecma_get_resource_name (bytecode_header_p);
ecma_string_t *str_p = ecma_get_string_from_value (resource_name);
ecma_stringbuilder_t builder = ecma_stringbuilder_create ();
@ -89,9 +91,27 @@ vm_get_backtrace (uint32_t max_depth) /**< maximum backtrace depth, 0 = unlimite
ecma_stringbuilder_append_byte (&builder, LIT_CHAR_COLON);
}
ecma_string_t *line_str_p = ecma_new_ecma_string_from_uint32 (context_p->current_line);
ecma_stringbuilder_append (&builder, line_str_p);
ecma_deref_ecma_string (line_str_p);
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO)
{
jerry_backtrace_location_t location;
ecma_line_info_get (ecma_compiled_code_get_line_info (bytecode_header_p),
(uint32_t) (context_p->byte_code_p - context_p->byte_code_start_p),
&location);
ecma_string_t *line_str_p = ecma_new_ecma_string_from_uint32 (location.line);
ecma_stringbuilder_append (&builder, line_str_p);
ecma_deref_ecma_string (line_str_p);
ecma_stringbuilder_append_byte (&builder, LIT_CHAR_COLON);
line_str_p = ecma_new_ecma_string_from_uint32 (location.column);
ecma_stringbuilder_append (&builder, line_str_p);
ecma_deref_ecma_string (line_str_p);
}
else
{
ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *)"1:1", 3);
}
ecma_string_t *builder_str_p = ecma_stringbuilder_finalize (&builder);
ecma_fast_array_set_property (array_p, index, ecma_make_string_value (builder_str_p));

View File

@ -4629,23 +4629,6 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
continue;
}
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
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_LINE_INFO */
case VM_OC_NONE:
default:
{
@ -5112,9 +5095,6 @@ vm_init_exec (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
frame_ctx_p->prev_context_p = JERRY_CONTEXT (vm_top_context_p);
frame_ctx_p->block_result = ECMA_VALUE_UNDEFINED;
#if JERRY_LINE_INFO
frame_ctx_p->current_line = 0;
#endif /* JERRY_LINE_INFO */
frame_ctx_p->context_depth = 0;
frame_ctx_p->status_flags = (uint8_t) ((shared_p->status_flags & VM_FRAME_CTX_DIRECT_EVAL)
| (bytecode_header_p->status_flags & VM_FRAME_CTX_IS_STRICT));

View File

@ -234,9 +234,6 @@ typedef enum
VM_OC_BREAKPOINT_ENABLED, /**< enabled breakpoint for debugger */
VM_OC_BREAKPOINT_DISABLED, /**< disabled breakpoint for debugger */
#endif /* JERRY_DEBUGGER */
#if JERRY_LINE_INFO
VM_OC_LINE, /**< line number of the next statement */
#endif /* JERRY_LINE_INFO */
#if JERRY_ESNEXT
VM_OC_CHECK_VAR, /**< check redeclared vars in the global scope */
VM_OC_CHECK_LET, /**< check redeclared lets in the global scope */
@ -319,9 +316,6 @@ typedef enum
VM_OC_BREAKPOINT_ENABLED = VM_OC_NONE, /**< enabled breakpoint for debugger is unused */
VM_OC_BREAKPOINT_DISABLED = VM_OC_NONE, /**< disabled breakpoint for debugger is unused */
#endif /* !JERRY_DEBUGGER */
#if !JERRY_LINE_INFO
VM_OC_LINE = VM_OC_NONE, /**< line number of the next statement is unused */
#endif /* !JERRY_LINE_INFO */
#if !JERRY_ESNEXT
VM_OC_EXT_VAR_EVAL = VM_OC_NONE, /**< variable and function evaluation for
* functions with separate argument context */

View File

@ -75,7 +75,7 @@ backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */
{
TEST_ASSERT (!jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 2);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 3);
TEST_ASSERT (handler_args_p[0] == *function_p);
TEST_ASSERT (handler_args_p[1] == *this_p);
return true;
@ -85,7 +85,7 @@ backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */
{
TEST_ASSERT (jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 7);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 6);
TEST_ASSERT (handler_args_p[2] == *function_p);
TEST_ASSERT (jerry_value_is_undefined (*this_p));
return true;
@ -96,7 +96,7 @@ backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */
TEST_ASSERT (frame_index == 3);
TEST_ASSERT (!jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 11);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 3);
TEST_ASSERT (handler_args_p[3] == *function_p);
TEST_ASSERT (global == *this_p);
@ -125,7 +125,7 @@ async_backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information
{
TEST_ASSERT (jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 3);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 3);
TEST_ASSERT (handler_args_p[0] == *function_p);
return true;
}
@ -133,7 +133,7 @@ async_backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information
TEST_ASSERT (frame_index == 2);
TEST_ASSERT (!jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 8);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 3);
TEST_ASSERT (handler_args_p[1] == *function_p);
return true;
} /* async_backtrace_callback */
@ -159,14 +159,14 @@ class_backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information
{
TEST_ASSERT (jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 3);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 12);
return false;
}
TEST_ASSERT (frame_index == 2);
TEST_ASSERT (jerry_backtrace_is_strict (frame_p));
TEST_ASSERT (location_p->line == 2);
TEST_ASSERT (location_p->column == 1);
TEST_ASSERT (location_p->column == 5);
return false;
} /* class_backtrace_callback */
@ -294,17 +294,17 @@ test_get_backtrace_api_call (void)
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");
compare (backtrace, 0, "something.js:2:3");
compare (backtrace, 1, "something.js:6:3");
compare (backtrace, 2, "something.js:10:3");
compare (backtrace, 3, "something.js:13:1");
jerry_release_value (backtrace);
/* Depth set to 2 this time. */
source_p = ("function f() {\n"
" return backtrace(2);\n"
" 1; return backtrace(2);\n"
"}\n"
"\n"
"function g() {\n"
@ -324,8 +324,8 @@ test_get_backtrace_api_call (void)
TEST_ASSERT (jerry_get_array_length (backtrace) == 2);
compare (backtrace, 0, "something_else.js:2");
compare (backtrace, 1, "something_else.js:6");
compare (backtrace, 0, "something_else.js:2:6");
compare (backtrace, 1, "something_else.js:6:3");
jerry_release_value (backtrace);
@ -338,7 +338,7 @@ test_get_backtrace_api_call (void)
"\n"
"function g() {\n"
" 'use strict';\n"
" return o.f();\n"
" 1; return o.f();\n"
"}\n"
"\n"
"function h() {\n"
@ -458,9 +458,9 @@ test_exception_backtrace (void)
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");
compare (backtrace, 0, "bad.js:2:3");
compare (backtrace, 1, "bad.js:6:3");
compare (backtrace, 2, "bad.js:9:1");
jerry_release_value (backtrace);
@ -505,7 +505,7 @@ test_large_line_count (void)
TEST_ASSERT (jerry_get_array_length (backtrace) == 1);
compare (backtrace, 0, "bad.js:385");
compare (backtrace, 0, "bad.js:385:1");
jerry_release_value (backtrace);