From 23ade8f4ff4c60a5e8685b67fe81f84b77bc8360 Mon Sep 17 00:00:00 2001 From: Ilyong Cho Date: Fri, 3 Apr 2015 18:11:21 +0900 Subject: [PATCH] wip: Implementing external function api --- jerry-core/ecma/base/ecma-alloc.cpp | 1 + jerry-core/ecma/base/ecma-alloc.h | 12 ++ jerry-core/ecma/base/ecma-globals.h | 5 + .../ecma/operations/ecma-function-object.cpp | 126 +++++++++++++++--- .../ecma/operations/ecma-function-object.h | 5 +- jerry-core/ecma/operations/ecma-objects.cpp | 1 + jerry-core/jerry-api.h | 10 ++ jerry-core/jerry.cpp | 21 +++ jerry-core/vm/opcodes.cpp | 4 +- tests/unit/test_api.cpp | 20 +++ 10 files changed, 183 insertions(+), 22 deletions(-) diff --git a/jerry-core/ecma/base/ecma-alloc.cpp b/jerry-core/ecma/base/ecma-alloc.cpp index 0dae9bdf5..c22f24e2e 100644 --- a/jerry-core/ecma/base/ecma-alloc.cpp +++ b/jerry-core/ecma/base/ecma-alloc.cpp @@ -89,6 +89,7 @@ DECLARE_ROUTINES_FOR (collection_chunk) DECLARE_ROUTINES_FOR (string) DECLARE_ROUTINES_FOR (label_descriptor) DECLARE_ROUTINES_FOR (getter_setter_pointers) +DECLARE_ROUTINES_FOR (external_pointer) /** * @} diff --git a/jerry-core/ecma/base/ecma-alloc.h b/jerry-core/ecma/base/ecma-alloc.h index 43a76a964..f40fd4f11 100644 --- a/jerry-core/ecma/base/ecma-alloc.h +++ b/jerry-core/ecma/base/ecma-alloc.h @@ -121,6 +121,18 @@ extern ecma_getter_setter_pointers_t *ecma_alloc_getter_setter_pointers (void); */ extern void ecma_dealloc_getter_setter_pointers (ecma_getter_setter_pointers_t *pointer_pair_p); +/** +* Allocate memory for external pointer +* +* @return pointer to allocated memory +*/ +extern ecma_external_pointer_t *ecma_alloc_external_pointer (void); + +/** +* Dealloc memory from external pointer +*/ +extern void ecma_dealloc_external_pointer (ecma_external_pointer_t *ptr_p); + #endif /* JERRY_ECMA_ALLOC_H */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index ef4e31f3f..e44abd2a2 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -372,6 +372,8 @@ typedef enum and not host objects */ ECMA_OBJECT_TYPE_STRING, /**< String objects (15.5) */ ECMA_OBJECT_TYPE_FUNCTION, /**< Function objects (15.3), created through 13.2 routine */ + ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION, /**< Function object (15.3), created through 13.2 routine + but [[Code]] is in external handler. */ ECMA_OBJECT_TYPE_BOUND_FUNCTION, /**< Function objects (15.3), created through 15.3.4.5 routine */ ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION, /** One of built-in functions described in section 15 of ECMA-262 v5 specification */ @@ -810,6 +812,9 @@ typedef struct ecma_string_t } u; } ecma_string_t; + +typedef uintptr_t ecma_external_pointer_t; + /** * @} */ diff --git a/jerry-core/ecma/operations/ecma-function-object.cpp b/jerry-core/ecma/operations/ecma-function-object.cpp index 10e5347fc..cb21f03a9 100644 --- a/jerry-core/ecma/operations/ecma-function-object.cpp +++ b/jerry-core/ecma/operations/ecma-function-object.cpp @@ -25,6 +25,7 @@ #include "ecma-objects-general.h" #include "ecma-try-catch-macro.h" #include "jrt.h" +#include "jerry-api.h" /** \addtogroup ecma ECMA * @{ @@ -78,6 +79,51 @@ ecma_unpack_code_internal_property_value (uint32_t value, /**< packed value */ return opcode_idx; } /* ecma_unpack_code_internal_property_value */ +/** + * Pack the pointer to external handler to value + * that can be stored in an [[Code]] internal property. + * + * @return packed value + */ +static uint32_t +ecma_pack_external_code_internal_property_value (jerry_external_handler_t handler) +{ + uint32_t value; + if (sizeof (jerry_external_handler_t) == sizeof (uint32_t)) + { + value = static_cast (reinterpret_cast (handler)); + } + else + { + ecma_external_pointer_t *handler_p = ecma_alloc_external_pointer (); + *handler_p = reinterpret_cast (handler); + ECMA_SET_POINTER (value, handler_p); + } + return value; +} /* ecma_pack_external_code_internal_property_value */ + +/** + * Unpack 'is_strict' flag and opcode index from value + * that can be stored in an [[Code]] internal property. + * + * @return opcode index + */ +static jerry_external_handler_t +ecma_unpack_external_code_internal_property_value (uint32_t value) /**< packed value */ +{ + jerry_external_handler_t handler; + if (sizeof (ecma_external_pointer_t) == sizeof (uint32_t)) + { + handler = reinterpret_cast (static_cast (value)); + } + else + { + ecma_external_pointer_t* handler_p = ECMA_GET_POINTER (ecma_external_pointer_t, value); + handler = reinterpret_cast (*handler_p); + } + return handler; +} /* ecma_unpack_code_internal_property_value */ + /** * IsCallable operation. * @@ -139,12 +185,21 @@ ecma_op_create_function_object (ecma_string_t* formal_parameter_list_p[], /**< f ecma_length_t formal_parameters_number, /**< formal parameters list's length */ ecma_object_t *scope_p, /**< function's scope */ bool is_strict, /**< 'strict' flag */ - opcode_counter_t first_opcode_idx) /**< index of first opcode of function's body */ + bool is_external, /**< true if the function is an external */ + opcode_counter_t first_opcode_idx, /**< index of first opcode of function's body */ + jerry_external_handler_t external_handler) /**< external handler */ { + JERRY_ASSERT (!is_external || external_handler != NULL); + // 1., 4., 13. ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); - ecma_object_t *f = ecma_create_object (prototype_obj_p, true, ECMA_OBJECT_TYPE_FUNCTION); + ecma_object_type_t object_type = ECMA_OBJECT_TYPE_FUNCTION; + if (is_external) + { + object_type = ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION; + } + ecma_object_t *f = ecma_create_object (prototype_obj_p, true, object_type); ecma_deref_object (prototype_obj_p); @@ -179,8 +234,16 @@ 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); - code_prop_p->u.internal_property.value = ecma_pack_code_internal_property_value (is_strict, - first_opcode_idx); + if (is_external) + { + code_prop_p->u.internal_property.value = ecma_pack_external_code_internal_property_value (external_handler); + } + else + { + code_prop_p->u.internal_property.value = ecma_pack_code_internal_property_value (is_strict, + first_opcode_idx); + } + // 14. ecma_number_t* len_p = ecma_alloc_number (); @@ -467,7 +530,8 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ ecma_completion_value_t ret_value; - if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) + if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION + || ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION) { if (unlikely (ecma_get_object_is_builtin (func_obj_p))) { @@ -480,15 +544,24 @@ 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_object_t *scope_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, scope_prop_p->u.internal_property.value); + + // 8. + bool is_strict; + opcode_counter_t code_first_opcode_idx = 0; + + ecma_property_t *code_prop_p = ecma_get_internal_property (func_obj_p, ECMA_INTERNAL_PROPERTY_CODE); uint32_t code_prop_value = code_prop_p->u.internal_property.value; - bool is_strict; - // 8. - opcode_counter_t code_first_opcode_idx = ecma_unpack_code_internal_property_value (code_prop_value, &is_strict); + if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) + { + code_first_opcode_idx = ecma_unpack_code_internal_property_value (code_prop_value, &is_strict); + } + else /* ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION */ + { + is_strict = false; + } ecma_value_t this_binding; // 1. @@ -514,7 +587,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ // 5. ecma_object_t *local_env_p = ecma_create_decl_lex_env (scope_p); - // 9. + ECMA_TRY_CATCH (args_var_declaration_ret, ecma_function_call_setup_args_variables (func_obj_p, local_env_p, @@ -523,18 +596,29 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ is_strict), ret_value); - ecma_completion_value_t completion = run_int_from_pos (code_first_opcode_idx, - this_binding, - local_env_p, - is_strict, - false); - if (ecma_is_completion_value_return (completion)) + // 9. + if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) { - ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + ecma_completion_value_t completion = run_int_from_pos (code_first_opcode_idx, + this_binding, + local_env_p, + is_strict, + false); + if (ecma_is_completion_value_return (completion)) + { + ret_value = ecma_make_normal_completion_value (ecma_get_completion_value_value (completion)); + } + else + { + ret_value = completion; + } + } else { - ret_value = completion; + jerry_external_handler_t handler = ecma_unpack_external_code_internal_property_value (code_prop_value); + handler (NULL, NULL, 0, NULL); + ret_value = 0; } ECMA_FINALIZE (args_var_declaration_ret); @@ -681,7 +765,9 @@ ecma_op_function_declaration (ecma_object_t *lex_env_p, /**< lexical environment formal_parameter_list_length, lex_env_p, is_strict, - function_code_opcode_idx); + false, + function_code_opcode_idx, + 0); // c. bool func_already_declared = ecma_op_has_binding (lex_env_p, function_name_p); diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index 78dd7b088..66fc830ae 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -18,6 +18,7 @@ #include "ecma-globals.h" #include "vm.h" +#include "jerry-api.h" /** \addtogroup ecma ECMA * @{ @@ -34,7 +35,9 @@ ecma_op_create_function_object (ecma_string_t* formal_parameter_list_p[], ecma_length_t formal_parameters_number, ecma_object_t *scope_p, bool is_strict, - opcode_counter_t first_opcode_idx); + bool is_external, + opcode_counter_t first_opcode_idx, + jerry_external_handler_t external_handler); extern ecma_completion_value_t ecma_op_function_call (ecma_object_t *func_obj_p, diff --git a/jerry-core/ecma/operations/ecma-objects.cpp b/jerry-core/ecma/operations/ecma-objects.cpp index 616e0f418..0fe84da76 100644 --- a/jerry-core/ecma/operations/ecma-objects.cpp +++ b/jerry-core/ecma/operations/ecma-objects.cpp @@ -483,6 +483,7 @@ ecma_op_object_has_instance (ecma_object_t *obj_p, /**< the object */ } case ECMA_OBJECT_TYPE_FUNCTION: + case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION: case ECMA_OBJECT_TYPE_BOUND_FUNCTION: case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { diff --git a/jerry-core/jerry-api.h b/jerry-core/jerry-api.h index ce5f135e1..86d27afd5 100644 --- a/jerry-core/jerry-api.h +++ b/jerry-core/jerry-api.h @@ -80,6 +80,14 @@ typedef struct jerry_api_value_t }; } jerry_api_value_t; +/** + * Jerry external function handler type + */ +typedef bool (*jerry_external_handler_t) (const jerry_api_value_t *this_p, + const jerry_api_value_t *args_p [], + const int16_t args_cnt, + jerry_api_value_t *ret_val_p); + extern EXTERN_C ssize_t jerry_api_string_to_char_buffer (const jerry_api_string_t *string_p, char *buffer_p, @@ -101,6 +109,8 @@ extern EXTERN_C jerry_api_string_t* jerry_api_create_string (const char *v); extern EXTERN_C jerry_api_object_t* jerry_api_create_object (void); +extern EXTERN_C +jerry_api_object_t* jerry_api_create_external_function (jerry_external_handler_t handler); extern EXTERN_C bool jerry_api_is_function (const jerry_api_object_t *object_p); diff --git a/jerry-core/jerry.cpp b/jerry-core/jerry.cpp index e8cf6c103..5596fec08 100644 --- a/jerry-core/jerry.cpp +++ b/jerry-core/jerry.cpp @@ -23,6 +23,7 @@ #include "ecma-gc.h" #include "ecma-helpers.h" #include "ecma-init-finalize.h" +#include "ecma-lex-env.h" #include "ecma-objects.h" #include "ecma-objects-general.h" #include "jerry.h" @@ -341,6 +342,26 @@ jerry_api_create_object (void) return ecma_op_create_object_object_noarg (); } /* jerry_api_create_object */ +/** + * Create an external function object + * + * Note: + * caller should release the object with jerry_api_release_object, just when the value becomes unnecessary. + * + * @return pointer to created external function object + */ +jerry_api_object_t* +jerry_api_create_external_function (jerry_external_handler_t handler) +{ + return ecma_op_create_function_object (NULL, + 0, + ecma_get_globl_lexical_environment (), + false, + 0, + true, + handler); +} /* jerry_api_create_object */ + /** * Check if the specified object is a function object. * diff --git a/jerry-core/vm/opcodes.cpp b/jerry-core/vm/opcodes.cpp index 8f74d2e77..f03031602 100644 --- a/jerry-core/vm/opcodes.cpp +++ b/jerry-core/vm/opcodes.cpp @@ -580,7 +580,9 @@ opfunc_func_expr_n (opcode_t opdata, /**< operation data */ params_number, scope_p, is_strict, - int_data->pos); + false, + int_data->pos, + 0); ret_value = set_variable_value (int_data, lit_oc, dst_var_idx, diff --git a/tests/unit/test_api.cpp b/tests/unit/test_api.cpp index e998c1026..eab1290bc 100644 --- a/tests/unit/test_api.cpp +++ b/tests/unit/test_api.cpp @@ -59,6 +59,17 @@ test_api_init_api_value_string (jerry_api_value_t *out_value_p, /**< out: API va out_value_p->v_string = jerry_api_create_string (v); } /* test_api_init_api_value_string */ +static bool +handler (const jerry_api_value_t *this_p, + const jerry_api_value_t *args_p [], + const int16_t args_cnt, + jerry_api_value_t *ret_val_p) +{ + printf("ok %p %p %d %p\n", this_p, args_p, args_cnt, ret_val_p); + return true; +} + + int main (void) { @@ -68,6 +79,7 @@ main (void) ssize_t sz; jerry_api_value_t val_t, val_foo, val_bar, val_A, val_A_prototype, val_a, val_a_foo; jerry_api_object_t* global_obj_p; + jerry_api_object_t* external_func_p; jerry_api_value_t res, args [2]; char buffer [16]; @@ -180,6 +192,14 @@ main (void) jerry_api_release_value (&res); jerry_api_release_value (&val_a_foo); + external_func_p = jerry_api_create_external_function (handler); + assert (external_func_p != NULL); + + is_ok = jerry_api_call_function (external_func_p, global_obj_p, &res, NULL, 0); + assert (is_ok); + + jerry_api_release_object (external_func_p); + jerry_api_release_value (&val_a); jerry_api_release_object (global_obj_p);