/** * Dump scope to current scope * * NOTE: * This function is used for processing of function expressions as they should not be hoisted. * After parsing a function expression, it is immediately dumped to current scope via call of this function. */ void 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_count; instr_pos++) { 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_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } 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_count; instr_pos++) { 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 */
/** * Initialize a scope * * @return initialized scope */ scopes_tree 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)); tree->t.parent = (tree_header *) parent; tree->t.children = null_list; tree->t.children_num = 0; if (parent != NULL) { if (parent->t.children_num == 0) { parent->t.children = linked_list_init (sizeof (scopes_tree)); } linked_list_set_element (parent->t.children, parent->t.children_num, &tree); void *added = linked_list_element (parent->t.children, parent->t.children_num); JERRY_ASSERT (*(scopes_tree *) added == tree); parent->t.children_num++; } 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 */
op_meta scopes_tree_op_meta (scopes_tree tree, vm_instr_counter_t oc) { assert_tree (tree); 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 = (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) ( res + scopes_tree_count_instructions ( *(scopes_tree *) linked_list_element (t->t.children, i))); } return res; }
/** * 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) /**< scope */ { assert_tree (tree); size_t result = 0; if (lit_id_to_uid != null_hash) { hash_table_free (lit_id_to_uid); lit_id_to_uid = null_hash; } next_uid = 0; global_oc = 0; assert_tree (tree); vm_instr_counter_t instr_pos; bool header = true; for (instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) { op_meta *om_p = extract_op_meta (tree->instrs, instr_pos); if (om_p->op.op_idx != VM_OP_META && !header) { break; } if (om_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } 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_count; 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 */
void scopes_tree_free (scopes_tree tree) { assert_tree (tree); if (tree->t.children_num != 0) { for (uint8_t i = 0; i < tree->t.children_num; ++i) { scopes_tree_free (*(scopes_tree *) linked_list_element (tree->t.children, i)); } linked_list_free (tree->t.children); } linked_list_free (tree->instrs); linked_list_free (tree->var_decls); jsp_mm_free (tree); }
/* * 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, /**< 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_p); vm_instr_counter_t instr_pos; bool header = true; for (instr_pos = 0; instr_pos < tree->instrs_count; instr_pos++) { 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_p->op.op_idx == VM_OP_REG_VAR_DECL) { header = false; } 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_p, lit_ids_p); } for (; instr_pos < tree->instrs_count; instr_pos++) { data_p[global_oc] = generate_instr (tree->instrs, instr_pos, lit_ids_p); global_oc++; } } /* merge_subscopes */
void * linked_list_element (linked_list list, size_t element_num) { ASSERT_LIST (list); linked_list_header *header = (linked_list_header *) list; size_t block_size = linked_list_block_size (header->element_size); linked_list raw = list + sizeof (linked_list_header); if (block_size < header->element_size * (element_num + 1)) { if (header->next) { return linked_list_element ((linked_list) header->next, element_num - (block_size / header->element_size)); } else { return NULL; } } raw += header->element_size * element_num; return raw; }
/** * Get instruction from instruction list * * @return instruction at specified position */ static op_meta * 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 (instr_list, instr_pos); } /* extract_op_meta */