mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
This patch fixes #3751. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
3443 lines
111 KiB
C
3443 lines
111 KiB
C
/* 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 "js-parser-internal.h"
|
|
|
|
#if ENABLED (JERRY_PARSER)
|
|
#include "jcontext.h"
|
|
|
|
#include "ecma-helpers.h"
|
|
#include "lit-char-helpers.h"
|
|
|
|
/** \addtogroup parser Parser
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser JavaScript
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser_stmt Statement parser
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Parser statement types.
|
|
*
|
|
* When a new statement is added, the following
|
|
* arrays must be updated as well:
|
|
* - statement_lengths[]
|
|
* - parser_statement_flags[]
|
|
*/
|
|
typedef enum
|
|
{
|
|
PARSER_STATEMENT_START,
|
|
PARSER_STATEMENT_BLOCK,
|
|
#if ENABLED (JERRY_ES2015)
|
|
PARSER_STATEMENT_BLOCK_SCOPE,
|
|
PARSER_STATEMENT_PRIVATE_SCOPE,
|
|
PARSER_STATEMENT_BLOCK_CONTEXT,
|
|
PARSER_STATEMENT_PRIVATE_CONTEXT,
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
PARSER_STATEMENT_LABEL,
|
|
PARSER_STATEMENT_IF,
|
|
PARSER_STATEMENT_ELSE,
|
|
PARSER_STATEMENT_SWITCH,
|
|
PARSER_STATEMENT_SWITCH_NO_DEFAULT,
|
|
PARSER_STATEMENT_DO_WHILE,
|
|
PARSER_STATEMENT_WHILE,
|
|
PARSER_STATEMENT_FOR,
|
|
PARSER_STATEMENT_FOR_IN,
|
|
#if ENABLED (JERRY_ES2015)
|
|
PARSER_STATEMENT_FOR_OF,
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
PARSER_STATEMENT_WITH,
|
|
PARSER_STATEMENT_TRY,
|
|
} parser_statement_type_t;
|
|
|
|
/**
|
|
* Parser statement type flags.
|
|
*/
|
|
typedef enum
|
|
{
|
|
PARSER_STATM_NO_OPTS = 0, /**< no options */
|
|
PARSER_STATM_SINGLE_STATM = (1 << 0), /**< statment can form single statement context */
|
|
PARSER_STATM_HAS_BLOCK = (1 << 1), /**< statement always has a code block */
|
|
PARSER_STATM_BREAK_TARGET = (1 << 2), /**< break target statement */
|
|
PARSER_STATM_CONTINUE_TARGET = (1 << 3), /**< continue target statement */
|
|
PARSER_STATM_CONTEXT_BREAK = (1 << 4), /**< uses another instruction form when crosses their borders */
|
|
} parser_statement_flags_t;
|
|
|
|
/**
|
|
* Parser statement attributes.
|
|
* Note: the order of the attributes must be keep in sync with parser_statement_type_t
|
|
*/
|
|
static const uint8_t parser_statement_flags[] =
|
|
{
|
|
/* PARSER_STATEMENT_START */
|
|
PARSER_STATM_HAS_BLOCK,
|
|
/* PARSER_STATEMENT_BLOCK, */
|
|
PARSER_STATM_HAS_BLOCK,
|
|
#if ENABLED (JERRY_ES2015)
|
|
/* PARSER_STATEMENT_BLOCK_SCOPE, */
|
|
PARSER_STATM_HAS_BLOCK,
|
|
/* PARSER_STATEMENT_PRIVATE_SCOPE, */
|
|
PARSER_STATM_NO_OPTS,
|
|
/* PARSER_STATEMENT_BLOCK_CONTEXT, */
|
|
PARSER_STATM_HAS_BLOCK | PARSER_STATM_CONTEXT_BREAK,
|
|
/* PARSER_STATEMENT_PRIVATE_CONTEXT, */
|
|
PARSER_STATM_NO_OPTS,
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
/* PARSER_STATEMENT_LABEL */
|
|
PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_IF */
|
|
PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_ELSE */
|
|
PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_SWITCH */
|
|
PARSER_STATM_HAS_BLOCK | PARSER_STATM_BREAK_TARGET,
|
|
/* PARSER_STATEMENT_SWITCH_NO_DEFAULT */
|
|
PARSER_STATM_HAS_BLOCK | PARSER_STATM_BREAK_TARGET,
|
|
/* PARSER_STATEMENT_DO_WHILE */
|
|
PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_WHILE */
|
|
PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_FOR */
|
|
PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_FOR_IN */
|
|
PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK,
|
|
#if ENABLED (JERRY_ES2015)
|
|
/* PARSER_STATEMENT_FOR_OF */
|
|
PARSER_STATM_BREAK_TARGET | PARSER_STATM_CONTINUE_TARGET | PARSER_STATM_SINGLE_STATM | PARSER_STATM_CONTEXT_BREAK,
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
/* PARSER_STATEMENT_WITH */
|
|
PARSER_STATM_CONTEXT_BREAK | PARSER_STATM_SINGLE_STATM,
|
|
/* PARSER_STATEMENT_TRY */
|
|
PARSER_STATM_HAS_BLOCK | PARSER_STATM_CONTEXT_BREAK
|
|
};
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
/**
|
|
* Block statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint16_t scope_stack_top; /**< preserved top of scope stack */
|
|
uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */
|
|
} parser_block_statement_t;
|
|
|
|
/**
|
|
* Context of block statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
} parser_block_context_t;
|
|
|
|
#endif /* !ENABLED (JERRY_ES2015) */
|
|
|
|
/**
|
|
* Loop statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_node_t *branch_list_p; /**< list of breaks and continues targeting this statement */
|
|
} parser_loop_statement_t;
|
|
|
|
/**
|
|
* Label statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
lexer_lit_location_t label_ident; /**< name of the label */
|
|
parser_branch_node_t *break_list_p; /**< list of breaks targeting this label */
|
|
} parser_label_statement_t;
|
|
|
|
/**
|
|
* If/else statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
} parser_if_else_statement_t;
|
|
|
|
/**
|
|
* Switch statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t default_branch; /**< branch to the default case */
|
|
parser_branch_node_t *branch_list_p; /**< branches of case statements */
|
|
} parser_switch_statement_t;
|
|
|
|
/**
|
|
* Do-while statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint32_t start_offset; /**< start byte code offset */
|
|
} parser_do_while_statement_t;
|
|
|
|
/**
|
|
* While statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
scanner_location_t condition_location; /**< condition part */
|
|
uint32_t start_offset; /**< start byte code offset */
|
|
} parser_while_statement_t;
|
|
|
|
/**
|
|
* For statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
scanner_location_t condition_location; /**< condition part */
|
|
scanner_location_t expression_location; /**< expression part */
|
|
uint32_t start_offset; /**< start byte code offset */
|
|
} parser_for_statement_t;
|
|
|
|
/**
|
|
* For-in statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
uint32_t start_offset; /**< start byte code offset */
|
|
} parser_for_in_of_statement_t;
|
|
|
|
/**
|
|
* With statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_branch_t branch; /**< branch to the end */
|
|
} parser_with_statement_t;
|
|
|
|
/**
|
|
* Lexer token types.
|
|
*/
|
|
typedef enum
|
|
{
|
|
parser_try_block, /**< try block */
|
|
parser_catch_block, /**< catch block */
|
|
parser_finally_block, /**< finally block */
|
|
} parser_try_block_type_t;
|
|
|
|
/**
|
|
* Try statement.
|
|
*/
|
|
typedef struct
|
|
{
|
|
parser_try_block_type_t type; /**< current block type */
|
|
uint16_t scope_stack_top; /**< current top of scope stack */
|
|
uint16_t scope_stack_reg_top; /**< current top register of scope stack */
|
|
parser_branch_t branch; /**< branch to the end of the current block */
|
|
} parser_try_statement_t;
|
|
|
|
/**
|
|
* Returns the data consumed by a statement. It can be used
|
|
* to skip undesired frames on the stack during frame search.
|
|
*
|
|
* @return size consumed by a statement.
|
|
*/
|
|
static inline size_t
|
|
parser_statement_length (uint8_t type) /**< type of statement */
|
|
{
|
|
static const uint8_t statement_lengths[] =
|
|
{
|
|
/* PARSER_STATEMENT_BLOCK */
|
|
1,
|
|
#if ENABLED (JERRY_ES2015)
|
|
/* PARSER_STATEMENT_BLOCK_SCOPE */
|
|
(uint8_t) (sizeof (parser_block_statement_t) + 1),
|
|
/* PARSER_STATEMENT_PRIVATE_SCOPE */
|
|
(uint8_t) (sizeof (parser_block_statement_t) + 1),
|
|
/* PARSER_STATEMENT_BLOCK_CONTEXT */
|
|
(uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1),
|
|
/* PARSER_STATEMENT_PRIVATE_CONTEXT */
|
|
(uint8_t) (sizeof (parser_block_statement_t) + sizeof (parser_block_context_t) + 1),
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
/* PARSER_STATEMENT_LABEL */
|
|
(uint8_t) (sizeof (parser_label_statement_t) + 1),
|
|
/* PARSER_STATEMENT_IF */
|
|
(uint8_t) (sizeof (parser_if_else_statement_t) + 1),
|
|
/* PARSER_STATEMENT_ELSE */
|
|
(uint8_t) (sizeof (parser_if_else_statement_t) + 1),
|
|
/* PARSER_STATEMENT_SWITCH */
|
|
(uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
/* PARSER_STATEMENT_SWITCH_NO_DEFAULT */
|
|
(uint8_t) (sizeof (parser_switch_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
/* PARSER_STATEMENT_DO_WHILE */
|
|
(uint8_t) (sizeof (parser_do_while_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
/* PARSER_STATEMENT_WHILE */
|
|
(uint8_t) (sizeof (parser_while_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
/* PARSER_STATEMENT_FOR */
|
|
(uint8_t) (sizeof (parser_for_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
/* PARSER_STATEMENT_FOR_IN */
|
|
(uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
#if ENABLED (JERRY_ES2015)
|
|
/* PARSER_STATEMENT_FOR_OF */
|
|
(uint8_t) (sizeof (parser_for_in_of_statement_t) + sizeof (parser_loop_statement_t) + 1),
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
/* PARSER_STATEMENT_WITH */
|
|
(uint8_t) (sizeof (parser_with_statement_t) + 1 + 1),
|
|
/* PARSER_STATEMENT_TRY */
|
|
(uint8_t) (sizeof (parser_try_statement_t) + 1),
|
|
};
|
|
|
|
JERRY_ASSERT (type >= PARSER_STATEMENT_BLOCK && type <= PARSER_STATEMENT_TRY);
|
|
|
|
return statement_lengths[type - PARSER_STATEMENT_BLOCK];
|
|
} /* parser_statement_length */
|
|
|
|
/**
|
|
* Initialize stack iterator.
|
|
*/
|
|
static inline void
|
|
parser_stack_iterator_init (parser_context_t *context_p, /**< context */
|
|
parser_stack_iterator_t *iterator) /**< iterator */
|
|
{
|
|
iterator->current_p = context_p->stack.first_p;
|
|
iterator->current_position = context_p->stack.last_position;
|
|
} /* parser_stack_iterator_init */
|
|
|
|
/**
|
|
* Read the next byte from the stack.
|
|
*
|
|
* @return byte
|
|
*/
|
|
static inline uint8_t
|
|
parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator) /**< iterator */
|
|
{
|
|
JERRY_ASSERT (iterator->current_position > 0 && iterator->current_position <= PARSER_STACK_PAGE_SIZE);
|
|
return iterator->current_p->bytes[iterator->current_position - 1];
|
|
} /* parser_stack_iterator_read_uint8 */
|
|
|
|
/**
|
|
* Change last byte of the stack.
|
|
*/
|
|
static inline void
|
|
parser_stack_change_last_uint8 (parser_context_t *context_p, /**< context */
|
|
uint8_t new_value) /**< new value */
|
|
{
|
|
parser_mem_page_t *page_p = context_p->stack.first_p;
|
|
|
|
JERRY_ASSERT (page_p != NULL
|
|
&& context_p->stack_top_uint8 == page_p->bytes[context_p->stack.last_position - 1]);
|
|
|
|
page_p->bytes[context_p->stack.last_position - 1] = new_value;
|
|
context_p->stack_top_uint8 = new_value;
|
|
} /* parser_stack_change_last_uint8 */
|
|
|
|
/**
|
|
* Parse expression enclosed in parens.
|
|
*/
|
|
static inline void
|
|
parser_parse_enclosed_expr (parser_context_t *context_p) /**< context */
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_enclosed_expr */
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
|
|
/**
|
|
* Create a block context.
|
|
*
|
|
* @return true - when a context is created, false - otherwise
|
|
*/
|
|
static bool
|
|
parser_push_block_context (parser_context_t *context_p, /**< context */
|
|
bool is_private) /**< is private (bound to a statement) context */
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
|
|
|
parser_block_statement_t block_statement;
|
|
block_statement.scope_stack_top = context_p->scope_stack_top;
|
|
block_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
|
|
|
bool is_context_needed = false;
|
|
|
|
if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT))
|
|
{
|
|
parser_block_context_t block_context;
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc_forward_branch (context_p,
|
|
CBC_BLOCK_CREATE_CONTEXT,
|
|
&block_context.branch);
|
|
parser_stack_push (context_p, &block_context, sizeof (parser_block_context_t));
|
|
is_context_needed = true;
|
|
}
|
|
|
|
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
|
parser_stack_push (context_p, &block_statement, sizeof (parser_block_statement_t));
|
|
|
|
uint8_t statement_type;
|
|
|
|
if (is_private)
|
|
{
|
|
statement_type = (is_context_needed ? PARSER_STATEMENT_PRIVATE_CONTEXT : PARSER_STATEMENT_PRIVATE_SCOPE);
|
|
}
|
|
else
|
|
{
|
|
statement_type = (is_context_needed ? PARSER_STATEMENT_BLOCK_CONTEXT : PARSER_STATEMENT_BLOCK_SCOPE);
|
|
}
|
|
|
|
parser_stack_push_uint8 (context_p, statement_type);
|
|
|
|
return is_context_needed;
|
|
} /* parser_push_block_context */
|
|
|
|
/**
|
|
* Pop block context.
|
|
*/
|
|
static void
|
|
parser_pop_block_context (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT);
|
|
|
|
uint8_t type = context_p->stack_top_uint8;
|
|
|
|
parser_block_statement_t block_statement;
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &block_statement, sizeof (parser_block_statement_t));
|
|
|
|
context_p->scope_stack_top = block_statement.scope_stack_top;
|
|
context_p->scope_stack_reg_top = block_statement.scope_stack_reg_top;
|
|
|
|
if (type == PARSER_STATEMENT_BLOCK_CONTEXT || type == PARSER_STATEMENT_PRIVATE_CONTEXT)
|
|
{
|
|
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_block_context_t block_context;
|
|
parser_stack_pop (context_p, &block_context, sizeof (parser_block_context_t));
|
|
|
|
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
|
parser_set_branch_to_current_position (context_p, &block_context.branch);
|
|
}
|
|
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_pop_block_context */
|
|
|
|
/**
|
|
* Validate lexical context for a declaration.
|
|
*/
|
|
static void
|
|
parser_validate_lexical_context (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_LET
|
|
|| context_p->token.type == LEXER_KEYW_CONST
|
|
|| context_p->token.type == LEXER_KEYW_CLASS);
|
|
|
|
if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT);
|
|
}
|
|
} /* parser_validate_lexical_context */
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
/**
|
|
* Parse var statement.
|
|
*/
|
|
static void
|
|
parser_parse_var_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_VAR
|
|
|| context_p->token.type == LEXER_KEYW_LET
|
|
|| context_p->token.type == LEXER_KEYW_CONST);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
uint8_t declaration_type = context_p->token.type;
|
|
|
|
if (declaration_type != LEXER_KEYW_VAR)
|
|
{
|
|
parser_validate_lexical_context (context_p);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
while (true)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE))
|
|
{
|
|
parser_pattern_flags_t flags = PARSER_PATTERN_BINDING;
|
|
|
|
if (declaration_type == LEXER_KEYW_LET)
|
|
{
|
|
flags |= PARSER_PATTERN_LET;
|
|
}
|
|
else if (declaration_type == LEXER_KEYW_CONST)
|
|
{
|
|
flags |= PARSER_PATTERN_CONST;
|
|
}
|
|
|
|
parser_parse_initializer_by_next_char (context_p, flags);
|
|
}
|
|
else
|
|
{
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
#if ENABLED (JERRY_DEBUGGER) || ENABLED (JERRY_LINE_INFO)
|
|
parser_line_counter_t ident_line_counter = context_p->token.line;
|
|
#endif /* ENABLED (JERRY_DEBUGGER) || ENABLED (JERRY_LINE_INFO) */
|
|
|
|
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
parser_module_append_export_name (context_p);
|
|
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (declaration_type != LEXER_KEYW_VAR
|
|
&& context_p->token.keyword_type == LEXER_KEYW_LET)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEXICAL_LET_BINDING);
|
|
}
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED);
|
|
parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_ASSIGN)
|
|
{
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
&& ident_line_counter != context_p->last_breakpoint_line)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
|
parser_flush_cbc (context_p);
|
|
|
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, ident_line_counter);
|
|
|
|
context_p->last_breakpoint_line = ident_line_counter;
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
|
|
#if ENABLED (JERRY_LINE_INFO)
|
|
if (ident_line_counter != context_p->last_line_info_line)
|
|
{
|
|
parser_emit_line_info (context_p, ident_line_counter, false);
|
|
}
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
uint16_t index = context_p->lit_object.index;
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
|
|
cbc_opcode_t opcode = CBC_ASSIGN_SET_IDENT;
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (declaration_type != LEXER_KEYW_VAR
|
|
&& (index < PARSER_REGISTER_START))
|
|
{
|
|
opcode = CBC_INIT_LET;
|
|
|
|
if (scanner_literal_is_created (context_p, index))
|
|
{
|
|
opcode = CBC_ASSIGN_LET_CONST;
|
|
}
|
|
else if (declaration_type == LEXER_KEYW_CONST)
|
|
{
|
|
opcode = CBC_INIT_CONST;
|
|
}
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
parser_emit_cbc_literal (context_p, (uint16_t) opcode, index);
|
|
}
|
|
#if ENABLED (JERRY_ES2015)
|
|
else if (declaration_type == LEXER_KEYW_LET)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED);
|
|
|
|
uint16_t index = context_p->lit_object.index;
|
|
cbc_opcode_t opcode = CBC_MOV_IDENT;
|
|
|
|
if (index < PARSER_REGISTER_START)
|
|
{
|
|
opcode = (scanner_literal_is_created (context_p, index) ? CBC_ASSIGN_LET_CONST
|
|
: CBC_INIT_LET);
|
|
}
|
|
|
|
parser_emit_cbc_literal (context_p, (uint16_t) opcode, index);
|
|
}
|
|
else if (declaration_type == LEXER_KEYW_CONST)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_MISSING_ASSIGN_AFTER_CONST);
|
|
}
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
if (context_p->token.type != LEXER_COMMA)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT);
|
|
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
} /* parser_parse_var_statement */
|
|
|
|
/**
|
|
* Parse function statement.
|
|
*/
|
|
static void
|
|
parser_parse_function_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (JERRY_UNLIKELY (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM))
|
|
{
|
|
if (context_p->status_flags & PARSER_IS_STRICT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT);
|
|
}
|
|
|
|
if (context_p->stack_top_uint8 == PARSER_STATEMENT_IF
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_ELSE)
|
|
{
|
|
/* There must be a parser error later if this check fails. */
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
parser_push_block_context (context_p, true);
|
|
}
|
|
}
|
|
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_LABEL)
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t) + 1);
|
|
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
|
|
if (type == PARSER_STATEMENT_LABEL)
|
|
{
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t) + 1);
|
|
continue;
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_HAS_BLOCK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
parser_raise_error (context_p, PARSER_ERR_LABELLED_FUNC_NOT_IN_BLOCK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT);
|
|
}
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
parser_line_counter_t debugger_line = context_p->token.line;
|
|
parser_line_counter_t debugger_column = context_p->token.column;
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
bool is_generator_function = false;
|
|
|
|
if (lexer_consume_generator (context_p))
|
|
{
|
|
is_generator_function = true;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_expect_identifier (context_p, LEXER_NEW_IDENT_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_REDECLARED);
|
|
parser_raise_error (context_p, PARSER_ERR_VARIABLE_REDECLARED);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
parser_module_append_export_name (context_p);
|
|
context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT);
|
|
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
|
|
uint32_t status_flags = PARSER_FUNCTION_CLOSURE;
|
|
|
|
if (context_p->token.keyword_type >= LEXER_FIRST_NON_STRICT_ARGUMENTS)
|
|
{
|
|
status_flags |= PARSER_HAS_NON_STRICT_ARG;
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (is_generator_function)
|
|
{
|
|
status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD;
|
|
}
|
|
|
|
if (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC)
|
|
{
|
|
status_flags |= PARSER_IS_ASYNC_FUNCTION | PARSER_DISALLOW_AWAIT_YIELD;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
{
|
|
lexer_literal_t *name_p = context_p->lit_object.literal_p;
|
|
jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME,
|
|
JERRY_DEBUGGER_NO_SUBTYPE,
|
|
name_p->u.char_p,
|
|
name_p->prop.length);
|
|
|
|
/* Reset token position for the function. */
|
|
context_p->token.line = debugger_line;
|
|
context_p->token.column = debugger_column;
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
|
|
JERRY_ASSERT (context_p->scope_stack_top >= 2);
|
|
parser_scope_stack_t *scope_stack_p = context_p->scope_stack_p + context_p->scope_stack_top - 2;
|
|
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
|
|
while (literal_index != scope_stack_p->map_from)
|
|
{
|
|
scope_stack_p--;
|
|
|
|
JERRY_ASSERT (scope_stack_p >= context_p->scope_stack_p);
|
|
}
|
|
|
|
JERRY_ASSERT (scope_stack_p[1].map_from == PARSER_SCOPE_STACK_FUNC);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (!(context_p->status_flags & PARSER_IS_STRICT)
|
|
&& (scope_stack_p >= context_p->scope_stack_p + context_p->scope_stack_global_end))
|
|
{
|
|
bool copy_value = true;
|
|
|
|
parser_scope_stack_t *stack_p = context_p->scope_stack_p;
|
|
|
|
while (stack_p < scope_stack_p)
|
|
{
|
|
if (literal_index == stack_p->map_from
|
|
&& (stack_p->map_to & PARSER_SCOPE_STACK_NO_FUNCTION_COPY))
|
|
{
|
|
copy_value = false;
|
|
break;
|
|
}
|
|
stack_p++;
|
|
}
|
|
|
|
if (copy_value)
|
|
{
|
|
stack_p = context_p->scope_stack_p;
|
|
|
|
while (stack_p < scope_stack_p)
|
|
{
|
|
if (literal_index == stack_p->map_from)
|
|
{
|
|
JERRY_ASSERT (!(stack_p->map_to & PARSER_SCOPE_STACK_NO_FUNCTION_COPY));
|
|
|
|
uint16_t map_to = scanner_decode_map_to (stack_p);
|
|
uint16_t opcode = ((map_to >= PARSER_REGISTER_START) ? CBC_ASSIGN_LITERAL_SET_IDENT
|
|
: CBC_COPY_TO_GLOBAL);
|
|
|
|
parser_emit_cbc_literal_value (context_p,
|
|
opcode,
|
|
scanner_decode_map_to (scope_stack_p),
|
|
map_to);
|
|
break;
|
|
}
|
|
stack_p++;
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT))
|
|
{
|
|
parser_pop_block_context (context_p);
|
|
}
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_literal_t *literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to);
|
|
|
|
JERRY_ASSERT ((literal_p->type == LEXER_UNUSED_LITERAL || literal_p->type == LEXER_FUNCTION_LITERAL)
|
|
&& literal_p->status_flags == 0);
|
|
|
|
ecma_compiled_code_t *compiled_code_p = parser_parse_function (context_p, status_flags);
|
|
|
|
if (literal_p->type == LEXER_FUNCTION_LITERAL)
|
|
{
|
|
ecma_bytecode_deref (literal_p->u.bytecode_p);
|
|
}
|
|
|
|
literal_p->u.bytecode_p = compiled_code_p;
|
|
literal_p->type = LEXER_FUNCTION_LITERAL;
|
|
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_function_statement */
|
|
|
|
/**
|
|
* Parse if statement (starting part).
|
|
*/
|
|
static void
|
|
parser_parse_if_statement_start (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_if_else_statement_t if_statement;
|
|
|
|
parser_parse_enclosed_expr (context_p);
|
|
|
|
parser_emit_cbc_forward_branch (context_p,
|
|
CBC_BRANCH_IF_FALSE_FORWARD,
|
|
&if_statement.branch);
|
|
|
|
parser_stack_push (context_p, &if_statement, sizeof (parser_if_else_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_IF);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_parse_if_statement_start */
|
|
|
|
/**
|
|
* Parse if statement (ending part).
|
|
*
|
|
* @return true - if parsing an 'else' statement
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
parser_parse_if_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_if_else_statement_t if_statement;
|
|
parser_if_else_statement_t else_statement;
|
|
parser_stack_iterator_t iterator;
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_IF);
|
|
|
|
if (context_p->token.type != LEXER_KEYW_ELSE)
|
|
{
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &if_statement, sizeof (parser_if_else_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_set_branch_to_current_position (context_p, &if_statement.branch);
|
|
|
|
return false;
|
|
}
|
|
|
|
parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_ELSE);
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &if_statement, sizeof (parser_if_else_statement_t));
|
|
|
|
parser_emit_cbc_forward_branch (context_p,
|
|
CBC_JUMP_FORWARD,
|
|
&else_statement.branch);
|
|
|
|
parser_set_branch_to_current_position (context_p, &if_statement.branch);
|
|
|
|
parser_stack_iterator_write (&iterator, &else_statement, sizeof (parser_if_else_statement_t));
|
|
|
|
lexer_next_token (context_p);
|
|
return true;
|
|
} /* parser_parse_if_statement_end */
|
|
|
|
/**
|
|
* Parse with statement (starting part).
|
|
*/
|
|
static void
|
|
parser_parse_with_statement_start (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_with_statement_t with_statement;
|
|
|
|
if (context_p->status_flags & PARSER_IS_STRICT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_WITH_NOT_ALLOWED);
|
|
}
|
|
|
|
parser_parse_enclosed_expr (context_p);
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
uint8_t inside_with = (context_p->status_flags & PARSER_INSIDE_WITH) != 0;
|
|
|
|
context_p->status_flags |= PARSER_INSIDE_WITH;
|
|
parser_emit_cbc_ext_forward_branch (context_p,
|
|
CBC_EXT_WITH_CREATE_CONTEXT,
|
|
&with_statement.branch);
|
|
|
|
parser_stack_push (context_p, &with_statement, sizeof (parser_with_statement_t));
|
|
parser_stack_push_uint8 (context_p, inside_with);
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WITH);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_parse_with_statement_start */
|
|
|
|
/**
|
|
* Parse with statement (ending part).
|
|
*/
|
|
static void
|
|
parser_parse_with_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_with_statement_t with_statement;
|
|
|
|
JERRY_ASSERT (context_p->status_flags & PARSER_INSIDE_WITH);
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
|
|
if (!context_p->stack_top_uint8)
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~PARSER_INSIDE_WITH;
|
|
}
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &with_statement, sizeof (parser_with_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_flush_cbc (context_p);
|
|
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION);
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
|
parser_set_branch_to_current_position (context_p, &with_statement.branch);
|
|
} /* parser_parse_with_statement_end */
|
|
|
|
/**
|
|
* Parse do-while statement (ending part).
|
|
*/
|
|
static void
|
|
parser_parse_do_while_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_DO_WHILE);
|
|
|
|
if (context_p->token.type != LEXER_KEYW_WHILE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_WHILE_EXPECTED);
|
|
}
|
|
|
|
parser_stack_iterator_t iterator;
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
|
|
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p);
|
|
|
|
parser_parse_enclosed_expr (context_p);
|
|
|
|
if (context_p->last_cbc_opcode != CBC_PUSH_FALSE)
|
|
{
|
|
cbc_opcode_t opcode = CBC_BRANCH_IF_TRUE_BACKWARD;
|
|
if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_BRANCH_IF_FALSE_BACKWARD;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_JUMP_BACKWARD;
|
|
}
|
|
|
|
parser_do_while_statement_t do_while_statement;
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &do_while_statement, sizeof (parser_do_while_statement_t));
|
|
|
|
parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, do_while_statement.start_offset);
|
|
}
|
|
else
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
|
|
parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_do_while_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
|
} /* parser_parse_do_while_statement_end */
|
|
|
|
/**
|
|
* Parse while statement (starting part).
|
|
*/
|
|
static void
|
|
parser_parse_while_statement_start (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_while_statement_t while_statement;
|
|
parser_loop_statement_t loop;
|
|
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_WHILE);
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED);
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p
|
|
|| context_p->next_scanner_info_p->type == SCANNER_TYPE_WHILE);
|
|
|
|
if (context_p->next_scanner_info_p->source_p != context_p->source_p)
|
|
{
|
|
/* The prescanner couldn't find the end of the while condition. */
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
JERRY_ASSERT (context_p->token.type != LEXER_RIGHT_PAREN);
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &while_statement.branch);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
|
|
|
while_statement.start_offset = context_p->byte_code_size;
|
|
scanner_get_location (&while_statement.condition_location, context_p);
|
|
|
|
scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location);
|
|
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
loop.branch_list_p = NULL;
|
|
|
|
parser_stack_push (context_p, &while_statement, sizeof (parser_while_statement_t));
|
|
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_WHILE);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_parse_while_statement_start */
|
|
|
|
/**
|
|
* Parse while statement (ending part).
|
|
*/
|
|
static void JERRY_ATTR_NOINLINE
|
|
parser_parse_while_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_while_statement_t while_statement;
|
|
parser_loop_statement_t loop;
|
|
lexer_token_t current_token;
|
|
scanner_location_t location;
|
|
cbc_opcode_t opcode;
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_WHILE);
|
|
|
|
parser_stack_iterator_t iterator;
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &while_statement, sizeof (parser_while_statement_t));
|
|
|
|
scanner_get_location (&location, context_p);
|
|
current_token = context_p->token;
|
|
|
|
parser_set_branch_to_current_position (context_p, &while_statement.branch);
|
|
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
scanner_set_location (context_p, &while_statement.condition_location);
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
opcode = CBC_BRANCH_IF_TRUE_BACKWARD;
|
|
if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_BRANCH_IF_FALSE_BACKWARD;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_JUMP_BACKWARD;
|
|
}
|
|
|
|
parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_while_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, while_statement.start_offset);
|
|
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
/* Calling scanner_seek is unnecessary because all
|
|
* info blocks inside the while statement should be processed. */
|
|
scanner_set_location (context_p, &location);
|
|
context_p->token = current_token;
|
|
} /* parser_parse_while_statement_end */
|
|
|
|
/**
|
|
* Check whether the opcode is a valid LeftHandSide expression
|
|
* and convert it back to an assignment.
|
|
*
|
|
* @return the compatible assignment opcode
|
|
*/
|
|
static uint16_t
|
|
parser_check_left_hand_side_expression (parser_context_t *context_p, /**< context */
|
|
uint16_t opcode) /**< opcode to check */
|
|
{
|
|
if (opcode == CBC_PUSH_LITERAL
|
|
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
return CBC_ASSIGN_SET_IDENT;
|
|
}
|
|
else if (opcode == CBC_PUSH_PROP)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
return CBC_ASSIGN;
|
|
}
|
|
else if (opcode == CBC_PUSH_PROP_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
return CBC_ASSIGN_PROP_LITERAL;
|
|
}
|
|
else if (opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
return CBC_ASSIGN;
|
|
}
|
|
else if (opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL;
|
|
return CBC_ASSIGN;
|
|
}
|
|
else
|
|
{
|
|
/* Invalid LeftHandSide expression. */
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
|
return CBC_ASSIGN;
|
|
}
|
|
|
|
return opcode;
|
|
} /* parser_check_left_hand_side_expression */
|
|
|
|
/**
|
|
* Parse for statement (starting part).
|
|
*/
|
|
static void
|
|
parser_parse_for_statement_start (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FOR);
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED);
|
|
}
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
parser_for_in_of_statement_t for_in_of_statement;
|
|
scanner_location_t start_location, end_location;
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN
|
|
|| context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_OF);
|
|
|
|
bool is_for_in = (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN);
|
|
end_location = ((scanner_location_info_t *) context_p->next_scanner_info_p)->location;
|
|
|
|
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
|
|
|
scanner_get_location (&start_location, context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
uint8_t token_type = LEXER_EOS;
|
|
bool has_context = false;
|
|
|
|
if (context_p->token.type == LEXER_KEYW_VAR
|
|
|| context_p->token.type == LEXER_KEYW_LET
|
|
|| context_p->token.type == LEXER_KEYW_CONST)
|
|
{
|
|
token_type = context_p->token.type;
|
|
has_context = context_p->next_scanner_info_p->source_p == context_p->source_p;
|
|
JERRY_ASSERT (!has_context || context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
|
scanner_get_location (&start_location, context_p);
|
|
|
|
/* TODO: remove this after the pre-scanner supports strict mode detection. */
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p
|
|
&& context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION)
|
|
{
|
|
scanner_release_next (context_p, sizeof (scanner_info_t));
|
|
}
|
|
}
|
|
else if (context_p->token.type == LEXER_LITERAL && lexer_token_is_let (context_p))
|
|
{
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p
|
|
&& context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION)
|
|
{
|
|
scanner_release_next (context_p, sizeof (scanner_info_t));
|
|
}
|
|
else
|
|
{
|
|
token_type = LEXER_KEYW_LET;
|
|
has_context = (context_p->next_scanner_info_p->source_p == context_p->source_p);
|
|
scanner_get_location (&start_location, context_p);
|
|
}
|
|
}
|
|
|
|
if (has_context)
|
|
{
|
|
has_context = parser_push_block_context (context_p, true);
|
|
}
|
|
|
|
scanner_set_location (context_p, &end_location);
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR_IN);
|
|
|
|
bool is_for_in = true;
|
|
scanner_get_location (&start_location, context_p);
|
|
|
|
scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location);
|
|
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
/* The length of both 'in' and 'of' is two. */
|
|
const uint8_t *source_end_p = context_p->source_p - 2;
|
|
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth,
|
|
is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION
|
|
: PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc_ext_forward_branch (context_p,
|
|
is_for_in ? CBC_EXT_FOR_IN_CREATE_CONTEXT
|
|
: CBC_EXT_FOR_OF_CREATE_CONTEXT,
|
|
&for_in_of_statement.branch);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
|
for_in_of_statement.start_offset = context_p->byte_code_size;
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (has_context)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_CONTEXT);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
/* The expression parser must not read the 'in' or 'of' tokens. */
|
|
scanner_get_location (&end_location, context_p);
|
|
scanner_set_location (context_p, &start_location);
|
|
|
|
const uint8_t *original_source_end_p = context_p->source_end_p;
|
|
context_p->source_end_p = source_end_p;
|
|
scanner_seek (context_p);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (token_type == LEXER_EOS)
|
|
{
|
|
lexer_next_token (context_p);
|
|
}
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
lexer_next_token (context_p);
|
|
|
|
uint8_t token_type = context_p->token.type;
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
switch (token_type)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
case LEXER_KEYW_LET:
|
|
case LEXER_KEYW_CONST:
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
case LEXER_KEYW_VAR:
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE))
|
|
{
|
|
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
|
: CBC_EXT_FOR_OF_GET_NEXT);
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_INITIALIZER);
|
|
|
|
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
|
}
|
|
|
|
parser_pattern_flags_t flags = (PARSER_PATTERN_BINDING | PARSER_PATTERN_TARGET_ON_STACK);
|
|
|
|
if (token_type == LEXER_KEYW_LET)
|
|
{
|
|
flags |= PARSER_PATTERN_LET;
|
|
}
|
|
else if (token_type == LEXER_KEYW_CONST)
|
|
{
|
|
flags |= PARSER_PATTERN_CONST;
|
|
}
|
|
|
|
parser_parse_initializer_by_next_char (context_p, flags);
|
|
break;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_ASSIGN)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->status_flags & PARSER_IS_STRICT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_FOR_IN_OF_DECLARATION);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
parser_branch_t branch;
|
|
|
|
/* Initialiser is never executed. */
|
|
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &branch);
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA);
|
|
parser_set_branch_to_current_position (context_p, &branch);
|
|
}
|
|
|
|
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
|
: CBC_EXT_FOR_OF_GET_NEXT);
|
|
#if ENABLED (JERRY_ES2015)
|
|
#ifndef JERRY_NDEBUG
|
|
if (literal_index < PARSER_REGISTER_START
|
|
&& has_context
|
|
&& !scanner_literal_is_created (context_p, literal_index))
|
|
{
|
|
context_p->global_status_flags |= ECMA_PARSE_INTERNAL_FOR_IN_OFF_CONTEXT_ERROR;
|
|
}
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
uint16_t opcode = (has_context ? CBC_ASSIGN_LET_CONST : CBC_ASSIGN_SET_IDENT);
|
|
parser_emit_cbc_literal (context_p, opcode, literal_index);
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, literal_index);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
uint16_t opcode;
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR_LEFT_HAND_SIDE);
|
|
|
|
opcode = context_p->last_cbc_opcode;
|
|
|
|
/* The CBC_EXT_FOR_IN_CREATE_CONTEXT flushed the opcode combiner. */
|
|
JERRY_ASSERT (opcode != CBC_PUSH_TWO_LITERALS
|
|
&& opcode != CBC_PUSH_THREE_LITERALS);
|
|
|
|
opcode = parser_check_left_hand_side_expression (context_p, opcode);
|
|
|
|
parser_emit_cbc_ext (context_p, is_for_in ? CBC_EXT_FOR_IN_GET_NEXT
|
|
: CBC_EXT_FOR_OF_GET_NEXT);
|
|
parser_flush_cbc (context_p);
|
|
|
|
context_p->last_cbc_opcode = opcode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (context_p->token.type != LEXER_EOS)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
parser_raise_error (context_p, is_for_in ? PARSER_ERR_IN_EXPECTED : PARSER_ERR_OF_EXPECTED);
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
parser_raise_error (context_p, PARSER_ERR_IN_EXPECTED);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
scanner_set_location (context_p, &end_location);
|
|
context_p->source_end_p = original_source_end_p;
|
|
lexer_next_token (context_p);
|
|
|
|
loop.branch_list_p = NULL;
|
|
|
|
parser_stack_push (context_p, &for_in_of_statement, sizeof (parser_for_in_of_statement_t));
|
|
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
#if ENABLED (JERRY_ES2015)
|
|
parser_stack_push_uint8 (context_p, is_for_in ? PARSER_STATEMENT_FOR_IN
|
|
: PARSER_STATEMENT_FOR_OF);
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR_IN);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
return;
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_SEMICOLON)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
const uint8_t *source_p = context_p->source_p;
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
switch (context_p->token.type)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
case LEXER_LITERAL:
|
|
{
|
|
if (!lexer_token_is_let (context_p))
|
|
{
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
break;
|
|
}
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p
|
|
&& context_p->next_scanner_info_p->type != SCANNER_TYPE_BLOCK)
|
|
{
|
|
if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION)
|
|
{
|
|
scanner_release_next (context_p, sizeof (scanner_info_t));
|
|
}
|
|
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
break;
|
|
}
|
|
|
|
context_p->token.type = LEXER_KEYW_LET;
|
|
/* FALLTHRU */
|
|
}
|
|
case LEXER_KEYW_LET:
|
|
case LEXER_KEYW_CONST:
|
|
{
|
|
if (context_p->next_scanner_info_p->source_p == source_p)
|
|
{
|
|
parser_push_block_context (context_p, true);
|
|
}
|
|
/* FALLTHRU */
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
case LEXER_KEYW_VAR:
|
|
{
|
|
parser_parse_var_statement (context_p);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (context_p->token.type != LEXER_SEMICOLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED);
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->source_p != context_p->source_p
|
|
|| context_p->next_scanner_info_p->type == SCANNER_TYPE_FOR);
|
|
|
|
if (context_p->next_scanner_info_p->source_p != context_p->source_p
|
|
|| ((scanner_for_info_t *) context_p->next_scanner_info_p)->end_location.source_p == NULL)
|
|
{
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
/* Even though the scanning is failed, there might be valid statements
|
|
* inside the for statement which depend on scanner info blocks. */
|
|
scanner_release_next (context_p, sizeof (scanner_for_info_t));
|
|
}
|
|
|
|
/* The prescanner couldn't find the second semicolon or the closing paranthesis. */
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_SEMICOLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
|
|
JERRY_ASSERT (context_p->token.type != LEXER_RIGHT_PAREN);
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
parser_for_statement_t for_statement;
|
|
scanner_for_info_t *for_info_p = (scanner_for_info_t *) context_p->next_scanner_info_p;
|
|
|
|
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &for_statement.branch);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
|
|
|
for_statement.start_offset = context_p->byte_code_size;
|
|
scanner_get_location (&for_statement.condition_location, context_p);
|
|
for_statement.expression_location = for_info_p->expression_location;
|
|
|
|
scanner_set_location (context_p, &for_info_p->end_location);
|
|
scanner_release_next (context_p, sizeof (scanner_for_info_t));
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
loop.branch_list_p = NULL;
|
|
|
|
parser_stack_push (context_p, &for_statement, sizeof (parser_for_statement_t));
|
|
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_FOR);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_parse_for_statement_start */
|
|
|
|
/**
|
|
* Parse for statement (ending part).
|
|
*/
|
|
static void JERRY_ATTR_NOINLINE
|
|
parser_parse_for_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_for_statement_t for_statement;
|
|
parser_loop_statement_t loop;
|
|
lexer_token_t current_token;
|
|
scanner_location_t location;
|
|
cbc_opcode_t opcode;
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR);
|
|
|
|
parser_stack_iterator_t iterator;
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &for_statement, sizeof (parser_for_statement_t));
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
bool has_block_context = false;
|
|
uint8_t next_statement_type;
|
|
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_for_statement_t));
|
|
parser_stack_iterator_read (&iterator, &next_statement_type, 1);
|
|
|
|
if (next_statement_type == PARSER_STATEMENT_PRIVATE_CONTEXT)
|
|
{
|
|
has_block_context = true;
|
|
}
|
|
#endif
|
|
|
|
scanner_get_location (&location, context_p);
|
|
current_token = context_p->token;
|
|
|
|
scanner_set_location (context_p, &for_statement.expression_location);
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (has_block_context)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_CLONE_FULL_CONTEXT);
|
|
}
|
|
#endif
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
}
|
|
|
|
parser_set_branch_to_current_position (context_p, &for_statement.branch);
|
|
|
|
scanner_set_location (context_p, &for_statement.condition_location);
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_SEMICOLON)
|
|
{
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_SEMICOLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED);
|
|
}
|
|
|
|
opcode = CBC_BRANCH_IF_TRUE_BACKWARD;
|
|
if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_BRANCH_IF_FALSE_BACKWARD;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TRUE)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_JUMP_BACKWARD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
opcode = CBC_JUMP_BACKWARD;
|
|
}
|
|
|
|
parser_stack_pop (context_p, NULL, 1 + sizeof (parser_loop_statement_t) + sizeof (parser_for_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_emit_cbc_backward_branch (context_p, (uint16_t) opcode, for_statement.start_offset);
|
|
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT)
|
|
{
|
|
parser_pop_block_context (context_p);
|
|
}
|
|
#endif
|
|
|
|
/* Calling scanner_seek is unnecessary because all
|
|
* info blocks inside the for statement should be processed. */
|
|
scanner_set_location (context_p, &location);
|
|
context_p->token = current_token;
|
|
} /* parser_parse_for_statement_end */
|
|
|
|
/**
|
|
* Parse switch statement (starting part).
|
|
*/
|
|
static void JERRY_ATTR_NOINLINE
|
|
parser_parse_switch_statement_start (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_switch_statement_t switch_statement;
|
|
parser_loop_statement_t loop;
|
|
parser_stack_iterator_t iterator;
|
|
scanner_location_t start_location;
|
|
bool switch_case_was_found;
|
|
bool default_case_was_found;
|
|
parser_branch_node_t *case_branches_p = NULL;
|
|
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_SWITCH);
|
|
|
|
parser_parse_enclosed_expr (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
|
|
}
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p - 1)
|
|
{
|
|
parser_push_block_context (context_p, true);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->source_p == context_p->source_p
|
|
&& context_p->next_scanner_info_p->type == SCANNER_TYPE_SWITCH);
|
|
|
|
scanner_case_info_t *case_info_p = ((scanner_switch_info_t *) context_p->next_scanner_info_p)->case_p;
|
|
scanner_set_active (context_p);
|
|
|
|
if (case_info_p == NULL)
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_BRACE)
|
|
{
|
|
scanner_release_active (context_p, sizeof (scanner_switch_info_t));
|
|
|
|
parser_emit_cbc (context_p, CBC_POP);
|
|
parser_flush_cbc (context_p);
|
|
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
return;
|
|
}
|
|
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_SWITCH);
|
|
}
|
|
|
|
scanner_get_location (&start_location, context_p);
|
|
|
|
/* The reason of using an iterator is error management. If an error
|
|
* occures, parser_free_jumps() free all data. However, the branches
|
|
* created by parser_emit_cbc_forward_branch_item() would not be freed.
|
|
* To free these branches, the current switch data is always stored
|
|
* on the stack. If any change happens, this data is updated. Updates
|
|
* are done using the iterator. */
|
|
|
|
switch_statement.branch_list_p = NULL;
|
|
loop.branch_list_p = NULL;
|
|
|
|
parser_stack_push (context_p, &switch_statement, sizeof (parser_switch_statement_t));
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_SWITCH);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
switch_case_was_found = false;
|
|
default_case_was_found = false;
|
|
|
|
#if ENABLED (JERRY_LINE_INFO)
|
|
uint32_t last_line_info_line = context_p->last_line_info_line;
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
do
|
|
{
|
|
scanner_set_location (context_p, &case_info_p->location);
|
|
scanner_seek (context_p);
|
|
case_info_p = case_info_p->next_p;
|
|
|
|
/* The last letter of case and default is 'e' and 't' respectively. */
|
|
JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LOWERCASE_E
|
|
|| context_p->source_p[-1] == LIT_CHAR_LOWERCASE_T);
|
|
|
|
bool is_default = context_p->source_p[-1] == LIT_CHAR_LOWERCASE_T;
|
|
lexer_next_token (context_p);
|
|
|
|
if (is_default)
|
|
{
|
|
if (default_case_was_found)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_MULTIPLE_DEFAULTS_NOT_ALLOWED);
|
|
}
|
|
|
|
if (context_p->token.type != LEXER_COLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
|
|
}
|
|
|
|
default_case_was_found = true;
|
|
continue;
|
|
}
|
|
|
|
switch_case_was_found = true;
|
|
|
|
#if ENABLED (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);
|
|
}
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
if (context_p->token.type != LEXER_COLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
|
|
}
|
|
|
|
uint16_t opcode = CBC_BRANCH_IF_STRICT_EQUAL;
|
|
|
|
if (case_info_p == NULL
|
|
|| (case_info_p->next_p == NULL && case_info_p->location.source_p[-1] == LIT_CHAR_LOWERCASE_T))
|
|
{
|
|
/* There are no more 'case' statements in the switch. */
|
|
parser_emit_cbc (context_p, CBC_STRICT_EQUAL);
|
|
opcode = CBC_BRANCH_IF_TRUE_FORWARD;
|
|
}
|
|
|
|
parser_branch_node_t *new_case_p = parser_emit_cbc_forward_branch_item (context_p, opcode, NULL);
|
|
|
|
if (case_branches_p == NULL)
|
|
{
|
|
switch_statement.branch_list_p = new_case_p;
|
|
parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
}
|
|
else
|
|
{
|
|
case_branches_p->next_p = new_case_p;
|
|
}
|
|
|
|
case_branches_p = new_case_p;
|
|
}
|
|
while (case_info_p != NULL);
|
|
|
|
JERRY_ASSERT (switch_case_was_found || default_case_was_found);
|
|
|
|
#if ENABLED (JERRY_LINE_INFO)
|
|
context_p->last_line_info_line = last_line_info_line;
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
if (!switch_case_was_found)
|
|
{
|
|
/* There was no case statement, so the expression result
|
|
* of the switch must be popped from the stack */
|
|
parser_emit_cbc (context_p, CBC_POP);
|
|
}
|
|
|
|
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &switch_statement.default_branch);
|
|
parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
|
|
if (!default_case_was_found)
|
|
{
|
|
parser_stack_change_last_uint8 (context_p, PARSER_STATEMENT_SWITCH_NO_DEFAULT);
|
|
}
|
|
|
|
scanner_release_switch_cases (((scanner_switch_info_t *) context_p->active_scanner_info_p)->case_p);
|
|
scanner_release_active (context_p, sizeof (scanner_switch_info_t));
|
|
|
|
scanner_set_location (context_p, &start_location);
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_switch_statement_start */
|
|
|
|
/**
|
|
* Parse try statement (ending part).
|
|
*/
|
|
static void
|
|
parser_parse_try_statement_end (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_try_statement_t try_statement;
|
|
parser_stack_iterator_t iterator;
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY);
|
|
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &try_statement, sizeof (parser_try_statement_t));
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
context_p->scope_stack_top = try_statement.scope_stack_top;
|
|
context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top;
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (try_statement.type == parser_finally_block)
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
|
parser_set_branch_to_current_position (context_p, &try_statement.branch);
|
|
}
|
|
else
|
|
{
|
|
parser_set_branch_to_current_position (context_p, &try_statement.branch);
|
|
|
|
if (try_statement.type == parser_catch_block)
|
|
{
|
|
#if !ENABLED (JERRY_ES2015)
|
|
context_p->scope_stack_top = try_statement.scope_stack_top;
|
|
context_p->scope_stack_reg_top = try_statement.scope_stack_reg_top;
|
|
#endif /* !ENABLED (JERRY_ES2015) */
|
|
|
|
if (context_p->token.type != LEXER_KEYW_FINALLY)
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc (context_p, CBC_CONTEXT_END);
|
|
parser_flush_cbc (context_p);
|
|
|
|
try_statement.type = parser_finally_block;
|
|
}
|
|
}
|
|
else if (try_statement.type == parser_try_block
|
|
&& context_p->token.type != LEXER_KEYW_CATCH
|
|
&& context_p->token.type != LEXER_KEYW_FINALLY)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_CATCH_FINALLY_EXPECTED);
|
|
}
|
|
}
|
|
|
|
if (try_statement.type == parser_finally_block)
|
|
{
|
|
parser_stack_pop (context_p, NULL, (uint32_t) (sizeof (parser_try_statement_t) + 1));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
return;
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_KEYW_CATCH)
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED);
|
|
}
|
|
|
|
try_statement.type = parser_catch_block;
|
|
parser_emit_cbc_ext_forward_branch (context_p,
|
|
CBC_EXT_CATCH,
|
|
&try_statement.branch);
|
|
|
|
try_statement.scope_stack_top = context_p->scope_stack_top;
|
|
try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
bool block_found = false;
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
|
#ifndef JERRY_NDEBUG
|
|
block_found = true;
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV);
|
|
}
|
|
|
|
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
|
}
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (lexer_check_next_characters (context_p, LIT_CHAR_LEFT_SQUARE, LIT_CHAR_LEFT_BRACE))
|
|
{
|
|
parser_pattern_flags_t flags = (PARSER_PATTERN_BINDING
|
|
| PARSER_PATTERN_TARGET_ON_STACK
|
|
| PARSER_PATTERN_LET);
|
|
|
|
parser_parse_initializer_by_next_char (context_p, flags);
|
|
}
|
|
else
|
|
{
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
parser_emit_cbc_literal (context_p,
|
|
(literal_index >= PARSER_REGISTER_START) ? CBC_ASSIGN_SET_IDENT : CBC_ASSIGN_LET_CONST,
|
|
literal_index);
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, context_p->lit_object.index);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
JERRY_ASSERT (block_found);
|
|
#endif /* !JERRY_NDEBUG */
|
|
#if ENABLED (JERRY_ES2015)
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FINALLY);
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
|
|
}
|
|
|
|
try_statement.type = parser_finally_block;
|
|
parser_emit_cbc_ext_forward_branch (context_p,
|
|
CBC_EXT_FINALLY,
|
|
&try_statement.branch);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
|
|
|
if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV);
|
|
}
|
|
|
|
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_stack_iterator_write (&iterator, &try_statement, sizeof (parser_try_statement_t));
|
|
} /* parser_parse_try_statement_end */
|
|
|
|
/**
|
|
* Parse default statement.
|
|
*/
|
|
static void
|
|
parser_parse_default_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
parser_switch_statement_t switch_statement;
|
|
|
|
if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH
|
|
&& context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_DEFAULT_NOT_IN_SWITCH);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
/* Already checked in parser_parse_switch_statement_start. */
|
|
JERRY_ASSERT (context_p->token.type == LEXER_COLON);
|
|
lexer_next_token (context_p);
|
|
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
|
|
parser_set_branch_to_current_position (context_p, &switch_statement.default_branch);
|
|
} /* parser_parse_default_statement */
|
|
|
|
/**
|
|
* Parse case statement.
|
|
*/
|
|
static void
|
|
parser_parse_case_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
parser_switch_statement_t switch_statement;
|
|
parser_branch_node_t *branch_p;
|
|
|
|
if (context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH
|
|
&& context_p->stack_top_uint8 != PARSER_STATEMENT_SWITCH_NO_DEFAULT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_CASE_NOT_IN_SWITCH);
|
|
}
|
|
|
|
if (context_p->next_scanner_info_p->source_p != context_p->source_p)
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
JERRY_ASSERT (context_p->token.type != LEXER_COLON);
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_CASE);
|
|
|
|
scanner_set_location (context_p, &((scanner_location_info_t *) context_p->next_scanner_info_p)->location);
|
|
scanner_release_next (context_p, sizeof (scanner_location_info_t));
|
|
scanner_seek (context_p);
|
|
lexer_next_token (context_p);
|
|
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
parser_stack_iterator_skip (&iterator, 1 + sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
|
|
/* Free memory after the case statement is found. */
|
|
|
|
branch_p = switch_statement.branch_list_p;
|
|
JERRY_ASSERT (branch_p != NULL);
|
|
switch_statement.branch_list_p = branch_p->next_p;
|
|
parser_stack_iterator_write (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
|
|
parser_set_branch_to_current_position (context_p, &branch_p->branch);
|
|
parser_free (branch_p, sizeof (parser_branch_node_t));
|
|
} /* parser_parse_case_statement */
|
|
|
|
/**
|
|
* Parse break statement.
|
|
*/
|
|
static void
|
|
parser_parse_break_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
cbc_opcode_t opcode = CBC_JUMP_FORWARD;
|
|
|
|
lexer_next_token (context_p);
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
if (!(context_p->token.flags & LEXER_WAS_NEWLINE)
|
|
&& context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
/* The label with the same name is searched on the stack. */
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
if (type == PARSER_STATEMENT_START)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK_LABEL);
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK)
|
|
{
|
|
opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT;
|
|
}
|
|
|
|
if (type == PARSER_STATEMENT_LABEL)
|
|
{
|
|
parser_label_statement_t label_statement;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t));
|
|
|
|
if (lexer_current_is_literal (context_p, &label_statement.label_ident))
|
|
{
|
|
label_statement.break_list_p = parser_emit_cbc_forward_branch_item (context_p,
|
|
(uint16_t) opcode,
|
|
label_statement.break_list_p);
|
|
parser_stack_iterator_write (&iterator, &label_statement, sizeof (parser_label_statement_t));
|
|
lexer_next_token (context_p);
|
|
return;
|
|
}
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t));
|
|
}
|
|
else
|
|
{
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The first switch or loop statement is searched. */
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
if (type == PARSER_STATEMENT_START)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_BREAK);
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK)
|
|
{
|
|
opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT;
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_BREAK_TARGET)
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p,
|
|
(uint16_t) opcode,
|
|
loop.branch_list_p);
|
|
parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
return;
|
|
}
|
|
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
}
|
|
} /* parser_parse_break_statement */
|
|
|
|
/**
|
|
* Parse continue statement.
|
|
*/
|
|
static void
|
|
parser_parse_continue_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
cbc_opcode_t opcode = CBC_JUMP_FORWARD;
|
|
|
|
lexer_next_token (context_p);
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
if (!(context_p->token.flags & LEXER_WAS_NEWLINE)
|
|
&& context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
parser_stack_iterator_t loop_iterator;
|
|
|
|
loop_iterator.current_p = NULL;
|
|
|
|
/* The label with the same name is searched on the stack. */
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
|
|
if (type == PARSER_STATEMENT_START)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE_LABEL);
|
|
}
|
|
|
|
/* Only those labels are checked, whose are label of a loop. */
|
|
if (loop_iterator.current_p != NULL && type == PARSER_STATEMENT_LABEL)
|
|
{
|
|
parser_label_statement_t label_statement;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t));
|
|
|
|
if (lexer_current_is_literal (context_p, &label_statement.label_ident))
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
parser_stack_iterator_skip (&loop_iterator, 1);
|
|
parser_stack_iterator_read (&loop_iterator, &loop, sizeof (parser_loop_statement_t));
|
|
loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p,
|
|
(uint16_t) opcode,
|
|
loop.branch_list_p);
|
|
loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK;
|
|
parser_stack_iterator_write (&loop_iterator, &loop, sizeof (parser_loop_statement_t));
|
|
lexer_next_token (context_p);
|
|
return;
|
|
}
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t));
|
|
continue;
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK)
|
|
{
|
|
opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT;
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTINUE_TARGET)
|
|
{
|
|
loop_iterator = iterator;
|
|
}
|
|
else
|
|
{
|
|
loop_iterator.current_p = NULL;
|
|
}
|
|
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
}
|
|
}
|
|
|
|
/* The first loop statement is searched. */
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
if (type == PARSER_STATEMENT_START)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_CONTINUE);
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTINUE_TARGET)
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
loop.branch_list_p = parser_emit_cbc_forward_branch_item (context_p,
|
|
(uint16_t) opcode,
|
|
loop.branch_list_p);
|
|
loop.branch_list_p->branch.offset |= CBC_HIGHEST_BIT_MASK;
|
|
parser_stack_iterator_write (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
return;
|
|
}
|
|
|
|
if (parser_statement_flags[type] & PARSER_STATM_CONTEXT_BREAK)
|
|
{
|
|
opcode = CBC_JUMP_FORWARD_EXIT_CONTEXT;
|
|
}
|
|
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
}
|
|
} /* parser_parse_continue_statement */
|
|
|
|
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
/**
|
|
* Parse import statement.
|
|
* Note: See 15.2.2
|
|
*/
|
|
static void
|
|
parser_parse_import_statement (parser_context_t *context_p) /**< parser context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_IMPORT);
|
|
|
|
parser_module_check_request_place (context_p);
|
|
parser_module_context_init ();
|
|
|
|
context_p->module_current_node_p = parser_module_create_module_node (context_p);
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
/* Check for a ModuleSpecifier*/
|
|
if (context_p->token.type != LEXER_LITERAL
|
|
|| context_p->token.lit_location.type != LEXER_STRING_LITERAL)
|
|
{
|
|
if (!(context_p->token.type == LEXER_LEFT_BRACE
|
|
|| context_p->token.type == LEXER_MULTIPLY
|
|
|| (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL)))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_LITERAL_EXPECTED);
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_LITERAL)
|
|
{
|
|
/* Handle ImportedDefaultBinding */
|
|
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL);
|
|
|
|
ecma_string_t *local_name_p = ecma_new_ecma_string_from_utf8 (context_p->lit_object.literal_p->u.char_p,
|
|
context_p->lit_object.literal_p->prop.length);
|
|
|
|
if (parser_module_check_duplicate_import (context_p, local_name_p))
|
|
{
|
|
ecma_deref_ecma_string (local_name_p);
|
|
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_IMPORT_BINDING);
|
|
}
|
|
|
|
ecma_string_t *import_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_DEFAULT);
|
|
parser_module_add_names_to_node (context_p, import_name_p, local_name_p);
|
|
|
|
ecma_deref_ecma_string (local_name_p);
|
|
ecma_deref_ecma_string (import_name_p);
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_COMMA)
|
|
{
|
|
lexer_next_token (context_p);
|
|
if (context_p->token.type != LEXER_MULTIPLY
|
|
&& context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_EXPECTED);
|
|
}
|
|
}
|
|
else if (!lexer_token_is_identifier (context_p, "from", 4))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_FROM_COMMA_EXPECTED);
|
|
}
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_MULTIPLY)
|
|
{
|
|
/* NameSpaceImport*/
|
|
lexer_next_token (context_p);
|
|
if (!lexer_token_is_identifier (context_p, "as", 2))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_AS_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
if (context_p->token.type != LEXER_LITERAL)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
|
|
}
|
|
|
|
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_IDENT_LITERAL);
|
|
|
|
ecma_string_t *local_name_p = ecma_new_ecma_string_from_utf8 (context_p->lit_object.literal_p->u.char_p,
|
|
context_p->lit_object.literal_p->prop.length);
|
|
|
|
if (parser_module_check_duplicate_import (context_p, local_name_p))
|
|
{
|
|
ecma_deref_ecma_string (local_name_p);
|
|
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_IMPORT_BINDING);
|
|
}
|
|
|
|
ecma_string_t *import_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_ASTERIX_CHAR);
|
|
|
|
parser_module_add_names_to_node (context_p, import_name_p, local_name_p);
|
|
ecma_deref_ecma_string (local_name_p);
|
|
ecma_deref_ecma_string (import_name_p);
|
|
|
|
lexer_next_token (context_p);
|
|
}
|
|
else if (context_p->token.type == LEXER_LEFT_BRACE)
|
|
{
|
|
/* Handle NamedImports */
|
|
parser_module_parse_import_clause (context_p);
|
|
}
|
|
|
|
if (!lexer_token_is_identifier (context_p, "from", 4))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED);
|
|
}
|
|
lexer_next_token (context_p);
|
|
}
|
|
|
|
parser_module_handle_module_specifier (context_p);
|
|
parser_module_add_import_node_to_context (context_p);
|
|
|
|
context_p->module_current_node_p = NULL;
|
|
} /* parser_parse_import_statement */
|
|
|
|
/**
|
|
* Parse export statement.
|
|
*/
|
|
static void
|
|
parser_parse_export_statement (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_EXPORT);
|
|
|
|
parser_module_check_request_place (context_p);
|
|
parser_module_context_init ();
|
|
|
|
context_p->module_current_node_p = parser_module_create_module_node (context_p);
|
|
|
|
lexer_next_token (context_p);
|
|
switch (context_p->token.type)
|
|
{
|
|
case LEXER_KEYW_DEFAULT:
|
|
{
|
|
scanner_location_t location;
|
|
scanner_get_location (&location, context_p);
|
|
|
|
context_p->status_flags |= PARSER_MODULE_STORE_IDENT;
|
|
|
|
lexer_next_token (context_p);
|
|
if (context_p->token.type == LEXER_KEYW_CLASS)
|
|
{
|
|
context_p->status_flags |= PARSER_MODULE_DEFAULT_CLASS_OR_FUNC;
|
|
parser_parse_class (context_p, true);
|
|
}
|
|
else if (context_p->token.type == LEXER_KEYW_FUNCTION)
|
|
{
|
|
context_p->status_flags |= PARSER_MODULE_DEFAULT_CLASS_OR_FUNC;
|
|
parser_parse_function_statement (context_p);
|
|
}
|
|
else
|
|
{
|
|
/* Assignment expression */
|
|
scanner_set_location (context_p, &location);
|
|
|
|
/* 15.2.3.5 Use the synthetic name '*default*' as the identifier. */
|
|
lexer_construct_literal_object (context_p, &lexer_default_literal, lexer_default_literal.type);
|
|
|
|
context_p->token.lit_location.type = LEXER_IDENT_LITERAL;
|
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
|
|
|
|
context_p->module_identifier_lit_p = context_p->lit_object.literal_p;
|
|
|
|
/* Fake an assignment to the default identifier */
|
|
context_p->token.type = LEXER_ASSIGN;
|
|
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR_NO_COMMA | PARSE_EXPR_HAS_LITERAL);
|
|
}
|
|
|
|
ecma_string_t *name_p = ecma_new_ecma_string_from_utf8 (context_p->module_identifier_lit_p->u.char_p,
|
|
context_p->module_identifier_lit_p->prop.length);
|
|
ecma_string_t *export_name_p = ecma_get_magic_string (LIT_MAGIC_STRING_DEFAULT);
|
|
|
|
if (parser_module_check_duplicate_export (context_p, export_name_p))
|
|
{
|
|
ecma_deref_ecma_string (name_p);
|
|
ecma_deref_ecma_string (export_name_p);
|
|
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_EXPORT_IDENTIFIER);
|
|
}
|
|
|
|
parser_module_add_names_to_node (context_p,
|
|
export_name_p,
|
|
name_p);
|
|
ecma_deref_ecma_string (name_p);
|
|
ecma_deref_ecma_string (export_name_p);
|
|
break;
|
|
}
|
|
case LEXER_MULTIPLY:
|
|
{
|
|
lexer_next_token (context_p);
|
|
if (!lexer_token_is_identifier (context_p, "from", 4))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_FROM_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_module_handle_module_specifier (context_p);
|
|
break;
|
|
}
|
|
case LEXER_KEYW_VAR:
|
|
case LEXER_KEYW_LET:
|
|
case LEXER_KEYW_CONST:
|
|
{
|
|
context_p->status_flags |= PARSER_MODULE_STORE_IDENT;
|
|
parser_parse_var_statement (context_p);
|
|
break;
|
|
}
|
|
case LEXER_KEYW_CLASS:
|
|
{
|
|
context_p->status_flags |= PARSER_MODULE_STORE_IDENT;
|
|
parser_parse_class (context_p, true);
|
|
break;
|
|
}
|
|
case LEXER_KEYW_FUNCTION:
|
|
{
|
|
context_p->status_flags |= PARSER_MODULE_STORE_IDENT;
|
|
parser_parse_function_statement (context_p);
|
|
break;
|
|
}
|
|
case LEXER_LEFT_BRACE:
|
|
{
|
|
parser_module_parse_export_clause (context_p);
|
|
|
|
if (lexer_token_is_identifier (context_p, "from", 4))
|
|
{
|
|
lexer_next_token (context_p);
|
|
parser_module_handle_module_specifier (context_p);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_MULTIPLY_LITERAL_EXPECTED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_DEFAULT_CLASS_OR_FUNC | PARSER_MODULE_STORE_IDENT);
|
|
parser_module_add_export_node_to_context (context_p);
|
|
context_p->module_current_node_p = NULL;
|
|
} /* parser_parse_export_statement */
|
|
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
|
|
/**
|
|
* Parse label statement.
|
|
*/
|
|
static void
|
|
parser_parse_label (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
parser_label_statement_t label_statement;
|
|
|
|
parser_stack_iterator_init (context_p, &iterator);
|
|
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
if (type == PARSER_STATEMENT_START)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (type == PARSER_STATEMENT_LABEL)
|
|
{
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &label_statement, sizeof (parser_label_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t));
|
|
|
|
if (lexer_current_is_literal (context_p, &label_statement.label_ident))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_LABEL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
}
|
|
}
|
|
|
|
label_statement.label_ident = context_p->token.lit_location;
|
|
label_statement.break_list_p = NULL;
|
|
parser_stack_push (context_p, &label_statement, sizeof (parser_label_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_LABEL);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
} /* parser_parse_label */
|
|
|
|
/**
|
|
* Strict mode types for statement parsing.
|
|
*/
|
|
typedef enum
|
|
{
|
|
PARSER_USE_STRICT_NOT_FOUND = 0, /**< 'use strict' directive is not found */
|
|
PARSER_USE_STRICT_FOUND = 1, /**< 'use strict' directive is found but strict mode has already been enabled */
|
|
PARSER_USE_STRICT_SET = 2, /**< strict mode is enabled after 'use strict' directive is found */
|
|
} parser_strict_mode_type_t;
|
|
|
|
/**
|
|
* Parse statements.
|
|
*/
|
|
void
|
|
parser_parse_statements (parser_context_t *context_p) /**< context */
|
|
{
|
|
/* Statement parsing cannot be nested. */
|
|
JERRY_ASSERT (context_p->last_statement.current_p == NULL);
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_START);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
/* Set lexical enviroment for the debugger. */
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
context_p->last_breakpoint_line = 0;
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
|
|
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_RESOURCE_NAME);
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
#if ENABLED (JERRY_LINE_INFO)
|
|
context_p->last_line_info_line = 0;
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
while (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_STRING_LITERAL)
|
|
{
|
|
lexer_lit_location_t lit_location;
|
|
parser_strict_mode_type_t strict_mode = PARSER_USE_STRICT_NOT_FOUND;
|
|
|
|
JERRY_ASSERT (context_p->stack_depth <= 1);
|
|
#ifndef JERRY_NDEBUG
|
|
JERRY_ASSERT (context_p->context_stack_depth == context_p->stack_depth);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
if (lexer_string_is_use_strict (context_p))
|
|
{
|
|
strict_mode = PARSER_USE_STRICT_FOUND;
|
|
|
|
if (!(context_p->status_flags & PARSER_IS_STRICT))
|
|
{
|
|
/* The next token should be parsed in strict mode. */
|
|
context_p->status_flags |= PARSER_IS_STRICT;
|
|
strict_mode = PARSER_USE_STRICT_SET;
|
|
}
|
|
}
|
|
|
|
lit_location = context_p->token.lit_location;
|
|
lexer_next_token (context_p);
|
|
|
|
if (!lexer_string_is_directive (context_p))
|
|
{
|
|
/* The string is part of an expression statement. */
|
|
if (strict_mode == PARSER_USE_STRICT_SET)
|
|
{
|
|
context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT;
|
|
}
|
|
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
{
|
|
JERRY_ASSERT (context_p->last_breakpoint_line == 0);
|
|
|
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
|
parser_flush_cbc (context_p);
|
|
|
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, context_p->token.line);
|
|
|
|
context_p->last_breakpoint_line = context_p->token.line;
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
#if ENABLED (JERRY_LINE_INFO)
|
|
parser_emit_line_info (context_p, context_p->token.line, false);
|
|
#endif /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL);
|
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
|
|
/* The extra_value is used for saving the token. */
|
|
context_p->token.extra_value = context_p->token.type;
|
|
context_p->token.type = LEXER_EXPRESSION_START;
|
|
break;
|
|
}
|
|
|
|
#if ENABLED (JERRY_PARSER_DUMP_BYTE_CODE)
|
|
if (strict_mode == PARSER_USE_STRICT_SET && context_p->is_show_opcodes)
|
|
{
|
|
JERRY_DEBUG_MSG (" Note: switch to strict mode\n\n");
|
|
}
|
|
#endif /* ENABLED (JERRY_PARSER_DUMP_BYTE_CODE) */
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (strict_mode != PARSER_USE_STRICT_NOT_FOUND
|
|
&& (context_p->status_flags & PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_USE_STRICT_NOT_ALLOWED);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
if (context_p->token.type == LEXER_SEMICOLON)
|
|
{
|
|
lexer_next_token (context_p);
|
|
}
|
|
|
|
/* The last directive prologue can be the result of the script. */
|
|
if (!(context_p->status_flags & PARSER_IS_FUNCTION)
|
|
&& (context_p->token.type != LEXER_LITERAL
|
|
|| context_p->token.lit_location.type != LEXER_STRING_LITERAL))
|
|
{
|
|
lexer_construct_literal_object (context_p, &lit_location, LEXER_STRING_LITERAL);
|
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL);
|
|
parser_emit_cbc (context_p, CBC_POP_BLOCK);
|
|
parser_flush_cbc (context_p);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_IS_STRICT
|
|
&& context_p->status_flags & PARSER_HAS_NON_STRICT_ARG)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_NON_STRICT_ARG_DEFINITION);
|
|
}
|
|
|
|
while (context_p->token.type != LEXER_EOS
|
|
|| context_p->stack_top_uint8 != PARSER_STATEMENT_START)
|
|
{
|
|
#ifndef JERRY_NDEBUG
|
|
JERRY_ASSERT (context_p->stack_depth == context_p->context_stack_depth);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
JERRY_ASSERT (context_p->stack_top_uint8 != PARSER_STATEMENT_PRIVATE_SCOPE
|
|
&& context_p->stack_top_uint8 != PARSER_STATEMENT_PRIVATE_CONTEXT);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED
|
|
&& context_p->token.line != context_p->last_breakpoint_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_LET
|
|
&& context_p->token.type != LEXER_KEYW_CONST
|
|
&& context_p->token.type != LEXER_KEYW_FUNCTION
|
|
&& context_p->token.type != LEXER_KEYW_CASE
|
|
&& context_p->token.type != LEXER_KEYW_DEFAULT)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_DISABLED);
|
|
parser_flush_cbc (context_p);
|
|
|
|
parser_append_breakpoint_info (context_p, JERRY_DEBUGGER_BREAKPOINT_LIST, context_p->token.line);
|
|
|
|
context_p->last_breakpoint_line = context_p->token.line;
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
|
|
#if ENABLED (JERRY_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_LET
|
|
&& context_p->token.type != LEXER_KEYW_CONST
|
|
&& 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 /* ENABLED (JERRY_LINE_INFO) */
|
|
|
|
switch (context_p->token.type)
|
|
{
|
|
case LEXER_SEMICOLON:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case LEXER_RIGHT_BRACE:
|
|
{
|
|
if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case LEXER_LEFT_BRACE:
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
parser_push_block_context (context_p, false);
|
|
}
|
|
else
|
|
{
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
|
}
|
|
#else /* !ENABLED (JERRY_ES2015) */
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_BLOCK);
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_VAR:
|
|
#if ENABLED (JERRY_ES2015)
|
|
case LEXER_KEYW_LET:
|
|
case LEXER_KEYW_CONST:
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
{
|
|
parser_parse_var_statement (context_p);
|
|
break;
|
|
}
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
case LEXER_KEYW_CLASS:
|
|
{
|
|
parser_validate_lexical_context (context_p);
|
|
parser_parse_class (context_p, true);
|
|
goto consume_last_statement;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
|
|
case LEXER_KEYW_IMPORT:
|
|
{
|
|
parser_parse_import_statement (context_p);
|
|
break;
|
|
}
|
|
|
|
case LEXER_KEYW_EXPORT:
|
|
{
|
|
parser_parse_export_statement (context_p);
|
|
break;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
|
|
|
|
case LEXER_KEYW_FUNCTION:
|
|
{
|
|
parser_parse_function_statement (context_p);
|
|
goto consume_last_statement;
|
|
}
|
|
|
|
case LEXER_KEYW_IF:
|
|
{
|
|
parser_parse_if_statement_start (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_SWITCH:
|
|
{
|
|
parser_parse_switch_statement_start (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_DO:
|
|
{
|
|
parser_do_while_statement_t do_while_statement;
|
|
parser_loop_statement_t loop;
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
|
|
|
do_while_statement.start_offset = context_p->byte_code_size;
|
|
loop.branch_list_p = NULL;
|
|
|
|
parser_stack_push (context_p, &do_while_statement, sizeof (parser_do_while_statement_t));
|
|
parser_stack_push (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_DO_WHILE);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_WHILE:
|
|
{
|
|
parser_parse_while_statement_start (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_FOR:
|
|
{
|
|
parser_parse_for_statement_start (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_WITH:
|
|
{
|
|
parser_parse_with_statement_start (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_TRY:
|
|
{
|
|
parser_try_statement_t try_statement;
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
|
|
}
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
try_statement.type = parser_try_block;
|
|
parser_emit_cbc_ext_forward_branch (context_p,
|
|
CBC_EXT_TRY_CREATE_CONTEXT,
|
|
&try_statement.branch);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
try_statement.scope_stack_top = context_p->scope_stack_top;
|
|
try_statement.scope_stack_reg_top = context_p->scope_stack_reg_top;
|
|
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_BLOCK);
|
|
|
|
if (scanner_is_context_needed (context_p, PARSER_CHECK_BLOCK_CONTEXT))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_TRY_CREATE_ENV);
|
|
}
|
|
|
|
scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
parser_stack_push (context_p, &try_statement, sizeof (parser_try_statement_t));
|
|
parser_stack_push_uint8 (context_p, PARSER_STATEMENT_TRY);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_DEFAULT:
|
|
{
|
|
parser_parse_default_statement (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_CASE:
|
|
{
|
|
parser_parse_case_statement (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_KEYW_BREAK:
|
|
{
|
|
parser_parse_break_statement (context_p);
|
|
break;
|
|
}
|
|
|
|
case LEXER_KEYW_CONTINUE:
|
|
{
|
|
parser_parse_continue_statement (context_p);
|
|
break;
|
|
}
|
|
|
|
case LEXER_KEYW_THROW:
|
|
{
|
|
lexer_next_token (context_p);
|
|
if (context_p->token.flags & LEXER_WAS_NEWLINE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED);
|
|
}
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
parser_emit_cbc (context_p, CBC_THROW);
|
|
break;
|
|
}
|
|
|
|
case LEXER_KEYW_RETURN:
|
|
{
|
|
if (!(context_p->status_flags & PARSER_IS_FUNCTION))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_RETURN);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if ((context_p->token.flags & LEXER_WAS_NEWLINE)
|
|
|| context_p->token.type == LEXER_SEMICOLON
|
|
|| context_p->token.type == LEXER_EOS
|
|
|| context_p->token.type == LEXER_RIGHT_BRACE)
|
|
{
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED);
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE);
|
|
break;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
parser_emit_cbc (context_p, CBC_RETURN_WITH_BLOCK);
|
|
break;
|
|
}
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE);
|
|
break;
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL;
|
|
break;
|
|
}
|
|
|
|
parser_emit_cbc (context_p, CBC_RETURN);
|
|
break;
|
|
}
|
|
|
|
case LEXER_KEYW_DEBUGGER:
|
|
{
|
|
#if ENABLED (JERRY_DEBUGGER)
|
|
/* This breakpoint location is not reported to the
|
|
* debugger, so it is impossible to disable it. */
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_BREAKPOINT_ENABLED);
|
|
}
|
|
#endif /* ENABLED (JERRY_DEBUGGER) */
|
|
lexer_next_token (context_p);
|
|
break;
|
|
}
|
|
|
|
case LEXER_LITERAL:
|
|
{
|
|
if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
if (JERRY_UNLIKELY (lexer_check_next_character (context_p, LIT_CHAR_COLON)))
|
|
{
|
|
parser_parse_label (context_p);
|
|
lexer_consume_next_character (context_p);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (JERRY_UNLIKELY (lexer_token_is_let (context_p)))
|
|
{
|
|
if (context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
if (context_p->next_scanner_info_p->type == SCANNER_TYPE_LET_EXPRESSION)
|
|
{
|
|
scanner_release_next (context_p, sizeof (scanner_info_t));
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_IS_FUNCTION)
|
|
{
|
|
parser_parse_expression_statement (context_p, PARSE_EXPR);
|
|
break;
|
|
}
|
|
|
|
parser_parse_block_expression (context_p, PARSE_EXPR);
|
|
break;
|
|
}
|
|
|
|
context_p->token.type = LEXER_KEYW_LET;
|
|
parser_parse_var_statement (context_p);
|
|
break;
|
|
}
|
|
|
|
if (JERRY_UNLIKELY (lexer_token_is_async (context_p))
|
|
&& context_p->next_scanner_info_p->source_p == context_p->source_p)
|
|
{
|
|
bool is_statement = true;
|
|
|
|
if (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION)
|
|
{
|
|
is_statement = (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_STATEMENT) != 0;
|
|
|
|
JERRY_ASSERT (!is_statement || (context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC));
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_ERR_ASYNC_FUNCTION);
|
|
|
|
scanner_release_next (context_p, sizeof (scanner_info_t));
|
|
}
|
|
|
|
if (is_statement)
|
|
{
|
|
if (parser_statement_flags[context_p->stack_top_uint8] & PARSER_STATM_SINGLE_STATM)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION);
|
|
continue;
|
|
}
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
}
|
|
/* FALLTHRU */
|
|
}
|
|
|
|
default:
|
|
{
|
|
int options = PARSE_EXPR;
|
|
|
|
if (context_p->token.type == LEXER_EXPRESSION_START)
|
|
{
|
|
/* Restore the token type form the extra_value. */
|
|
context_p->token.type = context_p->token.extra_value;
|
|
options |= PARSE_EXPR_HAS_LITERAL;
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_IS_FUNCTION)
|
|
{
|
|
parser_parse_expression_statement (context_p, options);
|
|
}
|
|
else
|
|
{
|
|
parser_parse_block_expression (context_p, options);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_BRACE)
|
|
{
|
|
if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK)
|
|
{
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
lexer_next_token (context_p);
|
|
}
|
|
#if ENABLED (JERRY_ES2015)
|
|
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_BLOCK_CONTEXT)
|
|
{
|
|
parser_pop_block_context (context_p);
|
|
lexer_next_token (context_p);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH_NO_DEFAULT)
|
|
{
|
|
int has_default = (context_p->stack_top_uint8 == PARSER_STATEMENT_SWITCH);
|
|
parser_loop_statement_t loop;
|
|
parser_switch_statement_t switch_statement;
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_pop (context_p, &switch_statement, sizeof (parser_switch_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
JERRY_ASSERT (switch_statement.branch_list_p == NULL);
|
|
|
|
if (!has_default)
|
|
{
|
|
parser_set_branch_to_current_position (context_p, &switch_statement.default_branch);
|
|
}
|
|
|
|
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
|
lexer_next_token (context_p);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT)
|
|
{
|
|
parser_pop_block_context (context_p);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
}
|
|
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_TRY)
|
|
{
|
|
parser_parse_try_statement_end (context_p);
|
|
}
|
|
else if (context_p->stack_top_uint8 == PARSER_STATEMENT_START)
|
|
{
|
|
if (context_p->status_flags & PARSER_IS_CLOSURE)
|
|
{
|
|
parser_stack_pop_uint8 (context_p);
|
|
context_p->last_statement.current_p = NULL;
|
|
/* There is no lexer_next_token here, since the
|
|
* next token belongs to the parent context. */
|
|
return;
|
|
}
|
|
parser_raise_error (context_p, PARSER_ERR_INVALID_RIGHT_SQUARE);
|
|
}
|
|
}
|
|
else if (context_p->token.type == LEXER_SEMICOLON)
|
|
{
|
|
lexer_next_token (context_p);
|
|
}
|
|
else if (context_p->token.type != LEXER_EOS
|
|
&& !(context_p->token.flags & LEXER_WAS_NEWLINE))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_SEMICOLON_EXPECTED);
|
|
}
|
|
|
|
consume_last_statement:
|
|
while (true)
|
|
{
|
|
switch (context_p->stack_top_uint8)
|
|
{
|
|
case PARSER_STATEMENT_LABEL:
|
|
{
|
|
parser_label_statement_t label;
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &label, sizeof (parser_label_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_set_breaks_to_current_position (context_p, label.break_list_p);
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_IF:
|
|
{
|
|
if (parser_parse_if_statement_end (context_p))
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_ELSE:
|
|
{
|
|
parser_if_else_statement_t else_statement;
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &else_statement, sizeof (parser_if_else_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_set_branch_to_current_position (context_p, &else_statement.branch);
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_DO_WHILE:
|
|
{
|
|
parser_parse_do_while_statement_end (context_p);
|
|
if (context_p->token.type == LEXER_SEMICOLON)
|
|
{
|
|
lexer_next_token (context_p);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_WHILE:
|
|
{
|
|
parser_parse_while_statement_end (context_p);
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_FOR:
|
|
{
|
|
parser_parse_for_statement_end (context_p);
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_FOR_IN:
|
|
#if ENABLED (JERRY_ES2015)
|
|
case PARSER_STATEMENT_FOR_OF:
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
{
|
|
parser_for_in_of_statement_t for_in_of_statement;
|
|
parser_loop_statement_t loop;
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
bool is_for_in = (context_p->stack_top_uint8 == PARSER_STATEMENT_FOR_IN);
|
|
#else
|
|
bool is_for_in = true;
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
parser_stack_pop (context_p, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_pop (context_p, &for_in_of_statement, sizeof (parser_for_in_of_statement_t));
|
|
parser_stack_iterator_init (context_p, &context_p->last_statement);
|
|
|
|
parser_set_continues_to_current_position (context_p, loop.branch_list_p);
|
|
|
|
parser_flush_cbc (context_p);
|
|
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION
|
|
: PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
|
#ifndef JERRY_NDEBUG
|
|
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth,
|
|
is_for_in ? PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION
|
|
: PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
parser_emit_cbc_ext_backward_branch (context_p,
|
|
is_for_in ? CBC_EXT_BRANCH_IF_FOR_IN_HAS_NEXT
|
|
: CBC_EXT_BRANCH_IF_FOR_OF_HAS_NEXT,
|
|
for_in_of_statement.start_offset);
|
|
|
|
parser_set_breaks_to_current_position (context_p, loop.branch_list_p);
|
|
parser_set_branch_to_current_position (context_p, &for_in_of_statement.branch);
|
|
|
|
#if ENABLED (JERRY_ES2015)
|
|
if (context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_SCOPE
|
|
|| context_p->stack_top_uint8 == PARSER_STATEMENT_PRIVATE_CONTEXT)
|
|
{
|
|
parser_pop_block_context (context_p);
|
|
}
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
continue;
|
|
}
|
|
|
|
case PARSER_STATEMENT_WITH:
|
|
{
|
|
parser_parse_with_statement_end (context_p);
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
context_p->last_statement.current_p = NULL;
|
|
|
|
if (context_p->status_flags & PARSER_IS_CLOSURE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_STATEMENT_EXPECTED);
|
|
}
|
|
} /* parser_parse_statements */
|
|
|
|
/**
|
|
* Free jumps stored on the stack if a parse error is occured.
|
|
*/
|
|
void JERRY_ATTR_NOINLINE
|
|
parser_free_jumps (parser_stack_iterator_t iterator) /**< iterator position */
|
|
{
|
|
while (true)
|
|
{
|
|
uint8_t type = parser_stack_iterator_read_uint8 (&iterator);
|
|
parser_branch_node_t *branch_list_p = NULL;
|
|
|
|
switch (type)
|
|
{
|
|
case PARSER_STATEMENT_START:
|
|
{
|
|
return;
|
|
}
|
|
|
|
case PARSER_STATEMENT_LABEL:
|
|
{
|
|
parser_label_statement_t label;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &label, sizeof (parser_label_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_label_statement_t));
|
|
branch_list_p = label.break_list_p;
|
|
break;
|
|
}
|
|
|
|
case PARSER_STATEMENT_SWITCH:
|
|
case PARSER_STATEMENT_SWITCH_NO_DEFAULT:
|
|
{
|
|
parser_switch_statement_t switch_statement;
|
|
parser_loop_statement_t loop;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_read (&iterator, &switch_statement, sizeof (parser_switch_statement_t));
|
|
parser_stack_iterator_skip (&iterator, sizeof (parser_switch_statement_t));
|
|
|
|
branch_list_p = switch_statement.branch_list_p;
|
|
while (branch_list_p != NULL)
|
|
{
|
|
parser_branch_node_t *next_p = branch_list_p->next_p;
|
|
parser_free (branch_list_p, sizeof (parser_branch_node_t));
|
|
branch_list_p = next_p;
|
|
}
|
|
branch_list_p = loop.branch_list_p;
|
|
break;
|
|
}
|
|
|
|
case PARSER_STATEMENT_DO_WHILE:
|
|
case PARSER_STATEMENT_WHILE:
|
|
case PARSER_STATEMENT_FOR:
|
|
case PARSER_STATEMENT_FOR_IN:
|
|
#if ENABLED (JERRY_ES2015)
|
|
case PARSER_STATEMENT_FOR_OF:
|
|
#endif /* ENABLED (JERRY_ES2015) */
|
|
{
|
|
parser_loop_statement_t loop;
|
|
|
|
parser_stack_iterator_skip (&iterator, 1);
|
|
parser_stack_iterator_read (&iterator, &loop, sizeof (parser_loop_statement_t));
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type) - 1);
|
|
branch_list_p = loop.branch_list_p;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
parser_stack_iterator_skip (&iterator, parser_statement_length (type));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
while (branch_list_p != NULL)
|
|
{
|
|
parser_branch_node_t *next_p = branch_list_p->next_p;
|
|
parser_free (branch_list_p, sizeof (parser_branch_node_t));
|
|
branch_list_p = next_p;
|
|
}
|
|
}
|
|
} /* parser_free_jumps */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
* @}
|
|
*/
|
|
|
|
#endif /* ENABLED (JERRY_PARSER) */
|