diff --git a/jerry-core/ecma/base/ecma-gc.cpp b/jerry-core/ecma/base/ecma-gc.cpp index 32641e060e..00fcf9b861 100644 --- a/jerry-core/ecma/base/ecma-gc.cpp +++ b/jerry-core/ecma/base/ecma-gc.cpp @@ -320,7 +320,8 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ case ECMA_INTERNAL_PROPERTY_PRIMITIVE_NUMBER_VALUE: /* compressed pointer to a ecma_number_t */ case ECMA_INTERNAL_PROPERTY_PRIMITIVE_BOOLEAN_VALUE: /* a simple boolean value */ case ECMA_INTERNAL_PROPERTY_CLASS: /* an enum */ - case ECMA_INTERNAL_PROPERTY_CODE: /* an integer */ + case ECMA_INTERNAL_PROPERTY_CODE_BYTECODE: /* compressed pointer to a bytecode array */ + case ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET: /* an integer */ case ECMA_INTERNAL_PROPERTY_NATIVE_CODE: /* an external pointer */ case ECMA_INTERNAL_PROPERTY_NATIVE_HANDLE: /* an external pointer */ case ECMA_INTERNAL_PROPERTY_FREE_CALLBACK: /* an object's native free callback */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 8aebcdf4dd..887fde5a63 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -217,7 +217,9 @@ typedef enum ECMA_INTERNAL_PROPERTY_EXTENSIBLE, /**< [[Extensible]] */ ECMA_INTERNAL_PROPERTY_SCOPE, /**< [[Scope]] */ ECMA_INTERNAL_PROPERTY_PARAMETERS_MAP, /**< [[ParametersMap]] */ - ECMA_INTERNAL_PROPERTY_CODE, /**< [[Code]] */ + ECMA_INTERNAL_PROPERTY_CODE_BYTECODE, /**< first part of [[Code]] - compressed pointer to bytecode array */ + ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET, /**< second part of [[Code]] - offset in bytecode array and code flags + * (see also: ecma_pack_code_internal_property_value) */ ECMA_INTERNAL_PROPERTY_NATIVE_CODE, /**< native handler location descriptor */ ECMA_INTERNAL_PROPERTY_NATIVE_HANDLE, /**< native handle associated with an object */ ECMA_INTERNAL_PROPERTY_FREE_CALLBACK, /**< object's native free callback */ diff --git a/jerry-core/ecma/base/ecma-helpers.cpp b/jerry-core/ecma/base/ecma-helpers.cpp index a4badbea4b..2db1512ddf 100644 --- a/jerry-core/ecma/base/ecma-helpers.cpp +++ b/jerry-core/ecma/base/ecma-helpers.cpp @@ -793,7 +793,8 @@ ecma_free_internal_property (ecma_property_t *property_p) /**< the property */ case ECMA_INTERNAL_PROPERTY_PROTOTYPE: /* the property's value is located in ecma_object_t */ case ECMA_INTERNAL_PROPERTY_EXTENSIBLE: /* the property's value is located in ecma_object_t */ case ECMA_INTERNAL_PROPERTY_CLASS: /* an enum */ - case ECMA_INTERNAL_PROPERTY_CODE: /* an integer */ + case ECMA_INTERNAL_PROPERTY_CODE_BYTECODE: /* compressed pointer to a bytecode array */ + case ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET: /* an integer */ case ECMA_INTERNAL_PROPERTY_BUILT_IN_ID: /* an integer */ case ECMA_INTERNAL_PROPERTY_BUILT_IN_ROUTINE_ID: /* an integer */ case ECMA_INTERNAL_PROPERTY_EXTENSION_ID: /* an integer */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-function.cpp index 65308d23e9..04fddbe5e5 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function.cpp @@ -14,16 +14,13 @@ */ #include "ecma-alloc.h" -#include "ecma-builtins.h" #include "ecma-conversion.h" -#include "ecma-exceptions.h" #include "ecma-gc.h" -#include "ecma-globals.h" -#include "ecma-helpers.h" -#include "ecma-objects.h" #include "ecma-function-object.h" +#include "ecma-lex-env.h" #include "ecma-try-catch-macro.h" -#include "jrt.h" +#include "serializer.h" +#include "parser.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" @@ -59,6 +56,9 @@ ecma_builtin_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< /** * Handle calling [[Construct]] of built-in Function object * + * See also: + * ECMA-262 v5, 15.3. + * * @return completion-value */ ecma_completion_value_t @@ -67,7 +67,127 @@ ecma_builtin_function_dispatch_construct (const ecma_value_t *arguments_list_p, { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); - ECMA_BUILTIN_CP_UNIMPLEMENTED (arguments_list_p, arguments_list_len); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + /* Last string, if any, is the function's body, and the rest, if any - are the function's parameter names */ + MEM_DEFINE_LOCAL_ARRAY (string_params_p, + arguments_list_len == 0 ? 1 : arguments_list_len, + ecma_string_t*); + uint32_t params_count; + + size_t zt_strings_buffer_size; + + if (arguments_list_len == 0) + { + /* 3. */ + string_params_p[0] = ecma_new_ecma_string_from_magic_string_id (ECMA_MAGIC_STRING__EMPTY); + zt_strings_buffer_size = sizeof (ecma_char_t); + params_count = 1; + } + else + { + /* 4., 5., 6. */ + zt_strings_buffer_size = 0; + + params_count = 0; + while (params_count < arguments_list_len + && ecma_is_completion_value_empty (ret_value)) + { + ECMA_TRY_CATCH (str_arg_value, + ecma_op_to_string (arguments_list_p[params_count]), + ret_value); + + string_params_p[params_count] = ecma_copy_or_ref_ecma_string (ecma_get_string_from_value (str_arg_value)); + zt_strings_buffer_size += ((size_t) ecma_string_get_length (string_params_p[params_count]) + + sizeof (ecma_char_t)); + params_count++; + + ECMA_FINALIZE (str_arg_value); + } + } + + if (ecma_is_completion_value_empty (ret_value)) + { + JERRY_ASSERT (params_count >= 1); + + MEM_DEFINE_LOCAL_ARRAY (zt_string_params_p, + params_count, + ecma_char_t*); + MEM_DEFINE_LOCAL_ARRAY (zt_string_buffer_p, + zt_strings_buffer_size, + ecma_char_t); + + ssize_t zt_string_buffer_pos = 0; + for (uint32_t i = 0; i < params_count; i++) + { + ssize_t sz = ecma_string_to_zt_string (string_params_p[i], + &zt_string_buffer_p[zt_string_buffer_pos], + (ssize_t) zt_strings_buffer_size - zt_string_buffer_pos); + JERRY_ASSERT (sz > 0); + + zt_string_params_p[i] = zt_string_buffer_p + zt_string_buffer_pos; + + zt_string_buffer_pos += sz; + } + + parser_init (); + + /* + * FIXME: + * Handle syntax errors + */ + parser_parse_new_function ((const char **) zt_string_params_p, params_count); + const opcode_t* opcodes_p = (const opcode_t*) serializer_get_bytecode (); + serializer_print_opcodes (); + parser_free (); + + bool is_strict = false; + bool do_instantiate_arguments_object = true; + + opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (opcodes_p, + 0); + + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_STRICT) + { + is_strict = true; + } + + if ((scope_flags & OPCODE_SCOPE_CODE_FLAGS_NOT_REF_ARGUMENTS_IDENTIFIER) + && (scope_flags & OPCODE_SCOPE_CODE_FLAGS_NOT_REF_EVAL_IDENTIFIER)) + { + /* the code doesn't use 'arguments' identifier + * and doesn't perform direct call to eval, + * so Arguments object can't be referenced */ + do_instantiate_arguments_object = false; + } + + /* 11. */ + ecma_object_t *glob_lex_env_p = ecma_get_global_environment (); + + ecma_object_t *func_obj_p = ecma_op_create_function_object (params_count > 1u ? string_params_p : NULL, + (ecma_length_t) (params_count - 1u), + glob_lex_env_p, + is_strict, + do_instantiate_arguments_object, + opcodes_p, + 1); + + ecma_deref_object (glob_lex_env_p); + + ret_value = ecma_make_normal_completion_value (ecma_make_object_value (func_obj_p)); + + MEM_FINALIZE_LOCAL_ARRAY (zt_string_buffer_p); + MEM_FINALIZE_LOCAL_ARRAY (zt_string_params_p); + } + + for (uint32_t i = 0; i < params_count; i++) + { + ecma_deref_ecma_string (string_params_p[i]); + } + + MEM_FINALIZE_LOCAL_ARRAY (string_params_p); + + return ret_value; } /* ecma_builtin_function_dispatch_construct */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp index e9017c6d97..f7d6db0ba1 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp @@ -16,11 +16,13 @@ #include "ecma-alloc.h" #include "ecma-builtins.h" #include "ecma-conversion.h" +#include "ecma-eval.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-try-catch-macro.h" #include "jrt.h" +#include "vm.h" #define ECMA_BUILTINS_INTERNAL #include "ecma-builtins-internal.h" @@ -52,7 +54,37 @@ static ecma_completion_value_t ecma_builtin_global_object_eval (ecma_value_t this_arg, /**< this argument */ ecma_value_t x) /**< routine's first argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, x); + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + bool is_direct_eval = vm_is_direct_eval_form_call (); + JERRY_ASSERT (!(is_direct_eval + && !ecma_is_value_undefined (this_arg))); + + /* See also: ECMA-262 v5, 10.1.1 */ + bool is_called_from_strict_mode_code; + if (is_direct_eval) + { + is_called_from_strict_mode_code = vm_is_strict_mode (); + } + else + { + is_called_from_strict_mode_code = false; + } + + if (!ecma_is_value_string (x)) + { + /* step 1 */ + ret_value = ecma_make_normal_completion_value (ecma_copy_value (x, true)); + } + else + { + /* steps 2 to 8 */ + ret_value = ecma_op_eval (ecma_get_string_from_value (x), + is_direct_eval, + is_called_from_strict_mode_code); + } + + return ret_value; } /* ecma_builtin_global_object_eval */ /** diff --git a/jerry-core/ecma/operations/ecma-eval.cpp b/jerry-core/ecma/operations/ecma-eval.cpp index 84846e9bc2..85c777204c 100644 --- a/jerry-core/ecma/operations/ecma-eval.cpp +++ b/jerry-core/ecma/operations/ecma-eval.cpp @@ -20,6 +20,8 @@ #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lex-env.h" +#include "parser.h" +#include "serializer.h" #include "vm.h" /** \addtogroup ecma ECMA @@ -29,10 +31,11 @@ */ /** - * eval + * Perform 'eval' with code stored in ecma-string * * See also: - * ECMA-262 v5, 15.1.2.1 + * ecma_op_eval_chars_buffer + * ECMA-262 v5, 15.1.2.1 (steps 2 to 8) * * @return completion value */ @@ -41,7 +44,7 @@ ecma_op_eval (ecma_string_t *code_p, /**< code string */ bool is_direct, /**< is eval called directly (ECMA-262 v5, 15.1.2.1.1) */ bool is_called_from_strict_mode_code) /**< is eval is called from strict mode code */ { - ecma_completion_value_t completion; + ecma_completion_value_t ret_value; int32_t chars_num = ecma_string_get_length (code_p); MEM_DEFINE_LOCAL_ARRAY (code_zt_buffer_p, @@ -54,15 +57,51 @@ ecma_op_eval (ecma_string_t *code_p, /**< code string */ buf_size); JERRY_ASSERT (buffer_size_req == buf_size); - // FIXME: Get parser feedback about syntax correctness - bool is_syntax_correct = true; - bool is_strict_prologue; + ret_value = ecma_op_eval_chars_buffer (code_zt_buffer_p, + (size_t) buf_size, + is_direct, + is_called_from_strict_mode_code); + + MEM_FINALIZE_LOCAL_ARRAY (code_zt_buffer_p); + + return ret_value; +} /* ecma_op_eval */ + +/** + * Perform 'eval' with code stored in continuous character buffer + * + * See also: + * ecma_op_eval + * ECMA-262 v5, 15.1.2.1 (steps 2 to 8) + * + * @return completion value + */ +ecma_completion_value_t +ecma_op_eval_chars_buffer (const ecma_char_t *code_p, /**< code characters buffer */ + size_t code_buffer_size, /**< size of the buffer */ + bool is_direct, /**< is eval called directly (ECMA-262 v5, 15.1.2.1.1) */ + bool is_called_from_strict_mode_code) /**< is eval is called from strict mode code */ +{ + JERRY_ASSERT (code_p != NULL); + + ecma_completion_value_t completion; - // FIXME: Call parser - JERRY_UNIMPLEMENTED ("eval operation is not implemented"); + parser_init (); + bool is_syntax_correct = parser_parse_eval ((const char *) code_p, code_buffer_size); + const opcode_t* opcodes_p = (const opcode_t*) serializer_get_bytecode (); + serializer_print_opcodes (); + parser_free (); + + opcode_counter_t first_opcode_index = 0u; + bool is_strict_prologue = false; + opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (opcodes_p, + first_opcode_index++); + if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_STRICT) + { + is_strict_prologue = true; + } - // FIXME: - is_strict_prologue = false; + bool is_strict = (is_strict_prologue || (is_direct && is_called_from_strict_mode_code)); if (!is_syntax_correct) { @@ -85,8 +124,7 @@ ecma_op_eval (ecma_string_t *code_p, /**< code string */ lex_env_p = ecma_get_global_environment (); } - if (is_strict_prologue - || (is_direct && is_called_from_strict_mode_code)) + if (is_strict) { ecma_object_t *strict_lex_env_p = ecma_create_decl_lex_env (lex_env_p); ecma_deref_object (lex_env_p); @@ -94,9 +132,12 @@ ecma_op_eval (ecma_string_t *code_p, /**< code string */ lex_env_p = strict_lex_env_p; } - // FIXME: Call interpreter - completion = ecma_make_return_completion_value (ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED)); - JERRY_UNIMPLEMENTED ("eval operation is not implemented"); + completion = vm_run_from_pos (opcodes_p, + first_opcode_index, + this_binding, + lex_env_p, + is_strict, + true); if (ecma_is_completion_value_return (completion)) { @@ -112,11 +153,8 @@ ecma_op_eval (ecma_string_t *code_p, /**< code string */ ecma_free_value (this_binding, true); } - - MEM_FINALIZE_LOCAL_ARRAY (code_zt_buffer_p); - return completion; -} /* ecma_op_eval */ +} /* ecma_op_eval_chars_buffer */ /** * @} diff --git a/jerry-core/ecma/operations/ecma-eval.h b/jerry-core/ecma/operations/ecma-eval.h index 31ac125c16..f1fdd3a667 100644 --- a/jerry-core/ecma/operations/ecma-eval.h +++ b/jerry-core/ecma/operations/ecma-eval.h @@ -29,6 +29,12 @@ ecma_op_eval (ecma_string_t *code_p, bool is_direct, bool is_called_from_strict_mode_code); +extern ecma_completion_value_t +ecma_op_eval_chars_buffer (const ecma_char_t *code_p, + size_t code_buffer_size, + bool is_direct, + bool is_called_from_strict_mode_code); + /** * @} * @} diff --git a/jerry-core/ecma/operations/ecma-function-object.cpp b/jerry-core/ecma/operations/ecma-function-object.cpp index b6f46424d5..ebbca81500 100644 --- a/jerry-core/ecma/operations/ecma-function-object.cpp +++ b/jerry-core/ecma/operations/ecma-function-object.cpp @@ -157,7 +157,8 @@ ecma_op_create_function_object (ecma_string_t* formal_parameter_list_p[], /**< f bool is_strict, /**< 'strict' flag */ bool do_instantiate_arguments_object, /**< should an Arguments object be instantiated * for the function object upon call */ - opcode_counter_t first_opcode_idx) /**< index of first opcode of function's body */ + const opcode_t *opcodes_p, /**< byte-code array */ + opcode_counter_t first_opcode_index) /**< index of first opcode of function's body */ { // 1., 4., 13. ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); @@ -199,10 +200,13 @@ ecma_op_create_function_object (ecma_string_t* formal_parameter_list_p[], /**< f } // 12. - ecma_property_t *code_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE); + ecma_property_t *opcodes_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); + MEM_CP_SET_NON_NULL_POINTER (opcodes_prop_p->u.internal_property.value, opcodes_p); + + ecma_property_t *code_prop_p = ecma_create_internal_property (f, ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); code_prop_p->u.internal_property.value = ecma_pack_code_internal_property_value (is_strict, do_instantiate_arguments_object, - first_opcode_idx); + first_opcode_index); // 14. ecma_number_t* len_p = ecma_alloc_number (); @@ -612,15 +616,18 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ { /* Entering Function Code (ECMA-262 v5, 10.4.3) */ ecma_property_t *scope_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_SCOPE); - ecma_property_t *code_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_CODE); + ecma_property_t *opcodes_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_CODE_BYTECODE); + ecma_property_t *code_prop_p = ecma_get_internal_property (func_obj_p, + ECMA_INTERNAL_PROPERTY_CODE_FLAGS_AND_OFFSET); ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, scope_prop_p->u.internal_property.value); uint32_t code_prop_value = code_prop_p->u.internal_property.value; + // 8. bool is_strict; bool do_instantiate_args_obj; - // 8. + const opcode_t *opcodes_p = MEM_CP_GET_POINTER (const opcode_t, opcodes_prop_p->u.internal_property.value); opcode_counter_t code_first_opcode_idx = ecma_unpack_code_internal_property_value (code_prop_value, &is_strict, &do_instantiate_args_obj); @@ -659,7 +666,8 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ do_instantiate_args_obj), ret_value); - ecma_completion_value_t completion = vm_run_from_pos (code_first_opcode_idx, + ecma_completion_value_t completion = vm_run_from_pos (opcodes_p, + code_first_opcode_idx, this_binding, local_env_p, is_strict, @@ -853,6 +861,7 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ ecma_completion_value_t ecma_op_function_declaration (ecma_object_t *lex_env_p, /**< lexical environment */ ecma_string_t *function_name_p, /**< function name */ + const opcode_t *opcodes_p, /**< byte-code array */ opcode_counter_t function_code_opcode_idx, /**< index of first opcode of function code */ ecma_string_t* formal_parameter_list_p[], /**< formal parameters list */ ecma_length_t formal_parameter_list_length, /**< length of formal parameters list */ @@ -869,6 +878,7 @@ ecma_op_function_declaration (ecma_object_t *lex_env_p, /**< lexical environment lex_env_p, is_strict, do_instantiate_arguments_object, + opcodes_p, function_code_opcode_idx); // c. diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index 7e230ac1be..726c705fd7 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -35,6 +35,7 @@ ecma_op_create_function_object (ecma_string_t* formal_parameter_list_p[], ecma_object_t *scope_p, bool is_strict, bool do_instantiate_arguments_object, + const opcode_t *opcodes_p, opcode_counter_t first_opcode_idx); extern ecma_object_t* ecma_op_create_external_function_object (ecma_external_pointer_t code_p); @@ -57,6 +58,7 @@ ecma_op_function_has_instance (ecma_object_t *func_obj_p, extern ecma_completion_value_t ecma_op_function_declaration (ecma_object_t *lex_env_p, ecma_string_t *function_name_p, + const opcode_t *opcodes_p, opcode_counter_t function_code_opcode_idx, ecma_string_t* formal_parameter_list_p[], ecma_length_t formal_parameter_list_length, diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index f152ef7362..0cc2e37cac 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -1071,12 +1071,12 @@ jerry_api_eval (const char *source_p, /**< source code */ { jerry_assert_api_available (); - ecma_string_t *code_p = ecma_new_ecma_string ((const ecma_char_t*) source_p); - (void) source_size; - jerry_completion_code_t status; - ecma_completion_value_t completion = ecma_op_eval (code_p, is_direct, is_strict); + ecma_completion_value_t completion = ecma_op_eval_chars_buffer ((const ecma_char_t*) source_p, + source_size, + is_direct, + is_strict); if (ecma_is_completion_value_normal (completion)) { @@ -1108,7 +1108,7 @@ jerry_api_eval (const char *source_p, /**< source code */ } } - ecma_deref_ecma_string (code_p); + ecma_free_completion_value (completion); return status; } /* jerry_api_eval */ @@ -1215,8 +1215,9 @@ jerry_parse (const char* source_p, /**< script source */ bool is_show_opcodes = ((jerry_flags & JERRY_FLAG_SHOW_OPCODES) != 0); - parser_init (source_p, source_size, is_show_opcodes); - parser_parse_program (); + parser_set_show_opcodes (is_show_opcodes); + parser_init (); + parser_parse_script (source_p, source_size); const opcode_t* opcodes = (const opcode_t*) serializer_get_bytecode (); diff --git a/jerry-core/parser/js/bytecode-data.h b/jerry-core/parser/js/bytecode-data.h index 7e2f881791..207be7ee9a 100644 --- a/jerry-core/parser/js/bytecode-data.h +++ b/jerry-core/parser/js/bytecode-data.h @@ -34,12 +34,30 @@ */ #define BLOCK_SIZE 64 +/** + * Pointer to lit_id_hash_table precedes every independent bytecode region + */ +typedef struct __attribute__ ((aligned (MEM_ALIGNMENT))) +{ + lit_id_hash_table *lit_id_hash; +} opcodes_header_t; + typedef struct { const ecma_char_t *strings_buffer; const opcode_t *opcodes; - lit_id_hash_table *lit_id_hash; opcode_counter_t opcodes_count; } bytecode_data_t; +/** + * Macros to get a hash table corresponding to a bytecode region + */ +#define GET_HASH_TABLE_FOR_BYTECODE(opcodes) (((opcodes_header_t *) (((uint8_t *) (opcodes)) - \ + sizeof (opcodes_header_t)))->lit_id_hash) + +/** + * Macros to get a pointer to bytecode header by pointer to opcodes start + */ +#define GET_BYTECODE_HEADER(opcodes) ((opcodes_header_t *) (((uint8_t *) (opcodes)) - sizeof (opcodes_header_t))) + #endif // BYTECODE_DATA_H diff --git a/jerry-core/parser/js/lexer.cpp b/jerry-core/parser/js/lexer.cpp index e5b110b532..2cafb0ae0b 100644 --- a/jerry-core/parser/js/lexer.cpp +++ b/jerry-core/parser/js/lexer.cpp @@ -1466,26 +1466,37 @@ lexer_are_tokens_with_same_identifier (token id1, /**< identifier token (TOK_NAM return (id1.uid == id2.uid); } /* lexer_are_tokens_with_same_identifier */ +/** + * Initialize lexer to start parsing of a new source + */ void -lexer_init (const char *source, size_t source_size, bool show_opcodes) +lexer_init_source (const char *source, /**< script source */ + size_t source_size) /**< script source size in bytes */ +{ + saved_token = prev_token = sent_token = empty_token; + + buffer_size = source_size; + lexer_set_source (source); + lexer_set_strict_mode (false); +} /* lexer_init_source */ + +/** + * Intitialize lexer + */ +void +lexer_init (bool show_opcodes) /**< flag indicating if to dump opcodes */ { empty_token.type = TOK_EMPTY; empty_token.uid = 0; empty_token.loc = 0; - saved_token = prev_token = sent_token = empty_token; - #ifndef JERRY_NDEBUG allow_dump_lines = show_opcodes; #else /* JERRY_NDEBUG */ (void) show_opcodes; allow_dump_lines = false; #endif /* JERRY_NDEBUG */ - - buffer_size = source_size; - lexer_set_source (source); - lexer_set_strict_mode (false); -} +} /* lexer_init */ void lexer_free (void) diff --git a/jerry-core/parser/js/lexer.h b/jerry-core/parser/js/lexer.h index 4f1caba8be..914ad96397 100644 --- a/jerry-core/parser/js/lexer.h +++ b/jerry-core/parser/js/lexer.h @@ -19,6 +19,7 @@ #include "lit-literal.h" #define INVALID_VALUE 255 +#define EVAL_RET_VALUE 128 #define INVALID_LITERAL (rcs_cpointer_t::null_cp ()) /* Keywords. */ @@ -168,7 +169,8 @@ typedef struct */ #define TOKEN_EMPTY_INITIALIZER {0, TOK_EMPTY, 0} -void lexer_init (const char *, size_t, bool); +void lexer_init (bool); +void lexer_init_source (const char *, size_t); void lexer_free (void); token lexer_next_token (void); diff --git a/jerry-core/parser/js/opcodes-dumper.cpp b/jerry-core/parser/js/opcodes-dumper.cpp index 5abb95d824..f515cfe988 100644 --- a/jerry-core/parser/js/opcodes-dumper.cpp +++ b/jerry-core/parser/js/opcodes-dumper.cpp @@ -695,6 +695,22 @@ literal_operand (lit_cpointer_t lit_cp) return ret; } +/** + * Creates operand for eval's return value + * + * @return constructed operand + */ +operand +eval_ret_operand (void) +{ + operand ret; + + ret.type = OPERAND_TMP; + ret.data.uid = EVAL_RET_VALUE; + + return ret; +} /* eval_ret_operand */ + bool operand_is_empty (operand op) { @@ -748,6 +764,27 @@ dump_intrinsic (operand obj, operand arg) return dump_undefined_assignment_res (); } +/** + * Check that byte-code operand refers to 'eval' string + * + * @return true - if specified byte-code operand's type is literal, and value of corresponding + * literal is equal to ECMA_MAGIC_STRING_EVAL string, + * false - otherwise. + */ +bool +dumper_is_eval_literal (operand obj) /**< byte-code operand */ +{ + /* + * FIXME: Switch to corresponding magic string + */ + const ecma_char_t *eval_string_p = (const ecma_char_t *) "eval"; + bool is_eval_lit = (obj.type == OPERAND_LITERAL + && lit_literal_equal_type_zt (lit_get_literal_by_cp (obj.data.lit_id), + eval_string_p)); + + return is_eval_lit; +} /* dumper_is_eval_literal */ + void dump_boolean_assignment (operand op, bool is_true) { @@ -1061,14 +1098,30 @@ rewrite_varg_header_set_args_count (uint8_t args_count) } } +/** + * Dump 'meta' instruction of 'call additional information' type, + * containing call flags and, optionally, 'this' argument + */ void -dump_this_arg (operand this_arg) +dump_call_additional_info (opcode_call_flags_t flags, /**< call flags */ + operand this_arg) /**< 'this' argument - if flags include OPCODE_CALL_FLAGS_HAVE_THIS_ARG, + * or empty operand - otherwise */ { - JERRY_ASSERT (this_arg.type == OPERAND_TMP); - const opcode_t opcode = getop_meta (OPCODE_META_TYPE_THIS_ARG, this_arg.data.uid, INVALID_VALUE); + if (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + JERRY_ASSERT (this_arg.type == OPERAND_TMP); + JERRY_ASSERT (!operand_is_empty (this_arg)); + } + else + { + JERRY_ASSERT (operand_is_empty (this_arg)); + } + + const opcode_t opcode = getop_meta (OPCODE_META_TYPE_CALL_SITE_INFO, + flags, + (flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) ? this_arg.data.uid : INVALID_VALUE); serializer_dump_op_meta (create_op_meta_000 (opcode)); - return; -} +} /* dump_call_additional_info */ void dump_varg (operand op) diff --git a/jerry-core/parser/js/opcodes-dumper.h b/jerry-core/parser/js/opcodes-dumper.h index bb483c90be..94719a6e73 100644 --- a/jerry-core/parser/js/opcodes-dumper.h +++ b/jerry-core/parser/js/opcodes-dumper.h @@ -48,6 +48,7 @@ typedef enum __attr_packed___ operand empty_operand (void); operand literal_operand (lit_cpointer_t); +operand eval_ret_operand (void); bool operand_is_empty (operand); void dumper_init (void); @@ -60,6 +61,8 @@ void dumper_finish_scope (void); bool dumper_is_intrinsic (operand); operand dump_intrinsic (operand, operand); +extern bool dumper_is_eval_literal (operand); + void dump_boolean_assignment (operand, bool); operand dump_boolean_assignment_res (bool); void dump_string_assignment (operand, lit_cpointer_t); @@ -77,7 +80,7 @@ operand dump_variable_assignment_res (operand); void dump_varg_header_for_rewrite (varg_list_type, operand); operand rewrite_varg_header_set_args_count (uint8_t); -void dump_this_arg (operand); +void dump_call_additional_info (opcode_call_flags_t, operand); void dump_varg (operand); void dump_prop_name_and_value (operand, operand); diff --git a/jerry-core/parser/js/parser.cpp b/jerry-core/parser/js/parser.cpp index 2e40fc6f66..011ef6d3c4 100644 --- a/jerry-core/parser/js/parser.cpp +++ b/jerry-core/parser/js/parser.cpp @@ -30,7 +30,27 @@ #include "opcodes-dumper.h" #include "serializer.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 inside_eval = false; +static bool inside_function = false; +static bool parser_show_opcodes = false; enum { @@ -44,7 +64,7 @@ STATIC_STACK (scopes, scopes_tree) #define OPCODE_IS(OP, ID) (OP.op_idx == __op__idx_##ID) -static operand parse_expression (bool); +static operand parse_expression (bool, jsp_eval_ret_store_t); static void parse_statement (jsp_label_t *outermost_stmt_label_p); static operand parse_assignment_expression (bool); static void parse_source_element_list (bool); @@ -259,6 +279,9 @@ parse_property_assignment (void) token_after_newlines_must_be (TOK_OPEN_BRACE); skip_newlines (); + bool was_in_function = inside_function; + inside_function = true; + jsp_label_t *masked_label_set_p = jsp_label_mask_set (); parse_source_element_list (false); @@ -271,6 +294,9 @@ parse_property_assignment (void) dump_ret (); rewrite_function_end (VARG_FUNC_EXPR); + + inside_function = was_in_function; + if (is_setter) { dump_prop_setter_decl (name, func); @@ -290,11 +316,13 @@ parse_property_assignment (void) For each ALT dumps appropriate bytecode. Uses OBJ during dump if neccesary. Result tmp. */ static operand -parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, operand *this_arg) +parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, operand *this_arg_p) { token_type close_tt = TOK_CLOSE_PAREN; uint8_t args_num = 0; + JERRY_ASSERT (!(vlt != VARG_CALL_EXPR && this_arg_p != NULL)); + switch (vlt) { case VARG_FUNC_DECL: @@ -316,11 +344,58 @@ parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, opera { break; } - if (this_arg != NULL && this_arg->type == OPERAND_LITERAL) + + opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; + + operand this_arg = empty_operand (); + if (this_arg_p != NULL + && !operand_is_empty (*this_arg_p)) { - *this_arg = dump_variable_assignment_res (*this_arg); + call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_HAVE_THIS_ARG); + + if (this_arg_p->type == OPERAND_LITERAL) + { + /* + * FIXME: + * Base of CallExpression should be evaluated only once during evaluation of CallExpression + * + * See also: + * Evaluation of MemberExpression (ECMA-262 v5, 11.2.1) + */ + this_arg = dump_variable_assignment_res (*this_arg_p); + } + else + { + this_arg = *this_arg_p; + } + + /* + * Presence of explicit 'this' argument implies that it is not Direct call to Eval + * + * See also: + * ECMA-262 v5, 15.2.2.1 + */ } + else if (dumper_is_eval_literal (obj)) + { + call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM); + } + dump_varg_header_for_rewrite (vlt, obj); + + if (call_flags != OPCODE_CALL_FLAGS__EMPTY) + { + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + JERRY_ASSERT (!operand_is_empty (this_arg)); + dump_call_additional_info (call_flags, this_arg); + } + else + { + dump_call_additional_info (call_flags, empty_operand ()); + } + } + break; } case VARG_ARRAY_DECL: @@ -339,11 +414,6 @@ parse_argument_list (varg_list_type vlt, operand obj, uint8_t *args_count, opera break; } } - if (vlt == VARG_CALL_EXPR && this_arg != NULL && !operand_is_empty (*this_arg)) - { - dump_this_arg (*this_arg); - args_num++; - } skip_newlines (); while (!token_is (close_tt)) @@ -474,9 +544,11 @@ parse_function_declaration (void) dump_function_end_for_rewrite (); token_after_newlines_must_be (TOK_OPEN_BRACE); - skip_newlines (); + bool was_in_function = inside_function; + inside_function = true; + parse_source_element_list (false); next_token_must_be (TOK_CLOSE_BRACE); @@ -484,6 +556,8 @@ parse_function_declaration (void) dump_ret (); rewrite_function_end (VARG_FUNC_DECL); + inside_function = was_in_function; + STACK_DROP (scopes, 1); serializer_set_scope (STACK_TOP (scopes)); lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes))); @@ -522,6 +596,9 @@ parse_function_expression (void) token_after_newlines_must_be (TOK_OPEN_BRACE); skip_newlines (); + bool was_in_function = inside_function; + inside_function = true; + jsp_label_t *masked_label_set_p = jsp_label_mask_set (); parse_source_element_list (false); @@ -534,6 +611,8 @@ parse_function_expression (void) dump_ret (); rewrite_function_end (VARG_FUNC_EXPR); + inside_function = was_in_function; + return res; } @@ -611,7 +690,7 @@ parse_primary_expression (void) skip_newlines (); if (!token_is (TOK_CLOSE_PAREN)) { - operand res = parse_expression (true); + operand res = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); token_after_newlines_must_be (TOK_CLOSE_PAREN); return res; } @@ -683,7 +762,7 @@ parse_member_expression (operand *this_arg, operand *prop_gl) if (token_is (TOK_OPEN_SQUARE)) { skip_newlines (); - prop = parse_expression (true); + prop = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); next_token_must_be (TOK_CLOSE_SQUARE); } else if (token_is (TOK_DOT)) @@ -774,7 +853,7 @@ parse_call_expression (operand *this_arg_gl, operand *prop_gl) if (tok.type == TOK_OPEN_SQUARE) { skip_newlines (); - prop = parse_expression (true); + prop = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); next_token_must_be (TOK_CLOSE_SQUARE); } else if (tok.type == TOK_DOT) @@ -1532,12 +1611,19 @@ parse_assignment_expression (bool in_allowed) return expr; } -/* expression - : assignment_expression (LT!* ',' LT!* assignment_expression)* - ; +/** + * Parse an expression + * + * expression + * : assignment_expression (LT!* ',' LT!* assignment_expression)* + * ; + * + * @return operand which holds result of expression */ static operand -parse_expression (bool in_allowed) +parse_expression (bool in_allowed, /**< flag indicating if 'in' is allowed inside expression to parse */ + jsp_eval_ret_store_t dump_eval_ret_store) /**< flag indicating if 'eval' + * result code store should be dumped */ { operand expr = parse_assignment_expression (in_allowed); @@ -1555,8 +1641,16 @@ parse_expression (bool in_allowed) break; } } + + if (inside_eval + && dump_eval_ret_store == JSP_EVAL_RET_STORE_DUMP + && !inside_function) + { + dump_variable_assignment (eval_ret_operand () , expr); + } + return expr; -} +} /* parse_expression */ /* variable_declaration : Identifier LT!* initialiser? @@ -1646,7 +1740,7 @@ parse_plain_for (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab skip_token (); if (!token_is (TOK_CLOSE_PAREN)) { - parse_expression (true); + parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); } rewrite_jump_to_end (); @@ -1659,7 +1753,7 @@ parse_plain_for (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab } else { - const operand cond = parse_expression (true); + const operand cond = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); dump_continue_iterations_check (cond); } @@ -1749,7 +1843,7 @@ parse_for_or_for_in_statement (jsp_label_t *outermost_stmt_label_p) /**< outermo } /* expression contains left_hand_side_expression. */ - parse_expression (false); + parse_expression (false, JSP_EVAL_RET_STORE_NOT_DUMP); skip_newlines (); if (token_is (TOK_SEMICOLON)) @@ -1773,7 +1867,7 @@ parse_expression_inside_parens (void) { token_after_newlines_must_be (TOK_OPEN_PAREN); skip_newlines (); - const operand res = parse_expression (true); + const operand res = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); token_after_newlines_must_be (TOK_CLOSE_PAREN); return res; } @@ -1963,7 +2057,7 @@ parse_switch_statement (void) if (is_keyword (KW_CASE)) { skip_newlines (); - const operand case_expr = parse_expression (true); + const operand case_expr = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); next_token_must_be (TOK_COLON); dump_case_clause_check_for_rewrite (switch_expr, case_expr); skip_newlines (); @@ -2358,7 +2452,7 @@ parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab skip_token (); if (!token_is (TOK_SEMICOLON) && !token_is (TOK_NEWLINE)) { - const operand op = parse_expression (true); + const operand op = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); dump_retval (op); insert_semicolon (); return; @@ -2382,7 +2476,7 @@ parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab if (is_keyword (KW_THROW)) { skip_token (); - const operand op = parse_expression (true); + const operand op = parse_expression (true, JSP_EVAL_RET_STORE_NOT_DUMP); insert_semicolon (); dump_throw (op); return; @@ -2418,7 +2512,7 @@ parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab { lexer_save_token (tok); tok = temp; - parse_expression (true); + parse_expression (true, JSP_EVAL_RET_STORE_DUMP); skip_newlines (); if (!token_is (TOK_SEMICOLON)) { @@ -2429,7 +2523,7 @@ parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) lab } else { - parse_expression (true); + parse_expression (true, JSP_EVAL_RET_STORE_DUMP); skip_newlines (); if (!token_is (TOK_SEMICOLON)) { @@ -2748,15 +2842,25 @@ preparse_scope (bool is_global) } } -/* source_element_list - : source_element (LT!* source_element)* - ; */ +/** + * Parse source element list + * + * source_element_list + * : source_element (LT!* source_element)* + * ; + */ static void -parse_source_element_list (bool is_global) +parse_source_element_list (bool is_global) /**< flag indicating if we are parsing the global scope */ { dumper_new_scope (); preparse_scope (is_global); + if (inside_eval + && !inside_function) + { + dump_undefined_assignment (eval_ret_operand ()); + } + skip_newlines (); while (!token_is (TOK_EOF) && !token_is (TOK_CLOSE_BRACE)) { @@ -2766,14 +2870,22 @@ parse_source_element_list (bool is_global) lexer_save_token (tok); rewrite_reg_var_decl (); dumper_finish_scope (); -} +} /* parse_source_element_list */ -/* program - : LT!* source_element_list LT!* EOF! - ; */ -void -parser_parse_program (void) +/** + * Parse program + * + * program + * : LT!* source_element_list LT!* EOF! + * ; + */ +static void +parser_parse_program (const char *source, /**< source code buffer */ + size_t source_size, /**< source code size in bytes */ + bool in_new_function) /**< flag indicating if we are parsing body of a function */ { + lexer_init_source (source, source_size); + STACK_PUSH (scopes, scopes_tree_init (NULL)); serializer_set_scope (STACK_TOP (scopes)); lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes))); @@ -2783,7 +2895,19 @@ parser_parse_program (void) skip_newlines (); JERRY_ASSERT (token_is (TOK_EOF)); - dump_exit (); + + if (in_new_function) + { + dump_ret (); + } + else if (inside_eval) + { + dump_retval (eval_ret_operand ()); + } + else + { + dump_exit (); + } serializer_dump_literals (); serializer_merge_scopes_into_bytecode (); @@ -2791,13 +2915,66 @@ parser_parse_program (void) scopes_tree_free (STACK_TOP (scopes)); STACK_DROP (scopes, 1); -} +} /* parser_parse_program */ +/** + * Parse source script + */ void -parser_init (const char *source, size_t source_size, bool show_opcodes) +parser_parse_script (const char *source, /**< source script */ + size_t source_size) /**< source script size it bytes */ +{ + inside_eval = false; + inside_function = false; + parser_parse_program (source, source_size, false); +} /* parser_parse_script */ + +/** + * Parse string passed to eval() call + * + * @return true if parse succeeds + * false otherwise + */ +bool parser_parse_eval (const char *source, /**< string passed to eval() */ + size_t source_size) /**< string size in bytes */ { - lexer_init (source, source_size, show_opcodes); - serializer_set_show_opcodes (show_opcodes); + TODO (implement syntax error processing); + + inside_eval = true; + inside_function = false; + parser_parse_program (source, source_size, false); + return true; +} /* parser_parse_eval */ + +/** + * Parse a function created via new Function call + * + * NOTE: Array of arguments should contain at least one element. + * In case of new Function() call without parameters, empty string should be passed as argument to this + * function. + */ +void +parser_parse_new_function (const char **params, /**< array of arguments of new Function (p1, p2, ..., pn, body) call */ + size_t params_count) /**< total number of arguments passed to new Function (...) */ +{ + inside_eval = false; + inside_function = true; + + // Process arguments + JERRY_ASSERT (params_count > 0); + for (size_t i = 0; i < params_count - 1; ++i) + { + FIXME ("check parameter's name for syntax errors"); + lit_find_or_create_literal_from_charset ((ecma_char_t *) params[i], (ecma_length_t) strlen (params[i])); + } + parser_parse_program (params[params_count - 1], strlen (params[params_count - 1]), true); +} /* parser_parse_new_function */ + +void +parser_init () +{ + lexer_init (parser_show_opcodes); + serializer_set_show_opcodes (parser_show_opcodes); dumper_init (); syntax_init (); @@ -2806,6 +2983,15 @@ parser_init (const char *source, size_t source_size, bool show_opcodes) jsp_label_init (); } +/** + * Tell parser to dump bytecode + */ +void +parser_set_show_opcodes (bool show_opcodes) /**< flag indicating if to dump bytecode */ +{ + parser_show_opcodes = show_opcodes; +} /* parser_set_show_opcodes */ + void parser_free (void) { diff --git a/jerry-core/parser/js/parser.h b/jerry-core/parser/js/parser.h index c0cb0ec7b2..4792d96603 100644 --- a/jerry-core/parser/js/parser.h +++ b/jerry-core/parser/js/parser.h @@ -18,8 +18,11 @@ #include "jrt.h" -void parser_init (const char *, size_t, bool); -void parser_parse_program (void); +void parser_init (); +void parser_set_show_opcodes (bool); +void parser_parse_script (const char *, size_t); +bool parser_parse_eval (const char *, size_t); +void parser_parse_new_function (const char **, size_t); void parser_free (void); #endif diff --git a/jerry-core/parser/js/scopes-tree.cpp b/jerry-core/parser/js/scopes-tree.cpp index ba0cfbee28..5700c2b0b5 100644 --- a/jerry-core/parser/js/scopes-tree.cpp +++ b/jerry-core/parser/js/scopes-tree.cpp @@ -338,7 +338,6 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table change_uid (om, lit_ids, 0x011); break; } - case OPCODE_META_TYPE_THIS_ARG: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { @@ -351,6 +350,7 @@ generate_opcode (scopes_tree tree, opcode_counter_t opc_index, lit_id_hash_table case OPCODE_META_TYPE_CATCH: case OPCODE_META_TYPE_FINALLY: case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { change_uid (om, lit_ids, 0x000); @@ -477,7 +477,6 @@ count_new_literals_in_opcode (scopes_tree tree, opcode_counter_t opc_index) insert_uids_to_lit_id_map (om, 0x011); break; } - case OPCODE_META_TYPE_THIS_ARG: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { @@ -490,6 +489,7 @@ count_new_literals_in_opcode (scopes_tree tree, opcode_counter_t opc_index) case OPCODE_META_TYPE_CATCH: case OPCODE_META_TYPE_FINALLY: case OPCODE_META_TYPE_END_TRY_CATCH_FINALLY: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: { insert_uids_to_lit_id_map (om, 0x000); @@ -613,15 +613,18 @@ scopes_tree_raw_data (scopes_tree tree, lit_id_hash_table *lit_ids) global_oc = 0; /* Dump bytecode and fill literal indexes 'hash' table. */ - size_t size = ((size_t) (scopes_tree_count_opcodes (tree) + 1) * sizeof (opcode_t)); // +1 for valgrind - opcode_t *opcodes = (opcode_t *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); - memset (opcodes, 0, size); + // +1 for valgrind + size_t size = sizeof (opcodes_header_t) + (size_t) (scopes_tree_count_opcodes (tree) + 1) * sizeof (opcode_t); + opcodes_header_t *opcodes_data = (opcodes_header_t *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_LONG_TERM); + memset (opcodes_data, 0, size); + opcode_t *opcodes = (opcode_t *)(((uint8_t*) opcodes_data) + sizeof (opcodes_header_t)); merge_subscopes (tree, opcodes, lit_ids); if (lit_id_to_uid != null_hash) { hash_table_free (lit_id_to_uid); lit_id_to_uid = null_hash; } + opcodes_data->lit_id_hash = lit_ids; return opcodes; } diff --git a/jerry-core/parser/js/serializer.cpp b/jerry-core/parser/js/serializer.cpp index 06bb853593..21b4bda1e9 100644 --- a/jerry-core/parser/js/serializer.cpp +++ b/jerry-core/parser/js/serializer.cpp @@ -16,10 +16,11 @@ #include "serializer.h" #include "bytecode-data.h" #include "pretty-printer.h" +#include "array-list.h" static bytecode_data_t bytecode_data; static scopes_tree current_scope; - +static array_list bytecodes_cache; /**< storage of pointers to byetecodes */ static bool print_opcodes; op_meta @@ -40,15 +41,27 @@ serializer_get_opcode (opcode_counter_t oc) return bytecode_data.opcodes[oc]; } +/** + * 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, opcode_counter_t oc) +serializer_get_literal_cp_by_uid (uint8_t id, /**< literal idx */ + const opcode_t *opcodes_p, /**< pointer to bytecode */ + opcode_counter_t oc) /**< position in the bytecode */ { - if (bytecode_data.lit_id_hash == null_hash) + lit_id_hash_table *lit_id_hash = GET_HASH_TABLE_FOR_BYTECODE (opcodes_p == NULL ? bytecode_data.opcodes : opcodes_p); + if (lit_id_hash == null_hash) { return INVALID_LITERAL; } - return lit_id_hash_table_lookup (bytecode_data.lit_id_hash, id, oc); -} + return lit_id_hash_table_lookup (lit_id_hash, id, oc); +} /* serializer_get_literal_cp_by_uid */ const void * serializer_get_bytecode (void) @@ -72,11 +85,11 @@ serializer_set_scope (scopes_tree new_scope) void serializer_merge_scopes_into_bytecode (void) { - JERRY_ASSERT (bytecode_data.lit_id_hash == null_hash); bytecode_data.opcodes_count = scopes_tree_count_opcodes (current_scope); - bytecode_data.lit_id_hash = lit_id_hash_table_init (scopes_tree_count_literals_in_blocks (current_scope), - bytecode_data.opcodes_count); - bytecode_data.opcodes = scopes_tree_raw_data (current_scope, bytecode_data.lit_id_hash); + lit_id_hash_table *lit_id_hash = lit_id_hash_table_init (scopes_tree_count_literals_in_blocks (current_scope), + (size_t) bytecode_data.opcodes_count / BLOCK_SIZE + 1); + bytecode_data.opcodes = scopes_tree_raw_data (current_scope, lit_id_hash); + bytecodes_cache = array_list_append (bytecodes_cache, &bytecode_data.opcodes); } void @@ -172,9 +185,10 @@ serializer_init () bytecode_data.strings_buffer = NULL; bytecode_data.opcodes = NULL; - bytecode_data.lit_id_hash = null_hash; lit_init (); + + bytecodes_cache = array_list_init (sizeof (opcode_t *)); } void serializer_set_show_opcodes (bool show_opcodes) @@ -189,12 +203,13 @@ serializer_free (void) { mem_heap_free_block ((uint8_t *) bytecode_data.strings_buffer); } - if (bytecode_data.lit_id_hash != null_hash) - { - lit_id_hash_table_free (bytecode_data.lit_id_hash); - } - - mem_heap_free_block ((uint8_t *) bytecode_data.opcodes); lit_finalize (); + + for (size_t i = 0; i < array_list_len (bytecodes_cache); ++i) + { + lit_id_hash_table_free (GET_BYTECODE_HEADER (*(opcode_t **) array_list_element (bytecodes_cache, i))->lit_id_hash); + mem_heap_free_block (GET_BYTECODE_HEADER (*(opcode_t **) array_list_element (bytecodes_cache, i))); + } + array_list_free (bytecodes_cache); } diff --git a/jerry-core/parser/js/serializer.h b/jerry-core/parser/js/serializer.h index d12774a7f4..b62766ecae 100644 --- a/jerry-core/parser/js/serializer.h +++ b/jerry-core/parser/js/serializer.h @@ -26,7 +26,7 @@ void serializer_init (); void serializer_set_show_opcodes (bool show_opcodes); op_meta serializer_get_op_meta (opcode_counter_t); opcode_t serializer_get_opcode (opcode_counter_t); -lit_cpointer_t serializer_get_literal_cp_by_uid (uint8_t, opcode_counter_t); +lit_cpointer_t serializer_get_literal_cp_by_uid (uint8_t, const opcode_t*, opcode_counter_t); const void *serializer_get_bytecode (void); void serializer_set_strings_buffer (const ecma_char_t *); void serializer_dump_literals (); diff --git a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp b/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp index 1d8d008fa5..68874c294c 100644 --- a/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp +++ b/jerry-core/vm/opcodes-ecma-try-catch-finally.cpp @@ -43,7 +43,7 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ || (ecma_is_completion_value_empty (try_completion) && int_data->pos == try_end_oc)); int_data->pos = try_end_oc; - opcode_t next_opcode = vm_get_opcode (int_data->pos); + opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); if (ecma_is_completion_value_exit (try_completion)) @@ -59,13 +59,13 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ if (ecma_is_completion_value_throw (try_completion)) { - next_opcode = vm_get_opcode (int_data->pos); + next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); JERRY_ASSERT (next_opcode.data.meta.type == OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER); - lit_cpointer_t catch_exc_val_var_name_lit_cp = serializer_get_literal_cp_by_uid ( - next_opcode.data.meta.data_1, - int_data->pos); + lit_cpointer_t catch_exc_val_var_name_lit_cp = serializer_get_literal_cp_by_uid (next_opcode.data.meta.data_1, + int_data->opcodes_p, + int_data->pos); int_data->pos++; ecma_string_t *catch_exc_var_name_str_p = ecma_new_ecma_string_from_lit_cp (catch_exc_val_var_name_lit_cp); @@ -103,7 +103,7 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ int_data->pos = catch_end_oc; } - next_opcode = vm_get_opcode (int_data->pos); + next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); if (ecma_is_completion_value_exit (try_completion)) @@ -131,7 +131,7 @@ opfunc_try_block (opcode_t opdata, /**< operation data */ } } - next_opcode = vm_get_opcode (int_data->pos++); + next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos++); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); JERRY_ASSERT (next_opcode.data.meta.type == OPCODE_META_TYPE_END_TRY_CATCH_FINALLY); diff --git a/jerry-core/vm/opcodes-helpers-variables.cpp b/jerry-core/vm/opcodes-helpers-variables.cpp index 91462de166..e6d754866f 100644 --- a/jerry-core/vm/opcodes-helpers-variables.cpp +++ b/jerry-core/vm/opcodes-helpers-variables.cpp @@ -91,7 +91,7 @@ get_variable_value (int_data_t *int_data, /**< interpreter context */ else { ecma_string_t var_name_string; - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, int_data->opcodes_p, int_data->pos); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); ecma_new_ecma_string_on_stack_from_lit_cp (&var_name_string, lit_cp); @@ -158,7 +158,7 @@ set_variable_value (int_data_t *int_data, /**< interpreter context */ else { ecma_string_t var_name_string; - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, lit_oc); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, int_data->opcodes_p, lit_oc); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); ecma_new_ecma_string_on_stack_from_lit_cp (&var_name_string, lit_cp); diff --git a/jerry-core/vm/opcodes-varg.cpp b/jerry-core/vm/opcodes-varg.cpp index 00447ae3af..8fd0f7f803 100644 --- a/jerry-core/vm/opcodes-varg.cpp +++ b/jerry-core/vm/opcodes-varg.cpp @@ -44,7 +44,7 @@ fill_varg_list (int_data_t *int_data, /**< interpreter context */ if (ecma_is_completion_value_normal (evaluate_arg_completion)) { - opcode_t next_opcode = vm_get_opcode (int_data->pos); + opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); JERRY_ASSERT (next_opcode.data.meta.type == OPCODE_META_TYPE_VARG); @@ -92,11 +92,12 @@ fill_params_list (int_data_t *int_data, /**< interpreter context */ param_index < params_number; param_index++) { - opcode_t next_opcode = vm_get_opcode (int_data->pos); + opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); JERRY_ASSERT (next_opcode.data.meta.type == OPCODE_META_TYPE_VARG); const lit_cpointer_t param_name_lit_idx = serializer_get_literal_cp_by_uid (next_opcode.data.meta.data_1, + int_data->opcodes_p, int_data->pos); params_names[param_index] = ecma_new_ecma_string_from_lit_cp (param_name_lit_idx); diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index 150cb6cec9..04e04948fa 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -96,7 +96,7 @@ opfunc_assignment (opcode_t opdata, /**< operation data */ } else if (type_value_right == OPCODE_ARG_TYPE_STRING) { - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->opcodes_p, int_data->pos); ecma_string_t *string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); ret_value = set_variable_value (int_data, @@ -125,7 +125,7 @@ opfunc_assignment (opcode_t opdata, /**< operation data */ { ecma_number_t *num_p = int_data->tmp_num_p; - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->opcodes_p, int_data->pos); literal_t lit = lit_get_literal_by_cp (lit_cp); JERRY_ASSERT (lit->get_type () == LIT_NUMBER_T); @@ -140,7 +140,7 @@ opfunc_assignment (opcode_t opdata, /**< operation data */ { ecma_number_t *num_p = int_data->tmp_num_p; - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (src_val_descr, int_data->opcodes_p, int_data->pos); literal_t lit = lit_get_literal_by_cp (lit_cp); JERRY_ASSERT (lit->get_type () == LIT_NUMBER_T); @@ -395,7 +395,9 @@ ecma_completion_value_t opfunc_var_decl (opcode_t opdata, /**< operation data */ int_data_t *int_data) /**< interpreter context */ { - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (opdata.data.var_decl.variable_name, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (opdata.data.var_decl.variable_name, + int_data->opcodes_p, + int_data->pos); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); @@ -446,7 +448,7 @@ function_declaration (int_data_t *int_data, /**< interpreter context */ read_meta_opcode_counter (OPCODE_META_TYPE_FUNCTION_END, int_data) + int_data->pos); int_data->pos++; - opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (int_data->pos++); + opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (int_data->opcodes_p, int_data->pos++); if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_STRICT) { @@ -465,6 +467,7 @@ function_declaration (int_data_t *int_data, /**< interpreter context */ ecma_completion_value_t ret_value = ecma_op_function_declaration (int_data->lex_env_p, function_name_string_p, + int_data->opcodes_p, int_data->pos, args_names, args_number, @@ -491,7 +494,9 @@ opfunc_func_decl_n (opcode_t opdata, /**< operation data */ const idx_t function_name_idx = opdata.data.func_decl_n.name_lit_idx; const ecma_length_t params_number = opdata.data.func_decl_n.arg_list; - lit_cpointer_t function_name_lit_cp = serializer_get_literal_cp_by_uid (function_name_idx, int_data->pos); + lit_cpointer_t function_name_lit_cp = serializer_get_literal_cp_by_uid (function_name_idx, + int_data->opcodes_p, + int_data->pos); int_data->pos++; @@ -552,7 +557,8 @@ opfunc_func_expr_n (opcode_t opdata, /**< operation data */ int_data) + int_data->pos); int_data->pos++; - opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (int_data->pos++); + opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (int_data->opcodes_p, + int_data->pos++); if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_STRICT) { @@ -573,7 +579,9 @@ opfunc_func_expr_n (opcode_t opdata, /**< operation data */ { scope_p = ecma_create_decl_lex_env (int_data->lex_env_p); - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (function_name_lit_idx, lit_oc); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (function_name_lit_idx, + int_data->opcodes_p, + lit_oc); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); function_name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); @@ -590,6 +598,7 @@ opfunc_func_expr_n (opcode_t opdata, /**< operation data */ scope_p, is_strict, do_instantiate_arguments_object, + int_data->opcodes_p, int_data->pos); ret_value = set_variable_value (int_data, lit_oc, @@ -644,44 +653,44 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ int_data->pos++; - bool this_arg_var_idx_set = false; idx_t this_arg_var_idx = INVALID_VALUE; - idx_t args_number; - opcode_t next_opcode = vm_get_opcode (int_data->pos); + opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY; + + JERRY_ASSERT (!int_data->is_call_in_direct_eval_form); + + opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); if (next_opcode.op_idx == __op__idx_meta - && next_opcode.data.meta.type == OPCODE_META_TYPE_THIS_ARG) + && next_opcode.data.meta.type == OPCODE_META_TYPE_CALL_SITE_INFO) { - this_arg_var_idx = next_opcode.data.meta.data_1; - JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); + call_flags = (opcode_call_flags_t) next_opcode.data.meta.data_1; - this_arg_var_idx_set = true; + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + this_arg_var_idx = next_opcode.data.meta.data_2; + JERRY_ASSERT (is_reg_variable (int_data, this_arg_var_idx)); - JERRY_ASSERT (args_number_idx > 0); - args_number = (idx_t) (args_number_idx - 1); + JERRY_ASSERT ((call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) == 0); + } int_data->pos++; } - else - { - args_number = args_number_idx; - } - MEM_DEFINE_LOCAL_ARRAY (arg_values, args_number, ecma_value_t); + MEM_DEFINE_LOCAL_ARRAY (arg_values, args_number_idx, ecma_value_t); ecma_length_t args_read; ecma_completion_value_t get_arg_completion = fill_varg_list (int_data, - args_number, + args_number_idx, arg_values, &args_read); if (ecma_is_completion_value_empty (get_arg_completion)) { - JERRY_ASSERT (args_read == args_number); + JERRY_ASSERT (args_read == args_number_idx); ecma_completion_value_t get_this_completion_value; - if (this_arg_var_idx_set) + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) { get_this_completion_value = get_variable_value (int_data, this_arg_var_idx, false); } @@ -699,13 +708,18 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ } else { + if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) + { + int_data->is_call_in_direct_eval_form = true; + } + ecma_object_t *func_obj_p = ecma_get_object_from_value (func_value); ECMA_TRY_CATCH (call_ret_value, ecma_op_function_call (func_obj_p, this_value, arg_values, - args_number), + args_number_idx), ret_value); ret_value = set_variable_value (int_data, lit_oc, @@ -714,6 +728,15 @@ opfunc_call_n (opcode_t opdata, /**< operation data */ ECMA_FINALIZE (call_ret_value); + if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) + { + JERRY_ASSERT (int_data->is_call_in_direct_eval_form); + int_data->is_call_in_direct_eval_form = false; + } + else + { + JERRY_ASSERT (!int_data->is_call_in_direct_eval_form); + } } ecma_free_completion_value (get_this_completion_value); @@ -912,7 +935,7 @@ opfunc_obj_decl (opcode_t opdata, /**< operation data */ { JERRY_ASSERT (ecma_is_completion_value_empty (evaluate_prop_completion)); - opcode_t next_opcode = vm_get_opcode (int_data->pos); + opcode_t next_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (next_opcode.op_idx == __op__idx_meta); const opcode_meta_type type = (opcode_meta_type) next_opcode.data.meta.type; @@ -1320,7 +1343,7 @@ opfunc_with (opcode_t opdata, /**< operation data */ int_data->lex_env_p = new_env_p; #ifndef JERRY_NDEBUG - opcode_t meta_opcode = vm_get_opcode (with_end_oc); + opcode_t meta_opcode = vm_get_opcode (int_data->opcodes_p, with_end_oc); JERRY_ASSERT (meta_opcode.op_idx == __op__idx_meta); JERRY_ASSERT (meta_opcode.data.meta.type == OPCODE_META_TYPE_END_WITH); #endif /* !JERRY_NDEBUG */ @@ -1407,7 +1430,7 @@ evaluate_arg_for_typeof (int_data_t *int_data, /**< interpreter context */ } else { - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, int_data->pos); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (var_idx, int_data->opcodes_p, int_data->pos); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); ecma_string_t *var_name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); @@ -1523,7 +1546,7 @@ opfunc_delete_var (opcode_t opdata, /**< operation data */ ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); - lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (name_lit_idx, lit_oc); + lit_cpointer_t lit_cp = serializer_get_literal_cp_by_uid (name_lit_idx, int_data->opcodes_p, lit_oc); JERRY_ASSERT (lit_cp.packed_value != MEM_CP_NULL); ecma_string_t *name_string_p = ecma_new_ecma_string_from_lit_cp (lit_cp); @@ -1671,7 +1694,7 @@ opfunc_meta (opcode_t opdata, /**< operation data */ case OPCODE_META_TYPE_SCOPE_CODE_FLAGS: case OPCODE_META_TYPE_UNDEFINED: - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_FUNCTION_END: case OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER: { @@ -1708,7 +1731,7 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, /**< expected type of meta opcode */ int_data_t *int_data) /**< interpreter context */ { - opcode_t meta_opcode = vm_get_opcode (int_data->pos); + opcode_t meta_opcode = vm_get_opcode (int_data->opcodes_p, int_data->pos); JERRY_ASSERT (meta_opcode.data.meta.type == expected_type); const idx_t data_1 = meta_opcode.data.meta.data_1; diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index a936d60ee6..1afea172c0 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -59,7 +59,8 @@ typedef enum typedef enum { OPCODE_META_TYPE_UNDEFINED, /**< undefined meta (should be rewritten) */ - OPCODE_META_TYPE_THIS_ARG, /**< value (var_idx) of this used during call */ + OPCODE_META_TYPE_CALL_SITE_INFO, /**< optional additional information about call site + * (includes opcode_call_flags_t and can include 'this' argument) */ OPCODE_META_TYPE_VARG, /**< element (var_idx) of arguments' list */ OPCODE_META_TYPE_VARG_PROP_DATA, /**< name (lit_idx) and value (var_idx) for a data property descriptor */ OPCODE_META_TYPE_VARG_PROP_GETTER, /**< name (lit_idx) and getter (var_idx) for an accessor property descriptor */ @@ -74,6 +75,19 @@ typedef enum * (See also: opcode_scope_code_flags_t) */ } opcode_meta_type; +typedef enum : idx_t +{ + OPCODE_CALL_FLAGS__EMPTY = (0u), /**< initializer for empty flag set */ + OPCODE_CALL_FLAGS_HAVE_THIS_ARG = (1u << 0), /**< flag, indicating that call is performed + * with 'this' argument specified */ + OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM = (1u << 1) /**< flag, indicating that call is performed + * in form 'eval (...)', i.e. through 'eval' string + * without object base (i.e. with lexical environment + * as base), so it can be a direct call to eval + * See also: ECMA-262 v5, 15.1.2.1.1 + */ +} opcode_call_flags_t; + /** * Flags indicating various properties of a scope's code */ @@ -87,16 +101,24 @@ typedef enum : idx_t * 'eval' identifier */ } opcode_scope_code_flags_t; +/** + * Forward declaration of opcode structure + */ +struct opcode_t; + /** * Interpreter context */ typedef struct { + const opcode_t *opcodes_p; /**< pointer to array containing currently executed bytecode */ opcode_counter_t pos; /**< current opcode to execute */ ecma_value_t this_binding; /**< this binding for current context */ ecma_object_t *lex_env_p; /**< current lexical environment */ bool is_strict; /**< is current code execution mode strict? */ bool is_eval_code; /**< is current code executed with eval */ + bool is_call_in_direct_eval_form; /** flag, indicating if there is call of 'Direct call to eval' form in + * process (see also: OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) */ idx_t min_reg_num; /**< minimum idx used for register identification */ idx_t max_reg_num; /**< maximum idx used for register identification */ ecma_number_t* tmp_num_p; /**< an allocated number (to reduce temporary allocations) */ @@ -257,7 +279,7 @@ opcode_counter_t read_meta_opcode_counter (opcode_meta_type expected_type, int_d OP_ARGS_LIST (OP_DATA) #define __OP_STRUCT_FIELD(name, arg1, arg2, arg3) __op_##name name; -typedef struct +typedef struct opcode_t { idx_t op_idx; union diff --git a/jerry-core/vm/pretty-printer.cpp b/jerry-core/vm/pretty-printer.cpp index c8a1320b57..e39c27e14e 100644 --- a/jerry-core/vm/pretty-printer.cpp +++ b/jerry-core/vm/pretty-printer.cpp @@ -104,7 +104,7 @@ var_to_str (opcode_t opcode, lit_cpointer_t lit_ids[], opcode_counter_t oc, uint } else { - return lit_cp_to_str (serializer_get_literal_cp_by_uid (raw.uids[current_arg], oc)); + return lit_cp_to_str (serializer_get_literal_cp_by_uid (raw.uids[current_arg], NULL, oc)); } } @@ -266,15 +266,9 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) } case NAME_TO_ID (call_n): { - if (opm.op.data.call_n.arg_list == 0) - { - pp_printf ("%s = %s ();", opm.op, opm.lit_id, oc, 1); - } - else - { - vargs_num = opm.op.data.call_n.arg_list; - seen_vargs = 0; - } + vargs_num = opm.op.data.call_n.arg_list; + seen_vargs = 0; + break; } case NAME_TO_ID (native_call): @@ -381,13 +375,17 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) printf ("unknown meta;"); break; } - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: case OPCODE_META_TYPE_VARG: case OPCODE_META_TYPE_VARG_PROP_DATA: case OPCODE_META_TYPE_VARG_PROP_GETTER: case OPCODE_META_TYPE_VARG_PROP_SETTER: { - seen_vargs++; + if (opm.op.data.meta.type != OPCODE_META_TYPE_CALL_SITE_INFO) + { + seen_vargs++; + } + if (seen_vargs == vargs_num) { bool found = false; @@ -473,15 +471,26 @@ pp_op_meta (opcode_counter_t oc, op_meta opm, bool rewrite) for (opcode_counter_t counter = start; counter <= oc; counter++) { opcode_t meta_op = serializer_get_opcode (counter); + switch (meta_op.op_idx) { case NAME_TO_ID (meta): { switch (meta_op.data.meta.type) { - case OPCODE_META_TYPE_THIS_ARG: + case OPCODE_META_TYPE_CALL_SITE_INFO: { - pp_printf ("this_arg = %s", meta_op, NULL, counter, 2); + opcode_call_flags_t call_flags = (opcode_call_flags_t) meta_op.data.meta.data_1; + + if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG) + { + pp_printf ("this_arg = %s", meta_op, NULL, counter, 3); + } + if (call_flags & OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM) + { + printf ("['direct call to eval' form]"); + } + break; } case OPCODE_META_TYPE_VARG: diff --git a/jerry-core/vm/vm.cpp b/jerry-core/vm/vm.cpp index 203c709da4..1ad36b3f46 100644 --- a/jerry-core/vm/vm.cpp +++ b/jerry-core/vm/vm.cpp @@ -214,7 +214,8 @@ interp_mem_stats_context_exit (int_data_t *int_data_p, } static void -interp_mem_stats_opcode_enter (opcode_counter_t opcode_position, +interp_mem_stats_opcode_enter (const opcode_t *opcodes_p, + opcode_counter_t opcode_position, mem_heap_stats_t *out_heap_stats_p, mem_pools_stats_t *out_pools_stats_p) { @@ -235,7 +236,7 @@ interp_mem_stats_opcode_enter (opcode_counter_t opcode_position, out_pools_stats_p, true, false); - opcode_t opcode = vm_get_opcode (opcode_position); + opcode_t opcode = vm_get_opcode (opcodes_p, opcode_position); printf ("%s-- Opcode: %s (position %u) --\n", indent_prefix, __op_names[opcode.op_idx], (uint32_t) opcode_position); @@ -280,7 +281,7 @@ interp_mem_stats_opcode_exit (int_data_t *int_data_p, int_data_p->context_peak_allocated_pool_chunks = JERRY_MAX (int_data_p->context_peak_allocated_pool_chunks, pools_stats_after.allocated_chunks); - opcode_t opcode = vm_get_opcode (opcode_position); + opcode_t opcode = vm_get_opcode (int_data_p->opcodes_p, opcode_position); printf ("%s Allocated heap bytes: %5u -> %5u (%+5d, local %5u, peak %5u)\n", indent_prefix, @@ -375,7 +376,8 @@ vm_run_global (void) bool is_strict = false; opcode_counter_t start_pos = 0; - opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (start_pos++); + opcode_scope_code_flags_t scope_flags = vm_get_scope_flags (__program, + start_pos++); if (scope_flags & OPCODE_SCOPE_CODE_FLAGS_STRICT) { @@ -385,7 +387,8 @@ vm_run_global (void) ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); ecma_object_t *lex_env_p = ecma_get_global_environment (); - ecma_completion_value_t completion = vm_run_from_pos (start_pos, + ecma_completion_value_t completion = vm_run_from_pos (__program, + start_pos, ecma_make_object_value (glob_obj_p), lex_env_p, is_strict, @@ -454,12 +457,13 @@ vm_loop (int_data_t *int_data_p, /**< interpreter context */ || (run_scope_p->start_oc <= int_data_p->pos && int_data_p->pos <= run_scope_p->end_oc)); - const opcode_t *curr = &__program[int_data_p->pos]; + const opcode_t *curr = &int_data_p->opcodes_p[int_data_p->pos]; #ifdef MEM_STATS const opcode_counter_t opcode_pos = int_data_p->pos; - interp_mem_stats_opcode_enter (opcode_pos, + interp_mem_stats_opcode_enter (int_data_p->opcodes_p, + opcode_pos, &heap_stats_before, &pools_stats_before); #endif /* MEM_STATS */ @@ -514,7 +518,8 @@ vm_loop (int_data_t *int_data_p, /**< interpreter context */ * Run the code, starting from specified opcode */ ecma_completion_value_t -vm_run_from_pos (opcode_counter_t start_pos, /**< identifier of starting opcode */ +vm_run_from_pos (const opcode_t *opcodes_p, /**< byte-code array */ + opcode_counter_t start_pos, /**< identifier of starting opcode */ ecma_value_t this_binding_value, /**< value of 'ThisBinding' */ ecma_object_t *lex_env_p, /**< lexical environment to use */ bool is_strict, /**< is the code is strict mode code (ECMA-262 v5, 10.1.1) */ @@ -522,7 +527,7 @@ vm_run_from_pos (opcode_counter_t start_pos, /**< identifier of starting opcode { ecma_completion_value_t completion; - const opcode_t *curr = &__program[start_pos]; + const opcode_t *curr = &opcodes_p[start_pos]; JERRY_ASSERT (curr->op_idx == __op__idx_reg_var_decl); const idx_t min_reg_num = curr->data.reg_var_decl.min; @@ -534,11 +539,13 @@ vm_run_from_pos (opcode_counter_t start_pos, /**< identifier of starting opcode MEM_DEFINE_LOCAL_ARRAY (regs, regs_num, ecma_value_t); int_data_t int_data; + int_data.opcodes_p = opcodes_p; int_data.pos = (opcode_counter_t) (start_pos + 1); int_data.this_binding = this_binding_value; int_data.lex_env_p = lex_env_p; int_data.is_strict = is_strict; int_data.is_eval_code = is_eval_code; + int_data.is_call_in_direct_eval_form = false; int_data.min_reg_num = min_reg_num; int_data.max_reg_num = max_reg_num; int_data.tmp_num_p = ecma_alloc_number (); @@ -577,9 +584,10 @@ vm_run_from_pos (opcode_counter_t start_pos, /**< identifier of starting opcode * Get specified opcode from the program. */ opcode_t -vm_get_opcode (opcode_counter_t counter) /**< opcode counter */ +vm_get_opcode (const opcode_t *opcodes_p, /**< byte-code array */ + opcode_counter_t counter) /**< opcode counter */ { - return __program[ counter ]; + return opcodes_p[ counter ]; } /* vm_get_opcode */ /** @@ -588,14 +596,58 @@ vm_get_opcode (opcode_counter_t counter) /**< opcode counter */ * @return mask of scope code flags */ opcode_scope_code_flags_t -vm_get_scope_flags (opcode_counter_t counter) /**< opcode counter */ +vm_get_scope_flags (const opcode_t *opcodes_p, /**< byte-code array */ + opcode_counter_t counter) /**< opcode counter */ { - opcode_t flags_opcode = vm_get_opcode (counter); + opcode_t flags_opcode = vm_get_opcode (opcodes_p, counter); JERRY_ASSERT (flags_opcode.op_idx == __op__idx_meta && flags_opcode.data.meta.type == OPCODE_META_TYPE_SCOPE_CODE_FLAGS); return (opcode_scope_code_flags_t) flags_opcode.data.meta.data_1; } /* vm_get_scope_flags */ +/** + * Check whether currently executed code is strict mode code + * + * @return true - current code is executed in strict mode, + * false - otherwise. + */ +bool +vm_is_strict_mode (void) +{ + JERRY_ASSERT (vm_top_context_p != NULL); + + return vm_top_context_p->is_strict; +} /* vm_is_strict_mode */ + +/** + * Check whether currently performed call (on top of call-stack) is performed in form, + * meeting conditions of 'Direct Call to Eval' (see also: ECMA-262 v5, 15.1.2.1.1) + * + * Warning: + * the function should only be called from implementation + * of built-in 'eval' routine of Global object + * + * @return true - currently performed call is performed through 'eval' identifier, + * without 'this' argument, + * false - otherwise. + */ +bool +vm_is_direct_eval_form_call (void) +{ + if (vm_top_context_p != NULL) + { + return vm_top_context_p->is_call_in_direct_eval_form; + } + else + { + /* + * There is no any interpreter context, so call is performed not from a script. + * This implies that the call is indirect. + */ + return false; + } +} /* vm_is_direct_eval_form_call */ + /** * Get this binding of current execution context * diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 7ed8acc5cd..9934d2e4ea 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -24,14 +24,18 @@ extern void vm_init (const opcode_t* program_p, bool dump_mem_stats); extern void vm_finalize (void); extern jerry_completion_code_t vm_run_global (void); extern ecma_completion_value_t vm_loop (int_data_t *int_data, vm_run_scope_t *run_scope_p); -extern ecma_completion_value_t vm_run_from_pos (opcode_counter_t start_pos, +extern ecma_completion_value_t vm_run_from_pos (const opcode_t *opcodes_p, + opcode_counter_t start_pos, ecma_value_t this_binding_value, ecma_object_t *lex_env_p, bool is_strict, bool is_eval_code); -extern opcode_t vm_get_opcode (opcode_counter_t counter); -extern opcode_scope_code_flags_t vm_get_scope_flags (opcode_counter_t counter); +extern opcode_t vm_get_opcode (const opcode_t*, opcode_counter_t counter); +extern opcode_scope_code_flags_t vm_get_scope_flags (const opcode_t*, opcode_counter_t counter); + +extern bool vm_is_strict_mode (void); +extern bool vm_is_direct_eval_form_call (void); extern ecma_value_t vm_get_this_binding (void); extern ecma_object_t* vm_get_lex_env (void); diff --git a/tests/jerry/eval.js b/tests/jerry/eval.js new file mode 100644 index 0000000000..22eb187fd5 --- /dev/null +++ b/tests/jerry/eval.js @@ -0,0 +1,88 @@ +// Copyright 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. + +assert (eval () === undefined); +assert (eval (undefined) === undefined); +assert (eval (null) === null); +assert (eval (true) === true); +assert (eval (false) === false); +assert (eval (1) === 1); +assert (eval (eval) === eval); + +/* Indirect eval */ +function f1() +{ + var v1 = 'local value'; + + assert (v1 === 'local value'); + assert (typeof (this.v1) === 'undefined'); + + r = this.eval ('var v1 = "global value";'); + + assert (v1 === 'local value'); + assert (this.v1 === 'global value'); + assert (r === undefined); +}; + +f1 (); + +/* Direct eval from strict mode code */ +function f2 (global) +{ + 'use strict'; + var v2 = 'local value'; + + assert (v2 === 'local value'); + assert (typeof (global.v2) === 'undefined'); + + r = eval ('var v2 = "global value";'); + + assert (v2 === 'local value'); + assert (typeof (global.v2) === 'undefined'); + assert (r === undefined); +} + +f2 (this); + +var y; + +for (var i = 0; i < 100; i++) +{ + var r = eval ('var x =' + ' 1;'); + assert (typeof (x) === 'number'); + assert (r === undefined); + + delete x; + assert (typeof (x) === 'undefined'); + + r = eval ('"use ' + 's' + 't' + 'r' + 'i' + 'c' + 't"; va' + 'r x = 1;'); + assert (typeof (x) === 'undefined'); + assert (r === "use strict"); + + y = 'str'; + assert (typeof (y) === 'string'); + + delete y; + assert (typeof (y) === 'string'); + + r = eval ('var y = "another ' + 'string";'); + assert (y === 'another string'); + assert (r == undefined); + + delete y; + assert (typeof (y) === 'string'); + + r = eval ('if (true) 3; else 5;'); + assert (r === 3); +} diff --git a/tests/jerry/function_construct.js b/tests/jerry/function_construct.js new file mode 100644 index 0000000000..0a2d70364a --- /dev/null +++ b/tests/jerry/function_construct.js @@ -0,0 +1,81 @@ +// Copyright 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. + +var f = new Function (''); +assert (f () === undefined); + +var f = new Function ('"use strict"; f = undefined;'); +assert (f () === undefined && f === undefined); + +var f = new Function ('"use strict"; q = undefined;'); +try +{ + f (); + assert (false); +} +catch (e) +{ + assert (e instanceof ReferenceError); +} + +for (i = 1; i < 10; i ++) +{ + var f = new Function ('a', 'b', 'var q = a; b++; function f (k) {return q + k + b++;}; return f;'); + + var fns = new Array (); + + for (var n = 0; n < 10; n++) + { + var r = f (1, 7); + fns[n] = r; + + var check_value = 10; + + for (var m = 0; m < 100; m++) + { + var value = r (1); + assert (check_value++ === value); + } + } + + var check_value = 109; + for (var n = 0; n < 11; n++) + { + for (var m = 0; m < 10; m++) + { + var value = fns [m] (m * n); + assert (value == check_value + m * n); + } + + check_value++; + } +} + +try +{ + new Function ({ + toString : function () { + throw new TypeError(); + }, + valueOf : function () { + throw new TypeError(); + } + }); + + assert (false); +} +catch (e) +{ + assert (e instanceof TypeError); +} diff --git a/tests/unit/test-api.cpp b/tests/unit/test-api.cpp index ddd8977d00..9636fd049a 100644 --- a/tests/unit/test-api.cpp +++ b/tests/unit/test-api.cpp @@ -435,6 +435,26 @@ main (void) jerry_api_release_value (&val_t); jerry_api_release_value (&res); + const char *eval_code_src_p = "(function () { return 123; })"; + jerry_completion_code_t status = jerry_api_eval (eval_code_src_p, + strlen (eval_code_src_p), + false, + true, + &val_t); + JERRY_ASSERT (status == JERRY_COMPLETION_CODE_OK); + JERRY_ASSERT (val_t.type == JERRY_API_DATA_TYPE_OBJECT); + JERRY_ASSERT (jerry_api_is_function (val_t.v_object)); + + is_ok = jerry_api_call_function (val_t.v_object, + NULL, + &res, + NULL, 0); + JERRY_ASSERT (is_ok); + JERRY_ASSERT (res.type == JERRY_API_DATA_TYPE_FLOAT64 + && res.v_float64 == 123.0); + jerry_api_release_value (&res); + + jerry_api_release_value (&val_t); // cleanup. jerry_api_release_object (global_obj_p); diff --git a/tests/unit/test-parser.cpp b/tests/unit/test-parser.cpp index 48f9963f4e..2721e68b69 100644 --- a/tests/unit/test-parser.cpp +++ b/tests/unit/test-parser.cpp @@ -74,8 +74,9 @@ main (int __attr_unused___ argc, mem_init (); serializer_init (); - parser_init (program, strlen (program), true); - parser_parse_program (); + parser_set_show_opcodes (true); + parser_init (); + parser_parse_script (program, strlen (program)); parser_free (); opcode_t opcodes[] =