diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 674af1b5a..c30bc8811 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -2507,31 +2507,36 @@ dump_throw (operand op) dump_single_address (getop_throw_value, op); } +/** + * Checks if variable is already declared + * + * @return true if variable declaration already exists + * false otherwise + */ bool -dumper_variable_declaration_exists (lit_cpointer_t lit_id) +dumper_variable_declaration_exists (lit_cpointer_t lit_id) /**< literal which holds variable's name */ { - for (vm_instr_counter_t oc = (vm_instr_counter_t) (serializer_get_current_instr_counter () - 1); - oc > 0; oc--) + vm_instr_counter_t var_decls_count = (vm_instr_counter_t) serializer_get_current_var_decls_counter (); + for (vm_instr_counter_t oc = (vm_instr_counter_t) (0); oc < var_decls_count; oc++) { - const op_meta var_decl_op_meta = serializer_get_op_meta (oc); - if (var_decl_op_meta.op.op_idx != VM_OP_VAR_DECL) - { - break; - } + const op_meta var_decl_op_meta = serializer_get_var_decl (oc); if (var_decl_op_meta.lit_id[0].packed_value == lit_id.packed_value) { return true; } } return false; -} +} /* dumper_variable_declaration_exists */ +/** + * Dump instruction designating variable declaration + */ void -dump_variable_declaration (lit_cpointer_t lit_id) +dump_variable_declaration (lit_cpointer_t lit_id) /**< literal which holds variable's name */ { const vm_instr_t instr = getop_var_decl (LITERAL_TO_REWRITE); - serializer_dump_op_meta (create_op_meta_100 (instr, lit_id)); -} + serializer_dump_var_decl (create_op_meta_100 (instr, lit_id)); +} /* dump_variable_declaration */ /** * Dump template of 'meta' instruction for scope's code flags. diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index fd953431c..5d2f18a2b 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -829,7 +829,18 @@ parse_primary_expression (void) case TOK_NUMBER: case TOK_REGEXP: case TOK_STRING: return parse_literal (); - case TOK_NAME: return literal_operand (token_data_as_lit_cp ()); + case TOK_NAME: + { + if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "arguments")) + { + scopes_tree_set_arguments_used (STACK_TOP (scopes)); + } + if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "eval")) + { + scopes_tree_set_eval_used (STACK_TOP (scopes)); + } + return literal_operand (token_data_as_lit_cp ()); + } case TOK_OPEN_SQUARE: return parse_array_literal (); case TOK_OPEN_BRACE: return parse_object_literal (); case TOK_OPEN_PAREN: @@ -1812,6 +1823,15 @@ parse_variable_declaration (void) current_token_must_be (TOK_NAME); const operand name = literal_operand (token_data_as_lit_cp ()); + if (!dumper_variable_declaration_exists (token_data_as_lit_cp ())) + { + jsp_early_error_check_for_eval_and_arguments_in_strict_mode (literal_operand (token_data_as_lit_cp ()), + is_strict_mode (), + tok.loc); + + dump_variable_declaration (token_data_as_lit_cp ()); + } + skip_newlines (); if (token_is (TOK_EQ)) { @@ -1825,7 +1845,7 @@ parse_variable_declaration (void) } return name; -} +} /* parse_variable_declaration */ /* variable_declaration_list : variable_declaration @@ -2886,65 +2906,12 @@ parse_source_element (void) } /** - * Skip function's optional name and parentheses - * - * @return: true, if skipped successfully - * false, if open parentheses wasn't found (this means that keyword 'function' is used as - * a property name) + * Check for "use strict" in directive prologue */ -static bool -skip_optional_name_and_parens (void) -{ - if (token_is (TOK_NAME)) - { - token_after_newlines_must_be (TOK_OPEN_PAREN); - } - - if (token_is (TOK_OPEN_PAREN)) - { - skip_newlines (); - } - else - { - return false; - } - - while (!token_is (TOK_CLOSE_PAREN)) - { - skip_newlines (); - } - - return true; -} /* skip_optional_name_and_parens */ - static void -skip_function (void) -{ - skip_newlines (); - if (skip_optional_name_and_parens ()) - { - skip_newlines (); - jsp_skip_braces (TOK_OPEN_BRACE); - } -} - -static bool -var_declared (lit_cpointer_t var_cp) -{ - return dumper_variable_declaration_exists (var_cp); -} - -static void -preparse_scope (bool is_global) +check_directive_prologue_for_use_strict () { const locus start_loc = tok.loc; - const token_type end_tt = is_global ? TOK_EOF : TOK_CLOSE_BRACE; - - vm_instr_counter_t scope_code_flags_oc = dump_scope_code_flags_for_rewrite (); - - bool is_use_strict = false; - bool is_ref_arguments_identifier = false; - bool is_ref_eval_identifier = false; /* * Check Directive Prologue for Use Strict directive (see ECMA-262 5.1 section 14.1) @@ -2954,7 +2921,8 @@ preparse_scope (bool is_global) 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)) { - is_use_strict = true; + scopes_tree_set_strict_mode (STACK_TOP (scopes), true); + lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes))); break; } @@ -2966,186 +2934,15 @@ preparse_scope (bool is_global) } } - size_t nesting_level = 0; - while (nesting_level > 0 || !token_is (end_tt)) - { - if (token_is (TOK_OPEN_BRACE)) - { - nesting_level++; - } - else if (token_is (TOK_CLOSE_BRACE)) - { - nesting_level--; - } - else if (token_is (TOK_NAME)) - { - if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "arguments")) - { - is_ref_arguments_identifier = true; - } - else if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "eval")) - { - is_ref_eval_identifier = true; - } - } - - skip_newlines (); - } - - opcode_scope_code_flags_t scope_flags = OPCODE_SCOPE_CODE_FLAGS__EMPTY; - - if (is_use_strict) - { - scopes_tree_set_strict_mode (STACK_TOP (scopes), true); - - scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_STRICT); - } - - if (!is_ref_arguments_identifier) - { - scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER); - } - - if (!is_ref_eval_identifier) - { - scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER); - } - - rewrite_scope_code_flags (scope_code_flags_oc, scope_flags); - - lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes))); - - dump_reg_var_decl_for_rewrite (); - if (lit_utf8_iterator_pos_cmp (start_loc, tok.loc) != 0) { lexer_seek (start_loc); - skip_newlines (); - - bool is_in_var_declaration_list = false; - - size_t nesting_level = 0; - while (nesting_level > 0 || !token_is (end_tt)) - { - /* - * FIXME: - * Remove preparse_scope; move variable declaration search to main pass of parser. - * When byte-code and scope storages would be introduced, move variable declarations - * from byte-code to scope descriptor. - */ - if (token_is (TOK_NAME)) - { - if (is_in_var_declaration_list) - { - if (!var_declared (token_data_as_lit_cp ())) - { - jsp_early_error_check_for_eval_and_arguments_in_strict_mode (literal_operand (token_data_as_lit_cp ()), - is_strict_mode (), - tok.loc); - dump_variable_declaration (token_data_as_lit_cp ()); - } - } - - skip_newlines (); - - if (!token_is (TOK_COMMA) - && !token_is (TOK_EQ)) - { - is_in_var_declaration_list = false; - } - } - else if (is_in_var_declaration_list) - { - if (token_is (TOK_EQ)) - { - skip_newlines (); - - while (!token_is (end_tt) - && !token_is (TOK_COMMA) - && !token_is (TOK_SEMICOLON)) - { - if (is_keyword (KW_FUNCTION)) - { - skip_function (); - } - else if (token_is (TOK_OPEN_BRACE)) - { - jsp_skip_braces (TOK_OPEN_BRACE); - } - else if (token_is (TOK_OPEN_SQUARE)) - { - jsp_skip_braces (TOK_OPEN_SQUARE); - } - else if (token_is (TOK_OPEN_PAREN)) - { - jsp_skip_braces (TOK_OPEN_PAREN); - } - else if (token_is (TOK_KEYWORD)) - { - if (is_keyword (KW_VAR)) - { - is_in_var_declaration_list = false; - } - break; - } - else if (token_is (TOK_CLOSE_BRACE)) - { - /* the '}' would be handled during next iteration, reducing nesting level counter */ - is_in_var_declaration_list = false; - - break; - } - - skip_token (); - } - } - else if (token_is (TOK_COMMA)) - { - skip_newlines (); - } - else - { - is_in_var_declaration_list = false; - - skip_newlines (); - } - } - else - { - if (token_is (TOK_OPEN_BRACE)) - { - nesting_level++; - } - else if (token_is (TOK_CLOSE_BRACE)) - { - nesting_level--; - } - else if (token_is (TOK_OPEN_SQUARE)) - { - jsp_skip_braces (TOK_OPEN_SQUARE); - } - else if (is_keyword (KW_VAR)) - { - is_in_var_declaration_list = true; - } - else if (is_keyword (KW_FUNCTION)) - { - skip_function (); - } - - skip_newlines (); - } - } - - lexer_seek (start_loc); } else { - JERRY_ASSERT (token_is (end_tt)); - lexer_save_token (tok); } -} +} /* check_directive_prologue_for_use_strict */ /** * Parse source element list @@ -3155,10 +2952,17 @@ preparse_scope (bool is_global) * ; */ static void -parse_source_element_list (bool is_global) /**< flag indicating if we are parsing the global scope */ +parse_source_element_list (bool is_global) /**< flag, indicating that we parsing global context */ { + const token_type end_tt = is_global ? TOK_EOF : TOK_CLOSE_BRACE; + dumper_new_scope (); - preparse_scope (is_global); + + vm_instr_counter_t scope_code_flags_oc = dump_scope_code_flags_for_rewrite (); + + check_directive_prologue_for_use_strict (); + + dump_reg_var_decl_for_rewrite (); if (inside_eval && !inside_function) @@ -3172,7 +2976,33 @@ parse_source_element_list (bool is_global) /**< flag indicating if we are parsin parse_source_element (); skip_newlines (); } + + if (!token_is (end_tt)) + { + PARSE_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unexpected token", tok.loc); + } + lexer_save_token (tok); + + opcode_scope_code_flags_t scope_flags = OPCODE_SCOPE_CODE_FLAGS__EMPTY; + + scopes_tree fe_scope_tree = STACK_TOP (scopes); + if (fe_scope_tree->strict_mode) + { + scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_STRICT); + } + + if (!fe_scope_tree->ref_arguments) + { + scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER); + } + + if (!fe_scope_tree->ref_eval) + { + scope_flags = (opcode_scope_code_flags_t) (scope_flags | OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER); + } + rewrite_scope_code_flags (scope_code_flags_oc, scope_flags); + rewrite_reg_var_decl (); dumper_finish_scope (); } /* parse_source_element_list */ diff --git a/jerry-core/parser/js/scopes-tree.cpp b/jerry-core/parser/js/scopes-tree.cpp index 078225927..dec36a100 100644 --- a/jerry-core/parser/js/scopes-tree.cpp +++ b/jerry-core/parser/js/scopes-tree.cpp @@ -49,21 +49,44 @@ vm_instr_counter_t scopes_tree_instrs_num (scopes_tree t) { assert_tree (t); - return t->instrs_num; + return t->instrs_count; } +/** + * Get number of variable declarations in the scope + * + * @return number of variable declarations + */ +vm_instr_counter_t +scopes_tree_var_decls_num (scopes_tree t) /**< scope */ +{ + assert_tree (t); + return t->var_decls_cout; +} /* scopes_tree_var_decls_num */ + void scopes_tree_add_op_meta (scopes_tree tree, op_meta op) { assert_tree (tree); - linked_list_set_element (tree->instrs, tree->instrs_num++, &op); + linked_list_set_element (tree->instrs, tree->instrs_count++, &op); } +/** + * Add variable declaration to a scope + */ +void +scopes_tree_add_var_decl (scopes_tree tree, /**< scope, to which variable declaration is added */ + op_meta op) /**< variable declaration instruction */ +{ + assert_tree (tree); + linked_list_set_element (tree->var_decls, tree->var_decls_cout++, &op); +} /* scopes_tree_add_var_decl */ + void scopes_tree_set_op_meta (scopes_tree tree, vm_instr_counter_t oc, op_meta op) { assert_tree (tree); - JERRY_ASSERT (oc < tree->instrs_num); + JERRY_ASSERT (oc < tree->instrs_count); linked_list_set_element (tree->instrs, oc, &op); } @@ -71,23 +94,37 @@ void scopes_tree_set_instrs_num (scopes_tree tree, vm_instr_counter_t oc) { assert_tree (tree); - JERRY_ASSERT (oc < tree->instrs_num); - tree->instrs_num = oc; + JERRY_ASSERT (oc < tree->instrs_count); + tree->instrs_count = oc; } op_meta scopes_tree_op_meta (scopes_tree tree, vm_instr_counter_t oc) { assert_tree (tree); - JERRY_ASSERT (oc < tree->instrs_num); + JERRY_ASSERT (oc < tree->instrs_count); return *(op_meta *) linked_list_element (tree->instrs, oc); } +/** + * Get variable declaration for the specified scope + * + * @return instruction, declaring a variable + */ +op_meta +scopes_tree_var_decl (scopes_tree tree, /**< scope, from which variable declaration is retrieved */ + vm_instr_counter_t oc) /**< number of variable declaration in the scope */ +{ + assert_tree (tree); + JERRY_ASSERT (oc < tree->var_decls_cout); + return *(op_meta *) linked_list_element (tree->var_decls, oc); +} /* scopes_tree_var_decl */ + vm_instr_counter_t scopes_tree_count_instructions (scopes_tree t) { assert_tree (t); - vm_instr_counter_t res = t->instrs_num; + vm_instr_counter_t res = (vm_instr_counter_t) (t->instrs_count + t->var_decls_cout); for (uint8_t i = 0; i < t->t.children_num; i++) { res = (vm_instr_counter_t) ( @@ -212,24 +249,37 @@ insert_uids_to_lit_id_map (op_meta *om, uint16_t mask) } } +/** + * Get instruction from instruction list + * + * @return instruction at specified position + */ static op_meta * -extract_op_meta (scopes_tree tree, vm_instr_counter_t instr_pos) +extract_op_meta (linked_list instr_list, /**< instruction list */ + vm_instr_counter_t instr_pos) /**< position inside the list */ { - return (op_meta *) linked_list_element (tree->instrs, instr_pos); -} + return (op_meta *) linked_list_element (instr_list, instr_pos); +} /* extract_op_meta */ +/** + * Add instruction to an instruction list + * + * @return generated instruction + */ static vm_instr_t -generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_table *lit_ids) +generate_instr (linked_list instr_list, /**< instruction list */ + vm_instr_counter_t instr_pos, /**< position where to generate an instruction */ + lit_id_hash_table *lit_ids) /**< hash table binding operand identifiers and literals */ { start_new_block_if_necessary (); - op_meta *om = extract_op_meta (tree, instr_pos); + op_meta *om_p = extract_op_meta (instr_list, instr_pos); /* Now we should change uids of instructions. Since different instructions has different literals/tmps in different places, we should change only them. For each case possible literal positions are shown as 0xYYY literal, where Y is set to '1' when there is a possible literal in this position, and '0' otherwise. */ - switch (om->op.op_idx) + switch (om_p->op.op_idx) { case VM_OP_PROP_GETTER: case VM_OP_PROP_SETTER: @@ -256,7 +306,7 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case VM_OP_MULTIPLICATION: case VM_OP_REMAINDER: { - change_uid (om, lit_ids, 0x111); + change_uid (om_p, lit_ids, 0x111); break; } case VM_OP_CALL_N: @@ -273,18 +323,18 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case VM_OP_UNARY_PLUS: case VM_OP_UNARY_MINUS: { - change_uid (om, lit_ids, 0x110); + change_uid (om_p, lit_ids, 0x110); break; } case VM_OP_ASSIGNMENT: { - switch (om->op.data.assignment.type_value_right) + switch (om_p->op.data.assignment.type_value_right) { case OPCODE_ARG_TYPE_SIMPLE: case OPCODE_ARG_TYPE_SMALLINT: case OPCODE_ARG_TYPE_SMALLINT_NEGATE: { - change_uid (om, lit_ids, 0x100); + change_uid (om_p, lit_ids, 0x100); break; } case OPCODE_ARG_TYPE_NUMBER: @@ -293,7 +343,7 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case OPCODE_ARG_TYPE_STRING: case OPCODE_ARG_TYPE_VARIABLE: { - change_uid (om, lit_ids, 0x101); + change_uid (om_p, lit_ids, 0x101); break; } } @@ -313,7 +363,7 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case VM_OP_VAR_DECL: case VM_OP_RETVAL: { - change_uid (om, lit_ids, 0x100); + change_uid (om_p, lit_ids, 0x100); break; } case VM_OP_RET: @@ -322,24 +372,24 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case VM_OP_JMP_DOWN: case VM_OP_REG_VAR_DECL: { - change_uid (om, lit_ids, 0x000); + change_uid (om_p, lit_ids, 0x000); break; } case VM_OP_META: { - switch (om->op.data.meta.type) + switch (om_p->op.data.meta.type) { case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { - change_uid (om, lit_ids, 0x011); + change_uid (om_p, lit_ids, 0x011); break; } case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { - change_uid (om, lit_ids, 0x010); + change_uid (om_p, lit_ids, 0x010); break; } case OPCODE_META_TYPE_UNDEFINED: @@ -351,23 +401,27 @@ generate_instr (scopes_tree tree, vm_instr_counter_t instr_pos, lit_id_hash_tabl case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { - change_uid (om, lit_ids, 0x000); + change_uid (om_p, lit_ids, 0x000); break; } } break; } } - return om->op; -} + return om_p->op; +} /* generate_instr */ +/** + * Count number of literals in instruction which were not seen previously + * + * @return number of new literals + */ static idx_t -count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) +count_new_literals_in_instr (op_meta *om_p) /**< instruction */ { start_new_block_if_necessary (); idx_t current_uid = next_uid; - op_meta *om = extract_op_meta (tree, instr_pos); - switch (om->op.op_idx) + switch (om_p->op.op_idx) { case VM_OP_PROP_GETTER: case VM_OP_PROP_SETTER: @@ -394,7 +448,7 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case VM_OP_MULTIPLICATION: case VM_OP_REMAINDER: { - insert_uids_to_lit_id_map (om, 0x111); + insert_uids_to_lit_id_map (om_p, 0x111); break; } case VM_OP_CALL_N: @@ -411,18 +465,18 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case VM_OP_UNARY_PLUS: case VM_OP_UNARY_MINUS: { - insert_uids_to_lit_id_map (om, 0x110); + insert_uids_to_lit_id_map (om_p, 0x110); break; } case VM_OP_ASSIGNMENT: { - switch (om->op.data.assignment.type_value_right) + switch (om_p->op.data.assignment.type_value_right) { case OPCODE_ARG_TYPE_SIMPLE: case OPCODE_ARG_TYPE_SMALLINT: case OPCODE_ARG_TYPE_SMALLINT_NEGATE: { - insert_uids_to_lit_id_map (om, 0x100); + insert_uids_to_lit_id_map (om_p, 0x100); break; } case OPCODE_ARG_TYPE_NUMBER: @@ -431,7 +485,7 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case OPCODE_ARG_TYPE_STRING: case OPCODE_ARG_TYPE_VARIABLE: { - insert_uids_to_lit_id_map (om, 0x101); + insert_uids_to_lit_id_map (om_p, 0x101); break; } } @@ -450,7 +504,7 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case VM_OP_VAR_DECL: case VM_OP_RETVAL: { - insert_uids_to_lit_id_map (om, 0x100); + insert_uids_to_lit_id_map (om_p, 0x100); break; } case VM_OP_RET: @@ -459,24 +513,24 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case VM_OP_JMP_DOWN: case VM_OP_REG_VAR_DECL: { - insert_uids_to_lit_id_map (om, 0x000); + insert_uids_to_lit_id_map (om_p, 0x000); break; } case VM_OP_META: { - switch (om->op.data.meta.type) + switch (om_p->op.data.meta.type) { case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { - insert_uids_to_lit_id_map (om, 0x011); + insert_uids_to_lit_id_map (om_p, 0x011); break; } case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { - insert_uids_to_lit_id_map (om, 0x010); + insert_uids_to_lit_id_map (om_p, 0x010); break; } case OPCODE_META_TYPE_UNDEFINED: @@ -488,7 +542,7 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { - insert_uids_to_lit_id_map (om, 0x000); + insert_uids_to_lit_id_map (om_p, 0x000); break; } } @@ -496,16 +550,23 @@ count_new_literals_in_instr (scopes_tree tree, vm_instr_counter_t instr_pos) } } return (idx_t) (next_uid - current_uid); -} +} /* count_new_literals_in_instr */ -/* Before filling literal indexes 'hash' table we shall initiate it with number of neccesary literal indexes. - Since bytecode is divided into blocks and id of the block is a part of hash key, we shall divide bytecode - into blocks and count unique literal indexes used in each block. */ +/** + * Count slots needed for a scope's hash table + * + * Before filling literal indexes 'hash' table we shall initiate it with number of neccesary literal indexes. + * Since bytecode is divided into blocks and id of the block is a part of hash key, we shall divide bytecode + * into blocks and count unique literal indexes used in each block. + * + * @return total number of literals in scope + */ size_t -scopes_tree_count_literals_in_blocks (scopes_tree tree) +scopes_tree_count_literals_in_blocks (scopes_tree tree) /**< scope */ { assert_tree (tree); size_t result = 0; + if (lit_id_to_uid != null_hash) { hash_table_free (lit_id_to_uid); @@ -517,79 +578,99 @@ scopes_tree_count_literals_in_blocks (scopes_tree tree) assert_tree (tree); vm_instr_counter_t instr_pos; bool header = true; - for (instr_pos = 0; instr_pos < tree->instrs_num; instr_pos++) + for (instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) { - op_meta *om = extract_op_meta (tree, instr_pos); - if (om->op.op_idx != VM_OP_VAR_DECL - && om->op.op_idx != VM_OP_META && !header) + op_meta *om_p = extract_op_meta (tree->instrs, instr_pos); + if (om_p->op.op_idx != VM_OP_META && !header) { break; } - if (om->op.op_idx == VM_OP_REG_VAR_DECL) + if (om_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } - result += count_new_literals_in_instr (tree, instr_pos); + result += count_new_literals_in_instr (om_p); } + + for (vm_instr_counter_t var_decl_pos = 0; var_decl_pos < tree->var_decls_cout; var_decl_pos++) + { + op_meta *om_p = extract_op_meta (tree->var_decls, var_decl_pos); + result += count_new_literals_in_instr (om_p); + } + for (uint8_t child_id = 0; child_id < tree->t.children_num; child_id++) { result += scopes_tree_count_literals_in_blocks (*(scopes_tree *) linked_list_element (tree->t.children, child_id)); } - for (; instr_pos < tree->instrs_num; instr_pos++) + + for (; instr_pos < tree->instrs_count; instr_pos++) { - result += count_new_literals_in_instr (tree, instr_pos); + op_meta *om_p = extract_op_meta (tree->instrs, instr_pos); + result += count_new_literals_in_instr (om_p); } return result; -} +} /* scopes_tree_count_literals_in_blocks */ -/* This function performs functions hoisting. - - Each scope consists of four parts: - 1) Header with 'use strict' marker and reg_var_decl opcode - 2) Variable declarations, dumped by the preparser - 3) Function declarations - 4) Computational code - - Header and var_decls are dumped first, - then we shall recursively dump function declaration, - and finally, other instructions. - - For each instructions block (size of block is defined in bytecode-data.h) - literal indexes 'hash' table is filled. */ +/* + * This function performs functions hoisting. + * + * Each scope consists of four parts: + * 1) Header with 'use strict' marker and reg_var_decl opcode + * 2) Variable declarations, dumped by the preparser + * 3) Function declarations + * 4) Computational code + * + * Header and var_decls are dumped first, + * then we shall recursively dump function declaration, + * and finally, other instructions. + * + * For each instructions block (size of block is defined in bytecode-data.h) + * literal indexes 'hash' table is filled. + */ static void -merge_subscopes (scopes_tree tree, vm_instr_t *data, lit_id_hash_table *lit_ids) +merge_subscopes (scopes_tree tree, /**< scopes tree to merge */ + vm_instr_t *data_p, /**< instruction array, where the scopes are merged to */ + lit_id_hash_table *lit_ids_p) /**< literal indexes 'hash' table */ { assert_tree (tree); - JERRY_ASSERT (data); + JERRY_ASSERT (data_p); vm_instr_counter_t instr_pos; bool header = true; - for (instr_pos = 0; instr_pos < tree->instrs_num; instr_pos++) + for (instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) { - op_meta *om = extract_op_meta (tree, instr_pos); - if (om->op.op_idx != VM_OP_VAR_DECL - && om->op.op_idx != VM_OP_META && !header) + op_meta *om_p = extract_op_meta (tree->instrs, instr_pos); + if (om_p->op.op_idx != VM_OP_VAR_DECL + && om_p->op.op_idx != VM_OP_META && !header) { break; } - if (om->op.op_idx == VM_OP_REG_VAR_DECL) + if (om_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } - data[global_oc] = generate_instr (tree, instr_pos, lit_ids); + data_p[global_oc] = generate_instr (tree->instrs, instr_pos, lit_ids_p); global_oc++; } + + for (vm_instr_counter_t var_decl_pos = 0; var_decl_pos < tree->var_decls_cout; var_decl_pos++) + { + data_p[global_oc] = generate_instr (tree->var_decls, var_decl_pos, lit_ids_p); + global_oc++; + } + for (uint8_t child_id = 0; child_id < tree->t.children_num; child_id++) { merge_subscopes (*(scopes_tree *) linked_list_element (tree->t.children, child_id), - data, lit_ids); + data_p, lit_ids_p); } - for (; instr_pos < tree->instrs_num; instr_pos++) + + for (; instr_pos < tree->instrs_count; instr_pos++) { - data[global_oc] = generate_instr (tree, instr_pos, lit_ids); + data_p[global_oc] = generate_instr (tree->instrs, instr_pos, lit_ids_p); global_oc++; } -} +} /* merge_subscopes */ /* Postparser. Init literal indexes 'hash' table. @@ -631,12 +712,36 @@ scopes_tree_raw_data (scopes_tree tree, /**< scopes tree to convert to byte-code return instrs; } /* scopes_tree_raw_data */ +/** + * Set up a flag, indicating that scope should be executed in strict mode + */ void -scopes_tree_set_strict_mode (scopes_tree tree, bool strict_mode) +scopes_tree_set_strict_mode (scopes_tree tree, /**< scope */ + bool strict_mode) /**< value of the strict mode flag */ { assert_tree (tree); - tree->strict_mode = strict_mode ? 1 : 0; -} + tree->strict_mode = strict_mode; +} /* scopes_tree_set_strict_mode */ + +/** + * Set up a flag, indicating that "arguments" is used inside a scope + */ +void +scopes_tree_set_arguments_used (scopes_tree tree) /**< scope */ +{ + assert_tree (tree); + tree->ref_arguments = true; +} /* merge_subscopes */ + +/** + * Set up a flag, indicating that "eval" is used inside a scope + */ +void +scopes_tree_set_eval_used (scopes_tree tree) /**< scope */ +{ + assert_tree (tree); + tree->ref_eval = true; +} /* scopes_tree_set_eval_used */ bool scopes_tree_strict_mode (scopes_tree tree) @@ -645,8 +750,13 @@ scopes_tree_strict_mode (scopes_tree tree) return (bool) tree->strict_mode; } +/** + * Initialize a scope + * + * @return initialized scope + */ scopes_tree -scopes_tree_init (scopes_tree parent) +scopes_tree_init (scopes_tree parent) /**< parent scope */ { scopes_tree tree = (scopes_tree) jsp_mm_alloc (sizeof (scopes_tree_int)); memset (tree, 0, sizeof (scopes_tree_int)); @@ -664,11 +774,15 @@ scopes_tree_init (scopes_tree parent) JERRY_ASSERT (*(scopes_tree *) added == tree); parent->t.children_num++; } - tree->instrs_num = 0; - tree->strict_mode = 0; + tree->instrs_count = 0; + tree->strict_mode = false; + tree->ref_eval = false; + tree->ref_arguments = false; tree->instrs = linked_list_init (sizeof (op_meta)); + tree->var_decls_cout = 0; + tree->var_decls = linked_list_init (sizeof (op_meta)); return tree; -} +} /* scopes_tree_init */ void scopes_tree_free (scopes_tree tree) @@ -683,5 +797,6 @@ scopes_tree_free (scopes_tree tree) linked_list_free (tree->t.children); } linked_list_free (tree->instrs); + linked_list_free (tree->var_decls); jsp_mm_free (tree); } diff --git a/jerry-core/parser/js/scopes-tree.h b/jerry-core/parser/js/scopes-tree.h index 11e3ec729..301ce871a 100644 --- a/jerry-core/parser/js/scopes-tree.h +++ b/jerry-core/parser/js/scopes-tree.h @@ -39,12 +39,19 @@ typedef struct tree_header uint8_t children_num; } tree_header; +/** + * Structure for holding scope information during parsing + */ typedef struct { - tree_header t; - linked_list instrs; - vm_instr_counter_t instrs_num; - unsigned strict_mode:1; + tree_header t; /**< header */ + linked_list instrs; /**< instructions */ + vm_instr_counter_t instrs_count; /**< count of instructions */ + linked_list var_decls; /**< instructions for variable declarations */ + uint8_t var_decls_cout; /**< number of instructions for variable declarations */ + bool strict_mode: 1; /**< flag, indicating that scope's code should be executed in strict mode */ + bool ref_arguments: 1; /**< flag, indicating that "arguments" variable is used inside the scope */ + bool ref_eval: 1; /**< flag, indicating that "eval" is used inside the scope */ } scopes_tree_int; typedef scopes_tree_int * scopes_tree; @@ -52,14 +59,19 @@ typedef scopes_tree_int * scopes_tree; scopes_tree scopes_tree_init (scopes_tree); void scopes_tree_free (scopes_tree); vm_instr_counter_t scopes_tree_instrs_num (scopes_tree); +vm_instr_counter_t scopes_tree_var_decls_num (scopes_tree); void scopes_tree_add_op_meta (scopes_tree, op_meta); +void scopes_tree_add_var_decl (scopes_tree, op_meta); void scopes_tree_set_op_meta (scopes_tree, vm_instr_counter_t, op_meta); void scopes_tree_set_instrs_num (scopes_tree, vm_instr_counter_t); op_meta scopes_tree_op_meta (scopes_tree, vm_instr_counter_t); +op_meta scopes_tree_var_decl (scopes_tree, vm_instr_counter_t); size_t scopes_tree_count_literals_in_blocks (scopes_tree); vm_instr_counter_t scopes_tree_count_instructions (scopes_tree); vm_instr_t *scopes_tree_raw_data (scopes_tree, uint8_t *, size_t, lit_id_hash_table *); void scopes_tree_set_strict_mode (scopes_tree, bool); +void scopes_tree_set_arguments_used (scopes_tree); +void scopes_tree_set_eval_used (scopes_tree); bool scopes_tree_strict_mode (scopes_tree); #endif /* SCOPES_TREE_H */ diff --git a/jerry-core/parser/js/serializer.cpp b/jerry-core/parser/js/serializer.cpp index 8125c7324..51e8c6e74 100644 --- a/jerry-core/parser/js/serializer.cpp +++ b/jerry-core/parser/js/serializer.cpp @@ -17,6 +17,7 @@ #include "bytecode-data.h" #include "pretty-printer.h" #include "array-list.h" +#include "scopes-tree.h" static bytecode_data_t bytecode_data; static scopes_tree current_scope; @@ -33,6 +34,18 @@ serializer_get_op_meta (vm_instr_counter_t oc) return scopes_tree_op_meta (current_scope, oc); } +/** + * Get variable declaration of the current scope + * + * @return variable declaration instruction + */ +op_meta +serializer_get_var_decl (vm_instr_counter_t oc) /**< index of variable declaration */ +{ + JERRY_ASSERT (current_scope); + return scopes_tree_var_decl (current_scope, oc); +} /* serializer_get_var_decl */ + /** * Get byte-code instruction from current scope, or specified byte-code array * @@ -102,28 +115,33 @@ serializer_dump_subscope (scopes_tree tree) /**< scope to dump */ JERRY_ASSERT (tree != NULL); vm_instr_counter_t instr_pos; bool header = true; - for (instr_pos = 0; instr_pos < tree->instrs_num; instr_pos++) + for (instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) { - op_meta *om = (op_meta *) linked_list_element (tree->instrs, instr_pos); - if (om->op.op_idx != VM_OP_VAR_DECL - && om->op.op_idx != VM_OP_META && !header) + op_meta *om_p = (op_meta *) linked_list_element (tree->instrs, instr_pos); + if (om_p->op.op_idx != VM_OP_VAR_DECL + && om_p->op.op_idx != VM_OP_META && !header) { break; } - if (om->op.op_idx == VM_OP_REG_VAR_DECL) + if (om_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } - scopes_tree_add_op_meta (current_scope, *om); + scopes_tree_add_op_meta (current_scope, *om_p); + } + for (vm_instr_counter_t var_decl_pos = 0; var_decl_pos < tree->var_decls_cout; var_decl_pos++) + { + op_meta *om_p = (op_meta *) linked_list_element (tree->var_decls, var_decl_pos); + scopes_tree_add_op_meta (current_scope, *om_p); } for (uint8_t child_id = 0; child_id < tree->t.children_num; child_id++) { serializer_dump_subscope (*(scopes_tree *) linked_list_element (tree->t.children, child_id)); } - for (; instr_pos < tree->instrs_num; instr_pos++) + for (; instr_pos < tree->instrs_count; instr_pos++) { - op_meta *om = (op_meta *) linked_list_element (tree->instrs, instr_pos); - scopes_tree_add_op_meta (current_scope, *om); + op_meta *om_p = (op_meta *) linked_list_element (tree->instrs, instr_pos); + scopes_tree_add_op_meta (current_scope, *om_p); } } /* serializer_dump_subscope */ @@ -180,12 +198,34 @@ serializer_dump_op_meta (op_meta op) #endif } +/** + * Dump variable declaration into the current scope + */ +void +serializer_dump_var_decl (op_meta op) /**< variable declaration instruction */ +{ + JERRY_ASSERT (scopes_tree_instrs_num (current_scope) + current_scope->var_decls_cout < MAX_OPCODES); + + scopes_tree_add_var_decl (current_scope, op); +} /* serializer_dump_var_decl */ + vm_instr_counter_t serializer_get_current_instr_counter (void) { return scopes_tree_instrs_num (current_scope); } +/** + * Get number of variable declarations in the current scope + * + * @return count of variable declarations + */ +vm_instr_counter_t +serializer_get_current_var_decls_counter (void) +{ + return scopes_tree_var_decls_num (current_scope); +} /* serializer_get_current_var_decls_counter */ + vm_instr_counter_t serializer_count_instrs_in_subscopes (void) { diff --git a/jerry-core/parser/js/serializer.h b/jerry-core/parser/js/serializer.h index 4f8b2a706..1ce8b6036 100644 --- a/jerry-core/parser/js/serializer.h +++ b/jerry-core/parser/js/serializer.h @@ -25,6 +25,7 @@ void serializer_init (); void serializer_set_show_instrs (bool show_instrs); op_meta serializer_get_op_meta (vm_instr_counter_t); +op_meta serializer_get_var_decl (vm_instr_counter_t); vm_instr_t serializer_get_instr (const vm_instr_t*, vm_instr_counter_t); lit_cpointer_t serializer_get_literal_cp_by_uid (uint8_t, const vm_instr_t*, vm_instr_counter_t); void serializer_set_strings_buffer (const ecma_char_t *); @@ -32,7 +33,9 @@ void serializer_set_scope (scopes_tree); void serializer_dump_subscope (scopes_tree); const vm_instr_t *serializer_merge_scopes_into_bytecode (void); void serializer_dump_op_meta (op_meta); +void serializer_dump_var_decl (op_meta); vm_instr_counter_t serializer_get_current_instr_counter (void); +vm_instr_counter_t serializer_get_current_var_decls_counter (void); vm_instr_counter_t serializer_count_instrs_in_subscopes (void); void serializer_set_writing_position (vm_instr_counter_t); void serializer_rewrite_op_meta (vm_instr_counter_t, op_meta); diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index dd6d877aa..9af0bd99d 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -1810,6 +1810,7 @@ vm_read_instr_counter_from_meta (opcode_meta_type expected_type, /**< expected t vm_instr_t getop_##opcode_name (void) \ { \ vm_instr_t instr; \ + memset (&instr, 0, sizeof(instr)); \ instr.op_idx = VM_OP_##opcode_name_uppercase; \ return instr; \ } @@ -1817,6 +1818,7 @@ vm_read_instr_counter_from_meta (opcode_meta_type expected_type, /**< expected t vm_instr_t getop_##opcode_name (idx_t arg1_v) \ { \ vm_instr_t instr; \ + memset (&instr, 0, sizeof(instr)); \ instr.op_idx = VM_OP_##opcode_name_uppercase; \ instr.data.opcode_name.arg1 = arg1_v; \ return instr; \ @@ -1825,6 +1827,7 @@ vm_read_instr_counter_from_meta (opcode_meta_type expected_type, /**< expected t vm_instr_t getop_##opcode_name (idx_t arg1_v, idx_t arg2_v) \ { \ vm_instr_t instr; \ + memset (&instr, 0, sizeof(instr)); \ instr.op_idx = VM_OP_##opcode_name_uppercase; \ instr.data.opcode_name.arg1 = arg1_v; \ instr.data.opcode_name.arg2 = arg2_v; \