From 1ea178af6e4430550566bf7c95976a42dea2d63e Mon Sep 17 00:00:00 2001 From: rxbryan Date: Sat, 28 Jan 2023 21:40:02 +0100 Subject: [PATCH 01/12] add base support for exporting objects and classes in node_loader --- .../node_loader/bootstrap/lib/bootstrap.js | 95 ++++- .../node_loader/source/node_loader_impl.cpp | 326 +++++++++--------- 2 files changed, 260 insertions(+), 161 deletions(-) diff --git a/source/loaders/node_loader/bootstrap/lib/bootstrap.js b/source/loaders/node_loader/bootstrap/lib/bootstrap.js index 2ad993732..5c19d3e16 100644 --- a/source/loaders/node_loader/bootstrap/lib/bootstrap.js +++ b/source/loaders/node_loader/bootstrap/lib/bootstrap.js @@ -266,6 +266,7 @@ function node_loader_trampoline_discover_function(func) { if (node_loader_trampoline_is_callable(func)) { // Espree can't parse native code functions so we can do a workaround const str = func.toString().replace('{ [native code] }', '{}'); + const ast = espree.parse(`(${str})`, { ecmaVersion: 14 }); @@ -276,7 +277,7 @@ function node_loader_trampoline_discover_function(func) { if (node_loader_trampoline_is_valid_symbol(node)) { const args = node_loader_trampoline_discover_arguments(node); const discover = { - ptr: func, + func, signature: args, async: node.async, }; @@ -293,6 +294,91 @@ function node_loader_trampoline_discover_function(func) { } } +function node_loader_trampoline_discover_klass_attributes(node) { + let attributes = []; + for (let i = 0; i < node.length; i++) { + if (node[i].kind === 'constructor') + { + for (let exp of node[i].value.body.body) + { + if (exp.type === 'ExpressionStatement' && exp.expression.type === 'AssignmentExpression') { + let left = exp.expression.left; + + if (left.type == 'MemberExpression' && (left.object && left.object.type === 'ThisExpression')) { + attributes.push(left.property && left.property.name); + } + } + } + } + } + + return attributes; +} + +function node_loader_trampoline_discover_klass_methods(node, str) { + const ret = {}; + for (let method of node) { + if (method.type === 'MethodDefinition') { + let method_name = method.key.name; + if (method.kind === 'constructor') { + method_name = 'klass_' + method_name; + } + ret[method_name] = { + name: method.key.name, + signature: node_loader_trampoline_discover_arguments(method.value) + } + + if (method.kind === 'method' && str.substring(method.start-1, method.start+5) === 'static') { + ret[method_name].static = true; + } + } + } + + return ret +} + +function node_loader_trampoline_discover_klass(klass) { + try { + if (node_loader_trampoline_is_callable(klass)) { + const str = klass.toString(); + const ast = espree.parse(`(${str})`, { + ecmaVersion: 14 + }); + + const node = (ast.body[0].type === 'ExpressionStatement') && ast.body[0].expression; + if (node.type === 'ClassExpression') { + const methods = node_loader_trampoline_discover_klass_methods(node.body.body, str) + const discover = { + klass, + methods + }; + + if (node.id && node.id.name) { + discover['name'] = node.id.name; + } + + if (methods.klass_constructor) { + discover['attributes'] = node_loader_trampoline_discover_klass_attributes(node.body.body); + } + + return discover; + } + } + } catch (ex) { + console.log(`Exception while parsing '${klass}' in node_loader_trampoline_discover_klass`, ex); + } +} + +function node_loader_trampoline_discover_object(obj) { + if (typeof obj === 'object') { + const constructor = (obj && obj.constructor) && obj.constructor.name + if (constructor !== 'Object' && constructor !== 'Array') + return { + obj + }; + } +} + function node_loader_trampoline_discover(handle) { const discover = {}; @@ -305,8 +391,9 @@ function node_loader_trampoline_discover(handle) { for (let j = 0; j < keys.length; ++j) { const key = keys[j]; - const func = exports[key]; - const descriptor = node_loader_trampoline_discover_function(func); + const value = exports[key]; + const descriptor = node_loader_trampoline_discover_function(value) + || node_loader_trampoline_discover_klass(value) || node_loader_trampoline_discover_object(value); if (descriptor !== undefined) { discover[key] = descriptor; @@ -411,6 +498,8 @@ module.exports = ((impl, ptr) => { 'clear': node_loader_trampoline_clear, 'discover': node_loader_trampoline_discover, 'discover_function': node_loader_trampoline_discover_function, + 'discover_klass': node_loader_trampoline_discover_klass, + 'discover_object': node_loader_trampoline_discover_object, 'test': node_loader_trampoline_test, 'await_function': node_loader_trampoline_await_function(trampoline), 'await_future': node_loader_trampoline_await_future(trampoline), diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index c5aa00bfa..3e44006fb 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -3408,249 +3408,259 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); - /* Get function pointer */ - status = napi_get_named_property(env, function_descriptor, "ptr", &function_ptr); + /* Check if function pointer exists */ + bool is_func = false; - node_loader_impl_exception(env, status); - - /* Check function pointer type */ - status = napi_typeof(env, function_ptr, &valuetype); + status = napi_has_named_property(env, function_descriptor, "func", &is_func); node_loader_impl_exception(env, status); - if (valuetype != napi_function) + if (is_func == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function"); - } + /* Get function pointer */ + status = napi_get_named_property(env, function_descriptor, "func", &function_ptr); - /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check function pointer type */ + status = napi_typeof(env, function_ptr, &valuetype); - /* Check function pointer type */ - status = napi_typeof(env, function_sig, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function"); + } - if (valuetype != napi_object) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); - } + /* Get function signature */ + status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); - /* Get signature length */ - status = napi_get_array_length(env, function_sig, &function_sig_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check function pointer type */ + status = napi_typeof(env, function_sig, &valuetype); - /* Get function async */ - status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } - /* Check function async type */ - status = napi_typeof(env, function_is_async, &valuetype); + /* Get signature length */ + status = napi_get_array_length(env, function_sig, &function_sig_length); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (valuetype != napi_boolean) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); - } + /* Get function async */ + status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); - /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ - static const char types_str[] = "types"; - bool has_types = false; + node_loader_impl_exception(env, status); - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + /* Check function async type */ + status = napi_typeof(env, function_is_async, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_types == true) - { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + if (valuetype != napi_boolean) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); + } - node_loader_impl_exception(env, status); + /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ + static const char types_str[] = "types"; + bool has_types = false; - /* Check types array type */ - status = napi_typeof(env, function_types, &valuetype); + status = napi_has_named_property(env, function_descriptor, types_str, &has_types); node_loader_impl_exception(env, status); - if (valuetype != napi_object) + if (has_types == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); - } - } + status = napi_get_named_property(env, function_descriptor, types_str, &function_types); - /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ - static const char ret_str[] = "ret"; - bool has_ret = false; + node_loader_impl_exception(env, status); - status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + /* Check types array type */ + status = napi_typeof(env, function_types, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_ret == true) - { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); + } + } - node_loader_impl_exception(env, status); + /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ + static const char ret_str[] = "ret"; + bool has_ret = false; - /* Check return value type */ - status = napi_typeof(env, function_ret, &valuetype); + status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); node_loader_impl_exception(env, status); - if (valuetype != napi_string) + if (has_ret == true) { - napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); - } - } + status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); - /* Create node function */ - loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); - - /* Create reference to function pointer */ - status = napi_create_reference(env, function_ptr, 1, &node_func->func_ref); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Check return value type */ + status = napi_typeof(env, function_ret, &valuetype); - node_func->node_impl = discover_safe->node_impl; - node_func->impl = discover_safe->node_impl->impl; + node_loader_impl_exception(env, status); - /* Create function */ - function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + if (valuetype != napi_string) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); + } + } - if (f != NULL) - { - signature s = function_signature(f); - scope sp = context_scope(discover_safe->ctx); - bool is_async = false; + /* Create node function */ + loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); - /* Set function async */ - status = napi_get_value_bool(env, function_is_async, &is_async); + /* Create reference to function pointer */ + status = napi_create_reference(env, function_ptr, 1, &node_func->func_ref); node_loader_impl_exception(env, status); - function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + node_func->node_impl = discover_safe->node_impl; + node_func->impl = discover_safe->node_impl->impl; - /* Set return value if any */ - if (has_ret) + /* Create function */ + function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + + if (f != NULL) { - size_t return_type_length; - char *return_type_str = NULL; + signature s = function_signature(f); + scope sp = context_scope(discover_safe->ctx); + bool is_async = false; - /* Get return value string length */ - status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + /* Set function async */ + status = napi_get_value_bool(env, function_is_async, &is_async); node_loader_impl_exception(env, status); - if (return_type_length > 0) - { - return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); - } + function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); - if (return_type_str != NULL) + /* Set return value if any */ + if (has_ret) { - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); - - node_loader_impl_exception(env, status); + size_t return_type_length; + char *return_type_str = NULL; - signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); - - free(return_type_str); - } - } + /* Get return value string length */ + status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); - /* Set signature */ - for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) - { - napi_value parameter_name; - size_t parameter_name_length; - char *parameter_name_str = NULL; + node_loader_impl_exception(env, status); - /* Get signature parameter name */ - status = napi_get_element(env, function_sig, arg_index, ¶meter_name); + if (return_type_length > 0) + { + return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); + } - node_loader_impl_exception(env, status); + if (return_type_str != NULL) + { + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); - /* Get parameter name string length */ - status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + signature_set_return(s, loader_impl_type(discover_safe->node_impl->impl, return_type_str)); - if (parameter_name_length > 0) - { - parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + free(return_type_str); + } } - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); - - node_loader_impl_exception(env, status); - - /* Check if type info is available */ - if (has_types) + /* Set signature */ + for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) { - napi_value parameter_type; - size_t parameter_type_length; - char *parameter_type_str = NULL; + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; - /* Get signature parameter type */ - status = napi_get_element(env, function_types, arg_index, ¶meter_type); + /* Get signature parameter name */ + status = napi_get_element(env, function_sig, arg_index, ¶meter_name); node_loader_impl_exception(env, status); - /* Get parameter type string length */ - status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); node_loader_impl_exception(env, status); - if (parameter_type_length > 0) + if (parameter_name_length > 0) { - parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); } - /* Get parameter type string */ - status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); node_loader_impl_exception(env, status); - signature_set(s, (size_t)arg_index, parameter_name_str, loader_impl_type(discover_safe->node_impl->impl, parameter_type_str)); + /* Check if type info is available */ + if (has_types) + { + napi_value parameter_type; + size_t parameter_type_length; + char *parameter_type_str = NULL; + + /* Get signature parameter type */ + status = napi_get_element(env, function_types, arg_index, ¶meter_type); + + node_loader_impl_exception(env, status); + + /* Get parameter type string length */ + status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + + node_loader_impl_exception(env, status); - if (parameter_type_str != NULL) + if (parameter_type_length > 0) + { + parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + } + + /* Get parameter type string */ + status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + + node_loader_impl_exception(env, status); + + signature_set(s, (size_t)arg_index, parameter_name_str, loader_impl_type(discover_safe->node_impl->impl, parameter_type_str)); + + if (parameter_type_str != NULL) + { + free(parameter_type_str); + } + } + else { - free(parameter_type_str); + signature_set(s, (size_t)arg_index, parameter_name_str, NULL); + } + + if (parameter_name_str != NULL) + { + free(parameter_name_str); } } - else - { - signature_set(s, (size_t)arg_index, parameter_name_str, NULL); - } - if (parameter_name_str != NULL) + value v = value_create_function(f); + + if (scope_define(sp, function_name(f), v) != 0) { - free(parameter_name_str); + value_type_destroy(v); + discover_safe->result = 1; + break; } } - - value v = value_create_function(f); - - if (scope_define(sp, function_name(f), v) != 0) + else { - value_type_destroy(v); + free(node_func); discover_safe->result = 1; break; } } - else - { - free(node_func); - discover_safe->result = 1; - break; - } free(func_name_str); } @@ -4009,7 +4019,7 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi #endif /* Store the amount of async handles that we have for the node loader, - * so we can count the user defined async handles */ + * so we can count the user defined async handles */ node_impl->base_active_handles = node_loader_impl_async_handles_count(node_impl); node_impl->extra_active_handles.store(0); node_impl->event_loop_empty.store(false); @@ -5256,10 +5266,10 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env } /* NodeJS Loader needs to register that it is destroyed, because after this step - * some destructors can be still triggered, before the node_loader->destroy() has - * finished, so this destructors will try to execute the NodeJS unrefs while having - * the runtime (at least the NodeJS Loader related part) destroyed. - */ + * some destructors can be still triggered, before the node_loader->destroy() has + * finished, so this destructors will try to execute the NodeJS unrefs while having + * the runtime (at least the NodeJS Loader related part) destroyed. + */ loader_set_destroyed(node_impl->impl); } From 1aa8e5009e688439e91aa5336d34fa0e97aa9965 Mon Sep 17 00:00:00 2001 From: rxbryan Date: Tue, 14 Feb 2023 16:23:37 +0100 Subject: [PATCH 02/12] add support for classes in node_loader --- .../node_loader/source/node_loader_impl.cpp | 1694 ++++++++++++++--- .../reflect/include/reflect/reflect_class.h | 8 + .../reflect/include/reflect/reflect_object.h | 2 + source/reflect/source/reflect_class.c | 48 + source/reflect/source/reflect_object.c | 10 + 5 files changed, 1478 insertions(+), 284 deletions(-) diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index 3e44006fb..1b8029fd5 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -48,14 +48,6 @@ extern char **environ; #include #include -#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) - #include - - /* Required for the DelayLoad hook interposition, solves bug of NodeJS extensions requiring node.exe instead of node.dll*/ - #include - #pragma intrinsic(_ReturnAddress) -#endif - #include #include @@ -177,6 +169,12 @@ typedef struct loader_impl_async_clear_safe_type *loader_impl_async_clear_safe; struct loader_impl_async_discover_function_safe_type; typedef struct loader_impl_async_discover_function_safe_type *loader_impl_async_discover_function_safe; +struct loader_impl_discover_klass_safe_type; +typedef struct loader_impl_discover_klass_safe_type *loader_impl_discover_klass_safe; + +struct loader_impl_discover_object_safe_type; +typedef struct loader_impl_discover_object_safe_type *loader_impl_discover_object_safe; + struct loader_impl_async_discover_safe_type; typedef struct loader_impl_async_discover_safe_type *loader_impl_async_discover_safe; @@ -313,6 +311,22 @@ typedef struct loader_impl_node_future_type } * loader_impl_node_future; +typedef struct loader_impl_node_object_type +{ + loader_impl_node node_impl; + loader_impl impl; + napi_ref obj_ref; + +} * loader_impl_node_object; + +typedef struct loader_impl_node_class_type +{ + loader_impl_node node_impl; + loader_impl impl; + napi_ref klass_ref; + +} * loader_impl_node_class; + struct loader_impl_async_initialize_safe_type { loader_impl_node node_impl; @@ -355,6 +369,18 @@ struct loader_impl_async_discover_function_safe_type napi_value func; }; +struct loader_impl_discover_klass_safe_type +{ + loader_impl_node node_impl; + napi_value klass; +}; + +struct loader_impl_discover_object_safe_type +{ + loader_impl_node node_impl; + napi_value obj; +}; + struct loader_impl_async_discover_safe_type { loader_impl_node node_impl; @@ -446,6 +472,19 @@ typedef struct loader_impl_napi_to_value_callback_closure_type } * loader_impl_napi_to_value_callback_closure; +typedef struct loader_impl_class_constructor_callback_closure_type +{ + klass cls; + loader_impl_node node_impl; +} * loader_impl_class_constructor_callback_closure; + +typedef struct loader_impl_class_property_callback_closure_type +{ + klass cls; + loader_impl_node node_impl; + char *prop_name; +} * loader_impl_class_property_callback_closure; + /* Type conversion */ static napi_value node_loader_impl_napi_to_value_callback(napi_env env, napi_callback_info info); @@ -469,6 +508,40 @@ static void future_node_interface_destroy(future f, future_impl impl); static future_interface future_node_singleton(void); +/* Object */ +static int node_object_interface_create(object obj, object_impl impl); + +static value node_object_interface_get(object obj, object_impl impl, struct accessor_type *accessor); + +static int node_object_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v); + +static value node_object_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t argc); + +static value node_object_interface_method_await(object obj, object_impl impl, method m, object_args args, size_t size, object_resolve_callback resolve, object_reject_callback reject, void *ctx); + +static int node_object_interface_destructor(object obj, object_impl impl); + +static void node_object_interface_destroy(object obj, object_impl impl); + +static object_interface node_object_interface_singleton(void); + +/* Class */ +static int node_class_interface_create(klass cls, class_impl impl); + +static object node_class_interface_constructor(klass cls, class_impl impl, const char *name, constructor ctor, class_args args, size_t argc); + +static value node_class_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor); + +static int node_class_interface_static_set(klass cls, class_impl impl, struct accessor_type *accessor, value v); + +static value node_class_interface_static_invoke(klass cls, class_impl impl, method m, class_args args, size_t argc); + +static value node_class_interface_static_await(klass cls, class_impl impl, method m, class_args args, size_t size, class_resolve_callback resolve, class_reject_callback reject, void *ctx); + +static void node_class_interface_destroy(klass cls, class_impl impl); + +static class_interface node_class_interface_singleton(void); + /* JavaScript Thread Safe */ static void node_loader_impl_initialize_safe(napi_env env, loader_impl_async_initialize_safe initialize_safe); @@ -512,6 +585,12 @@ static napi_value node_loader_impl_async_clear_safe(napi_env env, napi_callback_ static value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_discover_function_safe discover_function_safe); +static value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_klass_safe discover_klass_safe); + +/* +static int node_loader_impl_discover_object_safe(napi_env env, loader_impl_discover_object_safe discover_object_safe); +*/ + static void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_safe discover_safe); static napi_value node_loader_impl_async_discover_safe(napi_env env, napi_callback_info info); @@ -539,12 +618,6 @@ static int64_t node_loader_impl_async_handles_count(loader_impl_node node_impl); static void node_loader_impl_try_destroy(loader_impl_node node_impl); -#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) -/* Required for the DelayLoad hook interposition, solves bug of NodeJS extensions requiring node.exe instead of node.dll */ -static HMODULE node_loader_node_dll_handle = NULL; -static HMODULE (*get_module_handle_a_ptr)(_In_opt_ LPCSTR) = NULL; /* TODO: Implement W version too? */ -#endif - /* -- Methods -- */ void node_loader_impl_exception(napi_env env, napi_status status) @@ -812,6 +885,243 @@ char *node_loader_impl_get_property_as_char(napi_env env, napi_value obj, const return str; } +static type_id *node_loader_impl_type_ids(void *args[], size_t size) +{ + type_id *ids = NULL; + + if (size > 0) + { + ids = (type_id *)malloc(sizeof(type_id) * size); + + for (size_t iterator = 0; iterator < size; ++iterator) + { + type_id id = value_type_id(args[iterator]); + + if (id >= 0 && id < TYPE_SIZE) + { + ids[iterator] = id; + } + else + { + ids[iterator] = METACALL_INVALID; + } + } + } + + return ids; +} + +static int node_loader_impl_class_check_method_type(signature s, type_id arg_ids[], size_t argc) +{ + size_t s_count = signature_count(s); + if (s_count == argc) + { + for (size_t i = 0; i < s_count; i++) + { + type t = signature_get_type(s, i); + if (arg_ids[i] != type_index(t)) + { + return 1; + } + } + return 0; + } + return 1; +} + +static napi_value node_loader_impl_class_method_property_callback(napi_env env, napi_callback_info info) +{ + napi_status status; + loader_impl_async_safe_cast closure_cast = { NULL }; + size_t argc = 1; + napi_value *argv = nullptr; + napi_value this_obj; + + napi_get_cb_info(env, info, &argc, NULL, &this_obj, &closure_cast.ptr); + closure_cast.safe->node_impl->env = env; + + if (argc != 1) + { + argv = new napi_value[argc]; + } + + napi_get_cb_info(env, info, &argc, argv, NULL, NULL); + + value v_obj; + status = napi_unwrap(env, this_obj, &v_obj); + node_loader_impl_exception(env, status); + + object obj_impl = value_to_object(v_obj); + + void **args = new void *[argc]; + + for (size_t iterator = 0; iterator < argc; ++iterator) + { + args[iterator] = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[iterator]); + } + + type_id *arg_ids = node_loader_impl_type_ids(args, argc); + vector methods = object_methods(obj_impl, closure_cast.safe->prop_name); + size_t n_methods = vector_size(methods); + + method m; + signature s; + if (n_methods == 1) + { + m = vector_at_type(methods, 0, method); + } + else if (n_methods > 1) + { + for (size_t iterator = 0; iterator < n_methods; ++iterator) + { + m = vector_at_type(methods, iterator, method); + s = method_signature(m); + if (node_loader_impl_class_check_method_type(s, arg_ids, argc) == 0) + { + break; + } + else + { + m = NULL; + } + } + } + + if (m == NULL) + { + napi_value undefined; + status = napi_get_undefined(env, &undefined); + node_loader_impl_exception(env, status); + return undefined; + } + + value result = object_call(obj_impl, m, args, argc); + return node_loader_impl_value_to_napi(closure_cast.safe->node_impl, env, result); +} + +static napi_value node_loader_impl_class_set_data_property_callback(napi_env env, napi_callback_info info) +{ + napi_status status; + loader_impl_async_safe_cast closure_cast = { NULL }; + size_t argc = 1; + napi_value *argv = new napi_value[argc]; + napi_value this_obj; + + napi_get_cb_info(env, info, &argc, argv, &this_obj, &closure_cast.ptr); + closure_cast.safe->node_impl->env = env; + + value arg = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[0]); + + value obj; + napi_unwrap(env, this_obj, &obj); + object obj_impl = value_to_object(obj); + + int res = object_set(obj_impl, closure_cast.safe->prop_name, arg); + + if (res != 0) + { + std::string error_msg = "NodeJS Loader Failed to set property:"; + error_msg += closure_cast.safe->prop_name; + error_msg += "in class: "; + error_msg += object_name(obj_impl); + napi_throw_error(env, NULL, error_msg.c_str()); + } + napi_value ret; + status = napi_create_int32(env, res, &ret); + node_loader_impl_exception(env, status); + + return ret; +} + +static napi_value node_loader_impl_class_get_data_property_callback(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast closure_cast = { NULL }; + napi_value this_obj; + napi_get_cb_info(env, info, NULL, NULL, &this_obj, &closure_cast.ptr); + closure_cast.safe->node_impl->env = env; + + value obj; + napi_unwrap(env, this_obj, &obj); + object obj_impl = value_to_object(obj); + value v = object_get(obj_impl, closure_cast.safe->prop_name); + + return node_loader_impl_value_to_napi(closure_cast.safe->node_impl, env, v); +} + +static napi_value node_loader_impl_class_constructor_callback(napi_env env, napi_callback_info info) +{ + napi_status status; + loader_impl_async_safe_cast closure_cast = { NULL }; + + napi_get_cb_info(env, info, NULL, NULL, NULL, &closure_cast.ptr); + /* Set environment */ + closure_cast.safe->node_impl->env = env; + constructor ctor = class_default_constructor(closure_cast.safe->cls); + + char *klass_name_str = const_cast(class_name(closure_cast.safe->cls)); + std::string error_msg = "NodeJS Loader No default constructor in class: "; + error_msg += klass_name_str; + if (ctor == NULL) + { + napi_throw_error(env, NULL, error_msg.c_str()); + } + + size_t argc = constructor_count(ctor); + napi_value *argv = new napi_value[argc]; + + napi_get_cb_info(env, info, &argc, argv, NULL, NULL); + + void **args = new void *[argc]; + + for (size_t iterator = 0; iterator < argc; ++iterator) + { + args[iterator] = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[iterator]); + } + + type_id *ids = node_loader_impl_type_ids(args, argc); + + ctor = class_constructor(closure_cast.safe->cls, ids, argc); + + //convert class_name to lower case + while (*klass_name_str != '\0') + { + *klass_name_str = std::tolower(*klass_name_str); + klass_name_str++; + } + + object o = class_new(closure_cast.safe->cls, klass_name_str, ctor, args, argc); + + if (ids != NULL) + { + free(ids); + } + + if (o == NULL) + { + return NULL; + } + + value v = value_create_object(o); + + if (v == NULL) + { + object_destroy(o); + error_msg = "NodeJS Loader Failed to create object for class: "; + error_msg += klass_name_str; + napi_throw_error(env, NULL, error_msg.c_str()); + } + + napi_value js_object; + status = napi_create_object(env, &js_object); + node_loader_impl_exception(env, status); + + //Todo: Implement finalize callback + status = napi_wrap(env, js_object, v, NULL, NULL, NULL); + node_loader_impl_exception(env, status); + + return js_object; +} + value node_loader_impl_napi_to_value(loader_impl_node node_impl, napi_env env, napi_value recv, napi_value v) { value ret = NULL; @@ -959,7 +1269,10 @@ value node_loader_impl_napi_to_value(loader_impl_node node_impl, napi_env env, n status = napi_create_reference(env, v, 1, &node_future->promise_ref); node_loader_impl_exception(env, status); - } + } /* + else if (node_loader_impl_is_object(env, v)) + { + }*/ else { /* TODO: Strict check if it is an object (map) */ @@ -1028,6 +1341,19 @@ value node_loader_impl_napi_to_value(loader_impl_node node_impl, napi_env env, n } else if (valuetype == napi_function) { + /* Check if function is an ES6 class */ + struct loader_impl_discover_klass_safe_type discover_klass_safe = { + node_impl, + v + }; + + ret = node_loader_impl_discover_klass_safe(env, &discover_klass_safe); + + if (ret != NULL) + { + return ret; + } + struct loader_impl_async_discover_function_safe_type discover_function_safe = { node_impl, v @@ -1264,19 +1590,113 @@ napi_value node_loader_impl_value_to_napi(loader_impl_node node_impl, napi_env e } else if (id == TYPE_CLASS) { - /* TODO */ - /* napi_throw_error(env, NULL, "NodeJS Loader class is not implemented"); */ - - /* klass cls = value_to_class(arg_value); + vector methods = class_method_names(cls); + vector static_methods = class_static_method_names(cls); + vector attributes = class_attribute_names(cls); + vector static_attributes = class_static_attribute_names(cls); - napi_define_class(env, cls->name, NAPI_AUTO_LENGTH, ) - */ + size_t property_count = vector_size(methods) + vector_size(static_methods) + vector_size(attributes) + vector_size(static_attributes); + napi_property_descriptor *properties = new napi_property_descriptor[property_count]; + + size_t iterator = 0; + //Define method properties + for (size_t i = 0; i < vector_size(methods) && iterator < property_count; iterator++, i++) + { + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; + + closure->cls = cls; + closure->node_impl = node_impl; + closure->prop_name = vector_at_type(methods, i, char *); + + properties[iterator].utf8name = vector_at_type(methods, i, char *); + properties[iterator].name = NULL; + properties[iterator].method = node_loader_impl_class_method_property_callback; + properties[iterator].getter = NULL; + properties[iterator].setter = NULL; + properties[iterator].value = NULL; + properties[iterator].attributes = napi_default_method; + properties[iterator].data = (void *)closure; + } + + //Define static method properties + for (size_t i = 0; i < vector_size(static_methods) && iterator < property_count; iterator++, i++) + { + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; + + closure->cls = cls; + closure->node_impl = node_impl; + closure->prop_name = vector_at_type(static_methods, i, char *); + + properties[iterator].utf8name = vector_at_type(static_methods, i, char *); + properties[iterator].name = NULL; + properties[iterator].method = node_loader_impl_class_method_property_callback; + properties[iterator].getter = NULL; + properties[iterator].setter = NULL; + properties[iterator].value = NULL; + properties[iterator].attributes = napi_static; + properties[iterator].data = (void *)closure; + } + + //Define attribute properties + for (size_t i = 0; i < vector_size(attributes) && iterator < property_count; iterator++, i++) + { + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; + + closure->cls = cls; + closure->node_impl = node_impl; + closure->prop_name = vector_at_type(attributes, i, char *); + + properties[iterator].utf8name = vector_at_type(attributes, i, char *); + properties[iterator].name = NULL; + properties[iterator].method = NULL; + properties[iterator].getter = node_loader_impl_class_get_data_property_callback; + properties[iterator].setter = node_loader_impl_class_set_data_property_callback; + properties[iterator].value = NULL; + properties[iterator].attributes = napi_default_jsproperty; + properties[iterator].data = (void *)closure; + } + + //NodeJS doesn't support static attributes so we define them as normal attributes + for (size_t i = 0; i < vector_size(static_attributes) && iterator < property_count; iterator++, i++) + { + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; + + closure->cls = cls; + closure->node_impl = node_impl; + closure->prop_name = vector_at_type(static_attributes, i, char *); + + properties[iterator].utf8name = vector_at_type(static_attributes, i, char *); + properties[iterator].name = NULL; + properties[iterator].method = NULL; + properties[iterator].getter = node_loader_impl_class_get_data_property_callback; + properties[iterator].setter = node_loader_impl_class_set_data_property_callback; + properties[iterator].value = NULL; + properties[iterator].attributes = napi_default_jsproperty; + properties[iterator].data = (void *)closure; + } + + napi_value result; + loader_impl_class_constructor_callback_closure ctor_data = new struct loader_impl_class_constructor_callback_closure_type; + + ctor_data->node_impl = node_impl; + ctor_data->cls = cls; + + status = napi_define_class(env, class_name(cls), NAPI_AUTO_LENGTH, node_loader_impl_class_constructor_callback, (void *)ctor_data, property_count, properties, &result); + node_loader_impl_exception(env, status); + return result; } else if (id == TYPE_OBJECT) { /* TODO */ napi_throw_error(env, NULL, "NodeJS Loader object is not implemented"); + /* + //Construct object dynamically + object obj = value_to_object(v); + klass cls = object_get_class(obj); + napi_value kl = node_loader_impl_value_to_napi(node_impl, env, value_create_class(cls)); + status = napi_new_instance(env, kl, ) + */ } else if (id == TYPE_NULL) { @@ -1772,63 +2192,364 @@ future_interface future_node_singleton() return &node_future_interface; } -void node_loader_impl_initialize_safe(napi_env env, loader_impl_async_initialize_safe initialize_safe) +int node_object_interface_create(object obj, object_impl impl) { - static const char initialize_str[] = "initialize"; - napi_value function_table_object; - napi_value initialize_str_value; - bool result = false; - napi_handle_scope handle_scope; - loader_impl_node node_impl = initialize_safe->node_impl; + (void)obj; + (void)impl; - /* Create scope */ - napi_status status = napi_open_handle_scope(env, &handle_scope); + /* + loader_impl_node_object node_obj = (loader_impl_node_object)impl; - node_loader_impl_exception(env, status); + node_obj->node_impl = NULL; + node_obj->impl = NULL;*/ - /* Get function table object from reference */ - status = napi_get_reference_value(env, node_impl->function_table_object_ref, &function_table_object); + return 0; +} - node_loader_impl_exception(env, status); +value node_object_interface_get(object obj, object_impl impl, struct accessor_type *accessor) +{ + (void)obj; - /* Create function string */ - status = napi_create_string_utf8(env, initialize_str, sizeof(initialize_str) - 1, &initialize_str_value); + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + napi_value obj_value = NULL; + napi_status status; - node_loader_impl_exception(env, status); + status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); + node_loader_impl_exception(node_obj->node_impl->env, status); - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, initialize_str_value, &result); + attribute attr = accessor->data.attr; + const char *key = (const char *)attribute_name(attr); + //type fieldType = (type)attribute_type(attr); - node_loader_impl_exception(env, status); + napi_value prop_value = NULL; + status = napi_get_named_property(node_obj->node_impl->env, obj_value, key, &prop_value); + node_loader_impl_exception(node_obj->node_impl->env, status); - if (result == true) - { - napi_value function_trampoline_initialize; - napi_valuetype valuetype; - napi_value argv[1]; + value v = node_loader_impl_napi_to_value(node_obj->node_impl, node_obj->node_impl->env, nullptr, prop_value); - status = napi_get_named_property(env, function_table_object, initialize_str, &function_trampoline_initialize); + return v; +} - node_loader_impl_exception(env, status); +int node_object_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v) +{ + (void)obj; - status = napi_typeof(env, function_trampoline_initialize, &valuetype); + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + napi_value obj_value = NULL; + napi_status status; - node_loader_impl_exception(env, status); + status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); + node_loader_impl_exception(node_obj->node_impl->env, status); - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function initialize in function table object"); - } + attribute attr = accessor->data.attr; + const char *key = (const char *)attribute_name(attr); + //type fieldType = (type)attribute_type(attr); - /* Create parameters */ - status = napi_create_string_utf8(env, initialize_safe->loader_library_path, strlen(initialize_safe->loader_library_path), &argv[0]); + napi_value prop_value = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, v); - node_loader_impl_exception(env, status); + status = napi_set_named_property(node_obj->node_impl->env, obj_value, key, prop_value); - /* Call to load from file function */ - napi_value global, return_value; + node_loader_impl_exception(node_obj->node_impl->env, status); - status = napi_get_reference_value(env, node_impl->global_ref, &global); + return 0; +} + +value node_object_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t argc) +{ + (void)obj; + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + napi_value obj_value; + napi_value result; + napi_status status; + + status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); + node_loader_impl_exception(node_obj->node_impl->env, status); + + const char *m_name = method_name(m); + + napi_value m_value = NULL; + status = napi_get_named_property(node_obj->node_impl->env, obj_value, m_name, &m_value); + node_loader_impl_exception(node_obj->node_impl->env, status); + + napi_value *m_args = new napi_value[argc]; + + for (size_t i = 0; i < argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, args[i]); + } + + napi_call_function(node_obj->node_impl->env, obj_value, m_value, argc, m_args, &result); + + value v = node_loader_impl_napi_to_value(node_obj->node_impl, node_obj->node_impl->env, nullptr, result); + + delete[] m_args; + + return v; +} + +value node_object_interface_method_await(object obj, object_impl impl, method m, object_args args, size_t size, object_resolve_callback resolve, object_reject_callback reject, void *ctx) +{ + // Class methods in JavaScript can't be async + (void)obj; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +int node_object_interface_destructor(object obj, object_impl impl) +{ + (void)obj; + (void)impl; + + /* Destructors are automatically called when ref count is zero and GC happens */ + + return 0; +} + +void node_object_interface_destroy(object obj, object_impl impl) +{ + (void)obj; + + loader_impl_node_object node_obj = (loader_impl_node_object)impl; + + if (node_obj != NULL) + { + if (loader_is_destroyed(node_obj->impl) != 0) + { + //Todo + } + + free(node_obj); + } +} + +object_interface node_object_interface_singleton(void) +{ + static struct object_interface_type node_object_interface = { + &node_object_interface_create, + &node_object_interface_get, + &node_object_interface_set, + &node_object_interface_method_invoke, + &node_object_interface_method_await, + &node_object_interface_destructor, + &node_object_interface_destroy + }; + + return &node_object_interface; +} + +int node_class_interface_create(klass cls, class_impl impl) +{ + (void)cls; + (void)impl; + + return 0; +} + +object node_class_interface_constructor(klass cls, class_impl impl, const char *name, constructor ctor, class_args args, size_t argc) +{ + (void)ctor; + + loader_impl_node_class node_klass = (loader_impl_node_class)impl; + + loader_impl_node_object node_obj = new struct loader_impl_node_object_type; + + object obj = object_create(name, ACCESSOR_TYPE_DYNAMIC, node_obj, &node_object_interface_singleton, cls); + + if (obj == NULL) + { + return NULL; + } + + napi_value *m_args = new napi_value[argc]; + + for (size_t i = 0; i < argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, args[i]); + } + + napi_status status; + napi_value klass_cons; + status = napi_get_reference_value(node_obj->node_impl->env, node_klass->klass_ref, &klass_cons); + node_loader_impl_exception(node_obj->node_impl->env, status); + + napi_value result; + status = napi_new_instance(node_obj->node_impl->env, klass_cons, argc, m_args, &result); + node_loader_impl_exception(node_obj->node_impl->env, status); + + //Todo check that object is an instance of constructor + + status = napi_create_reference(node_obj->node_impl->env, result, 1, &node_obj->obj_ref); + node_loader_impl_exception(node_obj->node_impl->env, status); + + delete[] m_args; + + return obj; +} + +value node_class_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor) +{ + (void)cls; + (void)impl; + (void)accessor; + + return NULL; +} + +int node_class_interface_static_set(klass cls, class_impl impl, struct accessor_type *accessor, value v) +{ + (void)cls; + (void)impl; + (void)accessor; + (void)v; + + return 0; +} + +value node_class_interface_static_invoke(klass cls, class_impl impl, method m, class_args args, size_t argc) +{ + (void)cls; + loader_impl_node_class node_klass = (loader_impl_node_class)impl; + napi_value klass_cons; + napi_value result; + napi_status status; + + status = napi_get_reference_value(node_klass->node_impl->env, node_klass->klass_ref, &klass_cons); + node_loader_impl_exception(node_klass->node_impl->env, status); + + const char *m_name = method_name(m); + + napi_value m_value = NULL; + status = napi_get_named_property(node_klass->node_impl->env, klass_cons, m_name, &m_value); + node_loader_impl_exception(node_klass->node_impl->env, status); + + napi_value *m_args = new napi_value[argc]; + + for (size_t i = 0; i < argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(node_klass->node_impl, node_klass->node_impl->env, args[i]); + } + + status = napi_call_function(node_klass->node_impl->env, klass_cons, m_value, argc, m_args, &result); + node_loader_impl_exception(node_klass->node_impl->env, status); + + value v = node_loader_impl_napi_to_value(node_klass->node_impl, node_klass->node_impl->env, nullptr, result); + + delete[] m_args; + + return v; +} + +value node_class_interface_static_await(klass cls, class_impl impl, method m, class_args args, size_t size, class_resolve_callback resolve, class_reject_callback reject, void *ctx) +{ + // Class methods in JavaScript can't be async + (void)cls; + (void)impl; + (void)m; + (void)args; + (void)size; + (void)resolve; + (void)reject; + (void)ctx; + + return NULL; +} + +void node_class_interface_destroy(klass cls, class_impl impl) +{ + loader_impl_node_class node_class = (loader_impl_node_class)impl; + + (void)cls; + + if (node_class != NULL) + { + if (loader_is_destroyed(node_class->impl) != 0) + { + //TODO + } + + free(node_class); + } +} + +class_interface node_class_interface_singleton(void) +{ + static struct class_interface_type node_class_interface = { + &node_class_interface_create, + &node_class_interface_constructor, + &node_class_interface_static_get, + &node_class_interface_static_set, + &node_class_interface_static_invoke, + &node_class_interface_static_await, + &node_class_interface_destroy + }; + + return &node_class_interface; +} + +void node_loader_impl_initialize_safe(napi_env env, loader_impl_async_initialize_safe initialize_safe) +{ + static const char initialize_str[] = "initialize"; + napi_value function_table_object; + napi_value initialize_str_value; + bool result = false; + napi_handle_scope handle_scope; + loader_impl_node node_impl = initialize_safe->node_impl; + + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); + + node_loader_impl_exception(env, status); + + /* Get function table object from reference */ + status = napi_get_reference_value(env, node_impl->function_table_object_ref, &function_table_object); + + node_loader_impl_exception(env, status); + + /* Create function string */ + status = napi_create_string_utf8(env, initialize_str, sizeof(initialize_str) - 1, &initialize_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, initialize_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value function_trampoline_initialize; + napi_valuetype valuetype; + napi_value argv[1]; + + status = napi_get_named_property(env, function_table_object, initialize_str, &function_trampoline_initialize); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_trampoline_initialize, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function initialize in function table object"); + } + + /* Create parameters */ + status = napi_create_string_utf8(env, initialize_safe->loader_library_path, strlen(initialize_safe->loader_library_path), &argv[0]); + + node_loader_impl_exception(env, status); + + /* Call to load from file function */ + napi_value global, return_value; + + status = napi_get_reference_value(env, node_impl->global_ref, &global); node_loader_impl_exception(env, status); @@ -3032,267 +3753,723 @@ value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_di { size_t func_name_length = 0; - status = napi_get_named_property(env, function_descriptor, "name", &func_name); + status = napi_get_named_property(env, function_descriptor, "name", &func_name); + + node_loader_impl_exception(env, status); + + status = napi_get_value_string_utf8(env, func_name, NULL, 0, &func_name_length); + + node_loader_impl_exception(env, status); + + if (func_name_length > 0) + { + func_name_str = static_cast(malloc(sizeof(char) * (func_name_length + 1))); + } + + /* Get function name */ + status = napi_get_value_string_utf8(env, func_name, func_name_str, func_name_length + 1, &func_name_length); + + node_loader_impl_exception(env, status); + } + + /* Retrieve the function properties */ + napi_value function_sig; + napi_value function_types = nullptr; + napi_value function_ret = nullptr; + napi_value function_is_async; + uint32_t function_sig_length; + + /* Get function signature */ + status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + + node_loader_impl_exception(env, status); + + /* Check function pointer type */ + status = napi_typeof(env, function_sig, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } + + /* Get signature length */ + status = napi_get_array_length(env, function_sig, &function_sig_length); + + node_loader_impl_exception(env, status); + + /* Get function async */ + status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + + node_loader_impl_exception(env, status); + + /* Check function async type */ + status = napi_typeof(env, function_is_async, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_boolean) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); + } + + /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ + static const char types_str[] = "types"; + bool has_types = false; + + status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + + node_loader_impl_exception(env, status); + + if (has_types == true) + { + status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + + node_loader_impl_exception(env, status); + + /* Check types array type */ + status = napi_typeof(env, function_types, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); + } + } + + /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ + static const char ret_str[] = "ret"; + bool has_ret = false; + + status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + + node_loader_impl_exception(env, status); + + if (has_ret == true) + { + status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + + node_loader_impl_exception(env, status); + + /* Check return value type */ + status = napi_typeof(env, function_ret, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_string) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); + } + } + + /* Create node function */ + loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); + + /* Create reference to function pointer */ + status = napi_create_reference(env, discover_function_safe->func, 1, &node_func->func_ref); + + node_loader_impl_exception(env, status); + + node_func->node_impl = discover_function_safe->node_impl; + node_func->impl = discover_function_safe->node_impl->impl; + + /* Create function */ + function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + + if (f != NULL) + { + signature s = function_signature(f); + bool is_async = false; + + /* Set function async */ + status = napi_get_value_bool(env, function_is_async, &is_async); + + node_loader_impl_exception(env, status); + + function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + + /* Set return value if any */ + if (has_ret) + { + size_t return_type_length; + char *return_type_str = NULL; + + /* Get return value string length */ + status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + + node_loader_impl_exception(env, status); + + if (return_type_length > 0) + { + return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); + } + + if (return_type_str != NULL) + { + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); + + node_loader_impl_exception(env, status); + + /* TODO: Implement return type with impl (may need an important refactor) */ + signature_set_return(s, /*loader_impl_type(discover_function_safe->impl, return_type_str)*/ NULL); + + free(return_type_str); + } + } + + /* Set signature */ + for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) + { + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; + + /* Get signature parameter name */ + status = napi_get_element(env, function_sig, arg_index, ¶meter_name); + + node_loader_impl_exception(env, status); + + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); + + node_loader_impl_exception(env, status); + + if (parameter_name_length > 0) + { + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + } + + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); + + node_loader_impl_exception(env, status); + + /* Check if type info is available */ + if (has_types) + { + napi_value parameter_type; + size_t parameter_type_length; + char *parameter_type_str = NULL; + + /* Get signature parameter type */ + status = napi_get_element(env, function_types, arg_index, ¶meter_type); + + node_loader_impl_exception(env, status); + + /* Get parameter type string length */ + status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + + node_loader_impl_exception(env, status); + + if (parameter_type_length > 0) + { + parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + } + + /* Get parameter type string */ + status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + + node_loader_impl_exception(env, status); + + /* TODO: Implement parameter type with impl (may need an important refactor) */ + signature_set(s, (size_t)arg_index, parameter_name_str, /*loader_impl_type(discover_function_safe->impl, parameter_type_str)*/ NULL); + + if (parameter_type_str != NULL) + { + free(parameter_type_str); + } + } + else + { + signature_set(s, (size_t)arg_index, parameter_name_str, NULL); + } + + if (parameter_name_str != NULL) + { + free(parameter_name_str); + } + } + + /* Create value with the function */ + function_value = value_create_function(f); + } + else + { + free(node_func); + } + + if (func_name_str != NULL) + { + free(func_name_str); + } + } + + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); + + return function_value; +} + +value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_klass_safe discover_klass_safe) +{ + static const char discover_klass_str[] = "discover_klass"; + napi_value discover_klass_str_value; + napi_value function_table_object; + bool result = false; + napi_handle_scope handle_scope; + klass c = NULL; + constructor ctor = NULL; + int ret = 1; + + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); + + node_loader_impl_exception(env, status); + + /* Get function table object from reference */ + status = napi_get_reference_value(env, discover_klass_safe->node_impl->function_table_object_ref, &function_table_object); + + node_loader_impl_exception(env, status); + + /* Create function string */ + status = napi_create_string_utf8(env, discover_klass_str, sizeof(discover_klass_str) - 1, &discover_klass_str_value); + + node_loader_impl_exception(env, status); + + /* Check if exists in the table */ + status = napi_has_own_property(env, function_table_object, discover_klass_str_value, &result); + + node_loader_impl_exception(env, status); + + if (result == true) + { + napi_value klass_trampoline_discover; + napi_valuetype valuetype; + napi_value argv[1]; + + status = napi_get_named_property(env, function_table_object, discover_klass_str, &klass_trampoline_discover); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, klass_trampoline_discover, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function discover in function table object"); + } + + /* Define parameters */ + argv[0] = discover_klass_safe->klass; + + /* Call to load from file function */ + napi_value global, klass_descriptor; + + status = napi_get_reference_value(env, discover_klass_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, klass_trampoline_discover, 1, argv, &klass_descriptor); + + node_loader_impl_exception(env, status); + + /* Convert return value (discover object) to context */ + napi_value klass_name; + char *klass_name_str = NULL; + bool has_name = false; + + status = napi_has_named_property(env, klass_descriptor, "name", &has_name); + + node_loader_impl_exception(env, status); + + /* Retrieve the class name if any */ + if (has_name == true) + { + size_t klass_name_length = 0; + + status = napi_get_named_property(env, klass_descriptor, "name", &klass_name); + + node_loader_impl_exception(env, status); + + status = napi_get_value_string_utf8(env, klass_name, NULL, 0, &klass_name_length); + + node_loader_impl_exception(env, status); + + if (klass_name_length > 0) + { + klass_name_str = static_cast(malloc(sizeof(char) * (klass_name_length + 1))); + } + + /* Get class name */ + status = napi_get_value_string_utf8(env, klass_name, klass_name_str, klass_name_length + 1, &klass_name_length); + + node_loader_impl_exception(env, status); + } + + loader_impl_node_class node_klass = new loader_impl_node_class_type(); + + node_klass->node_impl = discover_klass_safe->node_impl; + + status = napi_create_reference(env, discover_klass_safe->klass, 1, &node_klass->klass_ref); + + c = class_create(klass_name_str, ACCESSOR_TYPE_DYNAMIC, node_klass, &node_class_interface_singleton); + + napi_value methods; + bool has_methods = false; + + status = napi_has_named_property(env, klass_descriptor, "methods", &has_methods); + + node_loader_impl_exception(env, status); + + if (has_methods == true) + { + status = napi_get_named_property(env, klass_descriptor, "methods", &methods); + + node_loader_impl_exception(env, status); + + /* Discover constructor */ + napi_value klass_constructor; + bool has_constructor = false; + + status = napi_has_named_property(env, methods, "klass_constructor", &has_constructor); + + node_loader_impl_exception(env, status); + + if (has_constructor == true) + { + status = napi_get_named_property(env, methods, "klass_constructor", &klass_constructor); + + node_loader_impl_exception(env, status); + + napi_value constructor_sig; + bool has_sig = false; + + status = napi_has_named_property(env, klass_constructor, "signature", &has_sig); + + node_loader_impl_exception(env, status); + + if (has_sig == true) + { + status = napi_get_named_property(env, klass_constructor, "signature", &constructor_sig); + + node_loader_impl_exception(env, status); + + uint32_t constructor_sig_length = 0; + napi_valuetype valuetype; + + /* Check signature type */ + status = napi_typeof(env, constructor_sig, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } + + /* Get signature length */ + status = napi_get_array_length(env, constructor_sig, &constructor_sig_length); + + node_loader_impl_exception(env, status); + + ctor = constructor_create(constructor_sig_length, VISIBILITY_PUBLIC); + + /* Set signature */ + for (uint32_t arg_index = 0; arg_index < constructor_sig_length; ++arg_index) + { + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; + + /* Get signature parameter name */ + status = napi_get_element(env, constructor_sig, arg_index, ¶meter_name); + + node_loader_impl_exception(env, status); + + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); + + node_loader_impl_exception(env, status); + + if (parameter_name_length > 0) + { + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + } + + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - status = napi_get_value_string_utf8(env, func_name, NULL, 0, &func_name_length); + /* Todo: Discover type info */ - node_loader_impl_exception(env, status); + constructor_set(ctor, (size_t)arg_index, parameter_name_str, NULL); - if (func_name_length > 0) - { - func_name_str = static_cast(malloc(sizeof(char) * (func_name_length + 1))); + if (parameter_name_str != NULL) + { + free(parameter_name_str); + } + } + } + ret = class_register_constructor(c, ctor); + + if (ret != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Failed to register constructor in class %s", class_name(c)); + } } + else + { + ctor = constructor_create(0, VISIBILITY_PUBLIC); + ret = class_register_constructor(c, ctor); - /* Get function name */ - status = napi_get_value_string_utf8(env, func_name, func_name_str, func_name_length + 1, &func_name_length); + if (ret != 0) + { + log_write("metacall", LOG_LEVEL_ERROR, "Failed to register constructor in class %s", class_name(c)); + } + } - node_loader_impl_exception(env, status); - } + /* Discover methods */ + napi_value method_names; + uint32_t method_names_length; - /* Retrieve the function properties */ - napi_value function_sig; - napi_value function_types = nullptr; - napi_value function_ret = nullptr; - napi_value function_is_async; - uint32_t function_sig_length; + status = napi_get_property_names(env, methods, &method_names); - /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + status = napi_get_array_length(env, method_names, &method_names_length); - /* Check function pointer type */ - status = napi_typeof(env, function_sig, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + for (uint32_t index = 0; index < method_names_length; ++index) + { + napi_value method_name; + napi_value method_descriptor; + size_t method_name_length; + char *method_name_str = NULL; - if (valuetype != napi_object) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); - } + status = napi_get_element(env, method_names, index, &method_name); - /* Get signature length */ - status = napi_get_array_length(env, function_sig, &function_sig_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + status = napi_get_value_string_utf8(env, method_name, NULL, 0, &method_name_length); - /* Get function async */ - status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (method_name_length > 0) + { + method_name_str = static_cast(malloc(sizeof(char) * (method_name_length + 1))); + } - /* Check function async type */ - status = napi_typeof(env, function_is_async, &valuetype); + if (method_name_str == NULL) + { + return method_name_str; + } - node_loader_impl_exception(env, status); + /* Get function name */ + status = napi_get_value_string_utf8(env, method_name, method_name_str, method_name_length + 1, &method_name_length); - if (valuetype != napi_boolean) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS async flag"); - } + if (strcmp(method_name_str, "klass_constructor") == 0) + { + continue; + } - /* Optionally retrieve types if any in order to support typed supersets of JavaScript like TypeScript */ - static const char types_str[] = "types"; - bool has_types = false; + node_loader_impl_exception(env, status); - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + /* Get method descriptor */ + status = napi_get_named_property(env, methods, method_name_str, &method_descriptor); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_types == true) - { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + bool has_static = false; + napi_value static_value; + bool is_static_method = false; + napi_value method_sig; + bool has_sig = false; - node_loader_impl_exception(env, status); + status = napi_has_named_property(env, method_descriptor, "signature", &has_sig); - /* Check types array type */ - status = napi_typeof(env, function_types, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (has_sig == true) + { + status = napi_get_named_property(env, method_descriptor, "signature", &method_sig); - if (valuetype != napi_object) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS function types"); - } - } + node_loader_impl_exception(env, status); - /* Optionally retrieve return value type if any in order to support typed supersets of JavaScript like TypeScript */ - static const char ret_str[] = "ret"; - bool has_ret = false; + uint32_t method_sig_length = 0; + napi_valuetype valuetype; - status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + /* Check signature type */ + status = napi_typeof(env, method_sig, &valuetype); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - if (has_ret == true) - { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + if (valuetype != napi_object) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS signature"); + } - node_loader_impl_exception(env, status); + /* Get signature length */ + status = napi_get_array_length(env, method_sig, &method_sig_length); - /* Check return value type */ - status = napi_typeof(env, function_ret, &valuetype); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + method m = method_create(c, method_name_str, method_sig_length, NULL, VISIBILITY_PUBLIC, SYNCHRONOUS, NULL); - if (valuetype != napi_string) - { - napi_throw_type_error(env, nullptr, "Invalid NodeJS return type"); - } - } + status = napi_has_named_property(env, method_descriptor, "static", &has_static); - /* Create node function */ - loader_impl_node_function node_func = static_cast(malloc(sizeof(struct loader_impl_node_function_type))); + node_loader_impl_exception(env, status); - /* Create reference to function pointer */ - status = napi_create_reference(env, discover_function_safe->func, 1, &node_func->func_ref); + if (has_static != false) + { + status = napi_get_named_property(env, method_descriptor, "static", &static_value); - node_loader_impl_exception(env, status); + node_loader_impl_exception(env, status); - node_func->node_impl = discover_function_safe->node_impl; - node_func->impl = discover_function_safe->node_impl->impl; + status = napi_get_value_bool(env, static_value, &is_static_method); - /* Create function */ - function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + node_loader_impl_exception(env, status); + } - if (f != NULL) - { - signature s = function_signature(f); - bool is_async = false; + if (is_static_method) + { + class_register_static_method(c, m); + } + else + { + class_register_method(c, m); + } - /* Set function async */ - status = napi_get_value_bool(env, function_is_async, &is_async); + signature s = method_signature(m); + /* Set signature */ + for (uint32_t arg_index = 0; arg_index < method_sig_length; ++arg_index) + { + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; - node_loader_impl_exception(env, status); + /* Get signature parameter name */ + status = napi_get_element(env, method_sig, arg_index, ¶meter_name); - function_async(f, is_async == true ? ASYNCHRONOUS : SYNCHRONOUS); + node_loader_impl_exception(env, status); - /* Set return value if any */ - if (has_ret) - { - size_t return_type_length; - char *return_type_str = NULL; + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); - /* Get return value string length */ - status = napi_get_value_string_utf8(env, function_ret, NULL, 0, &return_type_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + if (parameter_name_length > 0) + { + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + } - if (return_type_length > 0) - { - return_type_str = static_cast(malloc(sizeof(char) * (return_type_length + 1))); - } + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); - if (return_type_str != NULL) - { - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, function_ret, return_type_str, return_type_length + 1, &return_type_length); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + /* Todo: Discover type info? */ + signature_set(s, (size_t)arg_index, parameter_name_str, NULL); - /* TODO: Implement return type with impl (may need an important refactor) */ - signature_set_return(s, /*loader_impl_type(discover_function_safe->impl, return_type_str)*/ NULL); + if (parameter_name_str != NULL) + { + free(parameter_name_str); + } + } - free(return_type_str); + /* Set return signature */ + signature_set_return(s, NULL); } } - /* Set signature */ - for (uint32_t arg_index = 0; arg_index < function_sig_length; ++arg_index) - { - napi_value parameter_name; - size_t parameter_name_length; - char *parameter_name_str = NULL; + /* Discover attributes*/ + bool has_attr = false; + napi_value attr_field; - /* Get signature parameter name */ - status = napi_get_element(env, function_sig, arg_index, ¶meter_name); + status = napi_has_named_property(env, klass_descriptor, "attributes", &has_attr); + + node_loader_impl_exception(env, status); + + if (has_attr == true) + { + status = napi_get_named_property(env, klass_descriptor, "attributes", &attr_field); node_loader_impl_exception(env, status); - /* Get parameter name string length */ - status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); + uint32_t attr_field_length = 0; + napi_valuetype valuetype; + + /* Check signature type */ + status = napi_typeof(env, attr_field, &valuetype); node_loader_impl_exception(env, status); - if (parameter_name_length > 0) + if (valuetype != napi_object) { - parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); + napi_throw_type_error(env, nullptr, "Invalid attribute format: Expected an Array"); } - /* Get parameter name string */ - status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); + /* Get signature length */ + status = napi_get_array_length(env, attr_field, &attr_field_length); node_loader_impl_exception(env, status); - /* Check if type info is available */ - if (has_types) + //attribute attr = attribute_create(c, name, t, NULL, VISIBILITY_PUBLIC, NULL); + + for (uint32_t arg_index = 0; arg_index < attr_field_length; ++arg_index) { - napi_value parameter_type; - size_t parameter_type_length; - char *parameter_type_str = NULL; + napi_value parameter_name; + size_t parameter_name_length; + char *parameter_name_str = NULL; - /* Get signature parameter type */ - status = napi_get_element(env, function_types, arg_index, ¶meter_type); + /* Get signature parameter name */ + status = napi_get_element(env, attr_field, arg_index, ¶meter_name); node_loader_impl_exception(env, status); - /* Get parameter type string length */ - status = napi_get_value_string_utf8(env, parameter_type, NULL, 0, ¶meter_type_length); + /* Get parameter name string length */ + status = napi_get_value_string_utf8(env, parameter_name, NULL, 0, ¶meter_name_length); node_loader_impl_exception(env, status); - if (parameter_type_length > 0) + if (parameter_name_length > 0) { - parameter_type_str = static_cast(malloc(sizeof(char) * (parameter_type_length + 1))); + parameter_name_str = static_cast(malloc(sizeof(char) * (parameter_name_length + 1))); } - /* Get parameter type string */ - status = napi_get_value_string_utf8(env, parameter_type, parameter_type_str, parameter_type_length + 1, ¶meter_type_length); + /* Get parameter name string */ + status = napi_get_value_string_utf8(env, parameter_name, parameter_name_str, parameter_name_length + 1, ¶meter_name_length); node_loader_impl_exception(env, status); - /* TODO: Implement parameter type with impl (may need an important refactor) */ - signature_set(s, (size_t)arg_index, parameter_name_str, /*loader_impl_type(discover_function_safe->impl, parameter_type_str)*/ NULL); + attribute attr = attribute_create(c, parameter_name_str, NULL, NULL, VISIBILITY_PUBLIC, NULL); + class_register_attribute(c, attr); - if (parameter_type_str != NULL) + if (parameter_name_str != NULL) { - free(parameter_type_str); + free(parameter_name_str); } } - else - { - signature_set(s, (size_t)arg_index, parameter_name_str, NULL); - } - - if (parameter_name_str != NULL) - { - free(parameter_name_str); - } } - - /* Create value with the function */ - function_value = value_create_function(f); - } - else - { - free(node_func); - } - - if (func_name_str != NULL) - { - free(func_name_str); } + return value_create_class(c); } - /* Close scope */ - status = napi_close_handle_scope(env, handle_scope); - - node_loader_impl_exception(env, status); + return NULL; +} - return function_value; +/* +static value node_loader_impl_discover_object_safe(napi_env env, loader_impl_discover_object_safe discover_object_safe) +{ } +*/ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_safe discover_safe) { @@ -3736,51 +4913,6 @@ void node_loader_impl_thread_safe_function_initialize(napi_env env, node_loader_impl_exception(env, status); } -#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) -/* TODO: _Ret_maybenull_ HMODULE WINAPI GetModuleHandleW(_In_opt_ LPCWSTR lpModuleName); */ -_Ret_maybenull_ HMODULE WINAPI get_module_handle_a_hook(_In_opt_ LPCSTR lpModuleName) -{ - /* This hooks GetModuleHandle, which is called as DelayLoad hook inside NodeJS - * extensions in order to retrieve the executable handle, which is supposed - * to have all N-API symbols. This trick is used because the design of NodeJS forces - * to compile statically node.dll into the executable, but this does not happen on - * MetaCall as it is embedded. We cannot change this behavior because it depends on - * NodeJS extension build system, which relies on DelayLoad mechanism. So what we are - * doing here is intercepting the GetModuleHandle call inside the DelayLoad hook, then - * getting the address from where this Win32 API was called, and if it commes from a - * NodeJS extension, then we return the node.dll module, otherwise we call to the original - * GetModuleHandle funciton. This method successfully hooks into the NodeJS mechanism and - * redirects properly the linker resolver system to the node.dll where symbols are located. - */ - if (lpModuleName == NULL) - { - HMODULE mod = NULL; - - if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, /* Behave like GetModuleHandle */ - (LPCTSTR)_ReturnAddress(), &mod) == TRUE) - { - static const char node_ext[] = ".node"; - char mod_name[MAX_PATH]; - size_t length = GetModuleFileName(mod, mod_name, MAX_PATH); - - /* It must contain a letter a part from the .node extension */ - if (length > sizeof(node_ext)) - { - char *ext = &mod_name[length - sizeof(node_ext) + 1]; - - if (strncmp(ext, node_ext, sizeof(node_ext)) == 0) - { - return node_loader_node_dll_handle; - } - } - } - } - - return get_module_handle_a_ptr(lpModuleName); -} -#endif - void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *function_table_object_ptr) { loader_impl_node node_impl = static_cast(node_impl_ptr); @@ -4019,17 +5151,11 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi #endif /* Store the amount of async handles that we have for the node loader, - * so we can count the user defined async handles */ + * so we can count the user defined async handles */ node_impl->base_active_handles = node_loader_impl_async_handles_count(node_impl); node_impl->extra_active_handles.store(0); node_impl->event_loop_empty.store(false); - /* On Windows, hook node extension loading mechanism in order to patch extensions linked to node.exe */ -#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) - node_loader_node_dll_handle = GetModuleHandle(NODEJS_LIBRARY_NAME); - get_module_handle_a_ptr = (HMODULE(*)(_In_opt_ LPCSTR))node_loader_hook_import_address_table("kernel32.dll", "GetModuleHandleA", &get_module_handle_a_hook); -#endif - /* Signal start condition */ uv_cond_signal(&node_impl->cond); @@ -5266,10 +6392,10 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env } /* NodeJS Loader needs to register that it is destroyed, because after this step - * some destructors can be still triggered, before the node_loader->destroy() has - * finished, so this destructors will try to execute the NodeJS unrefs while having - * the runtime (at least the NodeJS Loader related part) destroyed. - */ + * some destructors can be still triggered, before the node_loader->destroy() has + * finished, so this destructors will try to execute the NodeJS unrefs while having + * the runtime (at least the NodeJS Loader related part) destroyed. + */ loader_set_destroyed(node_impl->impl); } @@ -5374,7 +6500,7 @@ int node_loader_impl_destroy(loader_impl impl) #endif /* Print NodeJS execution result */ - log_write("metacall", LOG_LEVEL_DEBUG, "NodeJS execution return status %d", node_impl->result); + log_write("metacall", LOG_LEVEL_INFO, "NodeJS execution return status %d", node_impl->result); /* Restore stdin, stdout, stderr */ dup2(node_impl->stdin_copy, STDIN_FILENO); diff --git a/source/reflect/include/reflect/reflect_class.h b/source/reflect/include/reflect/reflect_class.h index f30a518a6..ad9967f6c 100644 --- a/source/reflect/include/reflect/reflect_class.h +++ b/source/reflect/include/reflect/reflect_class.h @@ -99,10 +99,18 @@ REFLECT_API method class_static_method(klass cls, const char *key, type_id ret, REFLECT_API method class_method(klass cls, const char *key, type_id ret, type_id args[], size_t size); +REFLECT_API vector class_method_names(klass cls); + +REFLECT_API vector class_static_method_names(klass cls); + REFLECT_API attribute class_static_attribute(klass cls, const char *key); REFLECT_API attribute class_attribute(klass cls, const char *key); +REFLECT_API vector class_attribute_names(klass cls); + +REFLECT_API vector class_static_attribute_names(klass cls); + REFLECT_API int class_register_constructor(klass cls, constructor ctor); REFLECT_API int class_register_static_method(klass cls, method m); diff --git a/source/reflect/include/reflect/reflect_object.h b/source/reflect/include/reflect/reflect_object.h index 648bcea54..a2f68e162 100644 --- a/source/reflect/include/reflect/reflect_object.h +++ b/source/reflect/include/reflect/reflect_object.h @@ -95,6 +95,8 @@ REFLECT_API method object_method(object obj, const char *key, type_id ret, type_ REFLECT_API const char *object_name(object obj); +REFLECT_API klass object_get_class(object obj); + REFLECT_API value object_metadata(object obj); REFLECT_API int object_delete(object obj); diff --git a/source/reflect/source/reflect_class.c b/source/reflect/source/reflect_class.c index a4bfb6545..5242e27fe 100644 --- a/source/reflect/source/reflect_class.c +++ b/source/reflect/source/reflect_class.c @@ -680,6 +680,30 @@ method class_method(klass cls, const char *key, type_id ret, type_id args[], siz return class_get_method_type_safe(class_methods(cls, key), ret, args, size); } +vector class_method_names(klass cls) +{ + vector v = vector_create_type(char *); + map_iterator it = map_iterator_begin(cls->methods); + while (map_iterator_end(&it) != 0) + { + char *key = (char *)map_iterator_get_key(it); + vector_push_back(v, (void *)key); + } + return v; +} + +vector class_static_method_names(klass cls) +{ + vector v = vector_create_type(char *); + map_iterator it = map_iterator_begin(cls->static_methods); + while (map_iterator_end(&it) != 0) + { + char *key = (char *)map_iterator_get_key(it); + vector_push_back(v, (void *)key); + } + return v; +} + attribute class_static_attribute(klass cls, const char *key) { if (cls == NULL || key == NULL) @@ -700,6 +724,30 @@ attribute class_attribute(klass cls, const char *key) return set_get(cls->attributes, (set_key)key); } +vector class_attribute_names(klass cls) +{ + vector v = vector_create_type(char *); + set_iterator it = set_iterator_begin(cls->attributes); + while (set_iterator_end(&it) != 0) + { + char *key = (char *)set_iterator_get_key(it); + vector_push_back(v, (void *)key); + } + return v; +} + +vector class_static_attribute_names(klass cls) +{ + vector v = vector_create_type(char *); + set_iterator it = set_iterator_begin(cls->static_attributes); + while (set_iterator_end(&it) != 0) + { + char *key = (char *)set_iterator_get_key(it); + vector_push_back(v, (void *)key); + } + return v; +} + int class_register_constructor(klass cls, constructor ctor) { if (cls == NULL || ctor == NULL) diff --git a/source/reflect/source/reflect_object.c b/source/reflect/source/reflect_object.c index 6e0aa35b6..39ea1f4bf 100644 --- a/source/reflect/source/reflect_object.c +++ b/source/reflect/source/reflect_object.c @@ -174,6 +174,16 @@ const char *object_name(object obj) return NULL; } +klass object_get_class(object obj) +{ + if (obj != NULL) + { + return obj->cls; + } + + return NULL; +} + value object_metadata_name(object obj) { static const char object_str[] = "name"; From 145e5c18c680e0da0e3240edbb83f77e2c001193 Mon Sep 17 00:00:00 2001 From: rxbryan Date: Wed, 15 Feb 2023 20:32:39 +0100 Subject: [PATCH 03/12] add test for node_loader class --- .../node_loader/source/node_loader_impl.cpp | 146 ++++++++----- source/scripts/node/test/CMakeLists.txt | 5 + source/scripts/node/test/source/test.js | 67 ++++++ source/tests/CMakeLists.txt | 1 + .../metacall-node-class-test/CMakeLists.txt | 154 ++++++++++++++ .../metacall-node-class-test/source/main.cpp | 28 +++ .../source/metacall_node_class_test.cpp | 201 ++++++++++++++++++ 7 files changed, 551 insertions(+), 51 deletions(-) create mode 100644 source/scripts/node/test/CMakeLists.txt create mode 100644 source/scripts/node/test/source/test.js create mode 100644 source/tests/metacall-node-class-test/CMakeLists.txt create mode 100644 source/tests/metacall-node-class-test/source/main.cpp create mode 100644 source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index 1b8029fd5..f796358f0 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -1054,16 +1054,16 @@ static napi_value node_loader_impl_class_constructor_callback(napi_env env, napi loader_impl_async_safe_cast closure_cast = { NULL }; napi_get_cb_info(env, info, NULL, NULL, NULL, &closure_cast.ptr); + /* Set environment */ closure_cast.safe->node_impl->env = env; - constructor ctor = class_default_constructor(closure_cast.safe->cls); char *klass_name_str = const_cast(class_name(closure_cast.safe->cls)); - std::string error_msg = "NodeJS Loader No default constructor in class: "; - error_msg += klass_name_str; + + constructor ctor = class_default_constructor(closure_cast.safe->cls); if (ctor == NULL) { - napi_throw_error(env, NULL, error_msg.c_str()); + log_write("metacall", LOG_LEVEL_INFO, "NodeJS Loader No default constructor in class: ", klass_name_str); } size_t argc = constructor_count(ctor); @@ -1083,13 +1083,13 @@ static napi_value node_loader_impl_class_constructor_callback(napi_env env, napi ctor = class_constructor(closure_cast.safe->cls, ids, argc); //convert class_name to lower case - while (*klass_name_str != '\0') + std::string obj_name(klass_name_str); + for (size_t i = 0; i < obj_name.size(); i++) { - *klass_name_str = std::tolower(*klass_name_str); - klass_name_str++; + obj_name[i] = std::tolower(obj_name[i]); } - object o = class_new(closure_cast.safe->cls, klass_name_str, ctor, args, argc); + object o = class_new(closure_cast.safe->cls, obj_name.c_str(), ctor, args, argc); if (ids != NULL) { @@ -1106,7 +1106,7 @@ static napi_value node_loader_impl_class_constructor_callback(napi_env env, napi if (v == NULL) { object_destroy(o); - error_msg = "NodeJS Loader Failed to create object for class: "; + std::string error_msg = "NodeJS Loader Failed to create object for class: "; error_msg += klass_name_str; napi_throw_error(env, NULL, error_msg.c_str()); } @@ -4474,7 +4474,7 @@ static value node_loader_impl_discover_object_safe(napi_env env, loader_impl_dis void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_safe discover_safe) { static const char discover_str[] = "discover"; - napi_value function_table_object; + napi_value discover_table_object; napi_value discover_str_value; bool result = false; napi_handle_scope handle_scope; @@ -4485,7 +4485,7 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); /* Get function table object from reference */ - status = napi_get_reference_value(env, discover_safe->node_impl->function_table_object_ref, &function_table_object); + status = napi_get_reference_value(env, discover_safe->node_impl->function_table_object_ref, &discover_table_object); node_loader_impl_exception(env, status); @@ -4495,21 +4495,21 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, discover_str_value, &result); + status = napi_has_own_property(env, discover_table_object, discover_str_value, &result); node_loader_impl_exception(env, status); if (result == true) { - napi_value function_trampoline_discover; + napi_value trampoline_discover; napi_valuetype valuetype; napi_value argv[1]; - status = napi_get_named_property(env, function_table_object, discover_str, &function_trampoline_discover); + status = napi_get_named_property(env, discover_table_object, discover_str, &trampoline_discover); node_loader_impl_exception(env, status); - status = napi_typeof(env, function_trampoline_discover, &valuetype); + status = napi_typeof(env, trampoline_discover, &valuetype); node_loader_impl_exception(env, status); @@ -4530,72 +4530,79 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); - status = napi_call_function(env, global, function_trampoline_discover, 1, argv, &discover_map); + status = napi_call_function(env, global, trampoline_discover, 1, argv, &discover_map); node_loader_impl_exception(env, status); /* Convert return value (discover object) to context */ - napi_value func_names; - uint32_t func_names_length; + napi_value prop_names; + uint32_t prop_names_length; - status = napi_get_property_names(env, discover_map, &func_names); + status = napi_get_property_names(env, discover_map, &prop_names); node_loader_impl_exception(env, status); - status = napi_get_array_length(env, func_names, &func_names_length); + status = napi_get_array_length(env, prop_names, &prop_names_length); node_loader_impl_exception(env, status); - for (uint32_t index = 0; index < func_names_length; ++index) + for (uint32_t index = 0; index < prop_names_length; ++index) { - napi_value func_name; - size_t func_name_length; - char *func_name_str = NULL; + napi_value prop_name; + size_t prop_name_length; + char *prop_name_str = NULL; - status = napi_get_element(env, func_names, index, &func_name); + status = napi_get_element(env, prop_names, index, &prop_name); node_loader_impl_exception(env, status); - status = napi_get_value_string_utf8(env, func_name, NULL, 0, &func_name_length); + status = napi_get_value_string_utf8(env, prop_name, NULL, 0, &prop_name_length); node_loader_impl_exception(env, status); - if (func_name_length > 0) + if (prop_name_length > 0) { - func_name_str = static_cast(malloc(sizeof(char) * (func_name_length + 1))); + prop_name_str = static_cast(malloc(sizeof(char) * (prop_name_length + 1))); } - if (func_name_str != NULL) + if (prop_name_str != NULL) { - napi_value function_descriptor; - napi_value function_ptr; - napi_value function_sig; - napi_value function_types = nullptr; - napi_value function_ret = nullptr; - napi_value function_is_async; - uint32_t function_sig_length; + napi_value property_descriptor; - /* Get function name */ - status = napi_get_value_string_utf8(env, func_name, func_name_str, func_name_length + 1, &func_name_length); + /* Get property name */ + status = napi_get_value_string_utf8(env, prop_name, prop_name_str, prop_name_length + 1, &prop_name_length); node_loader_impl_exception(env, status); - /* Get function descriptor */ - status = napi_get_named_property(env, discover_map, func_name_str, &function_descriptor); + /* Get property descriptor */ + status = napi_get_named_property(env, discover_map, prop_name_str, &property_descriptor); node_loader_impl_exception(env, status); - /* Check if function pointer exists */ bool is_func = false; - status = napi_has_named_property(env, function_descriptor, "func", &is_func); + status = napi_has_named_property(env, property_descriptor, "func", &is_func); + + node_loader_impl_exception(env, status); + + bool is_klass = false; + + status = napi_has_named_property(env, property_descriptor, "klass", &is_klass); node_loader_impl_exception(env, status); + /* Check if a function pointer exists */ if (is_func == true) { + napi_value function_ptr; + napi_value function_sig; + napi_value function_types = nullptr; + napi_value function_ret = nullptr; + napi_value function_is_async; + uint32_t function_sig_length; + /* Get function pointer */ - status = napi_get_named_property(env, function_descriptor, "func", &function_ptr); + status = napi_get_named_property(env, property_descriptor, "func", &function_ptr); node_loader_impl_exception(env, status); @@ -4610,7 +4617,7 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf } /* Get function signature */ - status = napi_get_named_property(env, function_descriptor, "signature", &function_sig); + status = napi_get_named_property(env, property_descriptor, "signature", &function_sig); node_loader_impl_exception(env, status); @@ -4630,7 +4637,7 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_loader_impl_exception(env, status); /* Get function async */ - status = napi_get_named_property(env, function_descriptor, "async", &function_is_async); + status = napi_get_named_property(env, property_descriptor, "async", &function_is_async); node_loader_impl_exception(env, status); @@ -4648,13 +4655,13 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf static const char types_str[] = "types"; bool has_types = false; - status = napi_has_named_property(env, function_descriptor, types_str, &has_types); + status = napi_has_named_property(env, property_descriptor, types_str, &has_types); node_loader_impl_exception(env, status); if (has_types == true) { - status = napi_get_named_property(env, function_descriptor, types_str, &function_types); + status = napi_get_named_property(env, property_descriptor, types_str, &function_types); node_loader_impl_exception(env, status); @@ -4673,13 +4680,13 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf static const char ret_str[] = "ret"; bool has_ret = false; - status = napi_has_named_property(env, function_descriptor, ret_str, &has_ret); + status = napi_has_named_property(env, property_descriptor, ret_str, &has_ret); node_loader_impl_exception(env, status); if (has_ret == true) { - status = napi_get_named_property(env, function_descriptor, ret_str, &function_ret); + status = napi_get_named_property(env, property_descriptor, ret_str, &function_ret); node_loader_impl_exception(env, status); @@ -4706,7 +4713,7 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf node_func->impl = discover_safe->node_impl->impl; /* Create function */ - function f = function_create(func_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); + function f = function_create(prop_name_str, (size_t)function_sig_length, node_func, &function_node_singleton); if (f != NULL) { @@ -4838,8 +4845,45 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf break; } } + else if (is_klass == true) + { + napi_value klass_ptr; + + /* Get klass pointer */ + status = napi_get_named_property(env, property_descriptor, "klass", &klass_ptr); + + node_loader_impl_exception(env, status); + + /* Check klass pointer type */ + status = napi_typeof(env, klass_ptr, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid NodeJS class"); + } + + struct loader_impl_discover_klass_safe_type discover_klass_safe = { + discover_safe->node_impl, + klass_ptr + }; + + value v = node_loader_impl_discover_klass_safe(env, &discover_klass_safe); + + if (v != NULL) + { + scope sp = context_scope(discover_safe->ctx); + if (scope_define(sp, class_name((klass)metacall_value_to_class(v)), v) != 0) + { + value_type_destroy(v); + discover_safe->result = 1; + break; + } + } + } - free(func_name_str); + free(prop_name_str); } } } diff --git a/source/scripts/node/test/CMakeLists.txt b/source/scripts/node/test/CMakeLists.txt new file mode 100644 index 000000000..738a820a1 --- /dev/null +++ b/source/scripts/node/test/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Configure nodejs project +# + +nodejs_project(test 0.1.0) diff --git a/source/scripts/node/test/source/test.js b/source/scripts/node/test/source/test.js new file mode 100644 index 000000000..be90b4c65 --- /dev/null +++ b/source/scripts/node/test/source/test.js @@ -0,0 +1,67 @@ +class Fibonacci { + fib_impl(n) { + if (n <= 2) { + return 1; + } else { + return fib_impl(n - 2) + fib_impl(n - 1); + } + } + + static fib(n) { + console.log("Fibbonaci of " + n + " = " + fib_impl(n)); + } +} + +class Test { + + constructor (num, str, cls, cls_args) { + this.int = 231; + this.b = 100; + this.hello_str = "hello!"; + + this.arr = [ 'K', 'G', 50, 100, "Hello", "world"]; + this.num = num; + this.str = str; + this.cls = Fibonacci; + + // TODO: Add test for this + this.obj = new cls(...cls_args); + // END-TODO + + console.log("NodeJs Loader: Test constructor Called!"); + } + + newFibonacci() { + return new Fibonacci; + } + + return_class() { + return Fibonacci; + } + + return_new_obj() { + return this.obj.return_bye('LBryan'); + } + + print() { + console.log(this.str); + } + + // Static method with no parameter and void return type + static hello(s) { + console.log("hello, world! running on " + s); + return 10; + } + + static sumArray(i) { + sum = 0; + + for (let c of i) + sum += c; + + return sum; + } + +} + +module.exports = Test \ No newline at end of file diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index cc9002ef7..e35c1bf8b 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -239,3 +239,4 @@ add_subdirectory(metacall_plugin_extension_destroy_order_test) add_subdirectory(metacall_cli_core_plugin_test) add_subdirectory(metacall_cli_core_plugin_await_test) add_subdirectory(metacall_backtrace_plugin_test) +add_subdirectory(metacall-node-class-test) diff --git a/source/tests/metacall-node-class-test/CMakeLists.txt b/source/tests/metacall-node-class-test/CMakeLists.txt new file mode 100644 index 000000000..b4057c196 --- /dev/null +++ b/source/tests/metacall-node-class-test/CMakeLists.txt @@ -0,0 +1,154 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_NODE) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-node-class-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_node_class_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + node_loader + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) + +if(OPTION_BUILD_SANITIZER) + set_tests_properties(${target} PROPERTIES + PASS_REGULAR_EXPRESSION "[ PASSED ]" + ) +endif() + +include(TestEnvironmentVariables) + +test_environment_variables(${target} + "" + ${TESTS_ENVIRONMENT_VARIABLES} +) diff --git a/source/tests/metacall-node-class-test/source/main.cpp b/source/tests/metacall-node-class-test/source/main.cpp new file mode 100644 index 000000000..4676e4763 --- /dev/null +++ b/source/tests/metacall-node-class-test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading ruby code at run-time into a process. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp b/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp new file mode 100644 index 000000000..2dc81cc7a --- /dev/null +++ b/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp @@ -0,0 +1,201 @@ +/* + * MetaCall Library by Parra Studios + * A library for providing a foreign function interface calls. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +class metacall_node_class_test : public testing::Test +{ +public: +}; + +TEST_F(metacall_node_class_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts[] = { + "scripts/classname.py" + }; + + EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + +/* Node */ +#if defined(OPTION_BUILD_LOADERS_NODE) + { + void *new_object_v = NULL; + { /* TEST LOAD FROM FILE */ + const char *node_scripts[] = { + "scripts/test.js" + }; + + static const char tag[] = "node"; + + ASSERT_EQ((int)0, (int)metacall_load_from_file(tag, node_scripts, sizeof(node_scripts) / sizeof(node_scripts[0]), NULL)); + } + + { /* TEST STATIC GET, SET AND INVOKE */ + void *myclass = metacall_class("Test"); + + void *pyclass_value = metacall("return_class_function"); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(pyclass_value)); + //void *pyclass = metacall_value_to_class(pyclass_value); + + static const char john[] = "John Doe"; + + const void *pyconstructor_params[] = { + metacall_value_create_string(john, sizeof(john) - 1), // param1 + metacall_value_create_int(999999) // param2 + }; + + void *constructor_params[] = { + metacall_value_create_int(10), + metacall_value_create_string("Test String", 11), + pyclass_value, + metacall_value_create_array(pyconstructor_params, sizeof(pyconstructor_params) / sizeof(pyconstructor_params[0])) + }; + + new_object_v = metacall_class_new(myclass, "test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); + ASSERT_NE((void *)NULL, (void *)new_object_v); + + metacall_value_destroy(new_object_v); + + { //Invoke static method + + { + void *args[] = { + metacall_value_create_int(1), + metacall_value_create_int(2), + metacall_value_create_int(3), + metacall_value_create_int(4), + metacall_value_create_int(5), + metacall_value_create_int(6), + metacall_value_create_int(7), + metacall_value_create_int(8), + metacall_value_create_int(9), + metacall_value_create_int(10) + }; + + void *ret = metacallt_class(myclass, "hello", METACALL_INT, args, sizeof(args) / sizeof(args[0])); + ASSERT_EQ((long)55, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + + { + void *args[] = { + metacall_value_create_string("Metacall", 8) + }; + + void *ret = metacallt_class(myclass, "sumArray", METACALL_INT, args, 1); + ASSERT_EQ((long)10, (long)metacall_value_to_long(ret)); + + metacall_value_destroy(ret); + } + } + } + + { /* TEST NON STATIC GET, SET and INVOKE FROM CLASS OBJECT */ + + void *new_object = metacall_value_to_object(new_object_v); + + { + void *param1 = metacall_object_get(new_object, "num"); + ASSERT_EQ((long)10, (long)metacall_value_to_long(param1)); + metacall_value_destroy(param1); + } + + { + void *param1 = metacall_object_get(new_object, "hello_str"); + ASSERT_EQ((std::string) "hello!", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + { + void *long_value = metacall_value_create_long(124124L); + int retcode = metacall_object_set(new_object, "b", long_value); + metacall_value_destroy(long_value); + ASSERT_EQ((int)0, int(retcode)); + + void *param2 = metacall_object_get(new_object, "b"); + ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((long)124124L, (long)metacall_value_to_long(param2)); + + metacall_value_destroy(param2); + } + + //Non Static function Invoke + { + void *ret = metacallv_object(new_object, "print", metacall_null_args, 0); + EXPECT_NE((void *)NULL, (void *)ret); + + metacall_value_destroy(ret); + } + + { + void *Fibonacci = metacallv_object(new_object, "return_class", metacall_null_args, 0); + ASSERT_EQ((enum metacall_value_id)METACALL_CLASS, (enum metacall_value_id)metacall_value_id(Fibonacci)); + + metacall_value_destroy(Fibonacci); + } + + { + void *param1 = metacallv_object(new_object, "return_bye", metacall_null_args, 0); + ASSERT_EQ((std::string) "bye LBryan", (std::string)metacall_value_to_string(param1)); + metacall_value_destroy(param1); + } + + metacall_value_destroy(new_object_v); + } + } +#endif /* OPTION_BUILD_LOADERS_NODE */ + + /* Print inspect information */ + { + size_t size = 0; + + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + + char *inspect_str = metacall_inspect(&size, allocator); + + EXPECT_NE((char *)NULL, (char *)inspect_str); + + EXPECT_GT((size_t)size, (size_t)0); + + std::cout << inspect_str << std::endl; + + metacall_allocator_free(allocator, inspect_str); + + metacall_allocator_destroy(allocator); + } + + EXPECT_EQ((int)0, (int)metacall_destroy()); +} From 18e1f8b127437a485ef4d85c081993c44b3c2469 Mon Sep 17 00:00:00 2001 From: rxbryan Date: Wed, 15 Feb 2023 20:32:39 +0100 Subject: [PATCH 04/12] add test for node_loader class add api for getting all keys and values in adt_set and adt_map --- source/adt/include/adt/adt_map.h | 4 + source/adt/include/adt/adt_set.h | 5 + source/adt/source/adt_map.c | 42 + source/adt/source/adt_set.c | 42 + .../node_loader/bootstrap/lib/bootstrap.js | 4 + .../node_loader/source/node_loader_impl.cpp | 1692 +++++++++++++---- .../reflect/include/reflect/reflect_class.h | 8 +- source/reflect/source/reflect_class.c | 46 +- source/scripts/node/CMakeLists.txt | 1 + source/scripts/node/node_test/CMakeLists.txt | 5 + .../node/node_test/source/node_test.js | 77 + .../source/metacall_node_class_test.cpp | 66 +- 12 files changed, 1605 insertions(+), 387 deletions(-) create mode 100644 source/scripts/node/node_test/CMakeLists.txt create mode 100644 source/scripts/node/node_test/source/node_test.js diff --git a/source/adt/include/adt/adt_map.h b/source/adt/include/adt/adt_map.h index ef1b45ebc..ca51cd07b 100644 --- a/source/adt/include/adt/adt_map.h +++ b/source/adt/include/adt/adt_map.h @@ -63,6 +63,10 @@ ADT_API int map_insert_array(map m, map_key keys[], map_value values[], size_t s ADT_API vector map_get(map m, map_key key); +ADT_API vector map_get_keys(map m); + +ADT_API vector map_get_values(map m); + ADT_API int map_contains(map m, map_key key); ADT_API int map_contains_any(map dest, map src); diff --git a/source/adt/include/adt/adt_set.h b/source/adt/include/adt/adt_set.h index 81140723b..9f8baf45e 100644 --- a/source/adt/include/adt/adt_set.h +++ b/source/adt/include/adt/adt_set.h @@ -15,6 +15,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -62,6 +63,10 @@ ADT_API int set_insert_array(set s, set_key keys[], set_value values[], size_t s ADT_API set_value set_get(set s, set_key key); +ADT_API vector set_get_keys(set s); + +ADT_API vector set_get_values(set s); + ADT_API int set_contains(set s, set_key key); ADT_API int set_contains_any(set dest, set src); diff --git a/source/adt/source/adt_map.c b/source/adt/source/adt_map.c index 5e4fd572e..b1ff7147f 100644 --- a/source/adt/source/adt_map.c +++ b/source/adt/source/adt_map.c @@ -251,6 +251,48 @@ vector map_get(map m, map_key key) return NULL; } +vector map_get_keys(map m) +{ + vector v = vector_create(sizeof(void *)); + bucket b; + pair p; + for (size_t iterator = 0; iterator < m->capacity; iterator++) + { + b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (size_t index = 0; index < b->count; index++) + { + p = &b->pairs[index]; + vector_push_back(v, &p->key); + } + } + } + return v; +} + +vector map_get_values(map m) +{ + vector v = vector_create(sizeof(void *)); + bucket b; + pair p; + for (size_t iterator = 0; iterator < m->capacity; iterator++) + { + b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (size_t index = 0; index < b->count; index++) + { + p = &b->pairs[index]; + vector_push_back(v, &p->value); + } + } + } + return v; +} + int map_contains(map m, map_key key) { if (m != NULL && key != NULL) diff --git a/source/adt/source/adt_set.c b/source/adt/source/adt_set.c index 2014e56c0..5f448bb7d 100644 --- a/source/adt/source/adt_set.c +++ b/source/adt/source/adt_set.c @@ -273,6 +273,48 @@ set_value set_get(set s, set_key key) return NULL; } +vector set_get_keys(set s) +{ + vector v = vector_create(sizeof(void *)); + bucket b; + pair p; + for (size_t iterator = 0; iterator < s->capacity; iterator++) + { + b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (size_t index = 0; index < b->count; index++) + { + p = &b->pairs[index]; + vector_push_back(v, &p->key); + } + } + } + return v; +} + +vector set_get_values(set s) +{ + vector v = vector_create(sizeof(void *)); + bucket b; + pair p; + for (size_t iterator = 0; iterator < s->capacity; iterator++) + { + b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) + { + for (size_t index = 0; index < b->count; index++) + { + p = &b->pairs[index]; + vector_push_back(v, &p->value); + } + } + } + return v; +} + int set_contains(set s, set_key key) { if (s != NULL && key != NULL) diff --git a/source/loaders/node_loader/bootstrap/lib/bootstrap.js b/source/loaders/node_loader/bootstrap/lib/bootstrap.js index 5c19d3e16..0430f3080 100644 --- a/source/loaders/node_loader/bootstrap/lib/bootstrap.js +++ b/source/loaders/node_loader/bootstrap/lib/bootstrap.js @@ -288,6 +288,10 @@ function node_loader_trampoline_discover_function(func) { return discover; } + else + { + return node_loader_trampoline_discover_klass(func); + } } } catch (ex) { console.log(`Exception while parsing '${func}' in node_loader_trampoline_discover_function`, ex); diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index f796358f0..f65b3e565 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -196,6 +196,27 @@ typedef struct loader_impl_async_future_delete_safe_type *loader_impl_async_futu struct loader_impl_async_destroy_safe_type; typedef struct loader_impl_async_destroy_safe_type *loader_impl_async_destroy_safe; +struct loader_impl_async_object_get_safe_type; +typedef struct loader_impl_async_object_get_safe_type *loader_impl_async_object_get_safe; + +struct loader_impl_async_object_set_safe_type; +typedef struct loader_impl_async_object_set_safe_type *loader_impl_async_object_set_safe; + +struct loader_impl_async_object_method_invoke_safe_type; +typedef struct loader_impl_async_object_method_invoke_safe_type *loader_impl_async_object_method_invoke_safe; + +struct loader_impl_async_object_destroy_safe_type; +typedef struct loader_impl_async_object_destroy_safe_type *loader_impl_async_object_destroy_safe; + +struct loader_impl_async_class_ctor_safe_type; +typedef struct loader_impl_async_class_ctor_safe_type *loader_impl_async_class_ctor_safe; + +struct loader_impl_async_class_static_invoke_safe_type; +typedef struct loader_impl_async_class_static_invoke_safe_type *loader_impl_async_class_static_invoke_safe; + +struct loader_impl_async_class_destroy_safe_type; +typedef struct loader_impl_async_class_destroy_safe_type *loader_impl_async_class_destroy_safe; + template union loader_impl_async_safe_cast { @@ -265,6 +286,34 @@ struct loader_impl_node_type loader_impl_async_destroy_safe destroy_safe; napi_threadsafe_function threadsafe_destroy; + napi_value object_get_safe_ptr; + loader_impl_async_object_get_safe object_get_safe; + napi_threadsafe_function threadsafe_object_get; + + napi_value object_set_safe_ptr; + loader_impl_async_object_set_safe object_set_safe; + napi_threadsafe_function threadsafe_object_set; + + napi_value object_method_invoke_safe_ptr; + loader_impl_async_object_method_invoke_safe object_method_invoke_safe; + napi_threadsafe_function threadsafe_object_method_invoke; + + napi_value object_destroy_safe_ptr; + loader_impl_async_object_destroy_safe object_destroy_safe; + napi_threadsafe_function threadsafe_object_destroy; + + napi_value class_ctor_safe_ptr; + loader_impl_async_class_ctor_safe class_ctor_safe; + napi_threadsafe_function threadsafe_class_ctor; + + napi_value class_static_invoke_safe_ptr; + loader_impl_async_class_static_invoke_safe class_static_invoke_safe; + napi_threadsafe_function threadsafe_class_static_invoke; + + napi_value class_destroy_safe_ptr; + loader_impl_async_class_destroy_safe class_destroy_safe; + napi_threadsafe_function threadsafe_class_destroy; + uv_thread_t thread; uv_loop_t *thread_loop; @@ -373,6 +422,7 @@ struct loader_impl_discover_klass_safe_type { loader_impl_node node_impl; napi_value klass; + napi_value klass_descriptor; }; struct loader_impl_discover_object_safe_type @@ -426,6 +476,71 @@ struct loader_impl_async_future_await_safe_type future_return ret; }; +struct loader_impl_async_object_get_safe_type +{ + loader_impl_node node_impl; + object obj; + loader_impl_node_object node_obj; + struct accessor_type *accessor; + value ret_v; +}; + +struct loader_impl_async_object_set_safe_type +{ + loader_impl_node node_impl; + object obj; + loader_impl_node_object node_obj; + struct accessor_type *accessor; + value v; +}; + +struct loader_impl_async_object_method_invoke_safe_type +{ + loader_impl_node node_impl; + object obj; + loader_impl_node_object node_obj; + method m; + void **args; + size_t argc; + value ret_v; +}; + +struct loader_impl_async_object_destroy_safe_type +{ + loader_impl_node node_impl; + object obj; + loader_impl_node_object node_obj; +}; + +struct loader_impl_async_class_ctor_safe_type +{ + loader_impl_node node_impl; + klass cls; + loader_impl_node_class node_klass; + const char *name; + void **args; + size_t argc; + object ret_obj; +}; + +struct loader_impl_async_class_static_invoke_safe_type +{ + loader_impl_node node_impl; + klass cls; + loader_impl_node_class node_klass; + method m; + void **args; + size_t argc; + value ret_v; +}; + +struct loader_impl_async_class_destroy_safe_type +{ + loader_impl_node node_impl; + klass cls; + loader_impl_node_class node_klass; +}; + typedef napi_value (*function_resolve_trampoline)(loader_impl_node, napi_env, function_resolve_callback, napi_value, napi_value, void *); typedef napi_value (*function_reject_trampoline)(loader_impl_node, napi_env, function_reject_callback, napi_value, napi_value, void *); @@ -476,13 +591,14 @@ typedef struct loader_impl_class_constructor_callback_closure_type { klass cls; loader_impl_node node_impl; + napi_ref constructor_ref; } * loader_impl_class_constructor_callback_closure; typedef struct loader_impl_class_property_callback_closure_type { klass cls; loader_impl_node node_impl; - char *prop_name; + const char *prop_name; } * loader_impl_class_property_callback_closure; /* Type conversion */ @@ -583,6 +699,34 @@ static void node_loader_impl_clear_safe(napi_env env, loader_impl_async_clear_sa static napi_value node_loader_impl_async_clear_safe(napi_env env, napi_callback_info info); +static void node_loader_impl_object_get_safe(napi_env env, loader_impl_async_object_get_safe object_get_safe); + +static napi_value node_loader_impl_async_object_get_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_object_set_safe(napi_env env, loader_impl_async_object_set_safe object_set_safe); + +static napi_value node_loader_impl_async_object_set_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_object_method_invoke_safe(napi_env env, loader_impl_async_object_method_invoke_safe object_method_invoke_safe); + +static napi_value node_loader_impl_async_object_method_invoke_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_object_destroy_safe(napi_env env, loader_impl_async_object_destroy_safe object_destroy_safe); + +static napi_value node_loader_impl_async_object_destroy_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_class_ctor_safe(napi_env env, loader_impl_async_class_ctor_safe class_ctor_safe); + +static napi_value node_loader_impl_async_class_ctor_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_class_static_invoke_safe(napi_env env, loader_impl_async_class_static_invoke_safe class_static_invoke_safe); + +static napi_value node_loader_impl_async_class_static_invoke_safe(napi_env env, napi_callback_info info); + +static void node_loader_impl_class_destroy_safe(napi_env env, loader_impl_async_class_destroy_safe class_destroy_safe); + +static napi_value node_loader_impl_async_class_destroy_safe(napi_env env, napi_callback_info info); + static value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_discover_function_safe discover_function_safe); static value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_klass_safe discover_klass_safe); @@ -940,7 +1084,7 @@ static napi_value node_loader_impl_class_method_property_callback(napi_env env, napi_get_cb_info(env, info, &argc, NULL, &this_obj, &closure_cast.ptr); closure_cast.safe->node_impl->env = env; - if (argc != 1) + if (argc > 0) { argv = new napi_value[argc]; } @@ -972,6 +1116,7 @@ static napi_value node_loader_impl_class_method_property_callback(napi_env env, } else if (n_methods > 1) { + //TODO: this implementation is problematic for (size_t iterator = 0; iterator < n_methods; ++iterator) { m = vector_at_type(methods, iterator, method); @@ -996,6 +1141,8 @@ static napi_value node_loader_impl_class_method_property_callback(napi_env env, } value result = object_call(obj_impl, m, args, argc); + delete[] args; + return node_loader_impl_value_to_napi(closure_cast.safe->node_impl, env, result); } @@ -1004,13 +1151,13 @@ static napi_value node_loader_impl_class_set_data_property_callback(napi_env env napi_status status; loader_impl_async_safe_cast closure_cast = { NULL }; size_t argc = 1; - napi_value *argv = new napi_value[argc]; + napi_value argv; napi_value this_obj; - napi_get_cb_info(env, info, &argc, argv, &this_obj, &closure_cast.ptr); + napi_get_cb_info(env, info, &argc, &argv, &this_obj, &closure_cast.ptr); closure_cast.safe->node_impl->env = env; - value arg = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[0]); + value arg = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv); value obj; napi_unwrap(env, this_obj, &obj); @@ -1051,75 +1198,109 @@ static napi_value node_loader_impl_class_get_data_property_callback(napi_env env static napi_value node_loader_impl_class_constructor_callback(napi_env env, napi_callback_info info) { napi_status status; + napi_value target; + status = napi_get_new_target(env, info, &target); + node_loader_impl_exception(env, status); + loader_impl_async_safe_cast closure_cast = { NULL }; - napi_get_cb_info(env, info, NULL, NULL, NULL, &closure_cast.ptr); + napi_value jsthis; + napi_get_cb_info(env, info, NULL, NULL, &jsthis, &closure_cast.ptr); /* Set environment */ closure_cast.safe->node_impl->env = env; - char *klass_name_str = const_cast(class_name(closure_cast.safe->cls)); + bool is_constructor = target != nullptr; - constructor ctor = class_default_constructor(closure_cast.safe->cls); - if (ctor == NULL) + if (is_constructor) { - log_write("metacall", LOG_LEVEL_INFO, "NodeJS Loader No default constructor in class: ", klass_name_str); - } + const char *klass_name_str = class_name(closure_cast.safe->cls); - size_t argc = constructor_count(ctor); - napi_value *argv = new napi_value[argc]; + constructor ctor = class_default_constructor(closure_cast.safe->cls); + if (ctor == NULL) + { + log_write("metacall", LOG_LEVEL_INFO, "NodeJS Loader No default constructor in class: ", klass_name_str); + } - napi_get_cb_info(env, info, &argc, argv, NULL, NULL); + size_t argc = constructor_count(ctor); + napi_value *argv = new napi_value[argc]; + if (argv == nullptr) + { + napi_throw_error(env, NULL, "NodeJS Loader memory allocation failed"); + } - void **args = new void *[argc]; + napi_get_cb_info(env, info, &argc, argv, NULL, NULL); - for (size_t iterator = 0; iterator < argc; ++iterator) - { - args[iterator] = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[iterator]); - } + void **args = new void *[argc]; + if (args == nullptr) + { + napi_throw_error(env, NULL, "NodeJS Loader memory allocation failed"); + } - type_id *ids = node_loader_impl_type_ids(args, argc); + for (size_t iterator = 0; iterator < argc; ++iterator) + { + args[iterator] = node_loader_impl_napi_to_value(closure_cast.safe->node_impl, env, NULL, argv[iterator]); + } - ctor = class_constructor(closure_cast.safe->cls, ids, argc); + type_id *ids = node_loader_impl_type_ids(args, argc); - //convert class_name to lower case - std::string obj_name(klass_name_str); - for (size_t i = 0; i < obj_name.size(); i++) - { - obj_name[i] = std::tolower(obj_name[i]); - } + if (ids != NULL) + { + ctor = class_constructor(closure_cast.safe->cls, ids, argc); + free(ids); + } - object o = class_new(closure_cast.safe->cls, obj_name.c_str(), ctor, args, argc); + //convert class_name to lower case + std::string obj_name(klass_name_str); + for (size_t i = 0; i < obj_name.size(); i++) + { + obj_name[i] = std::tolower(obj_name[i]); + } - if (ids != NULL) - { - free(ids); - } + object o = class_new(closure_cast.safe->cls, obj_name.c_str(), ctor, args, argc); - if (o == NULL) - { - return NULL; - } + delete[] args; - value v = value_create_object(o); + if (o == NULL) + { + napi_throw_error(env, NULL, "NodeJS Loader Failed to create object"); + } - if (v == NULL) - { - object_destroy(o); - std::string error_msg = "NodeJS Loader Failed to create object for class: "; - error_msg += klass_name_str; - napi_throw_error(env, NULL, error_msg.c_str()); + value v = value_create_object(o); + + if (v == NULL) + { + object_destroy(o); + std::string error_msg = "NodeJS Loader Failed to create object for class: "; + error_msg += klass_name_str; + napi_throw_error(env, NULL, error_msg.c_str()); + } + + //Todo: Implement finalize callback + status = napi_wrap(env, jsthis, v, NULL, NULL, NULL); + node_loader_impl_exception(env, status); + + return jsthis; } + else + { + napi_value cons; + status = napi_get_reference_value(env, closure_cast.safe->constructor_ref, &cons); + node_loader_impl_exception(env, status); - napi_value js_object; - status = napi_create_object(env, &js_object); - node_loader_impl_exception(env, status); + size_t argc; + status = napi_get_cb_info(env, info, &argc, NULL, NULL, NULL); + node_loader_impl_exception(env, status); - //Todo: Implement finalize callback - status = napi_wrap(env, js_object, v, NULL, NULL, NULL); - node_loader_impl_exception(env, status); + napi_value *argv = new napi_value[argc]; + status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL); + + napi_value instance; + status = napi_new_instance(env, cons, argc, argv, &instance); + node_loader_impl_exception(env, status); - return js_object; + return instance; + } } value node_loader_impl_napi_to_value(loader_impl_node node_impl, napi_env env, napi_value recv, napi_value v) @@ -1341,19 +1522,6 @@ value node_loader_impl_napi_to_value(loader_impl_node node_impl, napi_env env, n } else if (valuetype == napi_function) { - /* Check if function is an ES6 class */ - struct loader_impl_discover_klass_safe_type discover_klass_safe = { - node_impl, - v - }; - - ret = node_loader_impl_discover_klass_safe(env, &discover_klass_safe); - - if (ret != NULL) - { - return ret; - } - struct loader_impl_async_discover_function_safe_type discover_function_safe = { node_impl, v @@ -1591,89 +1759,102 @@ napi_value node_loader_impl_value_to_napi(loader_impl_node node_impl, napi_env e else if (id == TYPE_CLASS) { klass cls = value_to_class(arg_value); - vector methods = class_method_names(cls); - vector static_methods = class_static_method_names(cls); - vector attributes = class_attribute_names(cls); - vector static_attributes = class_static_attribute_names(cls); + vector methods = class_get_methods(cls); + vector static_methods = class_get_static_methods(cls); + vector attributes = class_get_attributes(cls); + vector static_attributes = class_get_static_attributes(cls); size_t property_count = vector_size(methods) + vector_size(static_methods) + vector_size(attributes) + vector_size(static_attributes); + napi_property_descriptor *properties = new napi_property_descriptor[property_count]; size_t iterator = 0; //Define method properties for (size_t i = 0; i < vector_size(methods) && iterator < property_count; iterator++, i++) { + method m = vector_at_type(methods, i, method); + const char *m_name = method_name(m); + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; closure->cls = cls; closure->node_impl = node_impl; - closure->prop_name = vector_at_type(methods, i, char *); + closure->prop_name = m_name; - properties[iterator].utf8name = vector_at_type(methods, i, char *); + properties[iterator].utf8name = m_name; properties[iterator].name = NULL; properties[iterator].method = node_loader_impl_class_method_property_callback; properties[iterator].getter = NULL; properties[iterator].setter = NULL; properties[iterator].value = NULL; properties[iterator].attributes = napi_default_method; - properties[iterator].data = (void *)closure; + properties[iterator].data = reinterpret_cast(closure); } //Define static method properties for (size_t i = 0; i < vector_size(static_methods) && iterator < property_count; iterator++, i++) { + method m = vector_at_type(static_methods, i, method); + const char *m_name = method_name(m); + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; closure->cls = cls; closure->node_impl = node_impl; - closure->prop_name = vector_at_type(static_methods, i, char *); + closure->prop_name = m_name; - properties[iterator].utf8name = vector_at_type(static_methods, i, char *); + properties[iterator].utf8name = m_name; properties[iterator].name = NULL; properties[iterator].method = node_loader_impl_class_method_property_callback; properties[iterator].getter = NULL; properties[iterator].setter = NULL; properties[iterator].value = NULL; properties[iterator].attributes = napi_static; - properties[iterator].data = (void *)closure; + properties[iterator].data = reinterpret_cast(closure); } //Define attribute properties for (size_t i = 0; i < vector_size(attributes) && iterator < property_count; iterator++, i++) { + attribute attr = vector_at_type(attributes, i, attribute); + const char *attr_name = attribute_name(attr); + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; closure->cls = cls; closure->node_impl = node_impl; - closure->prop_name = vector_at_type(attributes, i, char *); + closure->prop_name = attr_name; - properties[iterator].utf8name = vector_at_type(attributes, i, char *); + properties[iterator].utf8name = attr_name; properties[iterator].name = NULL; properties[iterator].method = NULL; properties[iterator].getter = node_loader_impl_class_get_data_property_callback; properties[iterator].setter = node_loader_impl_class_set_data_property_callback; properties[iterator].value = NULL; properties[iterator].attributes = napi_default_jsproperty; - properties[iterator].data = (void *)closure; + properties[iterator].data = reinterpret_cast(closure); } //NodeJS doesn't support static attributes so we define them as normal attributes for (size_t i = 0; i < vector_size(static_attributes) && iterator < property_count; iterator++, i++) { + attribute attr = vector_at_type(static_attributes, i, attribute); + const char *attr_name = attribute_name(attr); + loader_impl_class_property_callback_closure closure = new struct loader_impl_class_property_callback_closure_type; closure->cls = cls; closure->node_impl = node_impl; - closure->prop_name = vector_at_type(static_attributes, i, char *); + closure->prop_name = attr_name; - properties[iterator].utf8name = vector_at_type(static_attributes, i, char *); + properties[iterator].utf8name = attr_name; properties[iterator].name = NULL; properties[iterator].method = NULL; properties[iterator].getter = node_loader_impl_class_get_data_property_callback; properties[iterator].setter = node_loader_impl_class_set_data_property_callback; properties[iterator].value = NULL; properties[iterator].attributes = napi_default_jsproperty; - properties[iterator].data = (void *)closure; + properties[iterator].data = reinterpret_cast(closure); } napi_value result; @@ -1684,6 +1865,10 @@ napi_value node_loader_impl_value_to_napi(loader_impl_node node_impl, napi_env e status = napi_define_class(env, class_name(cls), NAPI_AUTO_LENGTH, node_loader_impl_class_constructor_callback, (void *)ctor_data, property_count, properties, &result); node_loader_impl_exception(env, status); + + status = napi_create_reference(env, result, 1, &ctor_data->constructor_ref); + node_loader_impl_exception(env, status); + return result; } else if (id == TYPE_OBJECT) @@ -2208,83 +2393,207 @@ int node_object_interface_create(object obj, object_impl impl) value node_object_interface_get(object obj, object_impl impl, struct accessor_type *accessor) { - (void)obj; - loader_impl_node_object node_obj = (loader_impl_node_object)impl; - napi_value obj_value = NULL; + value ret = NULL; napi_status status; - status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up call safe arguments */ + node_obj->node_impl->object_get_safe->node_impl = node_obj->node_impl; + node_obj->node_impl->object_get_safe->obj = obj; + node_obj->node_impl->object_get_safe->node_obj = node_obj; + node_obj->node_impl->object_get_safe->accessor = accessor; + node_obj->node_impl->object_get_safe->ret_v = NULL; - attribute attr = accessor->data.attr; - const char *key = (const char *)attribute_name(attr); - //type fieldType = (type)attribute_type(attr); + /* Check if we are in the JavaScript thread */ + if (node_obj->node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_object_get_safe(node_obj->node_impl->env, node_obj->node_impl->object_get_safe); - napi_value prop_value = NULL; - status = napi_get_named_property(node_obj->node_impl->env, obj_value, key, &prop_value); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up return of the static method call */ + ret = node_obj->node_impl->object_get_safe->ret_v; + } + /* Lock the mutex and set the parameters */ + else if (node_obj->node_impl->locked.load() == false && uv_mutex_trylock(&node_obj->node_impl->mutex) == 0) + { + node_obj->node_impl->locked.store(true); - value v = node_loader_impl_napi_to_value(node_obj->node_impl, node_obj->node_impl->env, nullptr, prop_value); + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_get); - return v; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object set in NodeJS loader"); + } + + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_obj->node_impl->threadsafe_object_get, nullptr, napi_tsfn_nonblocking); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to object set thread safe function failed in NodeJS loader"); + } + + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_get, napi_tsfn_release); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object set thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); + + /* Set up return of the function call */ + ret = node_obj->node_impl->object_get_safe->ret_v; + + node_obj->node_impl->locked.store(false); + + /* Unlock the mutex */ + uv_mutex_unlock(&node_obj->node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_object_interface_set, the call has not been executed in order to avoid the deadlock"); + } + + return ret; } int node_object_interface_set(object obj, object_impl impl, struct accessor_type *accessor, value v) { - (void)obj; - loader_impl_node_object node_obj = (loader_impl_node_object)impl; - napi_value obj_value = NULL; napi_status status; - status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up call safe arguments */ + node_obj->node_impl->object_set_safe->node_impl = node_obj->node_impl; + node_obj->node_impl->object_set_safe->obj = obj; + node_obj->node_impl->object_set_safe->node_obj = node_obj; + node_obj->node_impl->object_set_safe->accessor = accessor; + node_obj->node_impl->object_set_safe->v = v; - attribute attr = accessor->data.attr; - const char *key = (const char *)attribute_name(attr); - //type fieldType = (type)attribute_type(attr); + /* Check if we are in the JavaScript thread */ + if (node_obj->node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_object_set_safe(node_obj->node_impl->env, node_obj->node_impl->object_set_safe); + } + /* Lock the mutex and set the parameters */ + else if (node_obj->node_impl->locked.load() == false && uv_mutex_trylock(&node_obj->node_impl->mutex) == 0) + { + node_obj->node_impl->locked.store(true); + + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_set); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object set in NodeJS loader"); + } - napi_value prop_value = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, v); + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_obj->node_impl->threadsafe_object_set, nullptr, napi_tsfn_nonblocking); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to object set thread safe function failed in NodeJS loader"); + } + + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_set, napi_tsfn_release); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object set thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); - status = napi_set_named_property(node_obj->node_impl->env, obj_value, key, prop_value); + node_obj->node_impl->locked.store(false); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Unlock the mutex */ + uv_mutex_unlock(&node_obj->node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_object_interface_set, the call has not been executed in order to avoid the deadlock"); + } return 0; } value node_object_interface_method_invoke(object obj, object_impl impl, method m, object_args args, size_t argc) { - (void)obj; loader_impl_node_object node_obj = (loader_impl_node_object)impl; - napi_value obj_value; - napi_value result; + value ret = NULL; napi_status status; - status = napi_get_reference_value(node_obj->node_impl->env, node_obj->obj_ref, &obj_value); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up call safe arguments */ + node_obj->node_impl->object_method_invoke_safe->node_impl = node_obj->node_impl; + node_obj->node_impl->object_method_invoke_safe->obj = obj; + node_obj->node_impl->object_method_invoke_safe->node_obj = node_obj; + node_obj->node_impl->object_method_invoke_safe->m = m; + node_obj->node_impl->object_method_invoke_safe->args = args; + node_obj->node_impl->object_method_invoke_safe->argc = argc; + node_obj->node_impl->object_method_invoke_safe->ret_v = nullptr; - const char *m_name = method_name(m); + /* Check if we are in the JavaScript thread */ + if (node_obj->node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_object_method_invoke_safe(node_obj->node_impl->env, node_obj->node_impl->object_method_invoke_safe); - napi_value m_value = NULL; - status = napi_get_named_property(node_obj->node_impl->env, obj_value, m_name, &m_value); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up return of the static method call */ + ret = node_obj->node_impl->object_method_invoke_safe->ret_v; + } + /* Lock the mutex and set the parameters */ + else if (node_obj->node_impl->locked.load() == false && uv_mutex_trylock(&node_obj->node_impl->mutex) == 0) + { + node_obj->node_impl->locked.store(true); - napi_value *m_args = new napi_value[argc]; + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_obj->node_impl->threadsafe_object_method_invoke); - for (size_t i = 0; i < argc; i++) - { - m_args[i] = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, args[i]); - } + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for object method invoke in NodeJS loader"); + } - napi_call_function(node_obj->node_impl->env, obj_value, m_value, argc, m_args, &result); + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_obj->node_impl->threadsafe_object_method_invoke, nullptr, napi_tsfn_nonblocking); - value v = node_loader_impl_napi_to_value(node_obj->node_impl, node_obj->node_impl->env, nullptr, result); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to object method invoke thread safe function failed in NodeJS loader"); + } - delete[] m_args; + /* Release call safe function */ + status = napi_release_threadsafe_function(node_obj->node_impl->threadsafe_object_method_invoke, napi_tsfn_release); - return v; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release object method invoke thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_obj->node_impl->cond, &node_obj->node_impl->mutex); + + /* Set up return of the function call */ + ret = node_obj->node_impl->object_method_invoke_safe->ret_v; + + node_obj->node_impl->locked.store(false); + + /* Unlock the mutex */ + uv_mutex_unlock(&node_obj->node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_object_interface_method_invoke, the call has not been executed in order to avoid the deadlock"); + } + + return ret; } value node_object_interface_method_await(object obj, object_impl impl, method m, object_args args, size_t size, object_resolve_callback resolve, object_reject_callback reject, void *ctx) @@ -2314,24 +2623,77 @@ int node_object_interface_destructor(object obj, object_impl impl) void node_object_interface_destroy(object obj, object_impl impl) { - (void)obj; - loader_impl_node_object node_obj = (loader_impl_node_object)impl; if (node_obj != NULL) { if (loader_is_destroyed(node_obj->impl) != 0) { - //Todo - } + loader_impl_node node_impl = node_obj->node_impl; + napi_status status; - free(node_obj); - } -} + /* Set up class destroy safe arguments */ + node_obj->node_impl->object_destroy_safe->node_impl = node_impl; + node_obj->node_impl->object_destroy_safe->node_obj = node_obj; + node_obj->node_impl->object_destroy_safe->obj = obj; -object_interface node_object_interface_singleton(void) -{ - static struct object_interface_type node_object_interface = { + /* Check if we are in the JavaScript thread */ + if (node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_object_destroy_safe(node_impl->env, node_impl->object_destroy_safe); + } + /* Lock the mutex and set the parameters */ + else if (node_impl->locked.load() == false && uv_mutex_trylock(&node_impl->mutex) == 0) + { + node_impl->locked.store(true); + + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_impl->threadsafe_object_destroy); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function class_destroy in NodeJS loader"); + } + + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_impl->threadsafe_object_destroy, nullptr, napi_tsfn_nonblocking); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class_destroy_safe thread safe function failed in NodeJS loader"); + } + + /* Release call safe function */ + status = napi_release_threadsafe_function(node_impl->threadsafe_object_destroy, napi_tsfn_release); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class_destroy_safe thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_impl->cond, &node_impl->mutex); + + node_impl->locked.store(false); + + /* Unlock call safe mutex */ + uv_mutex_unlock(&node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_class_interface_destroy, the call has not been executed in order to avoid the deadlock"); + } + } + + /* Delete node class impl */ + delete node_obj; + } +} + +object_interface node_object_interface_singleton(void) +{ + static struct object_interface_type node_object_interface = { &node_object_interface_create, &node_object_interface_get, &node_object_interface_set, @@ -2358,39 +2720,73 @@ object node_class_interface_constructor(klass cls, class_impl impl, const char * loader_impl_node_class node_klass = (loader_impl_node_class)impl; - loader_impl_node_object node_obj = new struct loader_impl_node_object_type; + object ret = nullptr; + napi_status status; - object obj = object_create(name, ACCESSOR_TYPE_DYNAMIC, node_obj, &node_object_interface_singleton, cls); + /* Set up call safe arguments */ + node_klass->node_impl->class_ctor_safe->node_impl = node_klass->node_impl; + node_klass->node_impl->class_ctor_safe->cls = cls; + node_klass->node_impl->class_ctor_safe->node_klass = node_klass; + node_klass->node_impl->class_ctor_safe->name = name; + node_klass->node_impl->class_ctor_safe->args = args; + node_klass->node_impl->class_ctor_safe->argc = argc; + node_klass->node_impl->class_ctor_safe->ret_obj = nullptr; - if (obj == NULL) + /* Check if we are in the JavaScript thread */ + if (node_klass->node_impl->js_thread_id == std::this_thread::get_id()) { - return NULL; + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_class_ctor_safe(node_klass->node_impl->env, node_klass->node_impl->class_ctor_safe); + + /* Set up return of the constructor call */ + ret = node_klass->node_impl->class_ctor_safe->ret_obj; } + /* Lock the mutex and set the parameters */ + else if (node_klass->node_impl->locked.load() == false && uv_mutex_trylock(&node_klass->node_impl->mutex) == 0) + { + node_klass->node_impl->locked.store(true); - napi_value *m_args = new napi_value[argc]; + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_klass->node_impl->threadsafe_class_ctor); - for (size_t i = 0; i < argc; i++) - { - m_args[i] = node_loader_impl_value_to_napi(node_obj->node_impl, node_obj->node_impl->env, args[i]); - } + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for class constructor call in NodeJS loader"); + } - napi_status status; - napi_value klass_cons; - status = napi_get_reference_value(node_obj->node_impl->env, node_klass->klass_ref, &klass_cons); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_klass->node_impl->threadsafe_class_ctor, nullptr, napi_tsfn_nonblocking); - napi_value result; - status = napi_new_instance(node_obj->node_impl->env, klass_cons, argc, m_args, &result); - node_loader_impl_exception(node_obj->node_impl->env, status); + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class constructor thread safe function failed in NodeJS loader"); + } - //Todo check that object is an instance of constructor + /* Release call safe function */ + status = napi_release_threadsafe_function(node_klass->node_impl->threadsafe_class_ctor, napi_tsfn_release); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class constructor thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_klass->node_impl->cond, &node_klass->node_impl->mutex); - status = napi_create_reference(node_obj->node_impl->env, result, 1, &node_obj->obj_ref); - node_loader_impl_exception(node_obj->node_impl->env, status); + /* Set up return of the function call */ + ret = node_klass->node_impl->class_ctor_safe->ret_obj; - delete[] m_args; + node_klass->node_impl->locked.store(false); + + /* Unlock the mutex */ + uv_mutex_unlock(&node_klass->node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_class_interface_constructor, the call has not been executed in order to avoid the deadlock"); + } - return obj; + return ret; } value node_class_interface_static_get(klass cls, class_impl impl, struct accessor_type *accessor) @@ -2416,34 +2812,73 @@ value node_class_interface_static_invoke(klass cls, class_impl impl, method m, c { (void)cls; loader_impl_node_class node_klass = (loader_impl_node_class)impl; - napi_value klass_cons; - napi_value result; + value ret = NULL; napi_status status; - status = napi_get_reference_value(node_klass->node_impl->env, node_klass->klass_ref, &klass_cons); - node_loader_impl_exception(node_klass->node_impl->env, status); + /* Set up call safe arguments */ + node_klass->node_impl->class_static_invoke_safe->node_impl = node_klass->node_impl; + node_klass->node_impl->class_static_invoke_safe->cls = cls; + node_klass->node_impl->class_static_invoke_safe->node_klass = node_klass; + node_klass->node_impl->class_static_invoke_safe->m = m; + node_klass->node_impl->class_static_invoke_safe->args = args; + node_klass->node_impl->class_static_invoke_safe->argc = argc; + node_klass->node_impl->class_static_invoke_safe->ret_v = nullptr; - const char *m_name = method_name(m); + /* Check if we are in the JavaScript thread */ + if (node_klass->node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_class_static_invoke_safe(node_klass->node_impl->env, node_klass->node_impl->class_static_invoke_safe); - napi_value m_value = NULL; - status = napi_get_named_property(node_klass->node_impl->env, klass_cons, m_name, &m_value); - node_loader_impl_exception(node_klass->node_impl->env, status); + /* Set up return of the static method call */ + ret = node_klass->node_impl->class_static_invoke_safe->ret_v; + } + /* Lock the mutex and set the parameters */ + else if (node_klass->node_impl->locked.load() == false && uv_mutex_trylock(&node_klass->node_impl->mutex) == 0) + { + node_klass->node_impl->locked.store(true); - napi_value *m_args = new napi_value[argc]; + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_klass->node_impl->threadsafe_class_static_invoke); - for (size_t i = 0; i < argc; i++) - { - m_args[i] = node_loader_impl_value_to_napi(node_klass->node_impl, node_klass->node_impl->env, args[i]); - } + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function for static method invoke in NodeJS loader"); + } - status = napi_call_function(node_klass->node_impl->env, klass_cons, m_value, argc, m_args, &result); - node_loader_impl_exception(node_klass->node_impl->env, status); + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_klass->node_impl->threadsafe_class_static_invoke, nullptr, napi_tsfn_nonblocking); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to static method invoke thread safe function failed in NodeJS loader"); + } - value v = node_loader_impl_napi_to_value(node_klass->node_impl, node_klass->node_impl->env, nullptr, result); + /* Release call safe function */ + status = napi_release_threadsafe_function(node_klass->node_impl->threadsafe_class_static_invoke, napi_tsfn_release); - delete[] m_args; + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release static method invoke thread safe function in NodeJS loader"); + } - return v; + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_klass->node_impl->cond, &node_klass->node_impl->mutex); + + /* Set up return of the function call */ + ret = node_klass->node_impl->class_static_invoke_safe->ret_v; + + node_klass->node_impl->locked.store(false); + + /* Unlock the mutex */ + uv_mutex_unlock(&node_klass->node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_class_interface_static_invoke, the call has not been executed in order to avoid the deadlock"); + } + + return ret; } value node_class_interface_static_await(klass cls, class_impl impl, method m, class_args args, size_t size, class_resolve_callback resolve, class_reject_callback reject, void *ctx) @@ -2463,18 +2898,71 @@ value node_class_interface_static_await(klass cls, class_impl impl, method m, cl void node_class_interface_destroy(klass cls, class_impl impl) { - loader_impl_node_class node_class = (loader_impl_node_class)impl; - - (void)cls; + loader_impl_node_class node_klass = (loader_impl_node_class)impl; - if (node_class != NULL) + if (node_klass != NULL) { - if (loader_is_destroyed(node_class->impl) != 0) + if (loader_is_destroyed(node_klass->impl) != 0) { - //TODO + loader_impl_node node_impl = node_klass->node_impl; + napi_status status; + + /* Set up class destroy safe arguments */ + node_klass->node_impl->class_destroy_safe->node_impl = node_impl; + node_klass->node_impl->class_destroy_safe->node_klass = node_klass; + node_klass->node_impl->class_destroy_safe->cls = cls; + + /* Check if we are in the JavaScript thread */ + if (node_impl->js_thread_id == std::this_thread::get_id()) + { + /* We are already in the V8 thread, we can call safely */ + node_loader_impl_class_destroy_safe(node_impl->env, node_impl->class_destroy_safe); + } + /* Lock the mutex and set the parameters */ + else if (node_impl->locked.load() == false && uv_mutex_trylock(&node_impl->mutex) == 0) + { + node_impl->locked.store(true); + + /* Acquire the thread safe function in order to do the call */ + status = napi_acquire_threadsafe_function(node_impl->threadsafe_class_destroy); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to aquire thread safe function class_destroy in NodeJS loader"); + } + + /* Execute the thread safe call in a nonblocking manner */ + status = napi_call_threadsafe_function(node_impl->threadsafe_class_destroy, nullptr, napi_tsfn_nonblocking); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "call to class_destroy_safe thread safe function failed in NodeJS loader"); + } + + /* Release call safe function */ + status = napi_release_threadsafe_function(node_impl->threadsafe_class_destroy, napi_tsfn_release); + + if (status != napi_ok) + { + log_write("metacall", LOG_LEVEL_ERROR, "Unable to release class_destroy_safe thread safe function in NodeJS loader"); + } + + /* Wait for the execution of the safe call */ + uv_cond_wait(&node_impl->cond, &node_impl->mutex); + + node_impl->locked.store(false); + + /* Unlock call safe mutex */ + uv_mutex_unlock(&node_impl->mutex); + } + else + { + log_write("metacall", LOG_LEVEL_ERROR, "Potential deadlock detected in node_class_interface_destroy, the call has not been executed in order to avoid the deadlock"); + } } - free(node_class); + /* Delete node class impl */ + delete node_klass; } } @@ -2627,168 +3115,576 @@ void node_loader_impl_execution_path_safe(napi_env env, loader_impl_async_execut napi_valuetype valuetype; napi_value argv[1]; - status = napi_get_named_property(env, function_table_object, execution_path_str, &function_trampoline_execution_path); + status = napi_get_named_property(env, function_table_object, execution_path_str, &function_trampoline_execution_path); + + node_loader_impl_exception(env, status); + + status = napi_typeof(env, function_trampoline_execution_path, &valuetype); + + node_loader_impl_exception(env, status); + + if (valuetype != napi_function) + { + napi_throw_type_error(env, nullptr, "Invalid function execution_path in function table object"); + } + + /* Create parameters */ + status = napi_create_string_utf8(env, execution_path_safe->path, strlen(execution_path_safe->path), &argv[0]); + + node_loader_impl_exception(env, status); + + /* Call to load from file function */ + napi_value global, return_value; + + status = napi_get_reference_value(env, node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + status = napi_call_function(env, global, function_trampoline_execution_path, 1, argv, &return_value); + + node_loader_impl_exception(env, status); + } + + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); +} + +napi_value node_loader_impl_async_execution_path_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast execution_path_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &execution_path_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&execution_path_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + execution_path_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_execution_path_safe(env, execution_path_cast.safe); + + /* Clear environment */ + // execution_path_cast.safe->node_impl->env = NULL; + + /* Signal start condition */ + uv_cond_signal(&execution_path_cast.safe->node_impl->cond); + + uv_mutex_unlock(&execution_path_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_func_call_safe(napi_env env, loader_impl_async_func_call_safe func_call_safe) +{ + napi_handle_scope handle_scope; + size_t args_size; + value *args; + napi_value *argv; + loader_impl_node_function node_func; + size_t args_count; + signature s = function_signature(func_call_safe->func); + const size_t signature_args_size = signature_count(s); + + /* Get function data */ + args_size = func_call_safe->size; + node_func = func_call_safe->node_func; + args = func_call_safe->args; + + /* Allocate dynamically more space for values in case of variable arguments */ + argv = args_size > signature_args_size ? static_cast(malloc(sizeof(napi_value) * args_size)) : node_func->argv; + + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); + + node_loader_impl_exception(env, status); + + /* Build parameters */ + for (args_count = 0; args_count < args_size; ++args_count) + { + /* Define parameter */ + argv[args_count] = node_loader_impl_value_to_napi(func_call_safe->node_impl, env, args[args_count]); + } + + /* Get function reference */ + napi_value function_ptr; + + status = napi_get_reference_value(env, node_func->func_ref, &function_ptr); + + node_loader_impl_exception(env, status); + + /* Get global */ + napi_value global; + + status = napi_get_reference_value(env, func_call_safe->node_impl->global_ref, &global); + + node_loader_impl_exception(env, status); + + /* Call to function */ + napi_value func_return; + + status = napi_call_function(env, global, function_ptr, args_size, argv, &func_return); + + func_call_safe->ret = node_loader_impl_exception_value(func_call_safe->node_impl, env, status, func_call_safe->recv); + + if (func_call_safe->ret == NULL) + { + /* Convert function return to value */ + func_call_safe->ret = node_loader_impl_napi_to_value(func_call_safe->node_impl, env, func_call_safe->recv, func_return); + } + + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); + + if (args_size > signature_args_size) + { + free(argv); + } +} + +napi_value node_loader_impl_async_func_call_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast func_call_cast = { NULL }; + napi_status status; + napi_value recv; + + status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_call_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&func_call_cast.safe->node_impl->mutex); + + /* Store function recv for reentrant calls */ + func_call_cast.safe->recv = recv; + + /* Store environment for reentrant calls */ + func_call_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_func_call_safe(env, func_call_cast.safe); + + /* Clear environment */ + // func_call_cast.safe->node_impl->env = NULL; + + /* Signal function call condition */ + uv_cond_signal(&func_call_cast.safe->node_impl->cond); + + uv_mutex_unlock(&func_call_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_object_method_invoke_safe(napi_env env, loader_impl_async_object_method_invoke_safe object_method_invoke_safe) +{ + napi_value obj_value; + napi_value result; + napi_status status; + + status = napi_get_reference_value(env, object_method_invoke_safe->node_obj->obj_ref, &obj_value); + node_loader_impl_exception(env, status); + + const char *m_name = method_name(object_method_invoke_safe->m); + + napi_value m_value = NULL; + status = napi_get_named_property(env, obj_value, m_name, &m_value); + node_loader_impl_exception(env, status); + + napi_value *m_args = new napi_value[object_method_invoke_safe->argc]; + + for (size_t i = 0; i < object_method_invoke_safe->argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(object_method_invoke_safe->node_impl, env, object_method_invoke_safe->args[i]); + } + + napi_call_function(env, obj_value, m_value, object_method_invoke_safe->argc, m_args, &result); + + value v = node_loader_impl_napi_to_value(object_method_invoke_safe->node_impl, env, nullptr, result); + + delete[] m_args; + + object_method_invoke_safe->ret_v = v; +} + +napi_value node_loader_impl_async_object_method_invoke_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast object_method_invoke_cast = { NULL }; + napi_status status; + + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &object_method_invoke_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&object_method_invoke_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + object_method_invoke_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_object_method_invoke_safe(env, object_method_invoke_cast.safe); + + /* Signal function call condition */ + uv_cond_signal(&object_method_invoke_cast.safe->node_impl->cond); + + uv_mutex_unlock(&object_method_invoke_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_object_get_safe(napi_env env, loader_impl_async_object_get_safe object_get_safe) +{ + napi_value obj_value = NULL; + napi_status status; + + status = napi_get_reference_value(env, object_get_safe->node_obj->obj_ref, &obj_value); + node_loader_impl_exception(env, status); + + attribute attr = object_get_safe->accessor->data.attr; + const char *key = (const char *)attribute_name(attr); + //type fieldType = (type)attribute_type(attr); + + napi_value prop_value = NULL; + status = napi_get_named_property(env, obj_value, key, &prop_value); + node_loader_impl_exception(env, status); + + value v = node_loader_impl_napi_to_value(object_get_safe->node_impl, env, nullptr, prop_value); + + object_get_safe->ret_v = v; +} + +napi_value node_loader_impl_async_object_get_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast object_get_cast = { NULL }; + napi_status status; + + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &object_get_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&object_get_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + object_get_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_object_get_safe(env, object_get_cast.safe); + + /* Signal function call condition */ + uv_cond_signal(&object_get_cast.safe->node_impl->cond); + + uv_mutex_unlock(&object_get_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_object_set_safe(napi_env env, loader_impl_async_object_set_safe object_set_safe) +{ + napi_value obj_value = NULL; + napi_status status; + + status = napi_get_reference_value(env, object_set_safe->node_obj->obj_ref, &obj_value); + node_loader_impl_exception(env, status); + + attribute attr = object_set_safe->accessor->data.attr; + const char *key = (const char *)attribute_name(attr); + //type fieldType = (type)attribute_type(attr); + + napi_value prop_value = node_loader_impl_value_to_napi(object_set_safe->node_impl, env, object_set_safe->v); + + status = napi_set_named_property(env, obj_value, key, prop_value); + + node_loader_impl_exception(env, status); +} + +napi_value node_loader_impl_async_object_set_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast object_set_cast = { NULL }; + napi_status status; + + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &object_set_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&object_set_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + object_set_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_object_set_safe(env, object_set_cast.safe); + + /* Signal function call condition */ + uv_cond_signal(&object_set_cast.safe->node_impl->cond); + + uv_mutex_unlock(&object_set_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_object_destroy_safe(napi_env env, loader_impl_async_object_destroy_safe object_destroy_safe) +{ + uint32_t ref_count = 0; + napi_handle_scope handle_scope; + + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); + + node_loader_impl_exception(env, status); + + /* Clear class persistent reference */ + status = napi_reference_unref(env, object_destroy_safe->node_obj->obj_ref, &ref_count); + + node_loader_impl_exception(env, status); + + if (ref_count != 0) + { + /* TODO: Error handling */ + } + + status = napi_delete_reference(env, object_destroy_safe->node_obj->obj_ref); + + node_loader_impl_exception(env, status); + + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); +} + +napi_value node_loader_impl_async_object_destroy_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast object_destroy_safe_cast = { NULL }; + + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &object_destroy_safe_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock node implementation mutex */ + uv_mutex_lock(&object_destroy_safe_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + object_destroy_safe_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_object_destroy_safe(env, object_destroy_safe_cast.safe); + + /* Signal function destroy condition */ + uv_cond_signal(&object_destroy_safe_cast.safe->node_impl->cond); + + uv_mutex_unlock(&object_destroy_safe_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_class_static_invoke_safe(napi_env env, loader_impl_async_class_static_invoke_safe class_static_invoke_safe) +{ + napi_status status; + napi_handle_scope handle_scope; + + /* Create scope */ + status = napi_open_handle_scope(env, &handle_scope); + ; + napi_value klass_cons; + napi_value result; + + status = napi_get_reference_value(env, class_static_invoke_safe->node_klass->klass_ref, &klass_cons); + node_loader_impl_exception(env, status); + + const char *m_name = method_name(class_static_invoke_safe->m); + + napi_value m_value = NULL; + status = napi_get_named_property(env, klass_cons, m_name, &m_value); + node_loader_impl_exception(env, status); + + napi_value *m_args = new napi_value[class_static_invoke_safe->argc]; + + for (size_t i = 0; i < class_static_invoke_safe->argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(class_static_invoke_safe->node_impl, env, class_static_invoke_safe->args[i]); + } + + //Not sure if we need to pass a recv value since this is a static method + status = napi_call_function(env, klass_cons, m_value, class_static_invoke_safe->argc, m_args, &result); + node_loader_impl_exception(env, status); + + value v = node_loader_impl_napi_to_value(class_static_invoke_safe->node_impl, env, nullptr, result); + + delete[] m_args; + + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); + + class_static_invoke_safe->ret_v = v; +} + +napi_value node_loader_impl_async_class_static_invoke_safe(napi_env env, napi_callback_info info) +{ + loader_impl_async_safe_cast class_static_invoke_cast = { NULL }; + napi_status status; + + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &class_static_invoke_cast.ptr); + + node_loader_impl_exception(env, status); + + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&class_static_invoke_cast.safe->node_impl->mutex); + + /* Store environment for reentrant calls */ + class_static_invoke_cast.safe->node_impl->env = env; + + /* Call to the implementation function */ + node_loader_impl_class_static_invoke_safe(env, class_static_invoke_cast.safe); + + /* Signal function call condition */ + uv_cond_signal(&class_static_invoke_cast.safe->node_impl->cond); + + uv_mutex_unlock(&class_static_invoke_cast.safe->node_impl->mutex); + + return nullptr; +} + +void node_loader_impl_class_ctor_safe(napi_env env, loader_impl_async_class_ctor_safe class_ctor_safe) +{ + napi_handle_scope handle_scope; + + /* Create scope */ + napi_status status = napi_open_handle_scope(env, &handle_scope); + + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + loader_impl_node_object node_obj = new struct loader_impl_node_object_type; - status = napi_typeof(env, function_trampoline_execution_path, &valuetype); + object obj = object_create(class_ctor_safe->name, ACCESSOR_TYPE_DYNAMIC, node_obj, &node_object_interface_singleton, class_ctor_safe->cls); - node_loader_impl_exception(env, status); + if (obj == NULL) + { + class_ctor_safe->ret_obj = NULL; + return; + } - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function execution_path in function table object"); - } + /* Get loader implementation from class */ + node_obj->node_impl = class_ctor_safe->node_impl; + //node_obj->impl = node_klass->impl; - /* Create parameters */ - status = napi_create_string_utf8(env, execution_path_safe->path, strlen(execution_path_safe->path), &argv[0]); + napi_value *m_args = new napi_value[class_ctor_safe->argc]; - node_loader_impl_exception(env, status); + for (size_t i = 0; i < class_ctor_safe->argc; i++) + { + m_args[i] = node_loader_impl_value_to_napi(class_ctor_safe->node_impl, env, class_ctor_safe->args[i]); + } - /* Call to load from file function */ - napi_value global, return_value; + napi_value klass_cons; + status = napi_get_reference_value(env, class_ctor_safe->node_klass->klass_ref, &klass_cons); + node_loader_impl_exception(env, status); - status = napi_get_reference_value(env, node_impl->global_ref, &global); + napi_value result; + status = napi_new_instance(env, klass_cons, class_ctor_safe->argc, m_args, &result); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); + //Todo check that object is an instance of constructor - status = napi_call_function(env, global, function_trampoline_execution_path, 1, argv, &return_value); + status = napi_create_reference(env, result, 1, &node_obj->obj_ref); + node_loader_impl_exception(env, status); - node_loader_impl_exception(env, status); - } + delete[] m_args; /* Close scope */ status = napi_close_handle_scope(env, handle_scope); node_loader_impl_exception(env, status); + + class_ctor_safe->ret_obj = obj; } -napi_value node_loader_impl_async_execution_path_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_class_ctor_safe(napi_env env, napi_callback_info info) { - loader_impl_async_safe_cast execution_path_cast = { NULL }; + loader_impl_async_safe_cast class_ctor_safe_cast = { NULL }; + napi_status status; - napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &execution_path_cast.ptr); + status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &class_ctor_safe_cast.ptr); node_loader_impl_exception(env, status); - /* Lock node implementation mutex */ - uv_mutex_lock(&execution_path_cast.safe->node_impl->mutex); + /* Lock the call safe mutex and get the parameters */ + uv_mutex_lock(&class_ctor_safe_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - execution_path_cast.safe->node_impl->env = env; + class_ctor_safe_cast.safe->node_impl->env = env; + printf("calling class_ctor_safe async"); /* Call to the implementation function */ - node_loader_impl_execution_path_safe(env, execution_path_cast.safe); - - /* Clear environment */ - // execution_path_cast.safe->node_impl->env = NULL; + node_loader_impl_class_ctor_safe(env, class_ctor_safe_cast.safe); - /* Signal start condition */ - uv_cond_signal(&execution_path_cast.safe->node_impl->cond); + /* Signal function call condition */ + uv_cond_signal(&class_ctor_safe_cast.safe->node_impl->cond); - uv_mutex_unlock(&execution_path_cast.safe->node_impl->mutex); + uv_mutex_unlock(&class_ctor_safe_cast.safe->node_impl->mutex); return nullptr; } -void node_loader_impl_func_call_safe(napi_env env, loader_impl_async_func_call_safe func_call_safe) +void node_loader_impl_class_destroy_safe(napi_env env, loader_impl_async_class_destroy_safe class_destroy_safe) { + uint32_t ref_count = 0; napi_handle_scope handle_scope; - size_t args_size; - value *args; - napi_value *argv; - loader_impl_node_function node_func; - size_t args_count; - signature s = function_signature(func_call_safe->func); - const size_t signature_args_size = signature_count(s); - - /* Get function data */ - args_size = func_call_safe->size; - node_func = func_call_safe->node_func; - args = func_call_safe->args; - - /* Allocate dynamically more space for values in case of variable arguments */ - argv = args_size > signature_args_size ? static_cast(malloc(sizeof(napi_value) * args_size)) : node_func->argv; /* Create scope */ napi_status status = napi_open_handle_scope(env, &handle_scope); node_loader_impl_exception(env, status); - /* Build parameters */ - for (args_count = 0; args_count < args_size; ++args_count) - { - /* Define parameter */ - argv[args_count] = node_loader_impl_value_to_napi(func_call_safe->node_impl, env, args[args_count]); - } - - /* Get function reference */ - napi_value function_ptr; - - status = napi_get_reference_value(env, node_func->func_ref, &function_ptr); + /* Clear class persistent reference */ + status = napi_reference_unref(env, class_destroy_safe->node_klass->klass_ref, &ref_count); node_loader_impl_exception(env, status); - /* Get global */ - napi_value global; + if (ref_count != 0) + { + /* TODO: Error handling */ + } - status = napi_get_reference_value(env, func_call_safe->node_impl->global_ref, &global); + status = napi_delete_reference(env, class_destroy_safe->node_klass->klass_ref); node_loader_impl_exception(env, status); - /* Call to function */ - napi_value func_return; - - status = napi_call_function(env, global, function_ptr, args_size, argv, &func_return); - - func_call_safe->ret = node_loader_impl_exception_value(func_call_safe->node_impl, env, status, func_call_safe->recv); - - if (func_call_safe->ret == NULL) - { - /* Convert function return to value */ - func_call_safe->ret = node_loader_impl_napi_to_value(func_call_safe->node_impl, env, func_call_safe->recv, func_return); - } - /* Close scope */ status = napi_close_handle_scope(env, handle_scope); node_loader_impl_exception(env, status); - - if (args_size > signature_args_size) - { - free(argv); - } } -napi_value node_loader_impl_async_func_call_safe(napi_env env, napi_callback_info info) +napi_value node_loader_impl_async_class_destroy_safe(napi_env env, napi_callback_info info) { - loader_impl_async_safe_cast func_call_cast = { NULL }; - napi_status status; - napi_value recv; + loader_impl_async_safe_cast class_destroy_safe_cast = { NULL }; - status = napi_get_cb_info(env, info, nullptr, nullptr, &recv, &func_call_cast.ptr); + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &class_destroy_safe_cast.ptr); node_loader_impl_exception(env, status); - /* Lock the call safe mutex and get the parameters */ - uv_mutex_lock(&func_call_cast.safe->node_impl->mutex); - - /* Store function recv for reentrant calls */ - func_call_cast.safe->recv = recv; + /* Lock node implementation mutex */ + uv_mutex_lock(&class_destroy_safe_cast.safe->node_impl->mutex); /* Store environment for reentrant calls */ - func_call_cast.safe->node_impl->env = env; + class_destroy_safe_cast.safe->node_impl->env = env; /* Call to the implementation function */ - node_loader_impl_func_call_safe(env, func_call_cast.safe); - - /* Clear environment */ - // func_call_cast.safe->node_impl->env = NULL; + node_loader_impl_class_destroy_safe(env, class_destroy_safe_cast.safe); - /* Signal function call condition */ - uv_cond_signal(&func_call_cast.safe->node_impl->cond); + /* Signal function destroy condition */ + uv_cond_signal(&class_destroy_safe_cast.safe->node_impl->cond); - uv_mutex_unlock(&func_call_cast.safe->node_impl->mutex); + uv_mutex_unlock(&class_destroy_safe_cast.safe->node_impl->mutex); return nullptr; } @@ -3739,6 +4635,28 @@ value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_di node_loader_impl_exception(env, status); + /* Check if function is an ES6 class */ + bool is_klass = false; + status = napi_has_named_property(env, function_descriptor, "klass", &is_klass); + + node_loader_impl_exception(env, status); + + if (is_klass == true) + { + struct loader_impl_discover_klass_safe_type discover_klass_safe = { + discover_function_safe->node_impl, + discover_function_safe->func, + function_descriptor + }; + + value klass_value = node_loader_impl_discover_klass_safe(env, &discover_klass_safe); + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); + return klass_value; + } + /* Convert return value (discover object) to context */ napi_value func_name; char *func_name_str = NULL; @@ -4017,13 +4935,10 @@ value node_loader_impl_discover_function_safe(napi_env env, loader_impl_async_di value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_klass_safe discover_klass_safe) { - static const char discover_klass_str[] = "discover_klass"; - napi_value discover_klass_str_value; - napi_value function_table_object; - bool result = false; napi_handle_scope handle_scope; klass c = NULL; constructor ctor = NULL; + value klass_value = NULL; int ret = 1; /* Create scope */ @@ -4031,60 +4946,19 @@ value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_kl node_loader_impl_exception(env, status); - /* Get function table object from reference */ - status = napi_get_reference_value(env, discover_klass_safe->node_impl->function_table_object_ref, &function_table_object); - - node_loader_impl_exception(env, status); - - /* Create function string */ - status = napi_create_string_utf8(env, discover_klass_str, sizeof(discover_klass_str) - 1, &discover_klass_str_value); - - node_loader_impl_exception(env, status); - - /* Check if exists in the table */ - status = napi_has_own_property(env, function_table_object, discover_klass_str_value, &result); + /* Check if function is an ES6 class */ + bool is_klass = false; + status = napi_has_named_property(env, discover_klass_safe->klass_descriptor, "klass", &is_klass); node_loader_impl_exception(env, status); - if (result == true) + if (is_klass == true) { - napi_value klass_trampoline_discover; - napi_valuetype valuetype; - napi_value argv[1]; - - status = napi_get_named_property(env, function_table_object, discover_klass_str, &klass_trampoline_discover); - - node_loader_impl_exception(env, status); - - status = napi_typeof(env, klass_trampoline_discover, &valuetype); - - node_loader_impl_exception(env, status); - - if (valuetype != napi_function) - { - napi_throw_type_error(env, nullptr, "Invalid function discover in function table object"); - } - - /* Define parameters */ - argv[0] = discover_klass_safe->klass; - - /* Call to load from file function */ - napi_value global, klass_descriptor; - - status = napi_get_reference_value(env, discover_klass_safe->node_impl->global_ref, &global); - - node_loader_impl_exception(env, status); - - status = napi_call_function(env, global, klass_trampoline_discover, 1, argv, &klass_descriptor); - - node_loader_impl_exception(env, status); - - /* Convert return value (discover object) to context */ napi_value klass_name; char *klass_name_str = NULL; bool has_name = false; - status = napi_has_named_property(env, klass_descriptor, "name", &has_name); + status = napi_has_named_property(env, discover_klass_safe->klass_descriptor, "name", &has_name); node_loader_impl_exception(env, status); @@ -4093,7 +4967,7 @@ value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_kl { size_t klass_name_length = 0; - status = napi_get_named_property(env, klass_descriptor, "name", &klass_name); + status = napi_get_named_property(env, discover_klass_safe->klass_descriptor, "name", &klass_name); node_loader_impl_exception(env, status); @@ -4123,13 +4997,13 @@ value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_kl napi_value methods; bool has_methods = false; - status = napi_has_named_property(env, klass_descriptor, "methods", &has_methods); + status = napi_has_named_property(env, discover_klass_safe->klass_descriptor, "methods", &has_methods); node_loader_impl_exception(env, status); if (has_methods == true) { - status = napi_get_named_property(env, klass_descriptor, "methods", &methods); + status = napi_get_named_property(env, discover_klass_safe->klass_descriptor, "methods", &methods); node_loader_impl_exception(env, status); @@ -4393,13 +5267,13 @@ value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_kl bool has_attr = false; napi_value attr_field; - status = napi_has_named_property(env, klass_descriptor, "attributes", &has_attr); + status = napi_has_named_property(env, discover_klass_safe->klass_descriptor, "attributes", &has_attr); node_loader_impl_exception(env, status); if (has_attr == true) { - status = napi_get_named_property(env, klass_descriptor, "attributes", &attr_field); + status = napi_get_named_property(env, discover_klass_safe->klass_descriptor, "attributes", &attr_field); node_loader_impl_exception(env, status); @@ -4459,10 +5333,16 @@ value node_loader_impl_discover_klass_safe(napi_env env, loader_impl_discover_kl } } } - return value_create_class(c); + + klass_value = value_create_class(c); } - return NULL; + /* Close scope */ + status = napi_close_handle_scope(env, handle_scope); + + node_loader_impl_exception(env, status); + + return klass_value; } /* @@ -4866,7 +5746,8 @@ void node_loader_impl_discover_safe(napi_env env, loader_impl_async_discover_saf struct loader_impl_discover_klass_safe_type discover_klass_safe = { discover_safe->node_impl, - klass_ptr + klass_ptr, + property_descriptor }; value v = node_loader_impl_discover_klass_safe(env, &discover_klass_safe); @@ -5134,6 +6015,97 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi &node_impl->threadsafe_future_delete); } + /* Safe object get */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_object_get_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_object_get_safe, + (loader_impl_async_object_get_safe_type **)(&node_impl->object_get_safe), + &node_impl->object_get_safe_ptr, + &node_impl->threadsafe_object_get); + } + + /* Safe object set */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_object_set_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_object_set_safe, + (loader_impl_async_object_set_safe_type **)(&node_impl->object_set_safe), + &node_impl->object_set_safe_ptr, + &node_impl->threadsafe_object_set); + } + + /* Safe object method invoke */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_object_method_invoke_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_object_method_invoke_safe, + (loader_impl_async_object_method_invoke_safe_type **)(&node_impl->object_method_invoke_safe), + &node_impl->object_method_invoke_safe_ptr, + &node_impl->threadsafe_object_method_invoke); + } + + /* Safe object destroy */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_object_destroy_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_object_destroy_safe, + (loader_impl_async_object_destroy_safe_type **)(&node_impl->object_destroy_safe), + &node_impl->object_destroy_safe_ptr, + &node_impl->threadsafe_object_destroy); + } + + /* Safe class constructor */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_class_ctor_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_class_ctor_safe, + (loader_impl_async_class_ctor_safe_type **)(&node_impl->class_ctor_safe), + &node_impl->class_ctor_safe_ptr, + &node_impl->threadsafe_class_ctor); + } + + /* Safe class static invoke */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_class_static_invoke_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_class_static_invoke_safe, + (loader_impl_async_class_static_invoke_safe_type **)(&node_impl->class_static_invoke_safe), + &node_impl->class_static_invoke_safe_ptr, + &node_impl->threadsafe_class_static_invoke); + } + + /* Safe class destroy */ + { + static const char threadsafe_func_name_str[] = "node_loader_impl_async_class_destroy_safe"; + + node_loader_impl_thread_safe_function_initialize( + env, + threadsafe_func_name_str, sizeof(threadsafe_func_name_str), + &node_loader_impl_async_class_destroy_safe, + (loader_impl_async_class_destroy_safe_type **)(&node_impl->class_destroy_safe), + &node_impl->class_destroy_safe_ptr, + &node_impl->threadsafe_class_destroy); + } + /* Safe destroy */ { static const char threadsafe_func_name_str[] = "node_loader_impl_async_destroy_safe"; @@ -6357,6 +7329,55 @@ void node_loader_impl_destroy_safe_impl(loader_impl_node node_impl, napi_env env node_loader_impl_exception(env, status); } + /* Safe object get */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_object_get, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe object set */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_object_set, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe object method invoke */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_object_method_invoke, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe object destroy */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_object_destroy, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe class constructor */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_class_ctor, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe class static invoke */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_class_static_invoke, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + + /* Safe class destroy */ + { + status = napi_release_threadsafe_function(node_impl->threadsafe_class_destroy, napi_tsfn_abort); + + node_loader_impl_exception(env, status); + } + /* Safe destroy */ { status = napi_release_threadsafe_function(node_impl->threadsafe_destroy, napi_tsfn_abort); @@ -6533,6 +7554,13 @@ int node_loader_impl_destroy(loader_impl impl) delete node_impl->future_await_safe; delete node_impl->future_delete_safe; delete node_impl->destroy_safe; + delete node_impl->object_get_safe; + delete node_impl->object_set_safe; + delete node_impl->object_method_invoke_safe; + delete node_impl->object_destroy_safe; + delete node_impl->class_ctor_safe; + delete node_impl->class_static_invoke_safe; + delete node_impl->class_destroy_safe; #ifdef __ANDROID__ /* Close file descriptors */ diff --git a/source/reflect/include/reflect/reflect_class.h b/source/reflect/include/reflect/reflect_class.h index ad9967f6c..8bc64b6be 100644 --- a/source/reflect/include/reflect/reflect_class.h +++ b/source/reflect/include/reflect/reflect_class.h @@ -99,17 +99,17 @@ REFLECT_API method class_static_method(klass cls, const char *key, type_id ret, REFLECT_API method class_method(klass cls, const char *key, type_id ret, type_id args[], size_t size); -REFLECT_API vector class_method_names(klass cls); +REFLECT_API vector class_get_methods(klass cls); -REFLECT_API vector class_static_method_names(klass cls); +REFLECT_API vector class_get_static_methods(klass cls); REFLECT_API attribute class_static_attribute(klass cls, const char *key); REFLECT_API attribute class_attribute(klass cls, const char *key); -REFLECT_API vector class_attribute_names(klass cls); +REFLECT_API vector class_get_attributes(klass cls); -REFLECT_API vector class_static_attribute_names(klass cls); +REFLECT_API vector class_get_static_attributes(klass cls); REFLECT_API int class_register_constructor(klass cls, constructor ctor); diff --git a/source/reflect/source/reflect_class.c b/source/reflect/source/reflect_class.c index 5242e27fe..e85d32d0f 100644 --- a/source/reflect/source/reflect_class.c +++ b/source/reflect/source/reflect_class.c @@ -588,7 +588,7 @@ constructor class_default_constructor(klass cls) return NULL; } - return (constructor)vector_at(cls->constructors, 0); + return vector_at_type(cls->constructors, 0, constructor); } constructor class_constructor(klass cls, type_id args[], size_t size) @@ -680,28 +680,14 @@ method class_method(klass cls, const char *key, type_id ret, type_id args[], siz return class_get_method_type_safe(class_methods(cls, key), ret, args, size); } -vector class_method_names(klass cls) +vector class_get_methods(klass cls) { - vector v = vector_create_type(char *); - map_iterator it = map_iterator_begin(cls->methods); - while (map_iterator_end(&it) != 0) - { - char *key = (char *)map_iterator_get_key(it); - vector_push_back(v, (void *)key); - } - return v; + return map_get_values(cls->methods); } -vector class_static_method_names(klass cls) +vector class_get_static_methods(klass cls) { - vector v = vector_create_type(char *); - map_iterator it = map_iterator_begin(cls->static_methods); - while (map_iterator_end(&it) != 0) - { - char *key = (char *)map_iterator_get_key(it); - vector_push_back(v, (void *)key); - } - return v; + return map_get_values(cls->static_methods); } attribute class_static_attribute(klass cls, const char *key) @@ -724,28 +710,14 @@ attribute class_attribute(klass cls, const char *key) return set_get(cls->attributes, (set_key)key); } -vector class_attribute_names(klass cls) +vector class_get_attributes(klass cls) { - vector v = vector_create_type(char *); - set_iterator it = set_iterator_begin(cls->attributes); - while (set_iterator_end(&it) != 0) - { - char *key = (char *)set_iterator_get_key(it); - vector_push_back(v, (void *)key); - } - return v; + return set_get_values(cls->attributes); } -vector class_static_attribute_names(klass cls) +vector class_get_static_attributes(klass cls) { - vector v = vector_create_type(char *); - set_iterator it = set_iterator_begin(cls->static_attributes); - while (set_iterator_end(&it) != 0) - { - char *key = (char *)set_iterator_get_key(it); - vector_push_back(v, (void *)key); - } - return v; + return set_get_values(cls->static_attributes); } int class_register_constructor(klass cls, constructor ctor) diff --git a/source/scripts/node/CMakeLists.txt b/source/scripts/node/CMakeLists.txt index e876f5221..0bc35e153 100644 --- a/source/scripts/node/CMakeLists.txt +++ b/source/scripts/node/CMakeLists.txt @@ -23,3 +23,4 @@ add_subdirectory(derpyramda) add_subdirectory(gram) add_subdirectory(duplicated) add_subdirectory(ramda) +add_subdirectory(node_test) diff --git a/source/scripts/node/node_test/CMakeLists.txt b/source/scripts/node/node_test/CMakeLists.txt new file mode 100644 index 000000000..f8e60b603 --- /dev/null +++ b/source/scripts/node/node_test/CMakeLists.txt @@ -0,0 +1,5 @@ +# +# Configure nodejs project +# + +nodejs_project(node_test 0.1.0) diff --git a/source/scripts/node/node_test/source/node_test.js b/source/scripts/node/node_test/source/node_test.js new file mode 100644 index 000000000..a4e88a7ef --- /dev/null +++ b/source/scripts/node/node_test/source/node_test.js @@ -0,0 +1,77 @@ +class Fibonacci { + fib_impl(n) { + if (n <= 2) { + return 1; + } else { + return fib_impl(n - 2) + fib_impl(n - 1); + } + } + + static fib(n) { + console.log("Fibbonaci of " + n + " = " + fib_impl(n)); + } +} + +class Test { + + constructor (num, str, cls, cls_args) { + this.int = 231; + this.b = 100; + this.hello_str = "hello!"; + + this.arr = [ 'K', 'G', 50, 100, "Hello", "world"]; + this.num = num; + this.str = str; + this.cls = Fibonacci; + + this.obj = new cls(...cls_args); + + // TODO: This works, But we should add tests for it + const objWithoutNew = cls(...cls_args); + // END-TODO + + console.log("NodeJs Loader: Test constructor Called!"); + } + + newFibonacci() { + return new Fibonacci; + } + + return_class() { + return Fibonacci; + } + + print() { + console.log(this.str); + } + + test_value_to_napi_class_get(property) { + return this.obj[property] + } + + test_value_to_napi_class_set(property, value) { + this.obj[property] = value + } + + test_value_to_napi_class_method_invoke(method_name, args) { + return this.obj[method_name](...args) + } + + // Static method with no parameter and void return type + static hello(s) { + console.log("hello, world! from " + s); + return 10; + } + + static sumArray(i) { + let sum = 0; + + for (let c of i) + sum += c; + + return sum; + } + +} + +module.exports = Test \ No newline at end of file diff --git a/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp b/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp index 2dc81cc7a..06f69316a 100644 --- a/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp +++ b/source/tests/metacall-node-class-test/source/metacall_node_class_test.cpp @@ -52,7 +52,7 @@ TEST_F(metacall_node_class_test, DefaultConstructor) void *new_object_v = NULL; { /* TEST LOAD FROM FILE */ const char *node_scripts[] = { - "scripts/test.js" + "scripts/node_test.js" }; static const char tag[] = "node"; @@ -84,12 +84,10 @@ TEST_F(metacall_node_class_test, DefaultConstructor) new_object_v = metacall_class_new(myclass, "test", constructor_params, sizeof(constructor_params) / sizeof(constructor_params[0])); ASSERT_NE((void *)NULL, (void *)new_object_v); - metacall_value_destroy(new_object_v); - { //Invoke static method { - void *args[] = { + const void *args[] = { metacall_value_create_int(1), metacall_value_create_int(2), metacall_value_create_int(3), @@ -102,8 +100,10 @@ TEST_F(metacall_node_class_test, DefaultConstructor) metacall_value_create_int(10) }; - void *ret = metacallt_class(myclass, "hello", METACALL_INT, args, sizeof(args) / sizeof(args[0])); - ASSERT_EQ((long)55, (long)metacall_value_to_long(ret)); + void *argv = metacall_value_create_array(args, sizeof(args) / sizeof(args[0])); + + void *ret = metacallv_class(myclass, "sumArray", &argv, 1); + ASSERT_EQ((double)55, (double)metacall_value_to_double(ret)); metacall_value_destroy(ret); } @@ -113,8 +113,8 @@ TEST_F(metacall_node_class_test, DefaultConstructor) metacall_value_create_string("Metacall", 8) }; - void *ret = metacallt_class(myclass, "sumArray", METACALL_INT, args, 1); - ASSERT_EQ((long)10, (long)metacall_value_to_long(ret)); + void *ret = metacallv_class(myclass, "hello", args, 1); + ASSERT_EQ((double)10, (double)metacall_value_to_double(ret)); metacall_value_destroy(ret); } @@ -127,7 +127,8 @@ TEST_F(metacall_node_class_test, DefaultConstructor) { void *param1 = metacall_object_get(new_object, "num"); - ASSERT_EQ((long)10, (long)metacall_value_to_long(param1)); + ASSERT_EQ((double)10, (double)metacall_value_to_double(param1)); + metacall_value_destroy(param1); } @@ -144,8 +145,8 @@ TEST_F(metacall_node_class_test, DefaultConstructor) ASSERT_EQ((int)0, int(retcode)); void *param2 = metacall_object_get(new_object, "b"); - ASSERT_EQ((enum metacall_value_id)METACALL_LONG, (enum metacall_value_id)metacall_value_id(param2)); - ASSERT_EQ((long)124124L, (long)metacall_value_to_long(param2)); + ASSERT_EQ((enum metacall_value_id)METACALL_DOUBLE, (enum metacall_value_id)metacall_value_id(param2)); + ASSERT_EQ((double)124124, (double)metacall_value_to_double(param2)); metacall_value_destroy(param2); } @@ -165,10 +166,47 @@ TEST_F(metacall_node_class_test, DefaultConstructor) metacall_value_destroy(Fibonacci); } + //Value to napi class test { - void *param1 = metacallv_object(new_object, "return_bye", metacall_null_args, 0); - ASSERT_EQ((std::string) "bye LBryan", (std::string)metacall_value_to_string(param1)); - metacall_value_destroy(param1); + { + void *args = metacall_value_create_string("b", 1); + void *param1 = metacallv_object(new_object, "test_value_to_napi_class_get", &args, 1); + ASSERT_EQ((double)999999, (double)metacall_value_to_double(param1)); + metacall_value_destroy(param1); + } + + { + char str[] = "test value class to napi."; + void *args[] = { + metacall_value_create_string("a", 1), + metacall_value_create_string(str, sizeof(str)) + }; + + void *param1 = metacallv_object(new_object, "test_value_to_napi_class_set", args, sizeof(args) / sizeof(args[0])); + ASSERT_NE((void *)NULL, (void *)param1); + metacall_value_destroy(param1); + + void *arg = metacall_value_create_string("a", 1); + void *param2 = metacallv_object(new_object, "test_value_to_napi_class_get", &arg, 1); + ASSERT_EQ((std::string)str, (std::string)metacall_value_to_string(param2)); + metacall_value_destroy(param2); + } + + { + const void *method_args[] = { + metacall_value_create_int(4), + metacall_value_create_int(7) + }; + + void *args[] = { + metacall_value_create_string("check_args", 10), + metacall_value_create_array(method_args, 2) + }; + + void *param1 = metacallv_object(new_object, "test_value_to_napi_class_method_invoke", args, sizeof(args) / sizeof(args[0])); + ASSERT_EQ((double)15, (double)metacall_value_to_double(param1)); + metacall_value_destroy(param1); + } } metacall_value_destroy(new_object_v); From abfea4e1cf90061711fdc4acd8ef6fa456b588cd Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:25:29 -0500 Subject: [PATCH 05/12] Update adt_map.c --- source/adt/source/adt_map.c | 64 +++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/source/adt/source/adt_map.c b/source/adt/source/adt_map.c index b1ff7147f..113c03788 100644 --- a/source/adt/source/adt_map.c +++ b/source/adt/source/adt_map.c @@ -253,44 +253,68 @@ vector map_get(map m, map_key key) vector map_get_keys(map m) { - vector v = vector_create(sizeof(void *)); - bucket b; - pair p; - for (size_t iterator = 0; iterator < m->capacity; iterator++) + if (m != NULL) { - b = &m->buckets[iterator]; + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } - if (b->pairs != NULL && b->count > 0) + for (size_t iterator = 0; iterator < m->capacity; iterator++) { - for (size_t index = 0; index < b->count; index++) + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) { - p = &b->pairs[index]; - vector_push_back(v, &p->key); + size_t index; + + for (index = 0; index < b->count; index++) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->key); + } } } + + return v; } - return v; + + return NULL; } vector map_get_values(map m) { - vector v = vector_create(sizeof(void *)); - bucket b; - pair p; - for (size_t iterator = 0; iterator < m->capacity; iterator++) + if (m != NULL) { - b = &m->buckets[iterator]; + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } - if (b->pairs != NULL && b->count > 0) + for (size_t iterator = 0; iterator < m->capacity; iterator++) { - for (size_t index = 0; index < b->count; index++) + bucket b = &m->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) { - p = &b->pairs[index]; - vector_push_back(v, &p->value); + size_t index; + + for (index = 0; index < b->count; index++) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->value); + } } } + + return v; } - return v; + + return NULL; } int map_contains(map m, map_key key) From 58d69a548466b244c98051281fb1f590fd266f3d Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:27:03 -0500 Subject: [PATCH 06/12] Update adt_map.c --- source/adt/source/adt_map.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/adt/source/adt_map.c b/source/adt/source/adt_map.c index 113c03788..545396704 100644 --- a/source/adt/source/adt_map.c +++ b/source/adt/source/adt_map.c @@ -262,7 +262,7 @@ vector map_get_keys(map m) return NULL; } - for (size_t iterator = 0; iterator < m->capacity; iterator++) + for (size_t iterator = 0; iterator < m->capacity; ++iterator) { bucket b = &m->buckets[iterator]; @@ -270,7 +270,7 @@ vector map_get_keys(map m) { size_t index; - for (index = 0; index < b->count; index++) + for (index = 0; index < b->count; ++index) { pair p = &b->pairs[index]; vector_push_back(v, &p->key); @@ -295,7 +295,7 @@ vector map_get_values(map m) return NULL; } - for (size_t iterator = 0; iterator < m->capacity; iterator++) + for (size_t iterator = 0; iterator < m->capacity; ++iterator) { bucket b = &m->buckets[iterator]; @@ -303,7 +303,7 @@ vector map_get_values(map m) { size_t index; - for (index = 0; index < b->count; index++) + for (index = 0; index < b->count; ++index) { pair p = &b->pairs[index]; vector_push_back(v, &p->value); From faaf896ff2d171b415ef9c86e6c4e00496d703fc Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:31:03 -0500 Subject: [PATCH 07/12] Update adt_set.c --- source/adt/source/adt_set.c | 64 +++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/source/adt/source/adt_set.c b/source/adt/source/adt_set.c index 5f448bb7d..9fb2c45ab 100644 --- a/source/adt/source/adt_set.c +++ b/source/adt/source/adt_set.c @@ -275,44 +275,68 @@ set_value set_get(set s, set_key key) vector set_get_keys(set s) { - vector v = vector_create(sizeof(void *)); - bucket b; - pair p; - for (size_t iterator = 0; iterator < s->capacity; iterator++) + if (s != NULL) { - b = &s->buckets[iterator]; + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } - if (b->pairs != NULL && b->count > 0) + for (size_t iterator = 0; iterator < s->capacity; ++iterator) { - for (size_t index = 0; index < b->count; index++) + bucket b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) { - p = &b->pairs[index]; - vector_push_back(v, &p->key); + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->key); + } } } + + return v; } - return v; + + return NULL; } vector set_get_values(set s) { - vector v = vector_create(sizeof(void *)); - bucket b; - pair p; - for (size_t iterator = 0; iterator < s->capacity; iterator++) + if (s != NULL) { - b = &s->buckets[iterator]; + vector v = vector_create(sizeof(void *)); + + if (v == NULL) + { + return NULL; + } - if (b->pairs != NULL && b->count > 0) + for (size_t iterator = 0; iterator < s->capacity; ++iterator) { - for (size_t index = 0; index < b->count; index++) + bucket b = &s->buckets[iterator]; + + if (b->pairs != NULL && b->count > 0) { - p = &b->pairs[index]; - vector_push_back(v, &p->value); + size_t index; + + for (index = 0; index < b->count; ++index) + { + pair p = &b->pairs[index]; + vector_push_back(v, &p->value); + } } } + + return v; } - return v; + + return NULL; } int set_contains(set s, set_key key) From 929a674d5dee8b24982bb0cba559bc9adc0bb108 Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:43:02 -0500 Subject: [PATCH 08/12] Update node_loader_impl.cpp --- .../node_loader/source/node_loader_impl.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index f65b3e565..021e673de 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -48,6 +48,14 @@ extern char **environ; #include #include +#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) + #include + + /* Required for the DelayLoad hook interposition, solves bug of NodeJS extensions requiring node.exe instead of node.dll*/ + #include + #pragma intrinsic(_ReturnAddress) +#endif + #include #include @@ -762,6 +770,12 @@ static int64_t node_loader_impl_async_handles_count(loader_impl_node node_impl); static void node_loader_impl_try_destroy(loader_impl_node node_impl); +#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) +/* Required for the DelayLoad hook interposition, solves bug of NodeJS extensions requiring node.exe instead of node.dll */ +static HMODULE node_loader_node_dll_handle = NULL; +static HMODULE (*get_module_handle_a_ptr)(_In_opt_ LPCSTR) = NULL; /* TODO: Implement W version too? */ +#endif + /* -- Methods -- */ void node_loader_impl_exception(napi_env env, napi_status status) From c46413696f5a4fb083cf2f597cac2c85be5b970d Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:45:43 -0500 Subject: [PATCH 09/12] Update node_test.js --- source/scripts/node/node_test/source/node_test.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/source/scripts/node/node_test/source/node_test.js b/source/scripts/node/node_test/source/node_test.js index a4e88a7ef..9b1f86eed 100644 --- a/source/scripts/node/node_test/source/node_test.js +++ b/source/scripts/node/node_test/source/node_test.js @@ -33,10 +33,6 @@ class Test { console.log("NodeJs Loader: Test constructor Called!"); } - newFibonacci() { - return new Fibonacci; - } - return_class() { return Fibonacci; } @@ -74,4 +70,4 @@ class Test { } -module.exports = Test \ No newline at end of file +module.exports = Test From 9ee81e1a1324b141df128e18ccfce2b9f0bb8c9a Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:48:29 -0500 Subject: [PATCH 10/12] Delete test.js --- source/scripts/node/test/source/test.js | 67 ------------------------- 1 file changed, 67 deletions(-) delete mode 100644 source/scripts/node/test/source/test.js diff --git a/source/scripts/node/test/source/test.js b/source/scripts/node/test/source/test.js deleted file mode 100644 index be90b4c65..000000000 --- a/source/scripts/node/test/source/test.js +++ /dev/null @@ -1,67 +0,0 @@ -class Fibonacci { - fib_impl(n) { - if (n <= 2) { - return 1; - } else { - return fib_impl(n - 2) + fib_impl(n - 1); - } - } - - static fib(n) { - console.log("Fibbonaci of " + n + " = " + fib_impl(n)); - } -} - -class Test { - - constructor (num, str, cls, cls_args) { - this.int = 231; - this.b = 100; - this.hello_str = "hello!"; - - this.arr = [ 'K', 'G', 50, 100, "Hello", "world"]; - this.num = num; - this.str = str; - this.cls = Fibonacci; - - // TODO: Add test for this - this.obj = new cls(...cls_args); - // END-TODO - - console.log("NodeJs Loader: Test constructor Called!"); - } - - newFibonacci() { - return new Fibonacci; - } - - return_class() { - return Fibonacci; - } - - return_new_obj() { - return this.obj.return_bye('LBryan'); - } - - print() { - console.log(this.str); - } - - // Static method with no parameter and void return type - static hello(s) { - console.log("hello, world! running on " + s); - return 10; - } - - static sumArray(i) { - sum = 0; - - for (let c of i) - sum += c; - - return sum; - } - -} - -module.exports = Test \ No newline at end of file From 11d1be2eec5e6e6b6e31520f0dd9a02a40c1e59e Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:09:39 -0500 Subject: [PATCH 11/12] Update node_loader_impl.cpp --- .../node_loader/source/node_loader_impl.cpp | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index 021e673de..1f9bd94c2 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -5852,6 +5852,51 @@ void node_loader_impl_thread_safe_function_initialize(napi_env env, node_loader_impl_exception(env, status); } +#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) +/* TODO: _Ret_maybenull_ HMODULE WINAPI GetModuleHandleW(_In_opt_ LPCWSTR lpModuleName); */ +_Ret_maybenull_ HMODULE WINAPI get_module_handle_a_hook(_In_opt_ LPCSTR lpModuleName) +{ + /* This hooks GetModuleHandle, which is called as DelayLoad hook inside NodeJS + * extensions in order to retrieve the executable handle, which is supposed + * to have all N-API symbols. This trick is used because the design of NodeJS forces + * to compile statically node.dll into the executable, but this does not happen on + * MetaCall as it is embedded. We cannot change this behavior because it depends on + * NodeJS extension build system, which relies on DelayLoad mechanism. So what we are + * doing here is intercepting the GetModuleHandle call inside the DelayLoad hook, then + * getting the address from where this Win32 API was called, and if it commes from a + * NodeJS extension, then we return the node.dll module, otherwise we call to the original + * GetModuleHandle funciton. This method successfully hooks into the NodeJS mechanism and + * redirects properly the linker resolver system to the node.dll where symbols are located. + */ + if (lpModuleName == NULL) + { + HMODULE mod = NULL; + + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, /* Behave like GetModuleHandle */ + (LPCTSTR)_ReturnAddress(), &mod) == TRUE) + { + static const char node_ext[] = ".node"; + char mod_name[MAX_PATH]; + size_t length = GetModuleFileName(mod, mod_name, MAX_PATH); + + /* It must contain a letter a part from the .node extension */ + if (length > sizeof(node_ext)) + { + char *ext = &mod_name[length - sizeof(node_ext) + 1]; + + if (strncmp(ext, node_ext, sizeof(node_ext)) == 0) + { + return node_loader_node_dll_handle; + } + } + } + } + + return get_module_handle_a_ptr(lpModuleName); +} +#endif + void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *function_table_object_ptr) { loader_impl_node node_impl = static_cast(node_impl_ptr); @@ -6186,6 +6231,12 @@ void *node_loader_impl_register(void *node_impl_ptr, void *env_ptr, void *functi node_impl->extra_active_handles.store(0); node_impl->event_loop_empty.store(false); + /* On Windows, hook node extension loading mechanism in order to patch extensions linked to node.exe */ +#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER >= 1200) + node_loader_node_dll_handle = GetModuleHandle(NODEJS_LIBRARY_NAME); + get_module_handle_a_ptr = (HMODULE(*)(_In_opt_ LPCSTR))node_loader_hook_import_address_table("kernel32.dll", "GetModuleHandleA", &get_module_handle_a_hook); +#endif + /* Signal start condition */ uv_cond_signal(&node_impl->cond); @@ -7586,7 +7637,7 @@ int node_loader_impl_destroy(loader_impl impl) #endif /* Print NodeJS execution result */ - log_write("metacall", LOG_LEVEL_INFO, "NodeJS execution return status %d", node_impl->result); + log_write("metacall", LOG_LEVEL_DEBUG, "NodeJS execution return status %d", node_impl->result); /* Restore stdin, stdout, stderr */ dup2(node_impl->stdin_copy, STDIN_FILENO); From 39a9b749b7bce07efb47de0149bc9b1e121fa537 Mon Sep 17 00:00:00 2001 From: Bryan Elee Date: Wed, 22 Feb 2023 18:52:01 +0100 Subject: [PATCH 12/12] Update node_loader_impl.cpp --- source/loaders/node_loader/source/node_loader_impl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index 1f9bd94c2..daa348d92 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -3636,7 +3636,6 @@ napi_value node_loader_impl_async_class_ctor_safe(napi_env env, napi_callback_in /* Store environment for reentrant calls */ class_ctor_safe_cast.safe->node_impl->env = env; - printf("calling class_ctor_safe async"); /* Call to the implementation function */ node_loader_impl_class_ctor_safe(env, class_ctor_safe_cast.safe);