/* Copyright 2014-2016 Samsung Electronics Co., Ltd. * Copyright 2015 University of Szeged. * * 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 "bytecode-data.h" #include "ecma-helpers.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" #include "jsp-mm.h" #include "opcodes.h" #include "opcodes-dumper.h" #include "parser.h" #include "re-parser.h" #include "scopes-tree.h" #include "stack.h" #include "jsp-early-error.h" #include "jsp-internal.h" #include "vm.h" #include "rcs-records.h" /** * Flag, indicating whether result of expression * evaluation should be stored to 'eval result' * temporary variable. * * In other words, the flag indicates whether * 'eval result' store code should be dumped. * * See also: * parse_expression */ typedef enum { JSP_EVAL_RET_STORE_NOT_DUMP, /**< do not dump */ JSP_EVAL_RET_STORE_DUMP, /**< dump */ } jsp_eval_ret_store_t; static token tok; static bool parser_show_instrs = false; #define EMIT_ERROR(type, MESSAGE) PARSE_ERROR(type, MESSAGE, tok.loc) #define EMIT_ERROR_VARG(type, MESSAGE, ...) PARSE_ERROR_VARG(type, MESSAGE, tok.loc, __VA_ARGS__) /** * Parse procedure * * Parse of any Program production (ECMA-262 v5, 14) is performed in two stages: * - preparse stage: * - build tree of scopes (global / eval as root and functions as other nodes) * - gather necessary properties for each scope: * - maximum number of instructions * - maximum number of per-opcode-block unique literals * - flags, indicating whether scope contains `with`, `try`, etc. * - perform syntax checks * - dump stage: * - dump byte-code instructions * - fill idx-to-literal hash table * - perform byte-code optimizations * * Parser context * See also: * jsp_ctx_t * * General description * Each parse stage is performed using jsp_parse_source_element_list function with common general flow, * and local branching for actions, specific for particular stage. * * Parse of each element is performed in a specific parser state (see also, jsp_state_expr_t and jsp_state_t) * that determines productions expected by parser in the state. * * States are located on managed (non-native) parser stack, so the parser is recursive. * * Expression parse flow * Whenever parser expects an expression, it pushes new state that is initially either JSP_STATE_EXPR_EMPTY or a * particular fixed helper state. * * The new state also contains required expression type, indicating what type of expression is currently expected. * For states starting with JSP_STATE_EXPR_EMPTY, it is the expression type that indicates final production, * that is expected to be parsed in the pushed state (AssignmentExpression, LeftHandSideExpression, etc.). * In case of fixed helper states required type is the same as starting expression type. * * In each state parser checks current production for whether it can be extended in some way, acceptable by grammar. * If it can be extended, it is extended, possibly with pushing new state for parsing subexpression. * Otherwise, it is just promoted to next (higher / more general) expression type to repeat check with rules, * corresponding to the next type, or finish parse of current expression. * Upon finishing parse, associated with the current expression / helper state, `is_completed` flag of the state is * raised. * * Finish of an expression parse is indicated using `is_subexpr_end` local flag in jsp_parse_source_element_list * function. Upon finish, upper state is popped from stack and becomes current, merging the parsed subexpression * into its state. * * Statements parse flow * Parse of statements is very similar to parse of expressions. * * The states are either fixed (like JSP_STATE_STAT_STATEMENT_LIST), or starting from JSP_STATE_STAT_EMPTY * with required type equal to JSP_STATE_STAT_STATEMENT. * * Source elements list * Source elements parse state is represented with the single type - JSP_STATE_SOURCE_ELEMENTS. * * Labelled statements * Named labels are pushed as helper states to parser stack and are searched for upon occurence of break or continue * on label. * * Targets of simple break / continue are also searched through stack iteration, looking for iterational or switch * statements. */ typedef enum __attr_packed___ { /* ECMA-262 v5 expression types */ JSP_STATE_EXPR__BEGIN, JSP_STATE_EXPR_EMPTY, /**< no expression yet (at start) */ JSP_STATE_EXPR_FUNCTION, /**< FunctionExpression (11.2.5) */ JSP_STATE_EXPR_MEMBER, /**< MemberExpression (11.2) */ JSP_STATE_EXPR_CALL, /**< CallExpression (11.2) */ JSP_STATE_EXPR_LEFTHANDSIDE, /**< LeftHandSideExpression (11.2) */ JSP_STATE_EXPR__SIMPLE_BEGIN, JSP_STATE_EXPR_UNARY, /**< UnaryExpression (11.4) */ JSP_STATE_EXPR_MULTIPLICATIVE, /**< MultiplicativeExpression (11.5) */ JSP_STATE_EXPR_ADDITIVE, /**< AdditiveExpression (11.6) */ JSP_STATE_EXPR_SHIFT, /**< ShiftExpression (11.7) */ JSP_STATE_EXPR_RELATIONAL, /**< RelationalExpression (11.8) */ JSP_STATE_EXPR_EQUALITY, /**< EqualityExpression (11.9) */ JSP_STATE_EXPR_BITWISE_AND, /**< BitwiseAndExpression (11.10) */ JSP_STATE_EXPR_BITWISE_XOR, /**< BitwiseXorExpression (11.10) */ JSP_STATE_EXPR_BITWISE_OR, /**< BitwiseOrExpression (11.10) */ JSP_STATE_EXPR__SIMPLE_END, JSP_STATE_EXPR_LOGICAL_AND, /**< LogicalAndExpression (11.11) */ JSP_STATE_EXPR_LOGICAL_OR, /**< LogicalOrExpression (11.11) */ JSP_STATE_EXPR_CONDITION, /**< ConditionalExpression (11.12) */ JSP_STATE_EXPR_ASSIGNMENT, /**< AssignmentExpression (11.13) */ JSP_STATE_EXPR_EXPRESSION, /**< Expression (11.14) */ JSP_STATE_EXPR_ARRAY_LITERAL, /**< ArrayLiteral (11.1.4) */ JSP_STATE_EXPR_OBJECT_LITERAL, /**< ObjectLiteral (11.1.5) */ JSP_STATE_EXPR_DATA_PROP_DECL, /**< a data property (ObjectLiteral, 11.1.5) */ JSP_STATE_EXPR_ACCESSOR_PROP_DECL, /**< an accessor's property getter / setter (ObjectLiteral, 11.1.5) */ JSP_STATE_EXPR__END, JSP_STATE_STAT_EMPTY, /**< no statement yet (at start) */ JSP_STATE_STAT_IF_BRANCH_START, /**< IfStatement branch start */ JSP_STATE_STAT_IF_BRANCH_END, /**< IfStatement branch start */ JSP_STATE_STAT_STATEMENT, /**< Statement */ JSP_STATE_STAT_STATEMENT_LIST, /**< Statement list */ JSP_STATE_STAT_VAR_DECL, /**< VariableStatement */ JSP_STATE_STAT_VAR_DECL_FINISH, JSP_STATE_STAT_DO_WHILE, /**< IterationStatement */ JSP_STATE_STAT_WHILE, JSP_STATE_STAT_FOR_INIT_END, JSP_STATE_STAT_FOR_INCREMENT, JSP_STATE_STAT_FOR_COND, JSP_STATE_STAT_FOR_FINISH, JSP_STATE_STAT_FOR_IN, JSP_STATE_STAT_FOR_IN_EXPR, JSP_STATE_STAT_FOR_IN_FINISH, JSP_STATE_STAT_ITER_FINISH, JSP_STATE_STAT_SWITCH, JSP_STATE_STAT_SWITCH_BRANCH_EXPR, JSP_STATE_STAT_SWITCH_BRANCH, JSP_STATE_STAT_SWITCH_FINISH, JSP_STATE_STAT_TRY, JSP_STATE_STAT_CATCH_FINISH, JSP_STATE_STAT_FINALLY_FINISH, JSP_STATE_STAT_TRY_FINISH, JSP_STATE_STAT_WITH, JSP_STATE_STAT_EXPRESSION, JSP_STATE_STAT_RETURN, JSP_STATE_STAT_THROW, JSP_STATE_FUNC_DECL_FINISH, JSP_STATE_SOURCE_ELEMENTS, JSP_STATE_STAT_BLOCK, JSP_STATE_STAT_NAMED_LABEL } jsp_state_expr_t; typedef struct jsp_state_t { mem_cpointer_t prev_state_cp; /** pointer to previous state on the stack, or MEM_CP_NULL - for bottom stack element */ mem_cpointer_t next_state_cp; /** pointer to next state on the stack, or MEM_CP_NULL - for top stack element */ jsp_state_expr_t state; /**< current state */ jsp_state_expr_t req_state; /**< required state */ uint8_t is_completed : 1; /**< the expression parse completed, * no more tokens can be added to the expression */ uint8_t is_list_in_process : 1; /**< parsing a list, associated with the expression * (details depend on current expression type) */ uint8_t is_no_in_mode : 1; /**< expression is being parsed in NoIn mode (see also: ECMA-262 v5, 11.8) */ uint8_t is_fixed_ret_operand : 1; /**< the expression's evaluation should produce value that should be * put to register, specified by operand, specified in state */ uint8_t is_value_based_reference : 1; /**< flag, indicating whether current state represents evaluated expression * that evaluated to a value-based reference */ uint8_t is_get_value_dumped_for_main_operand : 1; uint8_t is_get_value_dumped_for_prop_operand : 1; uint8_t is_need_retval : 1; /**< flag, indicating whether result of the expression's * evaluation, if it is value, is used */ uint8_t is_complex_production : 1; /**< the expression is being parsed in complex production mode */ uint8_t var_decl : 1; /**< this flag tells that we are parsing VariableStatement, not VariableDeclarationList or VariableDeclaration inside IterationStatement */ uint8_t is_var_decl_no_in : 1; /**< this flag tells that we are parsing VariableDeclrationNoIn inside ForIn iteration statement */ uint8_t was_default : 1; /**< was default branch seen */ uint8_t is_default_branch : 1; /**< marks default branch of switch statement */ uint8_t is_simply_jumpable_border : 1; /**< flag, indicating whether simple jump could be performed * from current statement outside of the statement */ uint8_t is_dump_eval_ret_store : 1; /**< expression's result should be stored to eval's return register */ uint8_t is_stmt_list_control_flow_exit_stmt_occured : 1; /**< flag, indicating whether one of the following statements * occured immediately in the statement list, corresponding * to the statement: * - return * - break * - continue * - throw */ union u { u (void) { } struct expression { union u { struct { uint32_t list_length; vm_instr_counter_t header_pos; /**< position of a varg header instruction */ vm_idx_t reg_alloc_saved_state1; vm_idx_t reg_alloc_saved_state2; } varg_sequence; JERRY_STATIC_ASSERT (sizeof (varg_sequence) == 8); // Please, update size if changed struct { jsp_operand_t prop_name; bool is_setter; } accessor_prop_decl; JERRY_STATIC_ASSERT (sizeof (accessor_prop_decl) == 6); // Please, update size if changed struct { vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */ } logical_and; JERRY_STATIC_ASSERT (sizeof (logical_and) == 2); // Please, update size if changed struct { vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */ } logical_or; JERRY_STATIC_ASSERT (sizeof (logical_or) == 2); // Please, update size if changed struct { vm_instr_counter_t conditional_check_pos; vm_instr_counter_t jump_to_end_pos; } conditional; JERRY_STATIC_ASSERT (sizeof (conditional) == 4); // Please, update size if changed } u; JERRY_STATIC_ASSERT (sizeof (u) == 8); // Please, update size if changed jsp_operand_t operand; /**< operand, associated with expression */ jsp_operand_t prop_name_operand; /**< operand, describing second part of a value-based reference, * or empty operand (for Identifier references, values, or constants) */ jsp_token_type_t token_type; /**< token, related to current and, if binary, to previous expression */ } expression; JERRY_STATIC_ASSERT (sizeof (expression) == 20); // Please, update size if changed struct statement { union u { struct iterational { union u { struct loop_for_in { union u { locus iterator_expr_loc; locus body_loc; } u; lit_cpointer_t var_name_lit_cp; vm_instr_counter_t header_pos; } loop_for_in; JERRY_STATIC_ASSERT (sizeof (loop_for_in) == 8); // Please, update size if changed struct loop_while { union u { locus cond_expr_start_loc; locus end_loc; } u; vm_instr_counter_t next_iter_tgt_pos; vm_instr_counter_t jump_to_end_pos; } loop_while; JERRY_STATIC_ASSERT (sizeof (loop_while) == 8); // Please, update size if changed struct loop_do_while { vm_instr_counter_t next_iter_tgt_pos; } loop_do_while; JERRY_STATIC_ASSERT (sizeof (loop_do_while) == 2); // Please, update size if changed struct loop_for { union u1 { locus body_loc; locus condition_expr_loc; } u1; union u2 { locus increment_expr_loc; locus end_loc; } u2; vm_instr_counter_t next_iter_tgt_pos; vm_instr_counter_t jump_to_end_pos; } loop_for; JERRY_STATIC_ASSERT (sizeof (loop_for) == 12); // Please, update size if changed } u; JERRY_STATIC_ASSERT (sizeof (u) == 12); // Please, update size if changed vm_instr_counter_t continues_rewrite_chain; vm_instr_counter_t continue_tgt_oc; } iterational; JERRY_STATIC_ASSERT (sizeof (iterational) == 16); // Please, update size if changed struct if_statement { vm_instr_counter_t conditional_check_pos; vm_instr_counter_t jump_to_end_pos; } if_statement; JERRY_STATIC_ASSERT (sizeof (if_statement) == 4); // Please, update size if changed struct switch_statement { jsp_operand_t expr; vm_instr_counter_t default_label_oc; /**< MAX_OPCODES - if DefaultClause didn't occur, * start of StatementList for the DefaultClause, otherwise */ vm_instr_counter_t last_cond_check_jmp_oc; /**< position of last clause's check, * of MAX_OPCODES (at start, or after DefaultClause) */ vm_instr_counter_t skip_check_jmp_oc; /**< position of check for whether next condition check * should be performed or skipped */ vm_idx_t saved_reg_next; vm_idx_t saved_reg_max_for_temps; } switch_statement; JERRY_STATIC_ASSERT (sizeof (switch_statement) == 12); // Please, update size if changed struct with_statement { vm_instr_counter_t header_pos; } with_statement; JERRY_STATIC_ASSERT (sizeof (with_statement) == 2); // Please, update size if changed struct try_statement { vm_instr_counter_t try_pos; vm_instr_counter_t catch_pos; vm_instr_counter_t finally_pos; } try_statement; JERRY_STATIC_ASSERT (sizeof (try_statement) == 6); // Please, update size if changed } u; JERRY_STATIC_ASSERT (sizeof (u) == 16); // Please, update size if changed vm_instr_counter_t breaks_rewrite_chain; } statement; JERRY_STATIC_ASSERT (sizeof (statement) == 20); // Please, update size if changed struct named_label { lit_cpointer_t name_cp; } named_label; JERRY_STATIC_ASSERT (sizeof (named_label) == 2); // Please, update size if changed struct source_elements { uint16_t parent_scope_child_scopes_counter; vm_instr_counter_t reg_var_decl_oc; scope_type_t parent_scope_type; vm_idx_t saved_reg_next; vm_idx_t saved_reg_max_for_temps; union { mem_cpointer_t parent_scopes_tree_node_cp; mem_cpointer_t parent_bc_header_cp; } u; } source_elements; JERRY_STATIC_ASSERT (sizeof (source_elements) == 10); // Please, update size if changed } u; JERRY_STATIC_ASSERT (sizeof (u) == 20); // Please, update size if changed } jsp_state_t; JERRY_STATIC_ASSERT (sizeof (jsp_state_t) == 28); // Please, update if size is changed static void jsp_parse_source_element_list (jsp_ctx_t *, scope_type_t); /** * Initialize parser context */ void jsp_init_ctx (jsp_ctx_t *ctx_p, /**< parser context */ scope_type_t scope_type) /**< starting scope type (SCOPE_TYPE_GLOBAL or SCOPE_TYPE_EVAL) */ { JERRY_ASSERT (scope_type == SCOPE_TYPE_GLOBAL || scope_type == SCOPE_TYPE_EVAL); ctx_p->mode = PREPARSE; ctx_p->scope_type = scope_type; ctx_p->state_stack_p = NULL; ctx_p->u.preparse_stage.current_scope_p = NULL; } /* jsp_init_ctx */ /** * Check whether current stage is the dump stage * * @return true / false */ bool jsp_is_dump_mode (jsp_ctx_t *ctx_p) /**< parser context */ { return (ctx_p->mode == DUMP); } /* jsp_is_dump_mode */ /** * Switch to dump stage */ void jsp_switch_to_dump_mode (jsp_ctx_t *ctx_p, /**< parser context */ scopes_tree root_scope_p) /**< root of scopes tree */ { JERRY_ASSERT (ctx_p->mode == PREPARSE); JERRY_ASSERT (ctx_p->u.preparse_stage.current_scope_p == NULL); ctx_p->mode = DUMP; ctx_p->u.dump_stage.current_bc_header_p = NULL; ctx_p->u.dump_stage.next_scope_to_dump_p = root_scope_p; } /* jsp_switch_to_dump_mode */ /** * Check whether currently parsed code is strict mode code * * @return true / false */ bool jsp_is_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (bc_header_p != NULL); return bc_header_p->is_strict; } else { scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); JERRY_ASSERT (scope_p != NULL); return scope_p->strict_mode; } } /* jsp_is_strict_mode */ /** * Indicate that currently parsed code is strict mode code */ void jsp_set_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (bc_header_p != NULL && bc_header_p->is_strict); } else { scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); JERRY_ASSERT (scope_p != NULL); scope_p->strict_mode = true; } } /* jsp_set_strict_mode */ /** * Get current scope type * * @return scope type */ scope_type_t jsp_get_scope_type (jsp_ctx_t *ctx_p) /**< parser context */ { return ctx_p->scope_type; } /* jsp_get_scope_type */ /** * Set current scope type */ void jsp_set_scope_type (jsp_ctx_t *ctx_p, /**< parser context */ scope_type_t scope_type) /**< scope type */ { ctx_p->scope_type = scope_type; } /* jsp_set_scope_type */ /** * Get number of current scope's child scopes, for which parse was already started * * @return the value */ uint16_t jsp_get_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= bc_header_p->func_scopes_count); } else { scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p); JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= scope->child_scopes_num); } return ctx_p->processed_child_scopes_counter; } /* jsp_get_processed_child_scopes_counter */ /** * Set number of current scope's child scopes, for which parse was already started */ void jsp_set_processed_child_scopes_counter (jsp_ctx_t *ctx_p, /**< parser context */ uint16_t processed_child_scopes_counter) /**< the new value */ { if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); if (bc_header_p == NULL) { JERRY_ASSERT (processed_child_scopes_counter == 0); } else { JERRY_ASSERT (processed_child_scopes_counter <= bc_header_p->func_scopes_count); } } else { scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p); if (scope == NULL) { JERRY_ASSERT (processed_child_scopes_counter == 0); } else { JERRY_ASSERT (processed_child_scopes_counter <= scope->child_scopes_num); } } ctx_p->processed_child_scopes_counter = processed_child_scopes_counter; } /* jsp_set_processed_child_scopes_counter */ /** * Get and increment number of current scope's child scopes, for which parse was already started * * @return the value before increment */ uint16_t jsp_get_and_inc_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */ { uint16_t counter = jsp_get_processed_child_scopes_counter (ctx_p); uint16_t ret = counter++; jsp_set_processed_child_scopes_counter (ctx_p, counter); return ret; } /* jsp_get_and_inc_processed_child_scopes_counter */ /** * Get next scope to dump * * Actually, it the function is iterator interface, used for traversing scopes tree during dump stage. * It is assumed that node of scopes tree are linked into the list in pre-order traversal order. */ scopes_tree jsp_get_next_scopes_tree_node_to_dump (jsp_ctx_t *ctx_p) /**< parser context */ { JERRY_ASSERT (ctx_p->mode == DUMP); scopes_tree node = ctx_p->u.dump_stage.next_scope_to_dump_p; JERRY_ASSERT (node != NULL); ctx_p->u.dump_stage.next_scope_to_dump_p = MEM_CP_GET_POINTER (scopes_tree_int, node->next_scope_cp); return node; } /* jsp_get_next_scopes_tree_node_to_dump */ /** * Get current scope (node of scopes tree) * * Note: * Valid only during preparse stage * * @return the node */ scopes_tree jsp_get_current_scopes_tree_node (jsp_ctx_t *ctx_p) /**< parser context */ { JERRY_ASSERT (ctx_p->mode == PREPARSE); return ctx_p->u.preparse_stage.current_scope_p; } /* jsp_get_current_scopes_tree_node */ /** * Set current scope (node of scopes tree) * * Note: * Valid only during preparse stage */ void jsp_set_current_scopes_tree_node (jsp_ctx_t *ctx_p, /**< parser context */ scopes_tree scope_p) /**< scopes tree node */ { JERRY_ASSERT (ctx_p->mode == PREPARSE); JERRY_ASSERT (scope_p != ctx_p->u.preparse_stage.current_scope_p); ctx_p->u.preparse_stage.current_scope_p = scope_p; ctx_p->u.preparse_stage.tmp_lit_set_num = 0; jsp_set_processed_child_scopes_counter (ctx_p, 0); } /* jsp_set_current_scopes_tree_node */ /** * Get current scope's byte-code data header * * Note: * Valid only during dump stage * * @return the byte-code data header */ bytecode_data_header_t * jsp_get_current_bytecode_header (jsp_ctx_t *ctx_p) /**< parser context */ { JERRY_ASSERT (ctx_p->mode == DUMP); return ctx_p->u.dump_stage.current_bc_header_p; } /* jsp_get_current_bytecode_header */ /** * Set current scope's byte-code data header * * Note: * Valid only during dump stage */ void jsp_set_current_bytecode_header (jsp_ctx_t *ctx_p, /**< parser context */ bytecode_data_header_t *bc_header_p) /**< byte-code data header */ { JERRY_ASSERT (ctx_p->mode == DUMP); ctx_p->u.dump_stage.current_bc_header_p = bc_header_p; jsp_set_processed_child_scopes_counter (ctx_p, 0); } /* jsp_set_current_bytecode_header */ /** * Empty temporary literal set * * Note: * Valid only during preparse stage * * See also: * Definition of tmp_lit_set in jsp_ctx_t */ void jsp_empty_tmp_literal_set (jsp_ctx_t *ctx_p) /**< parser context */ { JERRY_ASSERT (ctx_p->mode == PREPARSE); ctx_p->u.preparse_stage.tmp_lit_set_num = 0; } /* jsp_empty_tmp_literal_set */ /** * Take into account the specified literal and adjust temporary literal set and unique literals counter accordingly * * Note: * Valid only during preparse stage * * See also: * Definition of tmp_lit_set in jsp_ctx_t */ void jsp_account_next_bytecode_to_literal_reference (jsp_ctx_t *ctx_p, /**< parser context */ lit_cpointer_t lit_cp) /**< literal identifier */ { JERRY_ASSERT (ctx_p->mode == PREPARSE); scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); uint8_t tmp_hash_table_index; for (tmp_hash_table_index = 0; tmp_hash_table_index < ctx_p->u.preparse_stage.tmp_lit_set_num; tmp_hash_table_index++) { if (ctx_p->u.preparse_stage.tmp_lit_set[tmp_hash_table_index].packed_value == lit_cp.packed_value) { break; } } if (tmp_hash_table_index == ctx_p->u.preparse_stage.tmp_lit_set_num) { scope_p->max_uniq_literals_num++; if (ctx_p->u.preparse_stage.tmp_lit_set_num < SCOPE_TMP_LIT_SET_SIZE) { ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num++] = lit_cp; } else { JERRY_ASSERT (ctx_p->u.preparse_stage.tmp_lit_set_num != 0); ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num - 1u] = lit_cp; } } } /* jsp_account_next_bytecode_to_literal_reference */ static bool token_is (jsp_token_type_t tt) { return (lexer_get_token_type (tok) == tt); } static uint16_t token_data (void) { return tok.uid; } /** * Get token data as `lit_cpointer_t` * * @return compressed pointer to token data */ static lit_cpointer_t token_data_as_lit_cp (void) { lit_cpointer_t cp; cp.packed_value = tok.uid; return cp; } /* token_data_as_lit_cp */ static void skip_token (jsp_ctx_t *ctx_p) { tok = lexer_next_token (false, jsp_is_strict_mode (ctx_p)); } /** * In case a regexp token is scanned as a division operator, rescan it */ static void rescan_regexp_token (jsp_ctx_t *ctx_p) { lexer_seek (tok.loc); tok = lexer_next_token (true, jsp_is_strict_mode (ctx_p)); } /* rescan_regexp_token */ static void seek_token (jsp_ctx_t *ctx_p, locus loc) { lexer_seek (loc); skip_token (ctx_p); } static bool is_keyword (jsp_token_type_t tt) { return (tt >= TOKEN_TYPE__KEYWORD_BEGIN && tt <= TOKEN_TYPE__KEYWORD_END); } static void assert_keyword (jsp_token_type_t kw) { if (!token_is (kw)) { EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected keyword '%s'", lexer_token_type_to_string (kw)); JERRY_UNREACHABLE (); } } static void current_token_must_be_check_and_skip_it (jsp_ctx_t *ctx_p, jsp_token_type_t tt) { if (!token_is (tt)) { EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt)); } skip_token (ctx_p); } static void current_token_must_be (jsp_token_type_t tt) { if (!token_is (tt)) { EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt)); } } /** * Skip block, defined with braces of specified type * * Note: * Missing corresponding brace is considered a syntax error * * Note: * Opening brace of the block to skip should be set as current * token when the routine is called */ static void jsp_skip_braces (jsp_ctx_t *ctx_p, /**< parser context */ jsp_token_type_t brace_type) /**< type of the opening brace */ { current_token_must_be (brace_type); jsp_token_type_t closing_bracket_type; if (brace_type == TOK_OPEN_PAREN) { closing_bracket_type = TOK_CLOSE_PAREN; } else if (brace_type == TOK_OPEN_BRACE) { closing_bracket_type = TOK_CLOSE_BRACE; } else { JERRY_ASSERT (brace_type == TOK_OPEN_SQUARE); closing_bracket_type = TOK_CLOSE_SQUARE; } skip_token (ctx_p); while (!token_is (closing_bracket_type) && !token_is (TOK_EOF)) { if (token_is (TOK_OPEN_PAREN) || token_is (TOK_OPEN_BRACE) || token_is (TOK_OPEN_SQUARE)) { jsp_skip_braces (ctx_p, lexer_get_token_type (tok)); } skip_token (ctx_p); } current_token_must_be (closing_bracket_type); } /* jsp_skip_braces */ /** * Find next token of specified type before the specified location * * Note: * If skip_brace_blocks is true, every { should correspond to } brace before search end location, * otherwise a syntax error is raised. * * @return true - if token was found (in the case, it is the current token, * and lexer locus points to it), * false - otherwise (in the case, lexer locus points to end_loc). */ static bool jsp_find_next_token_before_the_locus (jsp_ctx_t *ctx_p, /**< parser context */ jsp_token_type_t token_to_find, /**< token to search for (except TOK_EOF) */ locus end_loc, /**< location to search before */ bool skip_brace_blocks) /**< skip blocks, surrounded with { and } braces */ { JERRY_ASSERT (token_to_find != TOK_EOF); while (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) < 0) { if (skip_brace_blocks) { if (token_is (TOK_OPEN_BRACE)) { jsp_skip_braces (ctx_p, TOK_OPEN_BRACE); JERRY_ASSERT (token_is (TOK_CLOSE_BRACE)); skip_token (ctx_p); if (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) >= 0) { seek_token (ctx_p, end_loc); return false; } } else if (token_is (TOK_CLOSE_BRACE)) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unmatched } brace"); } } if (token_is (token_to_find)) { return true; } else { JERRY_ASSERT (!token_is (TOK_EOF)); } skip_token (ctx_p); } JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) == 0); return false; } /* jsp_find_next_token_before_the_locus */ /* property_name : Identifier | Keyword | StringLiteral | NumericLiteral ; */ static jsp_operand_t parse_property_name (void) { jsp_token_type_t tt = lexer_get_token_type (tok); if (is_keyword (tt)) { const char *s = lexer_token_type_to_string (lexer_get_token_type (tok)); lit_literal_t lit = lit_find_or_create_literal_from_utf8_string ((const lit_utf8_byte_t *) s, (lit_utf8_size_t)strlen (s)); return jsp_operand_t::make_string_lit_operand (rcs_cpointer_compress (lit)); } else { switch (tt) { case TOK_NAME: case TOK_STRING: { return jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); } case TOK_NUMBER: case TOK_SMALL_INT: { ecma_number_t num; if (lexer_get_token_type (tok) == TOK_NUMBER) { lit_literal_t num_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); JERRY_ASSERT (RCS_RECORD_IS_NUMBER (num_lit)); num = lit_number_literal_get_number (num_lit); } else { num = ((ecma_number_t) token_data ()); } lit_utf8_byte_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; lit_utf8_size_t sz = ecma_number_to_utf8_string (num, buff, sizeof (buff)); JERRY_ASSERT (sz <= ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER); lit_literal_t str_lit = lit_find_or_create_literal_from_utf8_string (buff, sz); return jsp_operand_t::make_string_lit_operand (rcs_cpointer_compress (str_lit)); } case TOK_NULL: case TOK_BOOL: { lit_magic_string_id_t id = (token_is (TOK_NULL) ? LIT_MAGIC_STRING_NULL : (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE)); lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id), lit_get_magic_string_size (id)); return jsp_operand_t::make_string_lit_operand (rcs_cpointer_compress (lit)); } default: { EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Wrong property name type: %s", lexer_token_type_to_string (lexer_get_token_type (tok))); } } } } /** * Check for "use strict" in directive prologue */ static void jsp_parse_directive_prologue (jsp_ctx_t *ctx_p) { const locus start_loc = tok.loc; /* * Check Directive Prologue for Use Strict directive (see ECMA-262 5.1 section 14.1) */ while (token_is (TOK_STRING)) { if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "use strict") && lexer_is_no_escape_sequences_in_token_string (tok)) { jsp_set_strict_mode (ctx_p); break; } skip_token (ctx_p); if (token_is (TOK_SEMICOLON)) { skip_token (ctx_p); } } seek_token (ctx_p, start_loc); } /* jsp_parse_directive_prologue */ static void jsp_state_push (jsp_ctx_t *ctx_p, jsp_state_t state) { jsp_state_t *new_top_state_p = (jsp_state_t *) jsp_mm_alloc (sizeof (jsp_state_t)); *new_top_state_p = state; new_top_state_p->next_state_cp = MEM_CP_NULL; MEM_CP_SET_POINTER (new_top_state_p->prev_state_cp, ctx_p->state_stack_p); if (ctx_p->state_stack_p != NULL) { MEM_CP_SET_NON_NULL_POINTER (ctx_p->state_stack_p->next_state_cp, new_top_state_p); } ctx_p->state_stack_p = new_top_state_p; } /* jsp_state_push */ static jsp_state_t * jsp_get_prev_state (jsp_state_t *state_p) { return MEM_CP_GET_POINTER (jsp_state_t, state_p->prev_state_cp); } /* jsp_get_prev_state */ static jsp_state_t * jsp_get_next_state (jsp_state_t *state_p) { return MEM_CP_GET_NON_NULL_POINTER (jsp_state_t, state_p->next_state_cp); } /* jsp_get_next_state */ static jsp_state_t * jsp_state_top (jsp_ctx_t *ctx_p) { JERRY_ASSERT (ctx_p->state_stack_p != NULL); return ctx_p->state_stack_p; } /* jsp_state_top */ static bool jsp_is_stack_contains_exactly_one_element (jsp_ctx_t *ctx_p) { jsp_state_t *top_state_p = jsp_state_top (ctx_p); return (top_state_p != NULL && jsp_get_prev_state (top_state_p) == NULL); } /* jsp_is_stack_contains_exactly_one_element */ static void jsp_state_pop (jsp_ctx_t *ctx_p) { jsp_state_t *top_state_p = ctx_p->state_stack_p; JERRY_ASSERT (top_state_p != NULL); jsp_state_t *prev_state_p = jsp_get_prev_state (top_state_p); if (prev_state_p != NULL) { prev_state_p->next_state_cp = MEM_CP_NULL; } jsp_mm_free (top_state_p); ctx_p->state_stack_p = prev_state_p; } /* jsp_state_pop */ static void jsp_stack_init (jsp_ctx_t *ctx_p) { JERRY_ASSERT (ctx_p->state_stack_p == NULL); } /* jsp_stack_init */ static void jsp_stack_finalize (jsp_ctx_t *ctx_p) { while (ctx_p->state_stack_p != NULL) { jsp_state_pop (ctx_p); } } /* jsp_stack_finalize */ static void jsp_push_new_expr_state (jsp_ctx_t *ctx_p, jsp_state_expr_t expr_type, jsp_state_expr_t req_state, bool in_allowed) { jsp_state_t new_state; new_state.state = expr_type; new_state.req_state = req_state; new_state.u.expression.operand = empty_operand (); new_state.u.expression.prop_name_operand = empty_operand (); new_state.u.expression.token_type = TOK_EMPTY; new_state.is_completed = false; new_state.is_list_in_process = false; new_state.is_fixed_ret_operand = false; new_state.is_value_based_reference = false; new_state.is_complex_production = false; new_state.var_decl = false; new_state.is_var_decl_no_in = false; new_state.is_get_value_dumped_for_main_operand = false; new_state.is_get_value_dumped_for_prop_operand = false; new_state.is_need_retval = true; new_state.prev_state_cp = MEM_CP_NULL; new_state.next_state_cp = MEM_CP_NULL; new_state.was_default = false; new_state.is_default_branch = false; new_state.is_simply_jumpable_border = false; new_state.is_dump_eval_ret_store = false; new_state.is_stmt_list_control_flow_exit_stmt_occured = false; new_state.is_no_in_mode = !in_allowed; jsp_state_push (ctx_p, new_state); } /* jsp_push_new_expr_state */ static void jsp_start_statement_parse (jsp_ctx_t *ctx_p, jsp_state_expr_t stat) { jsp_state_t new_state; new_state.state = stat; new_state.req_state = JSP_STATE_STAT_STATEMENT; new_state.u.statement.breaks_rewrite_chain = MAX_OPCODES; new_state.is_completed = false; new_state.is_list_in_process = false; new_state.is_no_in_mode = false; new_state.is_fixed_ret_operand = false; new_state.is_complex_production = false; new_state.var_decl = false; new_state.was_default = false; new_state.is_default_branch = false; new_state.is_simply_jumpable_border = false; new_state.is_stmt_list_control_flow_exit_stmt_occured = false; new_state.prev_state_cp = MEM_CP_NULL; new_state.next_state_cp = MEM_CP_NULL; new_state.is_value_based_reference = false; new_state.is_get_value_dumped_for_main_operand = false; new_state.is_get_value_dumped_for_prop_operand = false; new_state.is_need_retval = false; new_state.is_var_decl_no_in = false; new_state.is_dump_eval_ret_store = false; jsp_state_push (ctx_p, new_state); } /* jsp_start_statement_parse */ static void jsp_start_parse_source_element_list (jsp_ctx_t *ctx_p, mem_cpointer_t parent_scope_or_bc_header_cp, uint16_t parent_scope_processed_scopes_counter, scope_type_t scope_type) { const scope_type_t parent_scope_type = jsp_get_scope_type (ctx_p); jsp_start_statement_parse (ctx_p, JSP_STATE_SOURCE_ELEMENTS); jsp_set_scope_type (ctx_p, scope_type); jsp_state_t *source_elements_state_p = jsp_state_top (ctx_p); source_elements_state_p->req_state = JSP_STATE_SOURCE_ELEMENTS; if (jsp_is_dump_mode (ctx_p)) { source_elements_state_p->u.source_elements.u.parent_bc_header_cp = parent_scope_or_bc_header_cp; } else { source_elements_state_p->u.source_elements.u.parent_scopes_tree_node_cp = parent_scope_or_bc_header_cp; } dumper_save_reg_alloc_ctx (ctx_p, &source_elements_state_p->u.source_elements.saved_reg_next, &source_elements_state_p->u.source_elements.saved_reg_max_for_temps); source_elements_state_p->u.source_elements.reg_var_decl_oc = dump_reg_var_decl_for_rewrite (ctx_p); source_elements_state_p->u.source_elements.parent_scope_type = parent_scope_type; source_elements_state_p->u.source_elements.parent_scope_child_scopes_counter = parent_scope_processed_scopes_counter; if (scope_type == SCOPE_TYPE_EVAL) { dump_variable_assignment (ctx_p, jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET), jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED)); } } /* jsp_start_parse_source_element_list */ static jsp_operand_t jsp_start_parse_function_scope (jsp_ctx_t *ctx_p, jsp_operand_t func_name, bool is_function_expression, size_t *out_formal_parameters_num_p) { jsp_operand_t func; uint16_t index; if (jsp_is_dump_mode (ctx_p)) { index = jsp_get_and_inc_processed_child_scopes_counter (ctx_p); } else { index = 0u; } if (is_function_expression) { func = tmp_operand (); vm_idx_t idx1 = (vm_idx_t) jrt_extract_bit_field (index, 0, JERRY_BITSINBYTE); vm_idx_t idx2 = (vm_idx_t) jrt_extract_bit_field (index, JERRY_BITSINBYTE, JERRY_BITSINBYTE); dump_binary_op (ctx_p, VM_OP_FUNC_EXPR_REF, func, jsp_operand_t::make_idx_const_operand (idx1), jsp_operand_t::make_idx_const_operand (idx2)); } else { func = empty_operand (); } mem_cpointer_t parent_scope_or_bc_header_cp; uint16_t parent_scope_processed_scopes_counter = jsp_get_processed_child_scopes_counter (ctx_p); scopes_tree func_scope_p; if (!jsp_is_dump_mode (ctx_p)) { scopes_tree parent_scope_p = jsp_get_current_scopes_tree_node (ctx_p); MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_scope_p); scopes_tree_set_contains_functions (parent_scope_p); func_scope_p = scopes_tree_new_scope (parent_scope_p, SCOPE_TYPE_FUNCTION); jsp_set_current_scopes_tree_node (ctx_p, func_scope_p); scopes_tree_set_strict_mode (func_scope_p, scopes_tree_strict_mode (parent_scope_p)); } else { bytecode_data_header_t *parent_bc_header_p = jsp_get_current_bytecode_header (ctx_p); MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_bc_header_p); func_scope_p = jsp_get_next_scopes_tree_node_to_dump (ctx_p); bc_dump_single_scope (func_scope_p); mem_cpointer_t *parent_declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, parent_bc_header_p->declarations_cp); parent_declarations_p[index] = func_scope_p->bc_header_cp; bytecode_data_header_t *func_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, func_scope_p->bc_header_cp); jsp_set_current_bytecode_header (ctx_p, func_bc_header_p); scopes_tree_free_scope (func_scope_p); } /* parse formal parameters list */ size_t formal_parameters_num = 0; current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); JERRY_ASSERT (func_name.is_empty_operand () || func_name.is_string_lit_operand ()); varg_list_type vlt = is_function_expression ? VARG_FUNC_EXPR : VARG_FUNC_DECL; vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, vlt, func, func_name); locus formal_parameters_list_loc = tok.loc; while (!token_is (TOK_CLOSE_PAREN)) { current_token_must_be (TOK_NAME); jsp_operand_t formal_parameter_name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); skip_token (ctx_p); dump_varg (ctx_p, formal_parameter_name); formal_parameters_num++; if (token_is (TOK_COMMA)) { skip_token (ctx_p); } else { current_token_must_be (TOK_CLOSE_PAREN); } } skip_token (ctx_p); rewrite_varg_header_set_args_count (ctx_p, formal_parameters_num, varg_header_pos); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); jsp_parse_directive_prologue (ctx_p); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (func_name, jsp_is_strict_mode (ctx_p), tok.loc); } if (jsp_is_strict_mode (ctx_p)) { locus body_loc = tok.loc; seek_token (ctx_p, formal_parameters_list_loc); /* Check duplication of formal parameters names */ while (!token_is (TOK_CLOSE_PAREN)) { current_token_must_be (TOK_NAME); lit_literal_t current_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); locus current_parameter_loc = tok.loc; skip_token (ctx_p); if (token_is (TOK_COMMA)) { skip_token (ctx_p); } if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_emit_error_on_eval_and_arguments (current_parameter_name_lit, current_parameter_loc); } while (!token_is (TOK_CLOSE_PAREN)) { current_token_must_be (TOK_NAME); if (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) != 0) { lit_literal_t iter_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ()); if (lit_literal_equal_type (current_parameter_name_lit, iter_parameter_name_lit)) { PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Duplication of literal '%s' in FormalParameterList is not allowed in strict mode", tok.loc, lit_literal_to_str_internal_buf (iter_parameter_name_lit)); } } skip_token (ctx_p); if (token_is (TOK_COMMA)) { skip_token (ctx_p); } else { current_token_must_be (TOK_CLOSE_PAREN); } } seek_token (ctx_p, current_parameter_loc); JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) == 0); skip_token (ctx_p); if (token_is (TOK_COMMA)) { skip_token (ctx_p); } else { current_token_must_be (TOK_CLOSE_PAREN); } } seek_token (ctx_p, body_loc); } if (out_formal_parameters_num_p != NULL) { *out_formal_parameters_num_p = formal_parameters_num; } jsp_start_parse_source_element_list (ctx_p, parent_scope_or_bc_header_cp, parent_scope_processed_scopes_counter, SCOPE_TYPE_FUNCTION); return func; } static void jsp_finish_parse_function_scope (jsp_ctx_t *ctx_p) { current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE); } static void dump_get_value_for_state_if_const (jsp_ctx_t *ctx_p, jsp_state_t *state_p) { // todo: pass to dumper (void) ctx_p; JERRY_ASSERT (state_p->state > JSP_STATE_EXPR__BEGIN && state_p->state < JSP_STATE_EXPR__END); JERRY_ASSERT (!state_p->is_value_based_reference); if (state_p->u.expression.operand.is_string_lit_operand () || state_p->u.expression.operand.is_number_lit_operand () || state_p->u.expression.operand.is_regexp_lit_operand () || state_p->u.expression.operand.is_smallint_operand () || state_p->u.expression.operand.is_simple_value_operand ()) { jsp_operand_t reg = tmp_operand (); dump_variable_assignment (ctx_p, reg, state_p->u.expression.operand); state_p->u.expression.operand = reg; } } static bool dump_get_value_for_state_if_ref (jsp_ctx_t *ctx_p, jsp_state_t *state_p, bool is_dump_for_value_based_refs_only, bool is_check_only) { // TODO: pass to dumper (void) ctx_p; jsp_operand_t obj = state_p->u.expression.operand; jsp_operand_t prop_name = state_p->u.expression.prop_name_operand; bool is_value_based_reference = state_p->is_value_based_reference; jsp_operand_t val; bool is_dump = false; if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE) { if (is_value_based_reference) { if (!state_p->is_get_value_dumped_for_main_operand) { JERRY_ASSERT (!state_p->is_get_value_dumped_for_prop_operand); /* FIXME: if (obj.is_identifier_operand ()) { is_dump = true; if (!is_check_only) { jsp_operand_t general_reg = tmp_operand (); dump_variable_assignment (ctx_p, general_reg, obj); state_p->u.expression.operand = general_reg; state_p->is_get_value_dumped_for_main_operand = true; } } if (prop_name.is_identifier_operand ()) { is_dump = true; if (!is_check_only) { jsp_operand_t general_reg = tmp_operand (); dump_variable_assignment (ctx_p, general_reg, prop_name); state_p->u.expression.prop_name_operand = general_reg; state_p->is_get_value_dumped_for_prop_operand = true; } } */ } } } else { if (is_value_based_reference) { is_dump = true; JERRY_ASSERT (!prop_name.is_empty_operand ()); if (!is_check_only) { jsp_operand_t reg = tmp_operand (); dump_prop_getter (ctx_p, reg, obj, prop_name); val = reg; state_p->is_get_value_dumped_for_main_operand = true; state_p->is_get_value_dumped_for_prop_operand = true; } } else if (!is_dump_for_value_based_refs_only && (obj.is_identifier_operand () || obj.is_this_operand ())) { if (!state_p->is_get_value_dumped_for_main_operand) { is_dump = true; if (!is_check_only) { jsp_operand_t general_reg = tmp_operand (); dump_variable_assignment (ctx_p, general_reg, obj); val = general_reg; state_p->is_get_value_dumped_for_main_operand = true; } } } if (!is_check_only && is_dump) { JERRY_ASSERT (state_p->is_get_value_dumped_for_main_operand); JERRY_ASSERT (!state_p->is_value_based_reference || state_p->is_get_value_dumped_for_prop_operand); state_p->u.expression.operand = val; state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } } return is_dump; } static void dump_get_value_for_prev_states (jsp_ctx_t *ctx_p, jsp_state_t *state_p) { jsp_state_t *dump_state_border_for_p = jsp_get_prev_state (state_p); jsp_state_t *first_state_to_dump_for_p = state_p; while (dump_state_border_for_p != NULL) { if (dump_state_border_for_p->state > JSP_STATE_EXPR__BEGIN && dump_state_border_for_p->req_state < JSP_STATE_EXPR__END && (!dump_state_border_for_p->is_get_value_dumped_for_main_operand || !dump_state_border_for_p->is_get_value_dumped_for_prop_operand)) { first_state_to_dump_for_p = dump_state_border_for_p; dump_state_border_for_p = jsp_get_prev_state (dump_state_border_for_p); } else { break; } } JERRY_ASSERT (first_state_to_dump_for_p != NULL); jsp_state_t *state_iter_p = first_state_to_dump_for_p; while (state_iter_p != state_p) { dump_get_value_for_state_if_ref (ctx_p, state_iter_p, false, false); state_iter_p = jsp_get_next_state (state_iter_p); } } static void dump_get_value_if_ref (jsp_ctx_t *ctx_p, jsp_state_t *state_p, bool is_dump_for_value_based_refs_only) { if (dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, true)) { dump_get_value_for_prev_states (ctx_p, state_p); dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, false); } } static vm_instr_counter_t jsp_start_call_dump (jsp_ctx_t *ctx_p, jsp_state_t *state_p, vm_idx_t *out_state_p) { bool is_value_based_reference = state_p->is_value_based_reference; opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; jsp_operand_t obj, val; if (is_value_based_reference) { obj = state_p->u.expression.operand; jsp_operand_t prop_name = state_p->u.expression.prop_name_operand; if (obj.is_identifier_operand () && !state_p->is_get_value_dumped_for_main_operand) { jsp_operand_t reg = tmp_operand (); dump_variable_assignment (ctx_p, reg, obj); obj = reg; state_p->is_get_value_dumped_for_main_operand = true; } val = tmp_operand (); dump_prop_getter (ctx_p, val, obj, prop_name); state_p->is_get_value_dumped_for_prop_operand = true; call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_HAVE_THIS_ARG); /* * Presence of explicit 'this' argument implies that this is not Direct call to Eval * * See also: * ECMA-262 v5, 15.2.2.1 */ } else { dump_get_value_for_state_if_const (ctx_p, state_p); obj = state_p->u.expression.operand; if (dumper_is_eval_literal (obj)) { call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM); } /* * Note: * If function is called through Identifier, then the obj should be an Identifier reference, * not register variable. * Otherwise, if function is called immediately, without reference (for example, through anonymous * function expression), the obj should be a register variable. * * See also: * vm_helper_call_get_call_flags_and_this_arg */ val = obj; state_p->is_get_value_dumped_for_main_operand = true; } vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); jsp_operand_t ret = tmp_operand (); vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, VARG_CALL_EXPR, ret, val); *out_state_p = dumper_save_reg_alloc_counter (ctx_p); dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); if (call_flags != OPCODE_CALL_FLAGS__EMPTY) { if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) { dump_call_additional_info (ctx_p, call_flags, obj); } else { dump_call_additional_info (ctx_p, call_flags, empty_operand ()); } } state_p->u.expression.operand = ret; state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; return varg_header_pos; } /* jsp_start_call_dump */ static void jsp_finish_call_dump (jsp_ctx_t *ctx_p, uint32_t args_num, vm_instr_counter_t header_pos, vm_idx_t reg_alloc_saved_state) { (void) ctx_p; dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos); } /* jsp_finish_call_dump */ static vm_instr_counter_t __attr_unused___ jsp_start_construct_dump (jsp_ctx_t *ctx_p, jsp_state_t *state_p, vm_idx_t *out_state_p) { dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); state_p->is_get_value_dumped_for_main_operand = true; vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); jsp_operand_t ret = tmp_operand (); vm_instr_counter_t pos = dump_varg_header_for_rewrite (ctx_p, VARG_CONSTRUCT_EXPR, ret, state_p->u.expression.operand); state_p->u.expression.operand = ret; *out_state_p = dumper_save_reg_alloc_counter (ctx_p); dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); return pos; } /* jsp_start_construct_dump */ static void jsp_finish_construct_dump (jsp_ctx_t *ctx_p, uint32_t args_num, vm_instr_counter_t header_pos, vm_idx_t reg_alloc_saved_state) { // TODO: pass to dumper (void) ctx_p; dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos); } /* jsp_finish_construct_dump */ static void jsp_add_nested_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, vm_instr_counter_t *in_out_rewrite_chain_p) { // TODO: pass to dumper (void) ctx_p; *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, true, false, false, empty_operand (), *in_out_rewrite_chain_p); } static void jsp_add_simple_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, vm_instr_counter_t *in_out_rewrite_chain_p) { // TODO: pass to dumper (void) ctx_p; *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, false, false, empty_operand (), *in_out_rewrite_chain_p); } static void jsp_add_conditional_jump_to_rewrite_chain (jsp_ctx_t *ctx_p, vm_instr_counter_t *in_out_rewrite_chain_p, bool is_inverted_condition, jsp_operand_t condition) { // TODO: pass to dumper (void) ctx_p; *in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, true, is_inverted_condition, condition, *in_out_rewrite_chain_p); } static uint32_t jsp_rewrite_jumps_chain (jsp_ctx_t *ctx_p, vm_instr_counter_t *rewrite_chain_p, vm_instr_counter_t target_oc) { // TODO: pass to dumper (void) ctx_p; uint32_t count = 0; while (*rewrite_chain_p != MAX_OPCODES) { count++; *rewrite_chain_p = rewrite_simple_or_nested_jump_and_get_next (ctx_p, *rewrite_chain_p, target_oc); } return count; } /* jsp_rewrite_jumps_chain */ static bool jsp_is_assignment_expression_end (jsp_state_t *current_state_p) { jsp_token_type_t tt = lexer_get_token_type (tok); JERRY_ASSERT (current_state_p->state == JSP_STATE_EXPR_UNARY || current_state_p->state == JSP_STATE_EXPR_MULTIPLICATIVE || current_state_p->state == JSP_STATE_EXPR_ADDITIVE || current_state_p->state == JSP_STATE_EXPR_SHIFT || current_state_p->state == JSP_STATE_EXPR_RELATIONAL || current_state_p->state == JSP_STATE_EXPR_EQUALITY || current_state_p->state == JSP_STATE_EXPR_BITWISE_AND || current_state_p->state == JSP_STATE_EXPR_BITWISE_XOR || current_state_p->state == JSP_STATE_EXPR_BITWISE_OR); if ((tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN && tt <= TOKEN_TYPE__MULTIPLICATIVE_END) || (tt >= TOKEN_TYPE__ADDITIVE_BEGIN && tt <= TOKEN_TYPE__ADDITIVE_END) || (tt >= TOKEN_TYPE__SHIFT_BEGIN && tt <= TOKEN_TYPE__SHIFT_END) || (tt >= TOKEN_TYPE__RELATIONAL_BEGIN && tt <= TOKEN_TYPE__RELATIONAL_END) || (tt >= TOKEN_TYPE__EQUALITY_BEGIN && tt <= TOKEN_TYPE__EQUALITY_END) || (tt == TOK_AND) || (tt == TOK_XOR) || (tt == TOK_OR) || (tt == TOK_DOUBLE_AND) || (tt == TOK_DOUBLE_OR) || (tt == TOK_QUERY)) { return false; } return true; } static bool jsp_dump_unary_op (jsp_ctx_t *ctx_p, jsp_state_t *state_p, jsp_state_t *substate_p) { vm_op_t opcode; bool is_combined_with_assignment = false; jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); JERRY_ASSERT (prev_state_p != NULL); bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE && prev_state_p->u.expression.token_type == TOK_EQ && !prev_state_p->is_value_based_reference && !prev_state_p->is_need_retval && jsp_is_assignment_expression_end (state_p)); jsp_token_type_t tt = state_p->u.expression.token_type; state_p->u.expression.token_type = TOK_EMPTY; bool is_dump_simple_assignment = false; bool is_dump_undefined_or_boolean_true = true; /* the value is not valid until is_dump_simple_assignment is set */ switch (tt) { case TOK_DOUBLE_PLUS: case TOK_DOUBLE_MINUS: { if (!substate_p->is_value_based_reference) { if (!jsp_is_dump_mode (ctx_p)) { if (substate_p->u.expression.operand.is_identifier_operand ()) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (substate_p->u.expression.operand, jsp_is_strict_mode (ctx_p), tok.loc); } else { PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); } } } opcode = (tt == TOK_DOUBLE_PLUS ? VM_OP_PRE_INCR : VM_OP_PRE_DECR); break; } case TOK_PLUS: { dump_get_value_if_ref (ctx_p, substate_p, true); opcode = VM_OP_UNARY_PLUS; break; } case TOK_MINUS: { dump_get_value_if_ref (ctx_p, substate_p, true); opcode = VM_OP_UNARY_MINUS; break; } case TOK_COMPL: { dump_get_value_if_ref (ctx_p, substate_p, true); opcode = VM_OP_B_NOT; break; } case TOK_NOT: { dump_get_value_if_ref (ctx_p, substate_p, true); opcode = VM_OP_LOGICAL_NOT; break; } case TOK_KW_DELETE: { if (substate_p->is_value_based_reference) { opcode = VM_OP_DELETE_PROP; } else if (substate_p->u.expression.operand.is_identifier_operand ()) { if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_delete (jsp_is_strict_mode (ctx_p), tok.loc); } opcode = VM_OP_DELETE_VAR; } else { opcode = VM_OP__COUNT /* invalid opcode as it will not be used */; is_dump_simple_assignment = true; is_dump_undefined_or_boolean_true = false; /* dump boolean true value */ } break; } case TOK_KW_VOID: { dump_get_value_if_ref (ctx_p, substate_p, false); opcode = VM_OP__COUNT /* invalid opcode as it will not be used */; is_dump_simple_assignment = true; is_dump_undefined_or_boolean_true = true; /* dump undefined value */ break; } default: { dump_get_value_if_ref (ctx_p, substate_p, true); JERRY_ASSERT (tt == TOK_KW_TYPEOF); opcode = VM_OP_TYPEOF; break; } } jsp_operand_t dst; if (!substate_p->is_value_based_reference) { dump_get_value_for_state_if_const (ctx_p, substate_p); } if (dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true)) { dump_get_value_for_prev_states (ctx_p, substate_p); } if (is_try_combine_with_assignment && (is_dump_simple_assignment || !substate_p->is_value_based_reference || opcode == VM_OP_DELETE_PROP)) { dst = prev_state_p->u.expression.operand; is_combined_with_assignment = true; } else { dst = tmp_operand (); } if (is_dump_simple_assignment) { if (is_dump_undefined_or_boolean_true) { dump_variable_assignment (ctx_p, dst, jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED)); } else { dump_variable_assignment (ctx_p, dst, jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_TRUE)); } } else { if (substate_p->is_value_based_reference) { if (opcode == VM_OP_DELETE_PROP) { dump_binary_op (ctx_p, VM_OP_DELETE_PROP, dst, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand); } else { JERRY_ASSERT (opcode == VM_OP_PRE_INCR || opcode == VM_OP_PRE_DECR); jsp_operand_t reg = tmp_operand (); dump_prop_getter (ctx_p, reg, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand); dump_unary_op (ctx_p, opcode, reg, reg); dump_prop_setter (ctx_p, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand, reg); dst = reg; } } else { JERRY_ASSERT (!substate_p->is_value_based_reference); dump_unary_op (ctx_p, opcode, dst, substate_p->u.expression.operand); } } JERRY_ASSERT (!state_p->is_value_based_reference); state_p->u.expression.operand = dst; return is_combined_with_assignment; } static bool jsp_dump_binary_op (jsp_ctx_t *ctx_p, jsp_state_t *state_p, jsp_state_t *substate_p) { vm_op_t opcode; bool is_combined_with_assignment = false; jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); JERRY_ASSERT (prev_state_p != NULL); bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE && prev_state_p->u.expression.token_type == TOK_EQ && !prev_state_p->is_value_based_reference && !prev_state_p->is_need_retval && jsp_is_assignment_expression_end (state_p)); jsp_token_type_t tt = state_p->u.expression.token_type; state_p->u.expression.token_type = TOK_EMPTY; switch (tt) { case TOK_MULT: { opcode = VM_OP_MULTIPLICATION; break; } case TOK_DIV: { opcode = VM_OP_DIVISION; break; } case TOK_MOD: { opcode = VM_OP_REMAINDER; break; } case TOK_PLUS: { opcode = VM_OP_ADDITION; break; } case TOK_MINUS: { opcode = VM_OP_SUBSTRACTION; break; } case TOK_LSHIFT: { opcode = VM_OP_B_SHIFT_LEFT; break; } case TOK_RSHIFT: { opcode = VM_OP_B_SHIFT_RIGHT; break; } case TOK_RSHIFT_EX: { opcode = VM_OP_B_SHIFT_URIGHT; break; } case TOK_LESS: { opcode = VM_OP_LESS_THAN; break; } case TOK_GREATER: { opcode = VM_OP_GREATER_THAN; break; } case TOK_LESS_EQ: { opcode = VM_OP_LESS_OR_EQUAL_THAN; break; } case TOK_GREATER_EQ: { opcode = VM_OP_GREATER_OR_EQUAL_THAN; break; } case TOK_KW_INSTANCEOF: { opcode = VM_OP_INSTANCEOF; break; } case TOK_KW_IN: { opcode = VM_OP_IN; break; } case TOK_DOUBLE_EQ: { opcode = VM_OP_EQUAL_VALUE; break; } case TOK_NOT_EQ: { opcode = VM_OP_NOT_EQUAL_VALUE; break; } case TOK_TRIPLE_EQ: { opcode = VM_OP_EQUAL_VALUE_TYPE; break; } case TOK_NOT_DOUBLE_EQ: { opcode = VM_OP_NOT_EQUAL_VALUE_TYPE; break; } case TOK_AND: { opcode = VM_OP_B_AND; break; } case TOK_XOR: { opcode = VM_OP_B_XOR; break; } default: { JERRY_ASSERT (tt == TOK_OR); opcode = VM_OP_B_OR; break; } } jsp_operand_t dst, op1, op2; if (!state_p->is_value_based_reference) { dump_get_value_for_state_if_const (ctx_p, state_p); } if (!substate_p->is_value_based_reference) { dump_get_value_for_state_if_const (ctx_p, substate_p); } if (!state_p->is_value_based_reference && !substate_p->is_value_based_reference) { if (dump_get_value_for_state_if_ref (ctx_p, state_p, false, true) || dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true)) { dump_get_value_for_prev_states (ctx_p, state_p); } op1 = state_p->u.expression.operand; op2 = substate_p->u.expression.operand; if (is_try_combine_with_assignment) { dst = prev_state_p->u.expression.operand; is_combined_with_assignment = true; } else if (op1.is_register_operand ()) { dst = op1; } else { dst = tmp_operand (); } } else { dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_if_ref (ctx_p, substate_p, true); if (is_try_combine_with_assignment) { dst = prev_state_p->u.expression.operand; is_combined_with_assignment = true; } else { dst = state_p->u.expression.operand; } op1 = state_p->u.expression.operand; op2 = substate_p->u.expression.operand; } JERRY_ASSERT (!state_p->is_value_based_reference); state_p->u.expression.operand = dst; dump_binary_op (ctx_p, opcode, dst, op1, op2); return is_combined_with_assignment; } static lit_cpointer_t jsp_get_prop_name_after_dot (void) { if (token_is (TOK_NAME)) { return token_data_as_lit_cp (); } else if (is_keyword (lexer_get_token_type (tok))) { const char *s = lexer_token_type_to_string (lexer_get_token_type (tok)); lit_literal_t lit = lit_find_or_create_literal_from_utf8_string ((lit_utf8_byte_t *) s, (lit_utf8_size_t) strlen (s)); if (lit == NULL) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier"); } return rcs_cpointer_compress (lit); } else if (token_is (TOK_BOOL) || token_is (TOK_NULL)) { lit_magic_string_id_t id = (token_is (TOK_NULL) ? LIT_MAGIC_STRING_NULL : (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE)); lit_literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id), lit_get_magic_string_size (id)); return rcs_cpointer_compress (lit); } else { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier"); } } /* jsp_get_prop_name_after_dot */ #ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER /** * Try to perform move (allocation) of variables' values on registers * * Note: * The optimization is only applied to functions * * @return number of instructions removed from function's header */ static void jsp_try_move_vars_to_regs (jsp_ctx_t *ctx_p, jsp_state_t *state_p) { // TODO: pass to dumper (void) ctx_p; JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS); bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); bool may_replace_vars_with_regs = bc_header_p->is_vars_and_args_to_regs_possible; if (may_replace_vars_with_regs) { /* no subscopes, as no function declarations / eval etc. in the scope */ JERRY_ASSERT (bc_header_p->func_scopes_count == 0); vm_instr_counter_t instr_pos = 0u; const vm_instr_counter_t header_oc = instr_pos++; op_meta header_opm = dumper_get_op_meta (ctx_p, header_oc); JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N || header_opm.op.op_idx == VM_OP_FUNC_DECL_N); uint32_t args_num; if (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N) { args_num = header_opm.op.data.func_expr_n.arg_list; } else { args_num = header_opm.op.data.func_decl_n.arg_list; } dumper_start_move_of_vars_to_regs (ctx_p); mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, bc_header_p->declarations_cp); lit_cpointer_t *var_declarations_p = (lit_cpointer_t *) (declarations_p + bc_header_p->func_scopes_count); /* remove declarations of variables with names equal to an argument's name */ uint32_t var_decl_index = 0; while (var_decl_index < bc_header_p->var_decls_count) { lit_cpointer_t var_name_lit_cp = var_declarations_p[var_decl_index]; op_meta var_decl_opm; var_decl_opm.op.op_idx = VM_OP_VAR_DECL; var_decl_opm.lit_id[0] = var_name_lit_cp; var_decl_opm.lit_id[1] = NOT_A_LITERAL; var_decl_opm.lit_id[2] = NOT_A_LITERAL; bool is_removed = false; for (vm_instr_counter_t arg_index = instr_pos; arg_index < instr_pos + args_num; arg_index++) { op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_index); JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); if (meta_opm.lit_id[1].packed_value == var_name_lit_cp.packed_value) { var_declarations_p[var_decl_index] = NOT_A_LITERAL; is_removed = true; break; } } if (!is_removed) { if (dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &var_decl_opm)) { var_declarations_p[var_decl_index] = NOT_A_LITERAL; } } var_decl_index++; } if (dumper_start_move_of_args_to_regs (ctx_p, args_num)) { bc_header_p->is_args_moved_to_regs = true; bc_header_p->is_no_lex_env = true; dumper_rewrite_op_meta (ctx_p, header_oc, header_opm); /* * Mark duplicated arguments names as empty, * leaving only last declaration for each duplicated * argument name */ for (vm_instr_counter_t arg1_index = instr_pos; arg1_index < instr_pos + args_num; arg1_index++) { op_meta meta_opm1 = dumper_get_op_meta (ctx_p, arg1_index); JERRY_ASSERT (meta_opm1.op.op_idx == VM_OP_META); for (vm_instr_counter_t arg2_index = (vm_instr_counter_t) (arg1_index + 1u); arg2_index < instr_pos + args_num; arg2_index++) { op_meta meta_opm2 = dumper_get_op_meta (ctx_p, arg2_index); JERRY_ASSERT (meta_opm2.op.op_idx == VM_OP_META); if (meta_opm1.lit_id[1].packed_value == meta_opm2.lit_id[1].packed_value) { meta_opm1.op.data.meta.data_1 = VM_IDX_EMPTY; meta_opm1.lit_id[1] = NOT_A_LITERAL; dumper_rewrite_op_meta (ctx_p, arg1_index, meta_opm1); break; } } } for (vm_instr_counter_t arg_instr_pos = instr_pos; arg_instr_pos < instr_pos + args_num; arg_instr_pos++) { op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_instr_pos); JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META); opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type; JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG); if (meta_opm.op.data.meta.data_1 == VM_IDX_EMPTY) { JERRY_ASSERT (meta_opm.lit_id[1].packed_value == NOT_A_LITERAL.packed_value); dumper_alloc_reg_for_unused_arg (ctx_p); } else { /* the varg specifies argument name, and so should be a string literal */ JERRY_ASSERT (meta_opm.lit_id[1].packed_value != NOT_A_LITERAL.packed_value); bool is_replaced = dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &meta_opm); JERRY_ASSERT (is_replaced); } } } } } #endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ static void parse_expression_inside_parens_begin (jsp_ctx_t *ctx_p) { current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); } static void parse_expression_inside_parens_end (jsp_ctx_t *ctx_p) { current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); } static jsp_state_t * jsp_find_unnamed_label (jsp_ctx_t *ctx_p, bool is_label_for_break, bool *out_is_simply_jumpable_p) { *out_is_simply_jumpable_p = true; jsp_state_t *state_p = jsp_state_top (ctx_p); while (state_p != NULL) { if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) { break; } if (state_p->is_simply_jumpable_border) { *out_is_simply_jumpable_p = false; } bool is_iterational_stmt = (state_p->state == JSP_STATE_STAT_WHILE || state_p->state == JSP_STATE_STAT_DO_WHILE || state_p->state == JSP_STATE_STAT_FOR_IN || state_p->state == JSP_STATE_STAT_FOR_IN_EXPR || state_p->state == JSP_STATE_STAT_FOR_IN_FINISH || state_p->state == JSP_STATE_STAT_FOR_INCREMENT); bool is_switch_stmt = (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR); if ((is_switch_stmt && is_label_for_break) || is_iterational_stmt) { return state_p; } state_p = jsp_get_prev_state (state_p); } return NULL; } static jsp_state_t * jsp_find_named_label (jsp_ctx_t *ctx_p, lit_cpointer_t name_cp, bool *out_is_simply_jumpable_p) { *out_is_simply_jumpable_p = true; jsp_state_t *state_p = jsp_state_top (ctx_p); jsp_state_t *last_non_named_label_state_p = NULL; while (state_p != NULL) { if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) { break; } if (state_p->is_simply_jumpable_border) { *out_is_simply_jumpable_p = false; } if (state_p->state != JSP_STATE_STAT_NAMED_LABEL) { last_non_named_label_state_p = state_p; } else if (state_p->u.named_label.name_cp.packed_value == name_cp.packed_value) { return last_non_named_label_state_p; } state_p = jsp_get_prev_state (state_p); } return NULL; } static void insert_semicolon (jsp_ctx_t *ctx_p) { bool is_new_line_occured = lexer_is_preceded_by_newlines (tok); bool is_close_brace_or_eof = (token_is (TOK_CLOSE_BRACE) || token_is (TOK_EOF)); if (!is_new_line_occured && !is_close_brace_or_eof) { if (token_is (TOK_SEMICOLON)) { skip_token (ctx_p); } else if (!token_is (TOK_EOF)) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either ';' or newline token"); } } } #define JSP_COMPLETE_STATEMENT_PARSE() \ do \ { \ JERRY_ASSERT (substate_p == NULL); \ state_p->state = JSP_STATE_STAT_STATEMENT; \ JERRY_ASSERT (!state_p->is_completed); \ dumper_new_statement (ctx_p); \ } \ while (0) #define JSP_PUSH_STATE_AND_STATEMENT_PARSE(s) \ do \ { \ state_p->state = (s); \ jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); \ dumper_new_statement (ctx_p); \ } \ while (0) #define JSP_FINISH_SUBEXPR() \ JERRY_ASSERT (substate_p == jsp_state_top (ctx_p)); \ substate_p = NULL; \ jsp_state_pop (ctx_p); \ JERRY_ASSERT (state_p == jsp_state_top (ctx_p)); #define JSP_ASSIGNMENT_EXPR_COMBINE() \ jsp_state_pop (ctx_p); \ state_p = jsp_state_top (ctx_p); \ JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE); \ JERRY_ASSERT (!state_p->is_need_retval); \ state_p->state = JSP_STATE_EXPR_ASSIGNMENT; \ state_p->u.expression.operand = empty_operand (); \ state_p->u.expression.token_type = TOK_EMPTY; \ state_p->is_completed = true; /** * Parse source element list */ static void __attr_noinline___ jsp_parse_source_element_list (jsp_ctx_t *ctx_p, scope_type_t scope_type) { jsp_start_parse_source_element_list (ctx_p, MEM_CP_NULL, 0, scope_type); while (true) { bool is_source_elements_list_end = false; bool is_stmt_list_end = false, is_stmt_list_control_flow_exit_stmt_occured = false; bool is_subexpr_end = false; jsp_state_t *state_p = jsp_state_top (ctx_p), *substate_p = NULL; if (state_p->state == state_p->req_state && state_p->is_completed) { if (jsp_is_stack_contains_exactly_one_element (ctx_p)) { JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS); jsp_state_pop (ctx_p); break; } else { is_subexpr_end = (state_p->state > JSP_STATE_EXPR__BEGIN && state_p->state < JSP_STATE_EXPR__END); if (is_subexpr_end) { substate_p = state_p; state_p = jsp_get_prev_state (state_p); } else { if (state_p->req_state == JSP_STATE_SOURCE_ELEMENTS) { is_source_elements_list_end = true; } else if (state_p->req_state == JSP_STATE_STAT_STATEMENT_LIST) { is_stmt_list_end = true; is_stmt_list_control_flow_exit_stmt_occured = state_p->is_stmt_list_control_flow_exit_stmt_occured; } JERRY_ASSERT (state_p->is_completed); jsp_state_pop (ctx_p); state_p = jsp_state_top (ctx_p); } } } else { /* * Any expression production, if parse of the production is not stopped because required * production type was reached, eventually becomes Expression production. * * Because there are no any expression production higher than Expression, * its invalid to reach Expression production type if required production is lower * (i.e. is not Expression production type). */ JERRY_ASSERT (!(state_p->state == JSP_STATE_EXPR_EXPRESSION && state_p->req_state != JSP_STATE_EXPR_EXPRESSION)); } const bool in_allowed = !state_p->is_no_in_mode; if (state_p->state == JSP_STATE_SOURCE_ELEMENTS) { scope_type_t scope_type = jsp_get_scope_type (ctx_p); jsp_token_type_t end_token_type = (scope_type == SCOPE_TYPE_FUNCTION) ? TOK_CLOSE_BRACE : TOK_EOF; if (token_is (end_token_type)) { #ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER if (jsp_is_dump_mode (ctx_p)) { jsp_try_move_vars_to_regs (ctx_p, state_p); } else { scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p); JERRY_ASSERT (!scope_p->is_vars_and_args_to_regs_possible); /* * We don't try to perform replacement of local variables with registers for global code, eval code. * * For global and eval code the replacement can be connected with side effects, * that currently can only be figured out in runtime. For example, a variable * can be redefined as accessor property of the Global object. */ bool is_vars_and_args_to_regs_possible = (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_FUNCTION && !scope_p->ref_eval /* 'eval' can reference variables in a way, * that can't be figured out through static * analysis */ && !scope_p->ref_arguments /* 'arguments' variable, if declared, * should not be moved to a register, * as it is currently declared in * function's lexical environment * (generally, the problem is the same, * as with function's arguments) */ && !scope_p->contains_with /* 'with' create new lexical environment * and so can change way identifier * is evaluated */ && !scope_p->contains_try /* same for 'catch' */ && !scope_p->contains_delete /* 'delete' handles variable's names, * not values */ && !scope_p->contains_functions); /* nested functions * can reference variables * of current function */ scope_p->is_vars_and_args_to_regs_possible = is_vars_and_args_to_regs_possible; } #endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */ rewrite_reg_var_decl (ctx_p, state_p->u.source_elements.reg_var_decl_oc); if (scope_type == SCOPE_TYPE_FUNCTION) { dump_ret (ctx_p); if (jsp_is_dump_mode (ctx_p)) { bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p); if (parser_show_instrs) { bc_print_instrs (bc_header_p); } mem_cpointer_t parent_bc_header_cp = state_p->u.source_elements.u.parent_bc_header_cp; bytecode_data_header_t *parent_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t, parent_bc_header_cp); jsp_set_current_bytecode_header (ctx_p, parent_bc_header_p); } else { scopes_tree parent_scope_p = MEM_CP_GET_POINTER (scopes_tree_int, state_p->u.source_elements.u.parent_scopes_tree_node_cp); jsp_set_current_scopes_tree_node (ctx_p, parent_scope_p); } } else if (scope_type == SCOPE_TYPE_GLOBAL) { dump_ret (ctx_p); } else { JERRY_ASSERT (scope_type == SCOPE_TYPE_EVAL); dump_retval (ctx_p, jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET)); } jsp_set_scope_type (ctx_p, state_p->u.source_elements.parent_scope_type); jsp_set_processed_child_scopes_counter (ctx_p, state_p->u.source_elements.parent_scope_child_scopes_counter); dumper_restore_reg_alloc_ctx (ctx_p, state_p->u.source_elements.saved_reg_next, state_p->u.source_elements.saved_reg_max_for_temps, true); state_p->is_completed = true; } else { JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_SOURCE_ELEMENTS); } } else if (state_p->state == JSP_STATE_EXPR_EMPTY) { rescan_regexp_token (ctx_p); /* no subexpressions can occur, as expression parse is just started */ JERRY_ASSERT (!is_subexpr_end); JERRY_ASSERT (!state_p->is_completed); jsp_token_type_t tt = lexer_get_token_type (tok); if ((tt >= TOKEN_TYPE__UNARY_BEGIN && tt <= TOKEN_TYPE__UNARY_END) || tt == TOK_KW_DELETE || tt == TOK_KW_VOID || tt == TOK_KW_TYPEOF) { /* UnaryExpression */ state_p->state = JSP_STATE_EXPR_UNARY; state_p->u.expression.token_type = tt; if (!jsp_is_dump_mode (ctx_p)) { if (tt == TOK_KW_DELETE) { scopes_tree_set_contains_delete (jsp_get_current_scopes_tree_node (ctx_p)); } } jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_UNARY, in_allowed); } else if (token_is (TOK_KW_FUNCTION)) { /* FunctionExpression */ state_p->state = JSP_STATE_EXPR_FUNCTION; } else if (token_is (TOK_OPEN_SQUARE)) { dump_get_value_for_prev_states (ctx_p, state_p); /* ArrayLiteral */ vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); state_p->u.expression.operand = tmp_operand (); vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, VARG_ARRAY_DECL, state_p->u.expression.operand, empty_operand ()); vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p); dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); state_p->state = JSP_STATE_EXPR_ARRAY_LITERAL; state_p->is_list_in_process = true; state_p->u.expression.u.varg_sequence.list_length = 0; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos; } else if (token_is (TOK_OPEN_BRACE)) { dump_get_value_for_prev_states (ctx_p, state_p); /* ObjectLiteral */ vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); state_p->u.expression.operand = tmp_operand (); vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, VARG_OBJ_DECL, state_p->u.expression.operand, empty_operand ()); vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p); dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_start_checking_of_prop_names (); } state_p->state = JSP_STATE_EXPR_OBJECT_LITERAL; state_p->is_list_in_process = true; state_p->u.expression.u.varg_sequence.list_length = 0; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos; } else { /* MemberExpression (PrimaryExpression is immediately promoted to MemberExpression) */ state_p->state = JSP_STATE_EXPR_MEMBER; switch (lexer_get_token_type (tok)) { case TOK_OPEN_PAREN: { state_p->u.expression.token_type = TOK_OPEN_PAREN; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); break; } case TOK_KW_THIS: { state_p->u.expression.operand = jsp_operand_t::make_this_operand (); break; } case TOK_KW_NEW: { state_p->state = JSP_STATE_EXPR_MEMBER; state_p->u.expression.token_type = TOK_KW_NEW; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_MEMBER, true); break; } case TOK_NAME: { if (!jsp_is_dump_mode (ctx_p)) { if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "arguments")) { scopes_tree_set_arguments_used (jsp_get_current_scopes_tree_node (ctx_p)); } if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "eval")) { scopes_tree_set_eval_used (jsp_get_current_scopes_tree_node (ctx_p)); } } state_p->u.expression.operand = jsp_operand_t::make_identifier_operand (token_data_as_lit_cp ()); break; } case TOK_REGEXP: { state_p->u.expression.operand = jsp_operand_t::make_regexp_lit_operand (token_data_as_lit_cp ()); break; } case TOK_NULL: { state_p->u.expression.operand = jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_NULL); break; } case TOK_BOOL: { ecma_simple_value_t simple_value = (bool) token_data () ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE; state_p->u.expression.operand = jsp_operand_t::make_simple_value_operand (simple_value); break; } case TOK_SMALL_INT: { state_p->u.expression.operand = jsp_operand_t::make_smallint_operand ((uint8_t) token_data ()); break; } case TOK_NUMBER: { state_p->u.expression.operand = jsp_operand_t::make_number_lit_operand (token_data_as_lit_cp ()); break; } case TOK_STRING: { state_p->u.expression.operand = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); break; } default: { EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Unknown token %s", lexer_token_type_to_string (lexer_get_token_type (tok))); } } } skip_token (ctx_p); } else if (state_p->state == JSP_STATE_EXPR_FUNCTION) { JERRY_ASSERT (!state_p->is_completed); if (is_source_elements_list_end) { jsp_finish_parse_function_scope (ctx_p); state_p->state = JSP_STATE_EXPR_MEMBER; } else { jsp_operand_t name; if (token_is (TOK_NAME)) { name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); skip_token (ctx_p); } else { name = empty_operand (); } state_p->u.expression.operand = jsp_start_parse_function_scope (ctx_p, name, true, NULL); } } else if (state_p->state == JSP_STATE_EXPR_DATA_PROP_DECL) { JERRY_ASSERT (!state_p->is_completed); if (is_subexpr_end) { JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t prop_name = state_p->u.expression.operand; jsp_operand_t value = substate_p->u.expression.operand; JERRY_ASSERT (prop_name.is_string_lit_operand ()); dump_prop_name_and_value (ctx_p, prop_name, value); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_add_prop_name (prop_name, PROP_DATA); } state_p->is_completed = true; JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ()); state_p->u.expression.operand = parse_property_name (); skip_token (ctx_p); JERRY_ASSERT (token_is (TOK_COLON)); skip_token (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); } } else if (state_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL) { JERRY_ASSERT (!state_p->is_completed); if (is_source_elements_list_end) { jsp_finish_parse_function_scope (ctx_p); jsp_operand_t prop_name = state_p->u.expression.u.accessor_prop_decl.prop_name; jsp_operand_t func = state_p->u.expression.operand; bool is_setter = state_p->u.expression.u.accessor_prop_decl.is_setter; if (is_setter) { dump_prop_setter_decl (ctx_p, prop_name, func); } else { dump_prop_getter_decl (ctx_p, prop_name, func); } state_p->is_completed = true; } else { bool is_setter; current_token_must_be (TOK_NAME); lit_cpointer_t lit_cp = token_data_as_lit_cp (); skip_token (ctx_p); if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "get")) { is_setter = false; } else if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "set")) { is_setter = true; } else { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid property declaration"); } jsp_operand_t prop_name = parse_property_name (); skip_token (ctx_p); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_add_prop_name (prop_name, is_setter ? PROP_SET : PROP_GET); } size_t formal_parameters_num; const jsp_operand_t func = jsp_start_parse_function_scope (ctx_p, empty_operand (), true, &formal_parameters_num); size_t req_num_of_formal_parameters = is_setter ? 1 : 0; if (req_num_of_formal_parameters != formal_parameters_num) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid number of formal parameters"); } JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ()); state_p->u.expression.operand = func; state_p->u.expression.u.accessor_prop_decl.prop_name = prop_name; state_p->u.expression.u.accessor_prop_decl.is_setter = is_setter; } } else if (state_p->state == JSP_STATE_EXPR_OBJECT_LITERAL) { JERRY_ASSERT (!state_p->is_completed); JERRY_ASSERT (state_p->is_list_in_process); if (is_subexpr_end) { JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_DATA_PROP_DECL || substate_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL); state_p->u.expression.u.varg_sequence.list_length++; dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); if (token_is (TOK_COMMA)) { skip_token (ctx_p); } else { current_token_must_be (TOK_CLOSE_BRACE); } JSP_FINISH_SUBEXPR (); } if (token_is (TOK_CLOSE_BRACE)) { if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_duplication_of_prop_names (jsp_is_strict_mode (ctx_p), tok.loc); } skip_token (ctx_p); uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos); dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); state_p->state = JSP_STATE_EXPR_MEMBER; state_p->is_list_in_process = false; } else { state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); locus start_pos = tok.loc; skip_token (ctx_p); jsp_state_expr_t expr_type; if (token_is (TOK_COLON)) { expr_type = JSP_STATE_EXPR_DATA_PROP_DECL; } else { expr_type = JSP_STATE_EXPR_ACCESSOR_PROP_DECL; } seek_token (ctx_p, start_pos); jsp_push_new_expr_state (ctx_p, expr_type, expr_type, true); } } else if (state_p->state == JSP_STATE_EXPR_ARRAY_LITERAL) { JERRY_ASSERT (!state_p->is_completed); JERRY_ASSERT (state_p->is_list_in_process); if (is_subexpr_end) { dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); dump_varg (ctx_p, substate_p->u.expression.operand); JSP_FINISH_SUBEXPR (); state_p->u.expression.u.varg_sequence.list_length++; dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); if (token_is (TOK_COMMA)) { skip_token (ctx_p); } else { current_token_must_be (TOK_CLOSE_SQUARE); } } else { if (token_is (TOK_CLOSE_SQUARE)) { skip_token (ctx_p); uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos); dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); state_p->state = JSP_STATE_EXPR_MEMBER; state_p->is_list_in_process = false; } else if (token_is (TOK_COMMA)) { while (token_is (TOK_COMMA)) { skip_token (ctx_p); vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p); jsp_operand_t reg = tmp_operand (); dump_variable_assignment (ctx_p, reg, jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_ARRAY_HOLE)); dump_varg (ctx_p, reg); state_p->u.expression.u.varg_sequence.list_length++; dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state); } } else { state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); } } } else if (state_p->state == JSP_STATE_EXPR_MEMBER) { if (state_p->is_completed) { if (token_is (TOK_OPEN_PAREN)) { state_p->state = JSP_STATE_EXPR_CALL; state_p->is_completed = false; /* propagate to CallExpression */ state_p->state = JSP_STATE_EXPR_CALL; } else { /* propagate to LeftHandSideExpression */ state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE; JERRY_ASSERT (state_p->is_completed); } } else { if (is_subexpr_end) { if (state_p->is_list_in_process) { JERRY_ASSERT (state_p->u.expression.token_type == TOK_KW_NEW); JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); dump_varg (ctx_p, substate_p->u.expression.operand); JSP_FINISH_SUBEXPR (); dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); state_p->u.expression.u.varg_sequence.list_length++; if (token_is (TOK_CLOSE_PAREN)) { state_p->u.expression.token_type = TOK_EMPTY; state_p->is_list_in_process = false; uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; jsp_finish_construct_dump (ctx_p, list_len, header_pos, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); } else { current_token_must_be (TOK_COMMA); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); } skip_token (ctx_p); } else if (state_p->u.expression.token_type == TOK_OPEN_PAREN) { JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ()); state_p->u.expression.operand = substate_p->u.expression.operand; state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand; state_p->is_value_based_reference = substate_p->is_value_based_reference; state_p->u.expression.token_type = TOK_EMPTY; JSP_FINISH_SUBEXPR (); current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); } else if (state_p->u.expression.token_type == TOK_KW_NEW) { JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_MEMBER); JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ()); JERRY_ASSERT (!substate_p->u.expression.operand.is_empty_operand ()); state_p->u.expression.operand = substate_p->u.expression.operand; state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand; state_p->is_value_based_reference = substate_p->is_value_based_reference; JSP_FINISH_SUBEXPR (); bool is_arg_list_implicit = true; bool is_arg_list_empty = true; if (token_is (TOK_OPEN_PAREN)) { skip_token (ctx_p); is_arg_list_implicit = false; if (token_is (TOK_CLOSE_PAREN)) { skip_token (ctx_p); } else { is_arg_list_empty = false; } } if (!is_arg_list_implicit && !is_arg_list_empty) { dump_get_value_for_prev_states (ctx_p, state_p); vm_idx_t reg_alloc_saved_state1; vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1); state_p->is_list_in_process = true; state_p->u.expression.u.varg_sequence.list_length = 0; state_p->u.expression.u.varg_sequence.header_pos = header_pos; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); } else { vm_idx_t reg_alloc_saved_state1; vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1); state_p->u.expression.token_type = TOK_EMPTY; if (is_arg_list_implicit) { state_p->state = JSP_STATE_EXPR_MEMBER; state_p->is_completed = true; } jsp_finish_construct_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1); state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE); state_p->u.expression.token_type = TOK_EMPTY; current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_SQUARE); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); state_p->u.expression.prop_name_operand = substate_p->u.expression.operand; state_p->is_value_based_reference = true; JSP_FINISH_SUBEXPR (); } } else if (token_is (TOK_OPEN_SQUARE)) { skip_token (ctx_p); state_p->u.expression.token_type = TOK_OPEN_SQUARE; dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } else if (token_is (TOK_DOT)) { skip_token (ctx_p); lit_cpointer_t prop_name = jsp_get_prop_name_after_dot (); skip_token (ctx_p); dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); state_p->u.expression.prop_name_operand = tmp_operand (); dump_variable_assignment (ctx_p, state_p->u.expression.prop_name_operand, jsp_operand_t::make_string_lit_operand (prop_name)); state_p->is_value_based_reference = true; } else { state_p->is_completed = true; } } } else if (state_p->state == JSP_STATE_EXPR_CALL) { JERRY_ASSERT (!state_p->is_completed); if (is_subexpr_end) { if (state_p->is_list_in_process) { JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); dump_varg (ctx_p, substate_p->u.expression.operand); JSP_FINISH_SUBEXPR (); dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2); state_p->u.expression.u.varg_sequence.list_length++; if (token_is (TOK_CLOSE_PAREN)) { state_p->is_list_in_process = false; uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length; vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos; jsp_finish_call_dump (ctx_p, list_len, header_pos, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1); state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else { current_token_must_be (TOK_COMMA); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); } } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE); state_p->u.expression.token_type = TOK_EMPTY; current_token_must_be (TOK_CLOSE_SQUARE); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); state_p->u.expression.prop_name_operand = substate_p->u.expression.operand; state_p->is_value_based_reference = true; JSP_FINISH_SUBEXPR (); } skip_token (ctx_p); } else { if (token_is (TOK_OPEN_PAREN)) { skip_token (ctx_p); dump_get_value_for_prev_states (ctx_p, state_p); vm_idx_t reg_alloc_saved_state1; vm_instr_counter_t header_pos = jsp_start_call_dump (ctx_p, state_p, ®_alloc_saved_state1); if (token_is (TOK_CLOSE_PAREN)) { skip_token (ctx_p); jsp_finish_call_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1); state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else { state_p->is_list_in_process = true; state_p->u.expression.u.varg_sequence.list_length = 0; state_p->u.expression.u.varg_sequence.header_pos = header_pos; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1; state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); } } else if (token_is (TOK_OPEN_SQUARE)) { skip_token (ctx_p); state_p->u.expression.token_type = TOK_OPEN_SQUARE; dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } else if (token_is (TOK_DOT)) { skip_token (ctx_p); lit_cpointer_t prop_name = jsp_get_prop_name_after_dot (); skip_token (ctx_p); dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); state_p->u.expression.prop_name_operand = tmp_operand (); dump_variable_assignment (ctx_p, state_p->u.expression.prop_name_operand, jsp_operand_t::make_string_lit_operand (prop_name)); state_p->is_value_based_reference = true; } else { state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE; state_p->is_completed = true; } } } else if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE) { JERRY_ASSERT (state_p->is_completed); if (is_subexpr_end) { jsp_token_type_t tt = state_p->u.expression.token_type; JERRY_ASSERT (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END); dump_get_value_for_prev_states (ctx_p, state_p); dump_get_value_if_ref (ctx_p, substate_p, true); if (tt == TOK_EQ) { if (state_p->is_need_retval) { dump_get_value_if_ref (ctx_p, substate_p, false); dump_get_value_for_state_if_const (ctx_p, substate_p); } if (state_p->is_value_based_reference) { dump_get_value_for_state_if_const (ctx_p, substate_p); dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, substate_p->u.expression.operand); state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else { dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); } state_p->u.expression.operand = substate_p->u.expression.operand; if (state_p->is_need_retval) { JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); } else { state_p->u.expression.operand = empty_operand (); } } else { vm_op_t opcode; if (tt == TOK_MULT_EQ) { opcode = VM_OP_MULTIPLICATION; } else if (tt == TOK_DIV_EQ) { opcode = VM_OP_DIVISION; } else if (tt == TOK_MOD_EQ) { opcode = VM_OP_REMAINDER; } else if (tt == TOK_PLUS_EQ) { opcode = VM_OP_ADDITION; } else if (tt == TOK_MINUS_EQ) { opcode = VM_OP_SUBSTRACTION; } else if (tt == TOK_LSHIFT_EQ) { opcode = VM_OP_B_SHIFT_LEFT; } else if (tt == TOK_RSHIFT_EQ) { opcode = VM_OP_B_SHIFT_RIGHT; } else if (tt == TOK_RSHIFT_EX_EQ) { opcode = VM_OP_B_SHIFT_URIGHT; } else if (tt == TOK_AND_EQ) { opcode = VM_OP_B_AND; } else if (tt == TOK_XOR_EQ) { opcode = VM_OP_B_XOR; } else { JERRY_ASSERT (tt == TOK_OR_EQ); opcode = VM_OP_B_OR; } dump_get_value_for_state_if_const (ctx_p, substate_p); if (state_p->is_value_based_reference) { jsp_operand_t reg = tmp_operand (); dump_prop_getter (ctx_p, reg, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); dump_binary_op (ctx_p, opcode, reg, reg, substate_p->u.expression.operand); dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, reg); state_p->u.expression.operand = reg; state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else if (state_p->is_need_retval) { jsp_operand_t reg = tmp_operand (); dump_binary_op (ctx_p, opcode, reg, state_p->u.expression.operand, substate_p->u.expression.operand); dump_variable_assignment (ctx_p, state_p->u.expression.operand, reg); state_p->u.expression.operand = reg; } else { dump_binary_op (ctx_p, opcode, state_p->u.expression.operand, state_p->u.expression.operand, substate_p->u.expression.operand); state_p->u.expression.operand = empty_operand (); } } state_p->state = JSP_STATE_EXPR_ASSIGNMENT; state_p->u.expression.token_type = TOK_EMPTY; state_p->is_completed = true; JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); if (token_is (TOK_DOUBLE_PLUS) && !lexer_is_preceded_by_newlines (tok)) { const jsp_operand_t reg = tmp_operand (); if (state_p->is_value_based_reference) { const jsp_operand_t val = tmp_operand (); dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, val); dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val); state_p->u.expression.operand = reg; state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else if (state_p->u.expression.operand.is_identifier_operand ()) { if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, jsp_is_strict_mode (ctx_p), tok.loc); } dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, state_p->u.expression.operand); state_p->u.expression.operand = reg; } else { PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); } state_p->state = JSP_STATE_EXPR_UNARY; JERRY_ASSERT (state_p->is_completed); skip_token (ctx_p); } else if (token_is (TOK_DOUBLE_MINUS) && !lexer_is_preceded_by_newlines (tok)) { const jsp_operand_t reg = tmp_operand (); if (state_p->is_value_based_reference) { const jsp_operand_t val = tmp_operand (); dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand); dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, val); dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val); state_p->u.expression.operand = reg; state_p->u.expression.prop_name_operand = empty_operand (); state_p->is_value_based_reference = false; } else if (state_p->u.expression.operand.is_identifier_operand ()) { if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, jsp_is_strict_mode (ctx_p), tok.loc); } dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, state_p->u.expression.operand); state_p->u.expression.operand = reg; } else { PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); } state_p->state = JSP_STATE_EXPR_UNARY; JERRY_ASSERT (state_p->is_completed); skip_token (ctx_p); } else { jsp_token_type_t tt = lexer_get_token_type (tok); if (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END) { if (!state_p->is_value_based_reference) { if (!jsp_is_dump_mode (ctx_p)) { if (!state_p->u.expression.operand.is_identifier_operand ()) { PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc); } else { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand, jsp_is_strict_mode (ctx_p), tok.loc); } } } /* skip the assignment operator */ skip_token (ctx_p); state_p->u.expression.token_type = tt; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); } else { state_p->state = JSP_STATE_EXPR_UNARY; } } } } else if (state_p->state > JSP_STATE_EXPR__SIMPLE_BEGIN && state_p->state < JSP_STATE_EXPR__SIMPLE_END) { JERRY_STATIC_ASSERT (JSP_STATE_EXPR_MULTIPLICATIVE == JSP_STATE_EXPR_UNARY + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_ADDITIVE == JSP_STATE_EXPR_MULTIPLICATIVE + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_SHIFT == JSP_STATE_EXPR_ADDITIVE + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_RELATIONAL == JSP_STATE_EXPR_SHIFT + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_EQUALITY == JSP_STATE_EXPR_RELATIONAL + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_AND == JSP_STATE_EXPR_EQUALITY + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_XOR == JSP_STATE_EXPR_BITWISE_AND + 1u); JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_OR == JSP_STATE_EXPR_BITWISE_XOR + 1u); if (state_p->is_completed) { if (state_p->state == JSP_STATE_EXPR_BITWISE_OR) { state_p->state = JSP_STATE_EXPR_LOGICAL_AND; state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; } else { JERRY_ASSERT (state_p->state != state_p->req_state); JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_UNARY); state_p->state = (jsp_state_expr_t) (state_p->state + 1u); } state_p->is_completed = false; } else if (is_subexpr_end) { bool is_combined_with_assignment; if (state_p->state == JSP_STATE_EXPR_UNARY) { is_combined_with_assignment = jsp_dump_unary_op (ctx_p, state_p, substate_p); } else { is_combined_with_assignment = jsp_dump_binary_op (ctx_p, state_p, substate_p); } JSP_FINISH_SUBEXPR (); if (is_combined_with_assignment) { JSP_ASSIGNMENT_EXPR_COMBINE (); } } else { JERRY_ASSERT (!state_p->is_completed); jsp_state_expr_t new_state, subexpr_type; bool is_simple = true; jsp_token_type_t tt = lexer_get_token_type (tok); if (tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN && tt <= TOKEN_TYPE__MULTIPLICATIVE_END) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_MULTIPLICATIVE); new_state = JSP_STATE_EXPR_MULTIPLICATIVE; subexpr_type = JSP_STATE_EXPR_UNARY; } else if (tt >= TOKEN_TYPE__ADDITIVE_BEGIN && tt <= TOKEN_TYPE__ADDITIVE_END) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_ADDITIVE); new_state = JSP_STATE_EXPR_ADDITIVE; subexpr_type = JSP_STATE_EXPR_MULTIPLICATIVE; } else if (tt >= TOKEN_TYPE__SHIFT_BEGIN && tt <= TOKEN_TYPE__SHIFT_END) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_SHIFT); new_state = JSP_STATE_EXPR_SHIFT; subexpr_type = JSP_STATE_EXPR_ADDITIVE; } else if ((tt >= TOKEN_TYPE__RELATIONAL_BEGIN && tt <= TOKEN_TYPE__RELATIONAL_END) || tt == TOK_KW_INSTANCEOF || (in_allowed && tt == TOK_KW_IN)) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_RELATIONAL); new_state = JSP_STATE_EXPR_RELATIONAL; subexpr_type = JSP_STATE_EXPR_SHIFT; } else if (tt >= TOKEN_TYPE__EQUALITY_BEGIN && tt <= TOKEN_TYPE__EQUALITY_END) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_EQUALITY); new_state = JSP_STATE_EXPR_EQUALITY; subexpr_type = JSP_STATE_EXPR_RELATIONAL; } else if (tt == TOK_AND) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_AND); new_state = JSP_STATE_EXPR_BITWISE_AND; subexpr_type = JSP_STATE_EXPR_EQUALITY; } else if (tt == TOK_XOR) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_XOR); new_state = JSP_STATE_EXPR_BITWISE_XOR; subexpr_type = JSP_STATE_EXPR_BITWISE_AND; } else if (tt == TOK_OR) { JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_OR); new_state = JSP_STATE_EXPR_BITWISE_OR; subexpr_type = JSP_STATE_EXPR_BITWISE_XOR; } else { is_simple = false; } if (!is_simple && state_p->req_state >= JSP_STATE_EXPR_LOGICAL_AND) { state_p->state = JSP_STATE_EXPR_LOGICAL_AND; state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; } else { JERRY_ASSERT (is_simple || state_p->req_state < JSP_STATE_EXPR__SIMPLE_END); if (!is_simple || state_p->req_state < new_state) { state_p->state = state_p->req_state; state_p->is_completed = true; } else { skip_token (ctx_p); state_p->state = new_state; state_p->u.expression.token_type = tt; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, subexpr_type, in_allowed); } } } } else if (state_p->state == JSP_STATE_EXPR_LOGICAL_AND) { if (state_p->is_completed) { /* propagate to LogicalOrExpression */ state_p->state = JSP_STATE_EXPR_LOGICAL_OR; state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES; state_p->is_completed = false; } else { if (is_subexpr_end) { dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_if_ref (ctx_p, substate_p, true); JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_AND); JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); state_p->u.expression.token_type = TOK_EMPTY; JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); if (token_is (TOK_DOUBLE_AND)) { /* ECMA-262 v5, 11.11 (complex LogicalAndExpression) */ skip_token (ctx_p); /* * FIXME: * Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand * management for expressions */ if (!state_p->is_complex_production) { state_p->is_complex_production = true; state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES; JERRY_ASSERT (!state_p->is_fixed_ret_operand); jsp_operand_t ret = tmp_operand (); dump_get_value_if_ref (ctx_p, state_p, true); dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand); state_p->is_fixed_ret_operand = true; state_p->u.expression.operand = ret; } JERRY_ASSERT (state_p->is_complex_production); jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain, true, state_p->u.expression.operand); state_p->u.expression.token_type = TOK_DOUBLE_AND; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_BITWISE_OR, in_allowed); } else { /* end of LogicalAndExpression */ JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain, dumper_get_current_instr_counter (ctx_p)); state_p->is_complex_production = false; state_p->is_completed = true; } } } } else if (state_p->state == JSP_STATE_EXPR_LOGICAL_OR) { if (state_p->is_completed) { /* switching to ConditionalExpression */ if (token_is (TOK_QUERY)) { state_p->state = JSP_STATE_EXPR_CONDITION; state_p->is_completed = false; /* ECMA-262 v5, 11.12 */ skip_token (ctx_p); dump_get_value_if_ref (ctx_p, state_p, true); dump_get_value_for_state_if_const (ctx_p, state_p); vm_instr_counter_t conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p, state_p->u.expression.operand); state_p->u.expression.u.conditional.conditional_check_pos = conditional_check_pos; state_p->u.expression.token_type = TOK_QUERY; if (!state_p->is_fixed_ret_operand) { state_p->is_fixed_ret_operand = true; state_p->u.expression.operand = tmp_operand (); } jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); } else { /* just propagate */ state_p->state = JSP_STATE_EXPR_ASSIGNMENT; JERRY_ASSERT (state_p->is_completed); } } else { if (is_subexpr_end) { dump_get_value_if_ref (ctx_p, substate_p, true); JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_OR); JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); state_p->u.expression.token_type = TOK_EMPTY; JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); if (token_is (TOK_DOUBLE_OR)) { /* ECMA-262 v5, 11.11 (complex LogicalOrExpression) */ skip_token (ctx_p); /* * FIXME: * Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand * management for expressions */ if (!state_p->is_complex_production) { state_p->is_complex_production = true; state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES; jsp_operand_t ret; if (state_p->is_fixed_ret_operand) { JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); ret = state_p->u.expression.operand; } else { ret = tmp_operand (); dump_get_value_if_ref (ctx_p, state_p, true); dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand); state_p->is_fixed_ret_operand = true; state_p->u.expression.operand = ret; } } JERRY_ASSERT (state_p->is_complex_production); jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain, false, state_p->u.expression.operand); state_p->u.expression.token_type = TOK_DOUBLE_OR; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LOGICAL_AND, in_allowed); } else { /* end of LogicalOrExpression */ JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain, dumper_get_current_instr_counter (ctx_p)); state_p->is_complex_production = false; state_p->is_completed = true; } } } } else if (state_p->state == JSP_STATE_EXPR_ASSIGNMENT) { /* assignment production can't be continued at the point */ JERRY_ASSERT (!is_subexpr_end); JERRY_ASSERT (state_p->is_completed); JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY); /* 'assignment expression' production can't be continued with an operator, * so just propagating it to 'expression' production */ state_p->is_completed = false; state_p->state = JSP_STATE_EXPR_EXPRESSION; } else if (state_p->state == JSP_STATE_EXPR_CONDITION) { JERRY_ASSERT (!state_p->is_completed); JERRY_ASSERT (is_subexpr_end); /* ECMA-262 v5, 11.12 */ if (state_p->u.expression.token_type == TOK_QUERY) { current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); JERRY_ASSERT (state_p->is_fixed_ret_operand); JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); dump_get_value_if_ref (ctx_p, substate_p, true); dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); JSP_FINISH_SUBEXPR (); state_p->u.expression.u.conditional.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); rewrite_conditional_check (ctx_p, state_p->u.expression.u.conditional.conditional_check_pos); state_p->u.expression.token_type = TOK_COLON; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); } else { JERRY_ASSERT (state_p->u.expression.token_type == TOK_COLON); JERRY_ASSERT (state_p->is_fixed_ret_operand); JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ()); JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT); dump_get_value_if_ref (ctx_p, substate_p, true); dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand); JSP_FINISH_SUBEXPR (); rewrite_jump_to_end (ctx_p, state_p->u.expression.u.conditional.jump_to_end_pos); state_p->u.expression.token_type = TOK_EMPTY; state_p->is_fixed_ret_operand = false; state_p->state = JSP_STATE_EXPR_ASSIGNMENT; state_p->is_completed = true; } } else if (state_p->state == JSP_STATE_EXPR_EXPRESSION) { /* ECMA-262 v5, 11.14 */ if (is_subexpr_end) { JERRY_ASSERT (state_p->u.expression.token_type == TOK_COMMA); dump_get_value_if_ref (ctx_p, substate_p, false); if (state_p->is_need_retval) { /* * The operand should be already evaluated * * See also: * 11.14, step 2 */ JERRY_ASSERT (!state_p->is_value_based_reference && !state_p->u.expression.operand.is_identifier_operand ()); state_p->u.expression.operand = substate_p->u.expression.operand; } JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (!state_p->is_completed); if (token_is (TOK_COMMA)) { skip_token (ctx_p); JERRY_ASSERT (!token_is (TOK_COMMA)); state_p->u.expression.token_type = TOK_COMMA; /* ECMA-262 v5, 11.14, step 2 */ dump_get_value_if_ref (ctx_p, state_p, false); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed); } else { if (state_p->is_need_retval) { if (!state_p->is_value_based_reference) { dump_get_value_for_state_if_const (ctx_p, state_p); } } state_p->is_completed = true; } } } else if (state_p->state == JSP_STATE_STAT_EMPTY) { dumper_new_statement (ctx_p); if (token_is (TOK_KW_IF)) /* IfStatement */ { skip_token (ctx_p); parse_expression_inside_parens_begin (ctx_p); state_p->state = JSP_STATE_STAT_IF_BRANCH_START; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, in_allowed); } else if (token_is (TOK_OPEN_BRACE)) { skip_token (ctx_p); state_p->state = JSP_STATE_STAT_BLOCK; jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } else if (token_is (TOK_KW_VAR)) { state_p->state = JSP_STATE_STAT_VAR_DECL; state_p->var_decl = true; } else if (token_is (TOK_KW_DO) || token_is (TOK_KW_WHILE) || token_is (TOK_KW_FOR)) { state_p->u.statement.u.iterational.continues_rewrite_chain = MAX_OPCODES; state_p->u.statement.u.iterational.continue_tgt_oc = MAX_OPCODES; if (token_is (TOK_KW_DO)) { vm_instr_counter_t next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos = next_iter_tgt_pos; skip_token (ctx_p); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_DO_WHILE); } else if (token_is (TOK_KW_WHILE)) { skip_token (ctx_p); state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc = tok.loc; jsp_skip_braces (ctx_p, TOK_OPEN_PAREN); state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); skip_token (ctx_p); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WHILE); } else { current_token_must_be_check_and_skip_it (ctx_p, TOK_KW_FOR); current_token_must_be (TOK_OPEN_PAREN); locus for_open_paren_loc, for_body_statement_loc; for_open_paren_loc = tok.loc; jsp_skip_braces (ctx_p, TOK_OPEN_PAREN); skip_token (ctx_p); for_body_statement_loc = tok.loc; seek_token (ctx_p, for_open_paren_loc); bool is_plain_for = jsp_find_next_token_before_the_locus (ctx_p, TOK_SEMICOLON, for_body_statement_loc, true); seek_token (ctx_p, for_open_paren_loc); if (is_plain_for) { state_p->u.statement.u.iterational.u.loop_for.u1.body_loc = for_body_statement_loc; current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); // Initializer if (token_is (TOK_KW_VAR)) { state_p->state = JSP_STATE_STAT_FOR_INIT_END; jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_VAR_DECL); } else { if (!token_is (TOK_SEMICOLON)) { jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, false); jsp_state_top (ctx_p)->is_need_retval = false; } else { // Initializer is empty } state_p->state = JSP_STATE_STAT_FOR_INIT_END; } } else { current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); // Save Iterator location state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc = tok.loc; while (lit_utf8_iterator_pos_cmp (tok.loc, for_body_statement_loc) < 0) { if (jsp_find_next_token_before_the_locus (ctx_p, TOK_KW_IN, for_body_statement_loc, true)) { break; } else { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement"); } } JERRY_ASSERT (token_is (TOK_KW_IN)); skip_token (ctx_p); // Collection jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); state_p->state = JSP_STATE_STAT_FOR_IN; } } } else if (token_is (TOK_KW_SWITCH)) { skip_token (ctx_p); parse_expression_inside_parens_begin (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); state_p->state = JSP_STATE_STAT_SWITCH; } else if (token_is (TOK_SEMICOLON)) { skip_token (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } else if (token_is (TOK_KW_CONTINUE) || token_is (TOK_KW_BREAK)) { bool is_break = token_is (TOK_KW_BREAK); skip_token (ctx_p); jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) { prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; } jsp_state_t *labelled_stmt_p; bool is_simply_jumpable = true; if (!lexer_is_preceded_by_newlines (tok) && token_is (TOK_NAME)) { /* break or continue on a label */ labelled_stmt_p = jsp_find_named_label (ctx_p, token_data_as_lit_cp (), &is_simply_jumpable); if (labelled_stmt_p == NULL) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label not found"); } skip_token (ctx_p); } else { labelled_stmt_p = jsp_find_unnamed_label (ctx_p, is_break, &is_simply_jumpable); if (labelled_stmt_p == NULL) { if (is_break) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the break"); } else { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the continue"); } } } insert_semicolon (ctx_p); JERRY_ASSERT (labelled_stmt_p != NULL); vm_instr_counter_t *rewrite_chain_p; if (is_break) { rewrite_chain_p = &labelled_stmt_p->u.statement.breaks_rewrite_chain; } else { rewrite_chain_p = &labelled_stmt_p->u.statement.u.iterational.continues_rewrite_chain; } if (is_simply_jumpable) { jsp_add_simple_jump_to_rewrite_chain (ctx_p, rewrite_chain_p); } else { jsp_add_nested_jump_to_rewrite_chain (ctx_p, rewrite_chain_p); } JSP_COMPLETE_STATEMENT_PARSE (); } else if (token_is (TOK_KW_RETURN)) { if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_FUNCTION) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Return is illegal"); } jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) { prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; } skip_token (ctx_p); if (!token_is (TOK_SEMICOLON) && !lexer_is_preceded_by_newlines (tok) && !token_is (TOK_CLOSE_BRACE)) { jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); state_p->state = JSP_STATE_STAT_RETURN; } else { dump_ret (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } } else if (token_is (TOK_KW_TRY)) { skip_token (ctx_p); if (!jsp_is_dump_mode (ctx_p)) { scopes_tree_set_contains_try (jsp_get_current_scopes_tree_node (ctx_p)); } state_p->u.statement.u.try_statement.try_pos = dump_try_for_rewrite (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); state_p->is_simply_jumpable_border = true; state_p->state = JSP_STATE_STAT_TRY; jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } else if (token_is (TOK_KW_WITH)) { skip_token (ctx_p); if (jsp_is_strict_mode (ctx_p)) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "'with' expression is not allowed in strict mode."); } parse_expression_inside_parens_begin (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); state_p->state = JSP_STATE_STAT_WITH; } else if (token_is (TOK_KW_THROW)) { skip_token (ctx_p); jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) { prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true; } jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); state_p->state = JSP_STATE_STAT_THROW; } else if (token_is (TOK_KW_FUNCTION)) { skip_token (ctx_p); current_token_must_be (TOK_NAME); const jsp_operand_t func_name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); skip_token (ctx_p); state_p->state = JSP_STATE_FUNC_DECL_FINISH; jsp_start_parse_function_scope (ctx_p, func_name, false, NULL); } else { bool is_expression = true; if (token_is (TOK_NAME)) // LabelledStatement or ExpressionStatement { const token temp = tok; skip_token (ctx_p); if (token_is (TOK_COLON)) { skip_token (ctx_p); lit_cpointer_t name_cp; name_cp.packed_value = temp.uid; bool is_simply_jumpable; jsp_state_t *named_label_state_p = jsp_find_named_label (ctx_p, name_cp, &is_simply_jumpable); if (named_label_state_p != NULL) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label is duplicated"); } state_p->state = JSP_STATE_STAT_NAMED_LABEL; state_p->u.named_label.name_cp = name_cp; jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); is_expression = false; } else { seek_token (ctx_p, temp.loc); } } if (is_expression) { jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_EVAL) { jsp_state_top (ctx_p)->is_need_retval = false; } state_p->state = JSP_STATE_STAT_EXPRESSION; } } } else if (state_p->state == JSP_STATE_STAT_BLOCK) { JERRY_ASSERT (is_stmt_list_end); jsp_state_t *prev_state_p = jsp_get_prev_state (state_p); if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST) { prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = is_stmt_list_control_flow_exit_stmt_occured; } current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_START) { if (is_subexpr_end) // Finished parsing condition { parse_expression_inside_parens_end (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t cond = substate_p->u.expression.operand; state_p->u.statement.u.if_statement.conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p, cond); JSP_FINISH_SUBEXPR (); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_START); } else { if (token_is (TOK_KW_ELSE)) { skip_token (ctx_p); state_p->u.statement.u.if_statement.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_END); } else { rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos); JSP_COMPLETE_STATEMENT_PARSE (); } } } else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_END) { rewrite_jump_to_end (ctx_p, state_p->u.statement.u.if_statement.jump_to_end_pos); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_STATEMENT_LIST) { if (is_subexpr_end) { JSP_FINISH_SUBEXPR (); } while (token_is (TOK_SEMICOLON)) { skip_token (ctx_p); } if (token_is (TOK_CLOSE_BRACE) || (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT))) { state_p->is_completed = true; } else { jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); } } else if (state_p->state == JSP_STATE_STAT_VAR_DECL) { skip_token (ctx_p); locus name_loc = tok.loc; current_token_must_be (TOK_NAME); const lit_cpointer_t lit_cp = token_data_as_lit_cp (); const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (lit_cp); skip_token (ctx_p); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc); } if (!jsp_is_dump_mode (ctx_p)) { if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp)) { dump_variable_declaration (ctx_p, lit_cp); } } if (token_is (TOK_EQ)) { seek_token (ctx_p, name_loc); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true); jsp_state_top (ctx_p)->is_need_retval = false; } state_p->state = JSP_STATE_STAT_VAR_DECL_FINISH; } else if (state_p->state == JSP_STATE_STAT_VAR_DECL_FINISH) { if (is_subexpr_end) { JSP_FINISH_SUBEXPR (); } if (!token_is (TOK_COMMA)) { JSP_COMPLETE_STATEMENT_PARSE (); if (state_p->var_decl) { insert_semicolon (ctx_p); } } else { state_p->state = JSP_STATE_STAT_VAR_DECL; } } else if (state_p->state == JSP_STATE_STAT_DO_WHILE) { if (is_subexpr_end) { parse_expression_inside_parens_end (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); const jsp_operand_t cond = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos, cond); state_p->state = JSP_STATE_STAT_ITER_FINISH; } else { assert_keyword (TOK_KW_WHILE); skip_token (ctx_p); JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); parse_expression_inside_parens_begin (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } } else if (state_p->state == JSP_STATE_STAT_WHILE) { if (is_subexpr_end) { parse_expression_inside_parens_end (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); const jsp_operand_t cond = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos, cond); seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.end_loc); state_p->state = JSP_STATE_STAT_ITER_FINISH; } else { JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos); const locus end_loc = tok.loc; seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc); state_p->u.statement.u.iterational.u.loop_while.u.end_loc = end_loc; parse_expression_inside_parens_begin (ctx_p); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } } else if (state_p->state == JSP_STATE_STAT_FOR_INIT_END) { if (is_subexpr_end) { JSP_FINISH_SUBEXPR (); } // Jump -> ConditionCheck state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p); state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON); locus for_body_statement_loc = state_p->u.statement.u.iterational.u.loop_for.u1.body_loc; // Save Condition locus state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc = tok.loc; if (!jsp_find_next_token_before_the_locus (ctx_p, TOK_SEMICOLON, for_body_statement_loc, true)) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement"); } current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON); // Save Increment locus state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc = tok.loc; // Body seek_token (ctx_p, for_body_statement_loc); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_INCREMENT); } else if (state_p->state == JSP_STATE_STAT_FOR_INCREMENT) { if (is_subexpr_end) { JSP_FINISH_SUBEXPR (); state_p->state = JSP_STATE_STAT_FOR_COND; } else { // Save LoopEnd locus const locus loop_end_loc = tok.loc; // Setup ContinueTarget JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); // Increment seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc); state_p->u.statement.u.iterational.u.loop_for.u2.end_loc = loop_end_loc; if (!token_is (TOK_CLOSE_PAREN)) { jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); jsp_state_top (ctx_p)->is_need_retval = false; } else { state_p->state = JSP_STATE_STAT_FOR_COND; } } } else if (state_p->state == JSP_STATE_STAT_FOR_COND) { if (is_subexpr_end) { dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t cond = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos, cond); state_p->state = JSP_STATE_STAT_FOR_FINISH; } else { current_token_must_be (TOK_CLOSE_PAREN); // Setup ConditionCheck rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos); // Condition seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc); if (token_is (TOK_SEMICOLON)) { dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos, empty_operand ()); state_p->state = JSP_STATE_STAT_FOR_FINISH; } else { jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } } } else if (state_p->state == JSP_STATE_STAT_FOR_FINISH) { seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.end_loc); state_p->state = JSP_STATE_STAT_ITER_FINISH; } else if (state_p->state == JSP_STATE_STAT_FOR_IN) { current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); JERRY_ASSERT (is_subexpr_end); locus body_loc = tok.loc; // Dump for-in instruction dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t collection = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); state_p->u.statement.u.iterational.u.loop_for_in.header_pos = dump_for_in_for_rewrite (ctx_p, collection); // Dump assignment VariableDeclarationNoIn / LeftHandSideExpression <- VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc); if (token_is (TOK_KW_VAR)) { skip_token (ctx_p); locus name_loc = tok.loc; current_token_must_be (TOK_NAME); const lit_cpointer_t lit_cp = token_data_as_lit_cp (); const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (lit_cp); skip_token (ctx_p); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc); if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp)) { dump_variable_declaration (ctx_p, lit_cp); } } if (token_is (TOK_EQ)) { seek_token (ctx_p, name_loc); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, false); } state_p->is_var_decl_no_in = true; state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = lit_cp; } else { state_p->is_var_decl_no_in = false; state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = NOT_A_LITERAL; jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LEFTHANDSIDE, true); } // Body state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc = body_loc; state_p->state = JSP_STATE_STAT_FOR_IN_EXPR; } else if (state_p->state == JSP_STATE_STAT_FOR_IN_EXPR) { current_token_must_be (TOK_KW_IN); seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc); jsp_operand_t for_in_special_reg = jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME); if (!state_p->is_var_decl_no_in) { JERRY_ASSERT (is_subexpr_end); if (substate_p->is_value_based_reference) { dump_prop_setter (ctx_p, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand, for_in_special_reg); } else { dump_variable_assignment (ctx_p, substate_p->u.expression.operand, for_in_special_reg); } JSP_FINISH_SUBEXPR (); } else { JERRY_ASSERT (!is_subexpr_end); lit_cpointer_t var_name_lit_cp = state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp; dump_variable_assignment (ctx_p, jsp_operand_t::make_identifier_operand (var_name_lit_cp), for_in_special_reg); } state_p->is_simply_jumpable_border = true; JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_IN_FINISH); } else if (state_p->state == JSP_STATE_STAT_FOR_IN_FINISH) { // Save LoopEnd locus const locus loop_end_loc = tok.loc; // Setup ContinueTarget JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES); state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p); // Write position of for-in end to for_in instruction rewrite_for_in (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.header_pos); // Dump meta (OPCODE_META_TYPE_END_FOR_IN) dump_for_in_end (ctx_p); seek_token (ctx_p, loop_end_loc); state_p->state = JSP_STATE_STAT_ITER_FINISH; } else if (state_p->state == JSP_STATE_STAT_ITER_FINISH) { JSP_COMPLETE_STATEMENT_PARSE (); jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.u.iterational.continues_rewrite_chain, state_p->u.statement.u.iterational.continue_tgt_oc); } else if (state_p->state == JSP_STATE_STAT_SWITCH) { JERRY_ASSERT (is_subexpr_end); parse_expression_inside_parens_end (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, false); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t switch_expr = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); state_p->state = JSP_STATE_STAT_SWITCH_BRANCH_EXPR; state_p->u.statement.u.switch_statement.expr = switch_expr; state_p->u.statement.u.switch_statement.default_label_oc = MAX_OPCODES; state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc = MAX_OPCODES; state_p->u.statement.u.switch_statement.skip_check_jmp_oc = MAX_OPCODES; dumper_save_reg_alloc_ctx (ctx_p, &state_p->u.statement.u.switch_statement.saved_reg_next, &state_p->u.statement.u.switch_statement.saved_reg_max_for_temps); } else if (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR) { if (is_subexpr_end) { /* finished parse of an Expression in CaseClause */ dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); jsp_operand_t case_expr = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); jsp_operand_t switch_expr = state_p->u.statement.u.switch_statement.expr; jsp_operand_t condition_reg; if (case_expr.is_register_operand ()) { /* reuse the register */ condition_reg = case_expr; } else { condition_reg = tmp_operand (); } dump_binary_op (ctx_p, VM_OP_EQUAL_VALUE_TYPE, condition_reg, switch_expr, case_expr); jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, true, condition_reg); uint32_t num = jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.u.switch_statement.skip_check_jmp_oc, dumper_get_current_instr_counter (ctx_p)); JERRY_ASSERT (num <= 1); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } else if (token_is (TOK_CLOSE_BRACE)) { skip_token (ctx_p); vm_instr_counter_t last_cond_check_tgt_oc; if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES) { last_cond_check_tgt_oc = state_p->u.statement.u.switch_statement.default_label_oc; } else { last_cond_check_tgt_oc = dumper_get_current_instr_counter (ctx_p); } uint32_t num = jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, last_cond_check_tgt_oc); JERRY_ASSERT (num <= 1); JSP_COMPLETE_STATEMENT_PARSE (); } else { if (is_stmt_list_end && !is_stmt_list_control_flow_exit_stmt_occured && token_is (TOK_KW_CASE)) { jsp_add_simple_jump_to_rewrite_chain (ctx_p, &state_p->u.statement.u.switch_statement.skip_check_jmp_oc); } if (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT)) { if (!is_stmt_list_end /* no StatementList[opt] occured in the SwitchStatement yet, * so no conditions were checked for now and the DefaultClause * should be jumped over */ && token_is (TOK_KW_DEFAULT)) { /* first clause is DefaultClause */ JERRY_ASSERT (state_p->u.statement.u.switch_statement.default_label_oc == MAX_OPCODES); /* the check is unconditional as it is jump over DefaultClause */ jsp_add_simple_jump_to_rewrite_chain (ctx_p, &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc); } if (token_is (TOK_KW_CASE)) { skip_token (ctx_p); uint32_t num = jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc, dumper_get_current_instr_counter (ctx_p)); JERRY_ASSERT (num <= 1); dumper_restore_reg_alloc_ctx (ctx_p, state_p->u.statement.u.switch_statement.saved_reg_next, state_p->u.statement.u.switch_statement.saved_reg_max_for_temps, false); jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true); } else { JERRY_ASSERT (token_is (TOK_KW_DEFAULT)); skip_token (ctx_p); if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Duplication of 'default' clause"); } state_p->u.statement.u.switch_statement.default_label_oc = dumper_get_current_instr_counter (ctx_p); uint32_t num = jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.u.switch_statement.skip_check_jmp_oc, dumper_get_current_instr_counter (ctx_p)); JERRY_ASSERT (num <= 1); current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } } else { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected 'case' or' 'default' clause"); } } } else if (state_p->state == JSP_STATE_STAT_TRY) { rewrite_try (ctx_p, state_p->u.statement.u.try_statement.try_pos); if (!token_is (TOK_KW_CATCH) && !token_is (TOK_KW_FINALLY)) { EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either 'catch' or 'finally' token"); } if (token_is (TOK_KW_CATCH)) { skip_token (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN); current_token_must_be (TOK_NAME); const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ()); if (!jsp_is_dump_mode (ctx_p)) { jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), tok.loc); } skip_token (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN); state_p->u.statement.u.try_statement.catch_pos = dump_catch_for_rewrite (ctx_p, name); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); state_p->state = JSP_STATE_STAT_CATCH_FINISH; JERRY_ASSERT (state_p->is_simply_jumpable_border); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } else { JERRY_ASSERT (token_is (TOK_KW_FINALLY)); skip_token (ctx_p); state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); state_p->state = JSP_STATE_STAT_FINALLY_FINISH; JERRY_ASSERT (state_p->is_simply_jumpable_border); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } } else if (state_p->state == JSP_STATE_STAT_CATCH_FINISH) { rewrite_catch (ctx_p, state_p->u.statement.u.try_statement.catch_pos); if (token_is (TOK_KW_FINALLY)) { skip_token (ctx_p); state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p); current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE); state_p->state = JSP_STATE_STAT_FINALLY_FINISH; JERRY_ASSERT (state_p->is_simply_jumpable_border); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK); jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST); jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST; } else { state_p->state = JSP_STATE_STAT_TRY_FINISH; } } else if (state_p->state == JSP_STATE_STAT_FINALLY_FINISH) { rewrite_finally (ctx_p, state_p->u.statement.u.try_statement.finally_pos); state_p->state = JSP_STATE_STAT_TRY_FINISH; } else if (state_p->state == JSP_STATE_STAT_TRY_FINISH) { dump_end_try_catch_finally (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_WITH) { if (is_subexpr_end) { parse_expression_inside_parens_end (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); const jsp_operand_t expr = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); if (!jsp_is_dump_mode (ctx_p)) { scopes_tree_set_contains_with (jsp_get_current_scopes_tree_node (ctx_p)); } state_p->is_simply_jumpable_border = true; state_p->u.statement.u.with_statement.header_pos = dump_with_for_rewrite (ctx_p, expr); JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WITH); } else { rewrite_with (ctx_p, state_p->u.statement.u.with_statement.header_pos); dump_with_end (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } } else if (state_p->state == JSP_STATE_FUNC_DECL_FINISH) { JERRY_ASSERT (is_source_elements_list_end); jsp_finish_parse_function_scope (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_NAMED_LABEL) { jsp_state_pop (ctx_p); } else if (state_p->state == JSP_STATE_STAT_EXPRESSION) { JERRY_ASSERT (is_subexpr_end); insert_semicolon (ctx_p); dump_get_value_if_ref (ctx_p, substate_p, false); if (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_EVAL) { JERRY_ASSERT (substate_p->is_need_retval); dump_variable_assignment (ctx_p, jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET), substate_p->u.expression.operand); } JSP_FINISH_SUBEXPR (); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_RETURN) { JERRY_ASSERT (is_subexpr_end); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); const jsp_operand_t op = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); dump_retval (ctx_p, op); insert_semicolon (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } else if (state_p->state == JSP_STATE_STAT_THROW) { JERRY_ASSERT (is_subexpr_end); dump_get_value_if_ref (ctx_p, substate_p, true); dump_get_value_for_state_if_const (ctx_p, substate_p); const jsp_operand_t op = substate_p->u.expression.operand; JSP_FINISH_SUBEXPR (); dump_throw (ctx_p, op); insert_semicolon (ctx_p); JSP_COMPLETE_STATEMENT_PARSE (); } else { JERRY_ASSERT (state_p->state == JSP_STATE_STAT_STATEMENT); JERRY_ASSERT (!state_p->is_completed); vm_instr_counter_t break_tgt_oc = dumper_get_current_instr_counter (ctx_p); jsp_rewrite_jumps_chain (ctx_p, &state_p->u.statement.breaks_rewrite_chain, break_tgt_oc); state_p->is_completed = true; } JERRY_ASSERT (substate_p == NULL); } } /* jsp_parse_source_element_list */ /** * Parse program * * program * : LT!* source_element_list LT!* EOF! * ; * * @return true - if parse finished successfully (no SyntaxError was raised); * false - otherwise. */ static jsp_status_t parser_parse_program (const jerry_api_char_t *source_p, /**< source code buffer */ size_t source_size, /**< source code size in bytes */ bool in_eval, /**< flag indicating if we are parsing body of eval code */ bool is_strict, /**< flag, indicating whether current code * inherited strict mode from code of an outer scope */ const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array * (in case there were no syntax errors) */ bool *out_contains_functions_p) /**< out: optional (can be NULL, if the output is not needed) * flag, indicating whether the compiled byte-code * contains a function declaration / expression */ { JERRY_ASSERT (out_bytecode_data_p != NULL); const scope_type_t scope_type = (in_eval ? SCOPE_TYPE_EVAL : SCOPE_TYPE_GLOBAL); #ifndef JERRY_NDEBUG volatile bool is_pre_parse_finished = false; #endif /* !JERRY_NDEBUG */ jsp_status_t status; jsp_mm_init (); jsp_ctx_t ctx; jsp_init_ctx (&ctx, scope_type); jsp_stack_init (&ctx); dumper_init (&ctx, parser_show_instrs); jsp_early_error_init (); scopes_tree_init (); scopes_tree scope = scopes_tree_new_scope (NULL, scope_type); scopes_tree_set_strict_mode (scope, is_strict); jsp_set_current_scopes_tree_node (&ctx, scope); jmp_buf *jsp_early_error_label_p = jsp_early_error_get_early_error_longjmp_label (); int r = setjmp (*jsp_early_error_label_p); if (r == 0) { /* * Note: * Operations that could raise an early error can be performed only during execution of the block. */ lexer_init (source_p, source_size, parser_show_instrs); skip_token (&ctx); locus start_loc = tok.loc; jsp_parse_directive_prologue (&ctx); jsp_parse_source_element_list (&ctx, scope_type); JERRY_ASSERT (token_is (TOK_EOF)); jsp_set_current_scopes_tree_node (&ctx, NULL); scopes_tree_finish_build (); #ifndef JERRY_NDEBUG is_pre_parse_finished = true; #endif /* !JERRY_NDEBUG */ jsp_early_error_free (); if (out_contains_functions_p != NULL) { *out_contains_functions_p = scope->contains_functions; } jsp_switch_to_dump_mode (&ctx, scope); scopes_tree scope_to_dump_p = jsp_get_next_scopes_tree_node_to_dump (&ctx); JERRY_ASSERT (scope_to_dump_p == scope); bytecode_data_header_t *root_bc_header_p = bc_dump_single_scope (scope); scopes_tree_free_scope (scope); bc_register_root_bytecode_header (root_bc_header_p); *out_bytecode_data_p = root_bc_header_p; jsp_set_current_bytecode_header (&ctx, root_bc_header_p); seek_token (&ctx, start_loc); jsp_parse_source_element_list (&ctx, scope_type); jsp_set_current_bytecode_header (&ctx, NULL); jsp_stack_finalize (&ctx); JERRY_ASSERT (jsp_get_scope_type (&ctx) == scope_type); if (parser_show_instrs) { lit_dump_literals (); bc_print_instrs (root_bc_header_p); } scopes_tree_finalize (); status = JSP_STATUS_OK; } else { /* SyntaxError handling */ #ifndef JERRY_NDEBUG JERRY_ASSERT (!is_pre_parse_finished); #endif /* !JERRY_NDEBUG */ *out_bytecode_data_p = NULL; jsp_mm_free_all (); jsp_early_error_t type = jsp_early_error_get_type (); if (type == JSP_EARLY_ERROR_SYNTAX) { status = JSP_STATUS_SYNTAX_ERROR; } else { JERRY_ASSERT (type == JSP_EARLY_ERROR_REFERENCE); status = JSP_STATUS_REFERENCE_ERROR; } } jsp_mm_finalize (); return status; } /* parser_parse_program */ /** * Parse source script * * @return true - if parse finished successfully (no SyntaxError were raised); * false - otherwise. */ jsp_status_t parser_parse_script (const jerry_api_char_t *source, /**< source script */ size_t source_size, /**< source script size it bytes */ const bytecode_data_header_t **out_bytecode_data_p) /**< out: generated byte-code array * (in case there were no syntax errors) */ { return parser_parse_program (source, source_size, false, false, out_bytecode_data_p, NULL); } /* parser_parse_script */ /** * Parse string passed to eval() call * * @return true - if parse finished successfully (no SyntaxError were raised); * false - otherwise. */ jsp_status_t parser_parse_eval (const jerry_api_char_t *source, /**< string passed to eval() */ size_t source_size, /**< string size in bytes */ bool is_strict, /**< flag, indicating whether eval is called * from strict code in direct mode */ const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array * (in case there were no syntax errors) */ bool *out_contains_functions_p) /**< out: flag, indicating whether the compiled byte-code * contains a function declaration / expression */ { JERRY_ASSERT (out_contains_functions_p != NULL); return parser_parse_program (source, source_size, true, is_strict, out_bytecode_data_p, out_contains_functions_p); } /* parser_parse_eval */ /** * Tell parser whether to dump bytecode */ void parser_set_show_instrs (bool show_instrs) /**< flag indicating whether to dump bytecode */ { parser_show_instrs = show_instrs; } /* parser_set_show_instrs */