From f0abfbb31b0998867244b3c3675f4b6895b0080c Mon Sep 17 00:00:00 2001 From: Ruben Ayrapetyan Date: Tue, 22 Jul 2014 17:23:29 +0400 Subject: [PATCH] Refinement of exception handling code in 'multiplication' and 'assignment' opcode handlers. --- src/globals.h | 6 + src/libcoreint/opcodes.c | 268 +++++++++++++----------- src/libecmaobjects/ecma-helpers-value.c | 29 +-- src/libecmaobjects/ecma-helpers.h | 2 +- 4 files changed, 173 insertions(+), 132 deletions(-) diff --git a/src/globals.h b/src/globals.h index d01d5965e..e1cfa13f8 100644 --- a/src/globals.h +++ b/src/globals.h @@ -95,6 +95,12 @@ extern void jerry_RefUnusedVariables(int unused_variables_follow, ...); #define JERRY_UNIMPLEMENTED() JERRY_UNREACHABLE() #define JERRY_UNIMPLEMENTED_REF_UNUSED_VARS(...) do { JERRY_UNIMPLEMENTED(); if ( false ) { jerry_RefUnusedVariables( 0, __VA_ARGS__); } } while (0) +/** + * Conditions' likeliness, unlikeliness. + */ +#define likely( x ) ( __builtin_expect( !!( x ), 1 ) ) +#define unlikely( x ) ( __builtin_expect( !!( x ), 0 ) ) + /** * Exit */ diff --git a/src/libcoreint/opcodes.c b/src/libcoreint/opcodes.c index 57d1391bd..a74811914 100644 --- a/src/libcoreint/opcodes.c +++ b/src/libcoreint/opcodes.c @@ -19,11 +19,68 @@ #include "ecma-helpers.h" #include "ecma-number-arithmetic.h" #include "ecma-operations.h" +#include "globals.h" #include "interpreter.h" #include "jerry-libc.h" #include "mem-heap.h" #include "opcodes.h" +/** + * Note: + * The note describes exception handling in opcode handlers that perform operations, + * that can throw exceptions, and do not themself handle the exceptions. + * + * Generally, each opcode handler consists of sequence of operations. + * Some of these operations (exceptionable operations) can throw exceptions and other - cannot. + * + * 1. At the beginning of the handler there should be declared opcode handler's 'return value' variable. + * + * 2. All exceptionable operations except the last should be enclosed in TRY_CATCH macro. + * All subsequent operations in the opcode handler should be placed into block between + * the TRY_CATCH and corresponding FINALIZE. + * + * 3. The last exceptionable's operation result should be assigned directly to opcode handler's + * 'return value' variable without using TRY_CATCH macro. + * + * 4. After last FINALIZE statement there should be only one operator. + * The operator should return from the opcode handler with it's 'return value'. + * + * 5. No other operations with opcode handler's 'return value' variable should be performed. + */ + +/** + * The macro defines try-block that initializes variable 'var' with 'op' + * and checks for exceptions that might be thrown during initialization. + * + * If no exception was thrown, then code after the try-block is executed. + * Otherwise, throw-completion value is just copied to return_value. + * + * Note: + * Each TRY_CATCH should have it's own corresponding FINALIZE + * statement with same argument as corresponding TRY_CATCH's first argument. + */ +#define TRY_CATCH(var, op, return_value) \ + ecma_CompletionValue_t var = op; \ + if ( unlikely( ecma_is_completion_value_throw( var) ) ) \ + { \ + return_value = ecma_copy_completion_value( var); \ + } \ + else \ + { \ + JERRY_ASSERT( ecma_is_completion_value_normal( var) ) + +/** + * The macro marks end of code block that is executed if no exception + * was catched by corresponding TRY_CATCH and frees variable, + * initialized by the TRY_CATCH. + * + * Note: + * Each TRY_CATCH should be followed by FINALIZE with same + * argument as corresponding TRY_CATCH's first argument. + */ +#define FINALIZE(var) } \ + ecma_free_completion_value( var) + /** * String literal copy descriptor. */ @@ -91,6 +148,29 @@ free_string_literal_copy(string_literal_copy *str_lit_descr_p) /**< string liter return; } /* free_string_literal */ +/** + * Perform so-called 'strict eval or arguments reference' check + * that is used in definition of several statement handling algorithms, + * but has no ECMA-defined name. + * + * @return true - if ref is strict reference + * and it's base is lexical environment + * and it's referenced name is 'eval' or 'arguments'; + * false - otherwise. + */ +static bool +do_strict_eval_arguments_check( ecma_Reference_t ref) /**< ECMA-reference */ +{ + FIXME( Move magic strings to header file and make them ecma_Char_t[] ); + FIXME( Replace strcmp with ecma_Char_t[] comparator ); + return ( ref.is_strict + && ( __strcmp( (char*)ref.referenced_name_p, "eval") == 0 + || __strcmp( (char*)ref.referenced_name_p, "arguments") == 0 ) + && ( ref.base.m_ValueType == ECMA_TYPE_OBJECT ) + && ( ecma_GetPointer( ref.base.m_Value) != NULL ) + && ( ( (ecma_Object_t*) ecma_GetPointer( ref.base.m_Value) )->m_IsLexicalEnvironment ) ); +} /* do_strict_eval_arguments_check */ + /** * Get variable's value. * @@ -98,8 +178,10 @@ free_string_literal_copy(string_literal_copy *str_lit_descr_p) /**< string liter * Returned value must be freed with ecma_free_completion_value */ static ecma_CompletionValue_t -get_variable_value(struct __int_data *int_data, /* interpreter context */ - T_IDX var_idx) /* variable identifier */ +get_variable_value(struct __int_data *int_data, /**< interpreter context */ + T_IDX var_idx, /**< variable identifier */ + bool do_eval_or_arguments_check) /** run 'strict eval or arguments reference' check + See also: do_strict_eval_arguments_check */ { string_literal_copy var_name; ecma_Reference_t ref; @@ -110,7 +192,15 @@ get_variable_value(struct __int_data *int_data, /* interpreter context */ var_name.str_p, int_data->is_strict); - ret_value = ecma_op_get_value( ref); + if ( unlikely( do_eval_or_arguments_check + && do_strict_eval_arguments_check( ref) ) ) + { + ret_value = ecma_MakeThrowValue( ecma_NewStandardError( ECMA_ERROR_SYNTAX)); + } + else + { + ret_value = ecma_op_get_value( ref); + } ecma_FreeReference( ref); free_string_literal_copy( &var_name); @@ -138,7 +228,14 @@ set_variable_value(struct __int_data *int_data, /**< interpreter context */ var_name.str_p, int_data->is_strict); - ret_value = ecma_op_put_value( ref, value); + if ( unlikely( do_strict_eval_arguments_check( ref) ) ) + { + ret_value = ecma_MakeThrowValue( ecma_NewStandardError( ECMA_ERROR_SYNTAX)); + } + else + { + ret_value = ecma_op_put_value( ref, value); + } ecma_FreeReference( ref); free_string_literal_copy( &var_name); @@ -272,13 +369,15 @@ opfunc_assignment (OPCODE opdata, /**< operation data */ int_data->pos++; - ecma_Value_t right_value; + ecma_CompletionValue_t get_value_completion; switch ( type_value_right ) { case OPCODE_ARG_TYPE_SIMPLE: { - right_value = ecma_MakeSimpleValue( src_val_descr); + get_value_completion = ecma_MakeCompletionValue( ECMA_COMPLETION_TYPE_NORMAL, + ecma_MakeSimpleValue( src_val_descr), + ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_STRING: @@ -290,23 +389,16 @@ opfunc_assignment (OPCODE opdata, /**< operation data */ ecma_string_p = ecma_NewEcmaString( str_value.str_p); free_string_literal_copy( &str_value); - right_value.m_ValueType = ECMA_TYPE_STRING; - ecma_SetPointer( right_value.m_Value, ecma_string_p); + get_value_completion = ecma_MakeCompletionValue(ECMA_COMPLETION_TYPE_NORMAL, + ecma_make_string_value( ecma_string_p), + ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_VARIABLE: { - ecma_CompletionValue_t get_value_completion = get_variable_value( int_data, - src_val_descr); - if ( ecma_is_completion_value_normal( get_value_completion) ) - { - right_value = get_value_completion.value; - } else - { - JERRY_ASSERT( ecma_is_completion_value_throw( get_value_completion) ); - - return get_value_completion; - } + get_value_completion = get_variable_value(int_data, + src_val_descr, + false); break; } @@ -315,9 +407,9 @@ opfunc_assignment (OPCODE opdata, /**< operation data */ ecma_Number_t *num_p = ecma_AllocNumber(); *num_p = get_number_by_idx( src_val_descr); - right_value.m_ValueType = ECMA_TYPE_NUMBER; - ecma_SetPointer( right_value.m_Value, num_p); - + get_value_completion = ecma_MakeCompletionValue(ECMA_COMPLETION_TYPE_NORMAL, + ecma_MakeNumberValue( num_p), + ECMA_TARGET_ID_RESERVED); break; } case OPCODE_ARG_TYPE_SMALLINT: @@ -325,47 +417,30 @@ opfunc_assignment (OPCODE opdata, /**< operation data */ ecma_Number_t *num_p = ecma_AllocNumber(); *num_p = src_val_descr; - right_value.m_ValueType = ECMA_TYPE_NUMBER; - ecma_SetPointer( right_value.m_Value, num_p); - + get_value_completion = ecma_MakeCompletionValue(ECMA_COMPLETION_TYPE_NORMAL, + ecma_MakeNumberValue( num_p), + ECMA_TARGET_ID_RESERVED); break; } JERRY_UNIMPLEMENTED(); } - ecma_CompletionValue_t completion_value; + if ( unlikely( ecma_is_completion_value_throw( get_value_completion) ) ) + { + return get_value_completion; + } + else + { + JERRY_ASSERT( ecma_is_completion_value_normal( get_value_completion) ); + + ecma_CompletionValue_t assignment_completion_value = set_variable_value(int_data, + dst_var_idx, + get_value_completion.value); + + ecma_free_completion_value( get_value_completion); - string_literal_copy dst_variable_name; - ecma_Reference_t dst_reference; - - init_string_literal_copy( dst_var_idx, &dst_variable_name); - - dst_reference = ecma_OpGetIdentifierReference( int_data->lex_env_p, - dst_variable_name.str_p, - int_data->is_strict); - - FIXME( Move magic strings to header file and make them ecma_Char_t[] ); - FIXME( Replace strcmp with ecma_Char_t[] comparator ); - if ( dst_reference.is_strict - && ( __strcmp( (char*)dst_reference.referenced_name_p, "eval") == 0 - || __strcmp( (char*)dst_reference.referenced_name_p, "arguments") == 0 ) - && ( dst_reference.base.m_ValueType == ECMA_TYPE_OBJECT ) - && ( ecma_GetPointer( dst_reference.base.m_Value) != NULL ) - && ( ( (ecma_Object_t*) ecma_GetPointer( dst_reference.base.m_Value) )->m_IsLexicalEnvironment ) ) - { - completion_value = ecma_MakeThrowValue( ecma_NewStandardError( ECMA_ERROR_SYNTAX)); - } else - { - completion_value = ecma_op_put_value( dst_reference, right_value); - } - - ecma_FreeValue( right_value); - - ecma_FreeReference( dst_reference); - - free_string_literal_copy( &dst_variable_name); - - return completion_value; + return assignment_completion_value; + } } /* opfunc_assignment */ /** @@ -386,73 +461,30 @@ opfunc_multiplication (OPCODE opdata, /**< operation data */ int_data->pos++; - ecma_CompletionValue_t left_value = ecma_make_empty_completion_value(), - right_value = ecma_make_empty_completion_value(), - num_left_value = ecma_make_empty_completion_value(), - num_right_value = ecma_make_empty_completion_value(), - ret_value = ecma_make_empty_completion_value(); + ecma_CompletionValue_t ret_value; - left_value = get_variable_value( int_data, left_var_idx); + TRY_CATCH(left_value, get_variable_value( int_data, left_var_idx, false), ret_value); + TRY_CATCH(right_value, get_variable_value( int_data, right_var_idx, false), ret_value); + TRY_CATCH(num_left_value, ecma_op_to_number( left_value.value), ret_value); + TRY_CATCH(num_right_value, ecma_op_to_number( right_value.value), ret_value); - if ( ecma_is_completion_value_throw( left_value) ) - { - ret_value = ecma_copy_completion_value( left_value); - } - else - { - JERRY_ASSERT( ecma_is_completion_value_normal( left_value) ); + ecma_Number_t *left_p, *right_p, *res_p; + left_p = (ecma_Number_t*)ecma_GetPointer( num_left_value.value.m_Value); + right_p = (ecma_Number_t*)ecma_GetPointer( num_right_value.value.m_Value); - right_value = get_variable_value( int_data, right_var_idx); + res_p = ecma_AllocNumber(); + *res_p = ecma_op_number_multiply( *left_p, *right_p); - if ( ecma_is_completion_value_throw( right_value) ) - { - ret_value = ecma_copy_completion_value( right_value); - } - else - { - JERRY_ASSERT( ecma_is_completion_value_normal( right_value) ); + ret_value = set_variable_value(int_data, + dst_var_idx, + ecma_MakeNumberValue( res_p)); - num_left_value = ecma_op_to_number( left_value.value); + ecma_DeallocNumber( res_p); - if ( ecma_is_completion_value_throw( num_left_value) ) - { - ret_value = ecma_copy_completion_value( num_left_value); - } - else - { - JERRY_ASSERT( ecma_is_completion_value_normal( num_left_value) ); - - num_right_value = ecma_op_to_number( right_value.value); - - if ( ecma_is_completion_value_throw( num_right_value) ) - { - ret_value = ecma_copy_completion_value( num_right_value); - } - else - { - JERRY_ASSERT( ecma_is_completion_value_normal( num_right_value) ); - - ecma_Number_t *left_p, *right_p, *res_p; - left_p = (ecma_Number_t*)ecma_GetPointer( num_left_value.value.m_Value); - right_p = (ecma_Number_t*)ecma_GetPointer( num_right_value.value.m_Value); - res_p = ecma_AllocNumber(); - - *res_p = ecma_op_number_multiply( *left_p, *right_p); - - ret_value = set_variable_value( int_data, - dst_var_idx, - ecma_MakeNumberValue( res_p)); - - ecma_DeallocNumber( res_p); - } - } - } - } - - ecma_free_completion_value( left_value); - ecma_free_completion_value( right_value); - ecma_free_completion_value( num_left_value); - ecma_free_completion_value( num_right_value); + FINALIZE( num_right_value); + FINALIZE( num_left_value); + FINALIZE( right_value); + FINALIZE( left_value); return ret_value; } /* opfunc_multiplication */ diff --git a/src/libecmaobjects/ecma-helpers-value.c b/src/libecmaobjects/ecma-helpers-value.c index d3df38180..ce84b8118 100644 --- a/src/libecmaobjects/ecma-helpers-value.c +++ b/src/libecmaobjects/ecma-helpers-value.c @@ -105,6 +105,22 @@ ecma_MakeNumberValue( ecma_Number_t* num_p) /**< number to reference in value */ return number_value; } /* ecma_MakeNumberValue */ +/** + * String value constructor + */ +ecma_Value_t +ecma_make_string_value( ecma_ArrayFirstChunk_t* ecma_string_p) /**< string to reference in value */ +{ + JERRY_ASSERT( ecma_string_p != NULL ); + + ecma_Value_t string_value; + + string_value.m_ValueType = ECMA_TYPE_STRING; + ecma_SetPointer( string_value.m_Value, ecma_string_p); + + return string_value; +} /* ecma_make_string_value */ + /** * Object value constructor */ @@ -342,19 +358,6 @@ ecma_is_completion_value_throw( ecma_CompletionValue_t value) /**< completion va return ( value.type == ECMA_COMPLETION_TYPE_THROW ); } /* ecma_is_completion_value_throw */ -/** - * Check if the completion value is normal or throw value. - * - * @return true - if the completion type is normal or throw, - * false - otherwise. - */ -bool -ecma_is_completion_value_normal_or_throw( ecma_CompletionValue_t value) /**< completion value */ -{ - return ecma_is_completion_value_normal( value) - || ecma_is_completion_value_throw( value); -} /* ecma_is_completion_value_normal_or_throw */ - /** * Check if the completion value is specified normal simple value. * diff --git a/src/libecmaobjects/ecma-helpers.h b/src/libecmaobjects/ecma-helpers.h index 0168dc04b..0d055ce37 100644 --- a/src/libecmaobjects/ecma-helpers.h +++ b/src/libecmaobjects/ecma-helpers.h @@ -49,6 +49,7 @@ extern bool ecma_IsValueTrue( ecma_Value_t value); extern ecma_Value_t ecma_MakeSimpleValue( ecma_SimpleValue_t value); extern ecma_Value_t ecma_MakeNumberValue( ecma_Number_t* num_p); +extern ecma_Value_t ecma_make_string_value( ecma_ArrayFirstChunk_t* ecma_string_p); extern ecma_Value_t ecma_MakeObjectValue( ecma_Object_t* object_p); extern ecma_Value_t ecma_CopyValue( const ecma_Value_t value); extern void ecma_FreeValue( const ecma_Value_t value); @@ -61,7 +62,6 @@ extern void ecma_free_completion_value( ecma_CompletionValue_t completion_value) extern bool ecma_is_completion_value_normal( ecma_CompletionValue_t value); extern bool ecma_is_completion_value_throw( ecma_CompletionValue_t value); -extern bool ecma_is_completion_value_normal_or_throw( ecma_CompletionValue_t value); extern bool ecma_is_completion_value_normal_simple_value( ecma_CompletionValue_t value, ecma_SimpleValue_t simple_value); extern bool ecma_IsCompletionValueNormalFalse( ecma_CompletionValue_t value); extern bool ecma_IsCompletionValueNormalTrue( ecma_CompletionValue_t value);