/* Copyright 2014-2015 Samsung Electronics Co., Ltd. * * 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 "serializer.h" #include "bytecode-data.h" #include "pretty-printer.h" #include "array-list.h" #include "scopes-tree.h" static bytecode_data_header_t *first_bytecode_header_p; static scopes_tree current_scope; static bool print_instrs; static void serializer_print_instrs (const bytecode_data_header_t *); op_meta serializer_get_op_meta (vm_instr_counter_t oc) { JERRY_ASSERT (current_scope); 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 * * @return byte-code instruction */ vm_instr_t serializer_get_instr (const bytecode_data_header_t *bytecode_data_p, /**< pointer to byte-code data (or NULL, * if instruction should be taken from * instruction list of current scope) */ vm_instr_counter_t oc) /**< position of the intruction */ { if (bytecode_data_p == NULL) { return serializer_get_op_meta (oc).op; } else { JERRY_ASSERT (oc < bytecode_data_p->instrs_count); return bytecode_data_p->instrs_p[oc]; } } /* serializer_get_instr */ /** * Convert literal id (operand value of instruction) to compressed pointer to literal * * Bytecode is divided into blocks of fixed size and each block has independent encoding of variable names, * which are represented by 8 bit numbers - ids. * This function performs conversion from id to literal. * * @return compressed pointer to literal */ lit_cpointer_t serializer_get_literal_cp_by_uid (uint8_t id, /**< literal idx */ const bytecode_data_header_t *bytecode_data_p, /**< pointer to bytecode */ vm_instr_counter_t oc) /**< position in the bytecode */ { lit_id_hash_table *lit_id_hash = null_hash; if (bytecode_data_p) { lit_id_hash = MEM_CP_GET_POINTER (lit_id_hash_table, bytecode_data_p->lit_id_hash_cp); } else { lit_id_hash = MEM_CP_GET_POINTER (lit_id_hash_table, first_bytecode_header_p->lit_id_hash_cp); } if (lit_id_hash == null_hash) { return INVALID_LITERAL; } return lit_id_hash_table_lookup (lit_id_hash, id, oc); } /* serializer_get_literal_cp_by_uid */ void serializer_set_scope (scopes_tree new_scope) { current_scope = new_scope; } /** * 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 */ /** * Merge scopes tree into bytecode * * @return pointer to generated bytecode */ const bytecode_data_header_t * serializer_merge_scopes_into_bytecode (void) { const size_t buckets_count = scopes_tree_count_literals_in_blocks (current_scope); const vm_instr_counter_t instrs_count = scopes_tree_count_instructions (current_scope); const size_t blocks_count = JERRY_ALIGNUP (instrs_count, BLOCK_SIZE) / BLOCK_SIZE; const size_t bytecode_size = JERRY_ALIGNUP (instrs_count * sizeof (vm_instr_t), MEM_ALIGNMENT); const size_t hash_table_size = lit_id_hash_table_get_size_for_table (buckets_count, blocks_count); const size_t header_and_hash_table_size = JERRY_ALIGNUP (sizeof (bytecode_data_header_t) + hash_table_size, MEM_ALIGNMENT); uint8_t *buffer_p = (uint8_t*) mem_heap_alloc_block (bytecode_size + header_and_hash_table_size, MEM_HEAP_ALLOC_LONG_TERM); lit_id_hash_table *lit_id_hash = lit_id_hash_table_init (buffer_p + sizeof (bytecode_data_header_t), hash_table_size, buckets_count, blocks_count); vm_instr_t *bytecode_p = scopes_tree_raw_data (current_scope, buffer_p + header_and_hash_table_size, bytecode_size, lit_id_hash); bytecode_data_header_t *header_p = (bytecode_data_header_t *) buffer_p; MEM_CP_SET_POINTER (header_p->lit_id_hash_cp, lit_id_hash); header_p->instrs_p = bytecode_p; header_p->instrs_count = instrs_count; MEM_CP_SET_POINTER (header_p->next_header_cp, first_bytecode_header_p); first_bytecode_header_p = header_p; if (print_instrs) { lit_dump_literals (); serializer_print_instrs (header_p); } return header_p; } /* serializer_merge_scopes_into_bytecode */ void serializer_dump_op_meta (op_meta op) { JERRY_ASSERT (scopes_tree_instrs_num (current_scope) < MAX_OPCODES); scopes_tree_add_op_meta (current_scope, op); #ifdef JERRY_ENABLE_PRETTY_PRINTER if (print_instrs) { pp_op_meta (NULL, (vm_instr_counter_t) (scopes_tree_instrs_num (current_scope) - 1), op, false); } #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) { return (vm_instr_counter_t) (scopes_tree_count_instructions (current_scope) - scopes_tree_instrs_num (current_scope)); } void serializer_set_writing_position (vm_instr_counter_t oc) { scopes_tree_set_instrs_num (current_scope, oc); } void serializer_rewrite_op_meta (const vm_instr_counter_t loc, op_meta op) { scopes_tree_set_op_meta (current_scope, loc, op); #ifdef JERRY_ENABLE_PRETTY_PRINTER if (print_instrs) { pp_op_meta (NULL, loc, op, true); } #endif } static void serializer_print_instrs (const bytecode_data_header_t *bytecode_data_p) { #ifdef JERRY_ENABLE_PRETTY_PRINTER for (vm_instr_counter_t loc = 0; loc < bytecode_data_p->instrs_count; loc++) { op_meta opm; opm.op = bytecode_data_p->instrs_p[loc]; for (int i = 0; i < 3; i++) { opm.lit_id[i] = NOT_A_LITERAL; } pp_op_meta (bytecode_data_p, loc, opm, false); } #else (void) bytecode_data_p; #endif } void serializer_init () { current_scope = NULL; print_instrs = false; first_bytecode_header_p = NULL; lit_init (); } void serializer_set_show_instrs (bool show_instrs) { print_instrs = show_instrs; } /** * Deletes bytecode and associated hash table */ void serializer_remove_bytecode_data (const bytecode_data_header_t *bytecode_data_p) /**< pointer to bytecode data which * should be deleted */ { bytecode_data_header_t *prev_header = NULL; bytecode_data_header_t *cur_header_p = first_bytecode_header_p; while (cur_header_p != NULL) { if (cur_header_p == bytecode_data_p) { if (prev_header) { prev_header->next_header_cp = cur_header_p->next_header_cp; } else { first_bytecode_header_p = MEM_CP_GET_POINTER (bytecode_data_header_t, cur_header_p->next_header_cp); } mem_heap_free_block (cur_header_p); break; } prev_header = cur_header_p; } } /* serializer_remove_instructions */ void serializer_free (void) { lit_finalize (); while (first_bytecode_header_p != NULL) { bytecode_data_header_t *header_p = first_bytecode_header_p; first_bytecode_header_p = MEM_CP_GET_POINTER (bytecode_data_header_t, header_p->next_header_cp); mem_heap_free_block (header_p); } }